diff --git a/docs/manual/manual.adoc b/docs/manual/manual.adoc index aab8c43cce..470ede7b16 100644 --- a/docs/manual/manual.adoc +++ b/docs/manual/manual.adoc @@ -1130,14 +1130,19 @@ To generate a kickstart, use `oscap xccdf generate fix` command with the `--fix- The kickstart will be generated from kickstart snippets in XCCDF rules in the input SCAP content. The kickstart snippets need to be stored in `` elements with `system` attribute set to `urn:xccdf:fix:script:kickstart`. -When processing the kickstart snippets comming from the XCCDF Rules, each line is processed separately. +When processing the kickstart snippets from the XCCDF Rules, each line is processed separately. The following rules are applied on each line: -* lines starting with `#` are ignored -* empty lines are ignored -* lines starting with a supported command are processed -* lines starting with something else than a supported command are dropped -* excess whitespace are trimmed +* Lines starting with `#` are ignored. +* Empty lines are ignored. +* If a line starts with a supported block keyword, that line and all following lines until a line starting with `%end` are considered a block. Blocks are propagated to the output without any processing. +* Lines starting with a supported command are processed. +* Lines starting with something else than a supported command are dropped and error is produced. +* Excess whitespace are trimmed. + +Supported block keywords: + +* `%post` - represents a start of a `%post` kickstart section, all lines until corresponding '%end' are overthrown Supported commands: @@ -1145,7 +1150,6 @@ Supported commands: * `package remove package_name` - adds `-package_name` to `%packages` section in the kickstart * `service enable service_name` - adds `service_name` to list in the `--enabled=` option in the `services` command in commands section in the kickstart * `service disable service_name` - adds `service_name` to list in the `--disabled=` option in the `services` command in commands section in the kickstart -* `post command` - adds `command` to the `%post` section the kickstart * `logvol path size` - adds `logvol` entry to the commands section of the kickstart that will mount a partition of the given `size` in MB to the given `path` as a mount point * `bootloader option` or `bootloader option=value` - adds `option` or `option=value` to the list in the `--append=` option in the `bootloader` command in commands section in the kickstart diff --git a/src/XCCDF_POLICY/xccdf_policy_remediate.c b/src/XCCDF_POLICY/xccdf_policy_remediate.c index 3829f26652..aebe02c888 100644 --- a/src/XCCDF_POLICY/xccdf_policy_remediate.c +++ b/src/XCCDF_POLICY/xccdf_policy_remediate.c @@ -923,7 +923,6 @@ static int _parse_line(const char *line, struct kickstart_commands *cmds) KS_SERVICE_DISABLE, KS_LOGVOL, KS_LOGVOL_SIZE, - KS_POST, KS_BOOTLOADER, KS_ERROR }; @@ -939,8 +938,6 @@ static int _parse_line(const char *line, struct kickstart_commands *cmds) state = KS_PACKAGE; } else if (!strcmp(word, "service")) { state = KS_SERVICE; - } else if (!strcmp(word, "post")) { - state = KS_POST; } else if (!strcmp(word, "logvol")) { state = KS_LOGVOL; } else if (!strcmp(word, "bootloader")) { @@ -985,11 +982,6 @@ static int _parse_line(const char *line, struct kickstart_commands *cmds) case KS_SERVICE_DISABLE: oscap_list_add(cmds->service_disable, strdup(word)); break; - case KS_POST: - oscap_list_add(cmds->post, strdup(line + strlen("post "))); - /* we need to jump off because we have eaten the whole line */ - goto cleanup; - break; case KS_LOGVOL: current_logvol_cmd = malloc(sizeof(struct logvol_cmd)); current_logvol_cmd->path = strdup(word); @@ -1028,12 +1020,40 @@ static int _xccdf_policy_rule_generate_kickstart_fix(struct xccdf_policy *policy } char *dup = strdup(fix_text); char **lines = oscap_split(dup, "\n"); + enum states { + KS_R_P_NORMAL, + KS_R_P_POST_BLOCK + }; + int state = KS_R_P_NORMAL; + char *block = NULL; for (unsigned int i = 0; lines[i] != NULL; i++) { char *line = lines[i]; - oscap_trim(line); - if (*line == '#' || *line == '\0') - continue; - _parse_line(line, cmds); + char *trim_line = oscap_trim(strdup(line)); + switch (state) { + case KS_R_P_NORMAL: + if (oscap_str_startswith(trim_line, "%post")) { + state = KS_R_P_POST_BLOCK; + block = strdup(trim_line); + block = oscap_concat(block, "\n"); + } else if (*trim_line != '#' && *trim_line != '\0') { + _parse_line(trim_line, cmds); + } + break; + case KS_R_P_POST_BLOCK: + if (oscap_str_startswith(trim_line, "%end")) { + state = KS_R_P_NORMAL; + block = oscap_concat(block, trim_line); + block = oscap_concat(block, "\n"); + oscap_list_add(cmds->post, block); + block = NULL; + } else { + block = oscap_concat(block, line); + block = oscap_concat(block, "\n"); + } + default: + break; + } + free(trim_line); } free(lines); free(dup); @@ -1512,7 +1532,7 @@ static void _write_tailoring_to_fd(struct oscap_source *tailoring, int output_fd _write_text_to_fd(output_fd, "END_OF_TAILORING\n"); } -static int _generate_kickstart_post(struct kickstart_commands *cmds, const char *profile_id, const char *input_path, struct oscap_source *tailoring, int output_fd) +static int _generate_kickstart_oscap_post(struct kickstart_commands *cmds, const char *profile_id, const char *input_path, struct oscap_source *tailoring, int output_fd) { _write_text_to_fd(output_fd, "# Perform OpenSCAP hardening (required for security compliance)\n"); _write_text_to_fd(output_fd, "%post --erroronfail\n"); @@ -1530,15 +1550,21 @@ static int _generate_kickstart_post(struct kickstart_commands *cmds, const char _write_tailoring_to_fd(tailoring, output_fd); _write_text_to_fd_and_free(output_fd, oscap_command); _write_text_to_fd(output_fd, "[ $? -eq 0 -o $? -eq 2 ] || exit 1\n"); + _write_text_to_fd(output_fd, "%end\n"); + _write_text_to_fd(output_fd, "\n"); + return 0; +} + +static int _generate_kickstart_post(struct kickstart_commands *cmds, int output_fd) +{ struct oscap_iterator *post_it = oscap_iterator_new(cmds->post); while (oscap_iterator_has_more(post_it)) { - char *command = (char *) oscap_iterator_next(post_it); - _write_text_to_fd(output_fd, command); + _write_text_to_fd(output_fd, "# Additional %post section (required for security compliance)\n"); + char *post_content = (char *) oscap_iterator_next(post_it); + _write_text_to_fd(output_fd, post_content); _write_text_to_fd(output_fd, "\n"); } oscap_iterator_free(post_it); - _write_text_to_fd(output_fd, "%end\n"); - _write_text_to_fd(output_fd, "\n"); return 0; } @@ -1657,7 +1683,9 @@ static int _xccdf_policy_generate_fix_kickstart(struct oscap_list *rules_to_fix, _generate_kickstart_packages(&cmds, output_fd); const char *profile_id = xccdf_profile_get_id(xccdf_policy_get_profile(policy)); - _generate_kickstart_post(&cmds, profile_id, input_file_name, tailoring, output_fd); + _generate_kickstart_oscap_post(&cmds, profile_id, input_file_name, tailoring, output_fd); + + _generate_kickstart_post(&cmds, output_fd); _write_text_to_fd(output_fd, "# Reboot after the installation is complete\nreboot\n"); diff --git a/tests/API/XCCDF/unittests/test_remediation_kickstart.ds.xml b/tests/API/XCCDF/unittests/test_remediation_kickstart.ds.xml index 031aa6034a..5c82065375 100644 --- a/tests/API/XCCDF/unittests/test_remediation_kickstart.ds.xml +++ b/tests/API/XCCDF/unittests/test_remediation_kickstart.ds.xml @@ -60,6 +60,8 @@ +