diff --git a/src/OVAL/probes/independent/textfilecontent54_probe.c b/src/OVAL/probes/independent/textfilecontent54_probe.c index 24d766b918..8b31a029c5 100644 --- a/src/OVAL/probes/independent/textfilecontent54_probe.c +++ b/src/OVAL/probes/independent/textfilecontent54_probe.c @@ -151,23 +151,13 @@ static int process_file(const char *prefix, const char *path, const char *file, if (probe_path_is_blocked(whole_path, blocked_paths)) { goto cleanup; } - /* - * If stat() fails, don't report an error and just skip the file. - * This is an expected situation, because the fts_*() functions - * are called with the 'FTS_PHYSICAL' option. Normally, stumbling - * upon a symlink without a target would cause fts_read() to return - * the 'FTS_SLNONE' flag, but the 'FTS_PHYSICAL' option causes it - * to return 'FTS_SL' and the presence of a valid target has to - * be determined with stat(). - */ whole_path_with_prefix = oscap_path_join(prefix, whole_path); - if (stat(whole_path_with_prefix, &st) == -1) - goto cleanup; - if (!S_ISREG(st.st_mode)) - goto cleanup; - - fd = open(whole_path_with_prefix, O_RDONLY); + fd = open(whole_path_with_prefix, O_RDONLY | O_NONBLOCK); if (fd == -1) { + if (errno == ENOENT || errno == EACCES || errno == ENOTDIR + || errno == ENAMETOOLONG || errno == ELOOP) + goto cleanup; + SEXP_t *msg; msg = probe_msg_creatf(OVAL_MESSAGE_LEVEL_ERROR, "open(): '%s' %s.", whole_path, strerror(errno)); @@ -177,6 +167,19 @@ static int process_file(const char *prefix, const char *path, const char *file, ret = -1; goto cleanup; } + /* + * If fstat() fails, don't report an error and just skip the file. + * This is an expected situation, because the fts_*() functions + * are called with the 'FTS_PHYSICAL' option. Normally, stumbling + * upon a symlink without a target would cause fts_read() to return + * the 'FTS_SLNONE' flag, but the 'FTS_PHYSICAL' option causes it + * to return 'FTS_SL' and the presence of a valid target has to + * be determined with fstat(). + */ + if (fstat(fd, &st) == -1 + || !S_ISREG(st.st_mode) + || probe_fd_path_is_blocked(fd, prefix, blocked_paths)) + goto cleanup; do { buf_size += buf_inc; diff --git a/src/OVAL/probes/independent/textfilecontent_probe.c b/src/OVAL/probes/independent/textfilecontent_probe.c index 8689114001..b769d1bb88 100644 --- a/src/OVAL/probes/independent/textfilecontent_probe.c +++ b/src/OVAL/probes/independent/textfilecontent_probe.c @@ -58,9 +58,11 @@ #include #include +#include #include #include #include +#include #include #include "_seap.h" @@ -139,7 +141,7 @@ struct pfdata { static int process_file(const char *prefix, const char *path, const char *filename, void *arg, oval_schema_version_t over, struct oscap_list *blocked_paths) { struct pfdata *pfd = (struct pfdata *) arg; - int ret = 0, path_len, filename_len; + int ret = 0, path_len, filename_len, tmp_fd = -1; char *whole_path = NULL, *whole_path_with_prefix = NULL; FILE *fp = NULL; struct stat st; @@ -173,27 +175,24 @@ static int process_file(const char *prefix, const char *path, const char *filena if (probe_path_is_blocked(whole_path, blocked_paths)) { goto cleanup; } - - /* - * If stat() fails, don't report an error and just skip the file. - * This is an expected situation, because the fts_*() functions - * are called with the 'FTS_PHYSICAL' option. Normally, stumbling - * upon a symlink without a target would cause fts_read() to return - * the 'FTS_SLNONE' flag, but the 'FTS_PHYSICAL' option causes it - * to return 'FTS_SL' and the presence of a valid target has to - * be determined with stat(). - */ whole_path_with_prefix = oscap_path_join(prefix, whole_path); - if (stat(whole_path_with_prefix, &st) == -1) + tmp_fd = open(whole_path_with_prefix, O_RDONLY | O_NONBLOCK); + if (tmp_fd == -1) { + if (errno != ENOENT && errno != EACCES && errno != ENOTDIR + && errno != ENAMETOOLONG && errno != ELOOP) + ret = -2; goto cleanup; - if (!S_ISREG(st.st_mode)) + } + if (fstat(tmp_fd, &st) == -1 + || !S_ISREG(st.st_mode) + || probe_fd_path_is_blocked(tmp_fd, prefix, blocked_paths)) goto cleanup; - - fp = fopen(whole_path_with_prefix, "rb"); + fp = fdopen(tmp_fd, "rb"); if (fp == NULL) { ret = -2; goto cleanup; } + tmp_fd = -1; int cur_inst = 0; char line[4096]; @@ -218,6 +217,8 @@ static int process_file(const char *prefix, const char *path, const char *filena } cleanup: + if (tmp_fd != -1) + close(tmp_fd); if (fp != NULL) fclose(fp); if (whole_path != NULL) diff --git a/src/OVAL/probes/independent/yamlfilecontent_probe.c b/src/OVAL/probes/independent/yamlfilecontent_probe.c index 5a8c244deb..aee000d523 100644 --- a/src/OVAL/probes/independent/yamlfilecontent_probe.c +++ b/src/OVAL/probes/independent/yamlfilecontent_probe.c @@ -26,6 +26,10 @@ #include #include +#include +#include +#include +#include #include #include @@ -390,7 +394,8 @@ static int process_yaml(yaml_parser_t *parser, const char *yamlpath, SEXP_t *ite static int process_yaml_file(const char *prefix, const char *path, const char *filename, const char *yamlpath, probe_ctx *ctx) { - int ret = 0; + int ret = 0, tmp_fd = -1; + FILE *yaml_file = NULL; char *filepath = oscap_path_join(path, filename); if (probe_path_is_blocked(filepath, ctx->blocked_paths)) { @@ -402,12 +407,25 @@ static int process_yaml_file(const char *prefix, const char *path, const char *f yaml_parser_initialize(&parser); char *filepath_with_prefix = oscap_path_join(prefix, filepath); + struct stat st; - FILE *yaml_file = fopen(filepath_with_prefix, "r"); + tmp_fd = open(filepath_with_prefix, O_RDONLY | O_NONBLOCK); + if (tmp_fd == -1) { + if (errno != ENOENT && errno != EACCES && errno != ENOTDIR + && errno != ENAMETOOLONG && errno != ELOOP) + result_error("Unable to open file '%s': %s", filepath_with_prefix, strerror(errno)); + goto cleanup; + } + if (fstat(tmp_fd, &st) == -1 + || !S_ISREG(st.st_mode) + || probe_fd_path_is_blocked(tmp_fd, prefix, ctx->blocked_paths)) + goto cleanup; + yaml_file = fdopen(tmp_fd, "r"); if (yaml_file == NULL) { result_error("Unable to open file '%s': %s", filepath_with_prefix, strerror(errno)); goto cleanup; } + tmp_fd = -1; yaml_parser_set_input_file(&parser, yaml_file); @@ -429,6 +447,8 @@ static int process_yaml_file(const char *prefix, const char *path, const char *f } cleanup: + if (tmp_fd != -1) + close(tmp_fd); if (yaml_file != NULL) fclose(yaml_file); yaml_parser_delete(&parser); diff --git a/src/OVAL/probes/probe-api.c b/src/OVAL/probes/probe-api.c index d5f0ebeb28..1566d6bc3b 100644 --- a/src/OVAL/probes/probe-api.c +++ b/src/OVAL/probes/probe-api.c @@ -45,6 +45,8 @@ #include /* inet_pton() in probe_ent_from_cstr() */ #include #include +#include +#include #endif #include "debug_priv.h" @@ -1810,4 +1812,33 @@ bool probe_path_is_blocked(const char *path, struct oscap_list *blocked_paths) return res; } +bool probe_fd_path_is_blocked(int fd, const char *prefix, struct oscap_list *blocked_paths) +{ +#if defined(__linux__) + char proc_path[64]; + char resolved[PATH_MAX]; + const char *check_path; + + snprintf(proc_path, sizeof(proc_path), "/proc/self/fd/%d", fd); + ssize_t len = readlink(proc_path, resolved, sizeof(resolved) - 1); + if (len == -1) + return false; + resolved[len] = '\0'; + // blocked_paths are unprefixed, so we have strip prefix from check_path + check_path = resolved; + if (prefix && *prefix) { + size_t plen = strlen(prefix); + if (strncmp(resolved, prefix, plen) == 0) + check_path = resolved + plen; + } + + return probe_path_is_blocked(check_path, blocked_paths); +#else + (void)fd; + (void)prefix; + (void)blocked_paths; + return false; +#endif +} + /// @} diff --git a/src/OVAL/probes/public/probe-api.h b/src/OVAL/probes/public/probe-api.h index fbce8d27d2..ec47813b9e 100644 --- a/src/OVAL/probes/public/probe-api.h +++ b/src/OVAL/probes/public/probe-api.h @@ -546,4 +546,13 @@ OSCAP_API SEXP_t *probe_obj_getmask(SEXP_t *obj); */ OSCAP_API bool probe_path_is_blocked(const char *path, struct oscap_list *blocked_paths); +/** + * Check if the real path of an open file descriptor matches any blocked path. + * Resolves the fd target via /proc/self/fd on Linux; returns false on other platforms. + * @param fd open file descriptor + * @param prefix OSCAP_PROBE_ROOT prefix to strip from resolved path, or NULL + * @param blocked_paths list of blocked paths + */ +OSCAP_API bool probe_fd_path_is_blocked(int fd, const char *prefix, struct oscap_list *blocked_paths); + /// @} diff --git a/tests/probes/textfilecontent54/test_symlinks.sh b/tests/probes/textfilecontent54/test_symlinks.sh index 79135711a6..a9bf18cb74 100755 --- a/tests/probes/textfilecontent54/test_symlinks.sh +++ b/tests/probes/textfilecontent54/test_symlinks.sh @@ -15,6 +15,10 @@ sed "s@%PATH%@${tmpdir}@" $tpl > $input ln -s /etc/hosts ${tmpdir}/sl1 ln -s /nonexistent ${tmpdir}/sl2 ln -s ${tmpdir} ${tmpdir}/sl3 +mkdir -p ${tmpdir}/blocked +echo "blocked_content" > ${tmpdir}/blocked/secret +ln -s ${tmpdir}/blocked/secret ${tmpdir}/sl4 +export OSCAP_PROBE_IGNORE_PATHS="${tmpdir}/blocked" echo "Evaluating content." $OSCAP oval eval --results $result $input || [ $? == 2 ] @@ -25,10 +29,12 @@ echo "Testing syschar values." [ "$($XPATH $result 'string(/oval_results/results/system/tests/test[@test_id="oval:x:tst:2"]/@result)')" == "false" ] [ "$($XPATH $result 'string(/oval_results/results/system/tests/test[@test_id="oval:x:tst:3"]/@result)')" == "false" ] [ "$($XPATH $result 'string(/oval_results/results/system/tests/test[@test_id="oval:x:tst:4"]/@result)')" == "true" ] +[ "$($XPATH $result 'string(/oval_results/results/system/tests/test[@test_id="oval:x:tst:5"]/@result)')" == "false" ] echo "Testing syschar values." [ "$($XPATH $result 'string(/oval_results/results/system/oval_system_characteristics/collected_objects/object[@id="oval:x:obj:1"]/@flag)')" == "complete" ] [ "$($XPATH $result 'string(/oval_results/results/system/oval_system_characteristics/collected_objects/object[@id="oval:x:obj:2"]/@flag)')" == "does not exist" ] [ "$($XPATH $result 'string(/oval_results/results/system/oval_system_characteristics/collected_objects/object[@id="oval:x:obj:3"]/@flag)')" == "does not exist" ] [ "$($XPATH $result 'string(/oval_results/results/system/oval_system_characteristics/collected_objects/object[@id="oval:x:obj:4"]/@flag)')" == "complete" ] +[ "$($XPATH $result 'string(/oval_results/results/system/oval_system_characteristics/collected_objects/object[@id="oval:x:obj:5"]/@flag)')" == "does not exist" ] rm -rf $tmpdir diff --git a/tests/probes/textfilecontent54/test_symlinks.xml.tpl b/tests/probes/textfilecontent54/test_symlinks.xml.tpl index f41ce9d278..6309cecd7f 100644 --- a/tests/probes/textfilecontent54/test_symlinks.xml.tpl +++ b/tests/probes/textfilecontent54/test_symlinks.xml.tpl @@ -19,6 +19,7 @@ + @@ -36,6 +37,9 @@ + + + @@ -63,5 +67,12 @@ .* 1 + + + %PATH% + sl4 + .* + 1 +