{{% if 'sle' in product or 'slmicro' in product %}}
  {{% set pam_file = "/etc/pam.d/common-password" %}}
  {{% set line_pattern = "^[\s]*password[\s]+(?:(?:required))[\s]+pam_unix\.so[\s]+" %}}
{{% elif 'ubuntu' in product or 'debian' in product %}}
  {{% set pam_file = "/etc/pam.d/common-password" %}}
  {{% set line_pattern = "^[\s]*password[\s]+(?:\[success=\d+\s+default=ignore\])[\s]+pam_unix\.so[\s]+" %}}
{{% else %}}
  {{% set pam_file = "/etc/pam.d/system-auth" %}}
  {{% set line_pattern = "^[\s]*password[\s]+(?:(?:required)|(?:sufficient))[\s]+pam_unix\.so[\s]+" %}}
{{% endif %}}

<def-group>
  <definition class="compliance" id="{{{ rule_id }}}" version="2">
    {{{ oval_metadata("The password hashing algorithm should be set correctly in {{{ pam_file }}}.", rule_title=rule_title) }}}
    <criteria operator="AND">
      <criterion test_ref="test_pam_unix_hashing_algorithm_systemauth" />
    </criteria>
  </definition>

  {{% set pam_unix_algorithms = "\\b(sha512|yescrypt|gost_yescrypt|blowfish|sha256|md5|bigcrypt)\\b" %}}
  {{% set hashing_pattern = line_pattern + "(?!.*" + pam_unix_algorithms + "[^#]*" + pam_unix_algorithms + ")[^#]*" + pam_unix_algorithms + ".*$" %}}

  <!--
    In addition to the pam file, what usually differ between products are the controls in the
    pam_unix.so line. Therefore, only these differences were kept in Jinja2 conditionals, as
    variables. The hashing_pattern used in OVAL object below is a concatenation of the line
    pattern for pam_unix.so respective to each product and the regex to identify all hashing
    algorithms options defined in pam_unix.so line. Here is an example of the rendered regex using
    the line_pattern from the "else" section:
    "^[\s]*password[\s]+(?:(?:required)|(?:sufficient))[\s]+pam_unix\.so[\s]+(?!.*(sha512|yescrypt|gost_yescrypt|blowfish|sha256|md5|bigcrypt).*(sha512|yescrypt|gost_yescrypt|blowfish|sha256|md5|bigcrypt)).*(sha512|yescrypt|gost_yescrypt|blowfish|sha256|md5|bigcrypt).*$"

    This regex will only match if any of the hashing algorithm options is defined only once.
    If two options for hashing algorithms are defined, the system behavior may be unexpected.
    Therefore, the regex won't match and the rule check will fail. If only one option is defined,
    it will be comprared with the value defined by the variable.
  -->

  <ind:textfilecontent54_test id="test_pam_unix_hashing_algorithm_systemauth" version="2"
    check="all" check_existence="at_least_one_exists"
    comment="check if pam_unix.so hashing algorithm option is correct and specified only once in {{{ pam_file }}}">
    <ind:object object_ref="object_pam_unix_hashing_algorithm_systemauth"/>
    <ind:state state_ref="state_pam_unix_hashing_algorithm_systemauth"/>
  </ind:textfilecontent54_test>

  <ind:textfilecontent54_object id="object_pam_unix_hashing_algorithm_systemauth" version="1"
    comment="only one hashing algorithm option for pam_unix.so is found in {{{ pam_file }}}">
    <ind:filepath>{{{ pam_file }}}</ind:filepath>
    <ind:pattern operation="pattern match">{{{ hashing_pattern }}}</ind:pattern>
    <ind:instance datatype="int">1</ind:instance>
  </ind:textfilecontent54_object>

  <ind:textfilecontent54_state id="state_pam_unix_hashing_algorithm_systemauth" version="2">
    <ind:subexpression operation="pattern match" datatype="string" var_ref="var_password_hashing_algorithm_pam_regex"/>
  </ind:textfilecontent54_state>

  <local_variable datatype="string" id="var_password_hashing_algorithm_pam_regex" version="1" comment="Limit regex">
    <concat>
      <literal_component>^</literal_component>
      <variable_component var_ref="var_password_hashing_algorithm_pam"/>
      <literal_component>$</literal_component>
    </concat>
  </local_variable>

  <external_variable id="var_password_hashing_algorithm_pam" datatype="string" version="1"
    comment="hashing algorithm for pam_unix.so"/>
</def-group>
