diff --git a/components/pam.yml b/components/pam.yml
index 06c49ae3da7..ca43e987e4e 100644
--- a/components/pam.yml
+++ b/components/pam.yml
@@ -51,6 +51,7 @@ rules:
- accounts_password_pam_lcredit
- accounts_password_pam_maxclassrepeat
- accounts_password_pam_maxrepeat
+- accounts_password_pam_maxsequence
- accounts_password_pam_minclass
- accounts_password_pam_minlen
- accounts_password_pam_ocredit
diff --git a/controls/cis_ubuntu2404.yml b/controls/cis_ubuntu2404.yml
index 579024867bd..4e6b15eeaae 100644
--- a/controls/cis_ubuntu2404.yml
+++ b/controls/cis_ubuntu2404.yml
@@ -1964,8 +1964,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_password_pam_maxsequence
+ status: automated
- id: 5.3.3.2.6
title: Ensure password dictionary check is enabled (Automated)
diff --git a/linux_os/guide/system/accounts/accounts-pam/password_quality/password_quality_pwquality/accounts_password_pam_maxsequence/rule.yml b/linux_os/guide/system/accounts/accounts-pam/password_quality/password_quality_pwquality/accounts_password_pam_maxsequence/rule.yml
new file mode 100644
index 00000000000..6d57fc1b731
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-pam/password_quality/password_quality_pwquality/accounts_password_pam_maxsequence/rule.yml
@@ -0,0 +1,32 @@
+documentation_complete: true
+
+
+title: 'Limit the maximum number of sequential characters in passwords'
+
+description: |-
+ The pwquality maxsequence setting defines the maximum allowable length for consecutive
+ character sequences in a new password. Such sequences can be, e.g., 123 or abc. If the value is
+ set to 0, this check will be turned off.
+
+ Note: Passwords that consist mainly of such sequences are unlikely to meet the simplicity criteria
+ unless the sequence constitutes only a small portion of the overall password.
+
+rationale: |-
+ Use of a strong password helps to increase the time and resources required to
+ compromise the password. Password complexity, or strength, is a measure of the
+ effectiveness of a password in resisting attempts at guessing and brute-force attacks.
+
+ Password complexity is one important factor that determines the duration required to crack it.
+ A more intricate password results in a larger number of potential combinations that must be
+ tested before successfully compromising the password.
+
+severity: medium
+
+platform: package[pam]
+
+template:
+ name: accounts_password
+ vars:
+ variable: maxsequence
+ operation: less than or equal
+ zero_comparison_operation: greater than
diff --git a/linux_os/guide/system/accounts/accounts-pam/password_quality/password_quality_pwquality/var_password_pam_maxsequence.var b/linux_os/guide/system/accounts/accounts-pam/password_quality/password_quality_pwquality/var_password_pam_maxsequence.var
new file mode 100644
index 00000000000..70238a00705
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-pam/password_quality/password_quality_pwquality/var_password_pam_maxsequence.var
@@ -0,0 +1,17 @@
+documentation_complete: true
+
+title: maxsequence
+
+description: 'Maximum Number of Consecutive Character Sequences in a Password'
+
+type: number
+
+operator: equals
+
+interactive: false
+
+options:
+ 1: 1
+ 2: 2
+ 3: 3
+ default: 3
diff --git a/shared/macros/10-ansible.jinja b/shared/macros/10-ansible.jinja
index d8726a40bba..7baec3a04a5 100644
--- a/shared/macros/10-ansible.jinja
+++ b/shared/macros/10-ansible.jinja
@@ -847,6 +847,23 @@ The following macro remediates Audit syscall rule in :code:`/etc/audit/audit.rul
{{%- endif %}}
{{%- endmacro %}}
+{{#
+ Macro used to apply changes on pam-auth-update profiles. If the "profile_name" parameter is not defined, the
+ pam-auth-update will apply all profile changes by default.
+
+:param profile_name: Changes the default profile used by pam-auth-update.
+:type profile_name: str
+
+#}}
+{{% macro ansible_apply_pam_auth_update_changes(profile_name='') -%}}
+- name: '{{{ rule_title }}} - Ensure pam-auth-update profile changes are applied'
+ ansible.builtin.command:
+ {{%- if profile_name == '' %}}
+ cmd: pam-auth-update
+ {{%- else %}}
+ cmd: pam-auth-update --enable {{{ profile_name }}}
+ {{%- endif %}}
+{{%- endmacro %}}
{{#
Disable authselect feature if the authselect current profile is intact or inform that its
@@ -922,6 +939,17 @@ The following macro remediates Audit syscall rule in :code:`/etc/audit/audit.rul
register: result_authselect_present
{{%- endmacro -%}}
+{{#
+ Used to identify if pam-auth-update is present or not in the system.
+ Some macros can change the remediation behavior based on the presence of authselect.
+
+#}}
+{{%- macro ansible_check_pam_auth_update_presence() -%}}
+- name: '{{{ rule_title }}} - Check if system relies on pam-auth-update tool'
+ ansible.builtin.stat:
+ path: /usr/sbin/pam-auth-update
+ register: result_pam_auth_update_present
+{{%- endmacro -%}}
{{#
Ensure pam_lastlog.so PAM module shows the failed logins according to the system capabilities.
@@ -1252,6 +1280,46 @@ The following macro remediates Audit syscall rule in :code:`/etc/audit/audit.rul
- not result_faillock_conf_check.stat.exists
{{%- endmacro -%}}
+{{#
+ This macro ensures the pam_pwquality.so PAM module is enabled.
+ It is enabled using the pam-auth-update tool.
+
+:param path: The path of pam-auth-update configuration for pam_pwquality.so.
+:type parameter: str
+
+#}}
+{{%- macro ansible_pam_pwquality_enable(path) -%}}
+
+{{{ ansible_check_pam_auth_update_presence() }}}
+
+- name: {{{ rule_title }}} - Remediation where pam-auth-update tool is present
+ block:
+ - name: Check if {{{ path }}} exists
+ stat:
+ path: {{{ path }}}
+ register: pwquality_file_stat
+
+ - name: Put the content into {{{ path }}} if it does not exist
+ copy:
+ dest: {{{ path }}}
+ content: |+
+ Name: Pwquality password strength checking
+ Default: yes
+ Priority: 1024
+ Conflicts: cracklib
+ Password-Type: Primary
+ Password:
+ requisite pam_pwquality.so retry=3
+ force: yes
+ when: not pwquality_file_stat.stat.exists
+
+ {{{ ansible_apply_pam_auth_update_changes('cac_pwquality') | indent(4) }}}
+
+ when:
+ - result_pam_auth_update_present.stat.exists
+
+{{%- endmacro -%}}
+
{{#
Macro for Ansible remediation for adding a kernel command line argument to the GRUB 2 bootloader.
diff --git a/shared/templates/accounts_password/ansible.template b/shared/templates/accounts_password/ansible.template
index f25e7cc5428..858f462f33a 100644
--- a/shared/templates/accounts_password/ansible.template
+++ b/shared/templates/accounts_password/ansible.template
@@ -29,6 +29,10 @@
}}}
{{% endif %}}
+{{% if "ubuntu" in product %}}
+{{{ ansible_pam_pwquality_enable('/usr/share/pam-configs/cac_pwquality') }}}
+{{% endif %}}
+
- name: {{{ rule_title }}} - Ensure PAM variable {{{ VARIABLE }}} is set accordingly
ansible.builtin.lineinfile:
create: yes
diff --git a/shared/templates/accounts_password/bash.template b/shared/templates/accounts_password/bash.template
index 372db9e6140..9bc43015f8e 100644
--- a/shared/templates/accounts_password/bash.template
+++ b/shared/templates/accounts_password/bash.template
@@ -21,4 +21,8 @@ fi
}}}
{{% endif %}}
+{{% if product == "ubuntu2404" %}}
+{{{ bash_pam_pwquality_enable() }}}
+{{% endif %}}
+
{{{ bash_replace_or_append('/etc/security/pwquality.conf', '^' ~ VARIABLE , '$var_password_pam_' ~ VARIABLE , '%s = %s') }}}
diff --git a/shared/templates/accounts_password/tests/correct_value.pass.sh b/shared/templates/accounts_password/tests/correct_value.pass.sh
index fb462aa575d..286116a54d8 100644
--- a/shared/templates/accounts_password/tests/correct_value.pass.sh
+++ b/shared/templates/accounts_password/tests/correct_value.pass.sh
@@ -1,6 +1,10 @@
#!/bin/bash
# variables = var_password_pam_{{{ VARIABLE }}}={{{ TEST_VAR_VALUE }}}
+{{% if product == "ubuntu2404" %}}
+{{{ bash_pam_pwquality_enable() }}}
+{{% endif %}}
+
truncate -s 0 /etc/security/pwquality.conf
echo "{{{ VARIABLE }}} = {{{ TEST_CORRECT_VALUE }}}" >> /etc/security/pwquality.conf
diff --git a/shared/templates/accounts_password/tests/correct_value_directory.pass.sh b/shared/templates/accounts_password/tests/correct_value_directory.pass.sh
index 1d12e365537..60b92709f2a 100644
--- a/shared/templates/accounts_password/tests/correct_value_directory.pass.sh
+++ b/shared/templates/accounts_password/tests/correct_value_directory.pass.sh
@@ -6,6 +6,10 @@
# This test will ensure that OVAL also checks the configuration in
# /etc/security/pwquality.conf.d/*.conf files
+{{% if product == "ubuntu2404" %}}
+{{{ bash_pam_pwquality_enable() }}}
+{{% endif %}}
+
truncate -s 0 /etc/security/pwquality.conf
config_dir="/etc/security/pwquality.conf.d"
diff --git a/shared/templates/accounts_password/tests/duplicated_values.pass.sh b/shared/templates/accounts_password/tests/duplicated_values.pass.sh
index e7b7f957d3d..9c7d2cd8d38 100644
--- a/shared/templates/accounts_password/tests/duplicated_values.pass.sh
+++ b/shared/templates/accounts_password/tests/duplicated_values.pass.sh
@@ -1,6 +1,10 @@
#!/bin/bash
# variables = var_password_pam_{{{ VARIABLE }}}={{{ TEST_VAR_VALUE }}}
+{{% if product == "ubuntu2404" %}}
+{{{ bash_pam_pwquality_enable() }}}
+{{% endif %}}
+
truncate -s 0 /etc/security/pwquality.conf
echo "{{{ VARIABLE }}} = {{{ TEST_CORRECT_VALUE }}}" >> /etc/security/pwquality.conf
diff --git a/shared/templates/accounts_password/tests/multiple_correct_value.pass.sh b/shared/templates/accounts_password/tests/multiple_correct_value.pass.sh
index ee5f88b4886..def17658cf5 100644
--- a/shared/templates/accounts_password/tests/multiple_correct_value.pass.sh
+++ b/shared/templates/accounts_password/tests/multiple_correct_value.pass.sh
@@ -3,6 +3,10 @@
# platform = Oracle Linux 8
# variables = var_password_pam_{{{ VARIABLE }}}={{{ TEST_VAR_VALUE }}}
+{{% if product == "ubuntu2404" %}}
+{{{ bash_pam_pwquality_enable() }}}
+{{% endif %}}
+
truncate -s 0 /etc/security/pwquality.conf
echo "{{{ VARIABLE }}} = {{{ TEST_CORRECT_VALUE }}}" >> /etc/security/pwquality.conf