Enforce pam_faillock for Local Accounts Only
An XCCDF Rule
Description
The pam_faillock module's local_users_only
parameter controls requirements for
enforcing failed lockout attempts only for local user accounts and ignoring centralized user
account management failed attempt configurations.
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.warning alert: Management Warning
Using this rule bypasses pam_faillock's functionality and should be used in cases
where centralized management such as LDAP or Active Directory is in use.
Rationale
The operating system must provide automated mechanisms for supporting account management functions. Enterprise environments make application account management challenging and complex. A manual process for account management functions adds the risk of a potential oversight or other error. Locking out remote accounts may cause unintentional DoS.
- ID
- xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_enforce_local
- Severity
- Medium
- References
- CCI: Control Correlation IdentifierNIST Special Publication 800-53 (Revision 4): Security and Privacy Controls for Federal Information Systems and OrganizationsGPOS SRG: General Purpose Operating System Security Requirements GuideCCE: Common Configuration Enumeration
- Updated
Remediation - Ansible
- name: Gather the package facts
package_facts:
manager: auto
tags:
- CCE-83401-0
- NIST-800-53-AC-2(1) - accounts_passwords_pam_faillock_enforce_local
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Enforce pam_faillock for Local Accounts Only - Check if system relies on authselect
tool
ansible.builtin.stat:
path: /usr/bin/authselect
register: result_authselect_present
when: '"pam" in ansible_facts.packages'
tags:
- CCE-83401-0
- NIST-800-53-AC-2(1)
- accounts_passwords_pam_faillock_enforce_local
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Enforce pam_faillock for Local Accounts Only - Remediation where authselect
tool is present
block:
- name: Enforce pam_faillock for Local Accounts Only - Check integrity of authselect
current profile
ansible.builtin.command:
cmd: authselect check
register: result_authselect_check_cmd
changed_when: false
failed_when: false
- name: Enforce pam_faillock for Local Accounts Only - Informative message based
on the authselect integrity check result
ansible.builtin.assert:
that:
- result_authselect_check_cmd.rc == 0
fail_msg:
- 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.
success_msg:
- authselect integrity check passed
- name: Enforce pam_faillock for Local Accounts Only - Get authselect current features
ansible.builtin.shell:
cmd: authselect current | tail -n+3 | awk '{ print $2 }'
register: result_authselect_features
changed_when: false
when:
- result_authselect_check_cmd is success
- name: Enforce pam_faillock for Local Accounts Only - Ensure "with-faillock" feature
is enabled using authselect tool
ansible.builtin.command:
cmd: authselect enable-feature with-faillock
register: result_authselect_enable_feature_cmd
when:
- result_authselect_check_cmd is success
- result_authselect_features.stdout is not search("with-faillock")
- name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes
are applied
ansible.builtin.command:
cmd: authselect apply-changes -b
when:
- result_authselect_enable_feature_cmd is not skipped
- result_authselect_enable_feature_cmd is success
when:
- '"pam" in ansible_facts.packages'
- result_authselect_present.stat.exists
tags:
- CCE-83401-0
- NIST-800-53-AC-2(1)
- accounts_passwords_pam_faillock_enforce_local
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Enforce pam_faillock for Local Accounts Only - Remediation where authselect
tool is not present
block:
- name: Enforce pam_faillock for Local Accounts Only - Check if pam_faillock.so
is already enabled
ansible.builtin.lineinfile:
path: /etc/pam.d/system-auth
regexp: .*auth.*pam_faillock\.so (preauth|authfail)
state: absent
check_mode: true
changed_when: false
register: result_pam_faillock_is_enabled
- name: Enforce pam_faillock for Local Accounts Only - Enable pam_faillock.so preauth
editing PAM files
ansible.builtin.lineinfile:
path: '{{ item }}'
line: auth required pam_faillock.so preauth
insertbefore: ^auth.*sufficient.*pam_unix\.so.*
state: present
loop:
- /etc/pam.d/system-auth
- /etc/pam.d/password-auth
when:
- result_pam_faillock_is_enabled.found == 0
- name: Enforce pam_faillock for Local Accounts Only - Enable pam_faillock.so authfail
editing PAM files
ansible.builtin.lineinfile:
path: '{{ item }}'
line: auth required pam_faillock.so authfail
insertbefore: ^auth.*required.*pam_deny\.so.*
state: present
loop:
- /etc/pam.d/system-auth
- /etc/pam.d/password-auth
when:
- result_pam_faillock_is_enabled.found == 0
- name: Enforce pam_faillock for Local Accounts Only - Enable pam_faillock.so account
section editing PAM files
ansible.builtin.lineinfile:
path: '{{ item }}'
line: account required pam_faillock.so
insertbefore: ^account.*required.*pam_unix\.so.*
state: present
loop:
- /etc/pam.d/system-auth
- /etc/pam.d/password-auth
when:
- result_pam_faillock_is_enabled.found == 0
when:
- '"pam" in ansible_facts.packages'
- not result_authselect_present.stat.exists
tags:
- CCE-83401-0
- NIST-800-53-AC-2(1)
- accounts_passwords_pam_faillock_enforce_local
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Enforce pam_faillock for Local Accounts Only - Check the presence of /etc/security/faillock.conf
file
ansible.builtin.stat:
path: /etc/security/faillock.conf
register: result_faillock_conf_check
when: '"pam" in ansible_facts.packages'
tags:
- CCE-83401-0
- NIST-800-53-AC-2(1)
- accounts_passwords_pam_faillock_enforce_local
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Enforce pam_faillock for Local Accounts Only - Ensure the pam_faillock.so
local_users_only parameter in /etc/security/faillock.conf
ansible.builtin.lineinfile:
path: /etc/security/faillock.conf
regexp: ^\s*local_users_only
line: local_users_only
state: present
when:
- '"pam" in ansible_facts.packages'
- result_faillock_conf_check.stat.exists
tags:
- CCE-83401-0
- NIST-800-53-AC-2(1)
- accounts_passwords_pam_faillock_enforce_local
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Enforce pam_faillock for Local Accounts Only - Ensure the pam_faillock.so
local_users_only parameter not in PAM files
block:
- name: Enforce pam_faillock for Local Accounts Only - Check if /etc/pam.d/system-auth
file is present
ansible.builtin.stat:
path: /etc/pam.d/system-auth
register: result_pam_file_present
- name: Enforce pam_faillock for Local Accounts Only - Check the proper remediation
for the system
block:
- name: Enforce pam_faillock for Local Accounts Only - Define the PAM file to
be edited as a local fact
ansible.builtin.set_fact:
pam_file_path: /etc/pam.d/system-auth
- name: Enforce pam_faillock for Local Accounts Only - Check if system relies
on authselect tool
ansible.builtin.stat:
path: /usr/bin/authselect
register: result_authselect_present
- name: Enforce pam_faillock for Local Accounts Only - Ensure authselect custom
profile is used if authselect is present
block:
- name: Enforce pam_faillock for Local Accounts Only - Check integrity of authselect
current profile
ansible.builtin.command:
cmd: authselect check
register: result_authselect_check_cmd
changed_when: false
failed_when: false
- name: Enforce pam_faillock for Local Accounts Only - Informative message based
on the authselect integrity check result
ansible.builtin.assert:
that:
- result_authselect_check_cmd.rc == 0
fail_msg:
- 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.
success_msg:
- authselect integrity check passed
- name: Enforce pam_faillock for Local Accounts Only - Get authselect current
profile
ansible.builtin.shell:
cmd: authselect current -r | awk '{ print $1 }'
register: result_authselect_profile
changed_when: false
when:
- result_authselect_check_cmd is success
- name: Enforce pam_faillock for Local Accounts Only - Define the current authselect
profile as a local fact
ansible.builtin.set_fact:
authselect_current_profile: '{{ result_authselect_profile.stdout }}'
authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
when:
- result_authselect_profile is not skipped
- result_authselect_profile.stdout is match("custom/")
- name: Enforce pam_faillock for Local Accounts Only - Define the new authselect
custom profile as a local fact
ansible.builtin.set_fact:
authselect_current_profile: '{{ result_authselect_profile.stdout }}'
authselect_custom_profile: custom/hardening
when:
- result_authselect_profile is not skipped
- result_authselect_profile.stdout is not match("custom/")
- name: Enforce pam_faillock for Local Accounts Only - Get authselect current
features to also enable them in the custom profile
ansible.builtin.shell:
cmd: authselect current | tail -n+3 | awk '{ print $2 }'
register: result_authselect_features
changed_when: false
when:
- result_authselect_profile is not skipped
- authselect_current_profile is not match("custom/")
- name: Enforce pam_faillock for Local Accounts Only - Check if any custom profile
with the same name was already created
ansible.builtin.stat:
path: /etc/authselect/{{ authselect_custom_profile }}
register: result_authselect_custom_profile_present
changed_when: false
when:
- authselect_current_profile is not match("custom/")
- name: Enforce pam_faillock for Local Accounts Only - Create an authselect
custom profile based on the current profile
ansible.builtin.command:
cmd: authselect create-profile hardening -b {{ authselect_current_profile
}}
when:
- result_authselect_check_cmd is success
- authselect_current_profile is not match("custom/")
- not result_authselect_custom_profile_present.stat.exists
- name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes
are applied
ansible.builtin.command:
cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
when:
- result_authselect_check_cmd is success
- result_authselect_profile is not skipped
- authselect_current_profile is not match("custom/")
- authselect_custom_profile is not match(authselect_current_profile)
- name: Enforce pam_faillock for Local Accounts Only - Ensure the authselect
custom profile is selected
ansible.builtin.command:
cmd: authselect select {{ authselect_custom_profile }}
register: result_pam_authselect_select_profile
when:
- result_authselect_check_cmd is success
- result_authselect_profile is not skipped
- authselect_current_profile is not match("custom/")
- authselect_custom_profile is not match(authselect_current_profile)
- name: Enforce pam_faillock for Local Accounts Only - Restore the authselect
features in the custom profile
ansible.builtin.command:
cmd: authselect enable-feature {{ item }}
loop: '{{ result_authselect_features.stdout_lines }}'
register: result_pam_authselect_restore_features
when:
- result_authselect_profile is not skipped
- result_authselect_features is not skipped
- result_pam_authselect_select_profile is not skipped
- name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes
are applied
ansible.builtin.command:
cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
when:
- result_authselect_check_cmd is success
- result_authselect_profile is not skipped
- result_pam_authselect_restore_features is not skipped
- name: Enforce pam_faillock for Local Accounts Only - Change the PAM file to
be edited according to the custom authselect profile
ansible.builtin.set_fact:
pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
| basename }}
when:
- result_authselect_present.stat.exists
- name: Enforce pam_faillock for Local Accounts Only - Ensure the "local_users_only"
option from "pam_faillock.so" is not present in {{ pam_file_path }}
ansible.builtin.replace:
dest: '{{ pam_file_path }}'
regexp: (.*auth.*pam_faillock.so.*)\blocal_users_only\b=?[0-9a-zA-Z]*(.*)
replace: \1\2
register: result_pam_option_removal
- name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes
are applied
ansible.builtin.command:
cmd: authselect apply-changes -b
when:
- result_authselect_present.stat.exists
- result_pam_option_removal is changed
when:
- result_pam_file_present.stat.exists
- name: Enforce pam_faillock for Local Accounts Only - Check if /etc/pam.d/password-auth
file is present
ansible.builtin.stat:
path: /etc/pam.d/password-auth
register: result_pam_file_present
- name: Enforce pam_faillock for Local Accounts Only - Check the proper remediation
for the system
block:
- name: Enforce pam_faillock for Local Accounts Only - Define the PAM file to
be edited as a local fact
ansible.builtin.set_fact:
pam_file_path: /etc/pam.d/password-auth
- name: Enforce pam_faillock for Local Accounts Only - Check if system relies
on authselect tool
ansible.builtin.stat:
path: /usr/bin/authselect
register: result_authselect_present
- name: Enforce pam_faillock for Local Accounts Only - Ensure authselect custom
profile is used if authselect is present
block:
- name: Enforce pam_faillock for Local Accounts Only - Check integrity of authselect
current profile
ansible.builtin.command:
cmd: authselect check
register: result_authselect_check_cmd
changed_when: false
failed_when: false
- name: Enforce pam_faillock for Local Accounts Only - Informative message based
on the authselect integrity check result
ansible.builtin.assert:
that:
- result_authselect_check_cmd.rc == 0
fail_msg:
- 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.
success_msg:
- authselect integrity check passed
- name: Enforce pam_faillock for Local Accounts Only - Get authselect current
profile
ansible.builtin.shell:
cmd: authselect current -r | awk '{ print $1 }'
register: result_authselect_profile
changed_when: false
when:
- result_authselect_check_cmd is success
- name: Enforce pam_faillock for Local Accounts Only - Define the current authselect
profile as a local fact
ansible.builtin.set_fact:
authselect_current_profile: '{{ result_authselect_profile.stdout }}'
authselect_custom_profile: '{{ result_authselect_profile.stdout }}'
when:
- result_authselect_profile is not skipped
- result_authselect_profile.stdout is match("custom/")
- name: Enforce pam_faillock for Local Accounts Only - Define the new authselect
custom profile as a local fact
ansible.builtin.set_fact:
authselect_current_profile: '{{ result_authselect_profile.stdout }}'
authselect_custom_profile: custom/hardening
when:
- result_authselect_profile is not skipped
- result_authselect_profile.stdout is not match("custom/")
- name: Enforce pam_faillock for Local Accounts Only - Get authselect current
features to also enable them in the custom profile
ansible.builtin.shell:
cmd: authselect current | tail -n+3 | awk '{ print $2 }'
register: result_authselect_features
changed_when: false
when:
- result_authselect_profile is not skipped
- authselect_current_profile is not match("custom/")
- name: Enforce pam_faillock for Local Accounts Only - Check if any custom profile
with the same name was already created
ansible.builtin.stat:
path: /etc/authselect/{{ authselect_custom_profile }}
register: result_authselect_custom_profile_present
changed_when: false
when:
- authselect_current_profile is not match("custom/")
- name: Enforce pam_faillock for Local Accounts Only - Create an authselect
custom profile based on the current profile
ansible.builtin.command:
cmd: authselect create-profile hardening -b {{ authselect_current_profile
}}
when:
- result_authselect_check_cmd is success
- authselect_current_profile is not match("custom/")
- not result_authselect_custom_profile_present.stat.exists
- name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes
are applied
ansible.builtin.command:
cmd: authselect apply-changes -b --backup=before-hardening-custom-profile
when:
- result_authselect_check_cmd is success
- result_authselect_profile is not skipped
- authselect_current_profile is not match("custom/")
- authselect_custom_profile is not match(authselect_current_profile)
- name: Enforce pam_faillock for Local Accounts Only - Ensure the authselect
custom profile is selected
ansible.builtin.command:
cmd: authselect select {{ authselect_custom_profile }}
register: result_pam_authselect_select_profile
when:
- result_authselect_check_cmd is success
- result_authselect_profile is not skipped
- authselect_current_profile is not match("custom/")
- authselect_custom_profile is not match(authselect_current_profile)
- name: Enforce pam_faillock for Local Accounts Only - Restore the authselect
features in the custom profile
ansible.builtin.command:
cmd: authselect enable-feature {{ item }}
loop: '{{ result_authselect_features.stdout_lines }}'
register: result_pam_authselect_restore_features
when:
- result_authselect_profile is not skipped
- result_authselect_features is not skipped
- result_pam_authselect_select_profile is not skipped
- name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes
are applied
ansible.builtin.command:
cmd: authselect apply-changes -b --backup=after-hardening-custom-profile
when:
- result_authselect_check_cmd is success
- result_authselect_profile is not skipped
- result_pam_authselect_restore_features is not skipped
- name: Enforce pam_faillock for Local Accounts Only - Change the PAM file to
be edited according to the custom authselect profile
ansible.builtin.set_fact:
pam_file_path: /etc/authselect/{{ authselect_custom_profile }}/{{ pam_file_path
| basename }}
when:
- result_authselect_present.stat.exists
- name: Enforce pam_faillock for Local Accounts Only - Ensure the "local_users_only"
option from "pam_faillock.so" is not present in {{ pam_file_path }}
ansible.builtin.replace:
dest: '{{ pam_file_path }}'
regexp: (.*auth.*pam_faillock.so.*)\blocal_users_only\b=?[0-9a-zA-Z]*(.*)
replace: \1\2
register: result_pam_option_removal
- name: Enforce pam_faillock for Local Accounts Only - Ensure authselect changes
are applied
ansible.builtin.command:
cmd: authselect apply-changes -b
when:
- result_authselect_present.stat.exists
- result_pam_option_removal is changed
when:
- result_pam_file_present.stat.exists
when:
- '"pam" in ansible_facts.packages'
- result_faillock_conf_check.stat.exists
tags:
- CCE-83401-0
- NIST-800-53-AC-2(1)
- accounts_passwords_pam_faillock_enforce_local
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Enforce pam_faillock for Local Accounts Only - Ensure the pam_faillock.so
local_users_only parameter in PAM files
block:
- name: Enforce pam_faillock for Local Accounts Only - Check if pam_faillock.so
local_users_only parameter is already enabled in pam files
ansible.builtin.lineinfile:
path: /etc/pam.d/system-auth
regexp: .*auth.*pam_faillock\.so (preauth|authfail).*local_users_only
state: absent
check_mode: true
changed_when: false
register: result_pam_faillock_local_users_only_parameter_is_present
- name: Enforce pam_faillock for Local Accounts Only - Ensure the inclusion of pam_faillock.so
preauth local_users_only parameter in auth section
ansible.builtin.lineinfile:
path: '{{ item }}'
backrefs: true
regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so preauth.*)
line: \1required\3 local_users_only
state: present
loop:
- /etc/pam.d/system-auth
- /etc/pam.d/password-auth
when:
- result_pam_faillock_local_users_only_parameter_is_present.found == 0
- name: Enforce pam_faillock for Local Accounts Only - Ensure the inclusion of pam_faillock.so
authfail local_users_only parameter in auth section
ansible.builtin.lineinfile:
path: '{{ item }}'
backrefs: true
regexp: (^\s*auth\s+)([\w\[].*\b)(\s+pam_faillock.so authfail.*)
line: \1required\3 local_users_only
state: present
loop:
- /etc/pam.d/system-auth
- /etc/pam.d/password-auth
when:
- result_pam_faillock_local_users_only_parameter_is_present.found == 0
when:
- '"pam" in ansible_facts.packages'
- not result_faillock_conf_check.stat.exists
tags:
- CCE-83401-0
- NIST-800-53-AC-2(1)
- accounts_passwords_pam_faillock_enforce_local
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
Remediation - Shell Script
# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then
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
AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
for pam_file in "${AUTH_FILES[@]}"
do
if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then
sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth required pam_faillock.so preauth silent' "$pam_file"
sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth required pam_faillock.so authfail' "$pam_file"
sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account required pam_faillock.so' "$pam_file"
fi
sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required \3/g' "$pam_file"
done
fi
AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth")
FAILLOCK_CONF="/etc/security/faillock.conf"
if [ -f $FAILLOCK_CONF ]; then
regex="^\s*local_users_only"
line="local_users_only"
if ! grep -q $regex $FAILLOCK_CONF; then
echo $line >> $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.*\blocal_users_only\b' "$PAM_FILE_PATH"; then
sed -i -E --follow-symlinks 's/(.*auth.*pam_faillock.so.*)\blocal_users_only\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).*local_users_only' "$pam_file"; then
sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ local_users_only/' "$pam_file"
sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ local_users_only/' "$pam_file"
fi
done
fi
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi