diff --git a/src/OVAL/probes/independent/filehash58_probe.c b/src/OVAL/probes/independent/filehash58_probe.c
index 3431c5118b..8ca8d239b7 100644
--- a/src/OVAL/probes/independent/filehash58_probe.c
+++ b/src/OVAL/probes/independent/filehash58_probe.c
@@ -158,12 +158,16 @@ static int filehash58_cb(const char *prefix, const char *p, const char *f, const
/*
* Open the file
+ *
+ * O_NONBLOCK ensures that open() does not block indefinitely when the
+ * target is a FIFO (named pipe) with no writer, or another special file
+ * whose open can block. For regular files O_NONBLOCK has no effect.
*/
if (prefix == NULL) {
- fd = open(pbuf, O_RDONLY);
+ fd = open(pbuf, O_RDONLY | O_NONBLOCK);
} else {
char *path_with_prefix = oscap_path_join(prefix, pbuf);
- fd = open(path_with_prefix, O_RDONLY);
+ fd = open(path_with_prefix, O_RDONLY | O_NONBLOCK);
free(path_with_prefix);
}
@@ -186,6 +190,45 @@ static int filehash58_cb(const char *prefix, const char *p, const char *f, const
return 0;
}
+ /*
+ * Make sure we only try to hash regular files. Hashing a FIFO, socket,
+ * device or other special file makes no sense and could block the read.
+ * The type is checked on the already-opened descriptor (fstat) to avoid
+ * a TOCTOU race with the preceding open().
+ */
+ struct stat st;
+ if (fstat(fd, &st) != 0) {
+ char errbuf[__ERRBUF_SIZE] = {0};
+ oscap_strerror_r(errno, errbuf, sizeof errbuf - 1);
+ itm = probe_item_create(OVAL_INDEPENDENT_FILE_HASH58, NULL,
+ "filepath", OVAL_DATATYPE_STRING, pbuf,
+ "path", OVAL_DATATYPE_STRING, p,
+ "filename", OVAL_DATATYPE_STRING, f,
+ "hash_type",OVAL_DATATYPE_STRING, h,
+ NULL);
+ probe_item_add_msg(itm, OVAL_MESSAGE_LEVEL_ERROR,
+ "File \"%s\" status can't be read: %s", pbuf, errbuf);
+ probe_item_setstatus(itm, SYSCHAR_STATUS_ERROR);
+
+ probe_item_collect(ctx, itm);
+ close(fd);
+ return 0;
+ } else if (!S_ISREG(st.st_mode)) {
+ itm = probe_item_create(OVAL_INDEPENDENT_FILE_HASH58, NULL,
+ "filepath", OVAL_DATATYPE_STRING, pbuf,
+ "path", OVAL_DATATYPE_STRING, p,
+ "filename", OVAL_DATATYPE_STRING, f,
+ "hash_type",OVAL_DATATYPE_STRING, h,
+ NULL);
+ probe_item_add_msg(itm, OVAL_MESSAGE_LEVEL_ERROR,
+ "File \"%s\" is not a regular file.", pbuf);
+ probe_item_setstatus(itm, SYSCHAR_STATUS_ERROR);
+
+ probe_item_collect(ctx, itm);
+ close(fd);
+ return 0;
+ }
+
uint8_t hash_dst[1025];
size_t hash_dstlen = sizeof(hash_dst);
char hash_str[2051];
diff --git a/src/OVAL/probes/independent/filehash_probe.c b/src/OVAL/probes/independent/filehash_probe.c
index c5caf8e1bd..b9461d1d07 100644
--- a/src/OVAL/probes/independent/filehash_probe.c
+++ b/src/OVAL/probes/independent/filehash_probe.c
@@ -53,6 +53,7 @@
#include "filehash_probe.h"
#define FILE_SEPARATOR '/'
+#define __ERRBUF_SIZE 128
static int mem2hex (uint8_t *mem, size_t mlen, char *str, size_t slen)
{
@@ -111,17 +112,20 @@ static int filehash_cb (const char *prefix, const char *p, const char *f, probe_
/*
* Open the file
+ *
+ * O_NONBLOCK ensures that open() does not block indefinitely when the
+ * target is a FIFO (named pipe) with no writer, or another special file
+ * whose open can block. For regular files O_NONBLOCK has no effect.
*/
if (prefix == NULL) {
- fd = open(pbuf, O_RDONLY);
+ fd = open(pbuf, O_RDONLY | O_NONBLOCK);
} else {
char *path_with_prefix = oscap_path_join(prefix, pbuf);
- fd = open(path_with_prefix, O_RDONLY);
+ fd = open(path_with_prefix, O_RDONLY | O_NONBLOCK);
free(path_with_prefix);
}
if (fd < 0) {
- #define __ERRBUF_SIZE 128
char errbuf[__ERRBUF_SIZE] = {0};
oscap_strerror_r(errno, errbuf, sizeof errbuf - 1);
@@ -136,6 +140,43 @@ static int filehash_cb (const char *prefix, const char *p, const char *f, probe_
probe_item_setstatus(itm, SYSCHAR_STATUS_ERROR);
} else {
+ /*
+ * Make sure we only try to hash regular files. Hashing a FIFO,
+ * socket, device or other special file makes no sense and could
+ * block the read. The type is checked on the already-opened
+ * descriptor (fstat) to avoid a TOCTOU race with the open().
+ */
+ struct stat st;
+ if (fstat(fd, &st) != 0) {
+ char errbuf[__ERRBUF_SIZE] = {0};
+ oscap_strerror_r(errno, errbuf, sizeof errbuf - 1);
+ itm = probe_item_create(OVAL_INDEPENDENT_FILE_HASH, NULL,
+ "filepath", OVAL_DATATYPE_STRING, include_filepath ? pbuf : NULL,
+ "path", OVAL_DATATYPE_STRING, p,
+ "filename", OVAL_DATATYPE_STRING, f,
+ NULL);
+ probe_item_add_msg(itm, OVAL_MESSAGE_LEVEL_ERROR,
+ "File \"%s\" status can't be read: %s", pbuf, errbuf);
+ probe_item_setstatus(itm, SYSCHAR_STATUS_ERROR);
+
+ probe_item_collect(ctx, itm);
+ close(fd);
+ return 0;
+ } else if (!S_ISREG(st.st_mode)) {
+ itm = probe_item_create(OVAL_INDEPENDENT_FILE_HASH, NULL,
+ "filepath", OVAL_DATATYPE_STRING, include_filepath ? pbuf : NULL,
+ "path", OVAL_DATATYPE_STRING, p,
+ "filename", OVAL_DATATYPE_STRING, f,
+ NULL);
+ probe_item_add_msg(itm, OVAL_MESSAGE_LEVEL_ERROR,
+ "File \"%s\" is not a regular file.", pbuf);
+ probe_item_setstatus(itm, SYSCHAR_STATUS_ERROR);
+
+ probe_item_collect(ctx, itm);
+ close(fd);
+ return 0;
+ }
+
uint8_t md5_dst[16];
size_t md5_dstlen = sizeof md5_dst;
char md5_str[(sizeof md5_dst * 2) + 1];
diff --git a/tests/probes/filehash/CMakeLists.txt b/tests/probes/filehash/CMakeLists.txt
index dab1d4a50e..abc26f96e0 100644
--- a/tests/probes/filehash/CMakeLists.txt
+++ b/tests/probes/filehash/CMakeLists.txt
@@ -1,3 +1,4 @@
if(OPENSCAP_PROBE_INDEPENDENT_FILEHASH)
add_oscap_test("test_probes_filehash.sh" LABELS independent)
+ add_oscap_test("test_probes_filehash_dos.sh" LABELS independent)
endif()
diff --git a/tests/probes/filehash/dos.oval.xml b/tests/probes/filehash/dos.oval.xml
new file mode 100644
index 0000000000..59f20250ec
--- /dev/null
+++ b/tests/probes/filehash/dos.oval.xml
@@ -0,0 +1,40 @@
+
+
+ combine_ovals.py from SCAP Security Guide
+ ssg: [0, 1, 40], python: 3.6.5
+ 5.11
+ 2018-07-20T09:33:24
+
+
+
+
+ Test that reading a FIFO (named pipe) by the filehash probe doesn't freeze OpenSCAP.
+
+ Red Hat Enterprise Linux 7
+
+ description
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /tmp/oscap-test-fifo
+
+
+ /tmp/oscap-test-symlink
+
+
+
diff --git a/tests/probes/filehash/test_probes_filehash_dos.sh b/tests/probes/filehash/test_probes_filehash_dos.sh
new file mode 100755
index 0000000000..3bcca87af7
--- /dev/null
+++ b/tests/probes/filehash/test_probes_filehash_dos.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+
+# Copyright 2021 Red Hat Inc., Durham, North Carolina.
+# All Rights Reserved.
+#
+# OpenSCAP Probes Test Suite.
+#
+# Authors:
+# Jan Černý,
+
+set -e -o pipefail
+set -x
+. $builddir/tests/test_common.sh
+
+probecheck "filehash" || return 255
+require "md5sum" || return 255
+require "sha1sum" || return 255
+
+# Block the scan indefinitely
+rm -f /tmp/oscap-test-fifo
+mkfifo /tmp/oscap-test-fifo
+
+# Burn CPU forever
+rm -f /tmp/oscap-test-symlink
+ln -s /dev/zero /tmp/oscap-test-symlink
+
+result="$(mktemp)"
+
+$OSCAP oval eval --results "$result" "$srcdir/dos.oval.xml"
+[[ $? -eq 0 ]]
+assert_exists 1 '/oval_results/results/system/tests/test[@test_id="oval:xxx:tst:1"][@result="error"]'
+assert_exists 1 '/oval_results/results/system/tests/test[@test_id="oval:xxx:tst:2"][@result="error"]'
+
+rm -f /tmp/oscap-test-fifo
+rm -f /tmp/oscap-test-symlink
+rm -f "$result"
diff --git a/tests/probes/filehash58/CMakeLists.txt b/tests/probes/filehash58/CMakeLists.txt
index bd0bc0a690..2ae98ce8b7 100644
--- a/tests/probes/filehash58/CMakeLists.txt
+++ b/tests/probes/filehash58/CMakeLists.txt
@@ -1,4 +1,5 @@
if(ENABLE_PROBES_INDEPENDENT)
add_oscap_test("test_probes_filehash58.sh" LABELS independent)
+ add_oscap_test("test_probes_filehash58_dos.sh" LABELS independent)
add_oscap_test("rhbz1959570_segfault.sh" LABELS independent)
endif()
diff --git a/tests/probes/filehash58/dos.oval.xml b/tests/probes/filehash58/dos.oval.xml
new file mode 100644
index 0000000000..dbaa09742f
--- /dev/null
+++ b/tests/probes/filehash58/dos.oval.xml
@@ -0,0 +1,42 @@
+
+
+ combine_ovals.py from SCAP Security Guide
+ ssg: [0, 1, 40], python: 3.6.5
+ 5.11
+ 2018-07-20T09:33:24
+
+
+
+
+ Test that reading a FIFO (named pipe) by the filehash58 probe doesn't freeze OpenSCAP.
+
+ Red Hat Enterprise Linux 7
+
+ description
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /tmp/oscap-test-fifo
+ SHA-256
+
+
+ /tmp/oscap-test-symlink
+ SHA-256
+
+
+
diff --git a/tests/probes/filehash58/test_probes_filehash58_dos.sh b/tests/probes/filehash58/test_probes_filehash58_dos.sh
new file mode 100755
index 0000000000..aa8c9bd29b
--- /dev/null
+++ b/tests/probes/filehash58/test_probes_filehash58_dos.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+
+# Copyright 2021 Red Hat Inc., Durham, North Carolina.
+# All Rights Reserved.
+#
+# OpenSCAP Probes Test Suite.
+#
+# Authors:
+# Jan Černý,
+
+set -e -o pipefail
+set -x
+. $builddir/tests/test_common.sh
+
+probecheck "filehash58" || return 255
+require "sha256sum" || return 255
+require "sha512sum" || return 255
+
+# Block the scan indefinitely
+rm -f /tmp/oscap-test-fifo
+mkfifo /tmp/oscap-test-fifo
+
+# Burn CPU forever
+rm -f /tmp/oscap-test-symlink
+ln -s /dev/zero /tmp/oscap-test-symlink
+
+result="$(mktemp)"
+
+$OSCAP oval eval --results "$result" "$srcdir/dos.oval.xml"
+[[ $? -eq 0 ]]
+assert_exists 1 '/oval_results/results/system/tests/test[@test_id="oval:xxx:tst:1"][@result="error"]'
+assert_exists 1 '/oval_results/results/system/tests/test[@test_id="oval:xxx:tst:2"][@result="error"]'
+
+rm -f /tmp/oscap-test-fifo
+rm -f /tmp/oscap-test-symlink
+rm -f "$result"
+