Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update rules related to pam_pwhistory module to consider pwhistory.conf file #9994

Merged

Conversation

marcusburghardt
Copy link
Member

Description:

Recently the authselect tool was improved (authselect/authselect#321) to include a new feature to easily and properly enable the pam_pwhistory.so module.

The pam_pwhistoy.so module was also updated use the /etc/security/pwhistory.conf file in order to set its options without the need to edit PAM files directly.

This PR refactored the following rules to make them compatible with these new improvements in authselect and pam_pwhistory.so:

  • accounts_password_pam_pwhistory_remember_password_auth
  • accounts_password_pam_pwhistory_remember_system_auth
  • accounts_password_pam_unix_remember

In order to make the remediation consistent among the rules, new macros were also introduced for Ansible and Bash.

Rationale:

These rules are now compatible with systems which already use newer versions of authselect and pam_pwhistoy.so.
These updates will make sure that CI tests in CentOS Stream no longer fail for the related rules.

Review Hints:

The first rule I worked was the accounts_password_pam_pwhistory_remember_system_auth, so it will appear first in the commits. Once the rule was well tested and refined, the logic was replicated to accounts_password_pam_pwhistory_remember_password_auth and accounts_password_pam_unix_remember.

The accounts_password_pam_pwhistory_remember_system_auth and accounts_password_pam_pwhistory_remember_password_auth are almost identical, except they use a different file.

The accounts_password_pam_unix_remember is a little bit different since its OVAL also accepts the pam_unix.so module. However, the remediation is in favor of pam_pwhistory.so, so using the same approach of the other two rules.

I recommend to review this PR commit by commit since they are chronologically organized and detailed messages are describing each commit. This is a relatively complex PR but using this approach to review it should be much easier.

Relatively recent it was introduced the /etc/security/pwhistory.conf
file in order to make the PAM pam_pwhistory.so module easier to be
configured. Using this file also avoids the necessity to edit PAM files
directly and consequently mitigate PAM errors. Therefore, its a good
practice to use this file and more systems should be configured using
this approach. Now the OVAL is capable to properly assess these cases.

accounts_password_pam_pwhistory_remember_system_auth rule
@marcusburghardt marcusburghardt added Ansible Ansible remediation update. Test Suite Update in Test Suite. OVAL OVAL update. Related to the systems assessments. Bash Bash remediation update. Update Rule Issues or pull requests related to Rules updates. labels Dec 20, 2022
@marcusburghardt marcusburghardt added this to the 0.1.66 milestone Dec 20, 2022
@github-actions
Copy link

Start a new ephemeral environment with changes proposed in this pull request:

rhel8 (from CTF) Environment (using Fedora as testing environment)
Open in Gitpod

Fedora Testing Environment
Open in Gitpod

Oracle Linux 8 Environment
Open in Gitpod

@github-actions
Copy link

github-actions bot commented Dec 20, 2022

This datastream diff is auto generated by the check Compare DS/Generate Diff.
Due to the excessive size of the diff, it has been trimmed to fit the 65535-character limit.

Click here to see the trimmed diff
bash remediation for rule 'xccdf_org.ssgproject.content_rule_display_login_attempts' differs.
--- xccdf_org.ssgproject.content_rule_display_login_attempts
+++ xccdf_org.ssgproject.content_rule_display_login_attempts
@@ -4,6 +4,7 @@
 if [ -e "/etc/pam.d/postlogin" ] ; then
 PAM_FILE_PATH="/etc/pam.d/postlogin"
 if [ -f /usr/bin/authselect ]; then
+ 
 if ! authselect check; then
 echo "
 authselect integrity check failed. Remediation aborted!
@@ -12,6 +13,7 @@
 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
@@ -29,7 +31,7 @@
 fi
 PAM_FILE_NAME=$(basename "/etc/pam.d/postlogin")
 PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"
- 
+
 authselect apply-changes -b
 fi
 if ! grep -qP '^\s*session\s+'"required"'\s+pam_lastlog.so\s*.*' "$PAM_FILE_PATH"; then
@@ -55,14 +57,16 @@
 if [ -e "/etc/pam.d/postlogin" ] ; then
 PAM_FILE_PATH="/etc/pam.d/postlogin"
 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
+ 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
@@ -80,7 +84,7 @@
 fi
 PAM_FILE_NAME=$(basename "/etc/pam.d/postlogin")
 PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"
- 
+
 authselect apply-changes -b
 fi
 

ansible remediation for rule 'xccdf_org.ssgproject.content_rule_display_login_attempts' differs.
--- xccdf_org.ssgproject.content_rule_display_login_attempts
+++ xccdf_org.ssgproject.content_rule_display_login_attempts
@@ -45,12 +45,13 @@
 pam_file_path: /etc/pam.d/postlogin
 
 - name: Ensure PAM Displays Last Logon/Access Notification - Check if system relies
- on authselect
+ on authselect tool
 ansible.builtin.stat:
 path: /usr/bin/authselect
 register: result_authselect_present
 
- - name: Ensure PAM Displays Last Logon/Access Notification - Remediate using authselect
+ - name: Ensure PAM Displays Last Logon/Access Notification - Ensure authselect custom
+ profile is used if authselect is present
 block:
 
 - name: Ensure PAM Displays Last Logon/Access Notification - Check integrity of
@@ -314,12 +315,13 @@
 pam_file_path: /etc/pam.d/postlogin
 
 - name: Ensure PAM Displays Last Logon/Access Notification - Check if system relies
- on authselect
+ on authselect tool
 ansible.builtin.stat:
 path: /usr/bin/authselect
 register: result_authselect_present
 
- - name: Ensure PAM Displays Last Logon/Access Notification - Remediate using authselect
+ - name: Ensure PAM Displays Last Logon/Access Notification - Ensure authselect custom
+ profile is used if authselect is present
 block:
 
 - name: Ensure PAM Displays Last Logon/Access Notification - Check integrity of

bash remediation for rule 'xccdf_org.ssgproject.content_rule_account_passwords_pam_faillock_audit' differs.
--- xccdf_org.ssgproject.content_rule_account_passwords_pam_faillock_audit
+++ xccdf_org.ssgproject.content_rule_account_passwords_pam_faillock_audit
@@ -39,14 +39,16 @@
 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
+ 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
@@ -64,7 +66,7 @@
 fi
 PAM_FILE_NAME=$(basename "$pam_file")
 PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME"
- 
+
 authselect apply-changes -b
 fi
 

ansible remediation for rule 'xccdf_org.ssgproject.content_rule_account_passwords_pam_faillock_audit' differs.
--- xccdf_org.ssgproject.content_rule_account_passwords_pam_faillock_audit
+++ xccdf_org.ssgproject.content_rule_account_passwords_pam_faillock_audit
@@ -187,11 +187,13 @@
 pam_file_path: /etc/pam.d/system-auth
 
 - name: Account Lockouts Must Be Logged - Check if system relies on authselect
+ tool
 ansible.builtin.stat:
 path: /usr/bin/authselect
 register: result_authselect_present
 
- - name: Account Lockouts Must Be Logged - Remediate using authselect
+ - name: Account Lockouts Must Be Logged - Ensure authselect custom profile is
+ used if authselect is present
 block:
 
 - name: Account Lockouts Must Be Logged - Check integrity of authselect current
@@ -352,11 +354,13 @@
 pam_file_path: /etc/pam.d/password-auth
 
 - name: Account Lockouts Must Be Logged - Check if system relies on authselect
+ tool
 ansible.builtin.stat:
 path: /usr/bin/authselect
 register: result_authselect_present
 
- - name: Account Lockouts Must Be Logged - Remediate using authselect
+ - name: Account Lockouts Must Be Logged - Ensure authselect custom profile is
+ used if authselect is present
 block:
 
 - name: Account Lockouts Must Be Logged - Check integrity of authselect current

New content has different text for rule 'xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_password_auth'.
--- xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_password_auth
+++ xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_password_auth
@@ -6,11 +6,19 @@
 Do not allow users to reuse recent passwords. This can be accomplished by using the
 remember option for the pam_pwhistory PAM module.
 
-In the file /etc/pam.d/password-auth, make sure the parameter
-remember is present and it has a value equal to or greater than
-'xccdf_org.ssgproject.content_value_var_password_pam_remember'. For example:
-password control_flag pam_pwhistory.so ...existing_options... remember='xccdf_org.ssgproject.content_value_var_password_pam_remember' use_authtok
-control_flag should be one of the next values: 'xccdf_org.ssgproject.content_value_var_password_pam_remember_control_flag'
+
+On systems with newer versions of authselect, the pam_pwhistory PAM module
+can be enabled via authselect feature:
+authselect enable-feature with-pwhistory
+
+Otherwise, it should be enabled using an authselect custom profile.
+
+Newer systems also have the /etc/security/pwhistory.conf file for setting
+pam_pwhistory module options. This file should be used whenever available.
+Otherwise, the pam_pwhistory module options can be set in PAM files.
+
+The value for remember option must be equal or greater than
+'xccdf_org.ssgproject.content_value_var_password_pam_remember'
 
 [warning]:
 If the system relies on authselect tool to manage PAM settings, the remediation
@@ -19,6 +27,11 @@
 aborted in order to preserve intentional changes. In this case, an informative message will
 be shown in the remediation report.
 
+[warning]:
+Newer versions of authselect contain an authselect feature to easily and properly
+enable pam_pwhistory.so module. If this feature is not yet available in your
+system, an authselect custom profile must be used to avoid integrity issues in PAM files.
+
 [reference]:
 1
 
@@ -197,7 +210,8 @@
 SV-230368r810414_rule
 
 [rationale]:
-Preventing re-use of previous passwords helps ensure that a compromised password is not re-used by a user.
+Preventing re-use of previous passwords helps ensure that a compromised password is not
+re-used by a user.
 
 [ident]:
 CCE-83478-8

OVAL for rule 'xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_password_auth' differs.
--- oval:ssg-accounts_password_pam_pwhistory_remember_password_auth:def:1
+++ oval:ssg-accounts_password_pam_pwhistory_remember_password_auth:def:1
@@ -1,2 +1,9 @@
 criteria AND
 criterion oval:ssg-test_accounts_password_pam_pwhistory_remember_password_auth:tst:1
+criteria OR
+criteria AND
+criterion oval:ssg-test_accounts_password_pam_pwhistory_remember_password_auth_pamd:tst:1
+criterion oval:ssg-test_accounts_password_pam_pwhistory_remember_password_auth_no_pwhistory_conf:tst:1
+criteria AND
+criterion oval:ssg-test_accounts_password_pam_pwhistory_remember_password_auth_no_pamd:tst:1
+criterion oval:ssg-test_accounts_password_pam_pwhistory_remember_password_auth_pwhistory_conf:tst:1

OCIL for rule 'xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_password_auth' differs.
--- ocil:ssg-accounts_password_pam_pwhistory_remember_password_auth_ocil:questionnaire:1
+++ ocil:ssg-accounts_password_pam_pwhistory_remember_password_auth_ocil:questionnaire:1
@@ -1,9 +1,20 @@
-Verify Red Hat Enterprise Linux 8 is configured in the password-auth file to prohibit password reuse
-for a minimum of generations with the following command:
+Verify Red Hat Enterprise Linux 8 use the "pam_pwhistory.so" module in the /etc/pam.d/password-auth file
+and is configured to prohibit password reuse for a minimum of 
+generations.
 
-$ grep -i remember /etc/pam.d/password-auth
-password pam_pwhistory.so use_authtok remember= retry=3
- Is it the case that the line containing "pam_pwhistory.so" does not have the "remember" module argument set,
-is commented out, or the value of the "remember" module argument is set to less than
-"<sub idref="var_password_pam_remember" />"?
+Verify the "/etc/pam.d/password-auth" file with the following command:
+
+$ grep pam_pwhistory.so /etc/pam.d/password-auth
+password pam_pwhistory.so use_authtok remember=
+
+
+Verify the "/etc/security/pwhistory.conf" file using the following command:
+
+$ grep remember /etc/security/pwhistory.conf
+remember = 
+
+The pam_pwhistory.so "remember" option must be configured only in one file.
+ Is it the case that the pam_pwhistory.so module is not used, the "remember" module option is not set in
+/etc/pam.d/password-auth or in /etc/security/pwhistory.conf, or is set in both files, or is set
+with a value less than "<sub idref="var_password_pam_remember" />"?
 
bash remediation for rule 'xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_password_auth' differs.
--- xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_password_auth
+++ xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_password_auth
@@ -7,9 +7,8 @@
 
 var_password_pam_remember_control_flag="$(echo $var_password_pam_remember_control_flag | cut -d \, -f 1)"
 
-if [ -e "/etc/pam.d/password-auth" ] ; then
- PAM_FILE_PATH="/etc/pam.d/password-auth"
- if [ -f /usr/bin/authselect ]; then
+if [ -f /usr/bin/authselect ]; then
+ if authselect list-features minimal | grep -q with-pwhistory; then
 if ! authselect check; then
 echo "
 authselect integrity check failed. Remediation aborted!
@@ -18,6 +17,20 @@
 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-pwhistory
+
+ authselect apply-changes -b
+ else
+ 
+ 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
@@ -35,35 +48,135 @@
 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+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s*.*' "$PAM_FILE_PATH"; then
+ if ! grep -qP '^\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.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_pwhistory.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_pwhistory.so.*)/\1'"$var_password_pam_remember_control_flag"' \2/' "$PAM_FILE_PATH"
 else
- LAST_MATCH_LINE=$(grep -nP "^password.*requisite.*pam_pwquality\.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 '"$var_password_pam_remember_control_flag"' pam_pwhistory.so' "$PAM_FILE_PATH"
- else
- echo 'password '"$var_password_pam_remember_control_flag"' pam_pwhistory.so' >> "$PAM_FILE_PATH"
- fi
+ echo 'password '"$var_password_pam_remember_control_flag"' pam_pwhistory.so' >> "$PAM_FILE_PATH"
 fi
 fi
- # Check the option
- if ! grep -qP '^\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s*.*\sremember\b' "$PAM_FILE_PATH"; then
- sed -i -E --follow-symlinks '/\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so.*/ s/$/ remember='"$var_password_pam_remember"'/' "$PAM_FILE_PATH"
+ fi
+else
+ if ! grep -qP '^\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s*.*' "/etc/pam.d/password-auth"; then
+ # Line matching group + control + module was not found. Check group + module.
+ if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "/etc/pam.d/password-auth")" -eq 1 ]; then
+ # The control is updated only if one single line matches.
+ sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1'"$var_password_pam_remember_control_flag"' \2/' "/etc/pam.d/password-auth"
 else
- sed -i -E --follow-symlinks 's/(\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s+.*)('"remember"'=)[[:alnum:]]+\s*(.*)/\1\2'"$var_password_pam_remember"' \3/' "$PAM_FILE_PATH"
+ echo 'password '"$var_password_pam_remember_control_flag"' pam_pwhistory.so' >> "/etc/pam.d/password-auth"
 fi
+ fi
+fi
+
+PWHISTORY_CONF="/etc/security/pwhistory.conf"
+if [ -f $PWHISTORY_CONF ]; then
+ regex="^\s*remember\s*="
+ line="remember = $var_password_pam_remember"
+ if ! grep -q $regex $PWHISTORY_CONF; then
+ echo $line >> $PWHISTORY_CONF
+ else
+ sed -i --follow-symlinks 's|^\s*\(remember\s*=\s*\)\(\S\+\)|\1'"$var_password_pam_remember"'|g' $PWHISTORY_CONF
+ fi
+ 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.*\bpam_pwhistory.so\s.*\bremember\b' "$PAM_FILE_PATH"; then
+ sed -i -E --follow-symlinks 's/(.*password.*pam_pwhistory.so.*)\bremember\b=?[[:alnum:]]*(.*)/\1\2/g' "$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
+ 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+'"requisite"'\s+pam_pwhistory.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_pwhistory.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_pwhistory.so.*)/\1'"requisite"' \2/' "$PAM_FILE_PATH"
+ else
+ echo 'password '"requisite"' pam_pwhistory.so' >> "$PAM_FILE_PATH"
+ fi
+ fi
+ # Check the option
+ if ! grep -qP '^\s*password\s+'"requisite"'\s+pam_pwhistory.so\s*.*\sremember\b' "$PAM_FILE_PATH"; then
+ sed -i -E --follow-symlinks '/\s*password\s+'"requisite"'\s+pam_pwhistory.so.*/ s/$/ remember='"$var_password_pam_remember"'/' "$PAM_FILE_PATH"
+ else
+ sed -i -E --follow-symlinks 's/(\s*password\s+'"requisite"'\s+pam_pwhistory.so\s+.*)('"remember"'=)[[:alnum:]]+\s*(.*)/\1\2'"$var_password_pam_remember"' \3/' "$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

ansible remediation for rule 'xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_password_auth' differs.
--- xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_password_auth
+++ xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_password_auth
@@ -26,11 +26,11 @@
 tags:
 - always
 
-- name: 'Limit Password Reuse: password-auth - Check if /etc/pam.d/password-auth file
- is present'
+- name: 'Limit Password Reuse: password-auth - Check if system relies on authselect
+ tool'
 ansible.builtin.stat:
- path: /etc/pam.d/password-auth
- register: result_pam_file_present
+ path: /usr/bin/authselect
+ register: result_authselect_present
 when: '"pam" in ansible_facts.packages'
 tags:
 - CCE-83478-8
@@ -47,8 +47,101 @@
 - medium_severity
 - no_reboot_needed
 
-- name: 'Limit Password Reuse: password-auth - Check the proper remediation for the
- system'
+- name: 'Limit Password Reuse: password-auth - Collect the available authselect features'
+ ansible.builtin.command:
+ cmd: authselect list-features minimal
+ register: result_authselect_available_features
+ changed_when: false
+ when:
+ - '"pam" in ansible_facts.packages'
+ - result_authselect_present.stat.exists
+ tags:
+ - CCE-83478-8
+ - CJIS-5.6.2.1.1
+ - DISA-STIG-RHEL-08-020220
+ - NIST-800-171-3.5.8
+ - NIST-800-53-IA-5(1)(e)
+ - NIST-800-53-IA-5(f)
+ - PCI-DSS-Req-8.2.5
+ - accounts_password_pam_pwhistory_remember_password_auth
+ - configure_strategy
+ - low_complexity
+ - medium_disruption
+ - medium_severity
+ - no_reboot_needed
+
+- name: 'Limit Password Reuse: password-auth - Enable pam_pwhistory.so using authselect
+ feature'
+ block:
+
+ - name: 'Limit Password Reuse: password-auth - Check integrity of authselect current
+ profile'
+ ansible.builtin.command:
+ cmd: authselect check
+ register: result_authselect_check_cmd
+ changed_when: false
+ ignore_errors: true
+
+ - name: 'Limit Password Reuse: password-auth - Informative message based on the
+ authselect integrity check result'
+ ansible.builtin.assert:
+ that:
+ - result_authselect_check_cmd is success
+ 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: 'Limit Password Reuse: password-auth - 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: 'Limit Password Reuse: password-auth - Ensure "with-pwhistory" feature is
+ enabled using authselect tool'
+ ansible.builtin.command:
+ cmd: authselect enable-feature with-pwhistory
+ register: result_authselect_enable_feature_cmd
+ when:
+ - result_authselect_check_cmd is success
+ - result_authselect_features.stdout is not search("with-pwhistory")
+
+ - name: 'Limit Password Reuse: password-auth - 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
+ - result_authselect_available_features.stdout is search("with-pwhistory")
+ tags:
+ - CCE-83478-8
+ - CJIS-5.6.2.1.1
+ - DISA-STIG-RHEL-08-020220
+ - NIST-800-171-3.5.8
+ - NIST-800-53-IA-5(1)(e)
+ - NIST-800-53-IA-5(f)
+ - PCI-DSS-Req-8.2.5
+ - accounts_password_pam_pwhistory_remember_password_auth
+ - configure_strategy
+ - low_complexity
+ - medium_disruption
+ - medium_severity
+ - no_reboot_needed
+
+- name: 'Limit Password Reuse: password-auth - Enable pam_pwhistory.so in appropriate
+ PAM files'
 block:
 
 - name: 'Limit Password Reuse: password-auth - Define the PAM file to be edited
@@ -56,12 +149,14 @@
 ansible.builtin.set_fact:
 pam_file_path: /etc/pam.d/password-auth
 
- - name: 'Limit Password Reuse: password-auth - Check if system relies on authselect'
+ - name: 'Limit Password Reuse: password-auth - Check if system relies on authselect
+ tool'
 ansible.builtin.stat:
 path: /usr/bin/authselect
 register: result_authselect_present
 
- - name: 'Limit Password Reuse: password-auth - Remediate using authselect'
+ - name: 'Limit Password Reuse: password-auth - Ensure authselect custom profile
+ is used if authselect is present'
 block:
 
 - name: 'Limit Password Reuse: password-auth - Check integrity of authselect current
@@ -245,53 +340,10 @@
 when:
 - result_pam_line_present.found is defined
 - result_pam_line_present.found == 0
-
- - name: 'Limit Password Reuse: 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+{{ var_password_pam_remember_control_flag.split(",")[0]
- }}\s+pam_pwhistory.so\s*.*\sremember\b
- state: absent
- check_mode: true
- changed_when: false
- register: result_pam_module_remember_option_present
-
- - name: 'Limit Password Reuse: password-auth - Ensure the "remember" PAM option
- for "pam_pwhistory.so" is included in {{ pam_file_path }}'
- ansible.builtin.lineinfile:
- path: '{{ pam_file_path }}'
- backrefs: true
- regexp: ^(\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0]
- }}\s+pam_pwhistory.so.*)
- line: \1 remember={{ var_password_pam_remember }}
- state: present
- register: result_pam_remember_add
- when:
- - result_pam_module_remember_option_present.found == 0
-
- - name: 'Limit Password Reuse: password-auth - Ensure the required value for "remember"
- PAM option from "pam_pwhistory.so" in {{ pam_file_path }}'
- ansible.builtin.lineinfile:
- path: '{{ pam_file_path }}'
- backrefs: true
- regexp: ^(\s*password\s+{{ var_password_pam_remember_control_flag.split(",")[0]
- }}\s+pam_pwhistory.so\s+.*)(remember)=[0-9a-zA-Z]+\s*(.*)
- line: \1\2={{ var_password_pam_remember }} \3
- register: result_pam_remember_edit
- when:
- - result_pam_module_remember_option_present.found > 0
-
- - name: 'Limit Password Reuse: password-auth - Ensure authselect changes are applied'
- ansible.builtin.command:
- cmd: authselect apply-changes -b
- when:
- - result_authselect_present.stat.exists
- - (result_pam_remember_add is defined and result_pam_remember_add.changed) or
- (result_pam_remember_edit is defined and result_pam_remember_edit.changed)
 when:
 - '"pam" in ansible_facts.packages'
- - result_pam_file_present.stat.exists
+ - |
+ (result_authselect_available_features.stdout is defined and result_authselect_available_features.stdout is not search("with-pwhistory")) or result_authselect_available_features is not defined
 tags:
 - CCE-83478-8
 - CJIS-5.6.2.1.1
@@ -306,3 +358,484 @@
 - medium_disruption
 - medium_severity
 - no_reboot_needed
+
+- name: 'Limit Password Reuse: password-auth - Check the presence of /etc/security/pwhistory.conf
+ file'
+ ansible.builtin.stat:
+ path: /etc/security/pwhistory.conf
+ register: result_pwhistory_conf_check
+ when: '"pam" in ansible_facts.packages'
+ tags:
+ - CCE-83478-8
+ - CJIS-5.6.2.1.1
+ - DISA-STIG-RHEL-08-020220
+ - NIST-800-171-3.5.8
+ - NIST-800-53-IA-5(1)(e)
+ - NIST-800-53-IA-5(f)
+ - PCI-DSS-Req-8.2.5
+ - accounts_password_pam_pwhistory_remember_password_auth
+ - configure_strategy
+ - low_complexity
+ - medium_disruption
+ - medium_severity
+ - no_reboot_needed
+
+- name: 'Limit Password Reuse: password-auth - pam_pwhistory.so parameters are configured
+ in /etc/security/pwhistory.conf file'
+ block:
+
+ - name: 'Limit Password Reuse: password-auth - Ensure the pam_pwhistory.so remember
+ parameter in /etc/security/pwhistory.conf'
+ ansible.builtin.lineinfile:
+ path: /etc/security/pwhistory.conf
+ regexp: ^\s*remember\s*=
+ line: remember = {{ var_password_pam_remember }}
+ state: present
+
+ - name: 'Limit Password Reuse: password-auth - Ensure the pam_pwhistory.so remember
+ parameter is removed from PAM files'
+ block:
+
+ - name: 'Limit Password Reuse: 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
+
+ - name: 'Limit Password Reuse: password-auth - Check the proper remediation for
+ the system'
+ block:
+
+ - name: 'Limit Password Reuse: 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: 'Limit Password Reuse: password-auth - Check if system relies on authselect
+ tool'
+ ansible.builtin.stat:
+ path: /usr/bin/authselect
+ register: result_authselect_present
+
+ - name: 'Limit Password Reuse: password-auth - Ensure authselect custom profile
+ is used if authselect is present'
+ block:
+
+ - name: 'Limit Password Reuse: password-auth - Check integrity of authselect
+ current profile'
+ ansible.builtin.command:
+ cmd: authselect check
+ register: result_authselect_check_cmd
+ changed_when: false
+ ignore_errors: true
+
+ - name: 'Limit Password Reuse: password-auth - Informative message based on
+ the authselect integrity check result'
+ ansible.builtin.assert:
+ that:
+ - result_authselect_check_cmd is success
+ 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: password-auth - Ensure the "remember" option
+ from "pam_pwhistory.so" is not present in {{ pam_file_path }}'
+ ansible.builtin.replace:
+ dest: '{{ pam_file_path }}'
+ regexp: (.*password.*pam_pwhistory.so.*)\bremember\b=?[0-9a-zA-Z]*(.*)
+ replace: \1\2
+ register: result_pam_option_removal
+
+ - name: 'Limit Password Reuse: password-auth - 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_pwhistory_conf_check.stat.exists
+ tags:
+ - CCE-83478-8
+ - CJIS-5.6.2.1.1
+ - DISA-STIG-RHEL-08-020220
+ - NIST-800-171-3.5.8
+ - NIST-800-53-IA-5(1)(e)
+ - NIST-800-53-IA-5(f)
+ - PCI-DSS-Req-8.2.5
+ - accounts_password_pam_pwhistory_remember_password_auth
+ - configure_strategy
+ - low_complexity
+ - medium_disruption
+ - medium_severity
+ - no_reboot_needed
+
+- name: 'Limit Password Reuse: password-auth - pam_pwhistory.so parameters are configured
+ in PAM files'
+ block:
+
+ - name: 'Limit Password Reuse: 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: 'Limit Password Reuse: password-auth - Check if system relies on authselect
+ tool'
+ ansible.builtin.stat:
+ path: /usr/bin/authselect
+ register: result_authselect_present
+
+ - name: 'Limit Password Reuse: password-auth - Ensure authselect custom profile
+ is used if authselect is present'
+ block:
+
+ - name: 'Limit Password Reuse: password-auth - Check integrity of authselect current
+ profile'
+ ansible.builtin.command:
+ cmd: authselect check
+ register: result_authselect_check_cmd
+ changed_when: false
+ ignore_errors: true
+
+ - name: 'Limit Password Reuse: password-auth - Informative message based on the
+ authselect integrity check result'
+ ansible.builtin.assert:
+ that:
+ - result_authselect_check_cmd is success
+ 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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: 'Limit Password Reuse: 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+requisite\s+pam_pwhistory.so\s*.*
+ state: absent
+ check_mode: true
+ changed_when: false
+ register: result_pam_line_present
+
+ - name: 'Limit Password Reuse: password-auth - Include or update the PAM module
+ line in {{ pam_file_path }}'
+ block:
+
+ - name: 'Limit Password Reuse: 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_pwhistory.so\s*
+ state: absent
+ check_mode: true
+ changed_when: false
+ register: result_pam_line_other_control_present
+
+ - name: 'Limit Password Reuse: 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_pwhistory.so.*)
+ replace: \1requisite \2
+ register: result_pam_module_edit
+ when:
+ - result_pam_line_other_control_present.found == 1
+
+ - name: 'Limit Password Reuse: password-auth - Ensure the required PAM module
+ line is included in {{ pam_file_path }}'
+ ansible.builtin.lineinfile:
+ dest: '{{ pam_file_path }}'
+ line: password requisite pam_pwhistory.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: 'Limit Password Reuse: password-auth - Ensure authselect changes are applied'
+ ansible.builtin.command:
+ cmd: authselect apply-changes -b
+ when: |
+ result_authselect_present is defined and result_authselect_present.stat.exists and ((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: 'Limit Password Reuse: 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+requisite\s+pam_pwhistory.so\s*.*\sremember\b
+ state: absent
+ check_mode: true
+ changed_when: false
+ register: result_pam_module_remember_option_present
+
+ - name: 'Limit Password Reuse: password-auth - Ensure the "remember" PAM option
+ for "pam_pwhistory.so" is included in {{ pam_file_path }}'
+ ansible.builtin.lineinfile:
+ path: '{{ pam_file_path }}'
+ backrefs: true
+ regexp: ^(\s*password\s+requisite\s+pam_pwhistory.so.*)
+ line: \1 remember={{ var_password_pam_remember }}
+ state: present
+ register: result_pam_remember_add
+ when:
+ - result_pam_module_remember_option_present.found == 0
+
+ - name: 'Limit Password Reuse: password-auth - Ensure the required value for "remember"
+ PAM option from "pam_pwhistory.so" in {{ pam_file_path }}'
+ ansible.builtin.lineinfile:
+ path: '{{ pam_file_path }}'
+ backrefs: true
+ regexp: ^(\s*password\s+requisite\s+pam_pwhistory.so\s+.*)(remember)=[0-9a-zA-Z]+\s*(.*)
+ line: \1\2={{ var_password_pam_remember }} \3
+ register: result_pam_remember_edit
+ when:
+ - result_pam_module_remember_option_present.found > 0
+
+ - name: 'Limit Password Reuse: password-auth - Ensure authselect changes are applied'
+ ansible.builtin.command:
+ cmd: authselect apply-changes -b
+ when:
+ - result_authselect_present.stat.exists
+ - (result_pam_remember_add is defined and result_pam_remember_add.changed) or
+ (result_pam_remember_edit is defined and result_pam_remember_edit.changed)
+ when:
+ - '"pam" in ansible_facts.packages'
+ - not result_pwhistory_conf_check.stat.exists
+ tags:
+ - CCE-83478-8
+ - CJIS-5.6.2.1.1
+ - DISA-STIG-RHEL-08-020220
+ - NIST-800-171-3.5.8
+ - NIST-800-53-IA-5(1)(e)
+ - NIST-800-53-IA-5(f)
+ - PCI-DSS-Req-8.2.5
+ - accounts_password_pam_pwhistory_remember_password_auth
+ - configure_strategy
+ - low_complexity
+ - medium_disruption
+ - medium_severity
+ - no_reboot_needed

New content has different text for rule 'xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_system_auth'.
--- xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_system_auth
+++ xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_system_auth
@@ -6,12 +6,19 @@
 Do not allow users to reuse recent passwords. This can be accomplished by using the
 remember option for the pam_pwhistory PAM module.
 
-In the file /etc/pam.d/system-auth, make sure the parameter
-remember is present and it has a value equal to or greater than
+
+On systems with newer versions of authselect, the pam_pwhistory PAM module
+can be enabled via authselect feature:
+authselect enable-feature with-pwhistory
+
+Otherwise, it should be enabled using an authselect custom profile.
+
+Newer systems also have the /etc/security/pwhistory.conf file for setting
+pam_pwhistory module options. This file should be used whenever available.
+Otherwise, the pam_pwhistory module options can be set in PAM files.
+
+The value for remember option must be equal or greater than
 'xccdf_org.ssgproject.content_value_var_password_pam_remember'
-For example:
-password control_flag pam_pwhistory.so ...existing_options... remember='xccdf_org.ssgproject.content_value_var_password_pam_remember' use_authtok
-control_flag should be one of the next values: 'xccdf_org.ssgproject.content_value_var_password_pam_remember_control_flag'
 
 [warning]:
 If the system relies on authselect tool to manage PAM settings, the remediation
@@ -20,6 +27,11 @@
 aborted in order to preserve intentional changes. In this case, an informative message will
 be shown in the remediation report.
 
+[warning]:
+Newer versions of authselect contain an authselect feature to easily and properly
+enable pam_pwhistory.so module. If this feature is not yet available in your
+system, an authselect custom profile must be used to avoid integrity issues in PAM files.
+
 [reference]:
 1
 
@@ -198,7 +210,8 @@
 SV-251717r858745_rule
 
 [rationale]:
-Preventing re-use of previous passwords helps ensure that a compromised password is not re-used by a user.
+Preventing re-use of previous passwords helps ensure that a compromised password is not
+re-used by a user.
 
 [ident]:
 CCE-83480-4

OVAL for rule 'xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_system_auth' differs.
--- oval:ssg-accounts_password_pam_pwhistory_remember_system_auth:def:1
+++ oval:ssg-accounts_password_pam_pwhistory_remember_system_auth:def:1
@@ -1,2 +1,9 @@
 criteria AND
 criterion oval:ssg-test_accounts_password_pam_pwhistory_remember_system_auth:tst:1
+criteria OR
+criteria AND
+criterion oval:ssg-test_accounts_password_pam_pwhistory_remember_system_auth_pamd:tst:1
+criterion oval:ssg-test_accounts_password_pam_pwhistory_remember_system_auth_no_pwhistory_conf:tst:1
+criteria AND
+criterion oval:ssg-test_accounts_password_pam_pwhistory_remember_system_auth_no_pamd:tst:1
+criterion oval:ssg-test_accounts_password_pam_pwhistory_remember_system_auth_pwhistory_conf:tst:1

OCIL for rule 'xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_system_auth' differs.
--- ocil:ssg-accounts_password_pam_pwhistory_remember_system_auth_ocil:questionnaire:1
+++ ocil:ssg-accounts_password_pam_pwhistory_remember_system_auth_ocil:questionnaire:1
@@ -1,9 +1,20 @@
-Verify Red Hat Enterprise Linux 8 is configured in the system-auth file to prohibit password reuse
-for a minimum of generations with the following command:
+Verify Red Hat Enterprise Linux 8 use the "pam_pwhistory.so" module in the /etc/pam.d/system-auth file
+and is configured to prohibit password reuse for a minimum of 
+generations.
 
-$ grep -i remember /etc/pam.d/system-auth
-password pam_pwhistory.so use_authtok remember= retry=3
- Is it the case that the line containing "pam_pwhistory.so" does not have the "remember" module argument set,
-is commented out, or the value of the "remember" module argument is set to less than
-"<sub idref="var_password_pam_remember" />"?
+Verify the "/etc/pam.d/system-auth" file with the following command:
+
+$ grep pam_pwhistory.so /etc/pam.d/system-auth
+password pam_pwhistory.so use_authtok remember=
+
+
+Verify the "/etc/security/pwhistory.conf" file using the following command:
+
+$ grep remember /etc/security/pwhistory.conf
+remember = 
+
+The pam_pwhistory.so "remember" option must be configured only in one file.
+ Is it the case that the pam_pwhistory.so module is not used, the "remember" module option is not set in
+/etc/pam.d/system-auth or in /etc/security/pwhistory.conf, or is set in both files, or is set
+with a value less than "<sub idref="var_password_pam_remember" />"?
 
bash remediation for rule 'xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_system_auth' differs.
--- xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_system_auth
+++ xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_system_auth
@@ -7,9 +7,8 @@
 
 var_password_pam_remember_control_flag="$(echo $var_password_pam_remember_control_flag | cut -d \, -f 1)"
 
-if [ -e "/etc/pam.d/system-auth" ] ; then
- PAM_FILE_PATH="/etc/pam.d/system-auth"
- if [ -f /usr/bin/authselect ]; then
+if [ -f /usr/bin/authselect ]; then
+ if authselect list-features minimal | grep -q with-pwhistory; then
 if ! authselect check; then
 echo "
 authselect integrity check failed. Remediation aborted!
@@ -18,6 +17,20 @@
 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-pwhistory
+
+ authselect apply-changes -b
+ else
+ 
+ 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
@@ -35,35 +48,135 @@
 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+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s*.*' "$PAM_FILE_PATH"; then
+ if ! grep -qP '^\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.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_pwhistory.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_pwhistory.so.*)/\1'"$var_password_pam_remember_control_flag"' \2/' "$PAM_FILE_PATH"
 else
- LAST_MATCH_LINE=$(grep -nP "^password.*requisite.*pam_pwquality\.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 '"$var_password_pam_remember_control_flag"' pam_pwhistory.so' "$PAM_FILE_PATH"
- else
- echo 'password '"$var_password_pam_remember_control_flag"' pam_pwhistory.so' >> "$PAM_FILE_PATH"
- fi
+ echo 'password '"$var_password_pam_remember_control_flag"' pam_pwhistory.so' >> "$PAM_FILE_PATH"
 fi
 fi
- # Check the option
- if ! grep -qP '^\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s*.*\sremember\b' "$PAM_FILE_PATH"; then
- sed -i -E --follow-symlinks '/\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so.*/ s/$/ remember='"$var_password_pam_remember"'/' "$PAM_FILE_PATH"
+ fi
+else
+ if ! grep -qP '^\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s*.*' "/etc/pam.d/system-auth"; then
+ # Line matching group + control + module was not found. Check group + module.
+ if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "/etc/pam.d/system-auth")" -eq 1 ]; then
+ # The control is updated only if one single line matches.
+ sed -i -E --follow-symlinks 's/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1'"$var_password_pam_remember_control_flag"' \2/' "/etc/pam.d/system-auth"
 else
- sed -i -E --follow-symlinks 's/(\s*password\s+'"$var_password_pam_remember_control_flag"'\s+pam_pwhistory.so\s+.*)('"remember"'=)[[:alnum:]]+\s*(.*)/\1\2'"$var_password_pam_remember"' \3/' "$PAM_FILE_PATH"
+ echo 'password '"$var_password_pam_remember_control_flag"' pam_pwhistory.so' >> "/etc/pam.d/system-auth"
 fi
+ fi
+fi
+
+PWHISTORY_CONF="/etc/security/pwhistory.conf"
+if [ -f $PWHISTORY_CONF ]; then
+ regex="^\s*remember\s*="
+ line="remember = $var_password_pam_remember"
+ if ! grep -q $regex $PWHISTORY_CONF; then
+ echo $line >> $PWHISTORY_CONF
+ else
+ sed -i --follow-symlinks 's|^\s*\(remember\s*=\s*\)\(\S\+\)|\1'"$var_password_pam_remember"'|g' $PWHISTORY_CONF
+ fi
+ 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
+

... The diff is trimmed here ...

@marcusburghardt
Copy link
Member Author

I saw the Ansible remediation needs some adjustments. I am working on it.

@marcusburghardt
Copy link
Member Author

I saw the Ansible remediation needs some adjustments. I am working on it.

It should be fine now after the last commit.

A new feature to configure this module was recently included in
authselect. There is currently 3 scenarios where the module
configuration differs. On systems with newer versions of authselect, the
module should be enabled using authselect feature. On systems with older
versions of authselect, the module should be enabled using custom
profiles. On systems without authselect, the module is configured
directly editing PAM files. Since the macros were created, they were
also extended to use /etc/security/pwhistory.conf file whenever
ossible.
accounts_password_pam_pwhistory_remember_system_auth rule
The existing and relevant test scenarios were udpated to consider the
/etc/security/pwhistory.conf file. It was included a test scenario for
conflicting settings.
These macros were created in Ansible in aligment to the respective
macros in Bash.
accounts_password_pam_pwhistory_remember_system_auth rule
accounts_password_pam_pwhistory_remember_password_auth rule
accounts_password_pam_pwhistory_remember_password_auth rule
accounts_password_pam_pwhistory_remember_password_auth rule
@marcusburghardt
Copy link
Member Author

/retest

accounts_password_pam_pwhistory_remember_password_auth rule
accounts_password_pam_unix_remember rule
accounts_password_pam_unix_remember rule
accounts_password_pam_unix_remember rule
accounts_password_pam_unix_remember rule
accounts_password_pam_pwhistory_remember_password_auth
accounts_password_pam_pwhistory_remember_system_auth
The first condition was prone to fatal errors, as there were chances of
an object used in the criteria not being defined in some contexts.
The name "bash_validate_authselect_custom_profile" was not so intuitive
about the variables defined there. On the other hand, the equivalent
macro in Ansible was much clearer. In order to make it more readable,
the macro was renamed to "bash_ensure_pam_variables_and_authselect_profile"
and a more complete description was included. It is now more readble and
more aligned to the equivalent in Ansible. In addition, a jinja comment
was included when a macro is called to set or modify a variable.
Copy link
Collaborator

@vojtapolasek vojtapolasek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good now. Thank you for this improvement and excellent test scenarios.

@codeclimate
Copy link

codeclimate bot commented Dec 22, 2022

Code Climate has analyzed commit ed55009 and detected 0 issues on this pull request.

The test coverage on the diff in this pull request is 100.0% (50% is the threshold).

This pull request will bring the total coverage in the repository to 49.8% (0.0% change).

View more on Code Climate.

@vojtapolasek
Copy link
Collaborator

/retest

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Ansible Ansible remediation update. Bash Bash remediation update. OVAL OVAL update. Related to the systems assessments. Test Suite Update in Test Suite. Update Rule Issues or pull requests related to Rules updates.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants