Guide to the Secure Configuration of Red Hat Enterprise Linux 6

with profile Upstream STIG for Red Hat Enterprise Linux 6 Workstation
This profile is developed under the DoD consensus model and DISA FSO Vendor STIG process, serving as the upstream development environment for the Red Hat Enterprise Linux 6 Server STIG. As a result of the upstream/downstream relationship between the SCAP Security Guide project and the official DISA FSO STIG baseline, users should expect variance between SSG and DISA FSO content. For official DISA FSO STIG content, refer to http://iase.disa.mil/stigs/os/unix-linux/Pages/red-hat.aspx. While this profile is packaged by Red Hat as part of the SCAP Security Guide package, please note that commercial support of this SCAP content is NOT available. This profile is provided as example SCAP content with no endorsement for suitability or production readiness. Support for this profile is provided by the upstream SCAP Security Guide community on a best-effort basis. The upstream project homepage is https://www.open-scap.org/security-policies/scap-security-guide/.

This guide presents a catalog of security-relevant configuration settings for Red Hat Enterprise Linux 6. It is a rendering of content structured in the eXtensible Configuration Checklist Description Format (XCCDF) in order to support security automation. The SCAP content is is available in the scap-security-guide package which is developed at https://www.open-scap.org/security-policies/scap-security-guide.

Providing system administrators with such guidance informs them how to securely configure systems under their control in a variety of network roles. Policy makers and baseline creators can use this catalog of settings, with its associated references to higher-level security control catalogs, in order to assist them in security baseline creation. This guide is a catalog, not a checklist, and satisfaction of every item is not likely to be possible or sensible in many operational scenarios. However, the XCCDF format enables granular selection and adjustment of settings, and their association with OVAL and OCIL content provides an automated checking capability. Transformations of this document, and its associated automated checking content, are capable of providing baselines that meet a diverse set of policy objectives. Some example XCCDF Profiles, which are selections of items that form checklists and can be used as baselines, are available with this guide. They can be processed, in an automated fashion, with tools that support the Security Content Automation Protocol (SCAP). The DISA STIG for Red Hat Enterprise Linux 6, which provides required settings for US Department of Defense systems, is one example of a baseline created from this guidance.
Do not attempt to implement any of the settings in this guide without first testing them in a non-operational environment. The creators of this guidance assume no responsibility whatsoever for its use by other parties, and makes no guarantees, expressed or implied, about its quality, reliability, or any other characteristic.
Profile TitleUpstream STIG for Red Hat Enterprise Linux 6 Workstation
Profile IDxccdf_org.ssgproject.content_profile_stig-rhel6-workstation-upstream

Revision History

Current version: 0.1.31

  • draft (as of 2016-11-28)

Platforms

  • cpe:/o:redhat:enterprise_linux:6
  • cpe:/o:redhat:enterprise_linux:6::client
  • cpe:/o:redhat:enterprise_linux:6::computenode

Table of Contents

  1. System Settings
    1. Protect Random-Number Entropy Pool
    2. Installing and Maintaining Software
    3. File Permissions and Masks
    4. SELinux
    5. Account and Access Control
    6. Network Configuration and Firewalls
    7. Configure Syslog
    8. System Accounting with auditd
  2. Services
    1. Obsolete Services
    2. Base Services
    3. Cron and At Daemons
    4. SSH Server
    5. Avahi Server
    6. DHCP
    7. Network Time Protocol
    8. Mail Server Software
    9. LDAP
    10. NFS and RPC
    11. FTP Server
    12. Samba(SMB) Microsoft Windows File Sharing Server
    13. SNMP Server

Checklist

contains 228 rules

System Settingsgroup

Contains rules that check correct system settings.

contains 179 rules

Protect Random-Number Entropy Poolgroup

The I/O operations of the Linux kernel block layer due to their inherently unpredictable execution times have been traditionally considered as a reliable source to contribute to random-number entropy pool of the Linux kernel. This has changed with introduction of solid-state storage devices (SSDs) though.

contains 1 rule

Ensure Solid State Drives Do Not Contribute To Random-Number Entropy Poolrule

For each solid-state drive on the system, run:

 # echo 0 > /sys/block/DRIVE/queue/add_random

Rationale:

In contrast to traditional electromechanical magnetic disks, containing spinning disks and / or movable read / write heads, the solid-state storage devices (SSDs) do not contain moving / mechanical components. Therefore the I/O operation completion times are much more predictable for them.

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

# First obtain the list of block devices present on system into array
#
# Used lsblk options:
# -o NAME	Display only block device name
# -a		Display all devices (including empty ones) in the list
# -d		Don't print device holders or slaves information
# -n		Suppress printing of introductory heading line in the list
SYSTEM_BLOCK_DEVICES=($(/bin/lsblk -o NAME -a -d -n))

# For each SSD block device from that list
# (device where /sys/block/DEVICE/queue/rotation == 0)
for BLOCK_DEVICE in "${SYSTEM_BLOCK_DEVICES[@]}"
do
	# Verify the block device is SSD
	if grep -q "0" /sys/block/${BLOCK_DEVICE}/queue/rotational
	then
		# If particular SSD is configured to contribute to
		# random-number entropy pool, disable it
		if grep -q "1" /sys/block/${BLOCK_DEVICE}/queue/add_random
		then
			echo "0" > /sys/block/${BLOCK_DEVICE}/queue/add_random
		fi
	fi
done

Installing and Maintaining Softwaregroup

The following sections contain information on security-relevant choices during the initial operating system installation process and the setup of software updates.

contains 21 rules

Disk Partitioninggroup

To ensure separation and protection of data, there are top-level system directories which should be placed on their own physical partition or logical volume. The installer's default partitioning scheme creates separate logical volumes for /, /boot, and swap.

  • If starting with any of the default layouts, check the box to "Review and modify partitioning." This allows for the easy creation of additional logical volumes inside the volume group already created, though it may require making /'s logical volume smaller to create space. In general, using logical volumes is preferable to using partitions because they can be more easily adjusted later.
  • If creating a custom layout, create the partitions mentioned in the previous paragraph (which the installer will require anyway), as well as separate ones described in the following sections.
If a system has already been installed, and the default partitioning scheme was used, it is possible but nontrivial to modify it to create separate logical volumes for the directories listed above. The Logical Volume Manager (LVM) makes this possible. See the LVM HOWTO at http://tldp.org/HOWTO/LVM-HOWTO/ for more detailed information on LVM.

contains 6 rules

Ensure /tmp Located On Separate Partitionrule

The /tmp directory is a world-writable directory used for temporary file storage. Ensure it has its own partition or logical volume at installation time, or migrate it using LVM.

Rationale:

The /tmp partition is used as temporary storage by many programs. Placing /tmp in its own partition enables the setting of more restrictive mount options, which can help protect programs which use it.

identifiers:  CCE-26435-8, DISA FSO RHEL-06-000001

references:  SC-32, 1208

Ensure /var Located On Separate Partitionrule

The /var directory is used by daemons and other system services to store frequently-changing data. Ensure that /var has its own partition or logical volume at installation time, or migrate it using LVM.

Rationale:

Ensuring that /var is mounted on its own partition enables the setting of more restrictive mount options. This helps protect system services such as daemons or other programs which use it. It is not uncommon for the /var directory to contain world-writable directories installed by other software packages.

identifiers:  CCE-26639-5, DISA FSO RHEL-06-000002

references:  SC-32, 1208

Ensure /var/log Located On Separate Partitionrule

System logs are stored in the /var/log directory. Ensure that it has its own partition or logical volume at installation time, or migrate it using LVM.

Rationale:

Placing /var/log in its own partition enables better separation between log files and other files in /var/.

identifiers:  CCE-26215-4, DISA FSO RHEL-06-000003

references:  AU-9, SC-32, 1208

Ensure /var/log/audit Located On Separate Partitionrule

Audit logs are stored in the /var/log/audit directory. Ensure that it has its own partition or logical volume at installation time, or migrate it later using LVM. Make absolutely certain that it is large enough to store all audit logs that will be created by the auditing daemon.

Rationale:

Placing /var/log/audit in its own partition enables better separation between audit files and other files, and helps ensure that auditing cannot be halted due to the partition running out of space.

identifiers:  CCE-26436-6, DISA FSO RHEL-06-000004

references:  AU-4, AU-9, SC-32, 137, 138, 1208

Ensure /home Located On Separate Partitionrule

If user home directories will be stored locally, create a separate partition for /home at installation time (or migrate it later using LVM). If /home will be mounted from another system such as an NFS server, then creating a separate partition is not necessary at installation time, and the mountpoint can instead be configured later.

Rationale:

Ensuring that /home is mounted on its own partition enables the setting of more restrictive mount options, and also helps ensure that users cannot trivially fill partitions used for log or audit data storage.

identifiers:  CCE-26557-9, DISA FSO RHEL-06-000007

references:  SC-32, 1208

Encrypt Partitionsrule

Red Hat Enterprise Linux 6 natively supports partition encryption through the Linux Unified Key Setup-on-disk-format (LUKS) technology. The easiest way to encrypt a partition is during installation time.

For manual installations, select the Encrypt checkbox during partition creation to encrypt the partition. When this option is selected the system will prompt for a passphrase to use in decrypting the partition. The passphrase will subsequently need to be entered manually every time the system boots.

For automated/unattended installations, it is possible to use Kickstart by adding the --encrypted and --passphrase= options to the definition of each partition to be encrypted. For example, the following line would encrypt the root partition:

part / --fstype=ext3 --size=100 --onpart=hda1 --encrypted --passphrase=PASSPHRASE
Any PASSPHRASE is stored in the Kickstart in plaintext, and the Kickstart must then be protected accordingly. Omitting the --passphrase= option from the partition definition will cause the installer to pause and interactively ask for the passphrase during installation.

Detailed information on encrypting partitions using LUKS can be found on the Red Hat Documentation web site:
https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security_Guide/chap-Security_Guide-Encryption.html#sect-Security_Guide-LUKS_Disk_Encryption

Rationale:

The risk of a system's physical compromise, particularly mobile systems such as laptops, places its data at risk of compromise. Encrypting this data mitigates the risk of its loss if the system is lost.

identifiers:  CCE-27596-6, DISA FSO RHEL-06-000275

references:  SC-13, SC-28, 1019, 1199, 1200

Updating Softwaregroup

The yum command line tool is used to install and update software packages. The system also provides a graphical software update tool in the System menu, in the Administration submenu, called Software Update.

Red Hat Enterprise Linux systems contain an installed software catalog called the RPM database, which records metadata of installed packages. Consistently using yum or the graphical Software Update for all software installation allows for insight into the current inventory of installed software on the system.

contains 4 rules

Ensure Red Hat GPG Key Installedrule

To ensure the system can cryptographically verify base software packages come from Red Hat (and to connect to the Red Hat Network to receive them), the Red Hat GPG key must properly be installed. To install the Red Hat GPG key, run:

$ sudo rhn_register
If the system is not connected to the Internet or an RHN Satellite, then install the Red Hat GPG key from trusted media such as the Red Hat installation CD-ROM or DVD. Assuming the disc is mounted in /media/cdrom, use the following command as the root user to import it into the keyring:
$ sudo rpm --import /media/cdrom/RPM-GPG-KEY

Rationale:

The Red Hat GPG key is necessary to cryptographically verify packages are from Red Hat.

identifiers:  CCE-26506-6, DISA FSO RHEL-06-000008

references:  SI-7, MA-1(b), 351

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
# The two fingerprints below are retrieved from https://access.redhat.com/security/team/key
readonly REDHAT_RELEASE_2_FINGERPRINT="567E 347A D004 4ADE 55BA 8A5F 199E 2F91 FD43 1D51"
readonly REDHAT_AUXILIARY_FINGERPRINT="43A6 E49C 4A38 F4BE 9ABF 2A53 4568 9C88 2FA6 58E0"
# Location of the key we would like to import (once it's integrity verified)
readonly REDHAT_RELEASE_KEY="/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"

RPM_GPG_DIR_PERMS=$(stat -c %a "$(dirname "$REDHAT_RELEASE_KEY")")

# Verify /etc/pki/rpm-gpg directory permissions are safe
if [ "${RPM_GPG_DIR_PERMS}" -le "755" ]
then
  # If they are safe, try to obtain fingerprints from the key file
  # (to ensure there won't be e.g. CRC error)
  IFS=$'\n' GPG_OUT=($(gpg --with-fingerprint "${REDHAT_RELEASE_KEY}"))
  GPG_RESULT=$?
  # No CRC error, safe to proceed
  if [ "${GPG_RESULT}" -eq "0" ]
  then
    for ITEM in "${GPG_OUT[@]}"
    do
      # Filter just hexadecimal fingerprints from gpg's output from
      # processing of a key file
      RESULT=$(echo ${ITEM} | sed -n "s/[[:space:]]*Key fingerprint = \(.*\)/\1/p" | tr -s '[:space:]')
      # If fingerprint matches Red Hat's release 2 or auxiliary key import the key
      if [[ ${RESULT} ]] && ([[ ${RESULT} = "${REDHAT_RELEASE_2_FINGERPRINT}" ]] || \
                             [[ ${RESULT} = "${REDHAT_AUXILIARY_FINGERPRINT}" ]])
      then
        rpm --import "${REDHAT_RELEASE_KEY}"
      fi
    done
  fi
fi

Ensure gpgcheck Enabled In Main Yum Configurationrule

The gpgcheck option controls whether RPM packages' signatures are always checked prior to installation. To configure yum to check package signatures before installing them, ensure the following line appears in /etc/yum.conf in the [main] section:

gpgcheck=1

Rationale:

Ensuring the validity of packages' cryptographic signatures prior to installation ensures the authenticity of the software and protects against malicious tampering.

identifiers:  CCE-26709-6, DISA FSO RHEL-06-000013

references:  SI-7, MA-1(b), 352, 663

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
sed -i 's/gpgcheck=.*/gpgcheck=1/g' /etc/yum.conf

Ensure gpgcheck Enabled For All Yum Package Repositoriesrule

To ensure signature checking is not disabled for any repos, remove any lines from files in /etc/yum.repos.d of the form:

gpgcheck=0

Rationale:

Ensuring all packages' cryptographic signatures are valid prior to installation ensures the authenticity of the software and protects against malicious tampering.

identifiers:  CCE-26647-8, DISA FSO RHEL-06-000015

references:  SI-7, MA-1(b), 352, 663

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
sed -i 's/gpgcheck=.*/gpgcheck=1/g' /etc/yum.repos.d/*

Ensure Software Patches Installedrule

If the system is joined to the Red Hat Network, a Red Hat Satellite Server, or a yum server, run the following command to install updates:

$ sudo yum update
If the system is not configured to use one of these sources, updates (in the form of RPM packages) can be manually downloaded from the Red Hat Network and installed using rpm.

Rationale:

Installing software updates is a fundamental mitigation against the exploitation of publicly-known vulnerabilities.

identifiers:  CCE-27635-2, DISA FSO RHEL-06-000011

references:  SI-2, MA-1(b), 1227, 1233

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
yum -y update

Software Integrity Checkinggroup

Both the AIDE (Advanced Intrusion Detection Environment) software and the RPM package management system provide mechanisms for verifying the integrity of installed software. AIDE uses snapshots of file metadata (such as hashes) and compares these to current system files in order to detect changes. The RPM package management system can conduct integrity checks by comparing information in its metadata database with files installed on the system.

Integrity checking cannot prevent intrusions, but can detect that they have occurred. Requirements for software integrity checking may be highly dependent on the environment in which the system will be used. Snapshot-based approaches such as AIDE may induce considerable overhead in the presence of frequent software updates.

contains 6 rules

Verify Integrity with AIDEgroup

AIDE conducts integrity checks by comparing information about files with previously-gathered information. Ideally, the AIDE database is created immediately after initial system configuration, and then again after any software update. AIDE is highly configurable, with further configuration information located in /usr/share/doc/aide-VERSION.

contains 2 rules

Install AIDErule

Install the AIDE package with the command:

$ sudo yum install aide

Rationale:

The AIDE package must be installed if it is to be available for integrity checking.

identifiers:  CCE-27024-9, DISA FSO RHEL-06-000016

references:  CM-3(d), CM-3(e), CM-6(d), SC-28, SI-7, 1069

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

function package_command {

# Load function arguments into local variables
local package_operation=$1
local package=$2

# Check sanity of the input
if [ $# -ne "2" ]
then
  echo "Usage: package_command 'install/uninstall' 'rpm_package_name"
  echo "Aborting."
  exit 1
fi

# If dnf is installed, use dnf; otherwise, use yum
if [ -f "/usr/bin/dnf" ] ; then
  install_util="/usr/bin/dnf"
else
  install_util="/usr/bin/yum"
fi

if [ "$package_operation" != 'remove' ] ; then
  # If the rpm is not installed, install the rpm
  if ! /bin/rpm -q --quiet $package; then
    $install_util -y $package_operation $package
  fi
else
  # If the rpm is installed, uninstall the rpm
  if /bin/rpm -q --quiet $package; then
    $install_util -y $package_operation $package
  fi
fi

}

package_command install aide
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Ensure aide is installed
  package:
    name="{{item}}"
    state=present
  with_items:
    - aide
Remediation Puppet snippet:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_aide

class install_aide {
  package { 'aide':
    ensure => 'installed',
  }
}
Remediation script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=aide

Configure Periodic Execution of AIDErule

To implement a daily execution of AIDE at 4:05am using cron, add the following line to /etc/crontab:

05 4 * * * root /usr/sbin/aide --check
AIDE can be executed periodically through other means; this is merely one example.

Rationale:

By default, AIDE does not install itself for periodic execution. Periodically running AIDE is necessary to reveal unexpected changes in installed files.

identifiers:  CCE-27222-9, DISA FSO RHEL-06-000306

references:  CM-3(d), CM-3(e), CM-6(d), SC-28, SI-7, 374, 416, 1069, 1263, 1297, 1589

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
echo "05 4 * * * root /usr/sbin/aide --check" >> /etc/crontab

Verify Integrity with RPMgroup

The RPM package management system includes the ability to verify the integrity of installed packages by comparing the installed files with information about the files taken from the package metadata stored in the RPM database. Although an attacker could corrupt the RPM database (analogous to attacking the AIDE database as described above), this check can still reveal modification of important files. To list which files on the system differ from what is expected by the RPM database:

$ rpm -qVa
See the man page for rpm to see a complete explanation of each column.

contains 2 rules

Verify and Correct File Permissions with RPMrule

The RPM package management system can check file access permissions of installed software packages, including many that are important to system security. After locating a file with incorrect permissions, run the following command to determine which package owns it:

$ rpm -qf FILENAME
Next, run the following command to reset its permissions to the correct values:
$ sudo rpm --setperms PACKAGENAME

warning  Note: Due to a bug in the gdm package, the RPM verify command may continue to fail even after file permissions have been correctly set on /var/log/gdm. This is being tracked in Red Hat Bugzilla #1277603.
Rationale:

Permissions on system binaries and configuration files that are too generous could allow an unauthorized user to gain privileges that they should not have. The permissions set by the vendor should be maintained. Any deviations from this baseline should be investigated.

identifiers:  CCE-26731-0, DISA FSO RHEL-06-000518

references:  AC-6, CM-6(d), SI-7, 1493, 1494, 1495

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

# Declare array to hold list of RPM packages we need to correct permissions for
declare -a SETPERMS_RPM_LIST

# Create a list of files on the system having permissions different from what
# is expected by the RPM database
FILES_WITH_INCORRECT_PERMS=($(rpm -Va --nofiledigest | grep '^.M'))

# For each file path from that list:
# * Determine the RPM package the file path is shipped by,
# * Include it into SETPERMS_RPM_LIST array

for FILE_PATH in "${FILES_WITH_INCORRECT_PERMS[@]}"
do
	RPM_PACKAGE=$(rpm -qf "$FILE_PATH")
	SETPERMS_RPM_LIST=("${SETPERMS_RPM_LIST[@]}" "$RPM_PACKAGE")
done

# Remove duplicate mention of same RPM in $SETPERMS_RPM_LIST (if any)
SETPERMS_RPM_LIST=( $(echo "${SETPERMS_RPM_LIST[@]}" | sort -n | uniq) )

# For each of the RPM packages left in the list -- reset its permissions to the
# correct values
for RPM_PACKAGE in "${SETPERMS_RPM_LIST[@]}"
do
	rpm --setperms "${RPM_PACKAGE}"
done

Verify File Hashes with RPMrule

The RPM package management system can check the hashes of installed software packages, including many that are important to system security. Run the following command to list which files on the system have hashes that differ from what is expected by the RPM database:

$ rpm -Va | grep '^..5'
A "c" in the second column indicates that a file is a configuration file, which may appropriately be expected to change. If the file was not expected to change, investigate the cause of the change using audit logs or other means. The package can then be reinstalled to restore the file. Run the following command to determine which package owns the file:
$ rpm -qf FILENAME
The package can be reinstalled from a yum repository using the command:
$ sudo yum reinstall PACKAGENAME
Alternatively, the package can be reinstalled from trusted media using the command:
$ sudo rpm -Uvh PACKAGENAME

Rationale:

The hashes of important files like system executables should match the information given by the RPM database. Executables with erroneous hashes could be a sign of nefarious activity on the system.

identifiers:  CCE-27223-7, DISA FSO RHEL-06-000519

references:  CM-6(d), SI-7, 1496

Additional Security Softwaregroup

Additional security software that is not provided or supported by Red Hat can be installed to provide complementary or duplicative security capabilities to those provided by the base platform. Add-on software may not be appropriate for some specialized systems.

contains 2 rules

Install Intrusion Detection Softwarerule

The base Red Hat platform already includes a sophisticated auditing system that can detect intruder activity, as well as SELinux, which provides host-based intrusion prevention capabilities by confining privileged programs and user sessions which may become compromised.
In DoD environments, supplemental intrusion detection tools, such as, the McAfee Host-based Security System, are available to integrate with existing infrastructure. When these supplemental tools interfere with the proper functioning of SELinux, SELinux takes precedence.

Rationale:

Host-based intrusion detection tools provide a system-level defense when an intruder gains access to a system or network.

identifiers:  CCE-27409-2, DISA FSO RHEL-06-000285

references:  SC-7, 1263

Install Virus Scanning Softwarerule

Install virus scanning software, which uses signatures to search for the presence of viruses on the filesystem. The McAfee VirusScan Enterprise for Linux virus scanning tool is provided for DoD systems. Ensure virus definition files are no older than 7 days, or their last release. Configure the virus scanning software to perform scans dynamically on all accessed files. If this is not possible, configure the system to scan all altered files on the system on a daily basis. If the system processes inbound SMTP mail, configure the virus scanner to scan all received mail.

Rationale:

Virus scanning software can be used to detect if a system has been compromised by computer viruses, as well as to limit their spread to other systems.

identifiers:  CCE-27529-7, DISA FSO RHEL-06-000284

references:  SC-28, SI-3, 1239, 1668

GNOME Desktop Environmentgroup

GNOME is a graphical desktop environment bundled with many Linux distributions that allow users to easily interact with the operating system graphically rather than textually. The GNOME Graphical Display Manager (GDM) provides login, logout, and user switching contexts as well as display server management.

GNOME is developed by the GNOME Project and is considered the default Red Hat Graphical environment.

For more information on GNOME and the GNOME Project, see https://www.gnome.org

contains 5 rules

Configure GNOME Screen Lockinggroup

In the default GNOME desktop, the screen can be locked by choosing Lock Screen from the System menu.

The gconftool-2 program can be used to enforce mandatory screen locking settings for the default GNOME environment. The following sections detail commands to enforce idle activation of the screensaver, screen locking, a blank-screen screensaver, and an idle activation time.

Because users should be trained to lock the screen when they step away from the computer, the automatic locking feature is only meant as a backup. The Lock Screen icon from the System menu can also be dragged to the taskbar in order to facilitate even more convenient screen-locking.

The root account cannot be screen-locked, but this should have no practical effect as the root account should never be used to log into an X Windows environment, and should only be used to for direct login via console in emergency circumstances.

For more information about configuring GNOME screensaver, see http://live.gnome.org/GnomeScreensaver. For more information about enforcing preferences in the GNOME environment using the GConf configuration system, see http://projects.gnome.org/gconf and the man page gconftool-2(1).

contains 4 rules

Set GNOME Login Inactivity Timeoutrule

Run the following command to set the idle time-out value for inactivity in the GNOME desktop to 900 minutes:

$ sudo gconftool-2 \
  --direct \
  --config-source xml:readwrite:/etc/gconf/gconf.xml.mandatory \
  --type int \
  --set /desktop/gnome/session/idle_delay 900

Rationale:

Setting the idle delay controls when the screensaver will start, and can be combined with screen locking to prevent access from passersby.

identifiers:  CCE-26828-4, DISA FSO RHEL-06-000257

references:  AC-11(a), 57

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

inactivity_timeout_value="900"

# Install GConf2 package if not installed
if ! rpm -q GConf2; then
  yum -y install GConf2
fi

# Set the idle time-out value for inactivity in the GNOME desktop to meet the
# requirement
gconftool-2 --direct \
            --config-source "xml:readwrite:/etc/gconf/gconf.xml.mandatory" \
            --type int \
            --set /desktop/gnome/session/idle_delay ${inactivity_timeout_value}

GNOME Desktop Screensaver Mandatory Userule

Run the following command to activate the screensaver in the GNOME desktop after a period of inactivity:

$ sudo gconftool-2 --direct \
  --config-source xml:readwrite:/etc/gconf/gconf.xml.mandatory \
  --type bool \
  --set /apps/gnome-screensaver/idle_activation_enabled true

Rationale:

Enabling idle activation of the screensaver ensures the screensaver will be activated after the idle delay. Applications requiring continuous, real-time screen display (such as network management products) require the login session does not have administrator rights and the display station is located in a controlled-access area.

identifiers:  CCE-26600-7, DISA FSO RHEL-06-000258

references:  AC-11(a), 57

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Install GConf2 package if not installed
if ! rpm -q GConf2; then
  yum -y install GConf2
fi

# Set the screensaver activation in the GNOME desktop after a period of inactivity
gconftool-2 --direct \
            --config-source "xml:readwrite:/etc/gconf/gconf.xml.mandatory" \
            --type bool \
            --set /apps/gnome-screensaver/idle_activation_enabled true

Enable Screen Lock Activation After Idle Periodrule

Run the following command to activate locking of the screensaver in the GNOME desktop when it is activated:

$ sudo gconftool-2 --direct \
  --config-source xml:readwrite:/etc/gconf/gconf.xml.mandatory \
  --type bool \
  --set /apps/gnome-screensaver/lock_enabled true

Rationale:

Enabling the activation of the screen lock after an idle period ensures password entry will be required in order to access the system, preventing access by passersby.

identifiers:  CCE-26235-2, DISA FSO RHEL-06-000259

references:  AC-11(a), 57

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Install GConf2 package if not installed
if ! rpm -q GConf2; then
  yum -y install GConf2
fi

# Set the screensaver locking activation in the GNOME desktop when the
# screensaver is activated
gconftool-2 --direct \
            --config-source "xml:readwrite:/etc/gconf/gconf.xml.mandatory" \
            --type bool \
            --set /apps/gnome-screensaver/lock_enabled true

Implement Blank Screensaverrule

Run the following command to set the screensaver mode in the GNOME desktop to a blank screen:

$ sudo gconftool-2 --direct \
  --config-source xml:readwrite:/etc/gconf/gconf.xml.mandatory \
  --type string \
  --set /apps/gnome-screensaver/mode blank-only

Rationale:

Setting the screensaver mode to blank-only conceals the contents of the display from passersby.

identifiers:  CCE-26638-7, DISA FSO RHEL-06-000260

references:  AC-11(b), 60

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Install GConf2 package if not installed
if ! rpm -q GConf2; then
  yum -y install GConf2
fi

# Set the screensaver mode in the GNOME desktop to a blank screen
gconftool-2 --direct \
            --config-source "xml:readwrite:/etc/gconf/gconf.xml.mandatory" \
            --type string \
            --set /apps/gnome-screensaver/mode blank-only

GNOME System Settingsgroup

GNOME provides configuration and functionality to a graphical desktop environment that changes grahical configurations or allow a user to perform actions that users normally would not be able to do in non-graphical mode such as remote access configuration, power policies, Geo-location, etc. Configuring such settings in GNOME will prevent accidential graphical configuration changes by users from taking place.

contains 1 rule

Disable Ctrl-Alt-Del Reboot Key Sequence in GNOMErule

By default, GNOME will reboot the system if the Ctrl-Alt-Del key sequence is pressed.
To configure the system to ignore the Ctrl-Alt-Del key sequence from the Graphical User Interface (GUI) instead of rebooting the system, run the following:

$ sudo gconftool-2 --direct \
  --config-source xml:readwrite:/etc/gconf/gconf.xml.mandatory \
  --type string \
  --set /apps/gnome_settings_daemon/keybindings/power ""

Rationale:

A locally logged-in user who presses Ctrl-Alt-Del, when at the console, can reboot the system. If accidentally pressed, as could happen in the case of mixed OS environment, this can create the risk of short-term loss of availability of systems due to unintentional reboot.

references:  AC-6, 366, TBD

File Permissions and Masksgroup

Traditional Unix security relies heavily on file and directory permissions to prevent unauthorized users from reading or modifying files to which they should not have access.

Several of the commands in this section search filesystems for files or directories with certain characteristics, and are intended to be run on every local partition on a given system. When the variable PART appears in one of the commands below, it means that the command is intended to be run repeatedly, with the name of each local partition substituted for PART in turn.

The following command prints a list of all xfs partitions on the local system, which is the default filesystem for Red Hat Enterprise Linux 7 installations:

$ mount -t xfs | awk '{print $3}'
For any systems that use a different local filesystem type, modify this command as appropriate.

contains 33 rules

Restrict Partition Mount Optionsgroup

System partitions can be mounted with certain options that limit what files on those partitions can do. These options are set in the /etc/fstab configuration file, and can be used to make certain types of malicious behavior more difficult.

contains 6 rules

Add nodev Option to Removable Media Partitionsrule

The nodev mount option prevents files from being interpreted as character or block devices. Legitimate character and block devices should exist only in the /dev directory on the root partition or within chroot jails built for system services. Add the nodev option to the fourth column of /etc/fstab for the line which controls mounting of any removable media partitions.

Rationale:

The only legitimate location for device files is the /dev directory located on the root partition. An exception to this is chroot jails, and it is not advised to set nodev on partitions which contain their root filesystems.

identifiers:  CCE-26860-7

references:  AC-19(a), AC-19(d), AC-19(e), CM-7, MP-2

Add noexec Option to Removable Media Partitionsrule

The noexec mount option prevents the direct execution of binaries on the mounted filesystem. Preventing the direct execution of binaries from removable media (such as a USB key) provides a defense against malicious software that may be present on such untrusted media. Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of any removable media partitions.

Rationale:

Allowing users to execute binaries from removable media such as USB keys exposes the system to potential compromise.

identifiers:  CCE-27196-5, DISA FSO RHEL-06-000271

references:  AC-19(a), AC-19(d), AC-19(e), CM-7, MP-2, 87

Add nosuid Option to Removable Media Partitionsrule

The nosuid mount option prevents set-user-identifier (SUID) and set-group-identifier (SGID) permissions from taking effect. These permissions allow users to execute binaries with the same permissions as the owner and group of the file respectively. Users should not be allowed to introduce SUID and SGID files into the system via partitions mounted from removeable media. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of any removable media partitions.

Rationale:

The presence of SUID and SGID executables should be tightly controlled. Allowing users to introduce SUID or SGID binaries from partitions mounted off of removable media would allow them to introduce their own highly-privileged programs.

identifiers:  CCE-27056-1

references:  AC-19(a), AC-19(d), AC-19(e), CM-7, MP-2

Add nodev Option to /dev/shmrule

The nodev mount option can be used to prevent creation of device files in /dev/shm. Legitimate character and block devices should not exist within temporary directories like /dev/shm. Add the nodev option to the fourth column of /etc/fstab for the line which controls mounting of /dev/shm.

Rationale:

The only legitimate location for device files is the /dev directory located on the root partition. The only exception to this is chroot jails.

identifiers:  CCE-26778-1

references:  CM-7, MP-2

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

# Load /etc/fstab's /dev/shm row into DEV_SHM_FSTAB variable separating start &
# end of the filesystem mount options (4-th field) with the '#' character
DEV_SHM_FSTAB=$(sed -n "s/\(.*[[:space:]]\+\/dev\/shm[[:space:]]\+tmpfs[[:space:]]\+\)\([^[:space:]]\+\)/\1#\2#/p" /etc/fstab)

#Rest of script can trash /etc/fstab if $DEV_SHM_FSTAB is empty, check before continuing.
echo $DEV_SHM_FSTAB | grep -q -P '/dev/shm'
if [ $? -eq 0 ]; then
	# Save the:
	# * 1-th, 2-nd, 3-rd fields into DEV_SHM_HEAD variable
	# * 4-th field into DEV_SHM_OPTS variable, and
	# * 5-th, and 6-th fields into DEV_SHM_TAIL variable
	# splitting DEV_SHM_FSTAB variable value based on the '#' separator
	IFS='#' read DEV_SHM_HEAD DEV_SHM_OPTS DEV_SHM_TAIL <<< "$DEV_SHM_FSTAB"

	# Replace occurrence of 'defaults' key with the actual list of mount options
	# for Red Hat Enterprise Linux 6
	DEV_SHM_OPTS=${DEV_SHM_OPTS//defaults/rw,suid,dev,exec,auto,nouser,async,relatime}

	# 'dev' option (not prefixed with 'no') present in the list?
	echo $DEV_SHM_OPTS | grep -q -P '(?<!no)dev'
	if [ $? -eq 0 ]
	then
	        # 'dev' option found, replace with 'nodev'
	        DEV_SHM_OPTS=${DEV_SHM_OPTS//dev/nodev}
	fi

	# at least one 'nodev' present in the options list?
	echo $DEV_SHM_OPTS | grep -q -v 'nodev'
	if [ $? -eq 0 ]
	then
	        # 'nodev' not found yet, append it
	        DEV_SHM_OPTS="$DEV_SHM_OPTS,nodev"
	fi

	# DEV_SHM_OPTS now contains final list of mount options. Replace original form of /dev/shm row
	# in /etc/fstab with the corrected version
	sed -i "s#${DEV_SHM_HEAD}\(.*\)${DEV_SHM_TAIL}#${DEV_SHM_HEAD}${DEV_SHM_OPTS}${DEV_SHM_TAIL}#" /etc/fstab
fi

Add noexec Option to /dev/shmrule

The noexec mount option can be used to prevent binaries from being executed out of /dev/shm. It can be dangerous to allow the execution of binaries from world-writable temporary storage directories such as /dev/shm. Add the noexec option to the fourth column of /etc/fstab for the line which controls mounting of /dev/shm.

Rationale:

Allowing users to execute binaries from world-writable directories such as /dev/shm can expose the system to potential compromise.

identifiers:  CCE-26622-1

references:  CM-7, MP-2

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

# Load /etc/fstab's /dev/shm row into DEV_SHM_FSTAB variable separating start &
# end of the filesystem mount options (4-th field) with the '#' character
DEV_SHM_FSTAB=$(sed -n "s/\(.*[[:space:]]\+\/dev\/shm[[:space:]]\+tmpfs[[:space:]]\+\)\([^[:space:]]\+\)/\1#\2#/p" /etc/fstab)

#Rest of script can trash /etc/fstab if $DEV_SHM_FSTAB is empty, check before continuing.
echo $DEV_SHM_FSTAB | grep -q -P '/dev/shm'
if [ $? -eq 0 ]; then
	# Save the:
	# * 1-th, 2-nd, 3-rd fields into DEV_SHM_HEAD variable
	# * 4-th field into DEV_SHM_OPTS variable, and
	# * 5-th, and 6-th fields into DEV_SHM_TAIL variable
	# splitting DEV_SHM_FSTAB variable value based on the '#' separator
	IFS='#' read DEV_SHM_HEAD DEV_SHM_OPTS DEV_SHM_TAIL <<< "$DEV_SHM_FSTAB"

	# Replace occurrence of 'defaults' key with the actual list of mount options
	# for Red Hat Enterprise Linux 6
	DEV_SHM_OPTS=${DEV_SHM_OPTS//defaults/rw,suid,dev,exec,auto,nouser,async,relatime}

	# 'exec' option (not prefixed with 'no') present in the list?
	echo $DEV_SHM_OPTS | grep -q -P '(?<!no)exec'
	if [ $? -eq 0 ]
	then
	        # 'exec' option found, replace with 'noexec'
	        DEV_SHM_OPTS=${DEV_SHM_OPTS//exec/noexec}
	fi

	# at least one 'noexec' present in the options list?
	echo $DEV_SHM_OPTS | grep -q -v 'noexec'
	if [ $? -eq 0 ]
	then
	        # 'noexec' not found yet, append it
	        DEV_SHM_OPTS="$DEV_SHM_OPTS,noexec"
	fi

	# DEV_SHM_OPTS now contains final list of mount options. Replace original form of /dev/shm row
	# in /etc/fstab with the corrected version
	sed -i "s#${DEV_SHM_HEAD}\(.*\)${DEV_SHM_TAIL}#${DEV_SHM_HEAD}${DEV_SHM_OPTS}${DEV_SHM_TAIL}#" /etc/fstab

fi

Add nosuid Option to /dev/shmrule

The nosuid mount option can be used to prevent execution of setuid programs in /dev/shm. The SUID and SGID permissions should not be required in these world-writable directories. Add the nosuid option to the fourth column of /etc/fstab for the line which controls mounting of /dev/shm.

Rationale:

The presence of SUID and SGID executables should be tightly controlled. Users should not be able to execute SUID or SGID binaries from temporary storage partitions.

identifiers:  CCE-26486-1

references:  CM-7, MP-2

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

# Load /etc/fstab's /dev/shm row into DEV_SHM_FSTAB variable separating start &
# end of the filesystem mount options (4-th field) with the '#' character
DEV_SHM_FSTAB=$(sed -n "s/\(.*[[:space:]]\+\/dev\/shm[[:space:]]\+tmpfs[[:space:]]\+\)\([^[:space:]]\+\)/\1#\2#/p" /etc/fstab)

#Rest of script can trash /etc/fstab if $DEV_SHM_FSTAB is empty, check before continuing.
echo $DEV_SHM_FSTAB | grep -q -P '/dev/shm'
if [ $? -eq 0 ]; then
	# Save the:
	# * 1-th, 2-nd, 3-rd fields into DEV_SHM_HEAD variable
	# * 4-th field into DEV_SHM_OPTS variable, and
	# * 5-th, and 6-th fields into DEV_SHM_TAIL variable
	# splitting DEV_SHM_FSTAB variable value based on the '#' separator
	IFS='#' read DEV_SHM_HEAD DEV_SHM_OPTS DEV_SHM_TAIL <<< "$DEV_SHM_FSTAB"

	# Replace occurrence of 'defaults' key with the actual list of mount options
	# for Red Hat Enterprise Linux 6
	DEV_SHM_OPTS=${DEV_SHM_OPTS//defaults/rw,suid,dev,exec,auto,nouser,async,relatime}

	# 'suid' option (not prefixed with 'no') present in the list?
	echo $DEV_SHM_OPTS | grep -q -P '(?<!no)suid'
	if [ $? -eq 0 ]
	then
	        # 'suid' option found, replace with 'nosuid'
	        DEV_SHM_OPTS=${DEV_SHM_OPTS//suid/nosuid}
	fi

	# at least one 'nosuid' present in the options list?
	echo $DEV_SHM_OPTS | grep -q -v 'nosuid'
	if [ $? -eq 0 ]
	then
	        # 'nosuid' not found yet, append it
	        DEV_SHM_OPTS="$DEV_SHM_OPTS,nosuid"
	fi

	# DEV_SHM_OPTS now contains final list of mount options. Replace original form of /dev/shm row
	# in /etc/fstab with the corrected version
	sed -i "s#${DEV_SHM_HEAD}\(.*\)${DEV_SHM_TAIL}#${DEV_SHM_HEAD}${DEV_SHM_OPTS}${DEV_SHM_TAIL}#" /etc/fstab
fi

Restrict Dynamic Mounting and Unmounting of Filesystemsgroup

Linux includes a number of facilities for the automated addition and removal of filesystems on a running system. These facilities may be necessary in many environments, but this capability also carries some risk -- whether direct risk from allowing users to introduce arbitrary filesystems, or risk that software flaws in the automated mount facility itself could allow an attacker to compromise the system.

This command can be used to list the types of filesystems that are available to the currently executing kernel:

$ find /lib/modules/`uname -r`/kernel/fs -type f -name '*.ko'
If these filesystems are not required then they can be explicitly disabled in a configuratio file in /etc/modprobe.d.

contains 2 rules

Disable Modprobe Loading of USB Storage Driverrule

To prevent USB storage devices from being used, configure the kernel module loading system to prevent automatic loading of the USB storage driver. To configure the system to prevent the usb-storage kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d:

install usb-storage /bin/true
This will prevent the modprobe program from loading the usb-storage module, but will not prevent an administrator (or another program) from using the insmod program to load the module manually.

Rationale:

USB storage devices such as thumb drives can be used to introduce malicious software.

identifiers:  CCE-27016-5, DISA FSO RHEL-06-000503

references:  AC-19(a), AC-19(d), AC-19(e), 1250, 85

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
echo "install usb-storage /bin/true" > /etc/modprobe.d/usb-storage.conf
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Ensure kernel module 'usb-storage' is disabled
  lineinfile:
    create=yes
    dest="/etc/modprobe.d/{{item}}.conf"
    regexp="{{item}}"
    line="install {{item}} /bin/true"
  with_items:
    - usb-storage

Disable the Automounterrule

The autofs daemon mounts and unmounts filesystems, such as user home directories shared via NFS, on demand. In addition, autofs can be used to handle removable media, and the default configuration provides the cdrom device as /misc/cd. However, this method of providing access to removable media is not common, so autofs can almost always be disabled if NFS is not in use. Even if NFS is required, it may be possible to configure filesystem mounts statically by editing /etc/fstab rather than relying on the automounter.

The autofs service can be disabled with the following command:

$ sudo chkconfig autofs off

Rationale:

Disabling the automounter permits the administrator to statically control filesystem mounting through /etc/fstab.

identifiers:  CCE-26976-1, DISA FSO RHEL-06-000526

references:  AC-19(a), AC-19(d), AC-19(e), 366

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:disable

function service_command {

# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)

# Check sanity of the input
if [ $# -lt "2" ]
then
  echo "Usage: service_command 'enable/disable' 'service_name.service'"
  echo
  echo "To enable or disable xinetd services add \'xinetd=service_name\'"
  echo "as the last argument"
  echo "Aborting."
  exit 1
fi

# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
  service_util="/usr/bin/systemctl"
else
  service_util="/sbin/service"
  chkconfig_util="/sbin/chkconfig"
fi

# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
  service_state="enable"
  service_operation="start"
  chkconfig_state="on"
else
  service_state="disable"
  service_operation="stop"
  chkconfig_state="off"
fi

# If chkconfig_util is not empty, use chkconfig/service commands.
if ! [ "x$chkconfig_util" = x ] ; then
  $service_util $service $service_operation
  $chkconfig_util --level 0123456 $service $chkconfig_state
else
  $service_util $service_operation $service
  $service_util $service_state $service
fi

# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if ! [ "x$xinetd" = x ] ; then
  grep -qi disable /etc/xinetd.d/$xinetd && \

  if ! [ "$service_operation" != 'disable' ] ; then
    sed -i "s/disable.*/disable         = no/gI" /etc/xinetd.d/$xinetd
  else
    sed -i "s/disable.*/disable         = yes/gI" /etc/xinetd.d/$xinetd
  fi
fi

}

service_command disable autofs
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
- name: Disable service autofs
  service:
    name="{{item}}"
    enabled="no"
    state="stopped"
  with_items:
    - autofs

Verify Permissions on Important Files and Directoriesgroup

Permissions for many files on a system must be set restrictively to ensure sensitive information is properly protected. This section discusses important permission restrictions which can be verified to ensure that no harmful discrepancies have arisen.

contains 21 rules
contains 12 rules

Verify User Who Owns shadow Filerule

To properly set the owner of /etc/shadow, run the command:

$ sudo chown root /etc/shadow

Rationale:

The /etc/shadow file contains the list of local system accounts and stores password hashes. Protection of this file is critical for system security. Failure to give ownership of this file to root provides the designated owner with access to sensitive information which could weaken the system security posture.

identifiers:  CCE-26947-2, DISA FSO RHEL-06-000033

references:  AC-6, 225

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
chown root /etc/shadow

Verify Group Who Owns shadow Filerule

To properly set the group owner of /etc/shadow, run the command:

$ sudo chgrp root /etc/shadow

Rationale:

The /etc/shadow file stores password hashes. Protection of this file is critical for system security.

identifiers:  CCE-26967-0, DISA FSO RHEL-06-000034

references:  AC-6, 225

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
chgrp root /etc/shadow

Verify Permissions on shadow Filerule

To properly set the permissions of /etc/shadow, run the command:

$ sudo chmod 0000 /etc/shadow

Rationale:

The /etc/shadow file contains the list of local system accounts and stores password hashes. Protection of this file is critical for system security. Failure to give ownership of this file to root provides the designated owner with access to sensitive information which could weaken the system security posture.

identifiers:  CCE-26992-8, DISA FSO RHEL-06-000035

references:  AC-6, 225

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
chmod 0000 /etc/shadow
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:configure
- name: Ensure permission 0000 on /etc/shadow
  file:
    path="{{item}}"
    mode=0000
  with_items:
    - /etc/shadow

Verify User Who Owns group Filerule

To properly set the owner of /etc/group, run the command:

$ sudo chown root /etc/group

Rationale:

The /etc/group file contains information regarding groups that are configured on the system. Protection of this file is important for system security.

identifiers:  CCE-26822-7, DISA FSO RHEL-06-000042

references:  AC-6

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
chown root /etc/group

Verify Group Who Owns group Filerule

To properly set the group owner of /etc/group, run the command:

$ sudo chgrp root /etc/group

Rationale:

The /etc/group file contains information regarding groups that are configured on the system. Protection of this file is important for system security.

identifiers:  CCE-26930-8, DISA FSO RHEL-06-000043

references:  AC-6, 225

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
chgrp root /etc/group

Verify Permissions on group Filerule

To properly set the permissions of /etc/group, run the command:

$ sudo chmod 644 /etc/group

Rationale:

The /etc/group file contains information regarding groups that are configured on the system. Protection of this file is important for system security.

identifiers:  CCE-26954-8, DISA FSO RHEL-06-000044

references:  AC-6, 225

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
chmod 644 /etc/group

Verify User Who Owns gshadow Filerule

To properly set the owner of /etc/gshadow, run the command:

$ sudo chown root /etc/gshadow

Rationale:

The /etc/gshadow file contains group password hashes. Protection of this file is critical for system security.

identifiers:  CCE-27026-4, DISA FSO RHEL-06-000036

references:  AC-6, 366

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
chown root /etc/gshadow

Verify Group Who Owns gshadow Filerule

To properly set the group owner of /etc/gshadow, run the command:

$ sudo chgrp root /etc/gshadow

Rationale:

The /etc/gshadow file contains group password hashes. Protection of this file is critical for system security.

identifiers:  CCE-26975-3, DISA FSO RHEL-06-000037

references:  AC-6, 225

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
chgrp root /etc/gshadow

Verify Permissions on gshadow Filerule

To properly set the permissions of /etc/gshadow, run the command:

$ sudo chmod 0000 /etc/gshadow

Rationale:

The /etc/gshadow file contains group password hashes. Protection of this file is critical for system security.

identifiers:  CCE-26951-4, DISA FSO RHEL-06-000038

references:  AC-6, 225

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
chmod 0000 /etc/gshadow

Verify User Who Owns passwd Filerule

To properly set the owner of /etc/passwd, run the command:

$ sudo chown root /etc/passwd

Rationale:

The /etc/passwd file contains information about the users that are configured on the system. Protection of this file is critical for system security.

identifiers:  CCE-26953-0, DISA FSO RHEL-06-000039

references:  AC-6, 225

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
chown root /etc/passwd

Verify Group Who Owns passwd Filerule

To properly set the group owner of /etc/passwd, run the command:

$ sudo chgrp root /etc/passwd

Rationale:

The /etc/passwd file contains information about the users that are configured on the system. Protection of this file is critical for system security.

identifiers:  CCE-26856-5, DISA FSO RHEL-06-000040

references:  AC-6, 225

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
chgrp root /etc/passwd

Verify Permissions on passwd Filerule

To properly set the permissions of /etc/passwd, run the command:

$ sudo chmod 0644 /etc/passwd

Rationale:

If the /etc/passwd file is writable by a group-owner or the world the risk of its compromise is increased. The file contains the list of accounts on the system and associated information, and protection of this file is critical for system security.

identifiers:  CCE-26868-0, DISA FSO RHEL-06-000041

references:  AC-6, 225

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
chmod 0644 /etc/passwd

Verify File Permissions Within Some Important Directoriesgroup

Some directories contain files whose confidentiality or integrity is notably important and may also be susceptible to misconfiguration over time, particularly if unpackaged software is installed. As such, an argument exists to verify that files' permissions within these directories remain configured correctly and restrictively.

contains 4 rules

Verify that Shared Library Files Have Restrictive Permissionsrule

System-wide shared library files, which are linked to executables during process load time or run time, are stored in the following directories by default:

/lib
/lib64
/usr/lib
/usr/lib64
Kernel modules, which can be added to the kernel during runtime, are stored in /lib/modules. All files in these directories should not be group-writable or world-writable. If any file in these directories is found to be group-writable or world-writable, correct its permission with the following command:
$ sudo chmod go-w FILE

Rationale:

Files from shared library directories are loaded into the address space of processes (including privileged ones) or of the kernel itself at runtime. Restrictive permissions are necessary to protect the integrity of the system.

identifiers:  CCE-27381-3, DISA FSO RHEL-06-000045

references:  AC-6, 1499

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
DIRS="/lib /lib64 /usr/lib /usr/lib64"
for dirPath in $DIRS; do
	find $dirPath -perm /022 -type f -exec chmod go-w '{}' \;
done

Verify that Shared Library Files Have Root Ownershiprule

System-wide shared library files, which are linked to executables during process load time or run time, are stored in the following directories by default:

/lib
/lib64
/usr/lib
/usr/lib64
Kernel modules, which can be added to the kernel during runtime, are also stored in /lib/modules. All files in these directories should be owned by the root user. If the directory, or any file in these directories, is found to be owned by a user other than root correct its ownership with the following command:
$ sudo chown root FILE

Rationale:

Files from shared library directories are loaded into the address space of processes (including privileged ones) or of the kernel itself at runtime. Proper ownership is necessary to protect the integrity of the system.

identifiers:  CCE-27424-1, DISA FSO RHEL-06-000046

references:  AC-6, 1499

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
for LIBDIR in /usr/lib /usr/lib64 /lib /lib64
do
  if [ -d $LIBDIR ]
  then
    find -L $LIBDIR \! -user root -exec chown root {} \; 
  fi
done

Verify that System Executables Have Restrictive Permissionsrule

System executables are stored in the following directories by default:

/bin
/sbin
/usr/bin
/usr/libexec
/usr/local/bin
/usr/local/sbin
/usr/sbin
All files in these directories should not be group-writable or world-writable. If any file FILE in these directories is found to be group-writable or world-writable, correct its permission with the following command:
$ sudo chmod go-w FILE

Rationale:

System binaries are executed by privileged users, as well as system services, and restrictive permissions are necessary to ensure execution of these programs cannot be co-opted.

identifiers:  CCE-27289-8, DISA FSO RHEL-06-000047

references:  AC-6, 1499

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
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

Verify that System Executables Have Root Ownershiprule

System executables are stored in the following directories by default:

/bin
/sbin
/usr/bin
/usr/libexec
/usr/local/bin
/usr/local/sbin
/usr/sbin
All files in these directories should be owned by the root user. If any file FILE in these directories is found to be owned by a user other than root, correct its ownership with the following command:
$ sudo chown root FILE

Rationale:

System binaries are executed by privileged users as well as system services, and restrictive permissions are necessary to ensure that their execution of these programs cannot be co-opted.

identifiers:  CCE-27623-8, DISA FSO RHEL-06-000048

references:  AC-6, 1499

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
find /bin/ \
/usr/bin/ \
/usr/local/bin/ \
/sbin/ \
/usr/sbin/ \
/usr/local/sbin/ \
/usr/libexec \
\! -user root -execdir chown root {} \;

Verify that All World-Writable Directories Have Sticky Bits Setrule

When the so-called 'sticky bit' is set on a directory, only the owner of a given file may remove that file from the directory. Without the sticky bit, any user with write access to a directory may remove any file in the directory. Setting the sticky bit prevents users from removing each other's files. In cases where there is no reason for a directory to be world-writable, a better solution is to remove that permission rather than to set the sticky bit. However, if a directory is used by a particular application, consult that application's documentation instead of blindly changing modes.
To set the sticky bit on a world-writable directory DIR, run the following command:

$ sudo chmod +t DIR

Rationale:

Failing to set the sticky bit on public directories allows unauthorized users to delete files in the directory structure.

The only authorized public directories are those temporary directories supplied with the system, or those designed to be temporary file repositories. The setting is normally reserved for directories used by the system, by users for temporary file storage (such as /tmp), and for directories requiring global read/write access.

identifiers:  CCE-26840-9, DISA FSO RHEL-06-000336

references:  AC-6

Ensure No World-Writable Files Existrule

It is generally a good idea to remove global (other) write access to a file when it is discovered. However, check with documentation for specific applications before making changes. Also, monitor for recurring world-writable files, as these may be symptoms of a misconfigured application or user account.

Rationale:

Data in world-writable files can be modified by any user on the system. In almost all circumstances, files can be configured using a combination of user and group permissions to support whatever legitimate access is needed without the risk caused by world-writable files.

identifiers:  CCE-26910-0, DISA FSO RHEL-06-000282

references:  AC-6

Ensure All Files Are Owned by a Userrule

If any files are not owned by a user, then the cause of their lack of ownership should be investigated. Following this, the files should be deleted or assigned to an appropriate user.

Rationale:

Unowned files do not directly imply a security problem, but they are generally a sign that something is amiss. They may be caused by an intruder, by incorrect software installation or draft software removal, or by failure to remove all files belonging to a deleted account. The files should be repaired so they will not cause problems when accounts are created in the future, and the cause should be discovered and addressed.

identifiers:  CCE-27032-2

references:  AC-6, 224

Ensure All Files Are Owned by a Grouprule

If any files are not owned by a group, then the cause of their lack of group-ownership should be investigated. Following this, the files should be deleted or assigned to an appropriate group.

Rationale:

Unowned files do not directly imply a security problem, but they are generally a sign that something is amiss. They may be caused by an intruder, by incorrect software installation or draft software removal, or by failure to remove all files belonging to a deleted account. The files should be repaired so they will not cause problems when accounts are created in the future, and the cause should be discovered and addressed.

identifiers:  CCE-26872-2

references:  AC-6, 224

Ensure All World-Writable Directories Are Owned by a System Accountrule

All directories in local partitions which are world-writable should be owned by root or another system account. If any world-writable directories are not owned by a system account, this should be investigated. Following this, the files should be deleted or assigned to an appropriate group.

Rationale:

Allowing a user account to own a world-writable directory is undesirable because it allows the owner of that directory to remove or replace any files that may be placed in the directory by other users.

identifiers:  CCE-26642-9, DISA FSO RHEL-06-000337

references:  AC-6

Restrict Programs from Dangerous Execution Patternsgroup

The recommendations in this section are designed to ensure that the system's features to protect against potentially dangerous program execution are activated. These protections are applied at the system initialization or kernel level, and defend against certain types of badly-configured or compromised programs.

contains 4 rules

Daemon Umaskgroup

The umask is a per-process setting which limits the default permissions for creation of new files and directories. The system includes initialization scripts which set the default umask for system daemons.

contains 1 rule

Set Daemon Umaskrule

The file /etc/init.d/functions includes initialization parameters for most or all daemons started at boot time. The default umask of 022 prevents creation of group- or world-writable files. To set the default umask for daemons, edit the following line, inserting 022 or 027 for umask appropriately:

umask 027
Setting the umask to too restrictive a setting can cause serious errors at runtime. Many daemons on the system already individually restrict themselves to a umask of 077 in their own init scripts.

Rationale:

The umask influences the permissions assigned to files created by a process at run time. An unnecessarily permissive umask could result in files being created with insecure permissions.

identifiers:  CCE-27031-4, DISA FSO RHEL-06-000346

references:  AC-6

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

var_umask_for_daemons="027"

grep -q ^umask /etc/init.d/functions && \
  sed -i "s/umask.*/umask $var_umask_for_daemons/g" /etc/init.d/functions
if ! [ $? -eq 0 ]; then
    echo "umask $var_umask_for_daemons" >> /etc/init.d/functions
fi

Disable Core Dumpsgroup

A core dump file is the memory image of an executable program when it was terminated by the operating system due to errant behavior. In most cases, only software developers legitimately need to access these files. The core dump files may also contain sensitive information, or unnecessarily occupy large amounts of disk space.

Once a hard limit is set in /etc/security/limits.conf, a user cannot increase that limit within his or her own session. If access to core dumps is required, consider restricting them to only certain users or groups. See the limits.conf man page for more information.

The core dumps of setuid programs are further protected. The sysctl variable fs.suid_dumpable controls whether the kernel allows core dumps from these programs at all. The default value of 0 is recommended.

contains 1 rule

Disable Core Dumps for All Usersrule

To disable core dumps for all users, add the following line to /etc/security/limits.conf:

*     hard   core    0

Rationale:

A core dump includes a memory image taken at the time the operating system terminates an application. The memory image could contain sensitive data and is generally useful only for developers trying to debug problems.

identifiers:  CCE-27033-0, DISA FSO RHEL-06-000308

references:  SC-5

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
echo "*     hard   core    0" >> /etc/security/limits.conf

Enable ExecShieldgroup

ExecShield describes kernel features that provide protection against exploitation of memory corruption errors such as buffer overflows. These features include random placement of the stack and other memory regions, prevention of execution in memory that should only hold data, and special handling of text buffers. These protections are enabled by default and controlled through sysctl variables kernel.exec-shield and kernel.randomize_va_space.

contains 2 rules

Enable ExecShieldrule

To set the runtime status of the kernel.exec-shield kernel parameter, run the following command:

$ sudo sysctl -w kernel.exec-shield=1
If this is not the system's default value, add the following line to /etc/sysctl.conf:
kernel.exec-shield = 1

Rationale:

ExecShield uses the segmentation feature on all x86 systems to prevent execution in memory higher than a certain address. It writes an address as a limit in the code segment descriptor, to control where code can be executed, on a per-process basis. When the kernel places a process's memory regions such as the stack and heap higher than this address, the hardware prevents execution in that address range.

identifiers:  CCE-27007-4, DISA FSO RHEL-06-000079

references:  SC-39, 2530

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
#
# Set runtime for kernel.exec-shield
#
/sbin/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
#
if grep --silent ^kernel.exec-shield /etc/sysctl.conf ; then
	sed -i 's/^kernel.exec-shield.*/kernel.exec-shield = 1/g' /etc/sysctl.conf
else
	echo -e "\n# Set kernel.exec-shield to 1 per security requirements" >> /etc/sysctl.conf
	echo "kernel.exec-shield = 1" >> /etc/sysctl.conf
fi

Enable Randomized Layout of Virtual Address Spacerule

To set the runtime status of the kernel.randomize_va_space kernel parameter, run the following command:

$ sudo sysctl -w kernel.randomize_va_space=2
If this is not the system's default value, add the following line to /etc/sysctl.conf:
kernel.randomize_va_space = 2

Rationale:

Address space layout randomization (ASLR) makes it more difficult for an attacker to predict the location of attack code they have introduced into a process's address space during an attempt at exploitation. Additionally, ASLR makes it more difficult for an attacker to know the location of existing code in order to re-purpose it using return oriented programming (ROP) techniques.

identifiers:  CCE-26999-3, DISA FSO RHEL-06-000078

references:  SC-30(2)

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
#
# 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
#
if grep --silent ^kernel.randomize_va_space /etc/sysctl.conf ; then
	sed -i 's/^kernel.randomize_va_space.*/kernel.randomize_va_space = 2/g' /etc/sysctl.conf
else
	echo -e "\n# Set kernel.randomize_va_space to 2 per security requirements" >> /etc/sysctl.conf
	echo "kernel.randomize_va_space = 2" >> /etc/sysctl.conf
fi

SELinuxgroup

SELinux is a feature of the Linux kernel which can be used to guard against misconfigured or compromised programs. SELinux enforces the idea that programs should be limited in what files they can access and what actions they can take.

The default SELinux policy, as configured on Red Hat Enterprise Linux 6, has been sufficiently developed and debugged that it should be usable on almost any Red Hat machine with minimal configuration and a small amount of system administrator training. This policy prevents system services - including most of the common network-visible services such as mail servers, FTP servers, and DNS servers - from accessing files which those services have no valid reason to access. This action alone prevents a huge amount of possible damage from network attacks against services, from trojaned software, and so forth.

This guide recommends that SELinux be enabled using the default (targeted) policy on every Red Hat system, unless that system has unusual requirements which make a stronger policy appropriate.

contains 4 rules

Ensure SELinux Not Disabled in /etc/grub.confrule

SELinux can be disabled at boot time by an argument in /etc/grub.conf. Remove any instances of selinux=0 from the kernel arguments in that file to prevent SELinux from being disabled at boot.

Rationale:

Disabling a major host protection feature, such as SELinux, at boot time prevents it from confining system services at boot time. Further, it increases the chances that it will remain off during system operation.

identifiers:  CCE-26956-3, DISA FSO RHEL-06-000017

references:  AC-3, AC-3(3), AC-6, AU-9, 22, 32

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
sed -i --follow-symlinks "s/selinux=0//gI" /etc/grub.conf
sed -i --follow-symlinks "s/enforcing=0//gI" /etc/grub.conf

Ensure SELinux State is Enforcingrule

The SELinux state should be set to enforcing at system boot time. In the file /etc/selinux/config, add or correct the following line to configure the system to boot into enforcing mode:

SELINUX=enforcing

Rationale:

Setting the SELinux state to enforcing ensures SELinux is able to confine potentially compromised processes to the security policy, which is designed to prevent them from causing damage to the system or further elevating their privileges.

identifiers:  CCE-26969-6, DISA FSO RHEL-06-000020

references:  AC-3, AC-3(3), AC-4, AC-6, AU-9, 22, 32, 26

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

var_selinux_state="enforcing"

function replace_or_append {
  local config_file=$1
  local key=$2
  local value=$3
  local cce=$4
  local format=$5

  # Check sanity of the input
  if [ $# -lt "3" ]
  then
        echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
        echo
        echo "If symlinks need to be taken into account, add yes/no to the last argument"
        echo "to allow to 'follow_symlinks'."
        echo "Aborting."
        exit 1
  fi

  # Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
  # Otherwise, regular sed command will do.
  if test -L $config_file; then
    sed_command="sed -i --follow-symlinks"
  else
    sed_command="sed -i"
  fi

  # Test that the cce arg is not empty or does not equal $CCENUM.
  # If $CCENUM exists, it means that there is no CCE assigned.
  if ! [ "x$cce" = x ] && [ "$cce" != '$CCENUM' ]; then
    cce="CCE-${cce}"
  else
    cce="CCE"
  fi

  # Strip any search characters in the key arg so that the key can be replaced without
  # adding any search characters to the config file.
  stripped_key=$(sed "s/[\^=\$,;+]*//g" <<< $key)

  # If there is no print format specified in the last arg, use the default format.
  if ! [ "x$format" = x ] ; then
    printf -v formatted_output "$format" $stripped_key $value
  else
    formatted_output="$stripped_key = $value"
  fi

  # If the key exists, change it. Otherwise, add it to the config_file.
  if `grep -qi $key $config_file` ; then
    $sed_command "s/$key.*/$formatted_output/g" $config_file
  else
    echo -ne "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
    echo -ne "\n$formatted_output" >> $config_file
  fi

}

replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state 'CCE-26969-6' '%s=%s'

Configure SELinux Policyrule

The SELinux targeted policy is appropriate for general-purpose desktops and servers, as well as systems in many other roles. To configure the system to use this policy, add or correct the following line in /etc/selinux/config:

SELINUXTYPE=targeted
Other policies, such as mls, provide additional security labeling and greater confinement but are not compatible with many general-purpose use cases.

Rationale:

Setting the SELinux policy to targeted or a more specialized policy ensures the system will confine processes that are likely to be targeted for exploitation, such as network or system services. Note: During the development or debugging of SELinux modules, it is common to temporarily place non-production systems in permissive mode. In such temporary cases, SELinux policies should be developed, and once work is completed, the system should be reconfigured to targeted.

identifiers:  CCE-26875-5, DISA FSO RHEL-06-000023

references:  AC-3, AC-3(3), AC-4, AC-6, AU-9, 22, 32

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

var_selinux_policy_name="targeted"

function replace_or_append {
  local config_file=$1
  local key=$2
  local value=$3
  local cce=$4
  local format=$5

  # Check sanity of the input
  if [ $# -lt "3" ]
  then
        echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
        echo
        echo "If symlinks need to be taken into account, add yes/no to the last argument"
        echo "to allow to 'follow_symlinks'."
        echo "Aborting."
        exit 1
  fi

  # Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
  # Otherwise, regular sed command will do.
  if test -L $config_file; then
    sed_command="sed -i --follow-symlinks"
  else
    sed_command="sed -i"
  fi

  # Test that the cce arg is not empty or does not equal $CCENUM.
  # If $CCENUM exists, it means that there is no CCE assigned.
  if ! [ "x$cce" = x ] && [ "$cce" != '$CCENUM' ]; then
    cce="CCE-${cce}"
  else
    cce="CCE"
  fi

  # Strip any search characters in the key arg so that the key can be replaced without
  # adding any search characters to the config file.
  stripped_key=$(sed "s/[\^=\$,;+]*//g" <<< $key)

  # If there is no print format specified in the last arg, use the default format.
  if ! [ "x$format" = x ] ; then
    printf -v formatted_output "$format" $stripped_key $value
  else
    formatted_output="$stripped_key = $value"
  fi

  # If the key exists, change it. Otherwise, add it to the config_file.
  if `grep -qi $key $config_file` ; then
    $sed_command "s/$key.*/$formatted_output/g" $config_file
  else
    echo -ne "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
    echo -ne "\n$formatted_output" >> $config_file
  fi

}

replace_or_append '/etc/sysconfig/selinux' '^SELINUXTYPE=' $var_selinux_policy_name 'CCE-26875-5' '%s=%s'

Ensure No Device Files are Unknown to SELinuxrule

Device files, which are used for communication with important system resources, should be labeled with proper SELinux types. If any device files carry the SELinux type device_t, report the bug so that policy can be corrected. Supply information about what the device is and what programs use it.

Rationale:

If a device file carries the SELinux type device_t, then SELinux cannot properly restrict access to the device file.

identifiers:  CCE-26774-0, DISA FSO RHEL-06-000025

references:  AC-6, AU-9, CM-7, 22, 32

Account and Access Controlgroup

In traditional Unix security, if an attacker gains shell access to a certain login account, they can perform any action or access any file to which that account has access. Therefore, making it more difficult for unauthorized people to gain shell access to accounts, particularly to privileged accounts, is a necessary part of securing a system. This section introduces mechanisms for restricting access to accounts under Red Hat Enterprise Linux 6.

contains 48 rules

Protect Accounts by Restricting Password-Based Logingroup

Conventionally, Unix shell accounts are accessed by providing a username and password to a login program, which tests these values for correctness using the /etc/passwd and /etc/shadow files. Password-based login is vulnerable to guessing of weak passwords, and to sniffing and man-in-the-middle attacks against passwords entered over a network or at an insecure console. Therefore, mechanisms for accessing accounts by entering usernames and passwords should be restricted to those which are operationally necessary.

contains 15 rules

Restrict Root Loginsgroup

Direct root logins should be allowed only for emergency use. In normal situations, the administrator should access the system via a unique unprivileged account, and then use su or sudo to execute privileged commands. Discouraging administrators from accessing the root account directly ensures an audit trail in organizations with multiple administrators. Locking down the channels through which root can connect directly also reduces opportunities for password-guessing against the root account. The login program uses the file /etc/securetty to determine which interfaces should allow root logins. The virtual devices /dev/console and /dev/tty* represent the system consoles (accessible via the Ctrl-Alt-F1 through Ctrl-Alt-F6 keyboard sequences on a default installation). The default securetty file also contains /dev/vc/*. These are likely to be deprecated in most environments, but may be retained for compatibility. Root should also be prohibited from connecting via network protocols. Other sections of this document include guidance describing how to prevent root from logging in via SSH.

contains 4 rules

Restrict Serial Port Root Loginsrule

To restrict root logins on serial ports, ensure lines of this form do not appear in /etc/securetty:

ttyS0
ttyS1

Rationale:

Preventing direct root login to serial port interfaces helps ensure accountability for actions taken on the systems using the root account.

identifiers:  CCE-27047-0, DISA FSO RHEL-06-000028

references:  AC-6(2), 770

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
sed -i '/ttyS/d' /etc/securetty

Ensure that System Accounts Do Not Run a Shell Upon Loginrule

Some accounts are not associated with a human user of the system, and exist to perform some administrative function. Should an attacker be able to log into these accounts, they should not be granted access to a shell.

The login shell for each local account is stored in the last field of each line in /etc/passwd. System accounts are those user accounts with a user ID less than UID_MIN, where value of the UID_MIN directive is set in /etc/login.defs configuration file. In the default configuration UID_MIN is set to 500, thus system accounts are those user accounts with a user ID less than 500. The user ID is stored in the third field. If any system account SYSACCT (other than root) has a login shell, disable it with the command:

$ sudo usermod -s /sbin/nologin SYSACCT

warning  Do not perform the steps in this section on the root account. Doing so might cause the system to become inaccessible.
Rationale:

Ensuring shells are not given to system accounts upon login makes it more difficult for attackers to make use of system accounts.

identifiers:  CCE-26966-2

references:  AC-2, 178

Verify Only Root Has UID 0rule

If any account other than root has a UID of 0, this misconfiguration should be investigated and the accounts other than root should be removed or have their UID changed.

Rationale:

An account has root authority if it has a UID of 0. Multiple accounts with a UID of 0 afford more opportunity for potential intruders to guess a password for a privileged account. Proper configuration of sudo is recommended to afford multiple system administrators access to root privileges in an accountable manner.

identifiers:  CCE-26971-2, DISA FSO RHEL-06-000032

references:  AC-6, IA-2(1), 366

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
awk -F: '$3 == 0 && $1 != "root" { print $1 }' /etc/passwd | xargs passwd -l

Verify Proper Storage and Existence of Password Hashesgroup

By default, password hashes for local accounts are stored in the second field (colon-separated) in /etc/shadow. This file should be readable only by processes running with root credentials, preventing users from casually accessing others' password hashes and attempting to crack them. However, it remains possible to misconfigure the system and store password hashes in world-readable files such as /etc/passwd, or to even store passwords themselves in plaintext on the system. Using system-provided tools for password change/creation should allow administrators to avoid such misconfiguration.

contains 4 rules

Prevent Log In to Accounts With Empty Passwordrule

If an account is configured for password authentication but does not have an assigned password, it may be possible to log onto the account without authentication. Remove any instances of the nullok option in /etc/pam.d/system-auth to prevent logins with empty passwords.

Rationale:

If an account has an empty password, anyone could log in and run commands with the privileges of that account. Accounts with empty passwords should never be used in operational environments.

identifiers:  CCE-27038-9, DISA FSO RHEL-06-000030

references:  IA-5(b), IA-5(c), IA-5(1)(a)

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
sed --follow-symlinks -i 's/\<nullok\>//g' /etc/pam.d/system-auth

Verify All Account Password Hashes are Shadowedrule

If any password hashes are stored in /etc/passwd (in the second field, instead of an x), the cause of this misconfiguration should be investigated. The account should have its password reset and the hash should be properly stored, or the account should be deleted entirely.

Rationale:

The hashes for all user account passwords should be stored in the file /etc/shadow and never in /etc/passwd, which is readable by all users.

identifiers:  CCE-26476-2, DISA FSO RHEL-06-000031

references:  IA-5(h), 201

All GIDs referenced in /etc/passwd must be defined in /etc/grouprule

Add a group to the system for each GID referenced without a corresponding group.

Rationale:

Inconsistency in GIDs between /etc/passwd and /etc/group could lead to a user having unintended rights.

identifiers:  CCE-27379-7, DISA FSO RHEL-06-000294

references:  366

Verify No netrc Files Existrule

The .netrc files contain login information used to auto-login into FTP servers and reside in the user's home directory. These files may contain unencrypted passwords to remote FTP servers making them susceptible to access by unauthorized users and should not be used. Any .netrc files should be removed.

Rationale:

Unencrypted passwords for remote FTP servers may be stored in .netrc files. DoD policy requires passwords be encrypted in storage and not used in access scripts.

identifiers:  CCE-27225-2, DISA FSO RHEL-06-000347

references:  IA-5(h), AC-3, 196

Set Password Expiration Parametersgroup

The file /etc/login.defs controls several password-related settings. Programs such as passwd, su, and login consult /etc/login.defs to determine behavior with regard to password aging, expiration warnings, and length. See the man page login.defs(5) for more information.

Users should be forced to change their passwords, in order to decrease the utility of compromised passwords. However, the need to change passwords often should be balanced against the risk that users will reuse or write down passwords if forced to change them too often. Forcing password changes every 90-360 days, depending on the environment, is recommended. Set the appropriate value as PASS_MAX_DAYS and apply it to existing accounts with the -M flag.

The PASS_MIN_DAYS (-m) setting prevents password changes for 7 days after the first change, to discourage password cycling. If you use this setting, train users to contact an administrator for an emergency password change in case a new password becomes compromised. The PASS_WARN_AGE (-W) setting gives users 7 days of warnings at login time that their passwords are about to expire.

For example, for each existing human user USER, expiration parameters could be adjusted to a 180 day maximum password age, 7 day minimum password age, and 7 day warning period with the following command:

$ sudo chage -M 180 -m 7 -W 7 USER

contains 4 rules
contains 3 rules

Protect Accounts by Configuring PAMgroup

PAM, or Pluggable Authentication Modules, is a system which implements modular authentication for Linux programs. PAM provides a flexible and configurable architecture for authentication, and it should be configured to minimize exposure to unnecessary risk. This section contains guidance on how to accomplish that.

PAM is implemented as a set of shared objects which are loaded and invoked whenever an application wishes to authenticate a user. Typically, the application must be running as root in order to take advantage of PAM, because PAM's modules often need to be able to access sensitive stores of account information, such as /etc/shadow. Traditional privileged network listeners (e.g. sshd) or SUID programs (e.g. sudo) already meet this requirement. An SUID root application, userhelper, is provided so that programs which are not SUID or privileged themselves can still take advantage of PAM.

PAM looks in the directory /etc/pam.d for application-specific configuration information. For instance, if the program login attempts to authenticate a user, then PAM's libraries follow the instructions in the file /etc/pam.d/login to determine what actions should be taken.

One very important file in /etc/pam.d is /etc/pam.d/system-auth. This file, which is included by many other PAM configuration files, defines 'default' system authentication measures. Modifying this file is a good way to make far-reaching authentication changes, for instance when implementing a centralized authentication service.

warning  Be careful when making changes to PAM's configuration files. The syntax for these files is complex, and modifications can have unexpected consequences. The default configurations shipped with applications should be sufficient for most users.
warning  Running authconfig or system-config-authentication will re-write the PAM configuration files, destroying any manually made changes and replacing them with a series of system defaults. One reference to the configuration file syntax can be found at http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/sag-configuration-file.html.
contains 15 rules

Set Password Quality Requirementsgroup

The default pam_cracklib PAM module provides strength checking for passwords. It performs a number of checks, such as making sure passwords are not similar to dictionary words, are of at least a certain length, are not the previous password reversed, and are not simply a change of case from the previous password. It can also require passwords to be in certain character classes.

The man page pam_cracklib(8) provides information on the capabilities and configuration of each.

contains 7 rules

Set Password Quality Requirements, if using pam_cracklibgroup

The pam_cracklib PAM module can be configured to meet requirements for a variety of policies.

For example, to configure pam_cracklib to require at least one uppercase character, lowercase character, digit, and other (special) character, locate the following line in /etc/pam.d/system-auth:

password requisite pam_cracklib.so try_first_pass retry=3
and then alter it to read:
password required pam_cracklib.so try_first_pass retry=3 maxrepeat=3 minlen=14 dcredit=-1 ucredit=-1 ocredit=-1 lcredit=-1 difok=4
If no such line exists, add one as the first line of the password section in /etc/pam.d/system-auth. The arguments can be modified to ensure compliance with your organization's security policy. Discussion of each parameter follows.

warning  Note that the password quality requirements are not enforced for the root account for some reason.
contains 7 rules

Set Password Retry Prompts Permitted Per-Sessionrule

To configure the number of retry prompts that are permitted per-session:

Edit the pam_cracklib.so statement in /etc/pam.d/system-auth to show retry=3, or a lower value if site policy is more restrictive.

The DoD requirement is a maximum of 3 prompts per session.

Rationale:

Setting the password retry prompts that are permitted on a per-session basis to a low value requires some software, such as SSH, to re-connect. This can slow down and draw additional attention to some types of password-guessing attacks. Note that this is different from account lockout, which is provided by the pam_faillock module.

identifiers:  CCE-27123-9

references:  IA-5(c), 1092

Set Password to Maximum of Three Consecutive Repeating Charactersrule

The pam_cracklib module's maxrepeat parameter controls requirements for consecutive repeating characters. When set to a positive number, it will reject passwords which contain more than that number of consecutive characters. Add maxrepeat=3 after pam_cracklib.so to prevent a run of (3 + 1) or more identical characters:

password required pam_cracklib.so maxrepeat=3

Rationale:

Passwords with excessive repeating characters may be more vulnerable to password-guessing attacks.

identifiers:  CCE-27227-8, DISA FSO RHEL-06-000299

references:  IA-5(c), 366

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

var_password_pam_maxrepeat="3"

if grep -q "maxrepeat=" /etc/pam.d/system-auth; then   
	sed -i --follow-symlinks "s/\(maxrepeat *= *\).*/\1$var_password_pam_maxrepeat/" /etc/pam.d/system-auth
else
	sed -i --follow-symlinks "/pam_cracklib.so/ s/$/ maxrepeat=$var_password_pam_maxrepeat/" /etc/pam.d/system-auth
fi

Set Password Strength Minimum Digit Charactersrule

The pam_cracklib module's dcredit parameter controls requirements for usage of digits in a password. When set to a negative number, any password will be required to contain that many digits. When set to a positive number, pam_cracklib will grant +1 additional length credit for each digit. Add dcredit=-1 after pam_cracklib.so to require use of a digit in passwords.

Rationale:

Requiring digits makes password guessing attacks more difficult by ensuring a larger search space.

identifiers:  CCE-26374-9, DISA FSO RHEL-06-000056

references:  IA-5(b), IA-5(c), 194

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

var_password_pam_dcredit="-1"

if grep -q "dcredit=" /etc/pam.d/system-auth; then
	sed -i --follow-symlinks "s/\(dcredit *= *\).*/\1$var_password_pam_dcredit/" /etc/pam.d/system-auth
else
	sed -i --follow-symlinks "/pam_cracklib.so/ s/$/ dcredit=$var_password_pam_dcredit/" /etc/pam.d/system-auth
fi

Set Password Strength Minimum Uppercase Charactersrule

The pam_cracklib module's ucredit= parameter controls requirements for usage of uppercase letters in a password. When set to a negative number, any password will be required to contain that many uppercase characters. When set to a positive number, pam_cracklib will grant +1 additional length credit for each uppercase character. Add ucredit=-1 after pam_cracklib.so to require use of an upper case character in passwords.

Rationale:

Requiring a minimum number of uppercase characters makes password guessing attacks more difficult by ensuring a larger search space.

identifiers:  CCE-26601-5, DISA FSO RHEL-06-000057

references:  IA-5(b), IA-5(c), IA-5(1)(a), 192

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

var_password_pam_ucredit="-1"

if grep -q "ucredit=" /etc/pam.d/system-auth; then   
	sed -i --follow-symlinks "s/\(ucredit *= *\).*/\1$var_password_pam_ucredit/" /etc/pam.d/system-auth
else
	sed -i --follow-symlinks "/pam_cracklib.so/ s/$/ ucredit=$var_password_pam_ucredit/" /etc/pam.d/system-auth
fi

Set Password Strength Minimum Special Charactersrule

The pam_cracklib module's ocredit= parameter controls requirements for usage of special (or ``other'') characters in a password. When set to a negative number, any password will be required to contain that many special characters. When set to a positive number, pam_cracklib will grant +1 additional length credit for each special character. Add ocredit=-1 after pam_cracklib.so to require use of a special character in passwords.

Rationale:

Requiring a minimum number of special characters makes password guessing attacks more difficult by ensuring a larger search space.

identifiers:  CCE-26409-3, DISA FSO RHEL-06-000058

references:  IA-5(b), IA-5(c), IA-5(1)(a), 1619, 266

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

var_password_pam_ocredit="-1"

if grep -q "ocredit=" /etc/pam.d/system-auth; then   
	sed -i --follow-symlinks "s/\(ocredit *= *\).*/\1$var_password_pam_ocredit/" /etc/pam.d/system-auth
else
	sed -i --follow-symlinks "/pam_cracklib.so/ s/$/ ocredit=$var_password_pam_ocredit/" /etc/pam.d/system-auth
fi

Set Password Strength Minimum Lowercase Charactersrule

The pam_cracklib module's lcredit= parameter controls requirements for usage of lowercase letters in a password. When set to a negative number, any password will be required to contain that many lowercase characters. When set to a positive number, pam_cracklib will grant +1 additional length credit for each lowercase character. Add lcredit=-1 after pam_cracklib.so to require use of a lowercase character in passwords.

Rationale:

Requiring a minimum number of lowercase characters makes password guessing attacks more difficult by ensuring a larger search space.

identifiers:  CCE-26631-2, DISA FSO RHEL-06-000059

references:  IA-5(b), IA-5(c), IA-5(1)(a), 193

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

var_password_pam_lcredit="-1"

if grep -q "lcredit=" /etc/pam.d/system-auth; then   
	sed -i --follow-symlinks "s/\(lcredit *= *\).*/\1$var_password_pam_lcredit/" /etc/pam.d/system-auth
else
	sed -i --follow-symlinks "/pam_cracklib.so/ s/$/ lcredit=$var_password_pam_lcredit/" /etc/pam.d/system-auth
fi

Set Password Strength Minimum Different Charactersrule

The pam_cracklib module's difok parameter controls requirements for usage of different characters during a password change. Add difok=3 after pam_cracklib.so to require differing characters when changing passwords. The DoD requirement is 4.

Rationale:

Requiring a minimum number of different characters during password changes ensures that newly changed passwords should not resemble previously compromised ones. Note that passwords which are changed on compromised systems will still be compromised, however.

identifiers:  CCE-26615-5, DISA FSO RHEL-06-000060

references:  IA-5(b), IA-5(c), IA-5(1)(b), 195

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

var_password_pam_difok="3"

if grep -q "difok=" /etc/pam.d/system-auth; then   
	sed -i --follow-symlinks "s/\(difok *= *\).*/\1$var_password_pam_difok/" /etc/pam.d/system-auth
else
	sed -i --follow-symlinks "/pam_cracklib.so/ s/$/ difok=$var_password_pam_difok/" /etc/pam.d/system-auth
fi

Set Lockouts for Failed Password Attemptsgroup

The pam_faillock PAM module provides the capability to lock out user accounts after a number of failed login attempts. Its documentation is available in /usr/share/doc/pam-VERSION/txts/README.pam_faillock.

warning  Locking out user accounts presents the risk of a denial-of-service attack. The lockout policy must weigh whether the risk of such a denial-of-service attack outweighs the benefits of thwarting password guessing attacks.
contains 4 rules

Set Deny For Failed Password Attemptsrule

To configure the system to lock out accounts after a number of incorrect login attempts using pam_faillock.so, modify the content of both /etc/pam.d/system-auth and /etc/pam.d/password-auth as follows:

  • Add the following line immediately before the pam_unix.so statement in the AUTH section:
    auth required pam_faillock.so preauth silent deny=3 unlock_time=604800 fail_interval=900
  • Add the following line immediately after the pam_unix.so statement in the AUTH section:
    auth [default=die] pam_faillock.so authfail deny=3 unlock_time=604800 fail_interval=900
  • Add the following line immediately before the pam_unix.so statement in the ACCOUNT section:
    account required pam_faillock.so

Rationale:

Locking out user accounts after a number of incorrect attempts prevents direct password guessing attacks.

identifiers:  CCE-26844-1, DISA FSO RHEL-06-000061

references:  AC-7(a), 44

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

var_accounts_passwords_pam_faillock_deny="3"

AUTH_FILES[0]="/etc/pam.d/system-auth"
AUTH_FILES[1]="/etc/pam.d/password-auth"

for pamFile in "${AUTH_FILES[@]}"
do
	
	# pam_faillock.so already present?
	if grep -q "^auth.*pam_faillock.so.*" $pamFile; then

		# pam_faillock.so present, deny directive present?
		if grep -q "^auth.*[default=die].*pam_faillock.so.*authfail.*deny=" $pamFile; then

			# both pam_faillock.so & deny present, just correct deny directive value
			sed -i --follow-symlinks "s/\(^auth.*required.*pam_faillock.so.*preauth.*silent.*\)\(deny *= *\).*/\1\2$var_accounts_passwords_pam_faillock_deny/" $pamFile
			sed -i --follow-symlinks "s/\(^auth.*[default=die].*pam_faillock.so.*authfail.*\)\(deny *= *\).*/\1\2$var_accounts_passwords_pam_faillock_deny/" $pamFile

		# pam_faillock.so present, but deny directive not yet
		else

			# append correct deny value to appropriate places
			sed -i --follow-symlinks "/^auth.*required.*pam_faillock.so.*preauth.*silent.*/ s/$/ deny=$var_accounts_passwords_pam_faillock_deny/" $pamFile
			sed -i --follow-symlinks "/^auth.*[default=die].*pam_faillock.so.*authfail.*/ s/$/ deny=$var_accounts_passwords_pam_faillock_deny/" $pamFile
		fi

	# pam_faillock.so not present yet
	else

		# insert pam_faillock.so preauth & authfail rows with proper value of the 'deny' option
		sed -i --follow-symlinks "/^auth.*sufficient.*pam_unix.so.*/i auth        required      pam_faillock.so preauth silent deny=$var_accounts_passwords_pam_faillock_deny" $pamFile
		sed -i --follow-symlinks "/^auth.*sufficient.*pam_unix.so.*/a auth        [default=die] pam_faillock.so authfail deny=$var_accounts_passwords_pam_faillock_deny" $pamFile
		sed -i --follow-symlinks "/^account.*required.*pam_unix.so/i account     required      pam_faillock.so" $pamFile
	fi
done

Set Lockout Time For Failed Password Attemptsrule

To configure the system to lock out accounts after a number of incorrect login attempts and require an administrator to unlock the account using pam_faillock.so, modify the content of both /etc/pam.d/system-auth and /etc/pam.d/password-auth as follows:

  • Add the following line immediately before the pam_unix.so statement in the AUTH section:
    auth required pam_faillock.so preauth silent deny=3 unlock_time=604800 fail_interval=900
  • Add the following line immediately after the pam_unix.so statement in the AUTH section:
    auth [default=die] pam_faillock.so authfail deny=3 unlock_time=604800 fail_interval=900
  • Add the following line immediately before the pam_unix.so statement in the ACCOUNT section:
    account required pam_faillock.so

Rationale:

Locking out user accounts after a number of incorrect attempts prevents direct password guessing attacks. Ensuring that an administrator is involved in unlocking locked accounts draws appropriate attention to such situations.

identifiers:  CCE-27110-6, DISA FSO RHEL-06-000356

references:  AC-7(b), 47

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

var_accounts_passwords_pam_faillock_unlock_time="604800"

AUTH_FILES[0]="/etc/pam.d/system-auth"
AUTH_FILES[1]="/etc/pam.d/password-auth"

for pamFile in "${AUTH_FILES[@]}"
do
	
	# pam_faillock.so already present?
	if grep -q "^auth.*pam_faillock.so.*" $pamFile; then

		# pam_faillock.so present, unlock_time directive present?
		if grep -q "^auth.*[default=die].*pam_faillock.so.*authfail.*unlock_time=" $pamFile; then

			# both pam_faillock.so & unlock_time present, just correct unlock_time directive value
			sed -i --follow-symlinks "s/\(^auth.*required.*pam_faillock.so.*preauth.*silent.*\)\(unlock_time *= *\).*/\1\2$var_accounts_passwords_pam_faillock_unlock_time/" $pamFile
			sed -i --follow-symlinks "s/\(^auth.*[default=die].*pam_faillock.so.*authfail.*\)\(unlock_time *= *\).*/\1\2$var_accounts_passwords_pam_faillock_unlock_time/" $pamFile

		# pam_faillock.so present, but unlock_time directive not yet
		else

			# append correct unlock_time value to appropriate places
			sed -i --follow-symlinks "/^auth.*required.*pam_faillock.so.*preauth.*silent.*/ s/$/ unlock_time=$var_accounts_passwords_pam_faillock_unlock_time/" $pamFile
			sed -i --follow-symlinks "/^auth.*[default=die].*pam_faillock.so.*authfail.*/ s/$/ unlock_time=$var_accounts_passwords_pam_faillock_unlock_time/" $pamFile
		fi

	# pam_faillock.so not present yet
	else

		# insert pam_faillock.so preauth & authfail rows with proper value of the 'unlock_time' option
		sed -i --follow-symlinks "/^auth.*sufficient.*pam_unix.so.*/i auth        required      pam_faillock.so preauth silent unlock_time=$var_accounts_passwords_pam_faillock_unlock_time" $pamFile
		sed -i --follow-symlinks "/^auth.*sufficient.*pam_unix.so.*/a auth        [default=die] pam_faillock.so authfail unlock_time=$var_accounts_passwords_pam_faillock_unlock_time" $pamFile
		sed -i --follow-symlinks "/^account.*required.*pam_unix.so/i account     required      pam_faillock.so" $pamFile
	fi
done

Set Interval For Counting Failed Password Attemptsrule

Utilizing pam_faillock.so, the fail_interval directive configures the system to lock out accounts after a number of incorrect login attempts. Modify the content of both /etc/pam.d/system-auth and /etc/pam.d/password-auth as follows:

  • Add the following line immediately before the pam_unix.so statement in the AUTH section:
    auth required pam_faillock.so preauth silent deny=3 unlock_time=604800 fail_interval=900
  • Add the following line immediately after the pam_unix.so statement in the AUTH section:
    auth [default=die] pam_faillock.so authfail deny=3 unlock_time=604800 fail_interval=900
  • Add the following line immediately before the pam_unix.so statement in the ACCOUNT section:
    account required pam_faillock.so

Rationale:

Locking out user accounts after a number of incorrect attempts within a specific period of time prevents direct password guessing attacks.

identifiers:  CCE-27215-3, DISA FSO RHEL-06-000357

references:  AC-7(a), 1452

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

var_accounts_passwords_pam_faillock_fail_interval="900"

AUTH_FILES[0]="/etc/pam.d/system-auth"
AUTH_FILES[1]="/etc/pam.d/password-auth"

for pamFile in "${AUTH_FILES[@]}"
do
	
	# pam_faillock.so already present?
	if grep -q "^auth.*pam_faillock.so.*" $pamFile; then

		# pam_faillock.so present, 'fail_interval' directive present?
		if grep -q "^auth.*[default=die].*pam_faillock.so.*authfail.*fail_interval=" $pamFile; then

			# both pam_faillock.so & 'fail_interval' present, just correct 'fail_interval' directive value
			sed -i --follow-symlinks "s/\(^auth.*required.*pam_faillock.so.*preauth.*silent.*\)\(fail_interval *= *\).*/\1\2$var_accounts_passwords_pam_faillock_fail_interval/" $pamFile
			sed -i --follow-symlinks "s/\(^auth.*[default=die].*pam_faillock.so.*authfail.*\)\(fail_interval *= *\).*/\1\2$var_accounts_passwords_pam_faillock_fail_interval/" $pamFile

		# pam_faillock.so present, but 'fail_interval' directive not yet
		else

			# append correct 'fail_interval' value to appropriate places
			sed -i --follow-symlinks "/^auth.*required.*pam_faillock.so.*preauth.*silent.*/ s/$/ fail_interval=$var_accounts_passwords_pam_faillock_fail_interval/" $pamFile
			sed -i --follow-symlinks "/^auth.*[default=die].*pam_faillock.so.*authfail.*/ s/$/ fail_interval=$var_accounts_passwords_pam_faillock_fail_interval/" $pamFile
		fi

	# pam_faillock.so not present yet
	else

		# insert pam_faillock.so preauth & authfail rows with proper value of the 'fail_interval' option
		sed -i --follow-symlinks "/^auth.*sufficient.*pam_unix.so.*/i auth        required      pam_faillock.so preauth silent fail_interval=$var_accounts_passwords_pam_faillock_fail_interval" $pamFile
		sed -i --follow-symlinks "/^auth.*sufficient.*pam_unix.so.*/a auth        [default=die] pam_faillock.so authfail fail_interval=$var_accounts_passwords_pam_faillock_fail_interval" $pamFile
		sed -i --follow-symlinks "/^account.*required.*pam_unix.so/i account     required      pam_faillock.so" $pamFile
	fi
done

Limit Password Reuserule

Do not allow users to reuse recent passwords. This can be accomplished by using the remember option for the pam_unix or pam_pwhistory PAM modules. In the file /etc/pam.d/system-auth, append remember=5 to the line which refers to the pam_unix.so or pam_pwhistory.somodule, as shown below:

  • for the pam_unix.so case:
    password sufficient pam_unix.so existing_options remember=5
  • for the pam_pwhistory.so case:
    password requisite pam_pwhistory.so existing_options remember=5
The DoD STIG requirement is 5 passwords.

Rationale:

Preventing re-use of previous passwords helps ensure that a compromised password is not re-used by a user.

identifiers:  CCE-26741-9, DISA FSO RHEL-06-000274

references:  IA-5(f), IA-5(1)(e), 200

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

var_password_pam_unix_remember="5"

if grep -q "remember=" /etc/pam.d/system-auth; then   
	sed -i --follow-symlinks "s/\(^password.*sufficient.*pam_unix.so.*\)\(\(remember *= *\)[^ $]*\)/\1remember=$var_password_pam_unix_remember/" /etc/pam.d/system-auth
else
	sed -i --follow-symlinks "/^password[[:space:]]\+sufficient[[:space:]]\+pam_unix.so/ s/$/ remember=$var_password_pam_unix_remember/" /etc/pam.d/system-auth
fi

Set Password Hashing Algorithmgroup

The system's default algorithm for storing password hashes in /etc/shadow is SHA-512. This can be configured in several locations.

contains 3 rules

Set Password Hashing Algorithm in /etc/pam.d/system-authrule

In /etc/pam.d/system-auth, the password section of the file controls which PAM modules execute during a password change. Set the pam_unix.so module in the password section to include the argument sha512, as shown below:

password    sufficient    pam_unix.so sha512 other arguments...
This will help ensure when local users change their passwords, hashes for the new passwords will be generated using the SHA-512 algorithm. This is the default.

Rationale:

Using a stronger hashing algorithm makes password cracking attacks more difficult.

identifiers:  CCE-26303-8, DISA FSO RHEL-06-000062

references:  IA-5(b), IA-5(c), IA-5(1)(c), IA-7, 803

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
if ! grep -q "^password.*sufficient.*pam_unix.so.*sha512" /etc/pam.d/system-auth; then   
	sed -i --follow-symlinks "/^password.*sufficient.*pam_unix.so/ s/$/ sha512/" /etc/pam.d/system-auth
fi

Set Password Hashing Algorithm in /etc/login.defsrule

In /etc/login.defs, add or correct the following line to ensure the system will use SHA-512 as the hashing algorithm:

ENCRYPT_METHOD SHA512

Rationale:

Using a stronger hashing algorithm makes password cracking attacks more difficult.

identifiers:  CCE-27228-6, DISA FSO RHEL-06-000063

references:  IA-5(b), IA-5(c), IA-5(1)(c), IA-7, 803

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
if grep --silent ^ENCRYPT_METHOD /etc/login.defs ; then
	sed -i 's/^ENCRYPT_METHOD.*/ENCRYPT_METHOD SHA512/g' /etc/login.defs
else
	echo "" >> /etc/login.defs
	echo "ENCRYPT_METHOD SHA512" >> /etc/login.defs
fi

Set Password Hashing Algorithm in /etc/libuser.confrule

In /etc/libuser.conf, add or correct the following line in its [defaults] section to ensure the system will use the SHA-512 algorithm for password hashing:

crypt_style = sha512

Rationale:

Using a stronger hashing algorithm makes password cracking attacks more difficult.

identifiers:  CCE-27229-4, DISA FSO RHEL-06-000064

references:  IA-5(b), IA-5(c), IA-5(1)(c), IA-7, 803

Secure Session Configuration Files for Login Accountsgroup

When a user logs into a Unix account, the system configures the user's session by reading a number of files. Many of these files are located in the user's home directory, and may have weak permissions as a result of user error or misconfiguration. If an attacker can modify or even read certain types of account configuration information, they can often gain full access to the affected user's account. Therefore, it is important to test and correct configuration file permissions for interactive accounts, particularly those of privileged users such as root or system administrators.

contains 6 rules

Ensure that Users Have Sensible Umask Valuesgroup

The umask setting controls the default permissions for the creation of new files. With a default umask setting of 077, files and directories created by users will not be readable by any other user on the system. Users who wish to make specific files group- or world-readable can accomplish this by using the chmod command. Additionally, users can make all their files readable to their group by default by setting a umask of 027 in their shell configuration files. If default per-user groups exist (that is, if every user has a default group whose name is the same as that user's username and whose only member is the user), then it may even be safe for users to select a umask of 007, making it very easy to intentionally share files with groups of which the user is a member.

contains 4 rules

Ensure the Default Bash Umask is Set Correctlyrule

To ensure the default umask for users of the Bash shell is set properly, add or correct the umask setting in /etc/bashrc to read as follows:

umask 077

Rationale:

The umask value influences the permissions assigned to files when they are created. A misconfigured umask value could result in files with excessive permissions that can be read or written to by unauthorized users.

identifiers:  CCE-26917-5, DISA FSO RHEL-06-000342

references:  SA-8, 366

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

var_accounts_user_umask="077"

grep -q umask /etc/bashrc && \
  sed -i "s/umask.*/umask $var_accounts_user_umask/g" /etc/bashrc
if ! [ $? -eq 0 ]; then
    echo "umask $var_accounts_user_umask" >> /etc/bashrc
fi

Ensure the Default C Shell Umask is Set Correctlyrule

To ensure the default umask for users of the C shell is set properly, add or correct the umask setting in /etc/csh.cshrc to read as follows:

umask 077

Rationale:

The umask value influences the permissions assigned to files when they are created. A misconfigured umask value could result in files with excessive permissions that can be read or written to by unauthorized users.

identifiers:  CCE-27034-8, DISA FSO RHEL-06-000343

references:  SA-8, 366

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

var_accounts_user_umask="077"

grep -q umask /etc/csh.cshrc && \
  sed -i "s/umask.*/umask $var_accounts_user_umask/g" /etc/csh.cshrc
if ! [ $? -eq 0 ]; then
    echo "umask $var_accounts_user_umask" >> /etc/csh.cshrc
fi

Ensure the Default Umask is Set Correctly in /etc/profilerule

To ensure the default umask controlled by /etc/profile is set properly, add or correct the umask setting in /etc/profile to read as follows:

umask 077

Rationale:

The umask value influences the permissions assigned to files when they are created. A misconfigured umask value could result in files with excessive permissions that can be read or written to by unauthorized users.

identifiers:  CCE-26669-2, DISA FSO RHEL-06-000344

references:  SA-8, 366

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

var_accounts_user_umask="077"

grep -q umask /etc/profile && \
  sed -i "s/umask.*/umask $var_accounts_user_umask/g" /etc/profile
if ! [ $? -eq 0 ]; then
    echo "umask $var_accounts_user_umask" >> /etc/profile
fi

Set Interactive Session Timeoutrule

Terminating an idle session within a short time period reduces the window of opportunity for unauthorized personnel to take control of a management session enabled on the console or console port that has been left unattended.

Rationale:

identifiers:  CCE-27333-4

Protect Physical Console Accessgroup

It is impossible to fully protect a system from an attacker with physical access, so securing the space in which the system is located should be considered a necessary step. However, there are some steps which, if taken, make it more difficult for an attacker to quickly or undetectably modify a system from its console.

contains 9 rules

Set Boot Loader Passwordgroup

During the boot process, the boot loader is responsible for starting the execution of the kernel and passing options to it. The boot loader allows for the selection of different kernels - possibly on different partitions or media. The default Red Hat Enterprise Linux boot loader for x86 systems is called GRUB. Options it can pass to the kernel include single-user mode, which provides root access without any authentication, and the ability to disable SELinux. To prevent local users from modifying the boot parameters and endangering security, protect the boot loader configuration with a password and ensure its configuration file's permissions are set properly.

contains 4 rules

Verify /etc/grub.conf User Ownershiprule

The file /etc/grub.conf should be owned by the root user to prevent destruction or modification of the file. To properly set the owner of /etc/grub.conf, run the command:

$ sudo chown root /etc/grub.conf

Rationale:

Only root should be able to modify important boot parameters.

identifiers:  CCE-26995-1, DISA FSO RHEL-06-000065

references:  AC-6(7), 225

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
chown root /etc/grub.conf

Verify /etc/grub.conf Group Ownershiprule

The file /etc/grub.conf should be group-owned by the root group to prevent destruction or modification of the file. To properly set the group owner of /etc/grub.conf, run the command:

$ sudo chgrp root /etc/grub.conf

Rationale:

The root group is a highly-privileged group. Furthermore, the group-owner of this file should not have any access privileges anyway.

identifiers:  CCE-27022-3, DISA FSO RHEL-06-000066

references:  AC-6(7), 225

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
chgrp root /etc/grub.conf

Verify /boot/grub/grub.conf Permissionsrule

File permissions for /boot/grub/grub.conf should be set to 600, which is the default. To properly set the permissions of /boot/grub/grub.conf, run the command:

$ sudo chmod 600 /boot/grub/grub.conf

Rationale:

Proper permissions ensure that only the root user can modify important boot parameters.

identifiers:  CCE-26949-8, DISA FSO RHEL-06-000067

references:  AC-6(7), 225

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
chmod 600 /boot/grub/grub.conf

Set Boot Loader Passwordrule

The grub boot loader should have password protection enabled to protect boot-time settings. To do so, select a password and then generate a hash from it by running the following command:

$ grub-crypt --sha-512
When prompted to enter a password, insert the following line into /etc/grub.conf immediately after the header comments. (Use the output from grub-crypt as the value of password-hash):
password --encrypted password-hash
NOTE: To meet FISMA Moderate, the bootloader password MUST differ from the root password.

Rationale:

Password protection on the boot loader configuration ensures users with physical access cannot trivially alter important bootloader settings. These include which kernel to use, and whether to enter single-user mode.

identifiers:  CCE-26911-8, DISA FSO RHEL-06-000068

references:  IA-2(1), IA-5(e) AC-3, 213

Configure Screen Lockinggroup

When a user must temporarily leave an account logged-in, screen locking should be employed to prevent passersby from abusing the account. User education and training is particularly important for screen locking to be effective, and policies can be implemented to reinforce this.

Automatic screen locking is only meant as a safeguard for those cases where a user forgot to lock the screen.

contains 2 rules

Configure Console Screen Lockinggroup

A console screen locking mechanism is provided in the screen package, which is not installed by default.

contains 1 rule

Install the screen Packagerule

To enable console screen locking, install the screen package:

$ sudo yum install screen
Instruct users to begin new terminal sessions with the following command:
$ screen
The console can now be locked with the following key combination:
ctrl+a x

Rationale:

Installing screen ensures a console locking capability is available for users who may need to suspend console logins.

identifiers:  CCE-26940-7, DISA FSO RHEL-06-000071

references:  58

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

function package_command {

# Load function arguments into local variables
local package_operation=$1
local package=$2

# Check sanity of the input
if [ $# -ne "2" ]
then
  echo "Usage: package_command 'install/uninstall' 'rpm_package_name"
  echo "Aborting."
  exit 1
fi

# If dnf is installed, use dnf; otherwise, use yum
if [ -f "/usr/bin/dnf" ] ; then
  install_util="/usr/bin/dnf"
else
  install_util="/usr/bin/yum"
fi

if [ "$package_operation" != 'remove' ] ; then
  # If the rpm is not installed, install the rpm
  if ! /bin/rpm -q --quiet $package; then
    $install_util -y $package_operation $package
  fi
else
  # If the rpm is installed, uninstall the rpm
  if /bin/rpm -q --quiet $package; then
    $install_util -y $package_operation $package
  fi
fi

}

package_command install screen
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Ensure screen is installed
  package:
    name="{{item}}"
    state=present
  with_items:
    - screen
Remediation Puppet snippet:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_screen

class install_screen {
  package { 'screen':
    ensure => 'installed',
  }
}
Remediation script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=screen
contains 1 rule

Enable Smart Card Loginrule

To enable smart card authentication, consult the documentation at:

For guidance on enabling SSH to authenticate against a Common Access Card (CAC), consult documentation at:

Rationale:

Smart card login provides two-factor authentication stronger than that provided by a username and password combination. Smart cards leverage PKI (public key infrastructure) in order to provide and verify credentials.

identifiers:  CCE-27440-7, DISA FSO RHEL-06-000349

references:  765, 766, 767, 768, 771, 772, 884

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


# Install required packages
yum -y install esc
yum -y install pam_pkcs11

# Enable pcscd service

function service_command {

# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)

# Check sanity of the input
if [ $# -lt "2" ]
then
  echo "Usage: service_command 'enable/disable' 'service_name.service'"
  echo
  echo "To enable or disable xinetd services add \'xinetd=service_name\'"
  echo "as the last argument"
  echo "Aborting."
  exit 1
fi

# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
  service_util="/usr/bin/systemctl"
else
  service_util="/sbin/service"
  chkconfig_util="/sbin/chkconfig"
fi

# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
  service_state="enable"
  service_operation="start"
  chkconfig_state="on"
else
  service_state="disable"
  service_operation="stop"
  chkconfig_state="off"
fi

# If chkconfig_util is not empty, use chkconfig/service commands.
if ! [ "x$chkconfig_util" = x ] ; then
  $service_util $service $service_operation
  $chkconfig_util --level 0123456 $service $chkconfig_state
else
  $service_util $service_operation $service
  $service_util $service_state $service
fi

# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if ! [ "x$xinetd" = x ] ; then
  grep -qi disable /etc/xinetd.d/$xinetd && \

  if ! [ "$service_operation" != 'disable' ] ; then
    sed -i "s/disable.*/disable         = no/gI" /etc/xinetd.d/$xinetd
  else
    sed -i "s/disable.*/disable         = yes/gI" /etc/xinetd.d/$xinetd
  fi
fi

}

service_command enable pcscd

# Configure the expected /etc/pam.d/system-auth{,-ac} settings directly
#
# The code below will configure system authentication in the way smart card
# logins will be enabled, but also user login(s) via other method to be allowed
#
# NOTE: In contrast to Red Hat Enterprise Linux 7 version of this remediation
#       script (based on the testing) it does NOT seem to be possible to use
#       the 'authconfig' command to perform the remediation for us. Because:
#
#       * calling '/usr/sbin/authconfig --enablesmartcard --update'
#	  does not update all the necessary files, while
#
#	* calling '/usr/sbin/authconfig --enablesmartcard --updateall'
#	  discards the necessary changes on /etc/pam_pkcs11/pam_pkcs11.conf
#	  performed subsequently below
#
#	Therefore we configure /etc/pam.d/system-auth{,-ac} settings directly.
#

# Define system-auth config location
SYSTEM_AUTH_CONF="/etc/pam.d/system-auth"
# Define expected 'pam_env.so' row in $SYSTEM_AUTH_CONF
PAM_ENV_SO="auth.*required.*pam_env.so"

# Define 'pam_succeed_if.so' row to be appended past $PAM_ENV_SO row into $SYSTEM_AUTH_CONF
SYSTEM_AUTH_PAM_SUCCEED="\
auth        \[success=1 default=ignore\] pam_succeed_if.so service notin \
login:gdm:xdm:kdm:xscreensaver:gnome-screensaver:kscreensaver quiet use_uid"
# Define 'pam_pkcs11.so' row to be appended past $SYSTEM_AUTH_PAM_SUCCEED
# row into SYSTEM_AUTH_CONF file
SYSTEM_AUTH_PAM_PKCS11="\
auth        \[success=done authinfo_unavail=ignore ignore=ignore default=die\] \
pam_pkcs11.so card_only"

# Define smartcard-auth config location
SMARTCARD_AUTH_CONF="/etc/pam.d/smartcard-auth"
# Define 'pam_pkcs11.so' auth section to be appended past $PAM_ENV_SO into $SMARTCARD_AUTH_CONF
SMARTCARD_AUTH_SECTION="\
auth        [success=done ignore=ignore default=die] pam_pkcs11.so wait_for_card card_only"
# Define expected 'pam_permit.so' row in $SMARTCARD_AUTH_CONF
PAM_PERMIT_SO="account.*required.*pam_permit.so"
# Define 'pam_pkcs11.so' password section
SMARTCARD_PASSWORD_SECTION="\
password    required      pam_pkcs11.so"

# First Correct the SYSTEM_AUTH_CONF configuration
if ! grep -q 'pam_pkcs11.so' "$SYSTEM_AUTH_CONF"
then
	# Append (expected) pam_succeed_if.so row past the pam_env.so into SYSTEM_AUTH_CONF file
	sed -i --follow-symlinks -e '/^'"$PAM_ENV_SO"'/a '"$SYSTEM_AUTH_PAM_SUCCEED" "$SYSTEM_AUTH_CONF"
	# Append (expected) pam_pkcs11.so row past the pam_succeed_if.so into SYSTEM_AUTH_CONF file
	sed -i --follow-symlinks -e '/^'"$SYSTEM_AUTH_PAM_SUCCEED"'/a '"$SYSTEM_AUTH_PAM_PKCS11" "$SYSTEM_AUTH_CONF"
fi

# Then also correct the SMARTCARD_AUTH_CONF
if ! grep -q 'pam_pkcs11.so' "$SMARTCARD_AUTH_CONF"
then
	# Append (expected) SMARTCARD_AUTH_SECTION row past the pam_env.so into SMARTCARD_AUTH_CONF file
	sed -i --follow-symlinks -e '/^'"$PAM_ENV_SO"'/a '"$SMARTCARD_AUTH_SECTION" "$SMARTCARD_AUTH_CONF"
	# Append (expected) SMARTCARD_PASSWORD_SECTION row past the pam_permit.so into SMARTCARD_AUTH_CONF file
	sed -i --follow-symlinks -e '/^'"$PAM_PERMIT_SO"'/a '"$SMARTCARD_PASSWORD_SECTION" "$SMARTCARD_AUTH_CONF"
fi

# Perform /etc/pam_pkcs11/pam_pkcs11.conf settings below
# Define selected constants for later reuse
SP="[:space:]"
PAM_PKCS11_CONF="/etc/pam_pkcs11/pam_pkcs11.conf"

# Ensure OCSP is turned on in $PAM_PKCS11_CONF
# 1) First replace any occurrence of 'none' value of 'cert_policy' key setting with the correct configuration
# On Red Hat Enterprise Linux 6 a space isn't required between 'cert_policy' key and value assignment !!!
sed -i "s/^[$SP]*cert_policy=none;/    cert_policy=ca, ocsp_on, signature;/g" "$PAM_PKCS11_CONF"

# 2) Then append 'ocsp_on' value setting to each 'cert_policy' key in $PAM_PKCS11_CONF configuration line,
# which does not contain it yet
# On Red Hat Enterprise Linux 6 a space isn't required between 'cert_policy' key and value assignment !!!
sed -i "/ocsp_on/! s/^[$SP]*cert_policy=\(.*\);/    cert_policy=\1, ocsp_on;/" "$PAM_PKCS11_CONF"

Require Authentication for Single User Moderule

Single-user mode is intended as a system recovery method, providing a single user root access to the system by providing a boot option at startup. By default, no authentication is performed if single-user mode is selected.

To require entry of the root password even if the system is started in single-user mode, add or correct the following line in the file /etc/sysconfig/init:

SINGLE=/sbin/sulogin

Rationale:

This prevents attackers with physical access from trivially bypassing security on the machine and gaining root access. Such accesses are further prevented by configuring the bootloader password.

identifiers:  CCE-27040-5, DISA FSO RHEL-06-000069

references:  IA-2(1), AC-3, 213

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
grep -q ^SINGLE /etc/sysconfig/init && \
  sed -i "s/SINGLE.*/SINGLE=\/sbin\/sulogin/g" /etc/sysconfig/init
if ! [ $? -eq 0 ]; then
    echo "SINGLE=/sbin/sulogin" >> /etc/sysconfig/init
fi

Disable Ctrl-Alt-Del Reboot Activationrule

By default, the system includes the following line in /etc/init/control-alt-delete.conf to reboot the system when the Ctrl-Alt-Del key sequence is pressed:

exec /sbin/shutdown -r now "Control-Alt-Delete pressed"

To configure the system to log a message instead of rebooting the system, alter that line to read as follows:
exec /usr/bin/logger -p security.info "Control-Alt-Delete pressed"

warning  Disabling the Ctrl-Alt-Del key sequence in /etc/init/control-alt-delete.conf DOES NOT disable the Ctrl-Alt-Del key sequence if running in runlevel 6 (e.g. in GNOME, KDE, etc.)! The Ctrl-Alt-Del key sequence will only be disabled if running in the non-graphical runlevel 3.
Rationale:

A locally logged-in user who presses Ctrl-Alt-Del, when at the console, can reboot the system. If accidentally pressed, as could happen in the case of mixed OS environment, this can create the risk of short-term loss of availability of systems due to unintentional reboot. In the GNOME graphical environment, risk of unintentional reboot from the Ctrl-Alt-Del sequence is reduced because the user will be prompted before any action is taken. NOTE: When updating the initscripts package on a Red Hat Enterprise Linux 6 system, custom changes to /etc/init/control-alt-delete.conf may be overwritten. Refer to https://access.redhat.com/site/solutions/70464 for additional information.

identifiers:  CCE-27567-7, DISA FSO RHEL-06-000286

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# If system does not contain control-alt-delete.override,
if [ ! -f /etc/init/control-alt-delete.override ]; then

	# but does have control-alt-delete.conf file,
	if [ -f /etc/init/control-alt-delete.conf ]; then

		# then copy .conf to .override to maintain persistency
		cp /etc/init/control-alt-delete.conf /etc/init/control-alt-delete.override
	fi
fi
 
sed -i 's,^exec.*$,exec /usr/bin/logger -p authpriv.notice -t init "Ctrl-Alt-Del was pressed and ignored",' /etc/init/control-alt-delete.override

Disable Interactive Bootrule

To disable the ability for users to perform interactive startups, perform both of the following:

  1. Edit the file /etc/sysconfig/init. Add or correct the line:
    PROMPT=no
  2. Inspect the kernel boot arguments (which follow the word kernel) in /etc/grub.conf and ensure the confirm argument is not present.
Both the PROMPT option of the /etc/sysconfig/init file and the confirm kernel boot argument of the /etc/grub.conf file allow the console user to perform an interactive system startup, in which it is possible to select the set of services which are started on boot.

Rationale:

Using interactive boot, the console user could disable auditing, firewalls, or other services, weakening system security.

identifiers:  CCE-27043-9, DISA FSO RHEL-06-000070

references:  SC-2, AC-3, 213

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

# Ensure value of PROMPT key in /etc/sysconfig/init is set to 'no'
grep -q ^PROMPT /etc/sysconfig/init && \
  sed -i "s/PROMPT.*/PROMPT=no/g" /etc/sysconfig/init
if ! [ $? -eq 0 ]; then
    echo "PROMPT=no" >> /etc/sysconfig/init
fi

# Ensure 'confirm' kernel boot argument is not present in some of
# kernel lines in /etc/grub.conf
sed -i --follow-symlinks "s/confirm//gI" /etc/grub.conf

Warning Banners for System Accessesgroup

Each system should expose as little information about itself as possible.

System banners, which are typically displayed just before a login prompt, give out information about the service or the host's operating system. This might include the distribution name and the system kernel version, and the particular version of a network service. This information can assist intruders in gaining access to the system as it can reveal whether the system is running vulnerable software. Most network services can be configured to limit what information is displayed.

Many organizations implement security policies that require a system banner provide notice of the system's ownership, provide warning to unauthorized users, and remind authorized users of their consent to monitoring.

contains 3 rules
contains 2 rules

Enable GUI Warning Bannerrule

To enable displaying a login warning banner in the GNOME Display Manager's login screen, run the following command:

$ sudo gconftool-2 --direct \
  --config-source xml:readwrite:/etc/gconf/gconf.xml.mandatory \
  --type bool \
  --set /apps/gdm/simple-greeter/banner_message_enable true
To display a banner, this setting must be enabled and then banner text must also be set.

Rationale:

An appropriate warning message reinforces policy awareness during the login process and facilitates possible legal action against attackers.

identifiers:  CCE-27195-7, DISA FSO RHEL-06-000324

references:  AC-8(a), AC-8(b), AC-8(c), 48, 50

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
# Install GConf2 package if not installed
if ! rpm -q GConf2; then
  yum -y install GConf2
fi

# Enable displaying of a login warning banner in the GNOME Display Manager's
# login screen
gconftool-2 --direct \
            --config-source "xml:readwrite:/etc/gconf/gconf.xml.mandatory" \
            --type bool \
            --set /apps/gdm/simple-greeter/banner_message_enable true

Modify the System Login Bannerrule

To configure the system login banner:

Edit /etc/issue. Replace the default text with a message compliant with the local site policy or a legal disclaimer. The DoD required text is either:

You are accessing a U.S. Government (USG) Information System (IS) that is provided for USG-authorized use only. By using this IS (which includes any device attached to this IS), you consent to the following conditions:
-The USG routinely intercepts and monitors communications on this IS for purposes including, but not limited to, penetration testing, COMSEC monitoring, network operations and defense, personnel misconduct (PM), law enforcement (LE), and counterintelligence (CI) investigations.
-At any time, the USG may inspect and seize data stored on this IS.
-Communications using, or data stored on, this IS are not private, are subject to routine monitoring, interception, and search, and may be disclosed or used for any USG-authorized purpose.
-This IS includes security measures (e.g., authentication and access controls) to protect USG interests -- not for your personal benefit or privacy.
-Notwithstanding the above, using this IS does not constitute consent to PM, LE or CI investigative searching or monitoring of the content of privileged communications, or work product, related to personal representation or services by attorneys, psychotherapists, or clergy, and their assistants. Such communications and work product are private and confidential. See User Agreement for details.


OR:

Use of this or any other DoD interest computer system constitutes consent to monitoring at all times.
This is a DoD interest computer system. All DoD interest computer systems and related equipment are intended for the communication, transmission, processing, and storage of official U.S. Government or other authorized information only. All DoD interest computer systems are subject to monitoring at all times to ensure proper functioning of equipment and systems including security devices and systems, to prevent unauthorized use and violations of statutes and security regulations, to deter criminal activity, and for other similar purposes. Any user of a DoD interest computer system should be aware that any information placed in the system is subject to monitoring and is not subject to any expectation of privacy.
If monitoring of this or any other DoD interest computer system reveals possible evidence of violation of criminal statutes, this evidence and any other related information, including identification information about the user, may be provided to law enforcement officials. If monitoring of this or any other DoD interest computer systems reveals violations of security regulations or unauthorized use, employees who violate security regulations or make unauthorized use of DoD interest computer systems are subject to appropriate disciplinary action.
Use of this or any other DoD interest computer system constitutes consent to monitoring at all times.


OR:

I've read & consent to terms in IS user agreem't.

Rationale:

An appropriate warning message reinforces policy awareness during the login process and facilitates possible legal action against attackers.

identifiers:  CCE-26974-6, DISA FSO RHEL-06-000073

references:  AC-8(a), AC-8(b), AC-8(c), 48, 1384, 1385, 1386, 1387, 1388

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

login_banner_text="You[\s\n]+are[\s\n]+accessing[\s\n]+a[\s\n]+U.S.[\s\n]+Government[\s\n]+\(USG\)[\s\n]+Information[\s\n]+System[\s\n]+\(IS\)[\s\n]+that[\s\n]+is[\s\n]+provided[\s\n]+for[\s\n]+USG-authorized[\s\n]+use[\s\n]+only.[\s\n]*By[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+\(which[\s\n]+includes[\s\n]+any[\s\n]+device[\s\n]+attached[\s\n]+to[\s\n]+this[\s\n]+IS\),[\s\n]+you[\s\n]+consent[\s\n]+to[\s\n]+the[\s\n]+following[\s\n]+conditions\:[\s\n]*-[\s\n]*The[\s\n]+USG[\s\n]+routinely[\s\n]+intercepts[\s\n]+and[\s\n]+monitors[\s\n]+communications[\s\n]+on[\s\n]+this[\s\n]+IS[\s\n]+for[\s\n]+purposes[\s\n]+including,[\s\n]+but[\s\n]+not[\s\n]+limited[\s\n]+to,[\s\n]+penetration[\s\n]+testing,[\s\n]+COMSEC[\s\n]+monitoring,[\s\n]+network[\s\n]+operations[\s\n]+and[\s\n]+defense,[\s\n]+personnel[\s\n]+misconduct[\s\n]+\(PM\),[\s\n]+law[\s\n]+enforcement[\s\n]+\(LE\),[\s\n]+and[\s\n]+counterintelligence[\s\n]+\(CI\)[\s\n]+investigations.[\s\n]*-[\s\n]*At[\s\n]+any[\s\n]+time,[\s\n]+the[\s\n]+USG[\s\n]+may[\s\n]+inspect[\s\n]+and[\s\n]+seize[\s\n]+data[\s\n]+stored[\s\n]+on[\s\n]+this[\s\n]+IS.[\s\n]*-[\s\n]*Communications[\s\n]+using,[\s\n]+or[\s\n]+data[\s\n]+stored[\s\n]+on,[\s\n]+this[\s\n]+IS[\s\n]+are[\s\n]+not[\s\n]+private,[\s\n]+are[\s\n]+subject[\s\n]+to[\s\n]+routine[\s\n]+monitoring,[\s\n]+interception,[\s\n]+and[\s\n]+search,[\s\n]+and[\s\n]+may[\s\n]+be[\s\n]+disclosed[\s\n]+or[\s\n]+used[\s\n]+for[\s\n]+any[\s\n]+USG-authorized[\s\n]+purpose.[\s\n]*-[\s\n]*This[\s\n]+IS[\s\n]+includes[\s\n]+security[\s\n]+measures[\s\n]+\(e.g.,[\s\n]+authentication[\s\n]+and[\s\n]+access[\s\n]+controls\)[\s\n]+to[\s\n]+protect[\s\n]+USG[\s\n]+interests[\s\n]+--[\s\n]+not[\s\n]+for[\s\n]+your[\s\n]+personal[\s\n]+benefit[\s\n]+or[\s\n]+privacy.[\s\n]*-[\s\n]*Notwithstanding[\s\n]+the[\s\n]+above,[\s\n]+using[\s\n]+this[\s\n]+IS[\s\n]+does[\s\n]+not[\s\n]+constitute[\s\n]+consent[\s\n]+to[\s\n]+PM,[\s\n]+LE[\s\n]+or[\s\n]+CI[\s\n]+investigative[\s\n]+searching[\s\n]+or[\s\n]+monitoring[\s\n]+of[\s\n]+the[\s\n]+content[\s\n]+of[\s\n]+privileged[\s\n]+communications,[\s\n]+or[\s\n]+work[\s\n]+product,[\s\n]+related[\s\n]+to[\s\n]+personal[\s\n]+representation[\s\n]+or[\s\n]+services[\s\n]+by[\s\n]+attorneys,[\s\n]+psychotherapists,[\s\n]+or[\s\n]+clergy,[\s\n]+and[\s\n]+their[\s\n]+assistants.[\s\n]+Such[\s\n]+communications[\s\n]+and[\s\n]+work[\s\n]+product[\s\n]+are[\s\n]+private[\s\n]+and[\s\n]+confidential.[\s\n]+See[\s\n]+User[\s\n]+Agreement[\s\n]+for[\s\n]+details."

# There was a regular-expression matching various banners, needs to be expanded
expanded=$(echo "$login_banner_text" | sed 's/\[\\s\\n\][+*]/ /g;s/\\//g;s/[^-]- /\n\n-/g')
formatted=$(echo "$expanded" | fold -sw 80)

cat <<EOF >/etc/issue
$formatted
EOF

printf "\n" >> /etc/issue

Network Configuration and Firewallsgroup

Most machines must be connected to a network of some sort, and this brings with it the substantial risk of network attack. This section discusses the security impact of decisions about networking which must be made when configuring a system.

This section also discusses firewalls, network access controls, and other network security frameworks, which allow system-level rules to be written that can limit an attackers' ability to connect to your system. These rules can specify that network traffic should be allowed or denied from certain IP addresses, hosts, and networks. The rules can also specify which of the system's network services are available to particular hosts or networks.

contains 29 rules

Kernel Parameters Which Affect Networkinggroup

The sysctl utility is used to set parameters which affect the operation of the Linux kernel. Kernel parameters which affect networking and have security implications are described here.

contains 15 rules

Network Parameters for Hosts Onlygroup

If the system is not going to be used as a router, then setting certain kernel parameters ensure that the host will not perform routing of network traffic.

contains 3 rules

Disable Kernel Parameter for Sending ICMP Redirects by Defaultrule

To set the runtime status of the net.ipv4.conf.default.send_redirects kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.conf.default.send_redirects=0
If this is not the system's default value, add the following line to /etc/sysctl.conf:
net.ipv4.conf.default.send_redirects = 0

Rationale:

Sending ICMP redirects permits the system to instruct other systems to update their routing information. The ability to send ICMP redirects is only appropriate for systems acting as routers.

identifiers:  CCE-27001-7, DISA FSO RHEL-06-000080

references:  AC-4, CM-7, SC-5, SC-7, 1551

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
#
# Set runtime for net.ipv4.conf.default.send_redirects
#
/sbin/sysctl -q -n -w net.ipv4.conf.default.send_redirects=0

#
# If net.ipv4.conf.default.send_redirects present in /etc/sysctl.conf, change value to "0"
#	else, add "net.ipv4.conf.default.send_redirects = 0" to /etc/sysctl.conf
#
if grep --silent ^net.ipv4.conf.default.send_redirects /etc/sysctl.conf ; then
	sed -i 's/^net.ipv4.conf.default.send_redirects.*/net.ipv4.conf.default.send_redirects = 0/g' /etc/sysctl.conf
else
	echo -e "\n# Set net.ipv4.conf.default.send_redirects to 0 per security requirements" >> /etc/sysctl.conf
	echo "net.ipv4.conf.default.send_redirects = 0" >> /etc/sysctl.conf
fi

Disable Kernel Parameter for Sending ICMP Redirects for All Interfacesrule

To set the runtime status of the net.ipv4.conf.all.send_redirects kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.conf.all.send_redirects=0
If this is not the system's default value, add the following line to /etc/sysctl.conf:
net.ipv4.conf.all.send_redirects = 0

Rationale:

Sending ICMP redirects permits the system to instruct other systems to update their routing information. The ability to send ICMP redirects is only appropriate for systems acting as routers.

identifiers:  CCE-27004-1, DISA FSO RHEL-06-000081

references:  CM-7, SC-5(1), 1551

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
#
# Set runtime for net.ipv4.conf.all.send_redirects
#
/sbin/sysctl -q -n -w net.ipv4.conf.all.send_redirects=0

#
# If net.ipv4.conf.all.send_redirects present in /etc/sysctl.conf, change value to "0"
#	else, add "net.ipv4.conf.all.send_redirects = 0" to /etc/sysctl.conf
#
if grep --silent ^net.ipv4.conf.all.send_redirects /etc/sysctl.conf ; then
	sed -i 's/^net.ipv4.conf.all.send_redirects.*/net.ipv4.conf.all.send_redirects = 0/g' /etc/sysctl.conf
else
	echo -e "\n# Set net.ipv4.conf.all.send_redirects to 0 per security requirements" >> /etc/sysctl.conf
	echo "net.ipv4.conf.all.send_redirects = 0" >> /etc/sysctl.conf
fi

Disable Kernel Parameter for IP Forwardingrule

To set the runtime status of the net.ipv4.ip_forward kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.ip_forward=0
If this is not the system's default value, add the following line to /etc/sysctl.conf:
net.ipv4.ip_forward = 0

Rationale:

IP forwarding permits the kernel to forward packets from one network interface to another. The ability to forward packets between two networks is only appropriate for systems acting as routers.

identifiers:  CCE-26866-4, DISA FSO RHEL-06-000082

references:  CM-7, SC-5, 366

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
#
# Set runtime for net.ipv4.ip_forward
#
/sbin/sysctl -q -n -w net.ipv4.ip_forward=0

#
# If net.ipv4.ip_forward present in /etc/sysctl.conf, change value to "0"
#	else, add "net.ipv4.ip_forward = 0" to /etc/sysctl.conf
#
if grep --silent ^net.ipv4.ip_forward /etc/sysctl.conf ; then
	sed -i 's/^net.ipv4.ip_forward.*/net.ipv4.ip_forward = 0/g' /etc/sysctl.conf
else
	echo -e "\n# Set net.ipv4.ip_forward to 0 per security requirements" >> /etc/sysctl.conf
	echo "net.ipv4.ip_forward = 0" >> /etc/sysctl.conf
fi

Network Related Kernel Runtime Parameters for Hosts and Routersgroup

Certain kernel parameters should be set for systems which are acting as either hosts or routers to improve the system's ability defend against certain types of IPv4 protocol attacks.

contains 12 rules

Configure Kernel Parameter for Accepting Source-Routed Packets for All Interfacesrule

To set the runtime status of the net.ipv4.conf.all.accept_source_route kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.conf.all.accept_source_route=0
If this is not the system's default value, add the following line to /etc/sysctl.conf:
net.ipv4.conf.all.accept_source_route = 0

Rationale:

Accepting source-routed packets in the IPv4 protocol has few legitimate uses. It should be disabled unless it is absolutely required.

identifiers:  CCE-27037-1, DISA FSO RHEL-06-000083

references:  CM-7, SC-5, 1551

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv4_conf_all_accept_source_route_value="0"

#
# Set runtime for net.ipv4.conf.all.accept_source_route
#
/sbin/sysctl -q -n -w net.ipv4.conf.all.accept_source_route=$sysctl_net_ipv4_conf_all_accept_source_route_value

#
# If net.ipv4.conf.all.accept_source_route present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.conf.all.accept_source_route = value" to /etc/sysctl.conf
#
if grep --silent ^net.ipv4.conf.all.accept_source_route /etc/sysctl.conf ; then
	sed -i "s/^net.ipv4.conf.all.accept_source_route.*/net.ipv4.conf.all.accept_source_route = $sysctl_net_ipv4_conf_all_accept_source_route_value/g" /etc/sysctl.conf
else
	echo -e "\n# Set net.ipv4.conf.all.accept_source_route to $sysctl_net_ipv4_conf_all_accept_source_route_value per security requirements" >> /etc/sysctl.conf
	echo "net.ipv4.conf.all.accept_source_route = $sysctl_net_ipv4_conf_all_accept_source_route_value" >> /etc/sysctl.conf
fi

Configure Kernel Parameter for Accepting ICMP Redirects for All Interfacesrule

To set the runtime status of the net.ipv4.conf.all.accept_redirects kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.conf.all.accept_redirects=0
If this is not the system's default value, add the following line to /etc/sysctl.conf:
net.ipv4.conf.all.accept_redirects = 0

Rationale:

Accepting ICMP redirects has few legitimate uses. It should be disabled unless it is absolutely required.

identifiers:  CCE-27027-2, DISA FSO RHEL-06-000084

references:  CM-7, SC-5, 1503, 1551

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv4_conf_all_accept_redirects_value="0"

#
# Set runtime for net.ipv4.conf.all.accept_redirects
#
/sbin/sysctl -q -n -w net.ipv4.conf.all.accept_redirects=$sysctl_net_ipv4_conf_all_accept_redirects_value

#
# If net.ipv4.conf.all.accept_redirects present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.conf.all.accept_redirects = value" to /etc/sysctl.conf
#
if grep --silent ^net.ipv4.conf.all.accept_redirects /etc/sysctl.conf ; then
	sed -i "s/^net.ipv4.conf.all.accept_redirects.*/net.ipv4.conf.all.accept_redirects = $sysctl_net_ipv4_conf_all_accept_redirects_value/g" /etc/sysctl.conf
else
	echo -e "\n# Set net.ipv4.conf.all.accept_redirects to $sysctl_net_ipv4_conf_all_accept_redirects_value per security requirements" >> /etc/sysctl.conf
	echo "net.ipv4.conf.all.accept_redirects = $sysctl_net_ipv4_conf_all_accept_redirects_value" >> /etc/sysctl.conf
fi

Configure Kernel Parameter for Accepting Secure Redirects for All Interfacesrule

To set the runtime status of the net.ipv4.conf.all.secure_redirects kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.conf.all.secure_redirects=0
If this is not the system's default value, add the following line to /etc/sysctl.conf:
net.ipv4.conf.all.secure_redirects = 0

Rationale:

Accepting "secure" ICMP redirects (from those gateways listed as default gateways) has few legitimate uses. It should be disabled unless it is absolutely required.

identifiers:  CCE-26854-0, DISA FSO RHEL-06-000086

references:  AC-4, CM-7, SC-5, 1503, 1551

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv4_conf_all_secure_redirects_value="0"

#
# Set runtime for net.ipv4.conf.all.secure_redirects
#
/sbin/sysctl -q -n -w net.ipv4.conf.all.secure_redirects=$sysctl_net_ipv4_conf_all_secure_redirects_value

#
# If net.ipv4.conf.all.secure_redirects present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.conf.all.secure_redirects = value" to /etc/sysctl.conf
#
if grep --silent ^net.ipv4.conf.all.secure_redirects /etc/sysctl.conf ; then
	sed -i "s/^net.ipv4.conf.all.secure_redirects.*/net.ipv4.conf.all.secure_redirects = $sysctl_net_ipv4_conf_all_secure_redirects_value/g" /etc/sysctl.conf
else
	echo -e "\n# Set net.ipv4.conf.all.secure_redirects to $sysctl_net_ipv4_conf_all_secure_redirects_value per security requirements" >> /etc/sysctl.conf
	echo "net.ipv4.conf.all.secure_redirects = $sysctl_net_ipv4_conf_all_secure_redirects_value" >> /etc/sysctl.conf
fi

Configure Kernel Parameter to Log Martian Packetsrule

To set the runtime status of the net.ipv4.conf.all.log_martians kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.conf.all.log_martians=1
If this is not the system's default value, add the following line to /etc/sysctl.conf:
net.ipv4.conf.all.log_martians = 1

Rationale:

The presence of "martian" packets (which have impossible addresses) as well as spoofed packets, source-routed packets, and redirects could be a sign of nefarious network activity. Logging these packets enables this activity to be detected.

identifiers:  CCE-27066-0, DISA FSO RHEL-06-000088

references:  AC-3(10), CM-7, SC-5(3), 126

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv4_conf_all_log_martians_value="1"

#
# Set runtime for net.ipv4.conf.all.log_martians
#
/sbin/sysctl -q -n -w net.ipv4.conf.all.log_martians=$sysctl_net_ipv4_conf_all_log_martians_value

#
# If net.ipv4.conf.all.log_martians present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.conf.all.log_martians = value" to /etc/sysctl.conf
#
if grep --silent ^net.ipv4.conf.all.log_martians /etc/sysctl.conf ; then
	sed -i "s/^net.ipv4.conf.all.log_martians.*/net.ipv4.conf.all.log_martians = $sysctl_net_ipv4_conf_all_log_martians_value/g" /etc/sysctl.conf
else
	echo -e "\n# Set net.ipv4.conf.all.log_martians to $sysctl_net_ipv4_conf_all_log_martians_value per security requirements" >> /etc/sysctl.conf
	echo "net.ipv4.conf.all.log_martians = $sysctl_net_ipv4_conf_all_log_martians_value" >> /etc/sysctl.conf
fi

Configure Kernel Parameter for Accepting Source-Routed Packets By Defaultrule

To set the runtime status of the net.ipv4.conf.default.accept_source_route kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.conf.default.accept_source_route=0
If this is not the system's default value, add the following line to /etc/sysctl.conf:
net.ipv4.conf.default.accept_source_route = 0

Rationale:

Accepting source-routed packets in the IPv4 protocol has few legitimate uses. It should be disabled unless it is absolutely required.

identifiers:  CCE-26983-7, DISA FSO RHEL-06-000089

references:  AC-4, CM-7, SC-5, SC-7, 1551

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv4_conf_default_accept_source_route_value="0"

#
# Set runtime for net.ipv4.conf.default.accept_source_route
#
/sbin/sysctl -q -n -w net.ipv4.conf.default.accept_source_route=$sysctl_net_ipv4_conf_default_accept_source_route_value

#
# If net.ipv4.conf.default.accept_source_route present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.conf.default.accept_source_route = value" to /etc/sysctl.conf
#
if grep --silent ^net.ipv4.conf.default.accept_source_route /etc/sysctl.conf ; then
	sed -i "s/^net.ipv4.conf.default.accept_source_route.*/net.ipv4.conf.default.accept_source_route = $sysctl_net_ipv4_conf_default_accept_source_route_value/g" /etc/sysctl.conf
else
	echo -e "\n# Set net.ipv4.conf.default.accept_source_route to $sysctl_net_ipv4_conf_default_accept_source_route_value per security requirements" >> /etc/sysctl.conf
	echo "net.ipv4.conf.default.accept_source_route = $sysctl_net_ipv4_conf_default_accept_source_route_value" >> /etc/sysctl.conf
fi

Configure Kernel Parameter for Accepting ICMP Redirects By Defaultrule

To set the runtime status of the net.ipv4.conf.default.accept_redirects kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.conf.default.accept_redirects=0
If this is not the system's default value, add the following line to /etc/sysctl.conf:
net.ipv4.conf.default.accept_redirects = 0

Rationale:

This feature of the IPv4 protocol has few legitimate uses. It should be disabled unless it is absolutely required.

identifiers:  CCE-27015-7, DISA FSO RHEL-06-000091

references:  AC-4, CM-7, SC-5, SC-7, 1551

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv4_conf_default_accept_redirects_value="0"

#
# Set runtime for net.ipv4.conf.default.accept_redirects
#
/sbin/sysctl -q -n -w net.ipv4.conf.default.accept_redirects=$sysctl_net_ipv4_conf_default_accept_redirects_value

#
# If net.ipv4.conf.default.accept_redirects present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.conf.default.accept_redirects = value" to /etc/sysctl.conf
#
if grep --silent ^net.ipv4.conf.default.accept_redirects /etc/sysctl.conf ; then
	sed -i "s/^net.ipv4.conf.default.accept_redirects.*/net.ipv4.conf.default.accept_redirects = $sysctl_net_ipv4_conf_default_accept_redirects_value/g" /etc/sysctl.conf
else
	echo -e "\n# Set net.ipv4.conf.default.accept_redirects to $sysctl_net_ipv4_conf_default_accept_redirects_value per security requirements" >> /etc/sysctl.conf
	echo "net.ipv4.conf.default.accept_redirects = $sysctl_net_ipv4_conf_default_accept_redirects_value" >> /etc/sysctl.conf
fi

Configure Kernel Parameter for Accepting Secure Redirects By Defaultrule

To set the runtime status of the net.ipv4.conf.default.secure_redirects kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.conf.default.secure_redirects=0
If this is not the system's default value, add the following line to /etc/sysctl.conf:
net.ipv4.conf.default.secure_redirects = 0

Rationale:

Accepting "secure" ICMP redirects (from those gateways listed as default gateways) has few legitimate uses. It should be disabled unless it is absolutely required.

identifiers:  CCE-26831-8, DISA FSO RHEL-06-000090

references:  AC-4, CM-7, SC-5, SC-7, 1551

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv4_conf_default_secure_redirects_value="0"

#
# Set runtime for net.ipv4.conf.default.secure_redirects
#
/sbin/sysctl -q -n -w net.ipv4.conf.default.secure_redirects=$sysctl_net_ipv4_conf_default_secure_redirects_value

#
# If net.ipv4.conf.default.secure_redirects present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.conf.default.secure_redirects = value" to /etc/sysctl.conf
#
if grep --silent ^net.ipv4.conf.default.secure_redirects /etc/sysctl.conf ; then
	sed -i "s/^net.ipv4.conf.default.secure_redirects.*/net.ipv4.conf.default.secure_redirects = $sysctl_net_ipv4_conf_default_secure_redirects_value/g" /etc/sysctl.conf
else
	echo -e "\n# Set net.ipv4.conf.default.secure_redirects to $sysctl_net_ipv4_conf_default_secure_redirects_value per security requirements" >> /etc/sysctl.conf
	echo "net.ipv4.conf.default.secure_redirects = $sysctl_net_ipv4_conf_default_secure_redirects_value" >> /etc/sysctl.conf
fi

Configure Kernel Parameter to Ignore ICMP Broadcast Echo Requestsrule

To set the runtime status of the net.ipv4.icmp_echo_ignore_broadcasts kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=1
If this is not the system's default value, add the following line to /etc/sysctl.conf:
net.ipv4.icmp_echo_ignore_broadcasts = 1

Rationale:

Ignoring ICMP echo requests (pings) sent to broadcast or multicast addresses makes the system slightly more difficult to enumerate on the network.

identifiers:  CCE-26883-9, DISA FSO RHEL-06-000092

references:  CM-7, SC-5, 1551

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value="1"

#
# Set runtime for net.ipv4.icmp_echo_ignore_broadcasts
#
/sbin/sysctl -q -n -w net.ipv4.icmp_echo_ignore_broadcasts=$sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value

#
# If net.ipv4.icmp_echo_ignore_broadcasts present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.icmp_echo_ignore_broadcasts = value" to /etc/sysctl.conf
#
if grep --silent ^net.ipv4.icmp_echo_ignore_broadcasts /etc/sysctl.conf ; then
	sed -i "s/^net.ipv4.icmp_echo_ignore_broadcasts.*/net.ipv4.icmp_echo_ignore_broadcasts = $sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value/g" /etc/sysctl.conf
else
	echo -e "\n# Set net.ipv4.icmp_echo_ignore_broadcasts to $sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value per security requirements" >> /etc/sysctl.conf
	echo "net.ipv4.icmp_echo_ignore_broadcasts = $sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value" >> /etc/sysctl.conf
fi

Configure Kernel Parameter to Ignore Bogus ICMP Error Responsesrule

To set the runtime status of the net.ipv4.icmp_ignore_bogus_error_responses kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.icmp_ignore_bogus_error_responses=1
If this is not the system's default value, add the following line to /etc/sysctl.conf:
net.ipv4.icmp_ignore_bogus_error_responses = 1

Rationale:

Ignoring bogus ICMP error responses reduces log size, although some activity would not be logged.

identifiers:  CCE-26993-6, DISA FSO RHEL-06-000093

references:  CM-7, SC-5

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value="1"

#
# Set runtime for net.ipv4.icmp_ignore_bogus_error_responses
#
/sbin/sysctl -q -n -w net.ipv4.icmp_ignore_bogus_error_responses=$sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value

#
# If net.ipv4.icmp_ignore_bogus_error_responses present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.icmp_ignore_bogus_error_responses = value" to /etc/sysctl.conf
#
if grep --silent ^net.ipv4.icmp_ignore_bogus_error_responses /etc/sysctl.conf ; then
	sed -i "s/^net.ipv4.icmp_ignore_bogus_error_responses.*/net.ipv4.icmp_ignore_bogus_error_responses = $sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value/g" /etc/sysctl.conf
else
	echo -e "\n# Set net.ipv4.icmp_ignore_bogus_error_responses to $sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value per security requirements" >> /etc/sysctl.conf
	echo "net.ipv4.icmp_ignore_bogus_error_responses = $sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value" >> /etc/sysctl.conf
fi

Configure Kernel Parameter to Use TCP Syncookiesrule

To set the runtime status of the net.ipv4.tcp_syncookies kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.tcp_syncookies=1
If this is not the system's default value, add the following line to /etc/sysctl.conf:
net.ipv4.tcp_syncookies = 1

Rationale:

A TCP SYN flood attack can cause a denial of service by filling a system's TCP connection table with connections in the SYN_RCVD state. Syncookies can be used to track a connection when a subsequent ACK is received, verifying the initiator is attempting a valid connection and is not a flood source. This feature is activated when a flood condition is detected, and enables the system to continue servicing valid connection requests.

identifiers:  CCE-27053-8, DISA FSO RHEL-06-000095

references:  AC-4, SC-5(2), SC-5(3), 1092, 1095

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv4_tcp_syncookies_value="1"

#
# Set runtime for net.ipv4.tcp_syncookies
#
/sbin/sysctl -q -n -w net.ipv4.tcp_syncookies=$sysctl_net_ipv4_tcp_syncookies_value

#
# If net.ipv4.tcp_syncookies present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.tcp_syncookies = value" to /etc/sysctl.conf
#
if grep --silent ^net.ipv4.tcp_syncookies /etc/sysctl.conf ; then
	sed -i "s/^net.ipv4.tcp_syncookies.*/net.ipv4.tcp_syncookies = $sysctl_net_ipv4_tcp_syncookies_value/g" /etc/sysctl.conf
else
	echo -e "\n# Set net.ipv4.tcp_syncookies to $sysctl_net_ipv4_tcp_syncookies_value per security requirements" >> /etc/sysctl.conf
	echo "net.ipv4.tcp_syncookies = $sysctl_net_ipv4_tcp_syncookies_value" >> /etc/sysctl.conf
fi

Configure Kernel Parameter to Use Reverse Path Filtering for All Interfacesrule

To set the runtime status of the net.ipv4.conf.all.rp_filter kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.conf.all.rp_filter=1
If this is not the system's default value, add the following line to /etc/sysctl.conf:
net.ipv4.conf.all.rp_filter = 1

Rationale:

Enabling reverse path filtering drops packets with source addresses that should not have been able to be received on the interface they were received on. It should not be used on systems which are routers for complicated networks, but is helpful for end hosts and routers serving small networks.

identifiers:  CCE-26979-5, DISA FSO RHEL-06-000096

references:  AC-4, SC-5, SC-7, 1551

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv4_conf_all_rp_filter_value="1"

#
# Set runtime for net.ipv4.conf.all.rp_filter
#
/sbin/sysctl -q -n -w net.ipv4.conf.all.rp_filter=$sysctl_net_ipv4_conf_all_rp_filter_value

#
# If net.ipv4.conf.all.rp_filter present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.conf.all.rp_filter = value" to /etc/sysctl.conf
#
if grep --silent ^net.ipv4.conf.all.rp_filter /etc/sysctl.conf ; then
	sed -i "s/^net.ipv4.conf.all.rp_filter.*/net.ipv4.conf.all.rp_filter = $sysctl_net_ipv4_conf_all_rp_filter_value/g" /etc/sysctl.conf
else
	echo -e "\n# Set net.ipv4.conf.all.rp_filter to $sysctl_net_ipv4_conf_all_rp_filter_value per security requirements" >> /etc/sysctl.conf
	echo "net.ipv4.conf.all.rp_filter = $sysctl_net_ipv4_conf_all_rp_filter_value" >> /etc/sysctl.conf
fi

Configure Kernel Parameter to Use Reverse Path Filtering by Defaultrule

To set the runtime status of the net.ipv4.conf.default.rp_filter kernel parameter, run the following command:

$ sudo sysctl -w net.ipv4.conf.default.rp_filter=1
If this is not the system's default value, add the following line to /etc/sysctl.conf:
net.ipv4.conf.default.rp_filter = 1

Rationale:

Enabling reverse path filtering drops packets with source addresses that should not have been able to be received on the interface they were received on. It should not be used on systems which are routers for complicated networks, but is helpful for end hosts and routers serving small networks.

identifiers:  CCE-26915-9, DISA FSO RHEL-06-000097

references:  AC-4, SC-5, SC-7

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv4_conf_default_rp_filter_value="1"

#
# Set runtime for net.ipv4.conf.default.rp_filter
#
/sbin/sysctl -q -n -w net.ipv4.conf.default.rp_filter=$sysctl_net_ipv4_conf_default_rp_filter_value

#
# If net.ipv4.conf.default.rp_filter present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv4.conf.default.rp_filter = value" to /etc/sysctl.conf
#
if grep --silent ^net.ipv4.conf.default.rp_filter /etc/sysctl.conf ; then
	sed -i "s/^net.ipv4.conf.default.rp_filter.*/net.ipv4.conf.default.rp_filter = $sysctl_net_ipv4_conf_default_rp_filter_value/g" /etc/sysctl.conf
else
	echo -e "\n# Set net.ipv4.conf.default.rp_filter to $sysctl_net_ipv4_conf_default_rp_filter_value per security requirements" >> /etc/sysctl.conf
	echo "net.ipv4.conf.default.rp_filter = $sysctl_net_ipv4_conf_default_rp_filter_value" >> /etc/sysctl.conf
fi

Wireless Networkinggroup

Wireless networking, such as 802.11 (WiFi) and Bluetooth, can present a security risk to sensitive or classified systems and networks. Wireless networking hardware is much more likely to be included in laptop or portable systems than in desktops or servers.

Removal of hardware provides the greatest assurance that the wireless capability remains disabled. Acquisition policies often include provisions to prevent the purchase of equipment that will be used in sensitive spaces and includes wireless capabilities. If it is impractical to remove the wireless hardware, and policy permits the device to enter sensitive spaces as long as wireless is disabled, efforts should instead focus on disabling wireless capability via software.

contains 2 rules

Disable Wireless Through Software Configurationgroup

If it is impossible to remove the wireless hardware from the device in question, disable as much of it as possible through software. The following methods can disable software support for wireless networking, but note that these methods do not prevent malicious software or careless users from re-activating the devices.

contains 2 rules

Disable Bluetooth Servicerule

The bluetooth service can be disabled with the following command:

$ sudo chkconfig bluetooth off
$ sudo service bluetooth stop

Rationale:

Disabling the bluetooth service prevents the system from attempting connections to Bluetooth devices, which entails some security risk. Nevertheless, variation in this risk decision may be expected due to the utility of Bluetooth connectivity and its limited range.

identifiers:  CCE-27081-9, DISA FSO RHEL-06-000331

references:  AC-18(a), AC-18(d), AC-18(3), CM-7, 85, 1551

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:disable

function service_command {

# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)

# Check sanity of the input
if [ $# -lt "2" ]
then
  echo "Usage: service_command 'enable/disable' 'service_name.service'"
  echo
  echo "To enable or disable xinetd services add \'xinetd=service_name\'"
  echo "as the last argument"
  echo "Aborting."
  exit 1
fi

# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
  service_util="/usr/bin/systemctl"
else
  service_util="/sbin/service"
  chkconfig_util="/sbin/chkconfig"
fi

# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
  service_state="enable"
  service_operation="start"
  chkconfig_state="on"
else
  service_state="disable"
  service_operation="stop"
  chkconfig_state="off"
fi

# If chkconfig_util is not empty, use chkconfig/service commands.
if ! [ "x$chkconfig_util" = x ] ; then
  $service_util $service $service_operation
  $chkconfig_util --level 0123456 $service $chkconfig_state
else
  $service_util $service_operation $service
  $service_util $service_state $service
fi

# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if ! [ "x$xinetd" = x ] ; then
  grep -qi disable /etc/xinetd.d/$xinetd && \

  if ! [ "$service_operation" != 'disable' ] ; then
    sed -i "s/disable.*/disable         = no/gI" /etc/xinetd.d/$xinetd
  else
    sed -i "s/disable.*/disable         = yes/gI" /etc/xinetd.d/$xinetd
  fi
fi

}

service_command disable bluetooth
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:disable
- name: Disable service bluetooth
  service:
    name="{{item}}"
    enabled="no"
    state="stopped"
  with_items:
    - bluetooth

Disable Bluetooth Kernel Modulesrule

The kernel's module loading system can be configured to prevent loading of the Bluetooth module. Add the following to the appropriate /etc/modprobe.d configuration file to prevent the loading of the Bluetooth module:

install bluetooth /bin/true

Rationale:

If Bluetooth functionality must be disabled, preventing the kernel from loading the kernel module provides an additional safeguard against its activation.

identifiers:  CCE-26763-3, DISA FSO RHEL-06-000315

references:  AC-18(a), AC-18(d), AC-18(3), CM-7, 85, 1551

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
if grep --silent "^install bluetooth" /etc/modprobe.d/bluetooth.conf ; then
        sed -i 's/^install bluetooth.*/install bluetooth /bin/true/g' /etc/modprobe.d/bluetooth.conf
else
        echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/bluetooth.conf
        echo "install bluetooth /bin/true" >> /etc/modprobe.d/bluetooth.conf
fi
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Ensure kernel module 'bluetooth' is disabled
  lineinfile:
    create=yes
    dest="/etc/modprobe.d/{{item}}.conf"
    regexp="{{item}}"
    line="install {{item}} /bin/true"
  with_items:
    - bluetooth

IPv6group

The system includes support for Internet Protocol version 6. A major and often-mentioned improvement over IPv4 is its enormous increase in the number of available addresses. Another important feature is its support for automatic configuration of many network settings.

contains 2 rules

Disable Support for IPv6 Unless Neededgroup

Despite configuration that suggests support for IPv6 has been disabled, link-local IPv6 address auto-configuration occurs even when only an IPv4 address is assigned. The only way to effectively prevent execution of the IPv6 networking stack is to instruct the system not to activate the IPv6 kernel module.

contains 1 rule

Disable IPv6 Networking Support Automatic Loadingrule

To prevent the IPv6 kernel module (ipv6) from binding to the IPv6 networking stack, add the following line to /etc/modprobe.d/disabled.conf (or another file in /etc/modprobe.d):

options ipv6 disable=1
This permits the IPv6 module to be loaded (and thus satisfy other modules that depend on it), while disabling support for the IPv6 protocol.

Rationale:

Any unnecessary network stacks - including IPv6 - should be disabled, to reduce the vulnerability to exploitation.

identifiers:  CCE-27153-6, DISA FSO RHEL-06-000098

references:  CM-7, 1551

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

# Prevent the IPv6 kernel module (ipv6) from loading the IPv6 networking stack
echo "options ipv6 disable=1" > /etc/modprobe.d/ipv6.conf

# Since according to: https://access.redhat.com/solutions/72733
# "ipv6 disable=1" options doesn't always disable the IPv6 networking stack from
# loading, instruct also sysctl configuration to disable IPv6 according to:
# https://access.redhat.com/solutions/8709#rhel6disable

declare -a IPV6_SETTINGS=("net.ipv6.conf.all.disable_ipv6" "net.ipv6.conf.default.disable_ipv6")

for setting in ${IPV6_SETTINGS[@]}
do
	# Set runtime =1 for setting
	/sbin/sysctl -q -n -w "$setting=1"

	# If setting is present in /etc/sysctl.conf, change value to "1"
	# else, add "$setting = 1" to /etc/sysctl.conf
	if grep -q ^"$setting" /etc/sysctl.conf ; then
		sed -i "s/^$setting.*/$setting = 1/g" /etc/sysctl.conf
	else
		echo "" >> /etc/sysctl.conf
		echo "# Set $setting = 1 per security requirements" >> /etc/sysctl.conf
		echo "$setting = 1" >> /etc/sysctl.conf
	fi
done

Configure IPv6 Settings if Necessarygroup

A major feature of IPv6 is the extent to which systems implementing it can automatically configure their networking devices using information from the network. From a security perspective, manually configuring important configuration information is preferable to accepting it from the network in an unauthenticated fashion.

contains 1 rule

Disable Automatic Configurationgroup

Disable the system's acceptance of router advertisements and redirects by adding or correcting the following line in /etc/sysconfig/network (note that this does not disable sending router solicitations):

IPV6_AUTOCONF=no

contains 1 rule

Configure Accepting IPv6 Redirects By Defaultrule

To set the runtime status of the net.ipv6.conf.default.accept_redirects kernel parameter, run the following command:

$ sudo sysctl -w net.ipv6.conf.default.accept_redirects=0
If this is not the system's default value, add the following line to /etc/sysctl.conf:
net.ipv6.conf.default.accept_redirects = 0

Rationale:

An illicit ICMP redirect message could result in a man-in-the-middle attack.

identifiers:  CCE-27166-8, DISA FSO RHEL-06-000099

references:  CM-7, 1551, 4.4.1.2

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

sysctl_net_ipv6_conf_default_accept_redirects_value="0"

#
# Set runtime for net.ipv6.conf.default.accept_redirects
#
/sbin/sysctl -q -n -w net.ipv6.conf.default.accept_redirects=$sysctl_net_ipv6_conf_default_accept_redirects_value

#
# If net.ipv6.conf.default.accept_redirects present in /etc/sysctl.conf, change value to appropriate value
#	else, add "net.ipv6.conf.default.accept_redirects = value" to /etc/sysctl.conf
#
if grep --silent ^net.ipv6.conf.default.accept_redirects /etc/sysctl.conf ; then
	sed -i "s/^net.ipv6.conf.default.accept_redirects.*/net.ipv6.conf.default.accept_redirects = $sysctl_net_ipv6_conf_default_accept_redirects_value/g" /etc/sysctl.conf
else
	echo -e "\n# Set net.ipv6.conf.default.accept_redirects to $sysctl_net_ipv6_conf_default_accept_redirects_value per security requirements" >> /etc/sysctl.conf
	echo "net.ipv6.conf.default.accept_redirects = $sysctl_net_ipv6_conf_default_accept_redirects_value" >> /etc/sysctl.conf
fi

iptables and ip6tablesgroup

A host-based firewall called netfilter is included as part of the Linux kernel distributed with the system. It is activated by default. This firewall is controlled by the program iptables, and the entire capability is frequently referred to by this name. An analogous program called ip6tables handles filtering for IPv6.

Unlike TCP Wrappers, which depends on the network server program to support and respect the rules written, netfilter filtering occurs at the kernel level, before a program can even process the data from the network packet. As such, any program on the system is affected by the rules written.

This section provides basic information about strengthening the iptables and ip6tables configurations included with the system. For more complete information that may allow the construction of a sophisticated ruleset tailored to your environment, please consult the references at the end of this section.

contains 5 rules

Inspect and Activate Default Rulesgroup

View the currently-enforced iptables rules by running the command:

$ sudo iptables -nL --line-numbers
The command is analogous for ip6tables.

If the firewall does not appear to be active (i.e., no rules appear), activate it and ensure that it starts at boot by issuing the following commands (and analogously for ip6tables):
$ sudo service iptables restart
The default iptables rules are:
Chain INPUT (policy ACCEPT)
num  target     prot opt source       destination
1    ACCEPT     all  --  0.0.0.0/0    0.0.0.0/0    state RELATED,ESTABLISHED 
2    ACCEPT     icmp --  0.0.0.0/0    0.0.0.0/0
3    ACCEPT     all  --  0.0.0.0/0    0.0.0.0/0
4    ACCEPT     tcp  --  0.0.0.0/0    0.0.0.0/0    state NEW tcp dpt:22 
5    REJECT     all  --  0.0.0.0/0    0.0.0.0/0    reject-with icmp-host-prohibited 

Chain FORWARD (policy ACCEPT)
num  target     prot opt source       destination
1    REJECT     all  --  0.0.0.0/0    0.0.0.0/0    reject-with icmp-host-prohibited 

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source       destination
The ip6tables default rules are essentially the same.

contains 3 rules

Verify ip6tables Enabled if Using IPv6rule

The ip6tables service can be enabled with the following command:

$ sudo chkconfig --level 2345 ip6tables on

Rationale:

The ip6tables service provides the system's host-based firewalling capability for IPv6 and ICMPv6.

identifiers:  CCE-27006-6, DISA FSO RHEL-06-000103

references:  AC-4, CA-3(c), CM-7, 32, 66, 1115, 1118, 1092, 1117, 1098, 1100, 1097, 1414

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

function service_command {

# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)

# Check sanity of the input
if [ $# -lt "2" ]
then
  echo "Usage: service_command 'enable/disable' 'service_name.service'"
  echo
  echo "To enable or disable xinetd services add \'xinetd=service_name\'"
  echo "as the last argument"
  echo "Aborting."
  exit 1
fi

# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
  service_util="/usr/bin/systemctl"
else
  service_util="/sbin/service"
  chkconfig_util="/sbin/chkconfig"
fi

# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
  service_state="enable"
  service_operation="start"
  chkconfig_state="on"
else
  service_state="disable"
  service_operation="stop"
  chkconfig_state="off"
fi

# If chkconfig_util is not empty, use chkconfig/service commands.
if ! [ "x$chkconfig_util" = x ] ; then
  $service_util $service $service_operation
  $chkconfig_util --level 0123456 $service $chkconfig_state
else
  $service_util $service_operation $service
  $service_util $service_state $service
fi

# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if ! [ "x$xinetd" = x ] ; then
  grep -qi disable /etc/xinetd.d/$xinetd && \

  if ! [ "$service_operation" != 'disable' ] ; then
    sed -i "s/disable.*/disable         = no/gI" /etc/xinetd.d/$xinetd
  else
    sed -i "s/disable.*/disable         = yes/gI" /etc/xinetd.d/$xinetd
  fi
fi

}

service_command enable ip6tables
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Enable service ip6tables
  service:
    name="{{item}}"
    enabled="yes"
    state="started"
  with_items:
    - ip6tables

Set Default ip6tables Policy for Incoming Packetsrule

To set the default policy to DROP (instead of ACCEPT) for the built-in INPUT chain which processes incoming packets, add or correct the following line in /etc/sysconfig/ip6tables:

:INPUT DROP [0:0]
If changes were required, reload the ip6tables rules:
$ sudo service ip6tables reload

Rationale:

In ip6tables, the default policy is applied only after all the applicable rules in the table are examined for a match. Setting the default policy to DROP implements proper design for a firewall, i.e. any packets which are not explicitly permitted should not be accepted.

identifiers:  CCE-27317-7, DISA FSO RHEL-06-000523

references:  CM-7, 66

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
sed -i 's/^:INPUT ACCEPT.*/:INPUT DROP [0:0]/g' /etc/sysconfig/ip6tables

Verify iptables Enabledrule

The iptables service can be enabled with the following command:

$ sudo chkconfig --level 2345 iptables on

Rationale:

The iptables service provides the system's host-based firewalling capability for IPv4 and ICMP.

identifiers:  CCE-27018-1, DISA FSO RHEL-06-000117

references:  AC-4, CA-3(c), CM-7, 32, 66, 1115, 1118, 1092, 1117, 1098, 1100, 1097, 1414

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

function service_command {

# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)

# Check sanity of the input
if [ $# -lt "2" ]
then
  echo "Usage: service_command 'enable/disable' 'service_name.service'"
  echo
  echo "To enable or disable xinetd services add \'xinetd=service_name\'"
  echo "as the last argument"
  echo "Aborting."
  exit 1
fi

# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
  service_util="/usr/bin/systemctl"
else
  service_util="/sbin/service"
  chkconfig_util="/sbin/chkconfig"
fi

# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
  service_state="enable"
  service_operation="start"
  chkconfig_state="on"
else
  service_state="disable"
  service_operation="stop"
  chkconfig_state="off"
fi

# If chkconfig_util is not empty, use chkconfig/service commands.
if ! [ "x$chkconfig_util" = x ] ; then
  $service_util $service $service_operation
  $chkconfig_util --level 0123456 $service $chkconfig_state
else
  $service_util $service_operation $service
  $service_util $service_state $service
fi

# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if ! [ "x$xinetd" = x ] ; then
  grep -qi disable /etc/xinetd.d/$xinetd && \

  if ! [ "$service_operation" != 'disable' ] ; then
    sed -i "s/disable.*/disable         = no/gI" /etc/xinetd.d/$xinetd
  else
    sed -i "s/disable.*/disable         = yes/gI" /etc/xinetd.d/$xinetd
  fi
fi

}

service_command enable iptables
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Enable service iptables
  service:
    name="{{item}}"
    enabled="yes"
    state="started"
  with_items:
    - iptables

Strengthen the Default Rulesetgroup

The default rules can be strengthened. The system scripts that activate the firewall rules expect them to be defined in the configuration files iptables and ip6tables in the directory /etc/sysconfig. Many of the lines in these files are similar to the command line arguments that would be provided to the programs /sbin/iptables or /sbin/ip6tables - but some are quite different.

The following recommendations describe how to strengthen the default ruleset configuration file. An alternative to editing this configuration file is to create a shell script that makes calls to the iptables program to load in rules, and then invokes service iptables save to write those loaded rules to /etc/sysconfig/iptables.

The following alterations can be made directly to /etc/sysconfig/iptables and /etc/sysconfig/ip6tables. Instructions apply to both unless otherwise noted. Language and address conventions for regular iptables are used throughout this section; configuration for ip6tables will be either analogous or explicitly covered.

warning  The program system-config-securitylevel allows additional services to penetrate the default firewall rules and automatically adjusts /etc/sysconfig/iptables. This program is only useful if the default ruleset meets your security requirements. Otherwise, this program should not be used to make changes to the firewall configuration because it re-writes the saved configuration file.
contains 2 rules

Set Default iptables Policy for Incoming Packetsrule

To set the default policy to DROP (instead of ACCEPT) for the built-in INPUT chain which processes incoming packets, add or correct the following line in /etc/sysconfig/iptables:

:INPUT DROP [0:0]

Rationale:

In iptables the default policy is applied only after all the applicable rules in the table are examined for a match. Setting the default policy to DROP implements proper design for a firewall, i.e. any packets which are not explicitly permitted should not be accepted.

identifiers:  CCE-26444-0, DISA FSO RHEL-06-000120

references:  CM-7, 66, 1109, 1154, 1414

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
sed -i 's/^:INPUT ACCEPT.*/:INPUT DROP [0:0]/g' /etc/sysconfig/iptables

Set Default iptables Policy for Forwarded Packetsrule

To set the default policy to DROP (instead of ACCEPT) for the built-in FORWARD chain which processes packets that will be forwarded from one interface to another, add or correct the following line in /etc/sysconfig/iptables:

:FORWARD DROP [0:0]

Rationale:

In iptables, the default policy is applied only after all the applicable rules in the table are examined for a match. Setting the default policy to DROP implements proper design for a firewall, i.e. any packets which are not explicitly permitted should not be accepted.

identifiers:  CCE-27186-6, DISA FSO RHEL-06-000320

references:  CM-7, 1109

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
sed -i 's/^:FORWARD ACCEPT.*/:FORWARD DROP [0:0]/g' /etc/sysconfig/iptables

Uncommon Network Protocolsgroup

The system includes support for several network protocols which are not commonly used. Although security vulnerabilities in kernel networking code are not frequently discovered, the consequences can be dramatic. Ensuring uncommon network protocols are disabled reduces the system's risk to attacks targeted at its implementation of those protocols.

warning  Although these protocols are not commonly used, avoid disruption in your network environment by ensuring they are not needed prior to disabling them.
contains 4 rules

Disable DCCP Supportrule

The Datagram Congestion Control Protocol (DCCP) is a relatively new transport layer protocol, designed to support streaming media and telephony. To configure the system to prevent the dccp kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d:

install dccp /bin/true

Rationale:

Disabling DCCP protects the system against exploitation of any flaws in its implementation.

identifiers:  CCE-26448-1, DISA FSO RHEL-06-000124

references:  CM-7, 382

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
echo "install dccp /bin/true" > /etc/modprobe.d/dccp.conf
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Ensure kernel module 'dccp' is disabled
  lineinfile:
    create=yes
    dest="/etc/modprobe.d/{{item}}.conf"
    regexp="{{item}}"
    line="install {{item}} /bin/true"
  with_items:
    - dccp

Disable SCTP Supportrule

The Stream Control Transmission Protocol (SCTP) is a transport layer protocol, designed to support the idea of message-oriented communication, with several streams of messages within one connection. To configure the system to prevent the sctp kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d:

install sctp /bin/true

Rationale:

Disabling SCTP protects the system against exploitation of any flaws in its implementation.

identifiers:  CCE-26410-1, DISA FSO RHEL-06-000125

references:  CM-7, 382

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
echo "install sctp /bin/true" > /etc/modprobe.d/sctp.conf
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Ensure kernel module 'sctp' is disabled
  lineinfile:
    create=yes
    dest="/etc/modprobe.d/{{item}}.conf"
    regexp="{{item}}"
    line="install {{item}} /bin/true"
  with_items:
    - sctp

Disable RDS Supportrule

The Reliable Datagram Sockets (RDS) protocol is a transport layer protocol designed to provide reliable high- bandwidth, low-latency communications between nodes in a cluster. To configure the system to prevent the rds kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d:

install rds /bin/true

Rationale:

Disabling RDS protects the system against exploitation of any flaws in its implementation.

identifiers:  CCE-26239-4, DISA FSO RHEL-06-000126

references:  CM-7, 382

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
echo "install rds /bin/true" > /etc/modprobe.d/rds.conf
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Ensure kernel module 'rds' is disabled
  lineinfile:
    create=yes
    dest="/etc/modprobe.d/{{item}}.conf"
    regexp="{{item}}"
    line="install {{item}} /bin/true"
  with_items:
    - rds

Disable TIPC Supportrule

The Transparent Inter-Process Communication (TIPC) protocol is designed to provide communications between nodes in a cluster. To configure the system to prevent the tipc kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d:

install tipc /bin/true

Rationale:

Disabling TIPC protects the system against exploitation of any flaws in its implementation.

identifiers:  CCE-26696-5, DISA FSO RHEL-06-000127

references:  CM-7, 382

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
echo "install tipc /bin/true" > /etc/modprobe.d/tipc.conf
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable
- name: Ensure kernel module 'tipc' is disabled
  lineinfile:
    create=yes
    dest="/etc/modprobe.d/{{item}}.conf"
    regexp="{{item}}"
    line="install {{item}} /bin/true"
  with_items:
    - tipc

IPSec Supportgroup

Support for Internet Protocol Security (IPsec) is provided in Red Hat Enterprise Linux 6 with openswan and libreswan packages respectively.

contains 1 rule

Install openswan or libreswan Packagerule

The openswan and libreswan packages provide an implementation of IPsec and IKE, which permits the creation of secure tunnels over untrusted networks. The openswan package can be installed with the following command:

$ sudo yum install openswan
The libreswan package can be installed with the following command:
$ sudo yum install libreswan

Rationale:

Providing the ability for remote users or systems to initiate a secure VPN connection protects information when it is transmitted over a wide area network.

identifiers:  CCE-27626-1, DISA FSO RHEL-06-000321

references:  AC-17, MA-4, SC-8, 1130, 1131

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

function package_command {

# Load function arguments into local variables
local package_operation=$1
local package=$2

# Check sanity of the input
if [ $# -ne "2" ]
then
  echo "Usage: package_command 'install/uninstall' 'rpm_package_name"
  echo "Aborting."
  exit 1
fi

# If dnf is installed, use dnf; otherwise, use yum
if [ -f "/usr/bin/dnf" ] ; then
  install_util="/usr/bin/dnf"
else
  install_util="/usr/bin/yum"
fi

if [ "$package_operation" != 'remove' ] ; then
  # If the rpm is not installed, install the rpm
  if ! /bin/rpm -q --quiet $package; then
    $install_util -y $package_operation $package
  fi
else
  # If the rpm is installed, uninstall the rpm
  if /bin/rpm -q --quiet $package; then
    $install_util -y $package_operation $package
  fi
fi

}

package_command install openswan
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Ensure openswan is installed
  package:
    name="{{item}}"
    state=present
  with_items:
    - openswan
Remediation Puppet snippet:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_openswan

class install_openswan {
  package { 'openswan':
    ensure => 'installed',
  }
}
Remediation script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=openswan

Configure Sysloggroup

The syslog service has been the default Unix logging mechanism for many years. It has a number of downsides, including inconsistent log format, lack of authentication for received messages, and lack of authentication, encryption, or reliable transport for messages sent over a network. However, due to its long history, syslog is a de facto standard which is supported by almost all Unix applications.

In Red Hat Enterprise Linux 6, rsyslog has replaced ksyslogd as the syslog daemon of choice, and it includes some additional security features such as reliable, connection-oriented (i.e. TCP) transmission of logs, the option to log to database formats, and the encryption of log data en route to a central logging server. This section discusses how to configure rsyslog for best effect, and how to use tools provided with the system to maintain and monitor logs.

contains 7 rules

Ensure Proper Configuration of Log Filesgroup

The file /etc/rsyslog.conf controls where log message are written. These are controlled by lines called rules, which consist of a selector and an action. These rules are often customized depending on the role of the system, the requirements of the environment, and whatever may enable the administrator to most effectively make use of log data. The default rules in Red Hat Enterprise Linux 6 are:

*.info;mail.none;authpriv.none;cron.none                /var/log/messages
authpriv.*                                              /var/log/secure
mail.*                                                  -/var/log/maillog
cron.*                                                  /var/log/cron
*.emerg                                                 *
uucp,news.crit                                          /var/log/spooler
local7.*                                                /var/log/boot.log
See the man page rsyslog.conf(5) for more information. Note that the rsyslog daemon can be configured to use a timestamp format that some log processing programs may not understand. If this occurs, edit the file /etc/rsyslog.conf and add or edit the following line:
$ ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat

contains 3 rules

Ensure Log Files Are Owned By Appropriate Userrule

The owner of all log files written by rsyslog should be root. These log files are determined by the second part of each Rule line in /etc/rsyslog.conf and typically all appear in /var/log. For each log file LOGFILE referenced in /etc/rsyslog.conf, run the following command to inspect the file's owner:

$ ls -l LOGFILE
If the owner is not root, run the following command to correct this:
$ sudo chown root LOGFILE

Rationale:

The log files generated by rsyslog contain valuable information regarding system configuration, user authentication, and other such information. Log files should be protected from unauthorized access.

identifiers:  CCE-26812-8, DISA FSO RHEL-06-000133

references:  AC-6, SI-11, 1314

Ensure Log Files Are Owned By Appropriate Grouprule

The group-owner of all log files written by rsyslog should be root. These log files are determined by the second part of each Rule line in /etc/rsyslog.conf and typically all appear in /var/log. For each log file LOGFILE referenced in /etc/rsyslog.conf, run the following command to inspect the file's group owner:

$ ls -l LOGFILE
If the owner is not root, run the following command to correct this:
$ sudo chgrp root LOGFILE

Rationale:

The log files generated by rsyslog contain valuable information regarding system configuration, user authentication, and other such information. Log files should be protected from unauthorized access.

identifiers:  CCE-26821-9, DISA FSO RHEL-06-000134

references:  AC-6, SI-11, 1314

Ensure System Log Files Have Correct Permissionsrule

The file permissions for all log files written by rsyslog should be set to 600, or more restrictive. These log files are determined by the second part of each Rule line in /etc/rsyslog.conf and typically all appear in /var/log. For each log file LOGFILE referenced in /etc/rsyslog.conf, run the following command to inspect the file's permissions:

$ ls -l LOGFILE
If the permissions are not 600 or more restrictive, run the following command to correct this:
$ sudo chmod 0600 LOGFILE

Rationale:

Log files can contain valuable information regarding system configuration. If the system log files are not protected unauthorized users could change the logged data, eliminating their forensic value.

identifiers:  CCE-27190-8, DISA FSO RHEL-06-000135

references:  SI-11, 1314

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

# 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)
RSYSLOG_INCLUDE_CONFIG=($(grep -e "\$IncludeConfig[[:space:]]\+[^[:space:];]\+" /etc/rsyslog.conf | cut -d ' ' -f 2))
# Declare an array to hold the final list of different log file paths
declare -a LOG_FILE_PATHS

# 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_ETC_CONFIG}" "${RSYSLOG_INCLUDE_CONFIG[@]}"
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,
	# * 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 doesn't contain space (' '), colon (':'), and semicolon (';') characters
	# Search log file for path(s) only in case it exists!
	if [[ -f "${LOG_FILE}" ]]
	then
		MATCHED_ITEMS=$(sed -e "/^[[:space:]|#|$]/d ; s/[^\/]*[[:space:]]*\([^:;[:space:]]*\)/\1/g ; /^$/d" "${LOG_FILE}")
		# 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=("${LOG_FILE_PATHS[@]}" "${ARRAY_FOR_LOG_FILE[@]}")
		# Delete the temporary array
		unset ARRAY_FOR_LOG_FILE
	fi
done

for PATH in "${LOG_FILE_PATHS[@]}"
do
	# Sanity check - if particular $PATH is empty string, skip it from further processing
	if [ -z "$PATH" ]
	then
		continue
	fi
	# Per https://access.redhat.com/solutions/66805 '/var/log/boot.log' log file needs special care => perform it
	if [ "$PATH" == "/var/log/boot.log" ]
	then
		# Ensure permissions of /var/log/boot.log are configured to be updated in /etc/rc.local
		if ! /bin/grep -q "boot.log" "/etc/rc.local"
		then
			echo "/bin/chmod 600 /var/log/boot.log" >> /etc/rc.local
		fi
		# Ensure /etc/rc.d/rc.local has user-executable permission
		# (in order to be actually executed during boot)
		if [ "$(/usr/bin/stat -c %a /etc/rc.d/rc.local)" -ne 744 ]
		then
			/bin/chmod u+x /etc/rc.d/rc.local
		fi
	fi
	# Also for each log file check if its permissions differ from 600. If so, correct them
	if [ "$(/usr/bin/stat -c %a "$PATH")" -ne 600 ]
	then
		/bin/chmod 600 "$PATH"
	fi
done

Rsyslog Logs Sent To Remote Hostgroup

If system logs are to be useful in detecting malicious activities, it is necessary to send logs to a remote server. An intruder who has compromised the root account on a machine may delete the log entries which indicate that the system was attacked before they are seen by an administrator.

However, it is recommended that logs be stored on the local host in addition to being sent to the loghost, especially if rsyslog has been configured to use the UDP protocol to send messages over a network. UDP does not guarantee reliable delivery, and moderately busy sites will lose log messages occasionally, especially in periods of high traffic which may be the result of an attack. In addition, remote rsyslog messages are not authenticated in any way by default, so it is easy for an attacker to introduce spurious messages to the central log server. Also, some problems cause loss of network connectivity, which will prevent the sending of messages to the central server. For all of these reasons, it is better to store log messages both centrally and on each host, so that they can be correlated if necessary.

contains 1 rule

Ensure Logs Sent To Remote Hostrule

To configure rsyslog to send logs to a remote log server, open /etc/rsyslog.conf and read and understand the last section of the file, which describes the multiple directives necessary to activate remote logging. Along with these other directives, the system can be configured to forward its logs to a particular log server by adding or correcting one of the following lines, substituting loghost.example.com appropriately. The choice of protocol depends on the environment of the system; although TCP and RELP provide more reliable message delivery, they may not be supported in all environments.
To use UDP for log message delivery:

*.* @loghost.example.com

To use TCP for log message delivery:
*.* @@loghost.example.com

To use RELP for log message delivery:
*.* :omrelp:loghost.example.com

Rationale:

A log server (loghost) receives syslog messages from one or more systems. This data can be used as an additional log source in the event a system is compromised and its local logs are suspect. Forwarding log messages to a remote loghost also provides system administrators with a centralized place to view the status of multiple hosts within the enterprise.

identifiers:  CCE-26801-1, DISA FSO RHEL-06-000136

references:  AU-3(2), AU-9, 1348, 136

Ensure All Logs are Rotated by logrotategroup

Edit the file /etc/logrotate.d/syslog. Find the first line, which should look like this (wrapped for clarity):

/var/log/messages /var/log/secure /var/log/maillog /var/log/spooler \
  /var/log/boot.log /var/log/cron {
Edit this line so that it contains a one-space-separated listing of each log file referenced in /etc/rsyslog.conf.

All logs in use on a system must be rotated regularly, or the log files will consume disk space over time, eventually interfering with system operation. The file /etc/logrotate.d/syslog is the configuration file used by the logrotate program to maintain all log files written by syslog. By default, it rotates logs weekly and stores four archival copies of each log. These settings can be modified by editing /etc/logrotate.conf, but the defaults are sufficient for purposes of this guide.

Note that logrotate is run nightly by the cron job /etc/cron.daily/logrotate. If particularly active logs need to be rotated more often than once a day, some other mechanism must be used.

contains 1 rule

Ensure Logrotate Runs Periodicallyrule

The logrotate utility allows for the automatic rotation of log files. The frequency of rotation is specified in /etc/logrotate.conf, which triggers a cron task. To configure logrotate to run daily, add or correct the following line in /etc/logrotate.conf:

# rotate log files frequency
daily

Rationale:

Log files that are not properly rotated run the risk of growing so large that they fill up the /var/log partition. Valuable logging information could be lost if the /var/log partition becomes full.

identifiers:  CCE-27014-0, DISA FSO RHEL-06-000138

references:  AU-9, 366

Ensure rsyslog is Installedrule

Rsyslog is installed by default. The rsyslog package can be installed with the following command:

$ sudo yum install rsyslog

Rationale:

The rsyslog package provides the rsyslog daemon, which provides system logging services.

identifiers:  CCE-26809-4

references:  AU-9(2), 1311, 1312

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

function package_command {

# Load function arguments into local variables
local package_operation=$1
local package=$2

# Check sanity of the input
if [ $# -ne "2" ]
then
  echo "Usage: package_command 'install/uninstall' 'rpm_package_name"
  echo "Aborting."
  exit 1
fi

# If dnf is installed, use dnf; otherwise, use yum
if [ -f "/usr/bin/dnf" ] ; then
  install_util="/usr/bin/dnf"
else
  install_util="/usr/bin/yum"
fi

if [ "$package_operation" != 'remove' ] ; then
  # If the rpm is not installed, install the rpm
  if ! /bin/rpm -q --quiet $package; then
    $install_util -y $package_operation $package
  fi
else
  # If the rpm is installed, uninstall the rpm
  if /bin/rpm -q --quiet $package; then
    $install_util -y $package_operation $package
  fi
fi

}

package_command install rsyslog
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Ensure rsyslog is installed
  package:
    name="{{item}}"
    state=present
  with_items:
    - rsyslog
Remediation Puppet snippet:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
include install_rsyslog

class install_rsyslog {
  package { 'rsyslog':
    ensure => 'installed',
  }
}
Remediation script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

package --add=rsyslog

Enable rsyslog Servicerule

The rsyslog service provides syslog-style logging by default on Red Hat Enterprise Linux 6. The rsyslog service can be enabled with the following command:

$ sudo chkconfig --level 2345 rsyslog on

Rationale:

The rsyslog service must be running in order to provide logging services, which are essential to system administration.

identifiers:  CCE-26807-8

references:  AU-12, 1557, 1312, 1311

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

function service_command {

# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)

# Check sanity of the input
if [ $# -lt "2" ]
then
  echo "Usage: service_command 'enable/disable' 'service_name.service'"
  echo
  echo "To enable or disable xinetd services add \'xinetd=service_name\'"
  echo "as the last argument"
  echo "Aborting."
  exit 1
fi

# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
  service_util="/usr/bin/systemctl"
else
  service_util="/sbin/service"
  chkconfig_util="/sbin/chkconfig"
fi

# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
  service_state="enable"
  service_operation="start"
  chkconfig_state="on"
else
  service_state="disable"
  service_operation="stop"
  chkconfig_state="off"
fi

# If chkconfig_util is not empty, use chkconfig/service commands.
if ! [ "x$chkconfig_util" = x ] ; then
  $service_util $service $service_operation
  $chkconfig_util --level 0123456 $service $chkconfig_state
else
  $service_util $service_operation $service
  $service_util $service_state $service
fi

# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if ! [ "x$xinetd" = x ] ; then
  grep -qi disable /etc/xinetd.d/$xinetd && \

  if ! [ "$service_operation" != 'disable' ] ; then
    sed -i "s/disable.*/disable         = no/gI" /etc/xinetd.d/$xinetd
  else
    sed -i "s/disable.*/disable         = yes/gI" /etc/xinetd.d/$xinetd
  fi
fi

}

service_command enable rsyslog
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Enable service rsyslog
  service:
    name="{{item}}"
    enabled="yes"
    state="started"
  with_items:
    - rsyslog

System Accounting with auditdgroup

The audit service provides substantial capabilities for recording system activities. By default, the service audits about SELinux AVC denials and certain types of security-relevant events such as system logins, account modifications, and authentication events performed by programs such as sudo. Under its default configuration, auditd has modest disk space requirements, and should not noticeably impact system performance.

Government networks often have substantial auditing requirements and auditd can be configured to meet these requirements. Examining some example audit records demonstrates how the Linux audit system satisfies common requirements. The following example from Fedora Documentation available at http://docs.fedoraproject.org/en-US/Fedora/13/html/Security-Enhanced_Linux/sect-Security-Enhanced_Linux-Fixing_Problems-Raw_Audit_Messages.html shows the substantial amount of information captured in a two typical "raw" audit messages, followed by a breakdown of the most important fields. In this example the message is SELinux-related and reports an AVC denial (and the associated system call) that occurred when the Apache HTTP Server attempted to access the /var/www/html/file1 file (labeled with the samba_share_t type):

type=AVC msg=audit(1226874073.147:96): avc:  denied  { getattr } for pid=2465 comm="httpd"
path="/var/www/html/file1" dev=dm-0 ino=284133 scontext=unconfined_u:system_r:httpd_t:s0 
tcontext=unconfined_u:object_r:samba_share_t:s0 tclass=file

type=SYSCALL msg=audit(1226874073.147:96): arch=40000003 syscall=196 success=no exit=-13 
a0=b98df198 a1=bfec85dc a2=54dff4 a3=2008171 items=0 ppid=2463 pid=2465 auid=502 uid=48
gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=6 comm="httpd"
exe="/usr/sbin/httpd" subj=unconfined_u:system_r:httpd_t:s0 key=(null)
  • msg=audit(1226874073.147:96)
    • The number in parentheses is the unformatted time stamp (Epoch time) for the event, which can be converted to standard time by using the date command.
  • { getattr }
    • The item in braces indicates the permission that was denied. getattr indicates the source process was trying to read the target file's status information. This occurs before reading files. This action is denied due to the file being accessed having the wrong label. Commonly seen permissions include getattr, read, and write.
  • comm="httpd"
    • The executable that launched the process. The full path of the executable is found in the exe= section of the system call (SYSCALL) message, which in this case, is exe="/usr/sbin/httpd".
  • path="/var/www/html/file1"
    • The path to the object (target) the process attempted to access.
  • scontext="unconfined_u:system_r:httpd_t:s0"
    • The SELinux context of the process that attempted the denied action. In this case, it is the SELinux context of the Apache HTTP Server, which is running in the httpd_t domain.
  • tcontext="unconfined_u:object_r:samba_share_t:s0"
    • The SELinux context of the object (target) the process attempted to access. In this case, it is the SELinux context of file1. Note: the samba_share_t type is not accessible to processes running in the httpd_t domain.
  • From the system call (SYSCALL) message, two items are of interest:
    • success=no: indicates whether the denial (AVC) was enforced or not. success=no indicates the system call was not successful (SELinux denied access). success=yes indicates the system call was successful - this can be seen for permissive domains or unconfined domains, such as initrc_t and kernel_t.
    • exe="/usr/sbin/httpd": the full path to the executable that launched the process, which in this case, is exe="/usr/sbin/httpd".

contains 36 rules

Configure auditd Data Retentiongroup

The audit system writes data to /var/log/audit/audit.log. By default, auditd rotates 5 logs by size (6MB), retaining a maximum of 30MB of data in total, and refuses to write entries when the disk is too full. This minimizes the risk of audit data filling its partition and impacting other services. This also minimizes the risk of the audit daemon temporarily disabling the system if it cannot write audit log (which it can be configured to do). For a busy system or a system which is thoroughly auditing system activity, the default settings for data retention may be insufficient. The log file size needed will depend heavily on what types of events are being audited. First configure auditing to log all the events of interest. Then monitor the log size manually for awhile to determine what file size will allow you to keep the required data for the correct time period.

Using a dedicated partition for /var/log/audit prevents the auditd logs from disrupting system functionality if they fill, and, more importantly, prevents other activity in /var from filling the partition and stopping the audit trail. (The audit logs are size-limited and therefore unlikely to grow without bound unless configured to do so.) Some machines may have requirements that no actions occur which cannot be audited. If this is the case, then auditd can be configured to halt the machine if it runs out of space. Note: Since older logs are rotated, configuring auditd this way does not prevent older logs from being rotated away before they can be viewed. If your system is configured to halt when logging cannot be performed, make sure this can never happen under normal circumstances! Ensure that /var/log/audit is on its own partition, and that this partition is larger than the maximum amount of data auditd will retain normally.

references:  AU-11, 138

contains 6 rules

Configure auditd Number of Logs Retainedrule

Determine how many log files auditd should retain when it rotates logs. Edit the file /etc/audit/auditd.conf. Add or modify the following line, substituting NUMLOGS with the correct value of 5:

num_logs = NUMLOGS
Set the value to 5 for general-purpose systems. Note that values less than 2 result in no log rotation.

Rationale:

The total storage for audit log files must be large enough to retain log information over the period required. This is a function of the maximum log file size and the number of logs retained.

identifiers:  CCE-27522-2, DISA FSO RHEL-06-000159

references:  AU-1(b), AU-11, IR-5

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

var_auditd_num_logs="5"

AUDITCONFIG=/etc/audit/auditd.conf

grep -q ^num_logs $AUDITCONFIG && \
  sed -i 's/^num_logs.*/num_logs = '"$var_auditd_num_logs"'/g' $AUDITCONFIG
if ! [ $? -eq 0 ]; then
  echo "num_logs = $var_auditd_num_logs" >> $AUDITCONFIG
fi

Configure auditd Max Log File Sizerule

Determine the amount of audit data (in megabytes) which should be retained in each log file. Edit the file /etc/audit/auditd.conf. Add or modify the following line, substituting the correct value of 6 for STOREMB:

max_log_file = STOREMB
Set the value to 6 (MB) or higher for general-purpose systems. Larger values, of course, support retention of even more audit data.

Rationale:

The total storage for audit log files must be large enough to retain log information over the period required. This is a function of the maximum log file size and the number of logs retained.

identifiers:  CCE-27550-3, DISA FSO RHEL-06-000160

references:  AU-1(b), AU-11, IR-5

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

var_auditd_max_log_file="6"

AUDITCONFIG=/etc/audit/auditd.conf

grep -q ^max_log_file $AUDITCONFIG && \
  sed -i 's/^max_log_file.*/max_log_file = '"$var_auditd_max_log_file"'/g' $AUDITCONFIG
if ! [ $? -eq 0 ]; then
  echo "max_log_file = $var_auditd_max_log_file" >> $AUDITCONFIG
fi

Configure auditd max_log_file_action Upon Reaching Maximum Log Sizerule

The default action to take when the logs reach their maximum size is to rotate the log files, discarding the oldest one. To configure the action taken by auditd, add or correct the line in /etc/audit/auditd.conf:

max_log_file_action = ACTION
Possible values for ACTION are described in the auditd.conf man page. These include:
  • ignore
  • syslog
  • suspend
  • rotate
  • keep_logs
Set the ACTION to rotate to ensure log rotation occurs. This is the default. The setting is case-insensitive.

Rationale:

Automatically rotating logs (by setting this to rotate) minimizes the chances of the system unexpectedly running out of disk space by being overwhelmed with log data. However, for systems that must never discard log data, or which use external processes to transfer it and reclaim space, keep_logs can be employed.

identifiers:  CCE-27237-7, DISA FSO RHEL-06-000161

references:  AU-1(b), AU-4, AU-11, IR-5

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

var_auditd_max_log_file_action="rotate"

AUDITCONFIG=/etc/audit/auditd.conf

grep -q ^max_log_file_action $AUDITCONFIG && \
  sed -i 's/^max_log_file_action.*/max_log_file_action = '"$var_auditd_max_log_file_action"'/g' $AUDITCONFIG
if ! [ $? -eq 0 ]; then
  echo "max_log_file_action = $var_auditd_max_log_file_action" >> $AUDITCONFIG
fi

Configure auditd space_left Action on Low Disk Spacerule

The auditd service can be configured to take an action when disk space starts to run low. Edit the file /etc/audit/auditd.conf. Modify the following line, substituting ACTION appropriately:

space_left_action = ACTION
Possible values for ACTION are described in the auditd.conf man page. These include:
  • ignore
  • syslog
  • email
  • exec
  • suspend
  • single
  • halt
Set this to email (instead of the default, which is suspend) as it is more likely to get prompt attention. Acceptable values also include suspend, single, and halt.

Rationale:

Notifying administrators of an impending disk space problem may allow them to take corrective action prior to any disruption.

identifiers:  CCE-27238-5, DISA FSO RHEL-06-000005

references:  AU-1(b), AU-4, AU-5(b), IR-5, 140, 143

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

var_auditd_space_left_action="email"

#
# If space_left_action present in /etc/audit/auditd.conf, change value
# to var_auditd_space_left_action, else
# add "space_left_action = $var_auditd_space_left_action" to /etc/audit/auditd.conf
#

if grep --silent ^space_left_action /etc/audit/auditd.conf ; then
        sed -i 's/^space_left_action.*/space_left_action = '"$var_auditd_space_left_action"'/g' /etc/audit/auditd.conf
else
        echo -e "\n# Set space_left_action to $var_auditd_space_left_action per security requirements" >> /etc/audit/auditd.conf
        echo "space_left_action = $var_auditd_space_left_action" >> /etc/audit/auditd.conf
fi

Configure auditd admin_space_left Action on Low Disk Spacerule

The auditd service can be configured to take an action when disk space is running low but prior to running out of space completely. Edit the file /etc/audit/auditd.conf. Add or modify the following line, substituting ACTION appropriately:

admin_space_left_action = ACTION
Set this value to single to cause the system to switch to single-user mode for corrective action. Acceptable values also include suspend and halt. For certain systems, the need for availability outweighs the need to log all actions, and a different setting should be determined. Details regarding all possible values for ACTION are described in the auditd.conf man page.

Rationale:

Administrators should be made aware of an inability to record audit records. If a separate partition or logical volume of adequate size is used, running low on space for audit records should never occur.

identifiers:  CCE-27239-3

references:  AU-1(b), AU-4, AU-5(b), IR-5, 140, 1343

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

var_auditd_admin_space_left_action="single"

grep -q ^admin_space_left_action /etc/audit/auditd.conf && \
  sed -i "s/admin_space_left_action.*/admin_space_left_action = $var_auditd_admin_space_left_action/g" /etc/audit/auditd.conf
if ! [ $? -eq 0 ]; then
    echo "admin_space_left_action = $var_auditd_admin_space_left_action" >> /etc/audit/auditd.conf
fi

Configure auditd mail_acct Action on Low Disk Spacerule

The auditd service can be configured to send email to a designated account in certain situations. Add or correct the following line in /etc/audit/auditd.conf to ensure that administrators are notified via email for those situations:

action_mail_acct = root

Rationale:

Email sent to the root account is typically aliased to the administrators of the system, who can take appropriate action.

identifiers:  CCE-27241-9, DISA FSO RHEL-06-000313

references:  AU-1(b), AU-4, AU-5(a), IR-5, 139, 144

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable

var_auditd_action_mail_acct="root"

AUDITCONFIG=/etc/audit/auditd.conf

grep -q ^action_mail_acct $AUDITCONFIG && \
  sed -i 's/^action_mail_acct.*/action_mail_acct = '"$var_auditd_action_mail_acct"'/g' $AUDITCONFIG
if ! [ $? -eq 0 ]; then
  echo "action_mail_acct = $var_auditd_action_mail_acct" >> $AUDITCONFIG
fi

Configure auditd Rules for Comprehensive Auditinggroup

The auditd program can perform comprehensive monitoring of system activity. This section describes recommended configuration settings for comprehensive auditing, but a full description of the auditing system's capabilities is beyond the scope of this guide. The mailing list linux-audit@redhat.com exists to facilitate community discussion of the auditing system.

The audit subsystem supports extensive collection of events, including:

  • Tracing of arbitrary system calls (identified by name or number) on entry or exit.
  • Filtering by PID, UID, call success, system call argument (with some limitations), etc.
  • Monitoring of specific files for modifications to the file's contents or metadata.

Auditing rules at startup are controlled by the file /etc/audit/audit.rules. Add rules to it to meet the auditing requirements for your organization. Each line in /etc/audit/audit.rules represents a series of arguments that can be passed to auditctl and can be individually tested during runtime. See documentation in /usr/share/doc/audit-VERSION and in the related man pages for more details.

If copying any example audit rulesets from /usr/share/doc/audit-VERSION, be sure to comment out the lines containing arch= which are not appropriate for your system's architecture. Then review and understand the following rules, ensuring rules are activated as needed for the appropriate architecture.

After reviewing all the rules, reading the following sections, and editing as needed, the new rules can be activated as follows:
$ sudo service auditd restart

contains 28 rules

Records Events that Modify Date and Time Informationgroup

Arbitrary changes to the system time can be used to obfuscate nefarious activities in log files, as well as to confuse network services that are highly dependent upon an accurate system time. All changes to the system time should be audited.

contains 5 rules

Record attempts to alter time through adjtimexrule

On a 32-bit system, add the following to /etc/audit/audit.rules:

# audit_time_rules
-a always,exit -F arch=b32 -S adjtimex -k audit_time_rules
On a 64-bit system, add the following to /etc/audit/audit.rules:
# audit_time_rules
-a always,exit -F arch=b64 -S adjtimex -k audit_time_rules
The -k option allows for the specification of a key in string form that can be used for better reporting capability through ausearch and aureport. Multiple system calls can be defined on the same line to save space if desired, but is not required. See an example of multiple combined syscalls:
-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k audit_time_rules

Rationale:

Arbitrary changes to the system time can be used to obfuscate nefarious activities in log files, as well as to confuse network services that are highly dependent upon an accurate system time (such as sshd). All changes to the system time should be audited.

identifiers:  CCE-26242-8, DISA FSO RHEL-06-000165

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 1487, 169

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

function fix_audit_syscall_rule {

# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"

# Check sanity of the input
if [ $# -ne "5" ]
then
        echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
        echo "Aborting."
        exit 1
fi

# 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  |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect

# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
        echo "Unknown audit rules loading tool: $1. Aborting."
        echo "Use either 'auditctl' or 'augenrules'!"
        exit 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("${files_to_inspect[@]}" '/etc/audit/audit.rules' )
# 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
elif [ "$tool" == 'augenrules' ]
then
        # Extract audit $key from audit rule so we can use it later
        key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
        # Check if particular audit rule is already defined
        IFS=$'\n' matches=($(sed -s -n -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules))
        # Reset IFS back to default
        unset $IFS
        for match in "${matches[@]}"
        do
                files_to_inspect=("${files_to_inspect[@]}" "${match}")
        done
        # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
        if [ ${#files_to_inspect[@]} -eq "0" ]
        then
                files_to_inspect="/etc/audit/rules.d/$key.rules"
                if [ ! -e "$files_to_inspect" ]
                then
                        touch "$files_to_inspect"
                        chmod 0640 "$files_to_inspect"
                fi
        fi
fi

#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0

for audit_file in "${files_to_inspect[@]}"
do

        # Filter existing $audit_file rules' definitions to select those that:
        # * follow the rule pattern, and
        # * meet the hardware architecture requirement, and
        # * are current syscall group specific
        IFS=$'\n' existing_rules=($(sed -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d"  "$audit_file"))
        # Reset IFS back to default
        unset $IFS

        # Process rules found case-by-case
        for rule in "${existing_rules[@]}"
        do
                # Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
                if [ "${rule}" != "${full_rule}" ]
                then
                        # If so, isolate just '(-S \w)+' substring of that rule
                        rule_syscalls=$(echo $rule | grep -o -P '(-S \w+ )+')
                        # Check if list of '-S syscall' arguments of that rule is subset
                        # of '-S syscall' list of expected $full_rule
                        if grep -q -- "$rule_syscalls" <<< "$full_rule"
                        then
                                # Rule is covered (i.e. the list of -S syscalls for this rule is
                                # subset of -S syscalls of $full_rule => existing rule can be deleted
                                # Thus delete the rule from audit.rules & our array
                                sed -i -e "/$rule/d" "$audit_file"
                                existing_rules=("${existing_rules[@]//$rule/}")
                        else
                                # Rule isn't covered by $full_rule - it besides -S syscall arguments
                                # for this group contains also -S syscall arguments for other syscall
                                # group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
                                # since 'lchown' & 'fchownat' share 'chown' substring
                                # Therefore:
                                # * 1) delete the original rule from audit.rules
                                # (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
                                # * 2) delete the -S syscall arguments for this syscall group, but
                                # keep those not belonging to this syscall group
                                # (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
                                # * 3) append the modified (filtered) rule again into audit.rules
                                # if the same rule not already present
                                #
                                # 1) Delete the original rule
                                sed -i -e "/$rule/d" "$audit_file"
                                # 2) Delete syscalls for this group, but keep those from other groups
                                # Convert current rule syscall's string into array splitting by '-S' delimiter
                                IFS=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
                                # Reset IFS back to default
                                unset $IFS
                                # Declare new empty string to hold '-S syscall' arguments from other groups
                                new_syscalls_for_rule=''
                                # Walk through existing '-S syscall' arguments
                                for syscall_arg in "${rule_syscalls_as_array[@]}"
                                do
                                        # Skip empty $syscall_arg values
                                        if [ "$syscall_arg" == '' ]
                                        then
                                                continue
                                        fi
                                        # If the '-S syscall' doesn't belong to current group add it to the new list
                                        # (together with adding '-S' delimiter back for each of such item found)
                                        if grep -q -v -- "$group" <<< "$syscall_arg"
                                        then
                                                new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
                                        fi
                                done
                                # Replace original '-S syscall' list with the new one for this rule
                                updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
                                # Squeeze repeated whitespace characters in rule definition (if any) into one
                                updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
                                # 3) Append the modified / filtered rule again into audit.rules
                                #    (but only in case it's not present yet to prevent duplicate definitions)
                                if ! grep -q -- "$updated_rule" "$audit_file"
                                then
                                        echo "$updated_rule" >> "$audit_file"
                                fi
                        fi
                else
                        # $audit_file already contains the expected rule form for this
                        # architecture & key => don't insert it second time
                        append_expected_rule=1
                fi
        done

        # We deleted all rules that were subset of the expected one for this arch & key.
        # Also isolated rules containing system calls not from this system calls group.
        # Now append the expected rule if it's not present in $audit_file yet
        if [[ ${append_expected_rule} -eq "0" ]]
        then
                echo "$full_rule" >> "$audit_file"
        fi
done

}

function rhel6_perform_audit_adjtimex_settimeofday_stime_remediation {

# Perform the remediation for the 'adjtimex', 'settimeofday', and 'stime' audit
# system calls on Red Hat Enterprise Linux 6 OS
#
# Retrieve hardware architecture of the underlying system
[ $(getconf LONG_BIT) = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
        PATTERN="-a always,exit -F arch=${ARCH} -S .* -k *"
        # Create expected audit group and audit rule form for particular system call & architecture
        if [ ${ARCH} = "b32" ]
        then
                # 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
                GROUP="\(adjtimex\|settimeofday\|stime\)"
                FULL_RULE="-a always,exit -F arch=${ARCH} -S adjtimex -S settimeofday -S stime -k audit_time_rules"
        elif [ ${ARCH} = "b64" ]
        then
                # 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
                GROUP="\(adjtimex\|settimeofday\)"
                FULL_RULE="-a always,exit -F arch=${ARCH} -S adjtimex -S settimeofday -k audit_time_rules"
        fi
        # Perform the remediation itself
        fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done

}

rhel6_perform_audit_adjtimex_settimeofday_stime_remediation

Record attempts to alter time through settimeofdayrule

On a 32-bit system, add the following to /etc/audit/audit.rules:

# audit_time_rules
-a always,exit -F arch=b32 -S settimeofday -k audit_time_rules
On a 64-bit system, add the following to /etc/audit/audit.rules:
# audit_time_rules
-a always,exit -F arch=b64 -S settimeofday -k audit_time_rules
The -k option allows for the specification of a key in string form that can be used for better reporting capability through ausearch and aureport. Multiple system calls can be defined on the same line to save space if desired, but is not required. See an example of multiple combined syscalls:
-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k audit_time_rules

Rationale:

Arbitrary changes to the system time can be used to obfuscate nefarious activities in log files, as well as to confuse network services that are highly dependent upon an accurate system time (such as sshd). All changes to the system time should be audited.

identifiers:  CCE-27203-9, DISA FSO RHEL-06-000167

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 1487, 169

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

function fix_audit_syscall_rule {

# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"

# Check sanity of the input
if [ $# -ne "5" ]
then
        echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
        echo "Aborting."
        exit 1
fi

# 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  |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect

# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
        echo "Unknown audit rules loading tool: $1. Aborting."
        echo "Use either 'auditctl' or 'augenrules'!"
        exit 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("${files_to_inspect[@]}" '/etc/audit/audit.rules' )
# 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
elif [ "$tool" == 'augenrules' ]
then
        # Extract audit $key from audit rule so we can use it later
        key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
        # Check if particular audit rule is already defined
        IFS=$'\n' matches=($(sed -s -n -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules))
        # Reset IFS back to default
        unset $IFS
        for match in "${matches[@]}"
        do
                files_to_inspect=("${files_to_inspect[@]}" "${match}")
        done
        # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
        if [ ${#files_to_inspect[@]} -eq "0" ]
        then
                files_to_inspect="/etc/audit/rules.d/$key.rules"
                if [ ! -e "$files_to_inspect" ]
                then
                        touch "$files_to_inspect"
                        chmod 0640 "$files_to_inspect"
                fi
        fi
fi

#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0

for audit_file in "${files_to_inspect[@]}"
do

        # Filter existing $audit_file rules' definitions to select those that:
        # * follow the rule pattern, and
        # * meet the hardware architecture requirement, and
        # * are current syscall group specific
        IFS=$'\n' existing_rules=($(sed -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d"  "$audit_file"))
        # Reset IFS back to default
        unset $IFS

        # Process rules found case-by-case
        for rule in "${existing_rules[@]}"
        do
                # Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
                if [ "${rule}" != "${full_rule}" ]
                then
                        # If so, isolate just '(-S \w)+' substring of that rule
                        rule_syscalls=$(echo $rule | grep -o -P '(-S \w+ )+')
                        # Check if list of '-S syscall' arguments of that rule is subset
                        # of '-S syscall' list of expected $full_rule
                        if grep -q -- "$rule_syscalls" <<< "$full_rule"
                        then
                                # Rule is covered (i.e. the list of -S syscalls for this rule is
                                # subset of -S syscalls of $full_rule => existing rule can be deleted
                                # Thus delete the rule from audit.rules & our array
                                sed -i -e "/$rule/d" "$audit_file"
                                existing_rules=("${existing_rules[@]//$rule/}")
                        else
                                # Rule isn't covered by $full_rule - it besides -S syscall arguments
                                # for this group contains also -S syscall arguments for other syscall
                                # group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
                                # since 'lchown' & 'fchownat' share 'chown' substring
                                # Therefore:
                                # * 1) delete the original rule from audit.rules
                                # (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
                                # * 2) delete the -S syscall arguments for this syscall group, but
                                # keep those not belonging to this syscall group
                                # (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
                                # * 3) append the modified (filtered) rule again into audit.rules
                                # if the same rule not already present
                                #
                                # 1) Delete the original rule
                                sed -i -e "/$rule/d" "$audit_file"
                                # 2) Delete syscalls for this group, but keep those from other groups
                                # Convert current rule syscall's string into array splitting by '-S' delimiter
                                IFS=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
                                # Reset IFS back to default
                                unset $IFS
                                # Declare new empty string to hold '-S syscall' arguments from other groups
                                new_syscalls_for_rule=''
                                # Walk through existing '-S syscall' arguments
                                for syscall_arg in "${rule_syscalls_as_array[@]}"
                                do
                                        # Skip empty $syscall_arg values
                                        if [ "$syscall_arg" == '' ]
                                        then
                                                continue
                                        fi
                                        # If the '-S syscall' doesn't belong to current group add it to the new list
                                        # (together with adding '-S' delimiter back for each of such item found)
                                        if grep -q -v -- "$group" <<< "$syscall_arg"
                                        then
                                                new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
                                        fi
                                done
                                # Replace original '-S syscall' list with the new one for this rule
                                updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
                                # Squeeze repeated whitespace characters in rule definition (if any) into one
                                updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
                                # 3) Append the modified / filtered rule again into audit.rules
                                #    (but only in case it's not present yet to prevent duplicate definitions)
                                if ! grep -q -- "$updated_rule" "$audit_file"
                                then
                                        echo "$updated_rule" >> "$audit_file"
                                fi
                        fi
                else
                        # $audit_file already contains the expected rule form for this
                        # architecture & key => don't insert it second time
                        append_expected_rule=1
                fi
        done

        # We deleted all rules that were subset of the expected one for this arch & key.
        # Also isolated rules containing system calls not from this system calls group.
        # Now append the expected rule if it's not present in $audit_file yet
        if [[ ${append_expected_rule} -eq "0" ]]
        then
                echo "$full_rule" >> "$audit_file"
        fi
done

}

function rhel6_perform_audit_adjtimex_settimeofday_stime_remediation {

# Perform the remediation for the 'adjtimex', 'settimeofday', and 'stime' audit
# system calls on Red Hat Enterprise Linux 6 OS
#
# Retrieve hardware architecture of the underlying system
[ $(getconf LONG_BIT) = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
        PATTERN="-a always,exit -F arch=${ARCH} -S .* -k *"
        # Create expected audit group and audit rule form for particular system call & architecture
        if [ ${ARCH} = "b32" ]
        then
                # 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
                GROUP="\(adjtimex\|settimeofday\|stime\)"
                FULL_RULE="-a always,exit -F arch=${ARCH} -S adjtimex -S settimeofday -S stime -k audit_time_rules"
        elif [ ${ARCH} = "b64" ]
        then
                # 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
                GROUP="\(adjtimex\|settimeofday\)"
                FULL_RULE="-a always,exit -F arch=${ARCH} -S adjtimex -S settimeofday -k audit_time_rules"
        fi
        # Perform the remediation itself
        fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done

}

rhel6_perform_audit_adjtimex_settimeofday_stime_remediation

Record Attempts to Alter Time Through stimerule

Add the following line to /etc/audit/audit.rules for both 32-bit and 64-bit systems:

# audit_time_rules
-a always,exit -F arch=b32 -S stime -k audit_time_rules
Since the 64-bit version of the "stime" system call is not defined in the audit lookup table, the corresponding "-F arch=b64" form of this rule is not expected to be defined on 64-bit systems (the aforementioned "-F arch=b32" stime rule form itself is sufficient for both 32-bit and 64-bit systems). The -k option allows for the specification of a key in string form that can be used for better reporting capability through ausearch and aureport. Multiple system calls can be defined on the same line to save space if desired, but is not required. See an example of multiple combined syscalls:
-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k audit_time_rules

Rationale:

Arbitrary changes to the system time can be used to obfuscate nefarious activities in log files, as well as to confuse network services that are highly dependent upon an accurate system time (such as sshd). All changes to the system time should be audited.

identifiers:  CCE-27169-2, DISA FSO RHEL-06-000169

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 1487, 169

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable

function fix_audit_syscall_rule {

# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"

# Check sanity of the input
if [ $# -ne "5" ]
then
        echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
        echo "Aborting."
        exit 1
fi

# 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  |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect

# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
        echo "Unknown audit rules loading tool: $1. Aborting."
        echo "Use either 'auditctl' or 'augenrules'!"
        exit 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("${files_to_inspect[@]}" '/etc/audit/audit.rules' )
# 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
elif [ "$tool" == 'augenrules' ]
then
        # Extract audit $key from audit rule so we can use it later
        key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
        # Check if particular audit rule is already defined
        IFS=$'\n' matches=($(sed -s -n -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules))
        # Reset IFS back to default
        unset $IFS
        for match in "${matches[@]}"
        do
                files_to_inspect=("${files_to_inspect[@]}" "${match}")
        done
        # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
        if [ ${#files_to_inspect[@]} -eq "0" ]
        then
                files_to_inspect="/etc/audit/rules.d/$key.rules"
                if [ ! -e "$files_to_inspect" ]
                then
                        touch "$files_to_inspect"
                        chmod 0640 "$files_to_inspect"
                fi
        fi
fi

#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0

for audit_file in "${files_to_inspect[@]}"
do

        # Filter existing $audit_file rules' definitions to select those that:
        # * follow the rule pattern, and
        # * meet the hardware architecture requirement, and
        # * are current syscall group specific
        IFS=$'\n' existing_rules=($(sed -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d"  "$audit_file"))
        # Reset IFS back to default
        unset $IFS

        # Process rules found case-by-case
        for rule in "${existing_rules[@]}"
        do
                # Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
                if [ "${rule}" != "${full_rule}" ]
                then
                        # If so, isolate just '(-S \w)+' substring of that rule
                        rule_syscalls=$(echo $rule | grep -o -P '(-S \w+ )+')
                        # Check if list of '-S syscall' arguments of that rule is subset
                        # of '-S syscall' list of expected $full_rule
                        if grep -q -- "$rule_syscalls" <<< "$full_rule"
                        then
                                # Rule is covered (i.e. the list of -S syscalls for this rule is
                                # subset of -S syscalls of $full_rule => existing rule can be deleted
                                # Thus delete the rule from audit.rules & our array
                                sed -i -e "/$rule/d" "$audit_file"
                                existing_rules=("${existing_rules[@]//$rule/}")
                        else
                                # Rule isn't covered by $full_rule - it besides -S syscall arguments
                                # for this group contains also -S syscall arguments for other syscall
                                # group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
                                # since 'lchown' & 'fchownat' share 'chown' substring
                                # Therefore:
                                # * 1) delete the original rule from audit.rules
                                # (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
                                # * 2) delete the -S syscall arguments for this syscall group, but
                                # keep those not belonging to this syscall group
                                # (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
                                # * 3) append the modified (filtered) rule again into audit.rules
                                # if the same rule not already present
                                #
                                # 1) Delete the original rule
                                sed -i -e "/$rule/d" "$audit_file"
                                # 2) Delete syscalls for this group, but keep those from other groups
                                # Convert current rule syscall's string into array splitting by '-S' delimiter
                                IFS=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
                                # Reset IFS back to default
                                unset $IFS
                                # Declare new empty string to hold '-S syscall' arguments from other groups
                                new_syscalls_for_rule=''
                                # Walk through existing '-S syscall' arguments
                                for syscall_arg in "${rule_syscalls_as_array[@]}"
                                do
                                        # Skip empty $syscall_arg values
                                        if [ "$syscall_arg" == '' ]
                                        then
                                                continue
                                        fi
                                        # If the '-S syscall' doesn't belong to current group add it to the new list
                                        # (together with adding '-S' delimiter back for each of such item found)
                                        if grep -q -v -- "$group" <<< "$syscall_arg"
                                        then
                                                new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
                                        fi
                                done
                                # Replace original '-S syscall' list with the new one for this rule
                                updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
                                # Squeeze repeated whitespace characters in rule definition (if any) into one
                                updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
                                # 3) Append the modified / filtered rule again into audit.rules
                                #    (but only in case it's not present yet to prevent duplicate definitions)
                                if ! grep -q -- "$updated_rule" "$audit_file"
                                then
                                        echo "$updated_rule" >> "$audit_file"
                                fi
                        fi
                else
                        # $audit_file already contains the expected rule form for this
                        # architecture & key => don't insert it second time
                        append_expected_rule=1
                fi
        done

        # We deleted all rules that were subset of the expected one for this arch & key.
        # Also isolated rules containing system calls not from this system calls group.
        # Now append the expected rule if it's not present in $audit_file yet
        if [[ ${append_expected_rule} -eq "0" ]]
        then
                echo "$full_rule" >> "$audit_file"
        fi
done

}

function rhel6_perform_audit_adjtimex_settimeofday_stime_remediation {

# Perform the remediation for the 'adjtimex', 'settimeofday', and 'stime' audit
# system calls on Red Hat Enterprise Linux 6 OS
#
# Retrieve hardware architecture of the underlying system
[ $(getconf LONG_BIT) = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do
        PATTERN="-a always,exit -F arch=${ARCH} -S .* -k *"
        # Create expected audit group and audit rule form for particular system call & architecture
        if [ ${ARCH} = "b32" ]
        then
                # 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
                GROUP="\(adjtimex\|settimeofday\|stime\)"
                FULL_RULE="-a always,exit -F arch=${ARCH} -S adjtimex -S settimeofday -S stime -k audit_time_rules"
        elif [ ${ARCH} = "b64" ]
        then
                # 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
                GROUP="\(adjtimex\|settimeofday\)"
                FULL_RULE="-a always,exit -F arch=${ARCH} -S adjtimex -S settimeofday -k audit_time_rules"
        fi
        # Perform the remediation itself
        fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done

}

rhel6_perform_audit_adjtimex_settimeofday_stime_remediation

Record Attempts to Alter Time Through clock_settimerule

On a 32-bit system, add the following to /etc/audit/audit.rules:

# time-change
-a always,exit -F arch=b32 -S clock_settime -F a0=0x0 -F key=time-change
On a 64-bit system, add the following to /etc/audit/audit.rules:
# time-change
-a always,exit -F arch=b64 -S clock_settime -F a0=0x0 -F key=time-change
The -k option allows for the specification of a key in string form that can be used for better reporting capability through ausearch and aureport. Multiple system calls can be defined on the same line to save space if desired, but is not required. See an example of multiple combined syscalls:
-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k audit_time_rules

Rationale:

Arbitrary changes to the system time can be used to obfuscate nefarious activities in log files, as well as to confuse network services that are highly dependent upon an accurate system time (such as sshd). All changes to the system time should be audited.

identifiers:  CCE-27170-0, DISA FSO RHEL-06-000171

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 1487, 169

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


# 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
	PATTERN="-a always,exit -F arch=$ARCH -S clock_settime -F a0=.* \(-F key=\|-k \).*"
	GROUP="clock_settime"
	FULL_RULE="-a always,exit -F arch=$ARCH -S clock_settime -F a0=0x0 -k time-change"

function fix_audit_syscall_rule {

# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"

# Check sanity of the input
if [ $# -ne "5" ]
then
        echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
        echo "Aborting."
        exit 1
fi

# 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  |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect

# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
        echo "Unknown audit rules loading tool: $1. Aborting."
        echo "Use either 'auditctl' or 'augenrules'!"
        exit 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("${files_to_inspect[@]}" '/etc/audit/audit.rules' )
# 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
elif [ "$tool" == 'augenrules' ]
then
        # Extract audit $key from audit rule so we can use it later
        key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
        # Check if particular audit rule is already defined
        IFS=$'\n' matches=($(sed -s -n -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules))
        # Reset IFS back to default
        unset $IFS
        for match in "${matches[@]}"
        do
                files_to_inspect=("${files_to_inspect[@]}" "${match}")
        done
        # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
        if [ ${#files_to_inspect[@]} -eq "0" ]
        then
                files_to_inspect="/etc/audit/rules.d/$key.rules"
                if [ ! -e "$files_to_inspect" ]
                then
                        touch "$files_to_inspect"
                        chmod 0640 "$files_to_inspect"
                fi
        fi
fi

#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0

for audit_file in "${files_to_inspect[@]}"
do

        # Filter existing $audit_file rules' definitions to select those that:
        # * follow the rule pattern, and
        # * meet the hardware architecture requirement, and
        # * are current syscall group specific
        IFS=$'\n' existing_rules=($(sed -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d"  "$audit_file"))
        # Reset IFS back to default
        unset $IFS

        # Process rules found case-by-case
        for rule in "${existing_rules[@]}"
        do
                # Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
                if [ "${rule}" != "${full_rule}" ]
                then
                        # If so, isolate just '(-S \w)+' substring of that rule
                        rule_syscalls=$(echo $rule | grep -o -P '(-S \w+ )+')
                        # Check if list of '-S syscall' arguments of that rule is subset
                        # of '-S syscall' list of expected $full_rule
                        if grep -q -- "$rule_syscalls" <<< "$full_rule"
                        then
                                # Rule is covered (i.e. the list of -S syscalls for this rule is
                                # subset of -S syscalls of $full_rule => existing rule can be deleted
                                # Thus delete the rule from audit.rules & our array
                                sed -i -e "/$rule/d" "$audit_file"
                                existing_rules=("${existing_rules[@]//$rule/}")
                        else
                                # Rule isn't covered by $full_rule - it besides -S syscall arguments
                                # for this group contains also -S syscall arguments for other syscall
                                # group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
                                # since 'lchown' & 'fchownat' share 'chown' substring
                                # Therefore:
                                # * 1) delete the original rule from audit.rules
                                # (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
                                # * 2) delete the -S syscall arguments for this syscall group, but
                                # keep those not belonging to this syscall group
                                # (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
                                # * 3) append the modified (filtered) rule again into audit.rules
                                # if the same rule not already present
                                #
                                # 1) Delete the original rule
                                sed -i -e "/$rule/d" "$audit_file"
                                # 2) Delete syscalls for this group, but keep those from other groups
                                # Convert current rule syscall's string into array splitting by '-S' delimiter
                                IFS=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
                                # Reset IFS back to default
                                unset $IFS
                                # Declare new empty string to hold '-S syscall' arguments from other groups
                                new_syscalls_for_rule=''
                                # Walk through existing '-S syscall' arguments
                                for syscall_arg in "${rule_syscalls_as_array[@]}"
                                do
                                        # Skip empty $syscall_arg values
                                        if [ "$syscall_arg" == '' ]
                                        then
                                                continue
                                        fi
                                        # If the '-S syscall' doesn't belong to current group add it to the new list
                                        # (together with adding '-S' delimiter back for each of such item found)
                                        if grep -q -v -- "$group" <<< "$syscall_arg"
                                        then
                                                new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
                                        fi
                                done
                                # Replace original '-S syscall' list with the new one for this rule
                                updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
                                # Squeeze repeated whitespace characters in rule definition (if any) into one
                                updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
                                # 3) Append the modified / filtered rule again into audit.rules
                                #    (but only in case it's not present yet to prevent duplicate definitions)
                                if ! grep -q -- "$updated_rule" "$audit_file"
                                then
                                        echo "$updated_rule" >> "$audit_file"
                                fi
                        fi
                else
                        # $audit_file already contains the expected rule form for this
                        # architecture & key => don't insert it second time
                        append_expected_rule=1
                fi
        done

        # We deleted all rules that were subset of the expected one for this arch & key.
        # Also isolated rules containing system calls not from this system calls group.
        # Now append the expected rule if it's not present in $audit_file yet
        if [[ ${append_expected_rule} -eq "0" ]]
        then
                echo "$full_rule" >> "$audit_file"
        fi
done

}

	fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done

Record Attempts to Alter the localtime Filerule

Add the following to /etc/audit/audit.rules:

-w /etc/localtime -p wa -k audit_time_rules
The -k option allows for the specification of a key in string form that can be used for better reporting capability through ausearch and aureport and should always be used.

Rationale:

Arbitrary changes to the system time can be used to obfuscate nefarious activities in log files, as well as to confuse network services that are highly dependent upon an accurate system time (such as sshd). All changes to the system time should be audited.

identifiers:  CCE-27172-6, DISA FSO RHEL-06-000173

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 1487, 169

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


# Perform the remediation

function fix_audit_watch_rule {

# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"

# Check sanity of the input
if [ $# -ne "4" ]
then
        echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
        echo "Aborting."
        exit 1
fi

# 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  |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect

# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
        echo "Unknown audit rules loading tool: $1. Aborting."
        echo "Use either 'auditctl' or 'augenrules'!"
        exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("${files_to_inspect[@]}" '/etc/audit/audit.rules')
# 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/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
        # Case when particular audit rule is already defined in some of /etc/audit/rules.d/*.rules file
        # Get pair -- filepath : matching_row into @matches array
        IFS=$'\n' matches=($(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules))
        # Reset IFS back to default
        unset $IFS
        # 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=("${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/$key.rules' into list of files for inspection
                files_to_inspect="/etc/audit/rules.d/$key.rules"
                # If the $key.rules file doesn't exist yet, create it with correct permissions
                if [ ! -e "$files_to_inspect" ]
                then
                        touch "$files_to_inspect"
                        chmod 0640 "$files_to_inspect"
                fi
        fi
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]+$path" "$audit_rules_file"
        then
                # Rule is found => verify yet if existing rule definition contains
                # all of the required access type bits

                # Escape slashes in path for use in sed pattern below
                local esc_path=${path//$'/'/$'\/'}
                # Define BRE whitespace class shortcut
                local 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\+$esc_path$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 "$required_access_bits" | 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\+$esc_path$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 $path -p $required_access_bits -k $key" >> "$audit_rules_file"
        fi
done
}

fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"

Record Events that Modify the System's Discretionary Access Controlsgroup

At a minimum, the audit system should collect file permission changes for all users and root. Note that the "-F arch=b32" lines should be present even on a 64 bit system. These commands identify system calls for auditing. Even if the system is 64 bit it can still execute 32 bit system calls. Additionally, these rules can be configured in a number of ways while still achieving the desired effect. An example of this is that the "-S" calls could be split up and placed on separate lines, however, this is less efficient. Add the following to /etc/audit/audit.rules:

-a always,exit -F arch=b32 -S chmod -S fchmod -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod
    -a always,exit -F arch=b32 -S chown -S fchown -S fchownat -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod
    -a always,exit -F arch=b32 -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod
If your system is 64 bit then these lines should be duplicated and the arch=b32 replaced with arch=b64 as follows:
-a always,exit -F arch=b64 -S chmod -S fchmod -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod
    -a always,exit -F arch=b64 -S chown -S fchown -S fchownat -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod
    -a always,exit -F arch=b64 -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod

contains 13 rules

Record Events that Modify the System's Discretionary Access Controls - chmodrule

At a minimum the audit system should collect file permission changes for all users and root. Add the following to /etc/audit/audit.rules:

-a always,exit -F arch=b32 -S chmod -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S chmod  -F auid>=500 -F auid!=4294967295 -k perm_mod

warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.
Rationale:

The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.

identifiers:  CCE-26280-8, DISA FSO RHEL-06-000184

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 126

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


# Perform the remediation for 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
	PATTERN="-a always,exit -F arch=$ARCH -S .* -F auid>=500 -F auid!=4294967295 -k *"
	GROUP="chmod"
	FULL_RULE="-a always,exit -F arch=$ARCH -S chmod -S fchmod -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod"

function fix_audit_syscall_rule {

# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"

# Check sanity of the input
if [ $# -ne "5" ]
then
        echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
        echo "Aborting."
        exit 1
fi

# 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  |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect

# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
        echo "Unknown audit rules loading tool: $1. Aborting."
        echo "Use either 'auditctl' or 'augenrules'!"
        exit 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("${files_to_inspect[@]}" '/etc/audit/audit.rules' )
# 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
elif [ "$tool" == 'augenrules' ]
then
        # Extract audit $key from audit rule so we can use it later
        key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
        # Check if particular audit rule is already defined
        IFS=$'\n' matches=($(sed -s -n -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules))
        # Reset IFS back to default
        unset $IFS
        for match in "${matches[@]}"
        do
                files_to_inspect=("${files_to_inspect[@]}" "${match}")
        done
        # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
        if [ ${#files_to_inspect[@]} -eq "0" ]
        then
                files_to_inspect="/etc/audit/rules.d/$key.rules"
                if [ ! -e "$files_to_inspect" ]
                then
                        touch "$files_to_inspect"
                        chmod 0640 "$files_to_inspect"
                fi
        fi
fi

#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0

for audit_file in "${files_to_inspect[@]}"
do

        # Filter existing $audit_file rules' definitions to select those that:
        # * follow the rule pattern, and
        # * meet the hardware architecture requirement, and
        # * are current syscall group specific
        IFS=$'\n' existing_rules=($(sed -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d"  "$audit_file"))
        # Reset IFS back to default
        unset $IFS

        # Process rules found case-by-case
        for rule in "${existing_rules[@]}"
        do
                # Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
                if [ "${rule}" != "${full_rule}" ]
                then
                        # If so, isolate just '(-S \w)+' substring of that rule
                        rule_syscalls=$(echo $rule | grep -o -P '(-S \w+ )+')
                        # Check if list of '-S syscall' arguments of that rule is subset
                        # of '-S syscall' list of expected $full_rule
                        if grep -q -- "$rule_syscalls" <<< "$full_rule"
                        then
                                # Rule is covered (i.e. the list of -S syscalls for this rule is
                                # subset of -S syscalls of $full_rule => existing rule can be deleted
                                # Thus delete the rule from audit.rules & our array
                                sed -i -e "/$rule/d" "$audit_file"
                                existing_rules=("${existing_rules[@]//$rule/}")
                        else
                                # Rule isn't covered by $full_rule - it besides -S syscall arguments
                                # for this group contains also -S syscall arguments for other syscall
                                # group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
                                # since 'lchown' & 'fchownat' share 'chown' substring
                                # Therefore:
                                # * 1) delete the original rule from audit.rules
                                # (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
                                # * 2) delete the -S syscall arguments for this syscall group, but
                                # keep those not belonging to this syscall group
                                # (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
                                # * 3) append the modified (filtered) rule again into audit.rules
                                # if the same rule not already present
                                #
                                # 1) Delete the original rule
                                sed -i -e "/$rule/d" "$audit_file"
                                # 2) Delete syscalls for this group, but keep those from other groups
                                # Convert current rule syscall's string into array splitting by '-S' delimiter
                                IFS=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
                                # Reset IFS back to default
                                unset $IFS
                                # Declare new empty string to hold '-S syscall' arguments from other groups
                                new_syscalls_for_rule=''
                                # Walk through existing '-S syscall' arguments
                                for syscall_arg in "${rule_syscalls_as_array[@]}"
                                do
                                        # Skip empty $syscall_arg values
                                        if [ "$syscall_arg" == '' ]
                                        then
                                                continue
                                        fi
                                        # If the '-S syscall' doesn't belong to current group add it to the new list
                                        # (together with adding '-S' delimiter back for each of such item found)
                                        if grep -q -v -- "$group" <<< "$syscall_arg"
                                        then
                                                new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
                                        fi
                                done
                                # Replace original '-S syscall' list with the new one for this rule
                                updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
                                # Squeeze repeated whitespace characters in rule definition (if any) into one
                                updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
                                # 3) Append the modified / filtered rule again into audit.rules
                                #    (but only in case it's not present yet to prevent duplicate definitions)
                                if ! grep -q -- "$updated_rule" "$audit_file"
                                then
                                        echo "$updated_rule" >> "$audit_file"
                                fi
                        fi
                else
                        # $audit_file already contains the expected rule form for this
                        # architecture & key => don't insert it second time
                        append_expected_rule=1
                fi
        done

        # We deleted all rules that were subset of the expected one for this arch & key.
        # Also isolated rules containing system calls not from this system calls group.
        # Now append the expected rule if it's not present in $audit_file yet
        if [[ ${append_expected_rule} -eq "0" ]]
        then
                echo "$full_rule" >> "$audit_file"
        fi
done

}

	fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done

Record Events that Modify the System's Discretionary Access Controls - chownrule

At a minimum the audit system should collect file permission changes for all users and root. Add the following to /etc/audit/audit.rules:

-a always,exit -F arch=b32 -S chown -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S chown -F auid>=500 -F auid!=4294967295 -k perm_mod

warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.
Rationale:

The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.

identifiers:  CCE-27173-4, DISA FSO RHEL-06-000185

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 126

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


# Perform the remediation for 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
	PATTERN="-a always,exit -F arch=$ARCH -S .* -F auid>=500 -F auid!=4294967295 -k *"
	GROUP="chown"
	FULL_RULE="-a always,exit -F arch=$ARCH -S chown -S fchown -S fchownat -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod"

function fix_audit_syscall_rule {

# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"

# Check sanity of the input
if [ $# -ne "5" ]
then
        echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
        echo "Aborting."
        exit 1
fi

# 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  |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect

# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
        echo "Unknown audit rules loading tool: $1. Aborting."
        echo "Use either 'auditctl' or 'augenrules'!"
        exit 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("${files_to_inspect[@]}" '/etc/audit/audit.rules' )
# 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
elif [ "$tool" == 'augenrules' ]
then
        # Extract audit $key from audit rule so we can use it later
        key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
        # Check if particular audit rule is already defined
        IFS=$'\n' matches=($(sed -s -n -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules))
        # Reset IFS back to default
        unset $IFS
        for match in "${matches[@]}"
        do
                files_to_inspect=("${files_to_inspect[@]}" "${match}")
        done
        # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
        if [ ${#files_to_inspect[@]} -eq "0" ]
        then
                files_to_inspect="/etc/audit/rules.d/$key.rules"
                if [ ! -e "$files_to_inspect" ]
                then
                        touch "$files_to_inspect"
                        chmod 0640 "$files_to_inspect"
                fi
        fi
fi

#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0

for audit_file in "${files_to_inspect[@]}"
do

        # Filter existing $audit_file rules' definitions to select those that:
        # * follow the rule pattern, and
        # * meet the hardware architecture requirement, and
        # * are current syscall group specific
        IFS=$'\n' existing_rules=($(sed -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d"  "$audit_file"))
        # Reset IFS back to default
        unset $IFS

        # Process rules found case-by-case
        for rule in "${existing_rules[@]}"
        do
                # Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
                if [ "${rule}" != "${full_rule}" ]
                then
                        # If so, isolate just '(-S \w)+' substring of that rule
                        rule_syscalls=$(echo $rule | grep -o -P '(-S \w+ )+')
                        # Check if list of '-S syscall' arguments of that rule is subset
                        # of '-S syscall' list of expected $full_rule
                        if grep -q -- "$rule_syscalls" <<< "$full_rule"
                        then
                                # Rule is covered (i.e. the list of -S syscalls for this rule is
                                # subset of -S syscalls of $full_rule => existing rule can be deleted
                                # Thus delete the rule from audit.rules & our array
                                sed -i -e "/$rule/d" "$audit_file"
                                existing_rules=("${existing_rules[@]//$rule/}")
                        else
                                # Rule isn't covered by $full_rule - it besides -S syscall arguments
                                # for this group contains also -S syscall arguments for other syscall
                                # group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
                                # since 'lchown' & 'fchownat' share 'chown' substring
                                # Therefore:
                                # * 1) delete the original rule from audit.rules
                                # (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
                                # * 2) delete the -S syscall arguments for this syscall group, but
                                # keep those not belonging to this syscall group
                                # (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
                                # * 3) append the modified (filtered) rule again into audit.rules
                                # if the same rule not already present
                                #
                                # 1) Delete the original rule
                                sed -i -e "/$rule/d" "$audit_file"
                                # 2) Delete syscalls for this group, but keep those from other groups
                                # Convert current rule syscall's string into array splitting by '-S' delimiter
                                IFS=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
                                # Reset IFS back to default
                                unset $IFS
                                # Declare new empty string to hold '-S syscall' arguments from other groups
                                new_syscalls_for_rule=''
                                # Walk through existing '-S syscall' arguments
                                for syscall_arg in "${rule_syscalls_as_array[@]}"
                                do
                                        # Skip empty $syscall_arg values
                                        if [ "$syscall_arg" == '' ]
                                        then
                                                continue
                                        fi
                                        # If the '-S syscall' doesn't belong to current group add it to the new list
                                        # (together with adding '-S' delimiter back for each of such item found)
                                        if grep -q -v -- "$group" <<< "$syscall_arg"
                                        then
                                                new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
                                        fi
                                done
                                # Replace original '-S syscall' list with the new one for this rule
                                updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
                                # Squeeze repeated whitespace characters in rule definition (if any) into one
                                updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
                                # 3) Append the modified / filtered rule again into audit.rules
                                #    (but only in case it's not present yet to prevent duplicate definitions)
                                if ! grep -q -- "$updated_rule" "$audit_file"
                                then
                                        echo "$updated_rule" >> "$audit_file"
                                fi
                        fi
                else
                        # $audit_file already contains the expected rule form for this
                        # architecture & key => don't insert it second time
                        append_expected_rule=1
                fi
        done

        # We deleted all rules that were subset of the expected one for this arch & key.
        # Also isolated rules containing system calls not from this system calls group.
        # Now append the expected rule if it's not present in $audit_file yet
        if [[ ${append_expected_rule} -eq "0" ]]
        then
                echo "$full_rule" >> "$audit_file"
        fi
done

}

	fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done

Record Events that Modify the System's Discretionary Access Controls - fchmodrule

At a minimum the audit system should collect file permission changes for all users and root. Add the following to /etc/audit/audit.rules:

-a always,exit -F arch=b32 -S fchmod -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S fchmod -F auid>=500 -F auid!=4294967295 -k perm_mod

warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.
Rationale:

The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.

identifiers:  CCE-27174-2, DISA FSO RHEL-06-000186

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 126

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


# Perform the remediation for 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
	PATTERN="-a always,exit -F arch=$ARCH -S .* -F auid>=500 -F auid!=4294967295 -k *"
	GROUP="chmod"
	FULL_RULE="-a always,exit -F arch=$ARCH -S chmod -S fchmod -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod"

function fix_audit_syscall_rule {

# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"

# Check sanity of the input
if [ $# -ne "5" ]
then
        echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
        echo "Aborting."
        exit 1
fi

# 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  |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect

# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
        echo "Unknown audit rules loading tool: $1. Aborting."
        echo "Use either 'auditctl' or 'augenrules'!"
        exit 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("${files_to_inspect[@]}" '/etc/audit/audit.rules' )
# 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
elif [ "$tool" == 'augenrules' ]
then
        # Extract audit $key from audit rule so we can use it later
        key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
        # Check if particular audit rule is already defined
        IFS=$'\n' matches=($(sed -s -n -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules))
        # Reset IFS back to default
        unset $IFS
        for match in "${matches[@]}"
        do
                files_to_inspect=("${files_to_inspect[@]}" "${match}")
        done
        # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
        if [ ${#files_to_inspect[@]} -eq "0" ]
        then
                files_to_inspect="/etc/audit/rules.d/$key.rules"
                if [ ! -e "$files_to_inspect" ]
                then
                        touch "$files_to_inspect"
                        chmod 0640 "$files_to_inspect"
                fi
        fi
fi

#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0

for audit_file in "${files_to_inspect[@]}"
do

        # Filter existing $audit_file rules' definitions to select those that:
        # * follow the rule pattern, and
        # * meet the hardware architecture requirement, and
        # * are current syscall group specific
        IFS=$'\n' existing_rules=($(sed -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d"  "$audit_file"))
        # Reset IFS back to default
        unset $IFS

        # Process rules found case-by-case
        for rule in "${existing_rules[@]}"
        do
                # Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
                if [ "${rule}" != "${full_rule}" ]
                then
                        # If so, isolate just '(-S \w)+' substring of that rule
                        rule_syscalls=$(echo $rule | grep -o -P '(-S \w+ )+')
                        # Check if list of '-S syscall' arguments of that rule is subset
                        # of '-S syscall' list of expected $full_rule
                        if grep -q -- "$rule_syscalls" <<< "$full_rule"
                        then
                                # Rule is covered (i.e. the list of -S syscalls for this rule is
                                # subset of -S syscalls of $full_rule => existing rule can be deleted
                                # Thus delete the rule from audit.rules & our array
                                sed -i -e "/$rule/d" "$audit_file"
                                existing_rules=("${existing_rules[@]//$rule/}")
                        else
                                # Rule isn't covered by $full_rule - it besides -S syscall arguments
                                # for this group contains also -S syscall arguments for other syscall
                                # group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
                                # since 'lchown' & 'fchownat' share 'chown' substring
                                # Therefore:
                                # * 1) delete the original rule from audit.rules
                                # (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
                                # * 2) delete the -S syscall arguments for this syscall group, but
                                # keep those not belonging to this syscall group
                                # (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
                                # * 3) append the modified (filtered) rule again into audit.rules
                                # if the same rule not already present
                                #
                                # 1) Delete the original rule
                                sed -i -e "/$rule/d" "$audit_file"
                                # 2) Delete syscalls for this group, but keep those from other groups
                                # Convert current rule syscall's string into array splitting by '-S' delimiter
                                IFS=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
                                # Reset IFS back to default
                                unset $IFS
                                # Declare new empty string to hold '-S syscall' arguments from other groups
                                new_syscalls_for_rule=''
                                # Walk through existing '-S syscall' arguments
                                for syscall_arg in "${rule_syscalls_as_array[@]}"
                                do
                                        # Skip empty $syscall_arg values
                                        if [ "$syscall_arg" == '' ]
                                        then
                                                continue
                                        fi
                                        # If the '-S syscall' doesn't belong to current group add it to the new list
                                        # (together with adding '-S' delimiter back for each of such item found)
                                        if grep -q -v -- "$group" <<< "$syscall_arg"
                                        then
                                                new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
                                        fi
                                done
                                # Replace original '-S syscall' list with the new one for this rule
                                updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
                                # Squeeze repeated whitespace characters in rule definition (if any) into one
                                updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
                                # 3) Append the modified / filtered rule again into audit.rules
                                #    (but only in case it's not present yet to prevent duplicate definitions)
                                if ! grep -q -- "$updated_rule" "$audit_file"
                                then
                                        echo "$updated_rule" >> "$audit_file"
                                fi
                        fi
                else
                        # $audit_file already contains the expected rule form for this
                        # architecture & key => don't insert it second time
                        append_expected_rule=1
                fi
        done

        # We deleted all rules that were subset of the expected one for this arch & key.
        # Also isolated rules containing system calls not from this system calls group.
        # Now append the expected rule if it's not present in $audit_file yet
        if [[ ${append_expected_rule} -eq "0" ]]
        then
                echo "$full_rule" >> "$audit_file"
        fi
done

}

	fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done

Record Events that Modify the System's Discretionary Access Controls - fchmodatrule

At a minimum the audit system should collect file permission changes for all users and root. Add the following to /etc/audit/audit.rules:

-a always,exit -F arch=b32 -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod

warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.
Rationale:

The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.

identifiers:  CCE-27175-9, DISA FSO RHEL-06-000187

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 126

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


# Perform the remediation for 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
	PATTERN="-a always,exit -F arch=$ARCH -S .* -F auid>=500 -F auid!=4294967295 -k *"
	GROUP="chmod"
	FULL_RULE="-a always,exit -F arch=$ARCH -S chmod -S fchmod -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod"

function fix_audit_syscall_rule {

# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"

# Check sanity of the input
if [ $# -ne "5" ]
then
        echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
        echo "Aborting."
        exit 1
fi

# 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  |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect

# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
        echo "Unknown audit rules loading tool: $1. Aborting."
        echo "Use either 'auditctl' or 'augenrules'!"
        exit 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("${files_to_inspect[@]}" '/etc/audit/audit.rules' )
# 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
elif [ "$tool" == 'augenrules' ]
then
        # Extract audit $key from audit rule so we can use it later
        key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
        # Check if particular audit rule is already defined
        IFS=$'\n' matches=($(sed -s -n -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules))
        # Reset IFS back to default
        unset $IFS
        for match in "${matches[@]}"
        do
                files_to_inspect=("${files_to_inspect[@]}" "${match}")
        done
        # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
        if [ ${#files_to_inspect[@]} -eq "0" ]
        then
                files_to_inspect="/etc/audit/rules.d/$key.rules"
                if [ ! -e "$files_to_inspect" ]
                then
                        touch "$files_to_inspect"
                        chmod 0640 "$files_to_inspect"
                fi
        fi
fi

#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0

for audit_file in "${files_to_inspect[@]}"
do

        # Filter existing $audit_file rules' definitions to select those that:
        # * follow the rule pattern, and
        # * meet the hardware architecture requirement, and
        # * are current syscall group specific
        IFS=$'\n' existing_rules=($(sed -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d"  "$audit_file"))
        # Reset IFS back to default
        unset $IFS

        # Process rules found case-by-case
        for rule in "${existing_rules[@]}"
        do
                # Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
                if [ "${rule}" != "${full_rule}" ]
                then
                        # If so, isolate just '(-S \w)+' substring of that rule
                        rule_syscalls=$(echo $rule | grep -o -P '(-S \w+ )+')
                        # Check if list of '-S syscall' arguments of that rule is subset
                        # of '-S syscall' list of expected $full_rule
                        if grep -q -- "$rule_syscalls" <<< "$full_rule"
                        then
                                # Rule is covered (i.e. the list of -S syscalls for this rule is
                                # subset of -S syscalls of $full_rule => existing rule can be deleted
                                # Thus delete the rule from audit.rules & our array
                                sed -i -e "/$rule/d" "$audit_file"
                                existing_rules=("${existing_rules[@]//$rule/}")
                        else
                                # Rule isn't covered by $full_rule - it besides -S syscall arguments
                                # for this group contains also -S syscall arguments for other syscall
                                # group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
                                # since 'lchown' & 'fchownat' share 'chown' substring
                                # Therefore:
                                # * 1) delete the original rule from audit.rules
                                # (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
                                # * 2) delete the -S syscall arguments for this syscall group, but
                                # keep those not belonging to this syscall group
                                # (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
                                # * 3) append the modified (filtered) rule again into audit.rules
                                # if the same rule not already present
                                #
                                # 1) Delete the original rule
                                sed -i -e "/$rule/d" "$audit_file"
                                # 2) Delete syscalls for this group, but keep those from other groups
                                # Convert current rule syscall's string into array splitting by '-S' delimiter
                                IFS=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
                                # Reset IFS back to default
                                unset $IFS
                                # Declare new empty string to hold '-S syscall' arguments from other groups
                                new_syscalls_for_rule=''
                                # Walk through existing '-S syscall' arguments
                                for syscall_arg in "${rule_syscalls_as_array[@]}"
                                do
                                        # Skip empty $syscall_arg values
                                        if [ "$syscall_arg" == '' ]
                                        then
                                                continue
                                        fi
                                        # If the '-S syscall' doesn't belong to current group add it to the new list
                                        # (together with adding '-S' delimiter back for each of such item found)
                                        if grep -q -v -- "$group" <<< "$syscall_arg"
                                        then
                                                new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
                                        fi
                                done
                                # Replace original '-S syscall' list with the new one for this rule
                                updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
                                # Squeeze repeated whitespace characters in rule definition (if any) into one
                                updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
                                # 3) Append the modified / filtered rule again into audit.rules
                                #    (but only in case it's not present yet to prevent duplicate definitions)
                                if ! grep -q -- "$updated_rule" "$audit_file"
                                then
                                        echo "$updated_rule" >> "$audit_file"
                                fi
                        fi
                else
                        # $audit_file already contains the expected rule form for this
                        # architecture & key => don't insert it second time
                        append_expected_rule=1
                fi
        done

        # We deleted all rules that were subset of the expected one for this arch & key.
        # Also isolated rules containing system calls not from this system calls group.
        # Now append the expected rule if it's not present in $audit_file yet
        if [[ ${append_expected_rule} -eq "0" ]]
        then
                echo "$full_rule" >> "$audit_file"
        fi
done

}

	fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done

Record Events that Modify the System's Discretionary Access Controls - fchownrule

At a minimum the audit system should collect file permission changes for all users and root. Add the following to /etc/audit/audit.rules:

-a always,exit -F arch=b32 -S fchown -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S fchown -F auid>=500 -F auid!=4294967295 -k perm_mod

warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.
Rationale:

The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.

identifiers:  CCE-27177-5, DISA FSO RHEL-06-000188

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 126

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


# Perform the remediation for 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
	PATTERN="-a always,exit -F arch=$ARCH -S .* -F auid>=500 -F auid!=4294967295 -k *"
	GROUP="chown"
	FULL_RULE="-a always,exit -F arch=$ARCH -S chown -S fchown -S fchownat -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod"

function fix_audit_syscall_rule {

# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"

# Check sanity of the input
if [ $# -ne "5" ]
then
        echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
        echo "Aborting."
        exit 1
fi

# 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  |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect

# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
        echo "Unknown audit rules loading tool: $1. Aborting."
        echo "Use either 'auditctl' or 'augenrules'!"
        exit 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("${files_to_inspect[@]}" '/etc/audit/audit.rules' )
# 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
elif [ "$tool" == 'augenrules' ]
then
        # Extract audit $key from audit rule so we can use it later
        key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
        # Check if particular audit rule is already defined
        IFS=$'\n' matches=($(sed -s -n -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules))
        # Reset IFS back to default
        unset $IFS
        for match in "${matches[@]}"
        do
                files_to_inspect=("${files_to_inspect[@]}" "${match}")
        done
        # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
        if [ ${#files_to_inspect[@]} -eq "0" ]
        then
                files_to_inspect="/etc/audit/rules.d/$key.rules"
                if [ ! -e "$files_to_inspect" ]
                then
                        touch "$files_to_inspect"
                        chmod 0640 "$files_to_inspect"
                fi
        fi
fi

#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0

for audit_file in "${files_to_inspect[@]}"
do

        # Filter existing $audit_file rules' definitions to select those that:
        # * follow the rule pattern, and
        # * meet the hardware architecture requirement, and
        # * are current syscall group specific
        IFS=$'\n' existing_rules=($(sed -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d"  "$audit_file"))
        # Reset IFS back to default
        unset $IFS

        # Process rules found case-by-case
        for rule in "${existing_rules[@]}"
        do
                # Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
                if [ "${rule}" != "${full_rule}" ]
                then
                        # If so, isolate just '(-S \w)+' substring of that rule
                        rule_syscalls=$(echo $rule | grep -o -P '(-S \w+ )+')
                        # Check if list of '-S syscall' arguments of that rule is subset
                        # of '-S syscall' list of expected $full_rule
                        if grep -q -- "$rule_syscalls" <<< "$full_rule"
                        then
                                # Rule is covered (i.e. the list of -S syscalls for this rule is
                                # subset of -S syscalls of $full_rule => existing rule can be deleted
                                # Thus delete the rule from audit.rules & our array
                                sed -i -e "/$rule/d" "$audit_file"
                                existing_rules=("${existing_rules[@]//$rule/}")
                        else
                                # Rule isn't covered by $full_rule - it besides -S syscall arguments
                                # for this group contains also -S syscall arguments for other syscall
                                # group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
                                # since 'lchown' & 'fchownat' share 'chown' substring
                                # Therefore:
                                # * 1) delete the original rule from audit.rules
                                # (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
                                # * 2) delete the -S syscall arguments for this syscall group, but
                                # keep those not belonging to this syscall group
                                # (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
                                # * 3) append the modified (filtered) rule again into audit.rules
                                # if the same rule not already present
                                #
                                # 1) Delete the original rule
                                sed -i -e "/$rule/d" "$audit_file"
                                # 2) Delete syscalls for this group, but keep those from other groups
                                # Convert current rule syscall's string into array splitting by '-S' delimiter
                                IFS=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
                                # Reset IFS back to default
                                unset $IFS
                                # Declare new empty string to hold '-S syscall' arguments from other groups
                                new_syscalls_for_rule=''
                                # Walk through existing '-S syscall' arguments
                                for syscall_arg in "${rule_syscalls_as_array[@]}"
                                do
                                        # Skip empty $syscall_arg values
                                        if [ "$syscall_arg" == '' ]
                                        then
                                                continue
                                        fi
                                        # If the '-S syscall' doesn't belong to current group add it to the new list
                                        # (together with adding '-S' delimiter back for each of such item found)
                                        if grep -q -v -- "$group" <<< "$syscall_arg"
                                        then
                                                new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
                                        fi
                                done
                                # Replace original '-S syscall' list with the new one for this rule
                                updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
                                # Squeeze repeated whitespace characters in rule definition (if any) into one
                                updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
                                # 3) Append the modified / filtered rule again into audit.rules
                                #    (but only in case it's not present yet to prevent duplicate definitions)
                                if ! grep -q -- "$updated_rule" "$audit_file"
                                then
                                        echo "$updated_rule" >> "$audit_file"
                                fi
                        fi
                else
                        # $audit_file already contains the expected rule form for this
                        # architecture & key => don't insert it second time
                        append_expected_rule=1
                fi
        done

        # We deleted all rules that were subset of the expected one for this arch & key.
        # Also isolated rules containing system calls not from this system calls group.
        # Now append the expected rule if it's not present in $audit_file yet
        if [[ ${append_expected_rule} -eq "0" ]]
        then
                echo "$full_rule" >> "$audit_file"
        fi
done

}

	fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done

Record Events that Modify the System's Discretionary Access Controls - fchownatrule

At a minimum the audit system should collect file permission changes for all users and root. Add the following to /etc/audit/audit.rules:

-a always,exit -F arch=b32 -S fchownat -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S fchownat -F auid>=500 -F auid!=4294967295 -k perm_mod

warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.
Rationale:

The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.

identifiers:  CCE-27178-3, DISA FSO RHEL-06-000189

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 126

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


# Perform the remediation for 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
	PATTERN="-a always,exit -F arch=$ARCH -S .* -F auid>=500 -F auid!=4294967295 -k *"
	GROUP="chown"
	FULL_RULE="-a always,exit -F arch=$ARCH -S chown -S fchown -S fchownat -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod"

function fix_audit_syscall_rule {

# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"

# Check sanity of the input
if [ $# -ne "5" ]
then
        echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
        echo "Aborting."
        exit 1
fi

# 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  |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect

# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
        echo "Unknown audit rules loading tool: $1. Aborting."
        echo "Use either 'auditctl' or 'augenrules'!"
        exit 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("${files_to_inspect[@]}" '/etc/audit/audit.rules' )
# 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
elif [ "$tool" == 'augenrules' ]
then
        # Extract audit $key from audit rule so we can use it later
        key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
        # Check if particular audit rule is already defined
        IFS=$'\n' matches=($(sed -s -n -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules))
        # Reset IFS back to default
        unset $IFS
        for match in "${matches[@]}"
        do
                files_to_inspect=("${files_to_inspect[@]}" "${match}")
        done
        # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
        if [ ${#files_to_inspect[@]} -eq "0" ]
        then
                files_to_inspect="/etc/audit/rules.d/$key.rules"
                if [ ! -e "$files_to_inspect" ]
                then
                        touch "$files_to_inspect"
                        chmod 0640 "$files_to_inspect"
                fi
        fi
fi

#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0

for audit_file in "${files_to_inspect[@]}"
do

        # Filter existing $audit_file rules' definitions to select those that:
        # * follow the rule pattern, and
        # * meet the hardware architecture requirement, and
        # * are current syscall group specific
        IFS=$'\n' existing_rules=($(sed -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d"  "$audit_file"))
        # Reset IFS back to default
        unset $IFS

        # Process rules found case-by-case
        for rule in "${existing_rules[@]}"
        do
                # Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
                if [ "${rule}" != "${full_rule}" ]
                then
                        # If so, isolate just '(-S \w)+' substring of that rule
                        rule_syscalls=$(echo $rule | grep -o -P '(-S \w+ )+')
                        # Check if list of '-S syscall' arguments of that rule is subset
                        # of '-S syscall' list of expected $full_rule
                        if grep -q -- "$rule_syscalls" <<< "$full_rule"
                        then
                                # Rule is covered (i.e. the list of -S syscalls for this rule is
                                # subset of -S syscalls of $full_rule => existing rule can be deleted
                                # Thus delete the rule from audit.rules & our array
                                sed -i -e "/$rule/d" "$audit_file"
                                existing_rules=("${existing_rules[@]//$rule/}")
                        else
                                # Rule isn't covered by $full_rule - it besides -S syscall arguments
                                # for this group contains also -S syscall arguments for other syscall
                                # group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
                                # since 'lchown' & 'fchownat' share 'chown' substring
                                # Therefore:
                                # * 1) delete the original rule from audit.rules
                                # (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
                                # * 2) delete the -S syscall arguments for this syscall group, but
                                # keep those not belonging to this syscall group
                                # (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
                                # * 3) append the modified (filtered) rule again into audit.rules
                                # if the same rule not already present
                                #
                                # 1) Delete the original rule
                                sed -i -e "/$rule/d" "$audit_file"
                                # 2) Delete syscalls for this group, but keep those from other groups
                                # Convert current rule syscall's string into array splitting by '-S' delimiter
                                IFS=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
                                # Reset IFS back to default
                                unset $IFS
                                # Declare new empty string to hold '-S syscall' arguments from other groups
                                new_syscalls_for_rule=''
                                # Walk through existing '-S syscall' arguments
                                for syscall_arg in "${rule_syscalls_as_array[@]}"
                                do
                                        # Skip empty $syscall_arg values
                                        if [ "$syscall_arg" == '' ]
                                        then
                                                continue
                                        fi
                                        # If the '-S syscall' doesn't belong to current group add it to the new list
                                        # (together with adding '-S' delimiter back for each of such item found)
                                        if grep -q -v -- "$group" <<< "$syscall_arg"
                                        then
                                                new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
                                        fi
                                done
                                # Replace original '-S syscall' list with the new one for this rule
                                updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
                                # Squeeze repeated whitespace characters in rule definition (if any) into one
                                updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
                                # 3) Append the modified / filtered rule again into audit.rules
                                #    (but only in case it's not present yet to prevent duplicate definitions)
                                if ! grep -q -- "$updated_rule" "$audit_file"
                                then
                                        echo "$updated_rule" >> "$audit_file"
                                fi
                        fi
                else
                        # $audit_file already contains the expected rule form for this
                        # architecture & key => don't insert it second time
                        append_expected_rule=1
                fi
        done

        # We deleted all rules that were subset of the expected one for this arch & key.
        # Also isolated rules containing system calls not from this system calls group.
        # Now append the expected rule if it's not present in $audit_file yet
        if [[ ${append_expected_rule} -eq "0" ]]
        then
                echo "$full_rule" >> "$audit_file"
        fi
done

}

	fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done

Record Events that Modify the System's Discretionary Access Controls - fremovexattrrule

At a minimum the audit system should collect file permission changes for all users and root. Add the following to /etc/audit/audit.rules:

-a always,exit -F arch=b32 -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod

warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.
Rationale:

The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.

identifiers:  CCE-27179-1, DISA FSO RHEL-06-000190

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 126

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


# Perform the remediation for 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
	PATTERN="-a always,exit .* -F auid>=500 -F auid!=4294967295 -k *"
	GROUP="xattr"
	FULL_RULE="-a always,exit -F arch=${ARCH} -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod"

function fix_audit_syscall_rule {

# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"

# Check sanity of the input
if [ $# -ne "5" ]
then
        echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
        echo "Aborting."
        exit 1
fi

# 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  |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect

# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
        echo "Unknown audit rules loading tool: $1. Aborting."
        echo "Use either 'auditctl' or 'augenrules'!"
        exit 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("${files_to_inspect[@]}" '/etc/audit/audit.rules' )
# 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
elif [ "$tool" == 'augenrules' ]
then
        # Extract audit $key from audit rule so we can use it later
        key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
        # Check if particular audit rule is already defined
        IFS=$'\n' matches=($(sed -s -n -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules))
        # Reset IFS back to default
        unset $IFS
        for match in "${matches[@]}"
        do
                files_to_inspect=("${files_to_inspect[@]}" "${match}")
        done
        # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
        if [ ${#files_to_inspect[@]} -eq "0" ]
        then
                files_to_inspect="/etc/audit/rules.d/$key.rules"
                if [ ! -e "$files_to_inspect" ]
                then
                        touch "$files_to_inspect"
                        chmod 0640 "$files_to_inspect"
                fi
        fi
fi

#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0

for audit_file in "${files_to_inspect[@]}"
do

        # Filter existing $audit_file rules' definitions to select those that:
        # * follow the rule pattern, and
        # * meet the hardware architecture requirement, and
        # * are current syscall group specific
        IFS=$'\n' existing_rules=($(sed -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d"  "$audit_file"))
        # Reset IFS back to default
        unset $IFS

        # Process rules found case-by-case
        for rule in "${existing_rules[@]}"
        do
                # Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
                if [ "${rule}" != "${full_rule}" ]
                then
                        # If so, isolate just '(-S \w)+' substring of that rule
                        rule_syscalls=$(echo $rule | grep -o -P '(-S \w+ )+')
                        # Check if list of '-S syscall' arguments of that rule is subset
                        # of '-S syscall' list of expected $full_rule
                        if grep -q -- "$rule_syscalls" <<< "$full_rule"
                        then
                                # Rule is covered (i.e. the list of -S syscalls for this rule is
                                # subset of -S syscalls of $full_rule => existing rule can be deleted
                                # Thus delete the rule from audit.rules & our array
                                sed -i -e "/$rule/d" "$audit_file"
                                existing_rules=("${existing_rules[@]//$rule/}")
                        else
                                # Rule isn't covered by $full_rule - it besides -S syscall arguments
                                # for this group contains also -S syscall arguments for other syscall
                                # group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
                                # since 'lchown' & 'fchownat' share 'chown' substring
                                # Therefore:
                                # * 1) delete the original rule from audit.rules
                                # (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
                                # * 2) delete the -S syscall arguments for this syscall group, but
                                # keep those not belonging to this syscall group
                                # (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
                                # * 3) append the modified (filtered) rule again into audit.rules
                                # if the same rule not already present
                                #
                                # 1) Delete the original rule
                                sed -i -e "/$rule/d" "$audit_file"
                                # 2) Delete syscalls for this group, but keep those from other groups
                                # Convert current rule syscall's string into array splitting by '-S' delimiter
                                IFS=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
                                # Reset IFS back to default
                                unset $IFS
                                # Declare new empty string to hold '-S syscall' arguments from other groups
                                new_syscalls_for_rule=''
                                # Walk through existing '-S syscall' arguments
                                for syscall_arg in "${rule_syscalls_as_array[@]}"
                                do
                                        # Skip empty $syscall_arg values
                                        if [ "$syscall_arg" == '' ]
                                        then
                                                continue
                                        fi
                                        # If the '-S syscall' doesn't belong to current group add it to the new list
                                        # (together with adding '-S' delimiter back for each of such item found)
                                        if grep -q -v -- "$group" <<< "$syscall_arg"
                                        then
                                                new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
                                        fi
                                done
                                # Replace original '-S syscall' list with the new one for this rule
                                updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
                                # Squeeze repeated whitespace characters in rule definition (if any) into one
                                updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
                                # 3) Append the modified / filtered rule again into audit.rules
                                #    (but only in case it's not present yet to prevent duplicate definitions)
                                if ! grep -q -- "$updated_rule" "$audit_file"
                                then
                                        echo "$updated_rule" >> "$audit_file"
                                fi
                        fi
                else
                        # $audit_file already contains the expected rule form for this
                        # architecture & key => don't insert it second time
                        append_expected_rule=1
                fi
        done

        # We deleted all rules that were subset of the expected one for this arch & key.
        # Also isolated rules containing system calls not from this system calls group.
        # Now append the expected rule if it's not present in $audit_file yet
        if [[ ${append_expected_rule} -eq "0" ]]
        then
                echo "$full_rule" >> "$audit_file"
        fi
done

}

	fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done

Record Events that Modify the System's Discretionary Access Controls - fsetxattrrule

At a minimum the audit system should collect file permission changes for all users and root. Add the following to /etc/audit/audit.rules:

-a always,exit -F arch=b32 -S fsetxattr -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S fsetxattr -F auid>=500 -F auid!=4294967295 -k perm_mod

warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.
Rationale:

The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.

identifiers:  CCE-27180-9, DISA FSO RHEL-06-000191

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 126

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


# Perform the remediation for 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
	PATTERN="-a always,exit .* -F auid>=500 -F auid!=4294967295 -k *"
	GROUP="xattr"
	FULL_RULE="-a always,exit -F arch=${ARCH} -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod"

function fix_audit_syscall_rule {

# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"

# Check sanity of the input
if [ $# -ne "5" ]
then
        echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
        echo "Aborting."
        exit 1
fi

# 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  |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect

# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
        echo "Unknown audit rules loading tool: $1. Aborting."
        echo "Use either 'auditctl' or 'augenrules'!"
        exit 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("${files_to_inspect[@]}" '/etc/audit/audit.rules' )
# 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
elif [ "$tool" == 'augenrules' ]
then
        # Extract audit $key from audit rule so we can use it later
        key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
        # Check if particular audit rule is already defined
        IFS=$'\n' matches=($(sed -s -n -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules))
        # Reset IFS back to default
        unset $IFS
        for match in "${matches[@]}"
        do
                files_to_inspect=("${files_to_inspect[@]}" "${match}")
        done
        # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
        if [ ${#files_to_inspect[@]} -eq "0" ]
        then
                files_to_inspect="/etc/audit/rules.d/$key.rules"
                if [ ! -e "$files_to_inspect" ]
                then
                        touch "$files_to_inspect"
                        chmod 0640 "$files_to_inspect"
                fi
        fi
fi

#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0

for audit_file in "${files_to_inspect[@]}"
do

        # Filter existing $audit_file rules' definitions to select those that:
        # * follow the rule pattern, and
        # * meet the hardware architecture requirement, and
        # * are current syscall group specific
        IFS=$'\n' existing_rules=($(sed -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d"  "$audit_file"))
        # Reset IFS back to default
        unset $IFS

        # Process rules found case-by-case
        for rule in "${existing_rules[@]}"
        do
                # Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
                if [ "${rule}" != "${full_rule}" ]
                then
                        # If so, isolate just '(-S \w)+' substring of that rule
                        rule_syscalls=$(echo $rule | grep -o -P '(-S \w+ )+')
                        # Check if list of '-S syscall' arguments of that rule is subset
                        # of '-S syscall' list of expected $full_rule
                        if grep -q -- "$rule_syscalls" <<< "$full_rule"
                        then
                                # Rule is covered (i.e. the list of -S syscalls for this rule is
                                # subset of -S syscalls of $full_rule => existing rule can be deleted
                                # Thus delete the rule from audit.rules & our array
                                sed -i -e "/$rule/d" "$audit_file"
                                existing_rules=("${existing_rules[@]//$rule/}")
                        else
                                # Rule isn't covered by $full_rule - it besides -S syscall arguments
                                # for this group contains also -S syscall arguments for other syscall
                                # group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
                                # since 'lchown' & 'fchownat' share 'chown' substring
                                # Therefore:
                                # * 1) delete the original rule from audit.rules
                                # (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
                                # * 2) delete the -S syscall arguments for this syscall group, but
                                # keep those not belonging to this syscall group
                                # (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
                                # * 3) append the modified (filtered) rule again into audit.rules
                                # if the same rule not already present
                                #
                                # 1) Delete the original rule
                                sed -i -e "/$rule/d" "$audit_file"
                                # 2) Delete syscalls for this group, but keep those from other groups
                                # Convert current rule syscall's string into array splitting by '-S' delimiter
                                IFS=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
                                # Reset IFS back to default
                                unset $IFS
                                # Declare new empty string to hold '-S syscall' arguments from other groups
                                new_syscalls_for_rule=''
                                # Walk through existing '-S syscall' arguments
                                for syscall_arg in "${rule_syscalls_as_array[@]}"
                                do
                                        # Skip empty $syscall_arg values
                                        if [ "$syscall_arg" == '' ]
                                        then
                                                continue
                                        fi
                                        # If the '-S syscall' doesn't belong to current group add it to the new list
                                        # (together with adding '-S' delimiter back for each of such item found)
                                        if grep -q -v -- "$group" <<< "$syscall_arg"
                                        then
                                                new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
                                        fi
                                done
                                # Replace original '-S syscall' list with the new one for this rule
                                updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
                                # Squeeze repeated whitespace characters in rule definition (if any) into one
                                updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
                                # 3) Append the modified / filtered rule again into audit.rules
                                #    (but only in case it's not present yet to prevent duplicate definitions)
                                if ! grep -q -- "$updated_rule" "$audit_file"
                                then
                                        echo "$updated_rule" >> "$audit_file"
                                fi
                        fi
                else
                        # $audit_file already contains the expected rule form for this
                        # architecture & key => don't insert it second time
                        append_expected_rule=1
                fi
        done

        # We deleted all rules that were subset of the expected one for this arch & key.
        # Also isolated rules containing system calls not from this system calls group.
        # Now append the expected rule if it's not present in $audit_file yet
        if [[ ${append_expected_rule} -eq "0" ]]
        then
                echo "$full_rule" >> "$audit_file"
        fi
done

}

	fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done

Record Events that Modify the System's Discretionary Access Controls - lchownrule

At a minimum the audit system should collect file permission changes for all users and root. Add the following to /etc/audit/audit.rules:

-a always,exit -F arch=b32 -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod

warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.
Rationale:

The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.

identifiers:  CCE-27181-7, DISA FSO RHEL-06-000192

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 126

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


# Perform the remediation for 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
	PATTERN="-a always,exit -F arch=$ARCH -S .* -F auid>=500 -F auid!=4294967295 -k *"
	GROUP="chown"
	FULL_RULE="-a always,exit -F arch=$ARCH -S chown -S fchown -S fchownat -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod"

function fix_audit_syscall_rule {

# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"

# Check sanity of the input
if [ $# -ne "5" ]
then
        echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
        echo "Aborting."
        exit 1
fi

# 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  |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect

# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
        echo "Unknown audit rules loading tool: $1. Aborting."
        echo "Use either 'auditctl' or 'augenrules'!"
        exit 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("${files_to_inspect[@]}" '/etc/audit/audit.rules' )
# 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
elif [ "$tool" == 'augenrules' ]
then
        # Extract audit $key from audit rule so we can use it later
        key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
        # Check if particular audit rule is already defined
        IFS=$'\n' matches=($(sed -s -n -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules))
        # Reset IFS back to default
        unset $IFS
        for match in "${matches[@]}"
        do
                files_to_inspect=("${files_to_inspect[@]}" "${match}")
        done
        # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
        if [ ${#files_to_inspect[@]} -eq "0" ]
        then
                files_to_inspect="/etc/audit/rules.d/$key.rules"
                if [ ! -e "$files_to_inspect" ]
                then
                        touch "$files_to_inspect"
                        chmod 0640 "$files_to_inspect"
                fi
        fi
fi

#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0

for audit_file in "${files_to_inspect[@]}"
do

        # Filter existing $audit_file rules' definitions to select those that:
        # * follow the rule pattern, and
        # * meet the hardware architecture requirement, and
        # * are current syscall group specific
        IFS=$'\n' existing_rules=($(sed -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d"  "$audit_file"))
        # Reset IFS back to default
        unset $IFS

        # Process rules found case-by-case
        for rule in "${existing_rules[@]}"
        do
                # Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
                if [ "${rule}" != "${full_rule}" ]
                then
                        # If so, isolate just '(-S \w)+' substring of that rule
                        rule_syscalls=$(echo $rule | grep -o -P '(-S \w+ )+')
                        # Check if list of '-S syscall' arguments of that rule is subset
                        # of '-S syscall' list of expected $full_rule
                        if grep -q -- "$rule_syscalls" <<< "$full_rule"
                        then
                                # Rule is covered (i.e. the list of -S syscalls for this rule is
                                # subset of -S syscalls of $full_rule => existing rule can be deleted
                                # Thus delete the rule from audit.rules & our array
                                sed -i -e "/$rule/d" "$audit_file"
                                existing_rules=("${existing_rules[@]//$rule/}")
                        else
                                # Rule isn't covered by $full_rule - it besides -S syscall arguments
                                # for this group contains also -S syscall arguments for other syscall
                                # group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
                                # since 'lchown' & 'fchownat' share 'chown' substring
                                # Therefore:
                                # * 1) delete the original rule from audit.rules
                                # (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
                                # * 2) delete the -S syscall arguments for this syscall group, but
                                # keep those not belonging to this syscall group
                                # (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
                                # * 3) append the modified (filtered) rule again into audit.rules
                                # if the same rule not already present
                                #
                                # 1) Delete the original rule
                                sed -i -e "/$rule/d" "$audit_file"
                                # 2) Delete syscalls for this group, but keep those from other groups
                                # Convert current rule syscall's string into array splitting by '-S' delimiter
                                IFS=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
                                # Reset IFS back to default
                                unset $IFS
                                # Declare new empty string to hold '-S syscall' arguments from other groups
                                new_syscalls_for_rule=''
                                # Walk through existing '-S syscall' arguments
                                for syscall_arg in "${rule_syscalls_as_array[@]}"
                                do
                                        # Skip empty $syscall_arg values
                                        if [ "$syscall_arg" == '' ]
                                        then
                                                continue
                                        fi
                                        # If the '-S syscall' doesn't belong to current group add it to the new list
                                        # (together with adding '-S' delimiter back for each of such item found)
                                        if grep -q -v -- "$group" <<< "$syscall_arg"
                                        then
                                                new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
                                        fi
                                done
                                # Replace original '-S syscall' list with the new one for this rule
                                updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
                                # Squeeze repeated whitespace characters in rule definition (if any) into one
                                updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
                                # 3) Append the modified / filtered rule again into audit.rules
                                #    (but only in case it's not present yet to prevent duplicate definitions)
                                if ! grep -q -- "$updated_rule" "$audit_file"
                                then
                                        echo "$updated_rule" >> "$audit_file"
                                fi
                        fi
                else
                        # $audit_file already contains the expected rule form for this
                        # architecture & key => don't insert it second time
                        append_expected_rule=1
                fi
        done

        # We deleted all rules that were subset of the expected one for this arch & key.
        # Also isolated rules containing system calls not from this system calls group.
        # Now append the expected rule if it's not present in $audit_file yet
        if [[ ${append_expected_rule} -eq "0" ]]
        then
                echo "$full_rule" >> "$audit_file"
        fi
done

}

	fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done

Record Events that Modify the System's Discretionary Access Controls - lremovexattrrule

At a minimum the audit system should collect file permission changes for all users and root. Add the following to /etc/audit/audit.rules:

-a always,exit -F arch=b32 -S lremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S lremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod

warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.
Rationale:

The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.

identifiers:  CCE-27182-5, DISA FSO RHEL-06-000193

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 126

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


# Perform the remediation for 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
	PATTERN="-a always,exit .* -F auid>=500 -F auid!=4294967295 -k *"
	GROUP="xattr"
	FULL_RULE="-a always,exit -F arch=${ARCH} -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod"

function fix_audit_syscall_rule {

# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"

# Check sanity of the input
if [ $# -ne "5" ]
then
        echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
        echo "Aborting."
        exit 1
fi

# 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  |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect

# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
        echo "Unknown audit rules loading tool: $1. Aborting."
        echo "Use either 'auditctl' or 'augenrules'!"
        exit 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("${files_to_inspect[@]}" '/etc/audit/audit.rules' )
# 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
elif [ "$tool" == 'augenrules' ]
then
        # Extract audit $key from audit rule so we can use it later
        key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
        # Check if particular audit rule is already defined
        IFS=$'\n' matches=($(sed -s -n -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules))
        # Reset IFS back to default
        unset $IFS
        for match in "${matches[@]}"
        do
                files_to_inspect=("${files_to_inspect[@]}" "${match}")
        done
        # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
        if [ ${#files_to_inspect[@]} -eq "0" ]
        then
                files_to_inspect="/etc/audit/rules.d/$key.rules"
                if [ ! -e "$files_to_inspect" ]
                then
                        touch "$files_to_inspect"
                        chmod 0640 "$files_to_inspect"
                fi
        fi
fi

#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0

for audit_file in "${files_to_inspect[@]}"
do

        # Filter existing $audit_file rules' definitions to select those that:
        # * follow the rule pattern, and
        # * meet the hardware architecture requirement, and
        # * are current syscall group specific
        IFS=$'\n' existing_rules=($(sed -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d"  "$audit_file"))
        # Reset IFS back to default
        unset $IFS

        # Process rules found case-by-case
        for rule in "${existing_rules[@]}"
        do
                # Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
                if [ "${rule}" != "${full_rule}" ]
                then
                        # If so, isolate just '(-S \w)+' substring of that rule
                        rule_syscalls=$(echo $rule | grep -o -P '(-S \w+ )+')
                        # Check if list of '-S syscall' arguments of that rule is subset
                        # of '-S syscall' list of expected $full_rule
                        if grep -q -- "$rule_syscalls" <<< "$full_rule"
                        then
                                # Rule is covered (i.e. the list of -S syscalls for this rule is
                                # subset of -S syscalls of $full_rule => existing rule can be deleted
                                # Thus delete the rule from audit.rules & our array
                                sed -i -e "/$rule/d" "$audit_file"
                                existing_rules=("${existing_rules[@]//$rule/}")
                        else
                                # Rule isn't covered by $full_rule - it besides -S syscall arguments
                                # for this group contains also -S syscall arguments for other syscall
                                # group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
                                # since 'lchown' & 'fchownat' share 'chown' substring
                                # Therefore:
                                # * 1) delete the original rule from audit.rules
                                # (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
                                # * 2) delete the -S syscall arguments for this syscall group, but
                                # keep those not belonging to this syscall group
                                # (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
                                # * 3) append the modified (filtered) rule again into audit.rules
                                # if the same rule not already present
                                #
                                # 1) Delete the original rule
                                sed -i -e "/$rule/d" "$audit_file"
                                # 2) Delete syscalls for this group, but keep those from other groups
                                # Convert current rule syscall's string into array splitting by '-S' delimiter
                                IFS=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
                                # Reset IFS back to default
                                unset $IFS
                                # Declare new empty string to hold '-S syscall' arguments from other groups
                                new_syscalls_for_rule=''
                                # Walk through existing '-S syscall' arguments
                                for syscall_arg in "${rule_syscalls_as_array[@]}"
                                do
                                        # Skip empty $syscall_arg values
                                        if [ "$syscall_arg" == '' ]
                                        then
                                                continue
                                        fi
                                        # If the '-S syscall' doesn't belong to current group add it to the new list
                                        # (together with adding '-S' delimiter back for each of such item found)
                                        if grep -q -v -- "$group" <<< "$syscall_arg"
                                        then
                                                new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
                                        fi
                                done
                                # Replace original '-S syscall' list with the new one for this rule
                                updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
                                # Squeeze repeated whitespace characters in rule definition (if any) into one
                                updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
                                # 3) Append the modified / filtered rule again into audit.rules
                                #    (but only in case it's not present yet to prevent duplicate definitions)
                                if ! grep -q -- "$updated_rule" "$audit_file"
                                then
                                        echo "$updated_rule" >> "$audit_file"
                                fi
                        fi
                else
                        # $audit_file already contains the expected rule form for this
                        # architecture & key => don't insert it second time
                        append_expected_rule=1
                fi
        done

        # We deleted all rules that were subset of the expected one for this arch & key.
        # Also isolated rules containing system calls not from this system calls group.
        # Now append the expected rule if it's not present in $audit_file yet
        if [[ ${append_expected_rule} -eq "0" ]]
        then
                echo "$full_rule" >> "$audit_file"
        fi
done

}

	fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done

Record Events that Modify the System's Discretionary Access Controls - lsetxattrrule

At a minimum the audit system should collect file permission changes for all users and root. Add the following to /etc/audit/audit.rules:

-a always,exit -F arch=b32 -S lsetxattr -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S lsetxattr -F auid>=500 -F auid!=4294967295 -k perm_mod

warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.
Rationale:

The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.

identifiers:  CCE-27183-3, DISA FSO RHEL-06-000194

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 126

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


# Perform the remediation for 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
	PATTERN="-a always,exit .* -F auid>=500 -F auid!=4294967295 -k *"
	GROUP="xattr"
	FULL_RULE="-a always,exit -F arch=${ARCH} -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod"

function fix_audit_syscall_rule {

# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"

# Check sanity of the input
if [ $# -ne "5" ]
then
        echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
        echo "Aborting."
        exit 1
fi

# 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  |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect

# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
        echo "Unknown audit rules loading tool: $1. Aborting."
        echo "Use either 'auditctl' or 'augenrules'!"
        exit 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("${files_to_inspect[@]}" '/etc/audit/audit.rules' )
# 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
elif [ "$tool" == 'augenrules' ]
then
        # Extract audit $key from audit rule so we can use it later
        key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
        # Check if particular audit rule is already defined
        IFS=$'\n' matches=($(sed -s -n -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules))
        # Reset IFS back to default
        unset $IFS
        for match in "${matches[@]}"
        do
                files_to_inspect=("${files_to_inspect[@]}" "${match}")
        done
        # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
        if [ ${#files_to_inspect[@]} -eq "0" ]
        then
                files_to_inspect="/etc/audit/rules.d/$key.rules"
                if [ ! -e "$files_to_inspect" ]
                then
                        touch "$files_to_inspect"
                        chmod 0640 "$files_to_inspect"
                fi
        fi
fi

#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0

for audit_file in "${files_to_inspect[@]}"
do

        # Filter existing $audit_file rules' definitions to select those that:
        # * follow the rule pattern, and
        # * meet the hardware architecture requirement, and
        # * are current syscall group specific
        IFS=$'\n' existing_rules=($(sed -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d"  "$audit_file"))
        # Reset IFS back to default
        unset $IFS

        # Process rules found case-by-case
        for rule in "${existing_rules[@]}"
        do
                # Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
                if [ "${rule}" != "${full_rule}" ]
                then
                        # If so, isolate just '(-S \w)+' substring of that rule
                        rule_syscalls=$(echo $rule | grep -o -P '(-S \w+ )+')
                        # Check if list of '-S syscall' arguments of that rule is subset
                        # of '-S syscall' list of expected $full_rule
                        if grep -q -- "$rule_syscalls" <<< "$full_rule"
                        then
                                # Rule is covered (i.e. the list of -S syscalls for this rule is
                                # subset of -S syscalls of $full_rule => existing rule can be deleted
                                # Thus delete the rule from audit.rules & our array
                                sed -i -e "/$rule/d" "$audit_file"
                                existing_rules=("${existing_rules[@]//$rule/}")
                        else
                                # Rule isn't covered by $full_rule - it besides -S syscall arguments
                                # for this group contains also -S syscall arguments for other syscall
                                # group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
                                # since 'lchown' & 'fchownat' share 'chown' substring
                                # Therefore:
                                # * 1) delete the original rule from audit.rules
                                # (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
                                # * 2) delete the -S syscall arguments for this syscall group, but
                                # keep those not belonging to this syscall group
                                # (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
                                # * 3) append the modified (filtered) rule again into audit.rules
                                # if the same rule not already present
                                #
                                # 1) Delete the original rule
                                sed -i -e "/$rule/d" "$audit_file"
                                # 2) Delete syscalls for this group, but keep those from other groups
                                # Convert current rule syscall's string into array splitting by '-S' delimiter
                                IFS=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
                                # Reset IFS back to default
                                unset $IFS
                                # Declare new empty string to hold '-S syscall' arguments from other groups
                                new_syscalls_for_rule=''
                                # Walk through existing '-S syscall' arguments
                                for syscall_arg in "${rule_syscalls_as_array[@]}"
                                do
                                        # Skip empty $syscall_arg values
                                        if [ "$syscall_arg" == '' ]
                                        then
                                                continue
                                        fi
                                        # If the '-S syscall' doesn't belong to current group add it to the new list
                                        # (together with adding '-S' delimiter back for each of such item found)
                                        if grep -q -v -- "$group" <<< "$syscall_arg"
                                        then
                                                new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
                                        fi
                                done
                                # Replace original '-S syscall' list with the new one for this rule
                                updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
                                # Squeeze repeated whitespace characters in rule definition (if any) into one
                                updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
                                # 3) Append the modified / filtered rule again into audit.rules
                                #    (but only in case it's not present yet to prevent duplicate definitions)
                                if ! grep -q -- "$updated_rule" "$audit_file"
                                then
                                        echo "$updated_rule" >> "$audit_file"
                                fi
                        fi
                else
                        # $audit_file already contains the expected rule form for this
                        # architecture & key => don't insert it second time
                        append_expected_rule=1
                fi
        done

        # We deleted all rules that were subset of the expected one for this arch & key.
        # Also isolated rules containing system calls not from this system calls group.
        # Now append the expected rule if it's not present in $audit_file yet
        if [[ ${append_expected_rule} -eq "0" ]]
        then
                echo "$full_rule" >> "$audit_file"
        fi
done

}

	fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done

Record Events that Modify the System's Discretionary Access Controls - removexattrrule

At a minimum the audit system should collect file permission changes for all users and root. Add the following to /etc/audit/audit.rules:

-a always,exit -F arch=b32 -S removexattr -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S removexattr -F auid>=500 -F auid!=4294967295 -k perm_mod

warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.
Rationale:

The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.

identifiers:  CCE-27184-1, DISA FSO RHEL-06-000195

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 126

Remediation Shell script:   (show)

Complexity:low
Disruption:medium
Reboot:true
Strategy:disable


# Perform the remediation for 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
	PATTERN="-a always,exit .* -F auid>=500 -F auid!=4294967295 -k *"
	GROUP="xattr"
	FULL_RULE="-a always,exit -F arch=${ARCH} -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod"

function fix_audit_syscall_rule {

# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"

# Check sanity of the input
if [ $# -ne "5" ]
then
        echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
        echo "Aborting."
        exit 1
fi

# 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  |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect

# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
        echo "Unknown audit rules loading tool: $1. Aborting."
        echo "Use either 'auditctl' or 'augenrules'!"
        exit 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("${files_to_inspect[@]}" '/etc/audit/audit.rules' )
# 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
elif [ "$tool" == 'augenrules' ]
then
        # Extract audit $key from audit rule so we can use it later
        key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
        # Check if particular audit rule is already defined
        IFS=$'\n' matches=($(sed -s -n -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules))
        # Reset IFS back to default
        unset $IFS
        for match in "${matches[@]}"
        do
                files_to_inspect=("${files_to_inspect[@]}" "${match}")
        done
        # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
        if [ ${#files_to_inspect[@]} -eq "0" ]
        then
                files_to_inspect="/etc/audit/rules.d/$key.rules"
                if [ ! -e "$files_to_inspect" ]
                then
                        touch "$files_to_inspect"
                        chmod 0640 "$files_to_inspect"
                fi
        fi
fi

#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0

for audit_file in "${files_to_inspect[@]}"
do

        # Filter existing $audit_file rules' definitions to select those that:
        # * follow the rule pattern, and
        # * meet the hardware architecture requirement, and
        # * are current syscall group specific
        IFS=$'\n' existing_rules=($(sed -e "/${pattern}/!d" -e "/${arch}/!d" -e "/${group}/!d"  "$audit_file"))
        # Reset IFS back to default
        unset $IFS

        # Process rules found case-by-case
        for rule in "${existing_rules[@]}"
        do
                # Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
                if [ "${rule}" != "${full_rule}" ]
                then
                        # If so, isolate just '(-S \w)+' substring of that rule
                        rule_syscalls=$(echo $rule | grep -o -P '(-S \w+ )+')
                        # Check if list of '-S syscall' arguments of that rule is subset
                        # of '-S syscall' list of expected $full_rule
                        if grep -q -- "$rule_syscalls" <<< "$full_rule"
                        then
                                # Rule is covered (i.e. the list of -S syscalls for this rule is
                                # subset of -S syscalls of $full_rule => existing rule can be deleted
                                # Thus delete the rule from audit.rules & our array
                                sed -i -e "/$rule/d" "$audit_file"
                                existing_rules=("${existing_rules[@]//$rule/}")
                        else
                                # Rule isn't covered by $full_rule - it besides -S syscall arguments
                                # for this group contains also -S syscall arguments for other syscall
                                # group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
                                # since 'lchown' & 'fchownat' share 'chown' substring
                                # Therefore:
                                # * 1) delete the original rule from audit.rules
                                # (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
                                # * 2) delete the -S syscall arguments for this syscall group, but
                                # keep those not belonging to this syscall group
                                # (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
                                # * 3) append the modified (filtered) rule again into audit.rules
                                # if the same rule not already present
                                #
                                # 1) Delete the original rule
                                sed -i -e "/$rule/d" "$audit_file"
                                # 2) Delete syscalls for this group, but keep those from other groups
                                # Convert current rule syscall's string into array splitting by '-S' delimiter
                                IFS=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
                                # Reset IFS back to default
                                unset $IFS
                                # Declare new empty string to hold '-S syscall' arguments from other groups
                                new_syscalls_for_rule=''
                                # Walk through existing '-S syscall' arguments
                                for syscall_arg in "${rule_syscalls_as_array[@]}"
                                do
                                        # Skip empty $syscall_arg values
                                        if [ "$syscall_arg" == '' ]
                                        then
                                                continue
                                        fi
                                        # If the '-S syscall' doesn't belong to current group add it to the new list
                                        # (together with adding '-S' delimiter back for each of such item found)
                                        if grep -q -v -- "$group" <<< "$syscall_arg"
                                        then
                                                new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
                                        fi
                                done
                                # Replace original '-S syscall' list with the new one for this rule
                                updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
                                # Squeeze repeated whitespace characters in rule definition (if any) into one
                                updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
                                # 3) Append the modified / filtered rule again into audit.rules
                                #    (but only in case it's not present yet to prevent duplicate definitions)
                                if ! grep -q -- "$updated_rule" "$audit_file"
                                then
                                        echo "$updated_rule" >> "$audit_file"
                                fi
                        fi
                else
                        # $audit_file already contains the expected rule form for this
                        # architecture & key => don't insert it second time
                        append_expected_rule=1
                fi
        done

        # We deleted all rules that were subset of the expected one for this arch & key.
        # Also isolated rules containing system calls not from this system calls group.
        # Now append the expected rule if it's not present in $audit_file yet
        if [[ ${append_expected_rule} -eq "0" ]]
        then
                echo "$full_rule" >> "$audit_file"
        fi
done

}

	fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done

Record Events that Modify the System's Discretionary Access Controls - setxattrrule

At a minimum the audit system should collect file permission changes for all users and root. Add the following to /etc/audit/audit.rules:

-a always,exit -F arch=b32 -S setxattr -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S setxattr -F auid>=500 -F auid!=4294967295 -k perm_mod

warning  Note that these rules can be configured in a number of ways while still achieving the desired effect. Here the system calls have been placed independent of other system calls. Grouping these system calls with others as identifying earlier in this guide is more efficient.
Rationale:

The changing of file permissions could indicate that a user is attempting to gain access to information that would otherwise be disallowed. Auditing DAC modifications can facilitate the identification of patterns of abuse among both authorized and unauthorized users.