<def-group>
  <definition class="compliance" id="no_shelllogin_for_systemaccounts" version="2">
    {{{ oval_metadata("The root account is the only system account that should have
      a login shell.") }}}
    <criteria operator="OR">
      <!-- If SYS_UID_MIN and SYS_UID_MAX is not defined in /etc/login.defs
           perform the check that any /etc/passwd entry with shell defined
           has UID outside the default <0, UID_MIN -1> range.
           If at least one UID with shell defined exists within that range,
           the requirement isn't met -->
      <criteria operator="AND">
        <criterion comment="Test SYS_UID_MIN not defined in /etc/login.defs"
        test_ref="test_sys_uid_min_not_defined" />
        <criterion comment="Test SYS_UID_MAX not defined in /etc/login.defs"
        test_ref="test_sys_uid_max_not_defined" />
        <criterion comment="Test shell defined for UID from &lt;0, UID_MIN -1&gt;"
        test_ref="test_shell_defined_default_uid_range" />
      </criteria>
      <!-- If both SYS_UID_MIN and SYS_UID_MAX are defined in /etc/login.defs
           perform both checks:
           * That any /etc/passwd entry with shell defined has UID outside
           the range for reserved system accounts <0, SYS_UID_MIN> range,
           * That any /etc/passwd entries with shell defined has UID outside
           the range for dynamically allocated system accounts <SYS_UID_MIN, SYS_UID_MAX>
           If at least one UID with shell defined exists within some of the two
           ranges, the requirement isn't met -->
      <criteria operator="AND">
        <criterion comment="Test SYS_UID_MIN defined in /etc/login.defs"
        test_ref="test_sys_uid_min_not_defined" negate="true" />
        <criterion comment="Test SYS_UID_MAX defined in /etc/login.defs"
        test_ref="test_sys_uid_max_not_defined" negate="true" />
        <criterion comment="Test shell defined for reserved system UIDs"
        test_ref="test_shell_defined_reserved_uid_range" />
        <criterion comment="Test shell defined for dynamically allocated system UIDs"
        test_ref="test_shell_defined_dynalloc_uid_range" />
      </criteria>
    </criteria>
  </definition>

  <!-- First define OVAL entities used by both criteria above -->

  <!-- Get last occurrence of UID_MIN from /etc/login.defs as OVAL object -->
  <ind:textfilecontent54_object id="object_last_uid_min_from_etc_login_defs"
  version="1">
    <ind:behaviors singleline="true" />
    <ind:filepath>/etc/login.defs</ind:filepath>
    <!-- Last (uncommented) instance od UID_MIN directive -->
    <ind:pattern operation="pattern match">.*(?:^|\n)\s*(UID_MIN[\s]+[\d]+)\s*(?:$|\n)</ind:pattern>
    <ind:instance datatype="int" operation="greater than or equal">1</ind:instance>
  </ind:textfilecontent54_object>

  <!-- Get last occurrence of SYS_UID_MIN from /etc/login.defs as OVAL object -->
  <ind:textfilecontent54_object id="object_last_sys_uid_min_from_etc_login_defs"
  version="1">
    <ind:behaviors singleline="true" />
    <ind:filepath>/etc/login.defs</ind:filepath>
    <!-- Last (uncommented) instance of SYS_UID_MIN directive -->
    <ind:pattern operation="pattern match">.*(?:^|\n)\s*(SYS_UID_MIN[\s]+[\d]+)\s*(?:$|\n)</ind:pattern>
    <ind:instance datatype="int" operation="greater than or equal">1</ind:instance>
  </ind:textfilecontent54_object>

  <!-- Get last occurrence of SYS_UID_MAX from /etc/login.defs as OVAL object -->
  <ind:textfilecontent54_object id="object_last_sys_uid_max_from_etc_login_defs" version="1">
    <ind:behaviors singleline="true" />
    <ind:filepath>/etc/login.defs</ind:filepath>
    <!-- Last (uncommented) instance of SYS_UID_MAX directive -->
    <ind:pattern operation="pattern match">.*(?:^|\n)\s*(SYS_UID_MAX[\s]+[\d]+)\s*(?:$|\n)</ind:pattern>
    <ind:instance datatype="int" operation="greater than or equal">1</ind:instance>
  </ind:textfilecontent54_object>

  <!-- Get all /etc/passwd entries having shell defined as OVAL object -->
  <ind:textfilecontent54_object id="object_etc_passwd_entries" version="1">
    <ind:filepath>/etc/passwd</ind:filepath>
{{% if "ubuntu" in product or "sle" in product or "slmicro" in product %}}
    <ind:pattern operation="pattern match">^(?!root).*:x:([\d]+):[\d]+:[^:]*:[^:]*:(?!\/usr\/sbin\/nologin|\/sbin\/nologin|\/bin\/sync|\/sbin\/shutdown|\/sbin\/halt|\/bin\/false|\/usr\/bin\/false).*$</ind:pattern>
{{% else %}}
    <ind:pattern operation="pattern match">^(?!root).*:x:([\d]+):[\d]+:[^:]*:[^:]*:(?!\/usr\/sbin\/nologin|\/sbin\/nologin|\/bin\/sync|\/sbin\/shutdown|\/sbin\/halt).*$</ind:pattern>
{{% endif %}}
    <ind:instance datatype="int" operation="greater than or equal">1</ind:instance>
  </ind:textfilecontent54_object>

  <!-- FIRST CRITERION -->
  <!-- If both SYS_UID_MIN and SYS_UID_MAX aren't defined in /etc/login.defs
       perform the check that any /etc/passwd entry with shell defined has
       UID outside the <0, UID_MIN - 1> range -->

  <!-- Actual /etc/login.defs UID_MIN value as OVAL variable -->
  <local_variable id="variable_uid_min_value" datatype="int"
  comment="Value of last UID_MIN from /etc/login.defs" version="1">
    <regex_capture pattern="UID_MIN[\s]+(\d+)">
      <object_component item_field="subexpression"
      object_ref="object_last_uid_min_from_etc_login_defs" />
    </regex_capture>
  </local_variable>

  <!-- Perform the default <0, UID_MIN - 1> UID range test itself -->
  <!-- Thus check that any /etc/passwd entry with shell defined
       has UID outside of <0, UID_MIN -1> range -->
  <ind:textfilecontent54_test state_operator="OR" id="test_shell_defined_default_uid_range" check="all"
  check_existence="any_exist" comment="&lt;0, UID_MIN - 1&gt; system UIDs having shell set"
  version="1">
    <ind:object object_ref="object_etc_passwd_entries" />
    <ind:state state_ref="state_uid_less_than_zero" />
    <ind:state state_ref="state_uid_greater_than_or_equal_uid_min" />
  </ind:textfilecontent54_test>

  <ind:textfilecontent54_state id="state_uid_less_than_zero" version="1">
    <ind:subexpression datatype="int" operation="less than">0</ind:subexpression>
  </ind:textfilecontent54_state>
  <ind:textfilecontent54_state id="state_uid_greater_than_or_equal_uid_min" version="1">
    <ind:subexpression datatype="int" operation="greater than or equal" var_ref="variable_uid_min_value" />
  </ind:textfilecontent54_state>

  <!-- Test if SYS_UID_MIN not defined in /etc/login.defs -->
  <ind:textfilecontent54_test id="test_sys_uid_min_not_defined"
  comment="SYS_UID_MIN not defined in /etc/login.defs" check="all"
  check_existence="none_exist" version="1">
    <ind:object object_ref="object_last_sys_uid_min_from_etc_login_defs" />
  </ind:textfilecontent54_test>

  <!-- Test if SYS_UID_MAX not defined in /etc/login.defs -->
  <ind:textfilecontent54_test id="test_sys_uid_max_not_defined"
  comment="SYS_UID_MAX not defined in /etc/login.defs" check="all"
  check_existence="none_exist" version="1">
    <ind:object object_ref="object_last_sys_uid_max_from_etc_login_defs" />
  </ind:textfilecontent54_test>

  <!-- SECOND CRITERION -->
  <!-- If both SYS_UID_MIN and SYS_UID_MAX are defined in /etc/login.defs,
       perform more advanced test:
       * Check that any /etc/passwd entry with shell defined has UID
       outside the range of reserved system UIDs, thus <0, SYS_UID_MIN>,
       * Also check that any /etc/passwd entry with shell defined has
       UID outside the range of dynamically allocated system UIDs, thus
       <SYS_UID_MIN, SYS_UID_MAX>
  -->

  <!-- Actual /etc/login.defs SYS_UID_MIN value as OVAL variable -->
  <local_variable id="variable_sys_uid_min_value" datatype="int"
  comment="Value of last SYS_UID_MIN from /etc/login.defs" version="1">
    <regex_capture pattern="SYS_UID_MIN[\s]+(\d+)">
      <object_component item_field="subexpression"
      object_ref="object_last_sys_uid_min_from_etc_login_defs" />
    </regex_capture>
  </local_variable>

  <!-- Actual /etc/login.defs SYS_UID_MAX value as OVAL variable -->
  <local_variable id="variable_sys_uid_max_value" datatype="int"
  comment="Value of last SYS_UID_MAX from /etc/login.defs" version="1">
    <regex_capture pattern="SYS_UID_MAX[\s]+(\d+)">
      <object_component item_field="subexpression"
      object_ref="object_last_sys_uid_max_from_etc_login_defs" />
    </regex_capture>
  </local_variable>

  <!-- Perform the reserved UID range <0, SYS_UID_MIN> test itself -->
  <!-- Thus check that any /etc/passwd entry with shell defined
       has UID outside of <0, SYS_UID_MIN> range -->
  <ind:textfilecontent54_test state_operator="OR" id="test_shell_defined_reserved_uid_range" check="all"
  check_existence="any_exist" comment="&lt;0, SYS_UID_MIN&gt; system UIDs having shell set"
  version="1">
    <ind:object object_ref="object_etc_passwd_entries" />
    <ind:state state_ref="state_uid_less_than_zero" />
    <ind:state state_ref="state_uid_greater_than_or_equal_sys_uid_min" />
  </ind:textfilecontent54_test>

  <ind:textfilecontent54_state id="state_uid_greater_than_or_equal_sys_uid_min" version="1">
    <ind:subexpression datatype="int" operation="greater than or equal" var_ref="variable_sys_uid_min_value" />
  </ind:textfilecontent54_state>

  <!-- Perform the dynamically allocated UID range <SYS_UID_MIN, SYS_UID_MAX> test itself -->
  <!-- Thus check that any /etc/passwd entry with shell defined
       has UID outside of <SYS_UID_MIN, SYS_UID_MAX> range -->
  <ind:textfilecontent54_test state_operator="OR" id="test_shell_defined_dynalloc_uid_range" check="all"
  check_existence="any_exist" comment="&lt;SYS_UID_MIN, SYS_UID_MAX&gt; system UIDS having shell set"
  version="1">
    <ind:object object_ref="object_etc_passwd_entries" />
    <ind:state state_ref="state_uid_less_than_sys_uid_min" />
    <ind:state state_ref="state_uid_greater_than_or_equal_sys_uid_max" />
  </ind:textfilecontent54_test>

  <ind:textfilecontent54_state id="state_uid_less_than_sys_uid_min" version="1">
    <ind:subexpression datatype="int" operation="less than" var_ref="variable_sys_uid_min_value" />
  </ind:textfilecontent54_state>
  <ind:textfilecontent54_state id="state_uid_greater_than_or_equal_sys_uid_max" version="1">
    <ind:subexpression datatype="int" operation="greater than or equal" var_ref="variable_sys_uid_max_value" />
  </ind:textfilecontent54_state>
</def-group>
