{{# Very similar OVAL is used in several rules, differing primarily in faillock.so parameter. #}}
{{# For transferability, we define the parameter and corresponding regular expressions in jinja. #}}
{{# The rules should ideally use a single template. #}}

{{% set prm_name = "audit" %}}
{{% set prm_regex_conf = "^[\s]*audit" %}}
{{% set prm_regex_pamd = "^[\s]*auth[\s]+.+[\s]+pam_faillock.so[\s]+[^\n]*audit" %}}
{{% set description = "Account Lockouts Must Be Logged" %}}

<def-group>
  <definition class="compliance" id="{{{ rule_id }}}" version="4">
    {{{ oval_metadata(description) }}}
    <criteria operator="AND" comment="Check the proper configuration of pam_faillock.so">
      <criteria operator="AND" comment="Check if pam_faillock.so is properly enabled">
        <!-- 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_{{{ prm_name }}}_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_{{{ prm_name }}}_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_{{{ prm_name }}}_common_pam_faillock_account"
            comment="pam_faillock.so is properly defined in common-account"/>
      </criteria>

      <!-- pam_faillock.so parameters should be defined in /etc/security/faillock.conf whenever
           possible. But due to backwards compatibility, they are also allowed in pam files
           directly. In case they are defined in both places, pam files have precedence and this
           may confuse the assessment. The following tests ensure only one option is used. -->
      <criteria operator="OR" comment="Check expected value for pam_faillock.so {{{ prm_name }}} parameter">
        <criteria operator="AND"
            comment="Check expected pam_faillock.so {{{ prm_name }}} parameter in pam files">
          <criterion test_ref="test_accounts_passwords_pam_faillock_{{{ prm_name }}}_parameter_pamd_common"
              comment="Check the {{{ prm_name }}} parameter is present common-auth file"/>
          <criterion test_ref="test_accounts_passwords_pam_faillock_{{{ prm_name }}}_parameter_no_faillock_conf"
              comment="Ensure the {{{ prm_name }}} parameter is not present in /etc/security/faillock.conf"/>
        </criteria>
        <criteria operator="AND"
            comment="Check expected pam_faillock.so {{{ prm_name }}} parameter in faillock.conf">
          <criterion test_ref="test_accounts_passwords_pam_faillock_{{{ prm_name }}}_parameter_no_pamd_common"
              comment="Check the {{{ prm_name }}} parameter is not present common-auth file"/>
          <criterion test_ref="test_accounts_passwords_pam_faillock_{{{ prm_name }}}_parameter_faillock_conf"
              comment="Ensure the {{{ prm_name }}} parameter is present in /etc/security/faillock.conf"/>
        </criteria>
      </criteria>
    </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_{{{ prm_name }}}_pam_unix_regex"
                datatype="string" version="1"
                comment="regex to identify pam_unix.so in auth section of pam files">
    <value>^\s*auth.*pam_unix\.so</value>
  </constant_variable>

  <constant_variable id="var_accounts_passwords_pam_faillock_{{{ prm_name }}}_pam_faillock_auth_regex"
                datatype="string" version="1"
                comment="regex to identify pam_faillock.so entries in auth section of pam files">
      <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>
  </constant_variable>

  <constant_variable id="var_accounts_passwords_pam_faillock_{{{ prm_name }}}_pam_faillock_account_regex"
                datatype="string" version="1"
                comment="regex to identify pam_faillock.so entry in account section of pam files">
    <value>^\s*account\s+required\s+pam_faillock\.so\s*(#.*)?$</value>
  </constant_variable>

  <constant_variable
    id="var_accounts_passwords_pam_faillock_{{{ prm_name }}}_pam_faillock_{{{ prm_name }}}_parameter_regex"
    datatype="string" version="1"
    comment="regex to identify pam_faillock.so {{{ prm_name }}} entry in auth section of pam files">
    <value>{{{ prm_regex_pamd }}}</value>
  </constant_variable>

  <constant_variable
              id="var_accounts_passwords_pam_faillock_{{{ prm_name }}}_faillock_conf_{{{ prm_name }}}_parameter_regex"
              datatype="string" version="1"
              comment="regex to identify {{{ prm_name }}} entry in /etc/security/faillock.conf">
    <value>{{{ prm_regex_conf }}}</value>
  </constant_variable>

  <!-- Check occurrences of pam_unix.so in auth section of common-auth file -->
  <ind:textfilecontent54_test check="all" check_existence="none_exist" version="1"
        id="test_accounts_passwords_pam_faillock_{{{ prm_name }}}_common_pam_unix_auth"
        comment="No more than one pam_unix.so is expected in auth section of common-auth">
    <ind:object object_ref="object_accounts_passwords_pam_faillock_{{{ prm_name }}}_common_pam_unix_auth"/>
  </ind:textfilecontent54_test>

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

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

  <ind:textfilecontent54_object version="1"
        id="object_accounts_passwords_pam_faillock_{{{ prm_name }}}_common_pam_faillock_auth"
        comment="Check common definition of pam_faillock.so in auth section of common-auth">
    <ind:filepath>/etc/pam.d/common-auth</ind:filepath>
    <ind:pattern operation="pattern match"
                 var_ref="var_accounts_passwords_pam_faillock_{{{ prm_name }}}_pam_faillock_auth_regex"/>
    <ind:instance datatype="int" operation="equals">1</ind:instance>
  </ind:textfilecontent54_object>

  <!-- Check common definition of pam_faillock.so in common-account -->
  <ind:textfilecontent54_test check="all" check_existence="only_one_exists" version="1"
        id="test_accounts_passwords_pam_faillock_{{{ prm_name }}}_common_pam_faillock_account"
        comment="One and only one occurrence is expected in common-account">
    <ind:object
        object_ref="object_accounts_passwords_pam_faillock_{{{ prm_name }}}_common_pam_faillock_account"/>
  </ind:textfilecontent54_test>

  <ind:textfilecontent54_object version="1"
        id="object_accounts_passwords_pam_faillock_{{{ prm_name }}}_common_pam_faillock_account"
        comment="Check common definition of pam_faillock.so in account section of common-account">
    <ind:filepath>/etc/pam.d/common-account</ind:filepath>
    <ind:pattern operation="pattern match"
                 var_ref="var_accounts_passwords_pam_faillock_{{{ prm_name }}}_pam_faillock_account_regex"/>
    <ind:instance datatype="int" operation="equals">1</ind:instance>
  </ind:textfilecontent54_object>


  <!-- Check absence of {{{ prm_name }}} parameter in common-auth -->
  <ind:textfilecontent54_test check="all" check_existence="none_exist" version="1"
        id="test_accounts_passwords_pam_faillock_{{{ prm_name }}}_parameter_no_pamd_common"
        comment="Check the absence of {{{ prm_name }}} parameter in common-auth">
    <ind:object object_ref="object_accounts_passwords_pam_faillock_{{{ prm_name }}}_parameter_pamd_common"/>
  </ind:textfilecontent54_test>

  <!-- Check expected value of {{{ prm_name }}} parameter in common-auth -->
  <ind:textfilecontent54_test check="all" check_existence="all_exist" version="1"
        id="test_accounts_passwords_pam_faillock_{{{ prm_name }}}_parameter_pamd_common"
        comment="Check the expected {{{ prm_name }}} value in common-auth">
    <ind:object object_ref="object_accounts_passwords_pam_faillock_{{{ prm_name }}}_parameter_pamd_common"/>
  </ind:textfilecontent54_test>

  <ind:textfilecontent54_object version="1"
        id="object_accounts_passwords_pam_faillock_{{{ prm_name }}}_parameter_pamd_common"
        comment="Get the pam_faillock.so {{{ prm_name }}} parameter from common-auth file">
    <ind:filepath>/etc/pam.d/common-auth</ind:filepath>
    <ind:pattern operation="pattern match"
                 var_ref="var_accounts_passwords_pam_faillock_{{{ prm_name }}}_pam_faillock_{{{ prm_name }}}_parameter_regex"/>
    <ind:instance datatype="int" operation="equals">1</ind:instance>
  </ind:textfilecontent54_object>


  <!-- Check absence of {{{ prm_name }}} parameter in /etc/security/faillock.conf -->
  <ind:textfilecontent54_test check="all" check_existence="none_exist" version="1"
        id="test_accounts_passwords_pam_faillock_{{{ prm_name }}}_parameter_no_faillock_conf"
        comment="Check the absence of {{{ prm_name }}} parameter in /etc/security/faillock.conf">
    <ind:object object_ref="object_accounts_passwords_pam_faillock_{{{ prm_name }}}_parameter_faillock_conf"/>
  </ind:textfilecontent54_test>

  <!-- Check expected value of {{{ prm_name }}} parameter in /etc/security/faillock.conf -->
  <ind:textfilecontent54_test check="all" check_existence="all_exist" version="1"
        id="test_accounts_passwords_pam_faillock_{{{ prm_name }}}_parameter_faillock_conf"
        comment="Check the expected {{{ prm_name }}} value in /etc/security/faillock.conf">
    <ind:object object_ref="object_accounts_passwords_pam_faillock_{{{ prm_name }}}_parameter_faillock_conf"/>
  </ind:textfilecontent54_test>

  <ind:textfilecontent54_object version="1"
      id="object_accounts_passwords_pam_faillock_{{{ prm_name }}}_parameter_faillock_conf"
      comment="Check the expected pam_faillock.so {{{ prm_name }}} parameter in /etc/security/faillock.conf">
    <ind:filepath>/etc/security/faillock.conf</ind:filepath>
    <ind:pattern operation="pattern match"
          var_ref="var_accounts_passwords_pam_faillock_{{{ prm_name }}}_faillock_conf_{{{ prm_name }}}_parameter_regex"/>
    <ind:instance datatype="int" operation="equals">1</ind:instance>
  </ind:textfilecontent54_object>
</def-group>
