securing oracle linux 9 article 19 configuring authentication modules arguments pam arguments

In the previous part, Authentication Modules (PAM and Authselect) (CIS 5.3) was completed.

1. CIS Benchmark Requirements (Section 5.3.3)

  • 5.3.3 Configure PAM Arguments
    • 5.3.3.1 Configure pam_faillock module
      • 5.3.3.1.1 Ensure password failed attempts lockout is configured (Automated)
      • 5.3.3.1.2 Ensure password unlock time is configured (Automated)
      • 5.3.3.1.3 Ensure password failed attempts lockout includes root account (Automated)
    • 5.3.3.2 Configure pam_pwquality module
      • 5.3.3.2.1 Ensure password number of changed characters is configured (Automated)
      • 5.3.3.2.2 Ensure password length is configured (Automated)
      • 5.3.3.2.3 Ensure password complexity is configured (Manual)
      • 5.3.3.2.4 Ensure password same consecutive characters is configured (Automated)
      • 5.3.3.2.5 Ensure password maximum sequential characters is configured (Automated)
      • 5.3.3.2.6 Ensure password dictionary check is enabled (Automated)
      • 5.3.3.2.7 Ensure password quality is enforced for the root user (Automated)
    • 5.3.3.3 Configure pam_pwhistory module
      • 5.3.3.3.1 Ensure password history remember is configured (Automated)
      • 5.3.3.3.2 Ensure password history is enforced for the root user (Automated)
      • 5.3.3.3.3 Ensure pam_pwhistory includes use_authtok (Automated)
    • 5.3.3.4 Configure pam_unix module
      • 5.3.3.4.1 Ensure pam_unix does not include nullok (Automated)
      • 5.3.3.4.2 Ensure pam_unix does not include remember (Automated)
      • 5.3.3.4.3 Ensure pam_unix includes a strong password hashing algorithm (Automated)
      • 5.3.3.4.4 Ensure pam_unix includes use_authtok (Automated)

2. Concept & Rationale

  • Lockout with pam_faillock: After a specified number of failed login attempts (e.g., 5 times), the user account is locked for a defined period (e.g., 900 seconds). This policy is designed to prevent brute-force password guessing attacks and must also apply to the root user (even_deny_root).
  • Password quality with pam_pwquality: Enforces requirements such as minimum password length (e.g., 14 characters), dictionary word checking (dictcheck), preventing excessive repetition of consecutive characters (maxrepeat), and ensuring these strict policies are also applied to the root user.
  • Password history with pam_pwhistory: Retains previous passwords (e.g., the last 5 passwords) to prevent users from reusing them.
  • Base management with pam_unix: Completely blocks the ability to log in with an empty password (nullok) and requires the system to use strong hashing algorithms (such as yescrypt or sha512).

3. Oracle Database Compatibility (RAC, ASM, Grid)

Conflict level: Medium to High

Oracle-specific considerations:

  • User lockout crisis (faillock): Applying failed login restrictions to critical users such as oracle, grid, or automation accounts (like OEM Agents) is highly dangerous. Stale connections, scripts with expired passwords, or failed monitoring attempts can lock these users and cause cluster disruption or a complete database outage. These accounts must be excluded from the lockout process in the /etc/security/faillock.conf file.
  • Automated installations (pwquality): When running automation scripts (such as Ansible) responsible for creating database or OS users, generated passwords must strictly comply with these complexity rules; otherwise, the provisioning and installation process will halt with an authentication error.

4. How to Audit (Audit Script)

GitHub link for this script: modules/audit_19_PAM_Arguments.sh

If you are not familiar with Bash scripting, you can refer to the training published for database administrators on the site: Bash for Oracle DBAs.

#!/bin/bash
# Script: audit19.sh
# Purpose: Audit Script for Faillock & Password Quality (CIS Section 19)

echo "=========================================================================="
echo " CIS Requirement: Password Policies & Lockout Mechanisms"
echo " - Ensure faillock locks out users after 5 failed attempts for 15 mins."
echo " - Ensure pwquality enforces complexity (length, classes, dictcheck)."
echo " - Ensure password history remembers last 5 passwords."
echo " - Ensure yescrypt is used for password hashing."
echo " Oracle Context:"
echo " - WARNING: Strict faillock policies can lock out 'oracle' and 'grid'"
echo "   users during automated deployments (Ansible/Terraform). A PAM bypass"
echo "   should be implemented (covered in Sec 18) to avoid database outages."
echo "=========================================================================="

FAIL_COUNT=0

echo -e "\n[*] Checking faillock configuration (/etc/security/faillock.conf)..."
for param in "deny = 5" "unlock_time = 900" "even_deny_root"; do
    if grep -q -E "^\s*${param}" /etc/security/faillock.conf; then
        echo -e "  \e[32m[PASS]\e[0m $param is configured."
    else
        echo -e "  \e[31m[FAIL]\e[0m $param is missing or incorrect."
        FAIL_COUNT=$((FAIL_COUNT + 1))
    fi
done

echo -e "\n[*] Checking pwquality configuration (/etc/security/pwquality.conf)..."
for param in "difok = 8" "minlen = 14" "minclass = 4" "maxrepeat = 3" "maxsequence = 3" "dictcheck = 1" "enforce_for_root"; do
    if grep -q -E "^\s*${param}" /etc/security/pwquality.conf; then
        echo -e "  \e[32m[PASS]\e[0m $param is configured."
    else
        echo -e "  \e[31m[FAIL]\e[0m $param is missing or incorrect."
        FAIL_COUNT=$((FAIL_COUNT + 1))
    fi
done

echo -e "\n[*] Checking PAM configurations for pwhistory and yescrypt..."
for pam_file in /etc/authselect/system-auth /etc/authselect/password-auth; do
    echo -e "  \e[34m[INFO]\e[0m Checking $pam_file..."

    if grep -E '^\s*password\s+requisite\s+pam_pwhistory.so' $pam_file | grep -q 'remember=5' && \
       grep -E '^\s*password\s+requisite\s+pam_pwhistory.so' $pam_file | grep -q 'enforce_for_root'; then
        echo -e "  \e[32m[PASS]\e[0m pwhistory is set correctly."
    else
        echo -e "  \e[31m[FAIL]\e[0m Missing correct pwhistory settings."
        FAIL_COUNT=$((FAIL_COUNT + 1))
    fi

    if grep -E '^\s*password\s+(sufficient|required)\s+pam_unix.so' $pam_file | grep -q 'yescrypt'; then
        echo -e "  \e[32m[PASS]\e[0m pam_unix is using yescrypt."
    else
        echo -e "  \e[31m[FAIL]\e[0m yescrypt is not configured."
        FAIL_COUNT=$((FAIL_COUNT + 1))
    fi
done

if [ "$FAIL_COUNT" -eq 0 ]; then
    echo -e "\n\e[32m[+] AUDIT RESULT: PASS\e[0m"
else
    echo -e "\n\e[31m[-] AUDIT RESULT: FAIL ($FAIL_COUNT issues found)\e[0m"
fi

5. How to Remediate with Bash (Remediation Script)

GitHub link for this script: modules/remediate_19_PAM_Arguments.sh

#!/bin/bash

# =====================================================================
# OS Security Standard Remediation Script - Section 19
# Oracle Context: Strict password policies and lockout mechanisms.
# Oracle Exception: High faillock deny counts could lock out 'oracle'
# or 'grid'.
# =====================================================================

# Ensure script is run as root
if [ "$EUID" -ne 0 ]; then
  echo -e "\033[31m[ERROR] This script must be run as root.\033[0m"
  exit 1
fi

echo -e "\033[36m--- Configuring faillock.conf ---\033[0m"
FAILLOCK_CONF="/etc/security/faillock.conf"
sed -i -E 's/^\s*#?\s*deny\s*=.*/deny = 5/' $FAILLOCK_CONF
sed -i -E 's/^\s*#?\s*unlock_time\s*=.*/unlock_time = 900/' $FAILLOCK_CONF
sed -i -E 's/^\s*#?\s*even_deny_root.*/even_deny_root/' $FAILLOCK_CONF
grep -q "^deny = 5" $FAILLOCK_CONF || echo "deny = 5" >> $FAILLOCK_CONF
grep -q "^unlock_time = 900" $FAILLOCK_CONF || echo "unlock_time = 900" >> $FAILLOCK_CONF
grep -q "^even_deny_root" $FAILLOCK_CONF || echo "even_deny_root" >> $FAILLOCK_CONF

echo -e "\033[36m--- Configuring pwquality.conf ---\033[0m"
PWQUAL_CONF="/etc/security/pwquality.conf"
sed -i -E 's/^\s*#?\s*difok\s*=.*/difok = 8/' $PWQUAL_CONF
sed -i -E 's/^\s*#?\s*minlen\s*=.*/minlen = 14/' $PWQUAL_CONF
sed -i -E 's/^\s*#?\s*minclass\s*=.*/minclass = 4/' $PWQUAL_CONF
sed -i -E 's/^\s*#?\s*maxrepeat\s*=.*/maxrepeat = 3/' $PWQUAL_CONF
sed -i -E 's/^\s*#?\s*maxsequence\s*=.*/maxsequence = 3/' $PWQUAL_CONF
sed -i -E 's/^\s*#?\s*dictcheck\s*=.*/dictcheck = 1/' $PWQUAL_CONF
sed -i -E 's/^\s*#?\s*enforce_for_root.*/enforce_for_root/' $PWQUAL_CONF
grep -q "^maxsequence = 3" $PWQUAL_CONF || echo "maxsequence = 3" >> $PWQUAL_CONF

echo -e "\033[36m--- Configuring Custom Authselect Profile (oracle-sssd) ---\033[0m"
CUSTOM_PROFILE_DIR="/etc/authselect/custom/oracle-sssd"

if [ ! -d "$CUSTOM_PROFILE_DIR" ]; then
    echo -e "\033[33m[WARN] Custom profile 'oracle-sssd' not found. Creating it from sssd...\033[0m"
    authselect create-profile oracle-sssd -b sssd --symlinks
fi

for pam_file in "$CUSTOM_PROFILE_DIR/system-auth" "$CUSTOM_PROFILE_DIR/password-auth"; do
    if [ -f "$pam_file" ]; then
        # 1. Update pam_unix.so to use yescrypt instead of sha512
        sed -i 's/\bsha512\b/yescrypt/g' "$pam_file"

        # 2. Add remember=5 and enforce_for_root to pam_pwhistory.so
        # First ensure the line has the required parameters, if not, append them
        if grep -q "pam_pwhistory.so" "$pam_file"; then
            sed -i -E 's/(pam_pwhistory\.so.*)/\1/' "$pam_file"
            # Safely add remember=5 if missing
            if ! grep -q "pam_pwhistory.so.*remember=" "$pam_file"; then
                sed -i 's/pam_pwhistory.so/pam_pwhistory.so remember=5/g' "$pam_file"
            fi
            # Safely add enforce_for_root if missing
            if ! grep -q "pam_pwhistory.so.*enforce_for_root" "$pam_file"; then
                sed -i 's/pam_pwhistory.so/pam_pwhistory.so enforce_for_root/g' "$pam_file"
            fi
        fi
    fi
done

echo -e "\033[36m--- Applying Authselect Changes ---\033[0m"
# Re-apply the custom profile with required features
authselect select custom/oracle-sssd with-faillock without-nullok with-pwhistory --force
authselect apply-changes

echo -e "\033[32m[OK] Section 19 Remediation completed.\033[0m"

In the next part, we will cover Configuring Password Parameters.