To configure the number of retry prompts that are permitted per-session:
Edit the pam_pwquality.so statement in
/etc/pam.d/system-auth to show
retry=, or a lower value if site
policy is more restrictive. The DoD requirement is a maximum of 3 prompts
per session.
Rationale
Setting the password retry prompts that are permitted on a per-session basis to a low value
requires some software, such as SSH, to re-connect. This can slow down and
draw additional attention to some types of password-guessing attacks. Note that this
is different from account lockout, which is provided by the pam_faillock module.
ISA-62443-2-1-2009, Security for Industrial Automation and Control Systems Part 2-1: Establishing an Industrial Automation and Control Systems Security Program
# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then
var_password_pam_retry='<xccdf-1.2:sub xmlns:xccdf-1.2="http://checklists.nist.gov/xccdf/1.2" idref="xccdf_org.ssgproject.content_value_var_password_pam_retry" use="legacy"/>'
if [ -e "/etc/pam.d/system-auth" ] ; then
PAM_FILE_PATH="/etc/pam.d/system-auth"
if [ -f /usr/bin/authselect ]; then
if ! authselect check; then
echo "
authselect integrity check failed. Remediation aborted!
This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
It is not recommended to manually edit the PAM files when authselect tool is available.
In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
exit 1
fi
CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }')
# If not already in use, a custom profile is created preserving the enabled features.
if [[ ! $CURRENT_PROFILE == custom/* ]]; then
ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }')
authselect create-profile hardening -b $CURRENT_PROFILE
CURRENT_PROFILE="custom/hardening"
authselect apply-changes -b --backup=before-hardening-custom-profile
authselect select $CURRENT_PROFILE
for feature in $ENABLED_FEATURES; do
authselect enable-feature $feature;
done
authselect apply-changes -b --backup=after-hardening-custom-profile
fi
PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth")
PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"
authselect apply-changes -b
fi
if ! grep -qP '^\s*password\s+'"requisite"'\s+pam_pwquality.so\s*.*' "$PAM_FILE_PATH"; then
# Line matching group + control + module was not found. Check group + module.
if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwquality.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then
# The control is updated only if one single line matches.
sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_pwquality.so.*)/\1'"requisite"' \2/' "$PAM_FILE_PATH"
else
LAST_MATCH_LINE=$(grep -nP "^\s*account" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1)
if [ ! -z $LAST_MATCH_LINE ]; then
sed -i --follow-symlinks $LAST_MATCH_LINE' a password '"requisite"' pam_pwquality.so' "$PAM_FILE_PATH"
else
echo 'password '"requisite"' pam_pwquality.so' >> "$PAM_FILE_PATH"
fi
fi
fi
# Check the option
if ! grep -qP '^\s*password\s+'"requisite"'\s+pam_pwquality.so\s*.*\sretry\b' "$PAM_FILE_PATH"; then
sed -i -E --follow-symlinks '/\s*password\s+'"requisite"'\s+pam_pwquality.so.*/ s/$/ retry='"$var_password_pam_retry"'/' "$PAM_FILE_PATH"
else
sed -i -E --follow-symlinks 's/(\s*password\s+'"requisite"'\s+pam_pwquality.so\s+.*)('"retry"'=)[[:alnum:]]+\s*(.*)/\1\2'"$var_password_pam_retry"' \3/' "$PAM_FILE_PATH"
fi
if [ -f /usr/bin/authselect ]; then
authselect apply-changes -b
fi
else
echo "/etc/pam.d/system-auth was not found" >&2
fi
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation - Ansible
- name: Gather the package facts
package_facts:
manager: auto
tags:
- CCE-27160-1
- CJIS-5.5.3
- DISA-STIG-RHEL-07-010119
- NIST-800-53-AC-7(a)
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(4)
- accounts_password_pam_retry
- configure_strategy
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- name: XCCDF Value var_password_pam_retry # promote to variable
set_fact:
var_password_pam_retry: !!str <xccdf-1.2:sub xmlns:xccdf-1.2="http://checklists.nist.gov/xccdf/1.2" idref="xccdf_org.ssgproject.content_value_var_password_pam_retry" use="legacy"/>
tags:
- always
- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted
Per-Session - Check if expected PAM module line is present in /etc/pam.d/system-auth
ansible.builtin.lineinfile:
path: /etc/pam.d/system-auth
regexp: ^\s*password\s+{{ 'requisite' | regex_escape() }}\s+pam_pwquality.so\s*.*
state: absent
check_mode: true
changed_when: false
register: result_pam_line_present
when: '"pam" in ansible_facts.packages'
tags:
- CCE-27160-1
- CJIS-5.5.3
- DISA-STIG-RHEL-07-010119
- NIST-800-53-AC-7(a)
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(4)
- accounts_password_pam_retry
- configure_strategy
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted
Per-Session - Include or update the PAM module line in /etc/pam.d/system-auth
block:
- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
Permitted Per-Session - Check if required PAM module line is present in /etc/pam.d/system-auth
with different control
ansible.builtin.lineinfile:
path: /etc/pam.d/system-auth
regexp: ^\s*password\s+.*\s+pam_pwquality.so\s*
state: absent
check_mode: true
changed_when: false
register: result_pam_line_other_control_present
- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
Permitted Per-Session - Ensure the correct control for the required PAM module
line in /etc/pam.d/system-auth
ansible.builtin.replace:
dest: /etc/pam.d/system-auth
regexp: ^(\s*password\s+).*(\bpam_pwquality.so.*)
replace: \1requisite \2
register: result_pam_module_edit
when:
- result_pam_line_other_control_present.found == 1
- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
Permitted Per-Session - Ensure the required PAM module line is included in /etc/pam.d/system-auth
ansible.builtin.lineinfile:
dest: /etc/pam.d/system-auth
insertafter: ^\s*account
line: password requisite pam_pwquality.so
register: result_pam_module_add
when:
- result_pam_line_other_control_present.found == 0 or result_pam_line_other_control_present.found
> 1
- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts
Permitted Per-Session - Ensure authselect changes are applied
ansible.builtin.command:
cmd: authselect apply-changes -b
when:
- result_authselect_present is defined
- result_authselect_present.stat.exists
- |-
(result_pam_module_add is defined and result_pam_module_add.changed)
or (result_pam_module_edit is defined and result_pam_module_edit.changed)
when:
- '"pam" in ansible_facts.packages'
- result_pam_line_present.found is defined
- result_pam_line_present.found == 0
tags:
- CCE-27160-1
- CJIS-5.5.3
- DISA-STIG-RHEL-07-010119
- NIST-800-53-AC-7(a)
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(4)
- accounts_password_pam_retry
- configure_strategy
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted
Per-Session - Check if the required PAM module option is present in /etc/pam.d/system-auth
ansible.builtin.lineinfile:
path: /etc/pam.d/system-auth
regexp: ^\s*password\s+{{ 'requisite' | regex_escape() }}\s+pam_pwquality.so\s*.*\sretry\b
state: absent
check_mode: true
changed_when: false
register: result_pam_module_retry_option_present
when: '"pam" in ansible_facts.packages'
tags:
- CCE-27160-1
- CJIS-5.5.3
- DISA-STIG-RHEL-07-010119
- NIST-800-53-AC-7(a)
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(4)
- accounts_password_pam_retry
- configure_strategy
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted
Per-Session - Ensure the "retry" PAM option for "pam_pwquality.so" is included
in /etc/pam.d/system-auth
ansible.builtin.lineinfile:
path: /etc/pam.d/system-auth
backrefs: true
regexp: ^(\s*password\s+{{ 'requisite' | regex_escape() }}\s+pam_pwquality.so.*)
line: \1 retry={{ var_password_pam_retry }}
state: present
register: result_pam_retry_add
when:
- '"pam" in ansible_facts.packages'
- result_pam_module_retry_option_present.found == 0
tags:
- CCE-27160-1
- CJIS-5.5.3
- DISA-STIG-RHEL-07-010119
- NIST-800-53-AC-7(a)
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(4)
- accounts_password_pam_retry
- configure_strategy
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- name: Ensure PAM Enforces Password Requirements - Authentication Retry Prompts Permitted
Per-Session - Ensure the required value for "retry" PAM option from "pam_pwquality.so"
in /etc/pam.d/system-auth
ansible.builtin.lineinfile:
path: /etc/pam.d/system-auth
backrefs: true
regexp: ^(\s*password\s+{{ 'requisite' | regex_escape() }}\s+pam_pwquality.so\s+.*)(retry)=[0-9a-zA-Z]+\s*(.*)
line: \1\2={{ var_password_pam_retry }} \3
register: result_pam_retry_edit
when:
- '"pam" in ansible_facts.packages'
- result_pam_module_retry_option_present.found > 0
tags:
- CCE-27160-1
- CJIS-5.5.3
- DISA-STIG-RHEL-07-010119
- NIST-800-53-AC-7(a)
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(4)
- accounts_password_pam_retry
- configure_strategy
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed