Set PAM''s Password Hashing Algorithm - password-auth
Description
The PAM system service can be configured to only store encrypted
representations of passwords. In
/etc/pam.d/password-auth
,
the
password
section of the file controls which PAM modules execute
during a password change. Set the pam_unix.so
module in the
password
section to include the argument sha512
, as shown
below:
password sufficient pam_unix.so sha512 *other arguments...*
This will help ensure when local users change their passwords, hashes for
the new passwords will be generated using the SHA-512 algorithm. This is
the default.
Rationale
Passwords need to be protected at all times, and encryption is the standard
method for protecting passwords. If passwords are not encrypted, they can
be plainly read (i.e., clear text) and easily compromised. Passwords that
are encrypted with a weak algorithm are no more protected than if they are
kepy in plain text.
This setting ensures user and group account administration utilities are
configured to store only encrypted representations of passwords.
Additionally, the crypt_style
configuration option ensures the use
of a strong hashing algorithm that makes password cracking attacks more
difficult.
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
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+'"sufficient"'\s+pam\_unix.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\_unix.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\_unix.so.\*)/\1'"sufficient"' \2/' "$PAM\_FILE\_PATH"
else
echo 'password '"sufficient"' pam\_unix.so' >> "$PAM\_FILE\_PATH"
fi
fi
# Check the option
if ! grep -qP '^\s\*password\s+'"sufficient"'\s+pam\_unix.so\s\*.\*\ssha512\b' "$PAM\_FILE\_PATH"; then
sed -i -E --follow-symlinks '/\s\*password\s+'"sufficient"'\s+pam\_unix.so.\*/ s/$/ sha512/' "$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
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Ansible playbook
The following playbook can be run with Ansible to remediate the issue.
- name: Gather the package facts
package\_facts:
manager: auto
tags:
- CCE-85943-9
- CJIS-5.6.2.2
- DISA-STIG-RHEL-07-010200
- NIST-800-171-3.13.11
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)(c)
- NIST-800-53-IA-5(c)
- PCI-DSS-Req-8.2.1
- configure\_strategy
- low\_complexity
- medium\_disruption
- medium\_severity
- no\_reboot\_needed
- set\_password\_hashing\_algorithm\_passwordauth
- name: Set PAM's Password Hashing Algorithm - password-auth - Check if /etc/pam.d/password-auth
file is present
ansible.builtin.stat:
path: /etc/pam.d/password-auth
register: result\_pam\_file\_present
when: '"pam" in ansible\_facts.packages'
tags:
- CCE-85943-9
- CJIS-5.6.2.2
- DISA-STIG-RHEL-07-010200
- NIST-800-171-3.13.11
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)(c)
- NIST-800-53-IA-5(c)
- PCI-DSS-Req-8.2.1
- configure\_strategy
- low\_complexity
- medium\_disruption
- medium\_severity
- no\_reboot\_needed
- set\_password\_hashing\_algorithm\_passwordauth
- name: Set PAM's Password Hashing Algorithm - password-auth - Check the proper remediation
for the system
block:
- name: Set PAM's Password Hashing Algorithm - password-auth - Define the PAM file
to be edited as a local fact
ansible.builtin.set\_fact:
pam\_file\_path: /etc/pam.d/password-auth
- name: Set PAM's Password Hashing Algorithm - password-auth - Check if system relies
on authselect tool
ansible.builtin.stat:
path: /usr/bin/authselect
register: result\_authselect\_present
- name: Set PAM's Password Hashing Algorithm - password-auth - Ensure authselect
custom profile is used if authselect is present
block:
- name: Set PAM's Password Hashing Algorithm - password-auth - Check integrity
of authselect current profile
ansible.builtin.command:
cmd: authselect check
register: result\_authselect\_check\_cmd
changed\_when: false
failed\_when: false
- name: Set PAM's Password Hashing Algorithm - password-auth - 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: Set PAM's Password Hashing Algorithm - password-auth - 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: Set PAM's Password Hashing Algorithm - password-auth - 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: Set PAM's Password Hashing Algorithm - password-auth - 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: Set PAM's Password Hashing Algorithm - password-auth - 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: Set PAM's Password Hashing Algorithm - password-auth - 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: Set PAM's Password Hashing Algorithm - password-auth - 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: Set PAM's Password Hashing Algorithm - password-auth - 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: Set PAM's Password Hashing Algorithm - password-auth - 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: Set PAM's Password Hashing Algorithm - password-auth - 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: Set PAM's Password Hashing Algorithm - password-auth - 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: Set PAM's Password Hashing Algorithm - password-auth - 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: Set PAM's Password Hashing Algorithm - password-auth - Check if expected
PAM module line is present in {{ pam\_file\_path }}
ansible.builtin.lineinfile:
path: '{{ pam\_file\_path }}'
regexp: ^\s\*password\s+sufficient\s+pam\_unix.so\s\*.\*
state: absent
check\_mode: true
changed\_when: false
register: result\_pam\_line\_present
- name: Set PAM's Password Hashing Algorithm - password-auth - Include or update
the PAM module line in {{ pam\_file\_path }}
block:
- name: Set PAM's Password Hashing Algorithm - password-auth - Check if required
PAM module line is present in {{ pam\_file\_path }} with different control
ansible.builtin.lineinfile:
path: '{{ pam\_file\_path }}'
regexp: ^\s\*password\s+.\*\s+pam\_unix.so\s\*
state: absent
check\_mode: true
changed\_when: false
register: result\_pam\_line\_other\_control\_present
- name: Set PAM's Password Hashing Algorithm - password-auth - Ensure the correct
control for the required PAM module line in {{ pam\_file\_path }}
ansible.builtin.replace:
dest: '{{ pam\_file\_path }}'
regexp: ^(\s\*password\s+).\*(\bpam\_unix.so.\*)
replace: \1sufficient \2
register: result\_pam\_module\_edit
when:
- result\_pam\_line\_other\_control\_present.found == 1
- name: Set PAM's Password Hashing Algorithm - password-auth - Ensure the required
PAM module line is included in {{ pam\_file\_path }}
ansible.builtin.lineinfile:
dest: '{{ pam\_file\_path }}'
line: password sufficient pam\_unix.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: Set PAM's Password Hashing Algorithm - password-auth - 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:
- result\_pam\_line\_present.found is defined
- result\_pam\_line\_present.found == 0
- name: Set PAM's Password Hashing Algorithm - password-auth - Check if the required
PAM module option is present in {{ pam\_file\_path }}
ansible.builtin.lineinfile:
path: '{{ pam\_file\_path }}'
regexp: ^\s\*password\s+sufficient\s+pam\_unix.so\s\*.\*\ssha512\b
state: absent
check\_mode: true
changed\_when: false
register: result\_pam\_module\_sha512\_option\_present
- name: Set PAM's Password Hashing Algorithm - password-auth - Ensure the "sha512"
PAM option for "pam\_unix.so" is included in {{ pam\_file\_path }}
ansible.builtin.lineinfile:
path: '{{ pam\_file\_path }}'
backrefs: true
regexp: ^(\s\*password\s+sufficient\s+pam\_unix.so.\*)
line: \1 sha512
state: present
register: result\_pam\_sha512\_add
when:
- result\_pam\_module\_sha512\_option\_present.found == 0
- name: Set PAM's Password Hashing Algorithm - password-auth - Ensure authselect
changes are applied
ansible.builtin.command:
cmd: authselect apply-changes -b
when:
- result\_authselect\_present.stat.exists
- |-
(result\_pam\_sha512\_add is defined and result\_pam\_sha512\_add.changed)
or (result\_pam\_sha512\_edit is defined and result\_pam\_sha512\_edit.changed)
when:
- '"pam" in ansible\_facts.packages'
- result\_pam\_file\_present.stat.exists
tags:
- CCE-85943-9
- CJIS-5.6.2.2
- DISA-STIG-RHEL-07-010200
- NIST-800-171-3.13.11
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)(c)
- NIST-800-53-IA-5(c)
- PCI-DSS-Req-8.2.1
- configure\_strategy
- low\_complexity
- medium\_disruption
- medium\_severity
- no\_reboot\_needed
- set\_password\_hashing\_algorithm\_passwordauth