diff --git a/src/XCCDF/benchmark.c b/src/XCCDF/benchmark.c index f561736d4..d6560b7c9 100644 --- a/src/XCCDF/benchmark.c +++ b/src/XCCDF/benchmark.c @@ -866,6 +866,19 @@ xccdf_benchmark_find_target_htable(const struct xccdf_benchmark *benchmark, xccd return XITEM(benchmark)->sub.benchmark.items_dict; } +int xccdf_benchmark_include_tailored_profiles(struct xccdf_benchmark *benchmark) +{ + struct oscap_list *profiles = XITEM(benchmark)->sub.benchmark.profiles; + struct oscap_htable_iterator *it = oscap_htable_iterator_new(XITEM(benchmark)->sub.benchmark.profiles_dict); + while(oscap_htable_iterator_has_more(it)) { + struct xccdf_profile *profile = oscap_htable_iterator_next_value(it); + if (xccdf_profile_get_tailoring(profile)) { + oscap_list_add(profiles, xccdf_profile_clone(profile)); + } + } + oscap_htable_iterator_free(it); + return 0; +} struct xccdf_plain_text *xccdf_plain_text_new(void) { diff --git a/src/XCCDF/item.h b/src/XCCDF/item.h index 35204744f..da76f854d 100644 --- a/src/XCCDF/item.h +++ b/src/XCCDF/item.h @@ -437,6 +437,7 @@ void xccdf_item_dump(struct xccdf_item *item, int depth); struct xccdf_item* xccdf_item_get_benchmark_internal(struct xccdf_item* item); bool xccdf_benchmark_parse(struct xccdf_item *benchmark, xmlTextReaderPtr reader); void xccdf_benchmark_dump(struct xccdf_benchmark *benchmark); +int xccdf_benchmark_include_tailored_profiles(struct xccdf_benchmark *benchmark); struct oscap_htable_iterator *xccdf_benchmark_get_cluster_items(struct xccdf_benchmark *benchmark, const char *cluster_id); bool xccdf_benchmark_register_item(struct xccdf_benchmark *benchmark, struct xccdf_item *item); bool xccdf_benchmark_unregister_item(struct xccdf_item *item); diff --git a/src/XCCDF/public/xccdf_session.h b/src/XCCDF/public/xccdf_session.h index ac96e2036..026432693 100644 --- a/src/XCCDF/public/xccdf_session.h +++ b/src/XCCDF/public/xccdf_session.h @@ -558,6 +558,14 @@ OSCAP_API int xccdf_session_build_policy_from_testresult(struct xccdf_session *s */ OSCAP_API int xccdf_session_add_report_from_source(struct xccdf_session *session, struct oscap_source *report_source); +/** + * Generate HTML guide form a loaded XCCDF session + * @param session XCCDF Session + * @param outfile path to the output file + * @returns zero on success + */ +OSCAP_API int xccdf_session_generate_guide(struct xccdf_session *session, const char *outfile); + /// @} /// @} #endif diff --git a/src/XCCDF/xccdf_session.c b/src/XCCDF/xccdf_session.c index 1005a030a..d38905f77 100644 --- a/src/XCCDF/xccdf_session.c +++ b/src/XCCDF/xccdf_session.c @@ -1746,3 +1746,44 @@ int xccdf_session_add_report_from_source(struct xccdf_session *session, struct o session->xccdf.result = result; return 0; } + +int xccdf_session_generate_guide(struct xccdf_session *session, const char *outfile) +{ + struct xccdf_policy *policy = xccdf_session_get_xccdf_policy(session); + if (policy == NULL) { + oscap_seterr(OSCAP_EFAMILY_OSCAP, "Could not get XCCDF policy."); + return 1; + } + struct xccdf_policy_model *model = xccdf_policy_get_model(policy); + if (model == NULL) { + oscap_seterr(OSCAP_EFAMILY_OSCAP, "Could not get XCCDF policy model."); + return 1; + } + struct xccdf_benchmark *benchmark = xccdf_policy_model_get_benchmark(model); + if (benchmark == NULL) { + oscap_seterr(OSCAP_EFAMILY_OSCAP, "Could not get XCCDF benchmark."); + return 1; + } + xccdf_benchmark_include_tailored_profiles(benchmark); + const char *params[] = { + "oscap-version", oscap_get_version(), + "benchmark_id", xccdf_benchmark_get_id(benchmark), + "profile_id", xccdf_session_get_profile_id(session), + NULL + }; + struct oscap_source *benchmark_source = xccdf_benchmark_export_source(benchmark, NULL); + if (benchmark_source == NULL) { + oscap_seterr(OSCAP_EFAMILY_OSCAP, + "Failed to export source from XCCDF benchmark %s.", + xccdf_benchmark_get_id(benchmark)); + return 1; + } + int ret = oscap_source_apply_xslt_path(benchmark_source, + "xccdf-guide.xsl", outfile, params, oscap_path_to_xslt()); + oscap_source_free(benchmark_source); + if (ret < 0) { + oscap_seterr(OSCAP_EFAMILY_OSCAP, "Could not apply XSLT."); + return 1; + } + return 0; +} \ No newline at end of file diff --git a/tests/API/XCCDF/tailoring/all.sh b/tests/API/XCCDF/tailoring/all.sh index dbed97a87..fc2ccd743 100755 --- a/tests/API/XCCDF/tailoring/all.sh +++ b/tests/API/XCCDF/tailoring/all.sh @@ -134,6 +134,21 @@ function test_api_xccdf_tailoring_profile_generate_fix { rm -f $tailoring_result $fix_result } +function test_api_xccdf_tailoring_profile_generate_guide { + local INPUT=$srcdir/$1 + local TAILORING=$srcdir/$2 + + guide=`mktemp` + # tailoring profile only with "always fail" rule and generate HTML guide + $OSCAP xccdf generate guide --tailoring-file $TAILORING --profile "xccdf_com.example.www_profile_customized" --output $guide $INPUT + + grep -q "Baseline Testing Profile 1 \[CUSTOMIZED\]" $guide + # profile 'customized' selects first rule and deselects the second + grep -q "xccdf_com.example.www_rule_first" $guide + grep -v "xccdf_com.example.www_rule_second" $guide + rm -f $guide +} + # Testing. test_init "test_api_xccdf_tailoring.log" @@ -154,6 +169,7 @@ test_run "test_api_xccdf_tailoring_autonegotiation" test_api_xccdf_tailoring_aut test_run "test_api_xccdf_tailoring_simple_include_in_arf" test_api_xccdf_tailoring_simple_include_in_arf simple-xccdf.xml simple-tailoring.xml test_run "test_api_xccdf_tailoring_profile_include_in_arf" test_api_xccdf_tailoring_profile_include_in_arf baseline.xccdf.xml baseline.tailoring.xml test_run "test_api_xccdf_tailoring_profile_generate_fix" test_api_xccdf_tailoring_profile_generate_fix baseline.xccdf.xml baseline.tailoring.xml +test_run "test_api_xccdf_tailoring_profile_generate_guide" test_api_xccdf_tailoring_profile_generate_guide baseline.xccdf.xml baseline.tailoring.xml test_exit diff --git a/utils/oscap-xccdf.c b/utils/oscap-xccdf.c index 472a2cc6f..87a0c7ad1 100644 --- a/utils/oscap-xccdf.c +++ b/utils/oscap-xccdf.c @@ -77,6 +77,7 @@ static bool getopt_xccdf(int argc, char **argv, struct oscap_action *action); static bool getopt_generate(int argc, char **argv, struct oscap_action *action); static int app_xccdf_xslt(const struct oscap_action *action); static int app_generate_fix(const struct oscap_action *action); +static int app_generate_guide(const struct oscap_action *action); #define XCCDF_SUBMODULES_NUM 7 #define XCCDF_GEN_SUBMODULES_NUM 5 /* See actual arrays @@ -244,12 +245,18 @@ static struct oscap_module XCCDF_GEN_GUIDE = { .help = GEN_OPTS "\nGuide Options:\n" " --output - Write the document into file.\n" - " --hide-profile-info - Do not output additional information about selected profile.\n" + " --hide-profile-info - This option has no effect.\n" " --benchmark-id - ID of XCCDF Benchmark in some component in the datastream that should be used.\n" + " (only applicable for source datastreams)\n" + " --xccdf-id - ID of component-ref with XCCDF in the datastream that should be evaluated.\n" + " (only applicable for source datastreams)\n" + " --tailoring-file - Use given XCCDF Tailoring file.\n" + " (only applicable for source datastreams)\n" + " --tailoring-id - Use given DS component as XCCDF Tailoring file.\n" " (only applicable for source datastreams)\n", .opt_parser = getopt_xccdf, - .user = "xccdf-guide.xsl", - .func = app_xccdf_xslt + .user = NULL, + .func = app_generate_guide }; static struct oscap_module XCCDF_GEN_FIX = { @@ -973,6 +980,50 @@ int app_generate_fix(const struct oscap_action *action) return ret; } +int app_generate_guide(const struct oscap_action *action) +{ + int ret = OSCAP_ERROR; + + struct oscap_source *source = oscap_source_new_from_file(action->f_xccdf); + struct xccdf_session *session = xccdf_session_new_from_source(source); + if (session == NULL) { + goto cleanup; + } + + xccdf_session_set_validation(session, action->validate, getenv("OSCAP_FULL_VALIDATION") != NULL); + xccdf_session_set_remote_resources(session, action->remote_resources, download_reporting_callback); + xccdf_session_set_user_tailoring_file(session, action->tailoring_file); + xccdf_session_set_user_tailoring_cid(session, action->tailoring_id); + if (xccdf_session_is_sds(session)) { + xccdf_session_set_component_id(session, action->f_xccdf_id); + xccdf_session_set_benchmark_id(session, action->f_benchmark_id); + } + xccdf_session_set_loading_flags(session, XCCDF_SESSION_LOAD_XCCDF); + + if (xccdf_session_load(session) != 0) { + goto cleanup; + } + + if (!xccdf_session_set_profile_id(session, action->profile)) { + if (action->profile != NULL) { + if (xccdf_set_profile_or_report_bad_id(session, action->profile, action->f_xccdf) == OSCAP_ERROR) + goto cleanup; + } else { + fprintf(stderr, "No Policy was found for default profile.\n"); + goto cleanup; + } + } + + if (xccdf_session_generate_guide(session, action->f_results) == 0) { + ret = OSCAP_OK; + } + +cleanup: + xccdf_session_free(session); + oscap_print_error(); + return ret; +} + int app_xccdf_xslt(const struct oscap_action *action) { const char *oval_template = action->oval_template; diff --git a/utils/oscap.8 b/utils/oscap.8 index 1b05f8c9e..3a50d1df5 100644 --- a/utils/oscap.8 +++ b/utils/oscap.8 @@ -331,10 +331,19 @@ Generate a HTML document containing a security guide from an XCCDF Benchmark. Un Write the guide to this file instead of standard output. .TP \fB\-\-hide-profile-info\fR -Information on chosen profile (e.g. rules selected by the profile) will be excluded from the document. +This option has no effect and is kept only for backward compatibility purposes. .TP \fB\-\-benchmark-id ID\fR Selects a component ref from any datastream that references a component with XCCDF Benchmark such that its @id attribute matches given string exactly. +.TP +\fB\-\-xccdf-id ID\fR +Takes component ref with given ID from checklists. This allows to select a particular XCCDF component even in cases where there are 2 XCCDFs in one datastream. If none is given, the first component from the checklists element is used. +.TP +\fB\-\-tailoring-file TAILORING_FILE\fR +Use given file for XCCDF tailoring. Select profile from tailoring file to apply using --profile. If both --tailoring-file and --tailoring-id are specified, --tailoring-file takes priority. +.TP +\fB\-\-tailoring-id COMPONENT_REF_ID\fR +Use tailoring component in input source datastream for XCCDF tailoring. The tailoring component must be specified by its Ref-ID (value of component-ref/@id attribute in input source datastream). Select profile from tailoring component to apply using --profile. If both --tailoring-file and --tailoring-id are specified, --tailoring-file takes priority. .RE .TP .B \fBreport\fR [\fIoptions\fR] xccdf-file