diff --git a/SOURCES/openscap-1.2.18-fix_trailing_whitespace_in_playbooks-PR_1268.patch b/SOURCES/openscap-1.2.18-fix_trailing_whitespace_in_playbooks-PR_1268.patch new file mode 100644 index 0000000..406fe11 --- /dev/null +++ b/SOURCES/openscap-1.2.18-fix_trailing_whitespace_in_playbooks-PR_1268.patch @@ -0,0 +1,103 @@ +From df7b06ffaab9f593ae672182a43e43b145fc7739 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= +Date: Tue, 13 Nov 2018 11:15:17 +0100 +Subject: [PATCH] Fix trailing whitespaces in Ansible Playbooks + +Code which generates Ansible remediations updated to: +* remove trailing whitespaces from the Ansible remediation lines, +* not indent empty Ansible remediation lines. +These issues were found by `ansible-lint`. Also trailing whitespaces +got removed from the generated remediation headers. +--- + src/XCCDF_POLICY/xccdf_policy_remediate.c | 38 ++++++++++++++++------- + 1 file changed, 27 insertions(+), 11 deletions(-) + +diff --git a/src/XCCDF_POLICY/xccdf_policy_remediate.c b/src/XCCDF_POLICY/xccdf_policy_remediate.c +index acf2c11951..344ff68859 100644 +--- a/src/XCCDF_POLICY/xccdf_policy_remediate.c ++++ b/src/XCCDF_POLICY/xccdf_policy_remediate.c +@@ -22,7 +22,8 @@ + #endif + + #include ++#include + #include + #include + #include + #include +@@ -103,6 +104,7 @@ static int _write_remediation_to_fd_and_free(int output_fd, const char* template + + char *current = text; + char *next_delim = NULL; ++ char *end = NULL; + + do { + next_delim = strchr(current, delim); +@@ -110,17 +112,27 @@ static int _write_remediation_to_fd_and_free(int output_fd, const char* template + *next_delim = '\0'; + } + +- // write indentation +- if (_write_text_to_fd(output_fd, indentation) != 0) { +- free(text); +- return 1; ++ // remove all trailing whitespaces ++ end = current + strlen(current) - 1; ++ while (isspace(*end)) { ++ *end = '\0'; ++ if (end == current) ++ break; ++ end--; + } + +- // write rest of line +- if (_write_text_to_fd(output_fd, current) != 0) { +- free(text); +- return 1; ++ if (strlen(current) != 0) { ++ // write indentation ++ if (_write_text_to_fd(output_fd, indentation) != 0) { ++ free(text); ++ return 1; ++ } ++ if (_write_text_to_fd(output_fd, current) != 0) { ++ free(text); ++ return 1; ++ } + } ++ + if (_write_text_to_fd(output_fd, "\n") != 0) { + free(text); + return 1; +@@ -130,8 +142,12 @@ static int _write_remediation_to_fd_and_free(int output_fd, const char* template + // text is NULL terminated to this is guaranteed to point to valid memory + current = next_delim + 1; + } ++ } while (next_delim != NULL); ++ ++ if (_write_text_to_fd(output_fd, "\n") != 0) { ++ free(text); ++ return 1; + } +- while (next_delim != NULL); + + free(text); + return 0; +@@ -852,7 +868,7 @@ static int _write_script_header_to_fd(struct xccdf_policy *policy, struct xccdf_ + "# Benchmark Version: %s\n#\n" + "# XCCDF Version: %s\n#\n" + "# This file was generated by OpenSCAP %s using:\n" +- "# $ oscap xccdf generate fix --profile %s%s%s xccdf-file.xml \n#\n" ++ "# $ oscap xccdf generate fix --profile %s%s%s xccdf-file.xml\n#\n" + "# This script is generated from an OpenSCAP profile without preliminary evaluation.\n" + "# It attempts to fix every selected rule, even if the system is already compliant.\n" + "#\n" +@@ -881,7 +897,7 @@ static int _write_script_header_to_fd(struct xccdf_policy *policy, struct xccdf_ + "# Evaluation Start Time: %s\n" + "# Evaluation End Time: %s\n#\n" + "# This file was generated by OpenSCAP %s using:\n" +- "# $ oscap xccdf generate fix --result-id %s%s%s xccdf-results.xml \n#\n" ++ "# $ oscap xccdf generate fix --result-id %s%s%s xccdf-results.xml\n#\n" + "# This script is generated from the results of a profile evaluation.\n" + "# It attempts to remediate all issues from the selected rules that failed the test.\n" + "#\n" diff --git a/SOURCES/openscap-1.2.18-memory-limits-PR_1803.patch b/SOURCES/openscap-1.2.18-memory-limits-PR_1803.patch new file mode 100644 index 0000000..9bdd2c7 --- /dev/null +++ b/SOURCES/openscap-1.2.18-memory-limits-PR_1803.patch @@ -0,0 +1,242 @@ +From 85a13f1f8e8d1bdbd0d5b96f543c808a1548db51 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= +Date: Mon, 30 Aug 2021 15:44:37 +0200 +Subject: [PATCH 1/3] Lower memory limits and improve their checking + +This patch attempts to mitigate problems caused by a large amount of +collected objects such as rhbz#1932833. + +Specifically, these changes are made: +- Lower the threshold so that the amount of used memory is checked when + only 1000 items are collected for the given OVAL object. That's + because 32768 items (the original value) is already a large amount which + occupies a lot of memory during further processing. +- Lower the memory usage ratio limit for the probe to 10 %. We have + found experimentally that giving the probe 15 % or more will cause the + oscap process to be killed when processing the collected data and + generating results. +- In the calling function probe_item_collect, distinguish between return + codes which means different behavior when there is insufficient memory + than when the memory consumption can't be checked. +- Improve the warning message to show greater details about memory + consumption to the user. +- Remove the check for the absolute amount of remaining free memory. As + we can see on the example of rhbz#1932833, on systems with large + amount of memory the remaining memory of 512 MB isn't enough memory for + openscap to process the collected data. At the same time, if we lowered + the usage ratio, we don't need this anymore. +- Remove useless message "spt:" from the verbose log because it's + produced many times and pollutes the log extremely. +--- + src/OVAL/probes/probe/icache.c | 23 +++++++++++------------ + src/common/memusage.c | 2 -- + 2 files changed, 11 insertions(+), 14 deletions(-) + +diff --git a/src/OVAL/probes/probe/icache.c b/src/OVAL/probes/probe/icache.c +index e67f5ebd3c..ccb7ee3057 100644 +--- a/src/OVAL/probes/probe/icache.c ++++ b/src/OVAL/probes/probe/icache.c +@@ -457,9 +457,8 @@ int probe_icache_nop(probe_icache_t *cache) + return (0); + } + +-#define PROBE_RESULT_MEMCHECK_CTRESHOLD 32768 /* item count */ +-#define PROBE_RESULT_MEMCHECK_MINFREEMEM 512 /* MiB */ +-#define PROBE_RESULT_MEMCHECK_MAXRATIO 0.8 /* max. memory usage ratio - used/total */ ++#define PROBE_RESULT_MEMCHECK_CTRESHOLD 1000 /* item count */ ++#define PROBE_RESULT_MEMCHECK_MAXRATIO 0.1 /* max. memory usage ratio - used/total */ + + /** + * Returns 0 if the memory constraints are not reached. Otherwise, 1 is returned. +@@ -481,18 +480,12 @@ static int probe_cobj_memcheck(size_t item_cnt) + c_ratio = (double)mu_proc.mu_rss/(double)(mu_sys.mu_total); + + if (c_ratio > PROBE_RESULT_MEMCHECK_MAXRATIO) { +- dW("Memory usage ratio limit reached! limit=%f, current=%f", +- PROBE_RESULT_MEMCHECK_MAXRATIO, c_ratio); ++ dW("Memory usage ratio limit reached! limit=%f, current=%f, used=%ld MB, free=%ld MB, total=%ld MB, count of items=%ld", ++ PROBE_RESULT_MEMCHECK_MAXRATIO, c_ratio, mu_proc.mu_rss / 1024, mu_sys.mu_realfree / 1024, mu_sys.mu_total / 1024, item_cnt); + errno = ENOMEM; + return (1); + } + +- if ((mu_sys.mu_realfree / 1024) < PROBE_RESULT_MEMCHECK_MINFREEMEM) { +- dW("Minimum free memory limit reached! limit=%zu, current=%zu", +- PROBE_RESULT_MEMCHECK_MINFREEMEM, mu_sys.mu_realfree / 1024); +- errno = ENOMEM; +- return (1); +- } + } + + return (0); +@@ -517,6 +510,7 @@ int probe_item_collect(struct probe_ctx *ctx, SEXP_t *item) + { + SEXP_t *cobj_content; + size_t cobj_itemcnt; ++ int memcheck_ret; + + assume_d(ctx != NULL, -1); + assume_d(ctx->probe_out != NULL, -1); +@@ -526,7 +520,12 @@ int probe_item_collect(struct probe_ctx *ctx, SEXP_t *item) + cobj_itemcnt = SEXP_list_length(cobj_content); + SEXP_free(cobj_content); + +- if (probe_cobj_memcheck(cobj_itemcnt) != 0) { ++ memcheck_ret = probe_cobj_memcheck(cobj_itemcnt); ++ if (memcheck_ret == -1) { ++ dE("Failed to check available memory"); ++ return -1; ++ } ++ if (memcheck_ret == 1) { + + /* + * Don't set the message again if the collected object is +diff --git a/src/common/memusage.c b/src/common/memusage.c +index af4ddb4d55..686ec9bc02 100644 +--- a/src/common/memusage.c ++++ b/src/common/memusage.c +@@ -117,8 +117,6 @@ static int read_status(const char *source, void *base, struct stat_parser *spt, + sp = oscap_bfind(spt, spt_size, sizeof(struct stat_parser), + linebuf, (int(*)(void *, void *))&cmpkey); + +- dI("spt: %s", linebuf); +- + if (sp == NULL) { + /* drop end of unread line */ + while (strchr(strval, '\n') == NULL) { + +From bb26be6ffcf2a88c09e4f237516ad71db555158f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= +Date: Tue, 7 Sep 2021 13:52:50 +0200 +Subject: [PATCH 2/3] Allow to set memory ratio by environment variable + +If the probe memory usage ratio limit will be too small or too big +in some situation, the user will be able to modify the limit easily +by setting the environment variable OSCAP_PROBE_MEMORY_USAGE_RATIO +to a different value. This can also help users when debugging memory +problems. +--- + docs/manual/manual.adoc | 1 + + src/OVAL/probes/probe/icache.c | 9 ++++----- + src/OVAL/probes/probe/probe.h | 1 + + src/OVAL/probes/probe/worker.c | 12 ++++++++++++ + 4 files changed, 18 insertions(+), 5 deletions(-) + +diff --git a/docs/manual/manual.adoc b/docs/manual/manual.adoc +index 135426b6f0..b9ad60ced1 100644 +--- a/docs/manual/manual.adoc ++++ b/docs/manual/manual.adoc +@@ -1925,6 +1925,7 @@ behaviour. + + * *OSCAP_FULL_VALIDATION=1* - validate all exported documents (slower) + * *SEXP_VALIDATE_DISABLE=1* - do not validate SEXP expressions (faster) ++* *OSCAP_PROBE_MEMORY_USAGE_RATIO* - maximum memory usage ratio (used/total) for OpenSCAP probes, default: 0.1 + + + +diff --git a/src/OVAL/probes/probe/icache.c b/src/OVAL/probes/probe/icache.c +index ccb7ee3057..08a0c9ad07 100644 +--- a/src/OVAL/probes/probe/icache.c ++++ b/src/OVAL/probes/probe/icache.c +@@ -458,13 +458,12 @@ int probe_icache_nop(probe_icache_t *cache) + } + + #define PROBE_RESULT_MEMCHECK_CTRESHOLD 1000 /* item count */ +-#define PROBE_RESULT_MEMCHECK_MAXRATIO 0.1 /* max. memory usage ratio - used/total */ + + /** + * Returns 0 if the memory constraints are not reached. Otherwise, 1 is returned. + * In case of an error, -1 is returned. + */ +-static int probe_cobj_memcheck(size_t item_cnt) ++static int probe_cobj_memcheck(size_t item_cnt, double max_ratio) + { + if (item_cnt > PROBE_RESULT_MEMCHECK_CTRESHOLD) { + struct proc_memusage mu_proc; +@@ -479,9 +478,9 @@ static int probe_cobj_memcheck(size_t item_cnt) + + c_ratio = (double)mu_proc.mu_rss/(double)(mu_sys.mu_total); + +- if (c_ratio > PROBE_RESULT_MEMCHECK_MAXRATIO) { ++ if (c_ratio > max_ratio) { + dW("Memory usage ratio limit reached! limit=%f, current=%f, used=%ld MB, free=%ld MB, total=%ld MB, count of items=%ld", +- PROBE_RESULT_MEMCHECK_MAXRATIO, c_ratio, mu_proc.mu_rss / 1024, mu_sys.mu_realfree / 1024, mu_sys.mu_total / 1024, item_cnt); ++ max_ratio, c_ratio, mu_proc.mu_rss / 1024, mu_sys.mu_realfree / 1024, mu_sys.mu_total / 1024, item_cnt); + errno = ENOMEM; + return (1); + } +@@ -520,7 +519,7 @@ int probe_item_collect(struct probe_ctx *ctx, SEXP_t *item) + cobj_itemcnt = SEXP_list_length(cobj_content); + SEXP_free(cobj_content); + +- memcheck_ret = probe_cobj_memcheck(cobj_itemcnt); ++ memcheck_ret = probe_cobj_memcheck(cobj_itemcnt, ctx->max_mem_ratio); + if (memcheck_ret == -1) { + dE("Failed to check available memory"); + return -1; +diff --git a/src/OVAL/probes/probe/probe.h b/src/OVAL/probes/probe/probe.h +index 0cfa234a2b..d98e741414 100644 +--- a/src/OVAL/probes/probe/probe.h ++++ b/src/OVAL/probes/probe/probe.h +@@ -73,6 +73,7 @@ struct probe_ctx { + SEXP_t *filters; /**< object filters (OVAL 5.8 and higher) */ + probe_icache_t *icache; /**< item cache */ + int offline_mode; ++ double max_mem_ratio; + }; + + typedef enum { +diff --git a/src/OVAL/probes/probe/worker.c b/src/OVAL/probes/probe/worker.c +index f89663544c..77f81e7885 100644 +--- a/src/OVAL/probes/probe/worker.c ++++ b/src/OVAL/probes/probe/worker.c +@@ -37,6 +37,10 @@ + + #include "worker.h" + ++/* default max. memory usage ratio - used/total */ ++/* can be overridden by environment variable OSCAP_PROBE_MEMORY_USAGE_RATIO */ ++#define OSCAP_PROBE_MEMORY_USAGE_RATIO_DEFAULT 0.1 ++ + extern bool OSCAP_GSYM(varref_handling); + extern void *OSCAP_GSYM(probe_arg); + +@@ -924,6 +928,14 @@ SEXP_t *probe_worker(probe_t *probe, SEAP_msg_t *msg_in, int *ret) + + pctx.offline_mode = probe->selected_offline_mode; + ++ pctx.max_mem_ratio = OSCAP_PROBE_MEMORY_USAGE_RATIO_DEFAULT; ++ char *max_ratio_str = getenv("OSCAP_PROBE_MEMORY_USAGE_RATIO"); ++ if (max_ratio_str != NULL) { ++ double max_ratio = strtod(max_ratio_str, NULL); ++ if (max_ratio != 0) ++ pctx.max_mem_ratio = max_ratio; ++ } ++ + /* simple object */ + pctx.icache = probe->icache; + pctx.filters = probe_prepare_filters(probe, probe_in); + +From 521756ac0668e0c686798db957f8c06ef7d55085 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= +Date: Wed, 15 Sep 2021 14:41:30 +0200 +Subject: [PATCH 3/3] Update src/OVAL/probes/probe/worker.c + +Co-authored-by: Evgeny Kolesnikov +--- + src/OVAL/probes/probe/worker.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/OVAL/probes/probe/worker.c b/src/OVAL/probes/probe/worker.c +index 77f81e7885..9b920edc15 100644 +--- a/src/OVAL/probes/probe/worker.c ++++ b/src/OVAL/probes/probe/worker.c +@@ -932,7 +932,7 @@ SEXP_t *probe_worker(probe_t *probe, SEAP_msg_t *msg_in, int *ret) + char *max_ratio_str = getenv("OSCAP_PROBE_MEMORY_USAGE_RATIO"); + if (max_ratio_str != NULL) { + double max_ratio = strtod(max_ratio_str, NULL); +- if (max_ratio != 0) ++ if (max_ratio > 0) + pctx.max_mem_ratio = max_ratio; + } + diff --git a/SOURCES/openscap-1.2.18-prevent_duplicate_vars_in_playbooks-PR_1274.patch b/SOURCES/openscap-1.2.18-prevent_duplicate_vars_in_playbooks-PR_1274.patch new file mode 100644 index 0000000..345d9b2 --- /dev/null +++ b/SOURCES/openscap-1.2.18-prevent_duplicate_vars_in_playbooks-PR_1274.patch @@ -0,0 +1,497 @@ +From 84e2041d3b6d9a240ddab3f39c7baed8e718bfab Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= +Date: Tue, 20 Nov 2018 12:00:48 +0100 +Subject: [PATCH 1/4] Prevent duplicate variables in Ansible Playbooks + +The same XCCDF value can appear in multiple rules, which means +there can be same variable in multiple Ansible task. But when +we generate a Playbook we should put the variable only once. +Ansible produces a warning if there are multiple definitions +of the same variable. +--- + src/XCCDF_POLICY/xccdf_policy_remediate.c | 180 ++++++++++++---------- + 1 file changed, 95 insertions(+), 85 deletions(-) + +diff --git a/src/XCCDF_POLICY/xccdf_policy_remediate.c b/src/XCCDF_POLICY/xccdf_policy_remediate.c +index 65544c7f74..6ac566225c 100644 +--- a/src/XCCDF_POLICY/xccdf_policy_remediate.c ++++ b/src/XCCDF_POLICY/xccdf_policy_remediate.c +@@ -134,11 +134,11 @@ static int _write_remediation_to_fd_and_free(int output_fd, const char* template + free(text); + return 1; + } +- } + +- if (_write_text_to_fd(output_fd, "\n") != 0) { +- free(text); +- return 1; ++ if (_write_text_to_fd(output_fd, "\n") != 0) { ++ free(text); ++ return 1; ++ } + } + + if (next_delim != NULL) { +@@ -610,7 +610,7 @@ static int _write_fix_missing_warning_to_fd(const char *sys, int output_fd, stru + } + } + +-static inline int _xccdf_policy_rule_generate_fix_ansible(const char *template, int output_fd, const char *fix_text, bool ansible_variable_mode) ++static inline int _parse_ansible_fix(const char *fix_text, struct oscap_list *variables, struct oscap_list *tasks) + { + #if defined USE_REGEX_PCRE + // TODO: Tolerate different indentation styles in this regex +@@ -649,69 +649,53 @@ static inline int _xccdf_policy_rule_generate_fix_ansible(const char *template, + return 1; + } + +- if (ansible_variable_mode) { +- // ovector[0] and [1] hold the start and end of the whole needle match +- // ovector[2] and [3] hold the start and end of the first capture group +- // ovector[4] and [5] hold the start and end of the second capture group +- char *variable_name = malloc((ovector[3] - ovector[2] + 1) * sizeof(char)); +- memcpy(variable_name, &fix_text[ovector[2]], ovector[3] - ovector[2]); +- variable_name[ovector[3] - ovector[2]] = '\0'; ++ // ovector[0] and [1] hold the start and end of the whole needle match ++ // ovector[2] and [3] hold the start and end of the first capture group ++ // ovector[4] and [5] hold the start and end of the second capture group ++ char *variable_name = malloc((ovector[3] - ovector[2] + 1) * sizeof(char)); ++ memcpy(variable_name, &fix_text[ovector[2]], ovector[3] - ovector[2]); ++ variable_name[ovector[3] - ovector[2]] = '\0'; + +- char *variable_value = malloc((ovector[5] - ovector[4] + 1) * sizeof(char)); +- memcpy(variable_value, &fix_text[ovector[4]], ovector[5] - ovector[4]); +- variable_value[ovector[5] - ovector[4]] = '\0'; ++ char *variable_value = malloc((ovector[5] - ovector[4] + 1) * sizeof(char)); ++ memcpy(variable_value, &fix_text[ovector[4]], ovector[5] - ovector[4]); ++ variable_value[ovector[5] - ovector[4]] = '\0'; + +- char *var_line = oscap_sprintf(" %s: %s", variable_name, variable_value); ++ char *var_line = oscap_sprintf(" %s: %s\n", variable_name, variable_value); + +- free(variable_name); +- free(variable_value); ++ free(variable_name); ++ free(variable_value); + +- if (_write_remediation_to_fd_and_free(output_fd, template, var_line) != 0) { +- pcre_free(re); +- return 1; +- } +- } +- else { +- // Remarks: ovector doesn't contain values relative to start_offset, it contains +- // absolute indices of fix_text. +- const int length_between_matches = ovector[0] - start_offset; +- char *remediation_part = malloc((length_between_matches + 1) * sizeof(char)); +- memcpy(remediation_part, &fix_text[start_offset], length_between_matches); +- remediation_part[length_between_matches] = '\0'; +- if (_write_remediation_to_fd_and_free(output_fd, template, remediation_part) != 0) { +- pcre_free(re); +- return 1; +- } ++ if (!oscap_list_contains(variables, var_line, (oscap_cmp_func) oscap_streq)) { ++ oscap_list_add(variables, var_line); + } + ++ // Remarks: ovector doesn't contain values relative to start_offset, it contains ++ // absolute indices of fix_text. ++ const int length_between_matches = ovector[0] - start_offset; ++ char *remediation_part = malloc((length_between_matches + 1) * sizeof(char)); ++ memcpy(remediation_part, &fix_text[start_offset], length_between_matches); ++ remediation_part[length_between_matches] = '\0'; ++ oscap_list_add(tasks, remediation_part); ++ + start_offset = ovector[1]; // next time start after the entire pattern + } + +- if (!ansible_variable_mode && fix_text_len - start_offset > 0) { ++ if (fix_text_len - start_offset > 0) { + char *remediation_part = malloc((fix_text_len - start_offset + 1) * sizeof(char)); + memcpy(remediation_part, &fix_text[start_offset], fix_text_len - start_offset); + remediation_part[fix_text_len - start_offset] = '\0'; +- +- if (_write_remediation_to_fd_and_free(output_fd, template, remediation_part) != 0) { +- pcre_free(re); +- return 1; +- } ++ oscap_list_add(tasks, remediation_part); + } + + pcre_free(re); + return 0; + #else + // TODO: Implement the post-process for posix regex as well +- if (ansible_variable_mode) { +- // this is not implemented so we don't write anything out for variables +- return 0; +- } +- else +- return _write_remediation_to_fd_and_free(output_fd, template, fix_text); ++ oscap_list_add(tasks, strdup(fix_text)); + #endif + } + +-static inline int _xccdf_policy_rule_generate_fix(struct xccdf_policy *policy, struct xccdf_rule *rule, const char *template, int output_fd, unsigned int current, unsigned int total, bool ansible_variable_mode) ++static int _xccdf_policy_rule_get_fix_text(struct xccdf_policy *policy, struct xccdf_rule *rule, const char *template, char **fix_text) + { + // Ensure that given Rule is selected and applicable (CPE). + const bool is_selected = xccdf_policy_is_item_selected(policy, xccdf_rule_get_id(rule)); +@@ -722,15 +706,8 @@ static inline int _xccdf_policy_rule_generate_fix(struct xccdf_policy *policy, s + // Find the most suitable fix. + const struct xccdf_fix *fix = _find_fix_for_template(policy, rule, template); + if (fix == NULL) { +- int ret = _write_fix_header_to_fd(template, output_fd, rule, current, total); +- if (ret != 0) +- return ret; +- ret = _write_fix_missing_warning_to_fd(template, output_fd, rule); +- if (ret != 0) +- return ret; +- ret = _write_fix_footer_to_fd(template, output_fd, rule); + dI("No fix element was found for Rule/@id=\"%s\"", xccdf_rule_get_id(rule)); +- return ret; ++ return 0; + } + dI("Processing a fix for Rule/@id=\"%s\"", xccdf_rule_get_id(rule)); + +@@ -744,37 +721,55 @@ static inline int _xccdf_policy_rule_generate_fix(struct xccdf_policy *policy, s + return res == 1; // Value 2 indicates warning. + } + // Refine. Resolve XML comments, CDATA and remaining elements +- char *fix_text = NULL; +- if (_xccdf_fix_decode_xml(cfix, &fix_text) != 0) { ++ if (_xccdf_fix_decode_xml(cfix, fix_text) != 0) { + oscap_seterr(OSCAP_EFAMILY_OSCAP, "A fix element for Rule/@id=\"%s\" contains unresolved child elements.", + xccdf_rule_get_id(rule)); + xccdf_fix_free(cfix); + return 1; + } + xccdf_fix_free(cfix); ++ return 0; ++} + +- int ret = _write_fix_header_to_fd(template, output_fd, rule, current, total); ++static int _xccdf_policy_rule_generate_fix(struct xccdf_policy *policy, struct xccdf_rule *rule, const char *template, int output_fd, unsigned int current, unsigned int total, bool ansible_variable_mode) ++{ ++ char *fix_text = NULL; ++ int ret = _xccdf_policy_rule_get_fix_text(policy, rule, template, &fix_text); ++ if (fix_text == NULL || ret != 0) { ++ ret = _write_fix_header_to_fd(template, output_fd, rule, current, total); ++ if (ret != 0) { ++ return ret; ++ } ++ ret = _write_fix_missing_warning_to_fd(template, output_fd, rule); ++ if (ret != 0) { ++ return ret; ++ } ++ ret = _write_fix_footer_to_fd(template, output_fd, rule); ++ return ret; ++ } ++ ret = _write_fix_header_to_fd(template, output_fd, rule, current, total); + if (ret != 0) { + free(fix_text); + return ret; + } ++ ret = _write_remediation_to_fd_and_free(output_fd, template, fix_text); ++ if (ret != 0) { ++ return ret; ++ } ++ ret = _write_fix_footer_to_fd(template, output_fd, rule); ++ return ret; ++} + +- if (oscap_streq(template, "urn:xccdf:fix:script:ansible")) { +- // Ansible is special because we have two output modes, variable and +- // task mode. In both cases we have to do post processing. + +- // We can't recover the return value (it is rewritten by a later call) to _write_fix_footer_to_fd +- _xccdf_policy_rule_generate_fix_ansible(template, output_fd, fix_text, ansible_variable_mode); +- free(fix_text); +- } +- else { +- ret = _write_remediation_to_fd_and_free(output_fd, template, fix_text); +- if (ret != 0) { +- return ret; +- } ++static int _xccdf_policy_rule_generate_ansible_fix(struct xccdf_policy *policy, struct xccdf_rule *rule, const char *template, struct oscap_list *variables, struct oscap_list *tasks) ++{ ++ char *fix_text = NULL; ++ int ret = _xccdf_policy_rule_get_fix_text(policy, rule, template, &fix_text); ++ if (fix_text == NULL) { ++ return ret; + } +- +- ret = _write_fix_footer_to_fd(template, output_fd, rule); ++ ret = _parse_ansible_fix(fix_text, variables, tasks); ++ free(fix_text); + return ret; + } + +@@ -988,30 +983,45 @@ int xccdf_policy_generate_fix(struct xccdf_policy *policy, struct xccdf_result * + + // In Ansible we have to generate variables first and then tasks + if (strcmp(sys, "urn:xccdf:fix:script:ansible") == 0) { ++ struct oscap_list *variables = oscap_list_new(); ++ struct oscap_list *tasks = oscap_list_new(); ++ struct oscap_iterator *rules_to_fix_it = oscap_iterator_new(rules_to_fix); ++ while (oscap_iterator_has_more(rules_to_fix_it)) { ++ struct xccdf_rule *rule = (struct xccdf_rule*)oscap_iterator_next(rules_to_fix_it); ++ ret = _xccdf_policy_rule_generate_ansible_fix(policy, rule, sys, variables, tasks); ++ if (ret != 0) ++ break; ++ } ++ oscap_iterator_free(rules_to_fix_it); ++ + _write_text_to_fd(output_fd, " vars:\n"); ++ struct oscap_iterator *variables_it = oscap_iterator_new(variables); ++ while(oscap_iterator_has_more(variables_it)) { ++ char *var_line = (char *) oscap_iterator_next(variables_it); ++ _write_text_to_fd(output_fd, var_line); ++ } ++ oscap_iterator_free(variables_it); ++ oscap_list_free(variables, free); + ++ _write_text_to_fd(output_fd, " tasks:\n"); ++ struct oscap_iterator *tasks_it = oscap_iterator_new(tasks); ++ while(oscap_iterator_has_more(tasks_it)) { ++ char *var_line = strdup((char *) oscap_iterator_next(tasks_it)); ++ _write_remediation_to_fd_and_free(output_fd, sys, var_line); ++ } ++ oscap_iterator_free(tasks_it); ++ oscap_list_free(tasks, free); ++ } else { + unsigned int current = 1; + struct oscap_iterator *rules_to_fix_it = oscap_iterator_new(rules_to_fix); + while (oscap_iterator_has_more(rules_to_fix_it)) { + struct xccdf_rule *rule = (struct xccdf_rule*)oscap_iterator_next(rules_to_fix_it); +- ret = _xccdf_policy_rule_generate_fix(policy, rule, sys, output_fd, current++, total, true); ++ ret = _xccdf_policy_rule_generate_fix(policy, rule, sys, output_fd, current++, total, false); + if (ret != 0) + break; + } + oscap_iterator_free(rules_to_fix_it); +- +- _write_text_to_fd(output_fd, " tasks:\n"); +- } +- +- unsigned int current = 1; +- struct oscap_iterator *rules_to_fix_it = oscap_iterator_new(rules_to_fix); +- while (oscap_iterator_has_more(rules_to_fix_it)) { +- struct xccdf_rule *rule = (struct xccdf_rule*)oscap_iterator_next(rules_to_fix_it); +- ret = _xccdf_policy_rule_generate_fix(policy, rule, sys, output_fd, current++, total, false); +- if (ret != 0) +- break; + } +- oscap_iterator_free(rules_to_fix_it); + + oscap_list_free(rules_to_fix, NULL); + + +From 8badfbf30f423ca2f9452ab3ac6daf8bb8d850e3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= +Date: Thu, 22 Nov 2018 13:43:40 +0100 +Subject: [PATCH 2/4] Remove unused parameter + +This parameter is not used in this function. The function is static +and it is used only at one place, so we can safely omit this parameter. +--- + src/XCCDF_POLICY/xccdf_policy_remediate.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/XCCDF_POLICY/xccdf_policy_remediate.c b/src/XCCDF_POLICY/xccdf_policy_remediate.c +index 6ac566225c..520e9ff4de 100644 +--- a/src/XCCDF_POLICY/xccdf_policy_remediate.c ++++ b/src/XCCDF_POLICY/xccdf_policy_remediate.c +@@ -731,7 +731,7 @@ static int _xccdf_policy_rule_get_fix_text(struct xccdf_policy *policy, struct x + return 0; + } + +-static int _xccdf_policy_rule_generate_fix(struct xccdf_policy *policy, struct xccdf_rule *rule, const char *template, int output_fd, unsigned int current, unsigned int total, bool ansible_variable_mode) ++static int _xccdf_policy_rule_generate_fix(struct xccdf_policy *policy, struct xccdf_rule *rule, const char *template, int output_fd, unsigned int current, unsigned int total) + { + char *fix_text = NULL; + int ret = _xccdf_policy_rule_get_fix_text(policy, rule, template, &fix_text); +@@ -1016,7 +1016,7 @@ int xccdf_policy_generate_fix(struct xccdf_policy *policy, struct xccdf_result * + struct oscap_iterator *rules_to_fix_it = oscap_iterator_new(rules_to_fix); + while (oscap_iterator_has_more(rules_to_fix_it)) { + struct xccdf_rule *rule = (struct xccdf_rule*)oscap_iterator_next(rules_to_fix_it); +- ret = _xccdf_policy_rule_generate_fix(policy, rule, sys, output_fd, current++, total, false); ++ ret = _xccdf_policy_rule_generate_fix(policy, rule, sys, output_fd, current++, total); + if (ret != 0) + break; + } + +From 90e0354c3eef165d8b467e6d843e9dfe1d264416 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= +Date: Mon, 26 Nov 2018 18:18:47 +0100 +Subject: [PATCH 3/4] Refactor: Extract functions + +To improve the readability of the code we can refactor the code +that is specific to Ansible to a separate method. We can also make +a similar change in the `else` branch. +--- + src/XCCDF_POLICY/xccdf_policy_remediate.c | 94 +++++++++++++---------- + 1 file changed, 54 insertions(+), 40 deletions(-) + +diff --git a/src/XCCDF_POLICY/xccdf_policy_remediate.c b/src/XCCDF_POLICY/xccdf_policy_remediate.c +index 520e9ff4de..bf36523dfd 100644 +--- a/src/XCCDF_POLICY/xccdf_policy_remediate.c ++++ b/src/XCCDF_POLICY/xccdf_policy_remediate.c +@@ -929,6 +929,56 @@ static int _write_script_header_to_fd(struct xccdf_policy *policy, struct xccdf_ + } + } + ++static int _xccdf_policy_generate_fix_ansible(struct oscap_list *rules_to_fix, struct xccdf_policy *policy, const char *sys, int output_fd) ++{ ++ int ret = 0; ++ struct oscap_list *variables = oscap_list_new(); ++ struct oscap_list *tasks = oscap_list_new(); ++ struct oscap_iterator *rules_to_fix_it = oscap_iterator_new(rules_to_fix); ++ while (oscap_iterator_has_more(rules_to_fix_it)) { ++ struct xccdf_rule *rule = (struct xccdf_rule*)oscap_iterator_next(rules_to_fix_it); ++ ret = _xccdf_policy_rule_generate_ansible_fix(policy, rule, sys, variables, tasks); ++ if (ret != 0) ++ break; ++ } ++ oscap_iterator_free(rules_to_fix_it); ++ ++ _write_text_to_fd(output_fd, " vars:\n"); ++ struct oscap_iterator *variables_it = oscap_iterator_new(variables); ++ while(oscap_iterator_has_more(variables_it)) { ++ char *var_line = (char *) oscap_iterator_next(variables_it); ++ _write_text_to_fd(output_fd, var_line); ++ } ++ oscap_iterator_free(variables_it); ++ oscap_list_free(variables, free); ++ ++ _write_text_to_fd(output_fd, " tasks:\n"); ++ struct oscap_iterator *tasks_it = oscap_iterator_new(tasks); ++ while(oscap_iterator_has_more(tasks_it)) { ++ char *var_line = strdup((char *) oscap_iterator_next(tasks_it)); ++ _write_remediation_to_fd_and_free(output_fd, sys, var_line); ++ } ++ oscap_iterator_free(tasks_it); ++ oscap_list_free(tasks, free); ++ return ret; ++} ++ ++static int _xccdf_policy_generate_fix_other(struct oscap_list *rules_to_fix, struct xccdf_policy *policy, const char *sys, int output_fd) ++{ ++ int ret = 0; ++ const unsigned int total = oscap_list_get_itemcount(rules_to_fix); ++ unsigned int current = 1; ++ struct oscap_iterator *rules_to_fix_it = oscap_iterator_new(rules_to_fix); ++ while (oscap_iterator_has_more(rules_to_fix_it)) { ++ struct xccdf_rule *rule = (struct xccdf_rule *) oscap_iterator_next(rules_to_fix_it); ++ ret = _xccdf_policy_rule_generate_fix(policy, rule, sys, output_fd, current++, total); ++ if (ret != 0) ++ break; ++ } ++ oscap_iterator_free(rules_to_fix_it); ++ return ret; ++} ++ + int xccdf_policy_generate_fix(struct xccdf_policy *policy, struct xccdf_result *result, const char *sys, int output_fd) + { + __attribute__nonnull__(policy); +@@ -979,48 +1029,12 @@ int xccdf_policy_generate_fix(struct xccdf_policy *policy, struct xccdf_result * + xccdf_rule_result_iterator_free(rr_it); + } + +- const unsigned int total = oscap_list_get_itemcount(rules_to_fix); +- +- // In Ansible we have to generate variables first and then tasks ++ // Ansible Playbooks are generated using a different function because ++ // in Ansible we have to generate variables first and then tasks + if (strcmp(sys, "urn:xccdf:fix:script:ansible") == 0) { +- struct oscap_list *variables = oscap_list_new(); +- struct oscap_list *tasks = oscap_list_new(); +- struct oscap_iterator *rules_to_fix_it = oscap_iterator_new(rules_to_fix); +- while (oscap_iterator_has_more(rules_to_fix_it)) { +- struct xccdf_rule *rule = (struct xccdf_rule*)oscap_iterator_next(rules_to_fix_it); +- ret = _xccdf_policy_rule_generate_ansible_fix(policy, rule, sys, variables, tasks); +- if (ret != 0) +- break; +- } +- oscap_iterator_free(rules_to_fix_it); +- +- _write_text_to_fd(output_fd, " vars:\n"); +- struct oscap_iterator *variables_it = oscap_iterator_new(variables); +- while(oscap_iterator_has_more(variables_it)) { +- char *var_line = (char *) oscap_iterator_next(variables_it); +- _write_text_to_fd(output_fd, var_line); +- } +- oscap_iterator_free(variables_it); +- oscap_list_free(variables, free); +- +- _write_text_to_fd(output_fd, " tasks:\n"); +- struct oscap_iterator *tasks_it = oscap_iterator_new(tasks); +- while(oscap_iterator_has_more(tasks_it)) { +- char *var_line = strdup((char *) oscap_iterator_next(tasks_it)); +- _write_remediation_to_fd_and_free(output_fd, sys, var_line); +- } +- oscap_iterator_free(tasks_it); +- oscap_list_free(tasks, free); ++ ret = _xccdf_policy_generate_fix_ansible(rules_to_fix, policy, sys, output_fd); + } else { +- unsigned int current = 1; +- struct oscap_iterator *rules_to_fix_it = oscap_iterator_new(rules_to_fix); +- while (oscap_iterator_has_more(rules_to_fix_it)) { +- struct xccdf_rule *rule = (struct xccdf_rule*)oscap_iterator_next(rules_to_fix_it); +- ret = _xccdf_policy_rule_generate_fix(policy, rule, sys, output_fd, current++, total); +- if (ret != 0) +- break; +- } +- oscap_iterator_free(rules_to_fix_it); ++ ret = _xccdf_policy_generate_fix_other(rules_to_fix, policy, sys, output_fd); + } + + oscap_list_free(rules_to_fix, NULL); + +From 2a11da0a6a1643669caadb72f1a217fafcfea6d5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= +Date: Wed, 28 Nov 2018 14:19:12 +0100 +Subject: [PATCH 4/4] Refactor: Remove duplicate code + +--- + src/XCCDF_POLICY/xccdf_policy_remediate.c | 23 +++++++---------------- + 1 file changed, 7 insertions(+), 16 deletions(-) + +diff --git a/src/XCCDF_POLICY/xccdf_policy_remediate.c b/src/XCCDF_POLICY/xccdf_policy_remediate.c +index bf36523dfd..15f9453938 100644 +--- a/src/XCCDF_POLICY/xccdf_policy_remediate.c ++++ b/src/XCCDF_POLICY/xccdf_policy_remediate.c +@@ -733,26 +733,17 @@ static int _xccdf_policy_rule_get_fix_text(struct xccdf_policy *policy, struct x + + static int _xccdf_policy_rule_generate_fix(struct xccdf_policy *policy, struct xccdf_rule *rule, const char *template, int output_fd, unsigned int current, unsigned int total) + { ++ int ret = _write_fix_header_to_fd(template, output_fd, rule, current, total); ++ if (ret != 0) { ++ return ret; ++ } + char *fix_text = NULL; +- int ret = _xccdf_policy_rule_get_fix_text(policy, rule, template, &fix_text); ++ ret = _xccdf_policy_rule_get_fix_text(policy, rule, template, &fix_text); + if (fix_text == NULL || ret != 0) { +- ret = _write_fix_header_to_fd(template, output_fd, rule, current, total); +- if (ret != 0) { +- return ret; +- } + ret = _write_fix_missing_warning_to_fd(template, output_fd, rule); +- if (ret != 0) { +- return ret; +- } +- ret = _write_fix_footer_to_fd(template, output_fd, rule); +- return ret; +- } +- ret = _write_fix_header_to_fd(template, output_fd, rule, current, total); +- if (ret != 0) { +- free(fix_text); +- return ret; ++ } else { ++ ret = _write_remediation_to_fd_and_free(output_fd, template, fix_text); + } +- ret = _write_remediation_to_fd_and_free(output_fd, template, fix_text); + if (ret != 0) { + return ret; + } diff --git a/SOURCES/openscap-1.3.1-improve_stig-viewer_output-PR_1319.patch b/SOURCES/openscap-1.3.1-improve_stig-viewer_output-PR_1319.patch new file mode 100644 index 0000000..70db972 --- /dev/null +++ b/SOURCES/openscap-1.3.1-improve_stig-viewer_output-PR_1319.patch @@ -0,0 +1,178 @@ +From 97ea3d5dd72e11996fac63bfdcf9691e1515f09a Mon Sep 17 00:00:00 2001 +From: Malte Kraus +Date: Fri, 29 Mar 2019 16:12:22 +0100 +Subject: [PATCH] improve --stig-viewer output when there is no 1:1 connection + between rules + +There can both be rules covering multiple STIG IDs (easy to handle: just +output the same result multiple times), and STIG IDs covered by more +than one rule. The DISA Stig Viewer seems to only consider the last +result for a given rule when it imports results, so when that last rule +happens to pass, that means the whole STIG will be marked as 'not a +finding'. To counteract that, this commit attempts to merge the results +in a sensible way - only if all tests passed, the STIG should be +marked as 'not a finding' in the way. +--- + src/XCCDF/result.c | 92 +++++++++++++++++++++++++++++++++--------- + src/XCCDF/xccdf_impl.h | 3 +- + 2 files changed, 76 insertions(+), 19 deletions(-) + +diff --git a/src/XCCDF/result.c b/src/XCCDF/result.c +index f389813d5..f0fa647b2 100644 +--- a/src/XCCDF/result.c ++++ b/src/XCCDF/result.c +@@ -924,13 +924,68 @@ void xccdf_result_to_dom(struct xccdf_result *result, xmlNode *result_node, xmlD + } + xccdf_setvalue_iterator_free(setvalues); + ++ struct oscap_htable *nodes_by_rule_id = oscap_htable_new(); ++ + struct xccdf_rule_result_iterator *rule_results = xccdf_result_get_rule_results(result); ++ if (use_stig_rule_id) { ++ while (xccdf_rule_result_iterator_has_more(rule_results)) { ++ struct xccdf_rule_result *rule_result = xccdf_rule_result_iterator_next(rule_results); ++ ++ const char *idref = xccdf_rule_result_get_idref(rule_result); ++ if (!idref) ++ continue; ++ ++ xccdf_test_result_type_t test_res = xccdf_rule_result_get_result(rule_result); ++ ++ const struct xccdf_item *item = xccdf_benchmark_get_member(associated_benchmark, XCCDF_RULE, idref); ++ if (!item) ++ continue; ++ ++ struct oscap_reference_iterator *references = xccdf_item_get_references(item); ++ while (oscap_reference_iterator_has_more(references)) { ++ struct oscap_reference *ref = oscap_reference_iterator_next(references); ++ if (strcmp(oscap_reference_get_href(ref), DISA_STIG_VIEWER_HREF) == 0) { ++ const char *stig_rule_id = oscap_reference_get_title(ref); ++ ++ xccdf_test_result_type_t other_res = (xccdf_test_result_type_t)oscap_htable_detach(nodes_by_rule_id, stig_rule_id); ++ xccdf_test_result_type_t wanted_res; ++ if (other_res == 0) { ++ wanted_res = test_res; ++ } else { ++ // if one test passed, and the other didn't, the other one should win ++ if (test_res == XCCDF_RESULT_PASS) { ++ wanted_res = other_res; ++ } else if (other_res == XCCDF_RESULT_PASS) { ++ wanted_res = test_res; ++ // if one had an error, that should win ++ } else if (test_res == XCCDF_RESULT_ERROR || other_res == XCCDF_RESULT_ERROR) { ++ wanted_res = XCCDF_RESULT_ERROR; ++ // next prio: failures ++ } else if (test_res == XCCDF_RESULT_FAIL || other_res == XCCDF_RESULT_FAIL) { ++ wanted_res = XCCDF_RESULT_FAIL; ++ // next prio: unknown ++ } else if (test_res == XCCDF_RESULT_UNKNOWN || other_res == XCCDF_RESULT_UNKNOWN) { ++ wanted_res = XCCDF_RESULT_UNKNOWN; ++ // otherwise, just pick the lower one (more or less arbitrarily) ++ } else { ++ wanted_res = (test_res < other_res) ? test_res : other_res; ++ } ++ } ++ oscap_htable_add(nodes_by_rule_id, stig_rule_id, (void*)wanted_res); ++ } ++ } ++ oscap_reference_iterator_free(references); ++ } ++ xccdf_rule_result_iterator_reset(rule_results); ++ } + while (xccdf_rule_result_iterator_has_more(rule_results)) { + struct xccdf_rule_result *rule_result = xccdf_rule_result_iterator_next(rule_results); +- xccdf_rule_result_to_dom(rule_result, doc, result_node, version_info, associated_benchmark, use_stig_rule_id); ++ xccdf_rule_result_to_dom(rule_result, doc, result_node, version_info, associated_benchmark, use_stig_rule_id, nodes_by_rule_id); + } + xccdf_rule_result_iterator_free(rule_results); + ++ oscap_htable_free0(nodes_by_rule_id); ++ + struct xccdf_score_iterator *scores = xccdf_result_get_scores(result); + while (xccdf_score_iterator_has_more(scores)) { + struct xccdf_score *score = xccdf_score_iterator_next(scores); +@@ -1081,36 +1136,40 @@ xmlNode *xccdf_target_identifier_to_dom(const struct xccdf_target_identifier *ti + } + } + +-xmlNode *xccdf_rule_result_to_dom(struct xccdf_rule_result *result, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info, struct xccdf_benchmark *benchmark, bool use_stig_rule_id) ++static void _xccdf_rule_result_to_dom_idref(struct xccdf_rule_result *result, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info, struct xccdf_benchmark *benchmark, const char *idref); ++void xccdf_rule_result_to_dom(struct xccdf_rule_result *result, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info, struct xccdf_benchmark *benchmark, bool use_stig_rule_id, struct oscap_htable *nodes_by_rule_id) + { + const char *idref = xccdf_rule_result_get_idref(result); + if (use_stig_rule_id) { + // Don't output rules with no stig ids + if (!idref || !benchmark) +- return NULL; ++ return; + +- struct xccdf_item *item = xccdf_benchmark_get_member(benchmark, XCCDF_RULE, idref); ++ const struct xccdf_item *item = xccdf_benchmark_get_member(benchmark, XCCDF_RULE, idref); + if (!item) +- return NULL; ++ return; + +- const char *stig_rule_id = NULL; +- struct oscap_reference_iterator *references = xccdf_item_get_references(XRULE(item)); ++ struct oscap_reference_iterator *references = xccdf_item_get_references(item); + while (oscap_reference_iterator_has_more(references)) { + struct oscap_reference *ref = oscap_reference_iterator_next(references); + if (strcmp(oscap_reference_get_href(ref), DISA_STIG_VIEWER_HREF[0]) == 0 || + strcmp(oscap_reference_get_href(ref), DISA_STIG_VIEWER_HREF[1]) == 0) { +- stig_rule_id = oscap_reference_get_title(ref); +- break; ++ const char *stig_rule_id = oscap_reference_get_title(ref); ++ ++ xccdf_test_result_type_t expected_res = (xccdf_test_result_type_t)oscap_htable_get(nodes_by_rule_id, stig_rule_id); ++ xccdf_test_result_type_t test_res = xccdf_rule_result_get_result(result); ++ if (expected_res == test_res) { ++ oscap_htable_detach(nodes_by_rule_id, stig_rule_id); ++ _xccdf_rule_result_to_dom_idref(result, doc, parent, version_info, benchmark, stig_rule_id); ++ } + } + } + oscap_reference_iterator_free(references); +- +- if (!stig_rule_id) +- return NULL; +- +- idref = stig_rule_id; ++ } else { ++ _xccdf_rule_result_to_dom_idref(result, doc, parent, version_info, benchmark, idref); + } +- ++} ++static void _xccdf_rule_result_to_dom_idref(struct xccdf_rule_result *result, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info, struct xccdf_benchmark *benchmark, const char *idref) { + xmlNs *ns_xccdf = lookup_xccdf_ns(doc, parent, version_info); + + xmlNode *result_node = xmlNewTextChild(parent, ns_xccdf, BAD_CAST "rule-result", NULL); +@@ -1203,8 +1262,6 @@ xmlNode *xccdf_rule_result_to_dom(struct xccdf_rule_result *result, xmlDoc *doc, + xccdf_check_to_dom(check, doc, result_node, version_info); + } + xccdf_check_iterator_free(checks); +- +- return result_node; + } + + bool xccdf_rule_result_override(struct xccdf_rule_result *rule_result, xccdf_test_result_type_t new_result, const char *waiver_time, const char *authority, struct oscap_text *remark) +diff --git a/src/XCCDF/xccdf_impl.h b/src/XCCDF/xccdf_impl.h +index b87e7f170..05e0edd85 100644 +--- a/src/XCCDF/xccdf_impl.h ++++ b/src/XCCDF/xccdf_impl.h +@@ -27,6 +27,7 @@ + #include + #include + #include ++#include + + OSCAP_HIDDEN_START; + +@@ -49,7 +50,7 @@ void xccdf_group_to_dom(struct xccdf_group *group, xmlNode *group_node, xmlDoc * + void xccdf_profile_to_dom(struct xccdf_profile *profile, xmlNode *profile_node, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info *version_info); + void xccdf_result_to_dom(struct xccdf_result *result, xmlNode *result_node, xmlDoc *doc, xmlNode *parent, bool use_stig_rule_id); + xmlNode *xccdf_target_identifier_to_dom(const struct xccdf_target_identifier *ti, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info); +-xmlNode *xccdf_rule_result_to_dom(struct xccdf_rule_result *result, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info, struct xccdf_benchmark *benchmark, bool use_stig_rule_id); ++void xccdf_rule_result_to_dom(struct xccdf_rule_result *result, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info, struct xccdf_benchmark *benchmark, bool use_stig_rule_id, struct oscap_htable *nodes_by_rule_id); + xmlNode *xccdf_ident_to_dom(struct xccdf_ident *ident, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info); + xmlNode *xccdf_setvalue_to_dom(struct xccdf_setvalue *setvalue, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info); + xmlNode *xccdf_override_to_dom(struct xccdf_override *override, xmlDoc *doc, xmlNode *parent, const struct xccdf_version_info* version_info); diff --git a/SOURCES/openscap-1.3.2-show_check_identifiers_at_multi-check_rules-PR_1426.patch b/SOURCES/openscap-1.3.2-show_check_identifiers_at_multi-check_rules-PR_1426.patch new file mode 100644 index 0000000..44a1de6 --- /dev/null +++ b/SOURCES/openscap-1.3.2-show_check_identifiers_at_multi-check_rules-PR_1426.patch @@ -0,0 +1,828 @@ +From f90f02bfa2d31f56b25238d750c4746d964aea42 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= +Date: Tue, 26 Nov 2019 13:52:36 +0100 +Subject: [PATCH 1/2] Show check identifiers at multi-check rules + +Shows OVAL Definition ID and Title when an XCCDF rule uses +multi-check='true'. The multi-check is used in rule +security_patches_up_to_date in SCAP 1.3 datastreams. It wasn't possible +to see which vulnerabilities have been found because all the checks have +the same XCCDF rule ID. + +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1771438 + +Fixes: #1320 +--- + src/OVAL/oval_agent.c | 35 ++++++++++---- + src/XCCDF_POLICY/public/xccdf_policy.h | 18 ++++++- + src/XCCDF_POLICY/xccdf_policy.c | 48 +++++++++++-------- + src/XCCDF_POLICY/xccdf_policy_engine.c | 4 +- + src/XCCDF_POLICY/xccdf_policy_engine_priv.h | 2 +- + src/XCCDF_POLICY/xccdf_policy_model_priv.h | 1 + + ...eck_content_ref_without_name_attr.oval.xml | 4 +- + .../test_xccdf_check_multi_check2.sh | 6 +++ + .../test_xccdf_check_multi_check2.xccdf.xml | 1 + + utils/oscap-xccdf.c | 8 ++++ + 10 files changed, 92 insertions(+), 35 deletions(-) + +diff --git a/src/OVAL/oval_agent.c b/src/OVAL/oval_agent.c +index a962fe379..986295bf6 100644 +--- a/src/OVAL/oval_agent.c ++++ b/src/OVAL/oval_agent.c +@@ -593,19 +593,34 @@ _oval_agent_list_definitions(void *usr, xccdf_policy_engine_query_t query_type, + { + __attribute__nonnull__(usr); + struct oval_agent_session *sess = (struct oval_agent_session *) usr; +- if (query_type != POLICY_ENGINE_QUERY_NAMES_FOR_HREF || (query_data != NULL && strcmp(sess->filename, (const char *) query_data))) ++ if (query_data != NULL && strcmp(sess->filename, (const char *) query_data)) { + return NULL; +- struct oval_definition_iterator *iterator = oval_definition_model_get_definitions(sess->def_model); +- struct oscap_stringlist *result = oscap_stringlist_new(); +- struct oval_definition *oval_def; +- +- while (oval_definition_iterator_has_more(iterator)) { +- oval_def = oval_definition_iterator_next(iterator); +- oscap_stringlist_add_string(result, oval_definition_get_id(oval_def)); + } ++ if (query_type == POLICY_ENGINE_QUERY_NAMES_FOR_HREF) { ++ struct oval_definition_iterator *iterator = oval_definition_model_get_definitions(sess->def_model); ++ struct oscap_stringlist *result = oscap_stringlist_new(); ++ ++ while (oval_definition_iterator_has_more(iterator)) { ++ struct oval_definition *oval_def = oval_definition_iterator_next(iterator); ++ oscap_stringlist_add_string(result, oval_definition_get_id(oval_def)); ++ } ++ ++ oval_definition_iterator_free(iterator); ++ return result; ++ } else if (query_type == POLICY_ENGINE_QUERY_OVAL_DEFS_FOR_HREF) { ++ struct oval_definition_iterator *iterator = oval_definition_model_get_definitions(sess->def_model); ++ struct oscap_list *result = oscap_list_new(); + +- oval_definition_iterator_free(iterator); +- return result; ++ while (oval_definition_iterator_has_more(iterator)) { ++ struct oval_definition *oval_def = oval_definition_iterator_next(iterator); ++ oscap_list_add(result, oval_def); ++ } ++ ++ oval_definition_iterator_free(iterator); ++ return result; ++ } else { ++ return NULL; ++ } + } + + bool xccdf_policy_model_register_engine_oval(struct xccdf_policy_model * model, struct oval_agent_session * usr) +diff --git a/src/SCE/Makefile.am b/src/SCE/Makefile.am +index 0c90c6da0..5849cf1a0 100644 +--- a/src/SCE/Makefile.am ++++ b/src/SCE/Makefile.am +@@ -12,7 +12,8 @@ AM_CPPFLAGS = @xml2_CFLAGS@ \ + -I$(top_srcdir)/src/source/public \ + -I$(top_srcdir)/src/XCCDF_POLICY/public \ + -I$(top_srcdir)/src/XCCDF/public \ +- -I$(top_srcdir)/src/CPE/public ++ -I$(top_srcdir)/src/CPE/public \ ++ -I$(top_srcdir)/src/OVAL/public + + AM_LDFLAGS = @xml2_LIBS@ + +diff --git a/src/XCCDF_POLICY/public/xccdf_policy.h b/src/XCCDF_POLICY/public/xccdf_policy.h +index aa2525e2b..5e85c476c 100644 +--- a/src/XCCDF_POLICY/public/xccdf_policy.h ++++ b/src/XCCDF_POLICY/public/xccdf_policy.h +@@ -35,6 +35,7 @@ + #include + #include + #include ++#include "oval_definitions.h" + + /** + * @struct xccdf_policy_model +@@ -69,6 +70,7 @@ struct xccdf_policy_iterator; + */ + typedef enum { + POLICY_ENGINE_QUERY_NAMES_FOR_HREF = 1, /// Considering xccdf:check-content-ref, what are possible @name attributes for given href? ++ POLICY_ENGINE_QUERY_OVAL_DEFS_FOR_HREF = 2, /// Considering xccdf:check-content-ref, what are OVAL definitions for given href? + } xccdf_policy_engine_query_t; + + /** +@@ -80,9 +82,11 @@ typedef enum { + * is always user data as registered. Second argument defines the query. Third argument is + * dependent on query and defined as follows: + * - (const char *)href -- for POLICY_ENGINE_QUERY_NAMES_FOR_HREF ++ * - (const char *)href -- for POLICY_ENGINE_QUERY_OVAL_DEFS_FOR_HREF + * + * Expected return type depends also on query as follows: +- * - (struct oscap_stringlists *) -- for POLICY_ENGINE_QUERY_NAMES_FOR_HREF ++ * - (struct oscap_stringlist *) -- for POLICY_ENGINE_QUERY_NAMES_FOR_HREF ++ * - (struct oscap_list *) -- for POLICY_ENGINE_QUERY_OVAL_DEFS_FOR_HREF + * - NULL shall be returned if the function doesn't understand the query. + */ + typedef void *(*xccdf_policy_engine_query_fn) (void *, xccdf_policy_engine_query_t, void *); +@@ -289,6 +293,19 @@ typedef int (*policy_reporter_start)(struct xccdf_rule *, void *); + */ + bool xccdf_policy_model_register_start_callback(struct xccdf_policy_model * model, policy_reporter_start func, void * usr); + ++typedef int (*policy_reporter_multicheck)(struct oval_definition*, void *); ++/** ++ * Function to register callback for checking system that will be called ++ * DURING each rule evaluation if rule sets multi-check="true". ++ * @param model XCCDF Policy Model ++ * @param func Callback - pointer to function called by XCCDF Policy system when rule parsed ++ * @param usr optional parameter for passing user data to callback ++ * @memberof xccdf_policy_model ++ * @return true if callback registered successfully, false otherwise ++ */ ++bool xccdf_policy_model_register_multicheck_callback(struct xccdf_policy_model *model, policy_reporter_multicheck func, void *usr); ++ ++ + /************************************************************/ + /** + * @name Getters +diff --git a/src/XCCDF_POLICY/xccdf_policy.c b/src/XCCDF_POLICY/xccdf_policy.c +index b406d7f59..079395c85 100644 +--- a/src/XCCDF_POLICY/xccdf_policy.c ++++ b/src/XCCDF_POLICY/xccdf_policy.c +@@ -378,20 +378,19 @@ xccdf_policy_evaluate_cb(struct xccdf_policy * policy, const char * sysname, con + } + + /** +- * Find all posible names for given check-content-ref/@href, considering also the check/@system. +- * This is usefull for multi-check="true" feature. +- * @return list of names (even empty) if the given href found, NULL otherwise. ++ * Find all possible names for given check-content-ref/@href, considering also the check/@system. ++ * This is useful for multi-check="true" feature. ++ * @return list of OVAL definitions if the given href found, NULL otherwise. + */ +-static struct oscap_stringlist * +-_xccdf_policy_get_namesfor_href(struct xccdf_policy *policy, const char *sysname, const char *href) ++static struct oscap_list *_xccdf_policy_get_oval_definitions_for_href(struct xccdf_policy *policy, const char *sysname, const char *href) + { + struct oscap_iterator *cb_it = _xccdf_policy_get_engines_by_sysname(policy, sysname); +- struct oscap_stringlist *result = NULL; ++ struct oscap_list *result = NULL; + while (oscap_iterator_has_more(cb_it) && result == NULL) { + struct xccdf_policy_engine *engine = (struct xccdf_policy_engine *) oscap_iterator_next(cb_it); + if (engine == NULL) + break; +- result = xccdf_policy_engine_query(engine, POLICY_ENGINE_QUERY_NAMES_FOR_HREF, (void *) href); ++ result = xccdf_policy_engine_query(engine, POLICY_ENGINE_QUERY_OVAL_DEFS_FOR_HREF, (void *) href); + } + oscap_iterator_free(cb_it); + return result; +@@ -1047,24 +1046,27 @@ _xccdf_policy_rule_evaluate(struct xccdf_policy * policy, const struct xccdf_rul + + if (content_name == NULL && xccdf_check_get_multicheck(check)) { + // parent element is Rule, @multi-check is required +- struct oscap_stringlist *names = _xccdf_policy_get_namesfor_href(policy, system_name, href); +- if (names != NULL) { ++ struct oscap_list *oval_definition_list = _xccdf_policy_get_oval_definitions_for_href(policy, system_name, href); ++ if (oval_definition_list != NULL) { + // multi-check is supported by checking-engine +- struct oscap_string_iterator *name_it = oscap_stringlist_get_strings(names); +- if (!oscap_string_iterator_has_more(name_it)) { ++ struct oscap_iterator *oval_definition_iterator = oscap_iterator_new(oval_definition_list); ++ if (!oscap_iterator_has_more(oval_definition_iterator)) { + // Super special case when oval file contains no definitions + // thus multi-check shall yield zero rule-results. + report = _xccdf_policy_report_rule_result(policy, result, rule, check, XCCDF_RESULT_UNKNOWN, "No definitions found for @multi-check."); +- oscap_string_iterator_free(name_it); +- oscap_stringlist_free(names); ++ oscap_iterator_free(oval_definition_iterator); ++ oscap_list_free(oval_definition_list, NULL); + xccdf_check_content_ref_iterator_free(content_it); + oscap_list_free(bindings, (oscap_destruct_func) xccdf_value_binding_free); + return report; + } +- while (oscap_string_iterator_has_more(name_it)) { +- const char *name = oscap_string_iterator_next(name_it); ++ while (oscap_iterator_has_more(oval_definition_iterator)) { ++ struct oval_definition *oval_definition = oscap_iterator_next(oval_definition_iterator); ++ if ((report = xccdf_policy_report_cb(policy, XCCDF_POLICY_OUTCB_MULTICHECK, (void *) oval_definition)) != 0) { ++ break; ++ } + struct xccdf_check *cloned_check = xccdf_check_clone(check); +- xccdf_check_inject_content_ref(cloned_check, content, name); ++ xccdf_check_inject_content_ref(cloned_check, content, oval_definition_get_id(oval_definition)); + int inner_ret = xccdf_policy_check_evaluate(policy, cloned_check); + if (inner_ret == -1) { + xccdf_check_free(cloned_check); +@@ -1073,12 +1075,13 @@ _xccdf_policy_rule_evaluate(struct xccdf_policy * policy, const struct xccdf_rul + } + if ((report = _xccdf_policy_report_rule_result(policy, result, rule, cloned_check, inner_ret, NULL)) != 0) + break; +- if (oscap_string_iterator_has_more(name_it)) ++ if (oscap_iterator_has_more(oval_definition_iterator)) { + if ((report = xccdf_policy_report_cb(policy, XCCDF_POLICY_OUTCB_START, (void *) rule)) != 0) + break; ++ } + } +- oscap_string_iterator_free(name_it); +- oscap_stringlist_free(names); ++ oscap_iterator_free(oval_definition_iterator); ++ oscap_list_free(oval_definition_list, NULL); + xccdf_check_content_ref_iterator_free(content_it); + oscap_list_free(bindings, (oscap_destruct_func) xccdf_value_binding_free); + xccdf_check_free(check); +@@ -1629,6 +1632,13 @@ bool xccdf_policy_model_register_output_callback(struct xccdf_policy_model * mod + return oscap_list_add(model->callbacks, reporter); + } + ++bool xccdf_policy_model_register_multicheck_callback(struct xccdf_policy_model *model, policy_reporter_multicheck func, void *usr) ++{ ++ __attribute__nonnull__(model); ++ struct reporter *reporter = reporter_new(XCCDF_POLICY_OUTCB_MULTICHECK, func, usr); ++ return oscap_list_add(model->callbacks, reporter); ++} ++ + struct xccdf_result * xccdf_policy_get_result_by_id(struct xccdf_policy * policy, const char * id) { + + struct xccdf_result_iterator * result_it; +diff --git a/src/XCCDF_POLICY/xccdf_policy_engine.c b/src/XCCDF_POLICY/xccdf_policy_engine.c +index 5e0a2b6b0..3529dd1b6 100644 +--- a/src/XCCDF_POLICY/xccdf_policy_engine.c ++++ b/src/XCCDF_POLICY/xccdf_policy_engine.c +@@ -69,9 +69,9 @@ xccdf_test_result_type_t xccdf_policy_engine_eval(struct xccdf_policy_engine *en + return ret; + } + +-struct oscap_stringlist *xccdf_policy_engine_query(struct xccdf_policy_engine *engine, xccdf_policy_engine_query_t query_type, void *query_data) ++struct oscap_list *xccdf_policy_engine_query(struct xccdf_policy_engine *engine, xccdf_policy_engine_query_t query_type, void *query_data) + { + if (engine->query_fn == NULL) + return NULL; +- return (struct oscap_stringlist *) engine->query_fn(engine->usr, query_type, query_data); ++ return (struct oscap_list *) engine->query_fn(engine->usr, query_type, query_data); + } +diff --git a/src/XCCDF_POLICY/xccdf_policy_engine_priv.h b/src/XCCDF_POLICY/xccdf_policy_engine_priv.h +index cdcb49613..bcf758b2e 100644 +--- a/src/XCCDF_POLICY/xccdf_policy_engine_priv.h ++++ b/src/XCCDF_POLICY/xccdf_policy_engine_priv.h +@@ -75,7 +75,7 @@ xccdf_test_result_type_t xccdf_policy_engine_eval(struct xccdf_policy_engine *en + * @param query_data Additional data for the checking engine query. + * @returns list of query results + */ +-struct oscap_stringlist *xccdf_policy_engine_query(struct xccdf_policy_engine *engine, xccdf_policy_engine_query_t query_type, void *query_data); ++struct oscap_list *xccdf_policy_engine_query(struct xccdf_policy_engine *engine, xccdf_policy_engine_query_t query_type, void *query_data); + + OSCAP_HIDDEN_END; + +diff --git a/src/XCCDF_POLICY/xccdf_policy_model_priv.h b/src/XCCDF_POLICY/xccdf_policy_model_priv.h +index c5223a7b8..8f503ef46 100644 +--- a/src/XCCDF_POLICY/xccdf_policy_model_priv.h ++++ b/src/XCCDF_POLICY/xccdf_policy_model_priv.h +@@ -33,6 +33,7 @@ OSCAP_HIDDEN_START; + + #define XCCDF_POLICY_OUTCB_START "urn:xccdf:system:callback:start" + #define XCCDF_POLICY_OUTCB_END "urn:xccdf:system:callback:output" ++#define XCCDF_POLICY_OUTCB_MULTICHECK "urn:xccdf:system:callback:multicheck" + + /** + * Remove checking engines with given system from xccdf_policy_model +diff --git a/tests/API/XCCDF/unittests/test_xccdf_check_content_ref_without_name_attr.oval.xml b/tests/API/XCCDF/unittests/test_xccdf_check_content_ref_without_name_attr.oval.xml +index 6d4868686..6efdb2f1c 100644 +--- a/tests/API/XCCDF/unittests/test_xccdf_check_content_ref_without_name_attr.oval.xml ++++ b/tests/API/XCCDF/unittests/test_xccdf_check_content_ref_without_name_attr.oval.xml +@@ -17,11 +17,11 @@ + + + +- PASSBla. ++ DEFINITION_1_TITLE_EXPECTED_PASSBla. + + + +- FAILBla. ++ DEFINITION_2_TITLE_EXPECTED_FAILBla. + + + +diff --git a/tests/API/XCCDF/unittests/test_xccdf_check_multi_check2.sh b/tests/API/XCCDF/unittests/test_xccdf_check_multi_check2.sh +index d5991aa0f..2c45ad0a3 100755 +--- a/tests/API/XCCDF/unittests/test_xccdf_check_multi_check2.sh ++++ b/tests/API/XCCDF/unittests/test_xccdf_check_multi_check2.sh +@@ -17,6 +17,12 @@ echo "Result file = $result" + [ -f $stderr ]; [ ! -s $stderr ]; rm $stderr + grep '^Result.*pass$' $stdout + grep '^Result.*fail$' $stdout ++[ $(grep -c '^Rule.*xccdf_moc.elpmaxe.www_rule_1' $stdout) == 2 ] ++[ $(grep -c '^Title.*The only rule in this benchmark' $stdout) == 2 ] ++grep '^OVAL Definition ID.*oval:moc.elpmaxe.www:def:1$' $stdout ++grep '^OVAL Definition Title.*DEFINITION_1_TITLE_EXPECTED_PASS$' $stdout ++grep '^OVAL Definition ID.*oval:moc.elpmaxe.www:def:2$' $stdout ++grep '^OVAL Definition Title.*DEFINITION_2_TITLE_EXPECTED_FAIL$' $stdout + rm $stdout + + $OSCAP xccdf validate-xml $result +diff --git a/tests/API/XCCDF/unittests/test_xccdf_check_multi_check2.xccdf.xml b/tests/API/XCCDF/unittests/test_xccdf_check_multi_check2.xccdf.xml +index 44dc4de49..1c87b9d51 100644 +--- a/tests/API/XCCDF/unittests/test_xccdf_check_multi_check2.xccdf.xml ++++ b/tests/API/XCCDF/unittests/test_xccdf_check_multi_check2.xccdf.xml +@@ -3,6 +3,7 @@ + incomplete + 1.0 + ++ The only rule in this benchmark + + + +diff --git a/utils/oscap-xccdf.c b/utils/oscap-xccdf.c +index 19ffc0859..59cd7bcf2 100644 +--- a/utils/oscap-xccdf.c ++++ b/utils/oscap-xccdf.c +@@ -392,6 +392,13 @@ static int callback_scr_result_progress(struct xccdf_rule_result *rule_result, v + return 0; + } + ++static int callback_scr_multicheck(struct oval_definition *definition, void *arg) ++{ ++ printf("OVAL Definition ID\t%s\n", oval_definition_get_id(definition)); ++ printf("OVAL Definition Title\t%s\n", oval_definition_get_title(definition)); ++ return 0; ++} ++ + /* + * Send XCCDF Rule Results info message to syslog + * +@@ -434,6 +441,7 @@ static void _register_progress_callback(struct xccdf_session *session, bool prog + xccdf_policy_model_register_start_callback(policy_model, callback_scr_rule, + (void *) xccdf_session_get_xccdf_policy(session)); + xccdf_policy_model_register_output_callback(policy_model, callback_scr_result, NULL); ++ xccdf_policy_model_register_multicheck_callback(policy_model, callback_scr_multicheck, NULL); + } + /* xccdf_policy_model_register_output_callback(policy_model, callback_syslog_result, NULL); */ + } +diff --git a/xsl/xccdf-report-impl.xsl b/xsl/xccdf-report-impl.xsl +index cc76a56cc..ba07b0a90 100644 +--- a/xsl/xccdf-report-impl.xsl ++++ b/xsl/xccdf-report-impl.xsl +@@ -346,19 +346,14 @@ Authors: + } + + +- +- +- +- +- +- +- +- +- +- +- +- +- ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -371,18 +366,26 @@ Authors: + rule-overview-leaf rule-overview-leaf- rule-overview-needs-attention + + +- +- +- +- +- +- ++ ++ ++ ++ ++ ++ ++ + +- ++ ++ () ++ ++ +  waived + + +- ++ ++ ++ ++ ++ + + + +@@ -394,7 +397,31 @@ Authors: + + + +- ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -718,179 +745,212 @@ Authors: + + + +- +- ++ ++ + ++ + ++ + +- +- +- +- +-
+-
+- This allows OpenSCAP JS to search the report rules +- +- +- +- +- +- +- +- +- +- +- +- +- +-
+-
+-

+- ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +- +- +- +- +- +- +- +- +- +- +- +- +- +-
Rule ID
Result ++ ++ ++ ++ ++ ++
++ ++
++
Multi-check rule ++ ++ yes ++ no ++ ++
OVAL Definition ID ++ ++
Time
Severity
Identifiers and References ++ ++ + +- +- + +- +- +-
+- +- +- +- +- ++ ++ +- +- +- +- +- +- +- +- ++ ++ ++ ++ ++ ++ ++ ++ ++ +- +- +- ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +- +- +- +- +- +- +- +- +- +- ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++
Rule ID
Result +- +- +- +- +- +-
+- ++
++ ++ ++ ++
++ This rule has been waived by at . ++
++ ++
++ ++ The previous result was   . ++ +
+-
Time
Severity
Identifiers and References +- +- +- +- +-
+- +- +- +-
+- This rule has been waived by at . +-
+- +-
+- +- The previous result was   . +- +-
+-
+-
Description
+-

+- +- ++ ++

Description
++

++ ++ ++ ++ ++ ++

++
Rationale
++

++ ++ ++ ++ ++ ++

++
Warnings ++ ++
++
++ warning  ++ + + + +-

+-
Rationale
+-

+- +- ++

++ ++ ++
++ ++
++ Evaluation messages ++
++
++ ++ ++   ++ ++

+                                     
+                                     
+-                                
+-                            

+-
Warnings +- +-
+-
+- warning  +- +- +- +- +-
+-
++ +
+-
++ ++ ++ ++ ++ ++ ++
++ ++ ++ ++ ++ + +- ++
++ + +- +-
+- +-
+- Evaluation messages +-
+-
+- +- +-   +- +-

+-                                            
+-                                            
+-                                        
+-
+-
+-
+-
+- +- +- +- +- +- +-
+- +- +- +- +- +- +-
+-

+-
+-
++ ++ ++ ++ ++ ++ ++ ++ ++ ++
++
++ This allows OpenSCAP JS to search the report rules ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++
++
++

++ ++ ++ ++ ++ ++

++
++
++ ++ ++ ++ ++ ++ ++
++
++
++
+
+ + diff --git a/SOURCES/openscap-1.3.4-fix_memory_leak_probe-file-PR_1594.patch b/SOURCES/openscap-1.3.4-fix_memory_leak_probe-file-PR_1594.patch new file mode 100644 index 0000000..d30bf38 --- /dev/null +++ b/SOURCES/openscap-1.3.4-fix_memory_leak_probe-file-PR_1594.patch @@ -0,0 +1,73 @@ +From 2c270e796ecc82567f5533bf1da35100b6eb4ee3 Mon Sep 17 00:00:00 2001 +From: Evgeny Kolesnikov +Date: Mon, 7 Sep 2020 08:03:38 +0200 +Subject: [PATCH] Fix memory leak + +It seems that `SEXP_listref_first` result is not a good candidate +to be (re)placed into a list. +--- + src/OVAL/probes/probe-api.c | 29 +++++++++++++---------------- + 1 file changed, 13 insertions(+), 16 deletions(-) + +diff --git a/src/OVAL/probes/probe-api.c b/src/OVAL/probes/probe-api.c +index 3bae38ebc..417138c3b 100644 +--- a/src/OVAL/probes/probe-api.c ++++ b/src/OVAL/probes/probe-api.c +@@ -130,6 +130,11 @@ SEXP_t *probe_item_attr_add(SEXP_t * item, const char *name, SEXP_t * val) + { + SEXP_t *n_ref, *ns; + ++ if (val == NULL) ++ ns = SEXP_string_new(name, strlen(name)); ++ else ++ ns = SEXP_string_newf(":%s", name); ++ + n_ref = SEXP_listref_first(item); + + if (SEXP_listp(n_ref)) { +@@ -137,13 +142,7 @@ SEXP_t *probe_item_attr_add(SEXP_t * item, const char *name, SEXP_t * val) + * There are already some attributes. + * Just add the new to the list. + */ +- if (val == NULL) +- ns = SEXP_string_new(name, strlen(name)); +- else +- ns = SEXP_string_newf(":%s", name); +- + SEXP_list_add(n_ref, ns); +- SEXP_free(ns); + + if (val != NULL) + SEXP_list_add(n_ref, val); +@@ -155,23 +154,21 @@ SEXP_t *probe_item_attr_add(SEXP_t * item, const char *name, SEXP_t * val) + * S-exp and the attribute. + */ + SEXP_t *nl; ++ SEXP_t *x_ref; + +- if (val == NULL) +- ns = SEXP_string_new(name, strlen(name)); +- else +- ns = SEXP_string_newf(":%s", name); +- +- nl = SEXP_list_new(n_ref, ns, val, NULL); ++ x_ref = SEXP_list_nth(item, 1); ++ nl = SEXP_list_new(x_ref, ns, val, NULL); ++ SEXP_free(x_ref); + +- SEXP_free(n_ref); +- SEXP_free(ns); +- +- n_ref = SEXP_list_replace(item, 1, nl); ++ x_ref = SEXP_list_replace(item, 1, nl); ++ SEXP_free(x_ref); + SEXP_free(nl); + } + + SEXP_free(n_ref); + ++ SEXP_free(ns); ++ + return (val); + } + diff --git a/SOURCES/openscap-1.3.4-fix_memory_leaks-PR_1595.patch b/SOURCES/openscap-1.3.4-fix_memory_leaks-PR_1595.patch new file mode 100644 index 0000000..ee4dc36 --- /dev/null +++ b/SOURCES/openscap-1.3.4-fix_memory_leaks-PR_1595.patch @@ -0,0 +1,150 @@ +From e3d754aad633f2f2ad2735a2eea63f84258fbea7 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= +Date: Mon, 7 Sep 2020 13:57:17 +0200 +Subject: [PATCH 1/8] Plug a memory leak + +When the key is already present in the hash table the values +are not inserted to the hash table therefore they are not +freed when the hash table is destroyed. + +Addressing: +6 bytes in 1 blocks are definitely lost in loss record 2 of 234 + at 0x483A809: malloc (vg_replace_malloc.c:307) + by 0x489F5DB: oscap_vsprintf (util.c:146) + by 0x489F6DD: oscap_sprintf (util.c:160) + by 0x4921E77: _xccdf_session_export_oval_result_file (xccdf_session.c:1493) + by 0x492209E: _build_oval_result_sources (xccdf_session.c:1538) + by 0x492210F: xccdf_session_export_oval (xccdf_session.c:1552) + by 0x410F77: app_evaluate_xccdf (oscap-xccdf.c:592) + by 0x40FFF8: oscap_module_call (oscap-tool.c:292) + by 0x4104C5: oscap_module_process (oscap-tool.c:382) + by 0x4130A9: main (oscap.c:88) + +45 bytes in 1 blocks are definitely lost in loss record 124 of 234 + at 0x483A809: malloc (vg_replace_malloc.c:307) + by 0x52262DE: strdup (in /usr/lib64/libc-2.31.so) + by 0x491E383: oscap_strdup (util.h:312) + by 0x4921E97: _xccdf_session_export_oval_result_file (xccdf_session.c:1495) + by 0x492209E: _build_oval_result_sources (xccdf_session.c:1538) + by 0x492210F: xccdf_session_export_oval (xccdf_session.c:1552) + by 0x410F77: app_evaluate_xccdf (oscap-xccdf.c:592) + by 0x40FFF8: oscap_module_call (oscap-tool.c:292) + by 0x4104C5: oscap_module_process (oscap-tool.c:382) + by 0x4130A9: main (oscap.c:88) +--- + src/XCCDF/xccdf_session.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/src/XCCDF/xccdf_session.c b/src/XCCDF/xccdf_session.c +index 25257e5106..cc7ac57c56 100644 +--- a/src/XCCDF/xccdf_session.c ++++ b/src/XCCDF/xccdf_session.c +@@ -1493,8 +1493,12 @@ static char *_xccdf_session_export_oval_result_file(struct xccdf_session *sessio + char *report_id = oscap_sprintf("oval%d", counter++); + const char *original_name = oval_agent_get_filename(oval_session); + char *results_file_name = oscap_strdup(name); +- oscap_htable_add(session->oval.results_mapping, original_name, results_file_name); +- oscap_htable_add(session->oval.arf_report_mapping, original_name, report_id); ++ if (!oscap_htable_add(session->oval.results_mapping, original_name, results_file_name)){ ++ free(results_file_name); ++ } ++ if (!oscap_htable_add(session->oval.arf_report_mapping, original_name, report_id)) { ++ free(report_id); ++ }; + + /* validate OVAL Results */ + if (session->validate && session->full_validation) { + +From 0e35eb691667876d13af6baf0ce4bf73e77cc50c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= +Date: Mon, 7 Sep 2020 14:14:12 +0200 +Subject: [PATCH 2/8] Plug a memory leak + +Addressing: +147 bytes in 1 blocks are definitely lost in loss record 220 of 234 + at 0x483A809: malloc (vg_replace_malloc.c:307) + by 0x489F5DB: oscap_vsprintf (util.c:146) + by 0x489F6DD: oscap_sprintf (util.c:160) + by 0x48B9CFB: ds_rds_create_from_dom (rds.c:710) + by 0x48BA3C1: ds_rds_create_source (rds.c:829) + by 0x491E83F: xccdf_session_create_arf_source (xccdf_session.c:241) + by 0x4922496: xccdf_session_export_arf (xccdf_session.c:1630) + by 0x411110: app_evaluate_xccdf (oscap-xccdf.c:617) + by 0x40FFF8: oscap_module_call (oscap-tool.c:292) + by 0x4104C5: oscap_module_process (oscap-tool.c:382) + by 0x4130A9: main (oscap.c:88) + +147 bytes in 1 blocks are definitely lost in loss record 221 of 234 + at 0x483A809: malloc (vg_replace_malloc.c:307) + by 0x489F5DB: oscap_vsprintf (util.c:146) + by 0x489F6DD: oscap_sprintf (util.c:160) + by 0x48B9D17: ds_rds_create_from_dom (rds.c:711) + by 0x48BA3C1: ds_rds_create_source (rds.c:829) + by 0x491E83F: xccdf_session_create_arf_source (xccdf_session.c:241) + by 0x4922496: xccdf_session_export_arf (xccdf_session.c:1630) + by 0x411110: app_evaluate_xccdf (oscap-xccdf.c:617) + by 0x40FFF8: oscap_module_call (oscap-tool.c:292) + by 0x4104C5: oscap_module_process (oscap-tool.c:382) + by 0x4130A9: main (oscap.c:88) +--- + src/DS/rds.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/XCCDF/xccdf_session.c b/src/XCCDF/xccdf_session.c +index cc7ac57c56..2272c99d4f 100644 +--- a/src/XCCDF/xccdf_session.c ++++ b/src/XCCDF/xccdf_session.c +@@ -1324,8 +1324,10 @@ static int _build_xccdf_result_source(struct xccdf_session *session) + if (oscap_source_save_as(stig_result, NULL) != 0) { + oscap_seterr(OSCAP_EFAMILY_OSCAP, "Could not save file: %s", + oscap_source_readable_origin(stig_result)); ++ oscap_source_free(stig_result); + return -1; + } ++ oscap_source_free(stig_result); + } + + /* validate XCCDF Results */ + +From 47d6e7746a68da1376b6122af101471c64d000ea Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= +Date: Mon, 7 Sep 2020 15:52:41 +0200 +Subject: [PATCH 4/8] Plug a memory leak + +When the component wasn't registered successfully, we should free the oscap_source structure. + +Addressing: +59,044 (56 direct, 58,988 indirect) bytes in 1 blocks are definitely lost in loss record 252 of 252 + at 0x483CAE9: calloc (vg_replace_malloc.c:760) + by 0x48C0038: oscap_source_new_from_xmlDoc (oscap_source.c:136) + by 0x48BBF01: ds_sds_register_xmlDoc (sds.c:250) + by 0x48BBFD5: ds_sds_register_component (sds.c:271) + by 0x48BC0C5: ds_sds_dump_local_component (sds.c:298) + by 0x48BC3FD: ds_sds_dump_component_by_href (sds.c:384) + by 0x48BC6AA: ds_sds_dump_component_ref_as (sds.c:451) + by 0x48BC969: ds_sds_dump_component_ref_as (sds.c:513) + by 0x48BCA69: ds_sds_dump_component_ref (sds.c:540) + by 0x48B7F74: ds_sds_session_register_component_with_dependencies (ds_sds_session.c:308) + by 0x491FD48: xccdf_session_load_cpe (xccdf_session.c:801) + by 0x491F43E: xccdf_session_load (xccdf_session.c:594) +--- + src/DS/sds.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/DS/sds.c b/src/DS/sds.c +index d901b30dc9..6a4f6847fc 100644 +--- a/src/DS/sds.c ++++ b/src/DS/sds.c +@@ -249,7 +249,9 @@ static int ds_sds_register_xmlDoc(struct ds_sds_session *session, xmlDoc* doc, x + + struct oscap_source *component_source = oscap_source_new_from_xmlDoc(new_doc, relative_filepath); + +- ds_sds_session_register_component_source(session, relative_filepath, component_source); ++ if (ds_sds_session_register_component_source(session, relative_filepath, component_source) != 0) { ++ oscap_source_free(component_source); ++ } + return 0; // TODO: Return value of ds_sds_session_register_component_source(). (commit message) + } + + + diff --git a/SOURCES/openscap-1.3.5-fix_segfaults_and_broken_test-PR_1669.patch b/SOURCES/openscap-1.3.5-fix_segfaults_and_broken_test-PR_1669.patch new file mode 100644 index 0000000..2196d54 --- /dev/null +++ b/SOURCES/openscap-1.3.5-fix_segfaults_and_broken_test-PR_1669.patch @@ -0,0 +1,146 @@ +From b8b90b4c04a130d9174148486b40bfc8454a290b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= +Date: Tue, 5 Jan 2021 16:31:04 +0100 +Subject: [PATCH 1/5] Fix a segmentation fault + +When generating results with --stig-viewer a segmentation fault +occured. This segfault has been caused by +9b40767967e533bdb340ca4c91f2fd1192694820. + +The patch makes the usage of benchmark and cloned benchmark in this +`if` block consistent with the previous `if` block above. +--- + src/XCCDF/xccdf_session.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/XCCDF/xccdf_session.c b/src/XCCDF/xccdf_session.c +index c88d90be05..9e54a98e9e 100644 +--- a/src/XCCDF/xccdf_session.c ++++ b/src/XCCDF/xccdf_session.c +@@ -1393,9 +1393,12 @@ static int _build_xccdf_result_source(struct xccdf_session *session) + } + + if (session->export.xccdf_stig_viewer_file != NULL) { ++ struct xccdf_benchmark *cloned_benchmark = xccdf_benchmark_clone(benchmark); + struct xccdf_result *cloned_result = xccdf_result_clone(session->xccdf.result); ++ xccdf_benchmark_add_result(cloned_benchmark, cloned_result); + struct oscap_source * stig_result = xccdf_result_stig_viewer_export_source(cloned_result, session->export.xccdf_stig_viewer_file); +- xccdf_result_free(cloned_result); ++ // cloned_result is freed during xccdf_benchmark_free ++ xccdf_benchmark_free(cloned_benchmark); + if (oscap_source_save_as(stig_result, NULL) != 0) { + oscap_seterr(OSCAP_EFAMILY_OSCAP, "Could not save file: %s", + oscap_source_readable_origin(stig_result)); + +From 0a73539af773ce261e2b5eb71ca6b2b83ff6e386 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= +Date: Tue, 5 Jan 2021 16:47:13 +0100 +Subject: [PATCH 2/5] Fix test for --stig-viewer + +The test masked the segfault in oscap command by ignoring the oscap +return code. Moreover, it ignored any failure in the Python script +stig-viewer-equivalence.py. The commit also adds an improvement to +test if the produced file isn't empty. +--- + tests/API/XCCDF/unittests/test_single_rule_stigw.sh | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/tests/API/XCCDF/unittests/test_single_rule_stigw.sh b/tests/API/XCCDF/unittests/test_single_rule_stigw.sh +index 15803ab94c..e258e72dd1 100755 +--- a/tests/API/XCCDF/unittests/test_single_rule_stigw.sh ++++ b/tests/API/XCCDF/unittests/test_single_rule_stigw.sh +@@ -19,8 +19,10 @@ echo "Result file = $result" + # evaluated when '--rule' option is not specified. + + # One of the rules is supposed to fail, so the return code of this line has to be 0 so the test can continue +-$OSCAP xccdf eval --stig-viewer "$result" "$srcdir/${name}.xccdf.xml" 2> "$stderr" || true ++$OSCAP xccdf eval --stig-viewer "$result" "$srcdir/${name}.xccdf.xml" 2> "$stderr" || ret=$? ++[ $ret == 2 ] + [ -f $stderr ]; [ ! -s $stderr ]; :> $stderr ++[ -s "$result" ] + +-"${PYTHON:-python}" "$srcdir/stig-viewer-equivalence.py" "$result" "$srcdir/correct_stigw_result.xml" 2> "$stderr" || ret=$? ++"${PYTHON:-python}" "$srcdir/stig-viewer-equivalence.py" "$result" "$srcdir/correct_stigw_result.xml" 2> "$stderr" + rm "$result" + +From 7011a6fc960515ff626f411349b8b8a267b80c02 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= +Date: Wed, 6 Jan 2021 12:08:31 +0100 +Subject: [PATCH 3/5] Fix segmentation fault + +The function call oscap_reference_get_href(ref) can return NULL +because href attribute of reference element is optional. +--- + src/XCCDF/result.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/src/XCCDF/result.c b/src/XCCDF/result.c +index 21b93fba74..a4fdec6cee 100644 +--- a/src/XCCDF/result.c ++++ b/src/XCCDF/result.c +@@ -1154,7 +1154,9 @@ void xccdf_result_to_dom(struct xccdf_result *result, xmlNode *result_node, xmlD + struct oscap_reference_iterator *references = xccdf_item_get_references(item); + while (oscap_reference_iterator_has_more(references)) { + struct oscap_reference *ref = oscap_reference_iterator_next(references); +- if (strcmp(oscap_reference_get_href(ref), DISA_STIG_VIEWER_HREF) == 0) { ++ const char *href = oscap_reference_get_href(ref); ++ if (href && (strcmp(href, DISA_STIG_VIEWER_HREF[0]) == 0 || ++ strcmp(href, DISA_STIG_VIEWER_HREF[1]) == 0)) { + const char *stig_rule_id = oscap_reference_get_title(ref); + + xccdf_test_result_type_t other_res = (xccdf_test_result_type_t)oscap_htable_detach(nodes_by_rule_id, stig_rule_id); +@@ -1367,8 +1368,9 @@ void xccdf_rule_result_to_dom(struct xccdf_rule_result *result, xmlDoc *doc, xml + struct oscap_reference_iterator *references = xccdf_item_get_references(item); + while (oscap_reference_iterator_has_more(references)) { + struct oscap_reference *ref = oscap_reference_iterator_next(references); +- if (strcmp(oscap_reference_get_href(ref), DISA_STIG_VIEWER_HREF[0]) == 0 || +- strcmp(oscap_reference_get_href(ref), DISA_STIG_VIEWER_HREF[1]) == 0) { ++ const char *href = oscap_reference_get_href(ref); ++ if (href && (strcmp(href, DISA_STIG_VIEWER_HREF[0]) == 0 || ++ strcmp(href, DISA_STIG_VIEWER_HREF[1]) == 0)) { + const char *stig_rule_id = oscap_reference_get_title(ref); + + xccdf_test_result_type_t expected_res = (xccdf_test_result_type_t)oscap_htable_get(nodes_by_rule_id, stig_rule_id); + +From 7ae1dd586128889bbaa8b3a20937d00feabd2ac0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= +Date: Wed, 6 Jan 2021 13:35:36 +0100 +Subject: [PATCH 4/5] Test reference elements without href attribute + +The href attribute is optional within xccdf:reference element. +Moreover, reference elements can contain Dublin Core elements instead of +a simple atomic value. Some SCAP content uses xccdf:reference without +href attribute, for example DISA STIG for RHEL 7 V3R1. There was a +segmentation fault because oscap expected the href attribute to be +present. See RHBZ #1911999 for more information. +--- + tests/API/XCCDF/unittests/test_single_rule.xccdf.xml | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/tests/API/XCCDF/unittests/test_single_rule.xccdf.xml b/tests/API/XCCDF/unittests/test_single_rule.xccdf.xml +index c942aac18a..c41b86173a 100644 +--- a/tests/API/XCCDF/unittests/test_single_rule.xccdf.xml ++++ b/tests/API/XCCDF/unittests/test_single_rule.xccdf.xml +@@ -1,5 +1,5 @@ + +- ++ + accepted + 1.0 + +@@ -26,6 +26,14 @@ + + + This rule always pass ++ Clever book, page 18, section 3 ++ ++ Test reference without href attribute ++ OpenSCAP Project ++ Dummy reference ++ Random subject ++ 12345 ++ + RHEL-07-040800 + SV-86937r1_rule + + diff --git a/SOURCES/openscap-1.3.5-fix_testresult_benchmark-PR_1655.patch b/SOURCES/openscap-1.3.5-fix_testresult_benchmark-PR_1655.patch new file mode 100644 index 0000000..81d5cf6 --- /dev/null +++ b/SOURCES/openscap-1.3.5-fix_testresult_benchmark-PR_1655.patch @@ -0,0 +1,211 @@ +From 9b40767967e533bdb340ca4c91f2fd1192694820 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= +Date: Wed, 2 Dec 2020 16:45:42 +0100 +Subject: [PATCH] Fix TestResult/benchmark/@href attribute + +Make the href attribute an URI according to SCAP specification Section +4.5. This should fix SCAPVAL error RES-253-2 which occured when +validating ARFs by SCAPVAL. See #1629 for the problem description. + +Also, it fixes a problem that xccdf_benchmark_item_clone didn't create +the clusters_dict hash table. + +The existing tests are extended to test this property, the +function test_sds_external_xccdf has been splitted into 2 functions. + +Fixes: #1629 +--- + src/DS/ds_sds_session.c | 18 ++++++++++++++++++ + src/DS/ds_sds_session_priv.h | 1 + + src/DS/public/ds_sds_session.h | 8 ++++++++ + src/DS/sds.c | 3 +++ + src/XCCDF/item.c | 1 + + src/XCCDF/xccdf_session.c | 25 ++++++++++++++++++++----- + tests/DS/test_ds_misc.sh | 30 +++++++++++++++++++++++------- + 7 files changed, 74 insertions(+), 12 deletions(-) + +diff --git a/src/DS/ds_sds_session.c b/src/DS/ds_sds_session.c +index c03d3d593e..9120ee3243 100644 +--- a/src/DS/ds_sds_session.c ++++ b/src/DS/ds_sds_session.c +@@ -50,7 +50,9 @@ struct ds_sds_session { + const char *target_dir; ///< Target directory for current split + const char *datastream_id; ///< ID of selected datastream + const char *checklist_id; ///< ID of selected checklist ++ const char *checklist_uri; ///< URI of selected checklist + struct oscap_htable *component_sources; ///< oscap_source for parsed components ++ struct oscap_htable *component_uris; ///< maps component refs to component URIs + bool fetch_remote_resources; ///< Allows loading of external components; + download_progress_calllback_t progress; ///< Callback to report progress of download. + }; +@@ -72,6 +74,7 @@ struct ds_sds_session *ds_sds_session_new_from_source(struct oscap_source *sourc + struct ds_sds_session *sds_session = (struct ds_sds_session *) calloc(1, sizeof(struct ds_sds_session)); + sds_session->source = source; + sds_session->component_sources = oscap_htable_new(); ++ sds_session->component_uris = oscap_htable_new(); + sds_session->progress = download_progress_empty_calllback; + return sds_session; + } +@@ -84,6 +87,7 @@ void ds_sds_session_free(struct ds_sds_session *sds_session) + oscap_acquire_cleanup_dir(&(sds_session->temp_dir)); + } + oscap_htable_free(sds_session->component_sources, (oscap_destruct_func) oscap_source_free); ++ oscap_htable_free(sds_session->component_uris, (oscap_destruct_func) free); + free(sds_session); + } + } +@@ -91,10 +95,13 @@ void ds_sds_session_free(struct ds_sds_session *sds_session) + void ds_sds_session_reset(struct ds_sds_session *session) + { + session->checklist_id = NULL; ++ session->checklist_uri = NULL; + session->datastream_id = NULL; + session->target_dir = NULL; + oscap_htable_free(session->component_sources, (oscap_destruct_func) oscap_source_free); + session->component_sources = oscap_htable_new(); ++ oscap_htable_free(session->component_uris, (oscap_destruct_func) free); ++ session->component_uris = oscap_htable_new(); + } + + struct ds_sds_index *ds_sds_session_get_sds_idx(struct ds_sds_session *session) +@@ -163,11 +170,21 @@ const char *ds_sds_session_get_checklist_id(const struct ds_sds_session *session + return session->checklist_id; + } + ++const char *ds_sds_session_get_checklist_uri(const struct ds_sds_session *session) ++{ ++ return session->checklist_uri; ++} ++ + struct oscap_htable *ds_sds_session_get_component_sources(struct ds_sds_session *session) + { + return session->component_sources; + } + ++struct oscap_htable *ds_sds_session_get_component_uris(struct ds_sds_session *session) ++{ ++ return session->component_uris; ++} ++ + const char *ds_sds_session_get_readable_origin(const struct ds_sds_session *session) + { + if (session->source == NULL) +@@ -210,6 +227,7 @@ struct oscap_source *ds_sds_session_select_checklist(struct ds_sds_session *sess + oscap_seterr(OSCAP_EFAMILY_OSCAP, "Could not extract %s with all dependencies from datastream.", session->checklist_id); + return NULL; + } ++ session->checklist_uri = oscap_htable_get(session->component_uris, session->checklist_id); + struct oscap_source *xccdf = oscap_htable_get(session->component_sources, session->checklist_id); + if (xccdf == NULL) { + oscap_seterr(OSCAP_EFAMILY_OSCAP, "Internal error: Could not acquire handle to '%s' source.", session->checklist_id); +diff --git a/src/DS/ds_sds_session_priv.h b/src/DS/ds_sds_session_priv.h +index 4aa559ebb4..f58231dc48 100644 +--- a/src/DS/ds_sds_session_priv.h ++++ b/src/DS/ds_sds_session_priv.h +@@ -37,6 +37,7 @@ xmlDoc *ds_sds_session_get_xmlDoc(struct ds_sds_session *session); + int ds_sds_session_register_component_source(struct ds_sds_session *session, const char *relative_filepath, struct oscap_source *component); + const char *ds_sds_session_get_target_dir(struct ds_sds_session *session); + struct oscap_htable *ds_sds_session_get_component_sources(struct ds_sds_session *session); ++struct oscap_htable *ds_sds_session_get_component_uris(struct ds_sds_session *session); + const char *ds_sds_session_get_readable_origin(const struct ds_sds_session *session); + bool ds_sds_session_fetch_remote_resources(struct ds_sds_session *session); + download_progress_calllback_t ds_sds_session_remote_resources_progress(struct ds_sds_session *session); +diff --git a/src/DS/public/ds_sds_session.h b/src/DS/public/ds_sds_session.h +index 1ce692ce21..20a85146cc 100644 +--- a/src/DS/public/ds_sds_session.h ++++ b/src/DS/public/ds_sds_session.h +@@ -119,6 +119,14 @@ const char *ds_sds_session_get_datastream_id(const struct ds_sds_sessi + */ + const char *ds_sds_session_get_checklist_id(const struct ds_sds_session *session); + ++/** ++ * Return URI of currently selected component representing XCCDF within the DataStream ++ * @memberof ds_sds_session ++ * @param session The Source DataStream session ++ * @returns URI of selected component or NULL ++ */ ++const char *ds_sds_session_get_checklist_uri(const struct ds_sds_session *session); ++ + /** + * Get component from Source DataStream by its href. This assumes that the component + * has been already cached by the session. You can cache component or its dependencies +diff --git a/src/DS/sds.c b/src/DS/sds.c +index b546ed3e65..20c683a7aa 100644 +--- a/src/DS/sds.c ++++ b/src/DS/sds.c +@@ -450,7 +450,10 @@ int ds_sds_dump_component_ref_as(const xmlNodePtr component_ref, struct ds_sds_s + + char* component_id = NULL; + ++ // make a copy of xlink_href because ds_sds_dump_component_by_href modifies its second argument ++ char *xlink_href_copy = oscap_strdup(xlink_href); + int ret = ds_sds_dump_component_by_href(session, xlink_href, target_filename_dirname, relative_filepath, cref_id, &component_id); ++ oscap_htable_add(ds_sds_session_get_component_uris(session), cref_id, xlink_href_copy); + + xmlFree(xlink_href); + xmlFree(cref_id); +diff --git a/src/XCCDF/item.c b/src/XCCDF/item.c +index 3657fb461a..295e4a7f00 100644 +--- a/src/XCCDF/item.c ++++ b/src/XCCDF/item.c +@@ -1155,6 +1155,7 @@ struct xccdf_benchmark_item * xccdf_benchmark_item_clone(struct xccdf_item *pare + clone->items_dict = oscap_htable_new(); + clone->profiles_dict = oscap_htable_new(); + clone->results_dict = oscap_htable_new(); ++ clone->clusters_dict = oscap_htable_new(); + clone->notices = oscap_list_clone(item->notices, (oscap_clone_func) xccdf_notice_clone); + clone->plain_texts = oscap_list_clone(item->plain_texts, (oscap_clone_func) xccdf_plain_text_clone); + +diff --git a/src/XCCDF/xccdf_session.c b/src/XCCDF/xccdf_session.c +index f1b8379591..c88d90be05 100644 +--- a/src/XCCDF/xccdf_session.c ++++ b/src/XCCDF/xccdf_session.c +@@ -1374,21 +1374,28 @@ static int _build_xccdf_result_source(struct xccdf_session *session) + oscap_seterr(OSCAP_EFAMILY_OSCAP, "No XCCDF results to export."); + return 1; + } +- struct xccdf_result* cloned_result = xccdf_result_clone(session->xccdf.result); +- xccdf_benchmark_add_result(benchmark, cloned_result); +- session->xccdf.result_source = xccdf_benchmark_export_source(benchmark, session->export.xccdf_file); + + if (session->export.xccdf_file != NULL) { ++ struct xccdf_benchmark *cloned_benchmark = xccdf_benchmark_clone(benchmark); ++ struct xccdf_result *cloned_result = xccdf_result_clone(session->xccdf.result); ++ xccdf_benchmark_add_result(cloned_benchmark, cloned_result); ++ struct oscap_source *xccdf_result_source = xccdf_benchmark_export_source(cloned_benchmark, session->export.xccdf_file); ++ // cloned_result is freed during xccdf_benchmark_free ++ xccdf_benchmark_free(cloned_benchmark); + // Export XCCDF result file only when explicitly requested +- if (oscap_source_save_as(session->xccdf.result_source, NULL) != 0) { ++ if (oscap_source_save_as(xccdf_result_source, NULL) != 0) { + oscap_seterr(OSCAP_EFAMILY_OSCAP, "Could not save file: %s", +- oscap_source_readable_origin(session->xccdf.result_source)); ++ oscap_source_readable_origin(xccdf_result_source)); ++ oscap_source_free(xccdf_result_source); + return -1; + } ++ oscap_source_free(xccdf_result_source); + } + + if (session->export.xccdf_stig_viewer_file != NULL) { ++ struct xccdf_result *cloned_result = xccdf_result_clone(session->xccdf.result); + struct oscap_source * stig_result = xccdf_result_stig_viewer_export_source(cloned_result, session->export.xccdf_stig_viewer_file); ++ xccdf_result_free(cloned_result); + if (oscap_source_save_as(stig_result, NULL) != 0) { + oscap_seterr(OSCAP_EFAMILY_OSCAP, "Could not save file: %s", + oscap_source_readable_origin(stig_result)); +@@ -1398,6 +1405,14 @@ static int _build_xccdf_result_source(struct xccdf_session *session) + oscap_source_free(stig_result); + } + ++ struct xccdf_result *cloned_result = xccdf_result_clone(session->xccdf.result); ++ if (xccdf_session_is_sds(session)) { ++ struct ds_sds_session *sds_session = xccdf_session_get_ds_sds_session(session); ++ const char *benchmark_uri = ds_sds_session_get_checklist_uri(sds_session); ++ xccdf_result_set_benchmark_uri(cloned_result, benchmark_uri); ++ } ++ xccdf_benchmark_add_result(benchmark, cloned_result); ++ session->xccdf.result_source = xccdf_benchmark_export_source(benchmark, session->export.xccdf_file); + /* validate XCCDF Results */ + if (session->validate && session->full_validation) { + if (oscap_source_validate(session->xccdf.result_source, _reporter, NULL)) { diff --git a/SPECS/openscap.spec b/SPECS/openscap.spec index 672d82f..46c75ce 100644 --- a/SPECS/openscap.spec +++ b/SPECS/openscap.spec @@ -1,3 +1,4 @@ +%define __python /usr/bin/python2 %{!?python_sitelib: %global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} %{!?python_sitearch: %global python_sitearch %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib(1)")} @@ -6,7 +7,7 @@ restorecon -R /usr/bin/oscap /usr/libexec/openscap; \ Name: openscap Version: 1.2.17 -Release: 13%{?dist} +Release: 14%{?dist} Summary: Set of open source libraries enabling integration of the SCAP line of standards Group: System Environment/Libraries License: LGPLv2+ @@ -39,6 +40,15 @@ Patch24: openscap-1.2.18-cvrf-segfault.patch Patch25: openscap-1.3.2-red-hat-errata-url-pr1388.patch Patch26: openscap-1.3.4-rpmverifyfile_leak-PR_1565.patch Patch27: openscap-1.2.18-gzip-compression-PR1576.patch +Patch28: openscap-1.3.4-fix_memory_leak_probe-file-PR_1594.patch +Patch29: openscap-1.2.18-fix_trailing_whitespace_in_playbooks-PR_1268.patch +Patch30: openscap-1.2.18-prevent_duplicate_vars_in_playbooks-PR_1274.patch +Patch31: openscap-1.3.2-show_check_identifiers_at_multi-check_rules-PR_1426.patch +Patch32: openscap-1.3.4-fix_memory_leaks-PR_1595.patch +Patch33: openscap-1.3.5-fix_testresult_benchmark-PR_1655.patch +Patch34: openscap-1.3.1-improve_stig-viewer_output-PR_1319.patch +Patch35: openscap-1.3.5-fix_segfaults_and_broken_test-PR_1669.patch +Patch36: openscap-1.2.18-memory-limits-PR_1803.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: swig libxml2-devel libxslt-devel perl-XML-Parser BuildRequires: rpm-devel @@ -181,6 +191,15 @@ Tool for scanning Atomic containers. %patch25 -p1 %patch26 -p1 %patch27 -p1 +%patch28 -p1 +%patch29 -p1 +%patch30 -p1 +%patch31 -p1 +%patch32 -p1 +%patch33 -p1 +%patch34 -p1 +%patch35 -p1 +%patch36 -p1 %build %ifarch sparc64 @@ -336,6 +355,15 @@ rm -rf $RPM_BUILD_ROOT %changelog +* Wed Aug 11 2021 Marcus Burghardt - 1.2.17-14 +- Fix memory leaks in probe-api (RHBZ#1861793) +- Prevent duplicate variables in Ansible Playbooks (RHBZ#1944683) +- Fix trailing whitespace in Ansible Playbooks +- Fix inconsistent result from security_patches_up_to_date (RHBZ#1858502) +- Fix multiple segfaults and broken test (RHBZ#1911999) +- Improve --stig-viewer output when there is no 1:1 connection between rules +- Lower memory limits and improve their checking (RHBZ#1932833) + * Mon Sep 21 2020 Jan Černý - 1.2.17-13 - Enable gzip compression when downloading remote content (RHBZ#1870147)