Skip to content

Commit

Permalink
Merge pull request #12779 from alanmcanonical/ubt24_5322
Browse files Browse the repository at this point in the history
Ubuntu 24.04: Implement rule 5.3.2.2 Ensure pam_faillock module is enabled
  • Loading branch information
dodys authored Jan 10, 2025
2 parents 42e1e03 + d145e2a commit 6f3c152
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 2 deletions.
1 change: 1 addition & 0 deletions components/pam.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ rules:
- accounts_passwords_pam_faillock_interval
- accounts_passwords_pam_faillock_silent
- accounts_passwords_pam_faillock_unlock_time
- accounts_passwords_pam_faillock_enabled
- accounts_passwords_pam_tally2
- accounts_passwords_pam_tally2_deny_root
- accounts_passwords_pam_tally2_unlock_time
Expand Down
5 changes: 3 additions & 2 deletions controls/cis_ubuntu2404.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1863,8 +1863,9 @@ controls:
levels:
- l1_server
- l1_workstation
status: planned
notes: TODO. Rule does not seem to be implemented, nor does it map to any rules in ubuntu2204 profile.
rules:
- accounts_passwords_pam_faillock_enabled
status: automated

- id: 5.3.2.3
title: Ensure pam_pwquality module is enabled (Automated)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# platform = multi_platform_ubuntu

{{{ bash_pam_faillock_enable() }}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<def-group>
<definition class="compliance" id="{{{ rule_id }}}" version="6">
{{{ oval_metadata(description) }}}
<criteria operator="AND" comment="Check the proper configuration of pam_faillock.so">
<!-- pam_unix.so is a control module present in all realistic scenarios and also used
as reference for the correct position of pam_faillock.so in auth section. If the
system is properly configured, it must appear only once in auth section. -->
<criterion test_ref="test_accounts_passwords_pam_faillock_common_pam_unix_auth"
comment="pam_unix.so appears only once in auth section of common-auth"/>
<criterion test_ref="test_accounts_passwords_pam_faillock_common_pam_faillock_auth"
comment="pam_faillock.so is properly defined in auth section of common-auth"/>
<criterion test_ref="test_accounts_passwords_pam_faillock_common_pam_faillock_account"
comment="pam_faillock.so is properly defined in common-account"/>
</criteria>
</definition>

<!-- The following tests demand complex regex which are necessary more than once.
These variables make simpler the usage of regex patterns. -->
<constant_variable id="var_accounts_passwords_pam_faillock_pam_unix_regex"
datatype="string" version="2"
comment="regex to identify pam_unix.so in auth section of pam files">
<value>^\s*auth\N+pam_unix\.so</value>
</constant_variable>

<constant_variable
id="var_accounts_passwords_pam_faillock_pam_faillock_auth_regex"
datatype="string" version="2"
comment="regex to identify pam_faillock.so entries in auth section of pam files">
{{% if 'debian' in product %}}
<value>^\s*auth\s+required\s+pam_faillock\.so.*preauth.*[\s\S]*^\s*auth.*pam_unix\.so[\s\S]*^\s*auth\s+\[default=die\]\s+pam_faillock\.so\s+authfail[\s\S]*^\s*auth\s+sufficient\s+pam_faillock\.so\s+authsucc</value>
{{% elif 'ubuntu' in product %}}
<value>^\s*auth\s+(requisite|required)\s+pam_faillock\.so.*preauth.*[\s\S]*^\s*auth.*pam_unix\.so[\s\S]*^\s*auth\s+\[default=die\]\s+pam_faillock\.so\s+authfail</value>
{{% elif 'openeuler' in product or 'kylinserver' in product %}}
<value>^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)?(?=.*?\bnew_authtok_reqd=ok\b)?(?=.*?\bignore=ignore\b)?(?=.*?\bdefault=bad\b)?.*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)?(?=.*?\bnew_authtok_reqd=done\b)?(?=.*?\bdefault=ignore\b)?.*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)?(?=.*?\bnew_authtok_reqd=ok\b)?(?=.*?\bignore=ignore\b)?(?=.*?\bdefault=die\b)?.*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail</value>
{{% else %}}
<value>^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+preauth[\s\S]*^[\s]*auth[\s]+(sufficient|\[(?=.*\bsuccess=done\b)(?=.*?\bnew_authtok_reqd=done\b)(?=.*?\bdefault=ignore\b).*\])[\s]+pam_unix\.so[\s\S]*^[\s]*auth[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\w\d=]+authfail</value>
{{% endif %}}
</constant_variable>

<constant_variable
id="var_accounts_passwords_pam_faillock_pam_faillock_account_regex"
datatype="string" version="2"
comment="regex to identify pam_faillock.so entry in account section of pam files">
{{% if 'debian' in product or 'ubuntu' in product %}}
<value>^\s*account\s+required\s+pam_faillock\.so\s*(#.*)?$</value>
{{% elif 'openeuler' in product or 'kylinserver' in product %}}
<value>^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)?(?=.*?\bnew_authtok_reqd=ok\b)?(?=.*?\bignore=ignore\b)?(?=.*?\bdefault=bad\b)?.*\])[\s]+pam_unix\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)?(?=.*?\bnew_authtok_reqd=ok\b)?(?=.*?\bignore=ignore\b)?(?=.*?\bdefault=bad\b)?.*\])[\s]+pam_faillock\.so</value>
{{% else %}}
<value>^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_faillock\.so[\s\S]*^[\s]*account[\s]+(required|\[(?=.*?\bsuccess=ok\b)(?=.*?\bnew_authtok_reqd=ok\b)(?=.*?\bignore=ignore\b)(?=.*?\bdefault=bad\b).*\])[\s]+pam_unix\.so</value>
{{% endif %}}
</constant_variable>

{{% macro generate_test_faillock_enabled(file_stem) %}}
<!-- Check occurences of pam_unix.so in auth section of {{{ file_stem }}}-auth file -->
<ind:textfilecontent54_test
check="all" check_existence="none_exist" version="2"
id="test_accounts_passwords_pam_faillock_{{{ file_stem }}}_pam_unix_auth"
comment="no more that one pam_unix.so is expected in auth section of {{{ file_stem }}}-auth">
<ind:object object_ref="object_accounts_passwords_pam_faillock_{{{ file_stem }}}_pam_unix_auth"/>
</ind:textfilecontent54_test>

<ind:textfilecontent54_object
version="2"
id="object_accounts_passwords_pam_faillock_{{{ file_stem }}}_pam_unix_auth"
comment="Get the second and subsequent occurrences of pam_unix.so in auth section of {{{ file_stem}}}-auth">
<ind:filepath>/etc/pam.d/{{{file_stem}}}-auth</ind:filepath>
<ind:pattern operation="pattern match" var_ref="var_accounts_passwords_pam_faillock_pam_unix_regex"/>
<ind:instance datatype="int" operation="greater than">1</ind:instance>
</ind:textfilecontent54_object>

<!-- Check common definition of pam_faillock.so in {{{ file_stem }}}-auth file -->
<ind:textfilecontent54_test
check="all" check_existence="only_one_exists" version="2"
id="test_accounts_passwords_pam_faillock_{{{ file_stem }}}_pam_faillock_auth"
comment="One and only one occurrence is expected in auth section of {{{ file_stem }}}-auth">
<ind:object
object_ref="object_accounts_passwords_pam_faillock_{{{ file_stem }}}_pam_faillock_auth"/>
</ind:textfilecontent54_test>

<ind:textfilecontent54_object
version="2"
id="object_accounts_passwords_pam_faillock_{{{ file_stem }}}_pam_faillock_auth"
comment="Check common definition of pam_faillock.so in auth section of common-auth">
<ind:filepath>/etc/pam.d/{{{ file_stem }}}-auth</ind:filepath>
<ind:pattern operation="pattern match"
var_ref="var_accounts_passwords_pam_faillock_pam_faillock_auth_regex"/>
<ind:instance datatype="int" operation="equals">1</ind:instance>
</ind:textfilecontent54_object>
{{% endmacro %}}

{{{ generate_test_faillock_enabled (file_stem="common") }}}

{{% macro generate_test_faillock_account(file_stem, file) %}}
<!-- Check common definition of pam_faillock.so in {{{ file_stem }}}-account -->
<ind:textfilecontent54_test
check="all" check_existence="only_one_exists" version="2"
id="test_accounts_passwords_pam_faillock_{{{ file_stem }}}_pam_faillock_account"
comment="One and only one occurrence is expected in {{{ file }}}">
<ind:object
object_ref="object_accounts_passwords_pam_faillock_{{{ file_stem }}}_pam_faillock_account"/>
</ind:textfilecontent54_test>

<ind:textfilecontent54_object
version="2"
id="object_accounts_passwords_pam_faillock_{{{ file_stem }}}_pam_faillock_account"
comment="Check common definition of pam_faillock.so in account section of {{{ file }}}">
<ind:filepath>/etc/pam.d/{{{ file }}}</ind:filepath>
<ind:pattern operation="pattern match"
var_ref="var_accounts_passwords_pam_faillock_pam_faillock_account_regex"/>
<ind:instance datatype="int" operation="equals">1</ind:instance>
</ind:textfilecontent54_object>
{{% endmacro %}}

{{{ generate_test_faillock_account (file_stem="common", file="common-account") }}}

</def-group>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
documentation_complete: true


title: 'Ensure pam_faillock module is enabled'

description: |-
The <tt>pam_faillock.so</tt> module maintains a list of failed authentication attempts per
user during a specified interval and locks the account in case there were more than the
configured number of consecutive failed authentications (this is defined by the <tt>deny</tt>
parameter in the faillock configuration). It stores the failure records into per-user files in
the tally directory.
rationale: |-
Locking out user IDs after n unsuccessful consecutive login attempts mitigates brute
force password attacks against your systems.
severity: medium

platform: package[pam]
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash
# platform = multi_platform_ubuntu
# packages = pam

echo 'auth requisite pam_faillock.so preauth' >> /etc/pam.d/common-auth
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash
# platform = multi_platform_ubuntu
# packages = pam

{{{ bash_enable_pam_faillock_directly_in_pam_files() }}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash
# platform = multi_platform_ubuntu

{{{ bash_enable_pam_faillock_directly_in_pam_files() }}}

echo > /etc/security/faillock.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash
# platform = multi_platform_ubuntu
# packages = pam

sed '/pam_faillock.so/d' /etc/pam.d/common-auth
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/bash
# platform = multi_platform_ubuntu
# packages = pam

# Multiple instances of pam_unix.so in auth section may, intentionally or not, interfere
# in the expected behaviour of pam_faillock.so. Remediation does not solve this automatically
# in order to preserve intentional changes.
cat << EOF > /usr/share/pam-configs/tmp_unix
Name: Unix authentication
Default: yes
Priority: 257
Auth-Type: Primary
Auth:
[success=end default=ignore] pam_unix.so nullok try_first_pass
Auth-Initial:
[success=end default=ignore] pam_unix.so nullok
Account-Type: Primary
Account:
[success=end new_authtok_reqd=done default=ignore] pam_unix.so
Account-Initial:
[success=end new_authtok_reqd=done default=ignore] pam_unix.so
Session-Type: Additional
Session:
required pam_unix.so
Session-Initial:
required pam_unix.so
Password-Type: Primary
Password:
[success=end default=ignore] pam_unix.so obscure use_authtok try_first_pass yescrypt
Password-Initial:
[success=end default=ignore] pam_unix.so obscure yescrypt
auth sufficient pam_unix.so
EOF
DEBIAN_FRONTEND=noninteractive pam-auth-update

rm -f /usr/share/pam-configs/tmp_unix

0 comments on commit 6f3c152

Please sign in to comment.