Ensure PAM password complexity module is enabled in password-auth

Cette page n'est pas encore disponible en français, sa traduction est en cours.
Si vous avez des questions ou des retours sur notre projet de traduction actuel, n'hésitez pas à nous contacter.

Description

To enable PAM password complexity in password-auth file: Edit the password section in /etc/pam.d/password-auth to show password requisite pam_pwquality.so. The pam_pwquality module should be enabled using the authselect tool. By default, authselect always configures pam_pwquality local_users_only as a part of local, sssd, and winbind profiles. No additional authselect feature is needed to be enabled.

Rationale

Enabling PAM password complexity permits to enforce strong passwords and consequently makes the system less prone to dictionary attacks.

Remediation

Shell script

The following script can be run on the host to remediate the issue.

#!/bin/bash

# Remediation is applicable only in certain platforms
if rpm --quiet -q kernel-core && { rpm --quiet -q libpwquality; }; 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 }')
            # The "local" profile does not contain essential security features required by multiple Benchmarks.
            # If currently used, it is replaced by "sssd", which is the best option in this case.
            if [[ $CURRENT_PROFILE == local ]]; then
                CURRENT_PROFILE="sssd"
            fi
            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_pwquality.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_pwquality.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_pwquality.so.*)/\1requisite \2/" "$PAM_FILE_PATH"
    else
        LAST_MATCH_LINE=$(grep -nP "^account.*required.*pam_permit\.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     requisite    pam_pwquality.so" "$PAM_FILE_PATH"
        else
            echo "password    requisite    pam_pwquality.so" >> "$PAM_FILE_PATH"
        fi
    fi
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-89505-2
  - accounts_password_pam_pwquality_password_auth
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed

- name: Ensure PAM password complexity module is enabled in 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:
  - '"kernel-core" in ansible_facts.packages'
  - '"libpwquality" in ansible_facts.packages'
  tags:
  - CCE-89505-2
  - accounts_password_pam_pwquality_password_auth
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed

- name: Ensure PAM password complexity module is enabled in password-auth - Check
    the proper remediation for the system
  block:

  - name: Ensure PAM password complexity module is enabled in 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: Ensure PAM password complexity module is enabled in password-auth - Check
      if system relies on authselect tool
    ansible.builtin.stat:
      path: /usr/bin/authselect
    register: result_authselect_present

  - name: Ensure PAM password complexity module is enabled in password-auth - Ensure
      authselect custom profile is used if authselect is present
    block:

    - name: Ensure PAM password complexity module is enabled in password-auth - Check
        integrity of authselect current profile
      ansible.builtin.command:
        cmd: authselect check
      register: result_authselect_check_cmd
      changed_when: false
      check_mode: false
      failed_when: false

    - name: Ensure PAM password complexity module is enabled in password-auth - Informative
        message based on the authselect integrity check result
      ansible.builtin.assert:
        that:
        - ansible_check_mode or 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: Ensure PAM password complexity module is enabled in 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: Ensure PAM password complexity module is enabled in 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: Ensure PAM password complexity module is enabled in 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: Ensure PAM password complexity module is enabled in 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
      check_mode: false
      when:
      - result_authselect_profile is not skipped
      - authselect_current_profile is not match("custom/")

    - name: Ensure PAM password complexity module is enabled in 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:
      - result_authselect_profile is not skipped
      - authselect_current_profile is not match("custom/")

    - name: Ensure PAM password complexity module is enabled in 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_profile is not skipped
      - result_authselect_check_cmd is success
      - authselect_current_profile is not match("^(custom/|local)")
      - not result_authselect_custom_profile_present.stat.exists

    - name: Ensure PAM password complexity module is enabled in password-auth - Create
        an authselect custom profile based on sssd profile
      ansible.builtin.command:
        cmd: authselect create-profile hardening -b sssd
      when:
      - result_authselect_profile is not skipped
      - result_authselect_check_cmd is success
      - authselect_current_profile is match("local")
      - not result_authselect_custom_profile_present.stat.exists

    - name: Ensure PAM password complexity module is enabled in 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: Ensure PAM password complexity module is enabled in 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: Ensure PAM password complexity module is enabled in 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: Ensure PAM password complexity module is enabled in 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: Ensure PAM password complexity module is enabled in 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:
      - authselect_custom_profile is defined
    when:
    - result_authselect_present.stat.exists

  - name: Ensure PAM password complexity module is enabled in password-auth - Define
      a fact for control already filtered in case filters are used
    ansible.builtin.set_fact:
      pam_module_control: requisite

  - name: Ensure PAM password complexity module is enabled in 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+{{ pam_module_control | regex_escape() }}\s+pam_pwquality.so\s*.*
      state: absent
    check_mode: true
    changed_when: false
    register: result_pam_line_present

  - name: Ensure PAM password complexity module is enabled in password-auth - Include
      or update the PAM module line in {{ pam_file_path }}
    block:

    - name: Ensure PAM password complexity module is enabled in 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_pwquality.so\s*
        state: absent
      check_mode: true
      changed_when: false
      register: result_pam_line_other_control_present

    - name: Ensure PAM password complexity module is enabled in 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_pwquality.so.*)
        replace: \1{{ pam_module_control }} \2
      register: result_pam_module_edit
      when:
      - result_pam_line_other_control_present.found == 1

    - name: Ensure PAM password complexity module is enabled in password-auth - Ensure
        the required PAM module line is included in {{ pam_file_path }}
      ansible.builtin.lineinfile:
        dest: '{{ pam_file_path }}'
        insertafter: ^account.*required.*pam_permit\.so
        line: password    {{ pam_module_control }}    pam_pwquality.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: Ensure PAM password complexity module is enabled in 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: Ensure PAM password complexity module is enabled in password-auth - Ensure
      authselect changes are applied
    ansible.builtin.command:
      cmd: authselect apply-changes -b
    when:
    - result_authselect_present.stat.exists
    - |-
      (result_pam_accounts_password_pam_pwquality_password_auth_add is defined and result_pam_accounts_password_pam_pwquality_password_auth_add.changed)
       or (result_pam_accounts_password_pam_pwquality_password_auth_edit is defined and result_pam_accounts_password_pam_pwquality_password_auth_edit.changed)
  when:
  - '"kernel-core" in ansible_facts.packages'
  - '"libpwquality" in ansible_facts.packages'
  - result_pam_file_present.stat.exists
  tags:
  - CCE-89505-2
  - accounts_password_pam_pwquality_password_auth
  - configure_strategy
  - low_complexity
  - medium_disruption
  - medium_severity
  - no_reboot_needed