From 15c15c350e697d58e89e319351a52d90d28b9009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Thu, 6 Mar 2025 09:43:41 +0100 Subject: [PATCH 1/6] OPENSCAP-5235: Block remediation on deployed bootc system OpenSCAP remediation is supposed to be used only at bootc container image build. Deployed bootc system is immutable, it can't be remediated with OpenSCAP and trying to do so would result in errors and bad user experience. We will update OpenSCAP to print error message for users in case they try to run remediation on an already deployed bootc system, informing them that it is not possible and that the openscap remediation must be performed during container build. --- src/XCCDF/xccdf_session.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/XCCDF/xccdf_session.c b/src/XCCDF/xccdf_session.c index 5ec127be68..0c91657f81 100644 --- a/src/XCCDF/xccdf_session.c +++ b/src/XCCDF/xccdf_session.c @@ -1906,6 +1906,27 @@ struct xccdf_rule_result_iterator *xccdf_session_get_rule_results(const struct x return xccdf_result_get_rule_results(session->xccdf.result); } +static int system_is_in_bootc_mode(void) +{ +#ifdef OS_WINDOWS + return 0; +#else + FILE *output = popen("bootc status --format json 2>/dev/null | jq \".status.booted\" 2>/dev/null", "r"); + if (output == NULL) { + return 0; + } + char buf[1024] = {0}; + int c; + size_t i = 0; + while (i < sizeof(buf) && (c = fgetc(output)) != EOF) { + buf[i] = c; + i++; + } + pclose(output); + return *buf != '\0' && strcmp(buf, "null\n") != 0; +#endif +} + int xccdf_session_remediate(struct xccdf_session *session) { int res = 0; @@ -1917,6 +1938,14 @@ int xccdf_session_remediate(struct xccdf_session *session) oscap_seterr(OSCAP_EFAMILY_OSCAP, "Can't perform remediation in offline mode: not implemented"); return 1; } + if (system_is_in_bootc_mode()) { + oscap_seterr(OSCAP_EFAMILY_OSCAP, + "Detected running Image Mode operating system. OpenSCAP can't " + "perform remediation of this system because majority of the " + "system is read-only. Please apply remediation during bootable " + "container image build using 'oscap-im' instead."); + return 1; + } xccdf_policy_model_unregister_engines(session->xccdf.policy_model, oval_sysname); if ((res = xccdf_session_load_oval(session)) != 0) return res; From e433f3622e507445140156aa10a171580d1a894f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Wed, 12 Mar 2025 10:00:17 +0100 Subject: [PATCH 2/6] Stop using jq The jq is additional tool. Currently it's available in both RHEL and CentOS base bootable container images. But, we better not rely on it if someone removes it in future. --- src/XCCDF/xccdf_session.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/XCCDF/xccdf_session.c b/src/XCCDF/xccdf_session.c index 0c91657f81..507619d97c 100644 --- a/src/XCCDF/xccdf_session.c +++ b/src/XCCDF/xccdf_session.c @@ -1906,14 +1906,19 @@ struct xccdf_rule_result_iterator *xccdf_session_get_rule_results(const struct x return xccdf_result_get_rule_results(session->xccdf.result); } -static int system_is_in_bootc_mode(void) +static bool _system_is_in_bootc_mode(void) { #ifdef OS_WINDOWS - return 0; + return false; #else - FILE *output = popen("bootc status --format json 2>/dev/null | jq \".status.booted\" 2>/dev/null", "r"); + #define BOOTC_PATH "/usr/bin/bootc" + struct stat statbuf; + if (stat(BOOTC_PATH, &statbuf) == -1) { + return false; + } + FILE *output = popen(BOOTC_PATH " status --format json 2>/dev/null", "r"); if (output == NULL) { - return 0; + return false; } char buf[1024] = {0}; int c; @@ -1923,7 +1928,7 @@ static int system_is_in_bootc_mode(void) i++; } pclose(output); - return *buf != '\0' && strcmp(buf, "null\n") != 0; + return *buf != '\0' && strstr(buf, "\"booted\":null") == NULL; #endif } @@ -1938,7 +1943,7 @@ int xccdf_session_remediate(struct xccdf_session *session) oscap_seterr(OSCAP_EFAMILY_OSCAP, "Can't perform remediation in offline mode: not implemented"); return 1; } - if (system_is_in_bootc_mode()) { + if (_system_is_in_bootc_mode()) { oscap_seterr(OSCAP_EFAMILY_OSCAP, "Detected running Image Mode operating system. OpenSCAP can't " "perform remediation of this system because majority of the " From 3f00d7b2961aeb76734dc3b8a5ccfa8cc50b8d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Wed, 12 Mar 2025 13:16:51 +0100 Subject: [PATCH 3/6] Move error at the beginning of evaluation We don't like the current behavior when user needs to wait for the initial scan results just to see the error. We will move the error so it is printed right away and the initial scan is not even performed. --- src/XCCDF/xccdf_session.c | 34 ----------------------------- utils/oscap-xccdf.c | 45 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/src/XCCDF/xccdf_session.c b/src/XCCDF/xccdf_session.c index 507619d97c..5ec127be68 100644 --- a/src/XCCDF/xccdf_session.c +++ b/src/XCCDF/xccdf_session.c @@ -1906,32 +1906,6 @@ struct xccdf_rule_result_iterator *xccdf_session_get_rule_results(const struct x return xccdf_result_get_rule_results(session->xccdf.result); } -static bool _system_is_in_bootc_mode(void) -{ -#ifdef OS_WINDOWS - return false; -#else - #define BOOTC_PATH "/usr/bin/bootc" - struct stat statbuf; - if (stat(BOOTC_PATH, &statbuf) == -1) { - return false; - } - FILE *output = popen(BOOTC_PATH " status --format json 2>/dev/null", "r"); - if (output == NULL) { - return false; - } - char buf[1024] = {0}; - int c; - size_t i = 0; - while (i < sizeof(buf) && (c = fgetc(output)) != EOF) { - buf[i] = c; - i++; - } - pclose(output); - return *buf != '\0' && strstr(buf, "\"booted\":null") == NULL; -#endif -} - int xccdf_session_remediate(struct xccdf_session *session) { int res = 0; @@ -1943,14 +1917,6 @@ int xccdf_session_remediate(struct xccdf_session *session) oscap_seterr(OSCAP_EFAMILY_OSCAP, "Can't perform remediation in offline mode: not implemented"); return 1; } - if (_system_is_in_bootc_mode()) { - oscap_seterr(OSCAP_EFAMILY_OSCAP, - "Detected running Image Mode operating system. OpenSCAP can't " - "perform remediation of this system because majority of the " - "system is read-only. Please apply remediation during bootable " - "container image build using 'oscap-im' instead."); - return 1; - } xccdf_policy_model_unregister_engines(session->xccdf.policy_model, oval_sysname); if ((res = xccdf_session_load_oval(session)) != 0) return res; diff --git a/utils/oscap-xccdf.c b/utils/oscap-xccdf.c index 54680b3595..7150387a0f 100644 --- a/utils/oscap-xccdf.c +++ b/utils/oscap-xccdf.c @@ -586,6 +586,33 @@ int xccdf_set_profile_or_report_bad_id(struct xccdf_session *session, const char return return_code; } + +static bool _system_is_in_bootc_mode(void) +{ +#ifdef OS_WINDOWS + return false; +#else + #define BOOTC_PATH "/usr/bin/bootc" + struct stat statbuf; + if (stat(BOOTC_PATH, &statbuf) == -1) { + return false; + } + FILE *output = popen(BOOTC_PATH " status --format json 2>/dev/null", "r"); + if (output == NULL) { + return false; + } + char buf[1024] = {0}; + int c; + size_t i = 0; + while (i < sizeof(buf) && (c = fgetc(output)) != EOF) { + buf[i] = c; + i++; + } + pclose(output); + return *buf != '\0' && strstr(buf, "\"booted\":null") == NULL; +#endif +} + /** * XCCDF Processing fucntion * @param action OSCAP Action structure @@ -596,6 +623,16 @@ int app_evaluate_xccdf(const struct oscap_action *action) struct xccdf_session *session = NULL; int result = OSCAP_ERROR; + + if (action->remediate && _system_is_in_bootc_mode()) { + fprintf(stderr, + "Detected running Image Mode operating system. OpenSCAP can't " + "perform remediation of this system because majority of the " + "system is read-only. Please apply remediation during bootable " + "container image build using 'oscap-im' instead."); + return result; + } + #if defined(HAVE_SYSLOG_H) int priority = LOG_NOTICE; @@ -797,6 +834,14 @@ int app_xccdf_remediate(const struct oscap_action *action) { struct xccdf_session *session = NULL; int result = OSCAP_ERROR; + if (_system_is_in_bootc_mode()) { + fprintf(stderr, + "Detected running Image Mode operating system. OpenSCAP can't " + "perform remediation of this system because majority of the " + "system is read-only. Please apply remediation during bootable " + "container image build using 'oscap-im' instead."); + return result; + } session = xccdf_session_new(action->f_xccdf); if (session == NULL) goto cleanup; From febe1949565533910ac3c719857da5feffbaa04b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Wed, 12 Mar 2025 13:29:15 +0100 Subject: [PATCH 4/6] Add missing newlines --- utils/oscap-xccdf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/oscap-xccdf.c b/utils/oscap-xccdf.c index 7150387a0f..35077e470b 100644 --- a/utils/oscap-xccdf.c +++ b/utils/oscap-xccdf.c @@ -629,7 +629,7 @@ int app_evaluate_xccdf(const struct oscap_action *action) "Detected running Image Mode operating system. OpenSCAP can't " "perform remediation of this system because majority of the " "system is read-only. Please apply remediation during bootable " - "container image build using 'oscap-im' instead."); + "container image build using 'oscap-im' instead.\n"); return result; } @@ -839,7 +839,7 @@ int app_xccdf_remediate(const struct oscap_action *action) "Detected running Image Mode operating system. OpenSCAP can't " "perform remediation of this system because majority of the " "system is read-only. Please apply remediation during bootable " - "container image build using 'oscap-im' instead."); + "container image build using 'oscap-im' instead.\n"); return result; } session = xccdf_session_new(action->f_xccdf); From dae96d329d355a2e107c20b9659482ff830d3bb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Wed, 12 Mar 2025 16:12:18 +0100 Subject: [PATCH 5/6] Use buffer on heap --- utils/oscap-xccdf.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/utils/oscap-xccdf.c b/utils/oscap-xccdf.c index 35077e470b..4571f1c801 100644 --- a/utils/oscap-xccdf.c +++ b/utils/oscap-xccdf.c @@ -593,6 +593,7 @@ static bool _system_is_in_bootc_mode(void) return false; #else #define BOOTC_PATH "/usr/bin/bootc" + #define CHUNK_SIZE 1024 struct stat statbuf; if (stat(BOOTC_PATH, &statbuf) == -1) { return false; @@ -601,12 +602,21 @@ static bool _system_is_in_bootc_mode(void) if (output == NULL) { return false; } - char buf[1024] = {0}; + size_t buf_size = CHUNK_SIZE; + char *buf = calloc(buf_size, sizeof(char)); int c; size_t i = 0; - while (i < sizeof(buf) && (c = fgetc(output)) != EOF) { - buf[i] = c; - i++; + while ((c = fgetc(output)) != EOF) { + if (i >= buf_size) { + buf_size += CHUNK_SIZE; + char *new_buf = realloc(buf, buf_size); + if (new_buf == NULL) { + pclose(output); + return false; + } + buf = new_buf; + } + buf[i++] = c; } pclose(output); return *buf != '\0' && strstr(buf, "\"booted\":null") == NULL; From ef833138517ad162ee4498cbaed41df07c9ed3d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cern=C3=BD?= Date: Thu, 13 Mar 2025 08:04:47 +0100 Subject: [PATCH 6/6] Prevent a memory leak --- utils/oscap-xccdf.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/utils/oscap-xccdf.c b/utils/oscap-xccdf.c index 4571f1c801..e727662ae4 100644 --- a/utils/oscap-xccdf.c +++ b/utils/oscap-xccdf.c @@ -604,6 +604,10 @@ static bool _system_is_in_bootc_mode(void) } size_t buf_size = CHUNK_SIZE; char *buf = calloc(buf_size, sizeof(char)); + if (buf == NULL) { + pclose(output); + return false; + } int c; size_t i = 0; while ((c = fgetc(output)) != EOF) { @@ -619,7 +623,9 @@ static bool _system_is_in_bootc_mode(void) buf[i++] = c; } pclose(output); - return *buf != '\0' && strstr(buf, "\"booted\":null") == NULL; + bool result = (*buf != '\0' && strstr(buf, "\"booted\":null") == NULL); + free(buf); + return result; #endif }