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 @@
+
+
Rule 1: Enable Audit Service
@@ -95,8 +97,6 @@
# the service commands will end up in the command section
service enable sshd
service disable telnet
-
- post mkdir /etc/scap
@@ -112,6 +112,30 @@
bootloader audit=1
+
+ Rule 8: Extra nochroot %post section
+
+ %post --nochroot
+ mkdir -p /etc/ddfds
+ %end
+
+
+
+ Rule 9: Crazy remediation
+
+ %post --nochroot
+ mkdir -p /etc/abcd
+ %end
+ service disable httpd
+ # other post section
+ %post
+ rm -rf /etc/xyz
+ # create a new path
+ feel /etc/xyz
+ %end
+ package install podman
+
+
diff --git a/tests/API/XCCDF/unittests/test_remediation_kickstart.sh b/tests/API/XCCDF/unittests/test_remediation_kickstart.sh
index c2c7203acd..97fbf7124a 100755
--- a/tests/API/XCCDF/unittests/test_remediation_kickstart.sh
+++ b/tests/API/XCCDF/unittests/test_remediation_kickstart.sh
@@ -6,18 +6,22 @@ set -e -o pipefail
function test_normal {
kickstart=$(mktemp)
stderr=$(mktemp)
+ expected_modified=$(mktemp)
+ kickstart_modified=$(mktemp)
+
+ sed "/This file was generated by OpenSCAP .* using:/d" "$srcdir/test_remediation_kickstart_expected.cfg" > "$expected_modified"
+ sed -i "s;TEST_DATA_STREAM_PATH;$srcdir/test_remediation_kickstart.ds.xml;" "$expected_modified"
$OSCAP xccdf generate fix --fix-type kickstart --output "$kickstart" --profile common "$srcdir/test_remediation_kickstart.ds.xml"
- grep -q '# Kickstart for Common hardening profile' "$kickstart"
- grep -q 'services --disabled=telnet --enabled=auditd,rsyslog,sshd' "$kickstart"
- grep -q 'logvol /var/tmp --name=vartmp --vgname=system --size=1024' "$kickstart"
- grep -q 'mkdir /etc/scap' "$kickstart"
- grep -q '\-usbguard' "$kickstart"
- grep -q 'bootloader --append="quick audit=1"' "$kickstart"
+ sed "/This file was generated by OpenSCAP .* using:/d" "$kickstart" > "$kickstart_modified"
+
+ diff -u "$expected_modified" "$kickstart_modified"
rm -rf "$kickstart"
rm -rf "$stderr"
+ rm -rf "$expected_modified"
+ rm -rf "$kickstart_modified"
}
function test_tailoring {
diff --git a/tests/API/XCCDF/unittests/test_remediation_kickstart.tailoring.xml b/tests/API/XCCDF/unittests/test_remediation_kickstart.tailoring.xml
index 76c184a1fd..5f04670209 100644
--- a/tests/API/XCCDF/unittests/test_remediation_kickstart.tailoring.xml
+++ b/tests/API/XCCDF/unittests/test_remediation_kickstart.tailoring.xml
@@ -8,5 +8,8 @@
+
+
+