<def-group oval_version="5.11">
  <definition class="compliance" id="rsyslog_files_permissions" version="1">
    {{{ oval_metadata("File permissions for all syslog log files should be set correctly.") }}}

    <criteria operator="AND">
      {{% if product in ["debian10", "debian11", "ubuntu1604", "ubuntu1804"] %}}
      <extend_definition comment="rsyslog daemon is used as local logging daemon" definition_ref="package_rsyslog_installed" />
      {{% endif %}}
      <criterion comment="Check permissions of all system log files" test_ref="test_rsyslog_files_permissions" />
    </criteria>

  </definition>

  <!-- First obtain rsyslog's $IncludeConfig directive and include() object (introduced in rsyslog v8.33.0) values.
       To workaround empty include objects case, when FunctionGroup operations return "does not exist" result, added empty string match -->
  <ind:textfilecontent54_object id="object_rfp_rsyslog_include_config_value" comment="rsyslog's $IncludeConfig directive and include() object values" version="1">
    <ind:filepath>/etc/rsyslog.conf</ind:filepath>
    <ind:pattern operation="pattern match">^(?:include\([\n\s]*file="([^\s;]+)".*|\$IncludeConfig[\s]+([^\s;]+))$</ind:pattern>
    <ind:instance datatype="int" operation="greater than or equal">1</ind:instance>
    <filter action="exclude">state_permissions_ignore_hidden_paths</filter>
  </ind:textfilecontent54_object>

  <ind:textfilecontent54_state id="state_permissions_ignore_hidden_paths" comment="ignore hidden conf files" version="1">
    <!-- Among the paths matched in object_rfp_rsyslog_include_config_value there can be paths from
         include() or $IncludeConfig that point to hidden dirs or files.
         Rsyslog ignores these conf files, so we should ignore them too.
    -->
    <ind:subexpression operation="pattern match">^.*\/\..*$</ind:subexpression>
  </ind:textfilecontent54_state>

  <!-- Turn that glob value into Perl's regex so it can be used as filepath pattern below -->
  <local_variable id="var_rfp_include_config_regex" datatype="string" version="1" comment="$IncludeConfig value converted to regex">
    <unique>
      <glob_to_regex>
        <object_component item_field="subexpression" object_ref="object_rfp_rsyslog_include_config_value" />
      </glob_to_regex>
    </unique>
  </local_variable>

  <!-- Create a variable_object from the regex variable
       If the variable has no values, there won't be any objects -->
  <ind:variable_object id="object_var_rfp_include_config_regex" comment="Make variable object from regex variable" version="1">
    <ind:var_ref>var_rfp_include_config_regex</ind:var_ref>
  </ind:variable_object>

  <local_variable id="var_rfp_syslog_config" datatype="string" version="1" comment="Locations of all rsyslog configuration files as collection">
    <literal_component datatype="string">^/etc/rsyslog.conf$</literal_component>
  </local_variable>

  <ind:variable_object id="object_var_rfp_syslog_config" comment="Make variable object for use" version="1">
    <ind:var_ref>var_rfp_syslog_config</ind:var_ref>
  </ind:variable_object>

  <!-- Combine the two variable_objects into one variable_object
       We do it this way to avoid referencing an empty variable in a state comparison, which
       will cause a test to evaluate to fail. Combining an empty set of objects is fine though -->
  <ind:variable_object id="object_var_rfp_all_log_files" comment="Filter out empty string" version="1">
    <set>
      <object_reference>object_var_rfp_include_config_regex</object_reference>
      <object_reference>object_var_rfp_syslog_config</object_reference>
    </set>
  </ind:variable_object>

  <!-- In element filepath of object_rfg_log_files_paths we need to pass a list of values,
       a list of objects won't do. So we make a local_variable from the variable_objects. -->
  <local_variable id="var_rfp_all_log_files" datatype="string" version="1" comment="Locations of all rsyslog configuration files as collection">
    <object_component object_ref="object_var_rfp_all_log_files" item_field="value"/>
  </local_variable>

  <!-- For each item from that collection (particular rsyslog's configuration file path) search
       that rsyslog's configuration file to select file paths for log files directives
  -->
  <ind:textfilecontent54_object id="object_rfp_log_files_paths" comment="All rsyslog configuration files" version="1">
    <ind:filepath operation="pattern match" var_ref="var_rfp_all_log_files" var_check="at least one" />
    <!-- Chunk of text retrieved from rsyslog's configuration file is considered
         to constitute a log file path if all of the following conditions are met:
         * the string represents a regular file on particular file system
           (verified via corresponding file_state below),
         * the chunk of text is in the last column in the row,
           (possibly suffixed by ';' character and rsyslog Template name),
         * contains at least one slash '/' character, and simultaneously
           doesn't contain any of ';', ':' and space characters,
         * the chunk was retrieved from a row not starting with space, '#',
           or '$' characters
    -->
    <ind:pattern operation="pattern match">^[^(\s|#|\$)]+[\s]+.*[\s]+-?(/+[^:;\s]+);*\.*$</ind:pattern>
    <ind:instance datatype="int" operation="greater than or equal">1</ind:instance>
    <filter action="exclude">state_permissions_ignore_include_paths</filter>
  </ind:textfilecontent54_object>

  <ind:textfilecontent54_state id="state_permissions_ignore_include_paths" comment="ignore" version="1">
    <!-- Among the paths matched in object_rfp_log_files_paths there can be paths from
         include() or $IncludeConfig statements.
         These paths are conf files, not log files. Their permissions don't need to be as
         required for log files, thus, lets exclude them from the list of objects found
    -->
    <ind:text operation="pattern match">(?:file="[^\s;]+"|\$IncludeConfig[\s]+[^\s;]+|\/dev\/.*)</ind:text>
  </ind:textfilecontent54_state>

  <!-- Define OVAL variable to hold all the various system log files locations
       retrieved from the different rsyslog configuration files
  -->
  <local_variable id="var_rfp_log_files_paths" datatype="string" version="1" comment="File paths of all rsyslog configuration files">
    <object_component item_field="subexpression" object_ref="object_rfp_log_files_paths" />
  </local_variable>

  <!-- Perform the test if all rsyslog system log files have correct permissions -->
  <unix:file_test check="all" check_existence="all_exist" id="test_rsyslog_files_permissions" version="1" comment="Permissions of system log files are correct">
    <unix:object object_ref="object_rsyslog_files_permissions" />
    <unix:state state_ref="state_rsyslog_files_permissions" />
  </unix:file_test>

  <unix:file_object id="object_rsyslog_files_permissions" comment="Various system log files" version="1">
    <unix:filepath datatype="string" var_ref="var_rfp_log_files_paths" var_check="at least one" />
  </unix:file_object>

  <unix:file_state id="state_rsyslog_files_permissions" version="1">
    <unix:type operation="equals">regular</unix:type>
    <unix:uexec datatype="boolean">false</unix:uexec>
    {{% if product in ["debian10", "debian11", "ubuntu1604", "ubuntu1804", "ubuntu2004", "ubuntu2204", "sle15", "sle12"] %}}
    <unix:gread datatype="boolean">true</unix:gread>
    {{% else %}}
    <unix:gread datatype="boolean">false</unix:gread>
    {{% endif %}}
    <unix:gwrite datatype="boolean">false</unix:gwrite>
    <unix:gexec datatype="boolean">false</unix:gexec>
    <unix:oread datatype="boolean">false</unix:oread>
    <unix:owrite datatype="boolean">false</unix:owrite>
    <unix:oexec datatype="boolean">false</unix:oexec>
  </unix:file_state>
</def-group>
