Set PAM''s Password Hashing Algorithm

Classification:

compliance

Framework:

Control:

Description

The PAM system service can be configured to only store encrypted representations of passwords. In “/etc/pam.d/system-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.

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

if [ -e "/etc/pam.d/system-auth" ] ; then
 PAM\_FILE\_PATH="/etc/pam.d/system-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/system-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/system-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-82043-1
 - 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
 - PCI-DSSv4-8.3.2
 - configure\_strategy
 - low\_complexity
 - medium\_disruption
 - medium\_severity
 - no\_reboot\_needed
 - set\_password\_hashing\_algorithm\_systemauth

- name: Set PAM's Password Hashing Algorithm - Check if /etc/pam.d/system-auth file
 is present
 ansible.builtin.stat:
 path: /etc/pam.d/system-auth
 register: result\_pam\_file\_present
 when: '"pam" in ansible\_facts.packages'
 tags:
 - CCE-82043-1
 - 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
 - PCI-DSSv4-8.3.2
 - configure\_strategy
 - low\_complexity
 - medium\_disruption
 - medium\_severity
 - no\_reboot\_needed
 - set\_password\_hashing\_algorithm\_systemauth

- name: Set PAM's Password Hashing Algorithm - Check the proper remediation for the
 system
 block:

 - name: Set PAM's Password Hashing Algorithm - Define the PAM file to be edited
 as a local fact
 ansible.builtin.set\_fact:
 pam\_file\_path: /etc/pam.d/system-auth

 - name: Set PAM's Password Hashing Algorithm - 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 - Ensure authselect custom profile
 is used if authselect is present
 block:

 - name: Set PAM's Password Hashing Algorithm - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - Include or update the PAM module
 line in {{ pam\_file\_path }}
 block:

 - name: Set PAM's Password Hashing Algorithm - 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 - 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 - 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 - 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 - 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 - 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 - 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-82043-1
 - 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
 - PCI-DSSv4-8.3.2
 - configure\_strategy
 - low\_complexity
 - medium\_disruption
 - medium\_severity
 - no\_reboot\_needed
 - set\_password\_hashing\_algorithm\_systemauth