Guide to the Secure Configuration of Red Hat Enterprise Linux 6

with profile PCI-DSS v3 Control Baseline for Red Hat Enterprise Linux 6
This is a *draft* profile for PCI-DSS v3

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 TitlePCI-DSS v3 Control Baseline for Red Hat Enterprise Linux 6
Profile IDxccdf_org.ssgproject.content_profile_pci-dss_centric

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. 2.
    1. 2.1
    2. 2.2
    3. 2.3
    4. 2.4
    5. 2.5
    6. 2.6
  2. 3.
    1. 3.1
    2. 3.2
    3. 3.3
    4. 3.4
    5. 3.5
    6. 3.6
    7. 3.7
  3. 4.
    1. 4.1
    2. 4.2
    3. 4.3
  4. 5.
    1. 5.1
    2. 5.2
    3. 5.3
    4. 5.4
  5. 6.
    1. 6.1
    2. 6.2
    3. 6.3
    4. 6.4
    5. 6.5
    6. 6.6
    7. 6.7
  6. 7.
    1. 7.1
    2. 7.2
    3. 7.3
  7. 8.
    1. 8.1
    2. 8.2
    3. 8.3
    4. 8.4
    5. 8.5
    6. 8.6
    7. 8.7
    8. 8.8
  8. 10.
    1. 10.1
    2. 10.2
    3. 10.3
    4. 10.4
    5. 10.5
    6. 10.6
    7. 10.7
    8. 10.8
  9. 11.
    1. 11.1
    2. 11.2
    3. 11.3
    4. 11.4
    5. 11.5
    6. 11.6
  10. Values
  11. Non PCI-DSS

Checklist

contains 94 rules

2.group

Do not use vendor-supplied defaults for system passwords and other

2.1group

Always change vendor-supplied

2.1.1group

For wireless environments

2.1.1.agroup

Interview responsible personnel and examine

2.1.1.bgroup

Interview personnel and examine policies and

2.1.1.cgroup

Examine vendor documentation and login to

2.1.1.dgroup

Examine vendor documentation and observe

2.1.1.egroup

Examine vendor documentation and observe

2.1.agroup

Choose a sample of system components, and attempt

2.1.bgroup

For the sample of system components, verify that all

2.1.cgroup

Interview personnel and examine supporting

2.2group

Develop configuration standards for

2.2.1group

Implement only one primary

2.2.1.agroup

Select a sample of system components and

2.2.1.bgroup

If virtualization technologies are used, inspect the

2.2.2group

Enable only necessary services,

2.2.2.agroup

Select a sample of system components and

2.2.2.bgroup

Identify any enabled insecure services, daemons,

2.2.3group

Implement additional security

2.2.3.agroup

Inspect configuration settings to verify that security

2.2.4group

Configure system security

2.2.4.agroup

Interview system administrators and/or security

2.2.4.bgroup

Examine the system configuration standards to

2.2.4.cgroup

Select a sample of system components and

2.2.5group

Remove all unnecessary

2.2.5.agroup

Select a sample of system components and

2.2.5.bgroup

. Examine the documentation and security

2.2.5.cgroup

. Examine the documentation and security

2.2.agroup

2.2.bgroup

Examine policies and interview personnel to

2.2.cgroup

Examine policies and interview personnel to

2.2.dgroup

Verify that system configuration standards include the

2.3group

Encrypt all non-console

2.3.agroup

Observe an administrator log on to each system and

2.3.bgroup

Review services and parameter files on systems to

2.3.cgroup

Observe an administrator log on to each system to

2.3.dgroup

Examine vendor documentation and interview

2.4group

Maintain an inventory of system

2.4.agroup

Examine system inventory to verify that a list of

2.4.bgroup

Interview personnel to verify the documented inventory

2.5group

Ensure that security policies and

2.6group

Shared hosting providers must

3.group

Protect stored cardholder data

3.1group

Keep cardholder data storage to a

3.1.agroup

Examine the data retention and disposal policies,

3.1.bgroup

Interview personnel to verify that:

3.1.cgroup

For a sample of system components that store cardholder

3.2group

Do not store sensitive authentication

3.2.1group

Do not store the full contents of

3.2.2group

Do not store the card verification

3.2.3group

Do not store the personal

3.2.agroup

For issuers and/or companies that support issuing

3.2.bgroup

For issuers and/or companies that support issuing

3.2.cgroup

For all other entities, if sensitive authentication data is

3.2.dgroup

For all other entities, if sensitive authentication data is

3.3group

Mask PAN when displayed (the first

3.3.agroup

Examine written policies and procedures for masking the

3.3.bgroup

Examine system configurations to verify that full PAN is

3.3.cgroup

Examine displays of PAN (for example, on screen, on

3.4group

Render PAN unreadable anywhere it

3.4.1group

If disk encryption is used (rather

3.4.1.agroup

If disk encryption is used, inspect the configuration

3.4.1.bgroup

Observe processes and interview personnel to verify

3.4.1.cgroup

Examine the configurations and observe the

3.4.agroup

Examine documentation about the system used to protect

3.4.bgroup

Examine several tables or files from a sample of data

3.4.cgroup

Examine a sample of removable media (for example,

3.4.dgroup

Examine a sample of audit logs to confirm that the PAN is

3.4.egroup

If

3.5group

Document and implement

3.5.1group

Restrict access to cryptographic

3.5.2group

Store secret and private keys

3.5.2.agroup

Examine documented procedures to verify that

3.5.2.bgroup

Examine system configurations and key storage

3.5.2.cgroup

Wherever key-encrypting keys are used, examine

3.5.3group

Store cryptographic keys in the

3.6group

Fully document and implement all

3.6.1group

Generation of strong

3.6.1.agroup

Verify that key-management procedures specify how

3.6.1.bgroup

Observe the method for generating keys to verify that

3.6.2group

Secure cryptographic key

3.6.2.agroup

Verify that key-management procedures specify how

3.6.2.bgroup

Observe the method for distributing keys to verify that

3.6.3group

Secure cryptographic key storage

3.6.3.agroup

Verify that key-management procedures specify how

3.6.3.bgroup

Observe the method for storing keys to verify that

3.6.4group

Cryptographic key changes for

3.6.4.agroup

Verify that key-management procedures include a

3.6.4.bgroup

Interview personnel to verify that keys are changed at

3.6.5group

Retirement or replacement (for

3.6.5.agroup

Verify that key-management procedures specify

3.6.5.bgroup

Interview personnel to verify the following processes

3.6.6group

If manual clear-text cryptographic

3.6.6.agroup

Verify that manual clear-text key-management

3.6.7group

Prevention of unauthorized

3.6.7.agroup

Verify that key-management procedures specify

3.6.7.bgroup

Interview personnel and/or observe processes to

3.6.8group

Requirement for cryptographic

3.6.8.agroup

Verify that key-management procedures specify

3.6.8.bgroup

Observe documentation or other evidence showing

3.6.bgroup

Examine the key-management procedures and processes

3.7group

Ensure that security policies and

4.group

Encrypt transmission of cardholder data across open, public networks

4.1group

Use strong cryptography and security

4.1.1group

Ensure wireless networks transmitting

4.1.agroup

Identify all locations where cardholder data is

4.1.bgroup

Review documented policies and procedures to verify

4.1.cgroup

Select and observe a sample of inbound and outbound

4.1.dgroup

Examine keys and certificates to verify that only

4.1.egroup

Examine system configurations to verify that the

4.1.fgroup

Examine system configurations to verify that the proper

4.1.ggroup

For TLS implementations, examine system

4.2group

Never send unprotected PANs by end-

4.2.agroup

If end-user messaging technologies are used to send

4.2.bgroup

Review written policies to verify the existence of a

4.3group

Ensure that security policies and

5.group

Protect all systems against malware and regularly update anti-virus

5.1group

Deploy anti-virus software on all

5.1.1group

Ensure that anti-virus programs

5.1.2group

For systems considered to be not

5.2group

Ensure that all anti-virus mechanisms

5.2.agroup

Examine policies and procedures to verify that anti-virus

5.2.bgroup

Examine anti-virus configurations, including the master

5.2.cgroup

Examine a sample of system components, including all

5.2.dgroup

Examine anti-virus configurations, including the master

5.3group

Ensure that anti-virus mechanisms

5.3.agroup

Examine anti-virus configurations, including the master

5.3.bgroup

Examine anti-virus configurations, including the master

5.3.cgroup

Interview responsible personnel and observe processes to

5.4group

Ensure that security policies and

6.group

Develop and maintain secure systems and applications

6.1group

Establish a process to identify security

6.1.agroup

Examine policies and procedures to verify that

6.1.bgroup

Interview responsible personnel and observe

6.2group

Ensure that all system components and

6.2.agroup

Examine policies and procedures related to security-

6.2.bgroup

For a sample of system components and related

6.3group

Develop internal and external software

6.3.1group

Remove development, test and/or

6.3.2group

Review custom code prior to release

6.3.2.agroup

Examine written software-development procedures

6.3.2.bgroup

Select a sample of recent custom application

6.3.agroup

Examine written software-development processes to

6.3.bgroup

Examine written software-development processes to

6.3.cgroup

Examine written software-development processes to

6.3.dgroup

Interview software developers to verify that written

6.4group

Follow change control processes and

6.4.1group

Separate development/test

6.4.1.agroup

Examine network documentation and network

6.4.1.bgroup

Examine access controls settings to verify that

6.4.2group

Separation of duties between

6.4.3group

Production data (live PANs) are not

6.4.3.agroup

Observe testing processes and interview

6.4.3.bgroup

Examine a sample of test data to verify production

6.4.4group

Removal of test data and accounts

6.4.4.agroup

Observe testing processes and interview

6.4.4.bgroup

Examine a sample of data and accounts from

6.4.5group

Change control procedures for the

6.4.5.agroup

Examine documented change control procedures

6.4.5.bgroup

For a sample of system components, interview

6.5group

Address common coding vulnerabilities in

6.5.1group

Injection flaws, particularly SQL

6.5.10group

Broken authentication and session

6.5.2group

Buffer overflows

6.5.3group

Insecure cryptographic storage

6.5.4group

Insecure communications

6.5.5group

Improper error handling

6.5.6group

Examine software-development policies and

6.5.7group

Cross-site scripting (XSS)

6.5.8group

Improper access control (such as

6.5.9group

Cross-site request forgery (CSRF)

6.5.agroup

Examine software-development policies and

6.5.bgroup

Interview a sample of developers to verify that they are

6.5.cgroup

Examine records of training to verify that software

6.6group

For public-facing web applications,

6.7group

Ensure that security policies and

7.group

Restrict access to cardholder data by business need to know

7.1group

Limit access to system

7.1.1group

Define access needs for

7.1.2group

Restrict access to privileged

7.1.2.agroup

Interview personnel responsible for assigning access to

7.1.2.bgroup

Select a sample of user IDs with privileged access and

7.1.3group

Assign access based on

7.1.4group

Require documented

7.2group

Establish an access control

7.2.1group

Coverage of all system

7.2.2group

Assignment of privileges to

7.2.3group

7.3group

Ensure that security policies and

8.group

Identify and authenticate access to system components

8.1group

Define and implement policies and

8.1.1group

Assign all users a unique ID

8.1.2group

Control addition, deletion, and

8.1.3group

Immediately revoke access for

8.1.3.agroup

Select a sample of users terminated in the past six

8.1.3.bgroup

Verify all physical authentication methods

8.1.4group

Remove/disable inactive user

8.1.5group

Manage IDs used by vendors to

8.1.5.agroup

Interview personnel and observe processes for

8.1.5.bgroup

Interview personnel and observe processes to verify

8.1.6group

Limit repeated access attempts

8.1.6.agroup

For a sample of system components, inspect system

8.1.6.bgroup

8.1.7group

Set the lockout duration to a

8.1.8group

If a session has been idle for

8.1.agroup

Review procedures and confirm they define processes for

8.1.bgroup

Verify that procedures are implemented for user

8.2group

In addition to assigning a unique ID,

8.2.1group

Using strong cryptography,

8.2.1.agroup

Examine vendor documentation and system

8.2.1.bgroup

For a sample of system components, examine

8.2.1.cgroup

For a sample of system components, examine data

8.2.1.dgroup

8.2.2group

Verify user identity before

8.2.3group

Passwords/phrases must meet

8.2.3.agroup

For a sample of system components, inspect system

8.2.3.bgroup

8.2.4group

Change user

8.2.4.agroup

For a sample of system components, inspect system

8.2.4.bgroup

8.2.5group

Do not allow an individual to

8.2.5.agroup

For a sample of system components, obtain and

8.2.5.bgroup

8.2.6group

Set passwords/phrases for first-

8.3group

Incorporate two-factor authentication

8.3.agroup

Examine system configurations for remote access servers

8.3.bgroup

Observe a sample of personnel (for example, users and

8.4group

Document and communicate

8.4.agroup

Examine

8.4.bgroup

Review authentication policies and procedures that are

8.4.cgroup

Interview a sample of users to verify that they are familiar

8.5group

Do not use group, shared, or generic

8.5.1group

8.5.agroup

For a sample of system components, examine user ID lists

8.5.bgroup

Examine authentication policies and procedures to verify

8.5.cgroup

Interview system administrators to verify that group and

8.6group

Where other authentication

8.6.agroup

Examine authentication policies and procedures to verify

8.6.bgroup

Interview security personnel to verify authentication

8.6.cgroup

Examine system configuration settings and/or physical

8.7group

All access to any database

8.7.agroup

Review database and application configuration settings

8.7.bgroup

Examine database and application configuration settings to

8.7.cgroup

Examine database access control settings and database

8.7.dgroup

Examine database access control settings, database

8.8group

Ensure that security policies and

10.group

Track and monitor all access to network resources and cardholder data

10.1group

Implement audit trails to link all

10.2group

Implement automated audit trails for

10.2.1group

All individual user accesses to

10.2.2group

All actions taken by any

10.2.3group

Access to all audit trails

10.2.4group

Invalid logical access attempts

10.2.5group

Use of and changes to

10.2.5.agroup

Verify use of identification and authentication

10.2.5.bgroup

Verify all elevation of privileges is logged.

10.2.5.cgroup

Verify all changes, additions, or deletions to any account

10.2.6group

Initialization, stopping, or

10.2.7group

Creation and deletion of system-

10.3group

Record at least the following audit

10.3.1group

User identification

10.3.2group

Type of event

10.3.3group

Date and time

10.3.4group

Success or failure indication

10.3.5group

Origination of event

10.3.6group

Identity or name of affected

10.4group

Using time-synchronization

10.4.1group

Critical systems have the

10.4.1.agroup

Examine the process for acquiring, distributing and

10.4.1.bgroup

Observe the time-related system-parameter settings for

10.4.2group

Time data is protected.

10.4.2.agroup

Examine system configurations and time-

10.4.2.bgroup

Examine system configurations, time synchronization

10.4.3group

Time settings are received from

10.5group

Secure audit trails so they cannot

10.5.1group

Limit viewing of audit trails to

10.5.2group

Protect audit trail files from

10.5.3group

Promptly back up audit trail files

10.5.4group

Write logs for external-facing

10.5.5group

Use file-integrity monitoring or

10.6group

Review logs and security events for

10.6.1group

Review the following at least

10.6.1.agroup

Examine security policies and procedures to verify that

10.6.1.bgroup

Observe processes and interview personnel to verify

10.6.2group

Review logs of all other system

10.6.2.agroup

Examine security policies and procedures to verify that

10.6.2.bgroup

10.6.3group

Follow up exceptions and

10.6.3.agroup

Examine security policies and procedures to verify that

10.6.3.bgroup

Observe processes and interview personnel to verify

10.7group

Retain audit trail history for at least

10.7.agroup

Examine security policies and procedures to verify that they

10.7.bgroup

Interview personnel and examine audit logs to verify that

10.7.cgroup

Interview personnel and observe processes to verify that at

10.8group

Ensure that security policies and

11.group

Regularly test security systems and processes

11.1group

Implement processes to test for the

11.1.1group

Maintain an inventory of

11.1.2group

Implement incident response

11.1.2.agroup

11.1.2.bgroup

Interview responsible personnel and/or inspect

11.1.agroup

Examine policies and procedures to verify processes

11.1.bgroup

Verify that the methodology is adequate to detect and

11.1.cgroup

If wireless scanning is utilized, examine output from

11.1.dgroup

If automated monitoring is utilized (for example,

11.2group

Run internal and external network

11.2.1group

Perform quarterly internal

11.2.1.agroup

Review the scan reports and verify that four

11.2.1.bgroup

Review the scan reports and verify that the scan

11.2.2group

Perform quarterly external

11.2.2.cgroup

Review the scan reports to verify that the scans

11.2.3group

Perform internal and external

11.2.3.agroup

Inspect and correlate change control

11.2.3.bgroup

Review scan reports and verify that the scan

11.2.3.cgroup

Validate that the scan was performed by a qualified

11.3group

Implement a methodology for

11.3.1group

Perform

11.3.1.agroup

Examine the scope of work and results from the

11.3.1.bgroup

Verify that the test was performed by a qualified

11.3.2group

Perform

11.3.2.agroup

Examine the scope of work and results from the

11.3.2.bgroup

Verify that the test was performed by a qualified

11.3.3group

Exploitable vulnerabilities found

11.3.4group

If segmentation is used to isolate

11.3.4.agroup

Examine segmentation controls and review

11.3.4.bgroup

Examine the results from the most recent

11.4group

Use intrusion-detection and/or

11.4.agroup

Examine system configurations and network diagrams

11.4.bgroup

Examine system configurations and interview

11.4.cgroup

Examine IDS/IPS configurations and vendor

11.5group

Deploy a change-detection

11.5.1group

Implement a process to respond to

11.5.agroup

Verify the use of a change-detection mechanism within

11.5.bgroup

Verify the mechanism is configured to alert personnel

11.6group

Ensure that security policies and

Valuesgroup

Group of values used in PCI-DSS profile

Non PCI-DSSgroup

Rules that are not part of PCI-DSS

contains 94 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

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

Build and Test AIDE Databaserule

Run the following command to generate a new database:

$ sudo /usr/sbin/aide --init
By default, the database will be written to the file /var/lib/aide/aide.db.new.gz. Storing the database, the configuration file /etc/aide.conf, and the binary /usr/sbin/aide (or hashes of these files), in a secure location (such as on read-only media) provides additional assurance about their integrity. The newly-generated database can be installed as follows:
$ sudo cp /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
To initiate a manual check, run the following command:
$ sudo /usr/sbin/aide --check
If this check produces any unexpected output, investigate.

Rationale:

For AIDE to be effective, an initial database of "known-good" information about files must be captured and it should be able to be verified against the installed files.

identifiers:  CCE-27135-3, DISA FSO RHEL-06-000018

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
/usr/sbin/aide --init
/bin/cp -p /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz

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 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

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

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

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 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

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

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 Minimum Lengthrule

The pam_cracklib module's minlen parameter controls requirements for minimum characters required in a password. Add minlen=7 after pam_pwquality to set minimum password length requirements.

Rationale:

Password length is one factor of several that helps to determine strength and how long it takes to crack a password. Use of more characters in a password helps to exponentially increase the time and/or resources required to compromise the password.

identifiers:  CCE-26615-5

references:  IA-5(1)(a), 205

Remediation Shell script:   (show)

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

var_password_pam_minlen="7"

if grep -q "minlen=" /etc/pam.d/system-auth
then
	sed -i --follow-symlinks "s/\(minlen *= *\).*/\1$var_password_pam_minlen/" /etc/pam.d/system-auth
else
	sed -i --follow-symlinks "/pam_cracklib.so/ s/$/ minlen=$var_password_pam_minlen/" /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 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 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=6 unlock_time=1800 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=6 unlock_time=1800 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="6"

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=6 unlock_time=1800 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=6 unlock_time=1800 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="1800"

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

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=4 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=4
  • for the pam_pwhistory.so case:
    password requisite pam_pwhistory.so existing_options remember=4
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="4"

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 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

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

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"

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

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

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

Enable auditd Servicerule

The auditd service is an essential userspace component of the Linux Auditing System, as it is responsible for writing audit records to disk. The auditd service can be enabled with the following command:

$ sudo chkconfig --level 2345 auditd on

Rationale:

Ensuring the auditd service is active ensures audit records generated by the kernel can be written to disk, or that appropriate actions will be taken if other obstacles exist.

identifiers:  CCE-27058-7, DISA FSO RHEL-06-000145

references:  AC-17(1), AU-1(b), AU-10, AU-12(a), AU-12(c), IR-5, 347, 157, 172, 880, 1353, 1462, 1487, 1115, 1454, 067, 158, 831, 1190, 1312, 1263, 130, 120, 1589

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 auditd
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Enable service auditd
  service:
    name="{{item}}"
    enabled="yes"
    state="started"
  with_items:
    - auditd

Enable Auditing for Processes Which Start Prior to the Audit Daemonrule

To ensure all processes can be audited, even those which start prior to the audit daemon, add the argument audit=1 to the kernel line in /etc/grub.conf, in the manner below:

kernel /vmlinuz-version ro vga=ext root=/dev/VolGroup00/LogVol00 rhgb quiet audit=1

Rationale:

Each process on the system carries an "auditable" flag which indicates whether its activities can be audited. Although auditd takes care of enabling this for all processes which launch after it does, adding the kernel argument ensures it is set for every process during boot.

identifiers:  CCE-26785-6, DISA FSO RHEL-06-000525

references:  AC-17(1), AU-14(1), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-10, IR-5, 169

Remediation Shell script:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
/sbin/grubby --update-kernel=ALL --args="audit=1"

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 to use audispd's syslog pluginrule

To configure the auditd service to use the syslog plug-in of the audispd audit event multiplexor, set the active line in /etc/audisp/plugins.d/syslog.conf to yes. Restart the auditd service:

$ sudo service auditd restart

Rationale:

The auditd service does not include the ability to send audit records to a centralized server for management directly. It does, however, include a plug-in for audit event multiplexor (audispd) to pass audit records to the local syslog server

identifiers:  CCE-26933-2, DISA FSO RHEL-06-000509

references:  AU-1(b), AU-3(2), IR-5, 136

Remediation Shell script:   (show)

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

grep -q ^active /etc/audisp/plugins.d/syslog.conf && \
  sed -i "s/active.*/active = yes/g" /etc/audisp/plugins.d/syslog.conf
if ! [ $? -eq 0 ]; then
    echo "active = yes" >> /etc/audisp/plugins.d/syslog.conf
fi

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 User/Group Informationrule

Add the following to /etc/audit/audit.rules, in order to capture events that modify account changes:

# audit_rules_usergroup_modification
-w /etc/group -p wa -k audit_rules_usergroup_modification
-w /etc/passwd -p wa -k audit_rules_usergroup_modification
-w /etc/gshadow -p wa -k audit_rules_usergroup_modification
-w /etc/shadow -p wa -k audit_rules_usergroup_modification
-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification

Rationale:

In addition to auditing new user and group accounts, these watches will alert the system administrator(s) to any modifications. Any unexpected users, groups, or modifications should be investigated for legitimacy.

identifiers:  CCE-26664-3, DISA FSO RHEL-06-000174

references:  AC-2(4), AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, 18, 1403, 1404, 1405, 1684, 1683, 1685, 1686

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/group" "wa" "audit_rules_usergroup_modification"
fix_audit_watch_rule "auditctl" "/etc/passwd" "wa" "audit_rules_usergroup_modification"

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/gshadow" "wa" "audit_rules_usergroup_modification"
fix_audit_watch_rule "auditctl" "/etc/shadow" "wa" "audit_rules_usergroup_modification"

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/security/opasswd" "wa" "audit_rules_usergroup_modification"

Record Events that Modify the System's Network Environmentrule

Add the following to /etc/audit/audit.rules, setting ARCH to either b32 or b64 as appropriate for your system:

# audit_rules_networkconfig_modification
-a always,exit -F arch=ARCH -S sethostname -S setdomainname -k audit_rules_networkconfig_modification
-w /etc/issue -p wa -k audit_rules_networkconfig_modification
-w /etc/issue.net -p wa -k audit_rules_networkconfig_modification
-w /etc/hosts -p wa -k audit_rules_networkconfig_modification
-w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification

Rationale:

The network environment should not be modified by anything other than administrator action. Any change to network parameters should be audited.

identifiers:  CCE-26648-6, DISA FSO RHEL-06-000182

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5

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 .* -k *"
	# Use escaped BRE regex to specify rule group
	GROUP="set\(host\|domain\)name"
	FULL_RULE="-a always,exit -F arch=$ARCH -S sethostname -S setdomainname -k audit_rules_networkconfig_modification"

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

# Then perform the remediations for the watch rules

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/issue" "wa" "audit_rules_networkconfig_modification"
fix_audit_watch_rule "auditctl" "/etc/issue.net" "wa" "audit_rules_networkconfig_modification"

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/hosts" "wa" "audit_rules_networkconfig_modification"
fix_audit_watch_rule "auditctl" "/etc/sysconfig/network" "wa" "audit_rules_networkconfig_modification"

System Audit Logs Must Have Mode 0640 or Less Permissiverule

If log_group in /etc/audit/auditd.conf is set to a group other than the root group account, change the mode of the audit log files with the following command:

$ sudo chmod 0640 audit_file

Otherwise, change the mode of the audit log files with the following command:
$ sudo chmod 0600 audit_file

Rationale:

If users can write to audit logs, audit trails can be modified or destroyed.

identifiers:  CCE-27243-5, DISA FSO RHEL-06-000383

references:  AC-6, AU-1(b), AU-9, IR-5, 166

Remediation Shell script:   (show)

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

if `grep -q ^log_group /etc/audit/auditd.conf` ; then
  GROUP=$(awk -F "=" '/log_group/ {print $2}' /etc/audit/auditd.conf | tr -d ' ')
  if ! [ "${GROUP}" == 'root' ] ; then
    chmod 0640 /var/log/audit/audit.log
    chmod 0440 /var/log/audit/audit.log.*
  else
    chmod 0600 /var/log/audit/audit.log
    chmod 0400 /var/log/audit/audit.log.*
  fi

  chmod 0640 /etc/audit/audit*
  chmod 0640 /etc/audit/rules.d/*
else
  chmod 0600 /var/log/audit/audit.log
  chmod 0400 /var/log/audit/audit.log.*
  chmod 0640 /etc/audit/audit*
  chmod 0640 /etc/audit/rules.d/*
fi

System Audit Logs Must Be Owned By Rootrule

To properly set the owner of /var/log, run the command:

$ sudo chown root /var/log

Rationale:

Failure to give ownership of the audit log files to root allows the designated owner, and unauthorized users, potential access to sensitive information.

identifiers:  CCE-27244-3, DISA FSO RHEL-06-000384

references:  AC-6, AU-1(b), AU-9, IR-5, 166

Remediation Shell script:   (show)

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

if `grep -q ^log_group /etc/audit/auditd.conf` ; then
  GROUP=$(awk -F "=" '/log_group/ {print $2}' /etc/audit/auditd.conf | tr -d ' ')
  if ! [ "${GROUP}" == 'root' ] ; then
    chown root.${GROUP} /var/log/audit
    chown root.${GROUP} /var/log/audit/audit.log*
  else
    chown root.root /var/log/audit
    chown root.root /var/log/audit/audit.log*
  fi
else
  chown root.root /var/log/audit
  chown root.root /var/log/audit/audit.log*
fi

Record Events that Modify the System's Mandatory Access Controlsrule

Add the following to /etc/audit/audit.rules:

-w /etc/selinux/ -p wa -k MAC-policy

Rationale:

The system's mandatory access policy (SELinux) should not be arbitrarily changed by anything other than administrator action. All changes to MAC policy should be audited.

identifiers:  CCE-26657-7, DISA FSO RHEL-06-000183

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5

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/selinux/" "wa" "MAC-policy"

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.

identifiers:  CCE-27185-8, DISA FSO RHEL-06-000196

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 Attempts to Alter Process and Session Initiation Informationrule

The audit system already collects process information for all users and root. To watch for attempted manual edits of files involved in storing such process information, add the following to /etc/audit/audit.rules:

-w /var/run/utmp -p wa -k session
-w /var/log/btmp -p wa -k session
-w /var/log/wtmp -p wa -k session

Rationale:

Manual editing of these files may indicate nefarious activity, such as an attacker attempting to remove evidence of an intrusion.

identifiers:  CCE-26610-6

references:  AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5

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" "/var/run/utmp" "wa" "session"
fix_audit_watch_rule "auditctl" "/var/log/btmp" "wa" "session"

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" "/var/log/wtmp" "wa" "session"

Ensure auditd Collects Unauthorized Access Attempts to Files (unsuccessful)rule

At a minimum the audit system should collect unauthorized file accesses for all users and root. Add the following to /etc/audit/audit.rules:

-a always,exit -F arch=b32 -S creat -S open -S openat -S open_by_handle_at -S truncate -S ftruncate -F exit=-EACCES -F auid>=500 -F auid!=4294967295 -k access
-a always,exit -F arch=b32 -S creat -S open -S openat -S open_by_handle_at -S truncate -S ftruncate -F exit=-EPERM -F auid>=500 -F auid!=4294967295 -k access
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S creat -S open -S openat -S open_by_handle_at -S truncate -S ftruncate -F exit=-EACCES -F auid>=500 -F auid!=4294967295 -k access
-a always,exit -F arch=b64 -S creat -S open -S openat -S open_by_handle_at -S truncate -S ftruncate -F exit=-EPERM -F auid>=500 -F auid!=4294967295 -k access

Rationale:

Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing these events could serve as evidence of potential system compromise.

identifiers:  CCE-26712-0, DISA FSO RHEL-06-000197

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 of the syscall rule
# Retrieve hardware architecture of the underlying system
[ $(getconf LONG_BIT) = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")

for ARCH in "${RULE_ARCHS[@]}"
do

	# First fix the -EACCES requirement
	PATTERN="-a always,exit -F arch=$ARCH -S .* -F exit=-EACCES -F auid>=500 -F auid!=4294967295 -k *"
	# Use escaped BRE regex to specify rule group
	GROUP="\(creat\|open\|truncate\)"
	FULL_RULE="-a always,exit -F arch=$ARCH -S creat -S open -S openat -S open_by_handle_at -S truncate -S ftruncate -F exit=-EACCES -F auid>=500 -F auid!=4294967295 -k access"

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"

	# Then fix the -EPERM requirement
	PATTERN="-a always,exit -F arch=$ARCH -S .* -F exit=-EPERM -F auid>=500 -F auid!=4294967295 -k *"
	# No need to change content of $GROUP variable - it's the same as for -EACCES case above
	FULL_RULE="-a always,exit -F arch=$ARCH -S creat -S open -S openat -S open_by_handle_at -S truncate -S ftruncate -F exit=-EPERM -F auid>=500 -F auid!=4294967295 -k access"

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

Ensure auditd Collects Information on the Use of Privileged Commandsrule

At a minimum the audit system should collect the execution of privileged commands for all users and root. To find the relevant setuid / setgid programs, run the following command for each local partition PART:

$ sudo find PART -xdev -type f -perm -4000 -o -type f -perm -2000 2>/dev/null
Then, for each setuid / setgid program on the system, add a line of the following form to /etc/audit/audit.rules, where SETUID_PROG_PATH is the full path to each setuid / setgid program in the list:
-a always,exit -F path=SETUID_PROG_PATH -F perm=x -F auid>=500 -F auid!=4294967295 -k privileged

Rationale:

Privileged programs are subject to escalation-of-privilege attacks, which attempt to subvert their normal role of providing some necessary but limited capability. As such, motivation exists to monitor these programs for unusual activity.

identifiers:  CCE-26457-2, DISA FSO RHEL-06-000198

references:  AC-3(10)), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AC-6(9), AU-12(a), AU-12(c), IR-5, 40

Remediation Shell script:   (show)

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


# Perform the remediation

function perform_audit_rules_privileged_commands_remediation {
#
# Load function arguments into local variables
local tool="$1"
local min_auid="$2"

# Check sanity of the input
if [ $# -ne "2" ]
then
        echo "Usage: perform_audit_rules_privileged_commands_remediation 'auditctl | augenrules' '500 | 1000'"
        echo "Aborting."
        exit 1
fi

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'to the list of files to be inspected,
# * specify '/etc/audit/audit.rules' as the output audit file, where
#   missing rules should be inserted
elif [ "$tool" == 'auditctl' ]
then
        files_to_inspect=("/etc/audit/audit.rules")
        output_audit_file="/etc/audit/audit.rules"
#
# If the audit tool is 'augenrules', then:
# * add '/etc/audit/rules.d/*.rules' to the list of files to be inspected
#   (split by newline),
# * specify /etc/audit/rules.d/privileged.rules' as the output file, where
#   missing rules should be inserted
elif [ "$tool" == 'augenrules' ]
then
        IFS=$'\n' files_to_inspect=($(find /etc/audit/rules.d -maxdepth 1 -type f -name *.rules -print))
        output_audit_file="/etc/audit/rules.d/privileged.rules"
fi

# Obtain the list of SUID/SGID binaries on the particular system (split by newline)
# into privileged_binaries array
IFS=$'\n' privileged_binaries=($(find / -xdev -type f -perm -4000 -o -type f -perm -2000 2>/dev/null))

# Keep list of SUID/SGID binaries that have been already handled within some previous iteration
declare -a sbinaries_to_skip=()

# For each found sbinary in privileged_binaries list
for sbinary in "${privileged_binaries[@]}"
do

        # Replace possible slash '/' character in sbinary definition so we could use it in sed expressions below
        sbinary_esc=${sbinary//$'/'/$'\/'}
        # Check if this sbinary wasn't already handled in some of the previous iterations
        # Return match only if whole sbinary definition matched (not in the case just prefix matched!!!)
        if [[ $(sed -ne "/${sbinary_esc}$/p" <<< ${sbinaries_to_skip[@]}) ]]
        then
                # If so, don't process it second time & go to process next sbinary
                continue
        fi

        # Reset the counter of inspected files when starting to check
        # presence of existing audit rule for new sbinary
        local count_of_inspected_files=0

        # For each audit rules file from the list of files to be inspected
        for afile in "${files_to_inspect[@]}"
        do

                # Search current audit rules file's content for match. Match criteria:
                # * existing rule is for the same SUID/SGID binary we are currently processing (but
                #   can contain multiple -F path= elements covering multiple SUID/SGID binaries)
                # * existing rule contains all arguments from expected rule form (though can contain
                #   them in arbitrary order)

                base_search=$(sed -e "/-a always,exit/!d" -e "/-F path=${sbinary_esc}$/!d"   \
                                  -e "/-F path=[^[:space:]]\+/!d" -e "/-F perm=.*/!d"       \
                                  -e "/-F auid>=${min_auid}/!d" -e "/-F auid!=4294967295/!d"  \
                                  -e "/-k privileged/!d" $afile)

                # Increase the count of inspected files for this sbinary
                count_of_inspected_files=$((count_of_inspected_files + 1))

                # Define expected rule form for this binary
                expected_rule="-a always,exit -F path=${sbinary} -F perm=x -F auid>=${min_auid} -F auid!=4294967295 -k privileged"

                # Require execute access type to be set for existing audit rule
                exec_access='x'

                # Search current audit rules file's content for presence of rule pattern for this sbinary
                if [[ $base_search ]]
                then

                        # Current audit rules file already contains rule for this binary =>
                        # Store the exact form of found rule for this binary for further processing
                        concrete_rule=$base_search

                        # Select all other SUID/SGID binaries possibly also present in the found rule
                        IFS=$'\n' handled_sbinaries=($(grep -o -e "-F path=[^[:space:]]\+" <<< $concrete_rule))
                        IFS=$' ' handled_sbinaries=(${handled_sbinaries[@]//-F path=/})

                        # Merge the list of such SUID/SGID binaries found in this iteration with global list ignoring duplicates
                        sbinaries_to_skip=($(for i in "${sbinaries_to_skip[@]}" "${handled_sbinaries[@]}"; do echo $i; done | sort -du))

                        # Separate concrete_rule into three sections using hash '#'
                        # sign as a delimiter around rule's permission section borders
                        concrete_rule=$(echo $concrete_rule | sed -n "s/\(.*\)\+\(-F perm=[rwax]\+\)\+/\1#\2#/p")

                        # Split concrete_rule into head, perm, and tail sections using hash '#' delimiter
                        IFS=$'#' read rule_head rule_perm rule_tail <<<  "$concrete_rule"

                        # Extract already present exact access type [r|w|x|a] from rule's permission section
                        access_type=${rule_perm//-F perm=/}

                        # Verify current permission access type(s) for rule contain 'x' (execute) permission
                        if ! grep -q "$exec_access" <<< "$access_type"
                        then

                                # If not, append the 'x' (execute) permission to the existing access type bits
                                access_type="$access_type$exec_access"
                                # Reconstruct the permissions section for the rule
                                new_rule_perm="-F perm=$access_type"
                                # Update existing rule in current audit rules file with the new permission section
                                sed -i "s#${rule_head}\(.*\)${rule_tail}#${rule_head}${new_rule_perm}${rule_tail}#" $afile

                        fi

                # If the required audit rule for particular sbinary wasn't found yet, insert it under following conditions:
                #
                # * in the "auditctl" mode of operation insert particular rule each time
                #   (because in this mode there's only one file -- /etc/audit/audit.rules to be inspected for presence of this rule),
                #
                # * in the "augenrules" mode of operation insert particular rule only once and only in case we have already
                #   searched all of the files from /etc/audit/rules.d/*.rules location (since that audit rule can be defined
                #   in any of those files and if not, we want it to be inserted only once into /etc/audit/rules.d/privileged.rules file)
                #
                elif [ "$tool" == "auditctl" ] || [[ "$tool" == "augenrules" && $count_of_inspected_files -eq "${#files_to_inspect[@]}" ]]
                then

                        # Current audit rules file's content doesn't contain expected rule for this
                        # SUID/SGID binary yet => append it
                        echo $expected_rule >> $output_audit_file
                fi

        done

done

}

perform_audit_rules_privileged_commands_remediation "auditctl" "500"

Ensure auditd Collects Information on Exporting to Media (successful)rule

At a minimum the audit system should collect media exportation events for all users and root. Add the following to /etc/audit/audit.rules, setting ARCH to either b32 or b64 as appropriate for your system:

-a always,exit -F arch=ARCH -S mount -F auid>=500 -F auid!=4294967295 -k export

Rationale:

The unauthorized exportation of data to external media could result in an information leak where classified information, Privacy Act information, and intellectual property could be lost. An audit trail should be created each time a filesystem is mounted to help identify and guard against information loss.

identifiers:  CCE-26573-6, DISA FSO RHEL-06-000199

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 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 .* -F auid>=500 -F auid!=4294967295 -k *"
	GROUP="mount"
	FULL_RULE="-a always,exit -F arch=$ARCH -S mount -F auid>=500 -F auid!=4294967295 -k export"

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

Ensure auditd Collects File Deletion Events by Userrule

At a minimum the audit system should collect file deletion events for all users and root. Add the following to /etc/audit/audit.rules, setting ARCH to either b32 or b64 as appropriate for your system:

-a always,exit -F arch=ARCH -S rmdir -S unlink -S unlinkat -S rename -S renameat -F auid>=500 -F auid!=4294967295 -k delete

Rationale:

Auditing file deletions will create an audit trail for files that are removed from the system. The audit trail could aid in system troubleshooting, as well as, detecting malicious processes that attempt to delete log files to conceal their presence.

identifiers:  CCE-26651-0, DISA FSO RHEL-06-000200

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 delete"
	# Use escaped BRE regex to specify rule group
	GROUP="\(rmdir\|unlink\|rename\)"
	FULL_RULE="-a always,exit -F arch=$ARCH -S rmdir -S unlink -S unlinkat -S rename -S renameat -F auid>=500 -F auid!=4294967295 -k delete"

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

Ensure auditd Collects System Administrator Actionsrule

At a minimum the audit system should collect administrator actions for all users and root. Add the following to /etc/audit/audit.rules:

-w /etc/sudoers -p wa -k actions

Rationale:

The actions taken by system administrators should be audited to keep a record of what was executed on the system, as well as, for accountability purposes.

identifiers:  CCE-26662-7, DISA FSO RHEL-06-000201

references:  AC-2(7)(b), 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

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/sudoers" "wa" "actions"

Ensure auditd Collects Information on Kernel Module Loading and Unloadingrule

Add the following to /etc/audit/audit.rules in order to capture kernel module loading and unloading events, setting ARCH to either b32 or b64 as appropriate for your system:

-w /sbin/insmod -p x -k modules
-w /sbin/rmmod -p x -k modules
-w /sbin/modprobe -p x -k modules
-a always,exit -F arch=ARCH -S init_module -S delete_module -k modules

Rationale:

The addition/removal of kernel modules can be used to alter the behavior of the kernel and potentially introduce malicious code into kernel space. It is important to have an audit trail of modules that have been introduced into the kernel.

identifiers:  CCE-26611-4, DISA FSO RHEL-06-000202

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


# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
# Note: 32-bit kernel modules can't be loaded / unloaded on 64-bit kernel =>
#       it's not required on a 64-bit system to check also for the presence
#       of 32-bit's equivalent of the corresponding rule. Therefore for
#       each system it's enought to check presence of system's native rule form.
[ $(getconf LONG_BIT) = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b64")

for ARCH in "${RULE_ARCHS[@]}"
do
	PATTERN="-a always,exit -F arch=$ARCH -S .* -k *"
	# Use escaped BRE regex to specify rule group
	GROUP="\(init\|delete\)_module"
	FULL_RULE="-a always,exit -F arch=$ARCH -S init_module -S delete_module -k modules"

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

# Then perform the remediations for the watch rules

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" "/sbin/insmod" "x" "modules"
fix_audit_watch_rule "auditctl" "/sbin/rmmod" "x" "modules"

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" "/sbin/modprobe" "x" "modules"

Make the auditd Configuration Immutablerule

Add the following to /etc/audit/audit.rules in order to make the configuration immutable:

-e 2
With this setting, a reboot will be required to change any audit rules.

Rationale:

Making the audit configuration immutable prevents accidental as well as malicious modification of the audit rules, although it may be problematic if legitimate changes are needed during system operation

identifiers:  CCE-26612-2

references:  AC-6, AU-1(b), AU-2(a), AU-2(c), AU-2(d), IR-5

Remediation Shell script:   (show)

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

readonly AUDIT_RULES='/etc/audit/audit.rules'

# If '-e .*' setting present in audit.rules already, delete it since the
# auditctl(8) manual page instructs it should be the last rule in configuration
sed -i '/-e[[:space:]]\+.*/d' $AUDIT_RULES

# Append '-e 2' requirement at the end of audit.rules
echo '' >> $AUDIT_RULES
echo '# Set the audit.rules configuration immutable per security requirements' >> $AUDIT_RULES
echo '# Reboot is required to change audit rules once this setting is applied' >> $AUDIT_RULES
echo '-e 2' >> $AUDIT_RULES

Set SSH Idle Timeout Intervalrule

SSH allows administrators to set an idle timeout interval. After this interval has passed, the idle user will be automatically logged out.

To set an idle timeout interval, edit the following line in /etc/ssh/sshd_config as follows:

ClientAliveInterval 900
The timeout interval is given in seconds. To have a timeout of 15 minutes, set interval to 900.

If a shorter timeout has already been set for the login shell, that value will preempt any SSH setting made here. Keep in mind that some processes may stop SSH from correctly detecting that the user is idle.

Rationale:

Causing idle users to be automatically logged out guards against compromises one system leading trivially to compromises on another.

identifiers:  CCE-26919-1, DISA FSO RHEL-06-000230

references:  AC-2(5), SA-8, 879, 1133

Remediation Shell script:   (show)

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

sshd_idle_timeout_value="900"

grep -q ^ClientAliveInterval /etc/ssh/sshd_config && \
  sed -i "s/ClientAliveInterval.*/ClientAliveInterval $sshd_idle_timeout_value/g" /etc/ssh/sshd_config
if ! [ $? -eq 0 ]; then
    echo "ClientAliveInterval $sshd_idle_timeout_value" >> /etc/ssh/sshd_config
fi

Enable the NTP Daemonrule

The ntpd service can be enabled with the following command:

$ sudo chkconfig --level 2345 ntpd on

Rationale:

Enabling the ntpd service ensures that the ntpd service will be running and that the system will synchronize its time to any servers specified. This is important whether the system is configured to be a client (and synchronize only its own clock) or it is also acting as an NTP server to other systems. Synchronizing time is essential for authentication services such as Kerberos, but it is also important for maintaining accurate logs and auditing possible security breaches.

The NTP daemon offers all of the functionality of ntpdate, which is now deprecated. Additional information on this is available at http://support.ntp.org/bin/view/Dev/DeprecatingNtpdate

identifiers:  CCE-27093-4, DISA FSO RHEL-06-000247

references:  AU-8(1), 160

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 ntpd
Remediation Ansible snippet:   (show)

Complexity:low
Disruption:low
Reboot:false
Strategy:enable
- name: Enable service ntpd
  service:
    name="{{item}}"
    enabled="yes"
    state="started"
  with_items:
    - ntpd

Specify a Remote NTP Serverrule

To specify a remote NTP server for time synchronization, edit the file /etc/ntp.conf. Add or correct the following lines, substituting the IP or hostname of a remote NTP server for ntpserver:

server ntpserver
This instructs the NTP software to contact that remote server to obtain time data.

Rationale:

Synchronizing with an NTP server makes it possible to collate system logs from multiple sources or correlate computer events with real time events.

identifiers:  CCE-27098-3, DISA FSO RHEL-06-000248

references:  AU-8(1), 160

Specify Additional Remote NTP Serversrule

Additional NTP servers can be specified for time synchronization in the file /etc/ntp.conf. To do so, add additional lines of the following form, substituting the IP address or hostname of a remote NTP server for ntpserver:

server ntpserver

Rationale:

Specifying additional NTP servers increases the availability of accurate time data, in the event that one of the specified servers becomes unavailable. This is typical for a system acting as an NTP server for other systems.

identifiers:  CCE-26958-9

references:  AU-8(1)

Red Hat and Red Hat Enterprise Linux are either registered trademarks or trademarks of Red Hat, Inc. in the United States and other countries. All other names are registered trademarks or trademarks of their respective companies.