Limit Password Reuse: password-auth
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.
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.