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" +