Limit Password Reuse: password-auth

Classification:

compliance

Framework:

Control:

Description

Do not allow users to reuse recent passwords. This can be accomplished by using the remember option for the pam_pwhistory PAM module.

In the file /etc/pam.d/password-auth, make sure the parameter remember is present and it has a value equal to or greater than 5

For example:

password requisite pam_pwhistory.so use_authtok remember=5

Rationale

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

Remediation

Shell script

The following script can be run on the host to remediate the issue.

# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then

var\_password\_pam\_remember='5'
var\_password\_pam\_remember\_control\_flag='requisite'


var\_password\_pam\_remember\_control\_flag="$(echo $var\_password\_pam\_remember\_control\_flag | cut -d \, -f 1)"

if [ -f /usr/bin/authselect ]; then
 if authselect list-features minimal | grep -q with-pwhistory; then
 if ! authselect check; then
 echo "
 authselect integrity check failed. Remediation aborted!
 This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact.
 It is not recommended to manually edit the PAM files when authselect tool is available.
 In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended."
 exit 1
 fi
 authselect enable-feature with-pwhistory

 authselect apply-changes -b
 else
 
 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/password-auth")
 PAM\_FILE\_PATH="/etc/authselect/$CURRENT\_PROFILE/$PAM\_FILE\_NAME"

 authselect apply-changes -b
 if ! grep -qP '^\s\*password\s+'"$var\_password\_pam\_remember\_control\_flag"'\s+pam\_pwhistory.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\_pwhistory.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\_pwhistory.so.\*)/\1'"$var\_password\_pam\_remember\_control\_flag"' \2/' "$PAM\_FILE\_PATH"
 else
 LAST\_MATCH\_LINE=$(grep -nP "^password.\*requisite.\*pam\_pwquality\.so" "$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 '"$var\_password\_pam\_remember\_control\_flag"' pam\_pwhistory.so' "$PAM\_FILE\_PATH"
 else
 echo 'password '"$var\_password\_pam\_remember\_control\_flag"' pam\_pwhistory.so' >> "$PAM\_FILE\_PATH"
 fi
 fi
 fi
 fi
else
 if ! grep -qP '^\s\*password\s+'"$var\_password\_pam\_remember\_control\_flag"'\s+pam\_pwhistory.so\s\*.\*' "/etc/pam.d/password-auth"; then
 # Line matching group + control + module was not found. Check group + module.
 if [ "$(grep -cP '^\s\*password\s+.\*\s+pam\_pwhistory.so\s\*' "/etc/pam.d/password-auth")" -eq 1 ]; then
 # The control is updated only if one single line matches.
 sed -i -E --follow-symlinks 's/^(\s\*password\s+).\*(\bpam\_pwhistory.so.\*)/\1'"$var\_password\_pam\_remember\_control\_flag"' \2/' "/etc/pam.d/password-auth"
 else
 LAST\_MATCH\_LINE=$(grep -nP "^password.\*requisite.\*pam\_pwquality\.so" "/etc/pam.d/password-auth" | tail -n 1 | cut -d: -f 1)
 if [ ! -z $LAST\_MATCH\_LINE ]; then
 sed -i --follow-symlinks $LAST\_MATCH\_LINE' a password '"$var\_password\_pam\_remember\_control\_flag"' pam\_pwhistory.so' "/etc/pam.d/password-auth"
 else
 echo 'password '"$var\_password\_pam\_remember\_control\_flag"' pam\_pwhistory.so' >> "/etc/pam.d/password-auth"
 fi
 fi
 fi
fi

PWHISTORY\_CONF="/etc/security/pwhistory.conf"
if [ -f $PWHISTORY\_CONF ]; then
 regex="^\s\*remember\s\*="
 line="remember = $var\_password\_pam\_remember"
 if ! grep -q $regex $PWHISTORY\_CONF; then
 echo $line >> $PWHISTORY\_CONF
 else
 sed -i --follow-symlinks 's|^\s\*\(remember\s\*=\s\*\)\(\S\+\)|\1'"$var\_password\_pam\_remember"'|g' $PWHISTORY\_CONF
 fi
 if [ -e "/etc/pam.d/password-auth" ] ; then
 PAM\_FILE\_PATH="/etc/pam.d/password-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/password-auth")
 PAM\_FILE\_PATH="/etc/authselect/$CURRENT\_PROFILE/$PAM\_FILE\_NAME"

 authselect apply-changes -b
 fi
 
 if grep -qP '^\s\*password\s.\*\bpam\_pwhistory.so\s.\*\bremember\b' "$PAM\_FILE\_PATH"; then
 sed -i -E --follow-symlinks 's/(.\*password.\*pam\_pwhistory.so.\*)\bremember\b=?[[:alnum:]]\*(.\*)/\1\2/g' "$PAM\_FILE\_PATH"
 fi
 if [ -f /usr/bin/authselect ]; then
 
 authselect apply-changes -b
 fi
 else
 echo "/etc/pam.d/password-auth was not found" >&2
 fi
else
 PAM\_FILE\_PATH="/etc/pam.d/password-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/password-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\_pwhistory.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\_pwhistory.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\_pwhistory.so.\*)/\1'"requisite"' \2/' "$PAM\_FILE\_PATH"
 else
 echo 'password '"requisite"' pam\_pwhistory.so' >> "$PAM\_FILE\_PATH"
 fi
 fi
 # Check the option
 if ! grep -qP '^\s\*password\s+'"requisite"'\s+pam\_pwhistory.so\s\*.\*\sremember\b' "$PAM\_FILE\_PATH"; then
 sed -i -E --follow-symlinks '/\s\*password\s+'"requisite"'\s+pam\_pwhistory.so.\*/ s/$/ remember='"$var\_password\_pam\_remember"'/' "$PAM\_FILE\_PATH"
 else
 sed -i -E --follow-symlinks 's/(\s\*password\s+'"requisite"'\s+pam\_pwhistory.so\s+.\*)('"remember"'=)[[:alnum:]]+\s\*(.\*)/\1\2'"$var\_password\_pam\_remember"' \3/' "$PAM\_FILE\_PATH"
 fi
 if [ -f /usr/bin/authselect ]; then
 
 authselect apply-changes -b
 fi
fi

else
 >&2 echo 'Remediation is not applicable, nothing was done'
fi

Warning

If the system relies on authselect tool to manage PAM settings, the remediation will also use authselect tool. However, if any manual modification was made in PAM files, the authselect integrity check will fail and the remediation will be aborted in order to preserve intentional changes. In this case, an informative message will be shown in the remediation report.