Utilizing pam_faillock.so, the fail_interval directive configures the system
to lock out an account after a number of incorrect login attempts within a specified time
period.
Ensure that the file /etc/security/faillock.conf contains the following entry:
fail_interval = <interval-in-seconds> where interval-in-seconds is or greater.
warning alert:
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.
If the system supports the /etc/security/faillock.conf file, the pam_faillock
parameters should be defined in faillock.conf file.
Rationale
By limiting the number of failed logon attempts the risk of unauthorized system
access via user password guessing, otherwise known as brute-forcing, is reduced.
Limits are imposed by locking the account.
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 dpkg-query --show --showformat='${db:Status-Status}\n' 'libpam-runtime' 2>/dev/null | grep -q installed; then
var_accounts_passwords_pam_faillock_fail_interval='<xccdf-1.2:sub xmlns:xccdf-1.2="http://checklists.nist.gov/xccdf/1.2" idref="xccdf_org.ssgproject.content_value_var_accounts_passwords_pam_faillock_fail_interval" use="legacy"/>'
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
authselect enable-feature with-faillock
authselect apply-changes -b
else
pam_file="/etc/pam.d/common-auth"
if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+preauth.*$' "$pam_file" ; then
sed -i --follow-symlinks '/^auth.*pam_unix\.so.*/i auth required pam_faillock.so preauth' "$pam_file"
fi
if ! grep -qE '^\s*auth\s+\[default=die\]\s+pam_faillock\.so\s+authfail.*$' "$pam_file" ; then
num_lines=$(sed -n 's/^auth.*success=\([0-9]\).*pam_unix\.so.*/\1/p' "$pam_file")
if [ ! -z "$num_lines" ]; then
echo "$num_lines"
pattern=""
for ((i=1; i <= num_lines; i++)); do
pattern="${pattern}n;"
done;
sed -i --follow-symlinks '/^auth.*pam_unix\.so.*/{'$pattern'a auth [default=die] pam_faillock.so authfail
}' "$pam_file"
else
sed -i --follow-symlinks '/^auth.*pam_unix\.so.*/a auth [default=die] pam_faillock.so authfail' "$pam_file"
fi
fi
if ! grep -qE '^\s*auth\s+sufficient\s+pam_faillock\.so\s+authsucc.*$' "$pam_file" ; then
sed -i --follow-symlinks '/^auth.*pam_faillock\.so.*authfail.*/a auth sufficient pam_faillock.so authsucc' "$pam_file"
fi
pam_file="/etc/pam.d/common-account"
if ! grep -qE '^\s*account\s+required\s+pam_faillock\.so.*$' "$pam_file" ; then
echo 'account required pam_faillock.so' >> "$pam_file"
fi
fi
AUTH_FILES=("/etc/pam.d/common-auth" "/etc/pam.d/password-auth")
FAILLOCK_CONF="/etc/security/faillock.conf"
if [ -f $FAILLOCK_CONF ]; then
regex="^\s*fail_interval\s*="
line="fail_interval = $var_accounts_passwords_pam_faillock_fail_interval"
if ! grep -q $regex $FAILLOCK_CONF; then
echo $line >> $FAILLOCK_CONF
else
sed -i --follow-symlinks 's|^\s*\(fail_interval\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_fail_interval"'|g' $FAILLOCK_CONF
fi
for pam_file in "${AUTH_FILES[@]}"
do
if [ -e "$pam_file" ] ; then
PAM_FILE_PATH="$pam_file"
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 "$pam_file")
PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"
authselect apply-changes -b
fi
if grep -qP '^\s*auth\s.*\bpam_faillock.so\s.*\bfail_interval\b' "$PAM_FILE_PATH"; then
sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\bfail_interval\b=?[[:alnum:]]*(.*)/\1\2/g' "$PAM_FILE_PATH"
fi
if [ -f /usr/bin/authselect ]; then
authselect apply-changes -b
fi
else
echo "$pam_file was not found" >&2
fi
done
else
for pam_file in "${AUTH_FILES[@]}"
do
if ! grep -qE '^\s*auth.*pam_faillock\.so (preauth|authfail).*fail_interval' "$pam_file"; then
sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ fail_interval='"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file"
sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ fail_interval='"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file"
else
sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*silent.*\)\('"fail_interval"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_fail_interval"'\3/' "$pam_file"
sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"fail_interval"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_fail_interval"'\3/' "$pam_file"
fi
done
fi
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi