Blob Blame History Raw
From cd1d4289581fa15527e516ddd07be814af7cba55 Mon Sep 17 00:00:00 2001
From: Evgeny Kolesnikov <ekolesni@redhat.com>
Date: Mon, 28 Aug 2023 19:53:20 +0200
Subject: [PATCH 1/4] Add PCRE2 library

Recognize PCRE2 library in the build system.

Add wrapper for pcre_/pcre2_ functions.
---
 CMakeLists.txt          |  16 ++-
 cmake/FindPCRE2.cmake   |  27 +++++
 config.h.in             |   2 +
 src/CMakeLists.txt      |   2 +-
 src/common/oscap_pcre.c | 257 ++++++++++++++++++++++++++++++++++++++++
 src/common/oscap_pcre.h | 106 +++++++++++++++++
 6 files changed, 408 insertions(+), 2 deletions(-)
 create mode 100644 cmake/FindPCRE2.cmake
 create mode 100644 src/common/oscap_pcre.c
 create mode 100644 src/common/oscap_pcre.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 74628cdd466..b6e5a825dc1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -106,7 +106,11 @@ find_package(Doxygen)
 find_package(GConf)
 find_package(Ldap)
 find_package(OpenDbx)
-find_package(PCRE REQUIRED)
+if(WITH_PCRE2)
+	find_package(PCRE2 REQUIRED)
+else()
+	find_package(PCRE REQUIRED)
+endif()
 find_package(PerlLibs)
 find_package(Popt)
 find_package(Systemd)
@@ -193,6 +197,13 @@ else()
 	set(YAML_FILTER_FOUND FALSE)
 endif()
 
+if(PCRE2_FOUND)
+	set(HAVE_PCRE2 1)
+	message("-- Using PCRE2")
+elseif(PCRE_FOUND)
+	message("-- Using PCRE")
+endif()
+
 check_library_exists(rt clock_gettime "" HAVE_CLOCK_GETTIME)
 check_function_exists(posix_memalign HAVE_POSIX_MEMALIGN)
 check_function_exists(memalign HAVE_MEMALIGN)
@@ -330,6 +341,8 @@ cmake_dependent_option(ENABLE_OSCAP_UTIL_CHROOT "enables the oscap-chroot utilit
 option(ENABLE_OSCAP_UTIL_AUTOTAILOR "enables the autotailor utility that is able to perform command-line tailoring" TRUE)
 option(ENABLE_OSCAP_REMEDIATE_SERVICE "enables the oscap-remediate service" FALSE)
 
+option(WITH_PCRE2 "use PCRE2 library" FALSE)
+
 # ---------- TEST-SUITE SWITCHES
 
 # Tests will be turned off on Windows, because the test suite uses bash
@@ -563,6 +576,7 @@ include_directories(
 	${XMLSEC_INCLUDE_DIRS}
 	${OPENSSL_INCLUDE_DIR}
 	${PCRE_INCLUDE_DIRS}
+	${PCRE2_INCLUDE_DIRS}
 )
 
 # Honor visibility properties for all target types
diff --git a/cmake/FindPCRE2.cmake b/cmake/FindPCRE2.cmake
new file mode 100644
index 00000000000..0c430b1fd22
--- /dev/null
+++ b/cmake/FindPCRE2.cmake
@@ -0,0 +1,27 @@
+# - Find pcre
+# Find the native PCRE2 headers and libraries.
+#
+# PCRE2_INCLUDE_DIRS	- where to find pcre2.h, etc.
+# PCRE2_LIBRARIES	- List of libraries when using pcre.
+# PCRE2_FOUND	- True if pcre found.
+
+# Look for the header file.
+FIND_PATH(PCRE2_INCLUDE_DIR pcre2.h)
+
+# Look for the library.
+FIND_LIBRARY(PCRE2_LIBRARY NAMES libpcre2.a pcre2-8)
+
+# Handle the QUIETLY and REQUIRED arguments and set PCRE2_FOUND to TRUE if all listed variables are TRUE.
+INCLUDE(FindPackageHandleStandardArgs)
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE2 DEFAULT_MSG PCRE2_LIBRARY PCRE2_INCLUDE_DIR)
+
+# Copy the results to the output variables.
+IF(PCRE2_FOUND)
+    SET(PCRE2_LIBRARIES ${PCRE2_LIBRARY})
+    SET(PCRE2_INCLUDE_DIRS ${PCRE2_INCLUDE_DIR})
+ELSE(PCRE2_FOUND)
+    SET(PCRE_LIBRARIES)
+    SET(PCRE_INCLUDE_DIRS)
+ENDIF(PCRE2_FOUND)
+
+MARK_AS_ADVANCED(PCRE2_INCLUDE_DIRS PCRE2_LIBRARIES)
diff --git a/config.h.in b/config.h.in
index db8afd91410..2bba00719c2 100644
--- a/config.h.in
+++ b/config.h.in
@@ -29,6 +29,8 @@
 #define OSCAP_DEFAULT_CPE_PATH "@OSCAP_DEFAULT_CPE_PATH@"
 #define OSCAP_TEMP_DIR "@OSCAP_TEMP_DIR@"
 
+#cmakedefine HAVE_PCRE2
+
 #cmakedefine HAVE_ATOMIC_BUILTINS
 
 #cmakedefine HAVE_ACL_EXTENDED_FILE
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e9339c62ed1..7fa94ff981f 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -81,7 +81,7 @@ set_target_properties(openscap PROPERTIES
 	C_VISIBILITY_PRESET hidden
 )
 
-target_link_libraries(openscap ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${XMLSEC_LIBRARIES} ${OPENSSL_LIBRARIES} ${LIBXSLT_EXSLT_LIBRARIES} ${PCRE_LIBRARIES} ${CURL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
+target_link_libraries(openscap ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${XMLSEC_LIBRARIES} ${OPENSSL_LIBRARIES} ${LIBXSLT_EXSLT_LIBRARIES} ${PCRE_LIBRARIES} ${PCRE2_LIBRARIES} ${CURL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT})
 if (BZIP2_FOUND)
 	target_link_libraries(openscap ${BZIP2_LIBRARIES})
 endif()
diff --git a/src/common/oscap_pcre.c b/src/common/oscap_pcre.c
new file mode 100644
index 00000000000..d2e45292ab1
--- /dev/null
+++ b/src/common/oscap_pcre.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2023 Red Hat Inc., Durham, North Carolina.
+ * All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ *      Evgenii Kolesnikov <ekolesni@redhat.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <memory.h>
+
+#ifdef HAVE_PCRE2
+#define PCRE2_CODE_UNIT_WIDTH 8
+#define PCRE2_ERR_BUF_SIZE 127
+#include <pcre2.h>
+#else
+#include <pcre.h>
+#endif
+
+#include "debug_priv.h"
+#include "oscap_pcre.h"
+
+
+struct oscap_pcre {
+#ifdef HAVE_PCRE2
+	pcre2_code_8           *re;
+	pcre2_match_context_8  *re_ctx;
+#else
+	pcre                   *re;
+	struct pcre_extra      *re_extra;
+#endif
+};
+
+
+static inline int _oscap_pcre_opts_to_pcre(oscap_pcre_options_t opts)
+{
+	int res = 0;
+#ifdef HAVE_PCRE2
+	if (opts & OSCAP_PCRE_OPTS_UTF8)
+		res |= PCRE2_UTF;
+	if (opts & OSCAP_PCRE_OPTS_MULTILINE)
+		res |= PCRE2_MULTILINE;
+	if (opts & OSCAP_PCRE_OPTS_DOTALL)
+		res |= PCRE2_DOTALL;
+	if (opts & OSCAP_PCRE_OPTS_CASELESS)
+		res |= PCRE2_CASELESS;
+	if (opts & OSCAP_PCRE_OPTS_NO_UTF8_CHECK)
+		res |= PCRE2_NO_UTF_CHECK;
+	if (opts & OSCAP_PCRE_OPTS_PARTIAL)
+		res |= PCRE2_PARTIAL_SOFT;
+#else
+	if (opts & OSCAP_PCRE_OPTS_UTF8)
+		res |= PCRE_UTF8;
+	if (opts & OSCAP_PCRE_OPTS_MULTILINE)
+		res |= PCRE_MULTILINE;
+	if (opts & OSCAP_PCRE_OPTS_DOTALL)
+		res |= PCRE_DOTALL;
+	if (opts & OSCAP_PCRE_OPTS_CASELESS)
+		res |= PCRE_CASELESS;
+	if (opts & OSCAP_PCRE_OPTS_NO_UTF8_CHECK)
+		res |= PCRE_NO_UTF8_CHECK;
+	if (opts & OSCAP_PCRE_OPTS_PARTIAL)
+		res |= PCRE_PARTIAL;
+#endif
+	return res;
+};
+
+static inline oscap_pcre_error_t _pcre_error_to_oscap_pcre(int res)
+{
+	switch (res) {
+#ifdef HAVE_PCRE2
+	case PCRE2_ERROR_NOMATCH:
+		return OSCAP_PCRE_ERR_NOMATCH;
+	case PCRE2_ERROR_PARTIAL:
+		return OSCAP_PCRE_ERR_PARTIAL;
+	case PCRE2_ERROR_UTF8_ERR1:
+	case PCRE2_ERROR_UTF8_ERR2:
+	case PCRE2_ERROR_UTF8_ERR3:
+	case PCRE2_ERROR_UTF8_ERR4:
+	case PCRE2_ERROR_UTF8_ERR5:
+	case PCRE2_ERROR_UTF8_ERR6:
+	case PCRE2_ERROR_UTF8_ERR7:
+	case PCRE2_ERROR_UTF8_ERR8:
+	case PCRE2_ERROR_UTF8_ERR9:
+	case PCRE2_ERROR_UTF8_ERR10:
+	case PCRE2_ERROR_UTF8_ERR11:
+	case PCRE2_ERROR_UTF8_ERR12:
+	case PCRE2_ERROR_UTF8_ERR13:
+	case PCRE2_ERROR_UTF8_ERR14:
+	case PCRE2_ERROR_UTF8_ERR15:
+	case PCRE2_ERROR_UTF8_ERR16:
+	case PCRE2_ERROR_UTF8_ERR17:
+	case PCRE2_ERROR_UTF8_ERR18:
+	case PCRE2_ERROR_UTF8_ERR19:
+	case PCRE2_ERROR_UTF8_ERR20:
+	case PCRE2_ERROR_UTF8_ERR21:
+		return OSCAP_PCRE_ERR_BADUTF8;
+	case PCRE2_ERROR_RECURSIONLIMIT:
+		return OSCAP_PCRE_ERR_RECURSIONLIMIT;
+#else
+	case PCRE_ERROR_NOMATCH:
+		return OSCAP_PCRE_ERR_NOMATCH;
+	case PCRE_ERROR_PARTIAL:
+		return OSCAP_PCRE_ERR_PARTIAL;
+	case PCRE_ERROR_BADPARTIAL:
+		return OSCAP_PCRE_ERR_BADPARTIAL;
+	case PCRE_ERROR_BADUTF8:
+		return OSCAP_PCRE_ERR_BADUTF8;
+	case PCRE_ERROR_RECURSIONLIMIT:
+		return OSCAP_PCRE_ERR_RECURSIONLIMIT;
+#endif
+	default:
+		dW("Unknown PCRE error code: %d", res);
+		return OSCAP_PCRE_ERR_UNKNOWN;
+	}
+}
+
+oscap_pcre_t *oscap_pcre_compile(const char *pattern, oscap_pcre_options_t options,
+                                 char **errptr, int *erroffset)
+{
+	oscap_pcre_t *res = malloc(sizeof(oscap_pcre_t));
+#ifdef HAVE_PCRE2
+	int errno;
+	PCRE2_SIZE erroffset2;
+	res->re_ctx = NULL;
+	dD("pcre2_compile_8: patt=%s", pattern);
+	res->re = pcre2_compile_8((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, _oscap_pcre_opts_to_pcre(options), &errno, &erroffset2, NULL);
+	if (res->re == NULL) {
+		PCRE2_UCHAR8 errmsg[PCRE2_ERR_BUF_SIZE];
+		pcre2_get_error_message_8(errno, errmsg, PCRE2_ERR_BUF_SIZE);
+		dW("pcre2_compile_8: error (at offset %d): %s", erroffset2, errmsg);
+		*erroffset = erroffset2;
+		*errptr = strdup((const char*)errmsg);
+	}
+#else
+	res->re_extra = NULL;
+	dD("pcre_compile: patt=%s", pattern);
+	res->re = pcre_compile(pattern, _oscap_pcre_opts_to_pcre(options), (const char **)errptr, erroffset, NULL);
+	if (res->re == NULL)
+		dW("pcre_compile: error (at offset %d): %s", *erroffset, *errptr);
+#endif
+	if (res->re == NULL) {
+		free(res);
+		res = NULL;
+	}
+	return res;
+}
+
+void oscap_pcre_optimize(oscap_pcre_t *opcre)
+{
+#ifdef HAVE_PCRE2
+	// This is a NOOP for PCRE2 as all patterns are optimized
+	// unless the library configured differently.
+#else
+	const char *errptr = NULL;
+	pcre_extra *extra = pcre_study(opcre->re, 0, &errptr);
+	if (extra != NULL) {
+		if (opcre->re_extra != NULL)
+			free(opcre->re_extra);
+		opcre->re_extra = extra;
+	}
+#endif
+}
+
+void oscap_pcre_set_match_limit_recursion(oscap_pcre_t *opcre, unsigned long limit)
+{
+#ifdef HAVE_PCRE2
+	if (opcre->re_ctx == NULL) {
+		opcre->re_ctx = pcre2_match_context_create_8(NULL);
+	}
+	pcre2_set_depth_limit_8(opcre->re_ctx, limit);
+#else
+	if (opcre->re_extra == NULL) {
+		opcre->re_extra = calloc(1, sizeof(struct pcre_extra));
+	}
+	opcre->re_extra->match_limit_recursion = limit;
+	opcre->re_extra->flags = PCRE_EXTRA_MATCH_LIMIT_RECURSION;
+#endif
+}
+
+int oscap_pcre_exec(const oscap_pcre_t *opcre, const char *subject,
+                    int length, int startoffset, oscap_pcre_options_t options,
+                    int *ovector, int ovecsize)
+{
+	int rc = 0;
+#ifdef HAVE_PCRE2
+	// The ovecsize is multiplied by 3 in the code for compatibility with PCRE1
+	int ovecsize2 = ovecsize/3;
+	pcre2_match_data_8 *mdata = pcre2_match_data_create_8(ovecsize2, NULL);
+	dD("pcre2_match_8: subj=%s", subject);
+	rc = pcre2_match_8(opcre->re, (PCRE2_SPTR8)subject, length, startoffset, _oscap_pcre_opts_to_pcre(options), mdata, opcre->re_ctx);
+	dD("pcre2_match_8: rc=%d, ", rc);
+	if (rc > PCRE2_ERROR_NOMATCH) {
+		PCRE2_SIZE *ovecp = pcre2_get_ovector_pointer_8(mdata);
+		uint32_t ovecp_count = pcre2_get_ovector_count_8(mdata);
+		dD("pcre2_match_8: pcre2_get_ovector_count_8=%d", ovecp_count);
+		for (int i = 0; i < rc; i++) {
+			if (i < ovecsize2) {
+				ovector[i*2] = ovecp[i*2];
+				ovector[i*2+1] = ovecp[i*2+1];
+			}
+		}
+	}
+	pcre2_match_data_free_8(mdata);
+#else
+	dD("pcre_exec: subj=%s", subject);
+	rc = pcre_exec(opcre->re, opcre->re_extra, subject, length, startoffset, _oscap_pcre_opts_to_pcre(options), ovector, ovecsize);
+	dD("pcre_exec: rc=%d, ", rc);
+#endif
+	return rc >= 0 ? rc : _pcre_error_to_oscap_pcre(rc);
+}
+
+void oscap_pcre_free(oscap_pcre_t *opcre)
+{
+	if (opcre != NULL) {
+#ifdef HAVE_PCRE2
+		if (opcre->re_ctx != NULL)
+			pcre2_match_context_free_8(opcre->re_ctx);
+		pcre2_code_free_8(opcre->re);
+#else
+		if (opcre->re_extra != NULL)
+			free(opcre->re_extra);
+		pcre_free(opcre->re);
+#endif
+		free(opcre);
+	}
+}
+
+
+void oscap_pcre_err_free(char *err)
+{
+	if (err != NULL) {
+#ifdef HAVE_PCRE2
+		free(err);
+#else
+		// PCRE1 error messages are static, NOOP.
+#endif
+	}
+}
+
diff --git a/src/common/oscap_pcre.h b/src/common/oscap_pcre.h
new file mode 100644
index 00000000000..df8acd01543
--- /dev/null
+++ b/src/common/oscap_pcre.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2023 Red Hat Inc., Durham, North Carolina.
+ * All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ *      Evgenii Kolesnikov <ekolesni@redhat.com>
+ */
+
+#ifndef OSCAP_PCRE_
+#define OSCAP_PCRE_
+
+typedef struct oscap_pcre oscap_pcre_t;
+
+typedef enum {
+	OSCAP_PCRE_OPTS_NONE                        = 0x0001,
+	OSCAP_PCRE_OPTS_UTF8                        = 0x0002,
+	OSCAP_PCRE_OPTS_MULTILINE                   = 0x0004,
+	OSCAP_PCRE_OPTS_DOTALL                      = 0x0008,
+	OSCAP_PCRE_OPTS_CASELESS                    = 0x0010,
+	OSCAP_PCRE_OPTS_NO_UTF8_CHECK               = 0x0020,
+	OSCAP_PCRE_OPTS_PARTIAL                     = 0x0040,
+} oscap_pcre_options_t;
+
+typedef enum {
+	OSCAP_PCRE_ERR_NOMATCH                      = -1,
+	OSCAP_PCRE_ERR_PARTIAL                      = -2,
+	OSCAP_PCRE_ERR_BADPARTIAL                   = -3,
+	OSCAP_PCRE_ERR_BADUTF8                      = -10,
+	OSCAP_PCRE_ERR_RECURSIONLIMIT               = -21,
+	OSCAP_PCRE_ERR_UNKNOWN                      = -100,
+} oscap_pcre_error_t;
+
+
+/**
+ * Compile a regular expression string into PCRE object and returns it.
+ * Caller is responsible for freeing the returned object or the error message
+ * if the result is NULL (USE oscap_pcre_err_free()!).
+ * @param pattern expresstion string
+ * @param options compile options
+ * @param errptr a return value for a string representation of error
+ * @param erroffset the offset in the expression where the problem was detected
+ * @return a PCRE object
+ * NULL on failure
+ */
+oscap_pcre_t* oscap_pcre_compile(const char *pattern, oscap_pcre_options_t options,
+                                 char **errptr, int *erroffset);
+
+/**
+ * Execute the compiled regular expression against a string subject and returns
+ * matches count (or a negative error code).
+ * @param opcre the oscap_pcre_t object
+ * @param subject target string
+ * @param length target string length
+ * @param startoffset the offset for the target string
+ * @param options match options
+ * @param ovector the output vector for match offset pairs, see pcre_exec for details
+ * @param ovecsize the size of ovector, see pcre_exec for details
+ * @return matches count
+ * negative error code on failure
+ */
+int oscap_pcre_exec(const oscap_pcre_t *opcre, const char *subject,
+                    int length, int startoffset, oscap_pcre_options_t options,
+                    int *ovector, int ovecsize);
+
+/**
+ * Free the compiled regular expression object.
+ * @param opcre the oscap_pcre_t object
+ */
+void oscap_pcre_free(oscap_pcre_t *opcre);
+
+/**
+ * Limit the compiled regular expression object's recursion depth for future
+ * matches.
+ * @param opcre the oscap_pcre_t object
+ * @param limit maximum depth
+ */
+void oscap_pcre_set_match_limit_recursion(oscap_pcre_t *opcre, unsigned long limit);
+
+/**
+ * Optimize the compiled regular expression object to increase matching speed.
+ * @param opcre the oscap_pcre_t object
+ */
+void oscap_pcre_optimize(oscap_pcre_t *opcre);
+
+
+/**
+ * Free the error message returned by oscap_pcre_compile. DON'T USE REGULAR free()!
+ * @param err the message
+ */
+void oscap_pcre_err_free(char *err);
+
+#endif

From b34bcbbf559a4f535c936d8e4dcde56da91b1ba0 Mon Sep 17 00:00:00 2001
From: Evgeny Kolesnikov <ekolesni@redhat.com>
Date: Mon, 28 Aug 2023 19:55:52 +0200
Subject: [PATCH 2/4] Add PCRE2 library #2

Use oscap_pcre_* wrapper in the code.
---
 docs/developer/developer.adoc                 |   8 +-
 docs/manual/manual.adoc                       |   2 +-
 src/CPE/cpename.c                             |  36 ++--
 src/OVAL/oval_component.c                     |  31 +--
 src/OVAL/oval_schema_version.c                |  14 +-
 .../probes/independent/system_info_probe.c    |  13 +-
 .../independent/textfilecontent54_probe.c     |  26 +--
 .../independent/textfilecontent_probe.c       |  11 +-
 .../independent/yamlfilecontent_probe.c       |  13 +-
 src/OVAL/probes/oval_fts.c                    | 202 +++---------------
 src/OVAL/probes/oval_fts.h                    |   5 +-
 src/OVAL/probes/unix/gconf_probe.c            |   1 -
 src/OVAL/probes/unix/linux/partition_probe.c  |  13 +-
 src/OVAL/probes/unix/linux/rpmverify_probe.c  |  11 +-
 .../probes/unix/linux/rpmverifyfile_probe.c   |  20 +-
 .../unix/linux/rpmverifypackage_probe.c       |   1 -
 src/OVAL/results/oval_cmp_basic.c             |  21 +-
 src/XCCDF/item.c                              |  14 +-
 src/XCCDF_POLICY/xccdf_policy_remediate.c     |  28 +--
 src/common/util.c                             |  30 ++-
 src/common/util.h                             |   4 +-
 .../OVAL/schema_version/test_schema_version.c |   5 +
 tests/API/XCCDF/unittests/CMakeLists.txt      |   1 +
 tests/API/probes/CMakeLists.txt               |   1 +
 tests/API/probes/oval_fts_list.c              |   6 +
 .../textfilecontent54/test_recursion_limit.sh |   2 +-
 tests/probes/xinetd/CMakeLists.txt            |   1 +
 27 files changed, 205 insertions(+), 315 deletions(-)

diff --git a/docs/developer/developer.adoc b/docs/developer/developer.adoc
index 52502b60ff1..f1a39b3e2b4 100644
--- a/docs/developer/developer.adoc
+++ b/docs/developer/developer.adoc
@@ -89,6 +89,12 @@ libxml-xpath-perl libperl-dev libbz2-dev librpm-dev g++ libapt-pkg-dev libyaml-d
 libxmlsec1-dev libxmlsec1-openssl
 ----
 
+Since version 1.3.9 OpenSCAP can use either PCRE or PCRE2 library to handle regular
+expressions. The default behaviour for 1.3.x line of versions is to try and link
+with PCRE. In order to switxh the build system to PCRE2 one must configure
+CMake with '-DWITH_PCRE2=True' parameter. Dependencies: Debian/Ubuntu — pcre2-dev,
+Fedora/RHEL — pcre2-devel.
+
 When you have all the build dependencies installed you can build the library.
 --
 
@@ -319,7 +325,7 @@ behaviour.
 * *OSCAP_FULL_VALIDATION=1* - validate all exported documents (slower)
 * *SEXP_VALIDATE_DISABLE=1* - do not validate SEXP expressions (faster)
 * *OSCAP_PCRE_EXEC_RECURSION_LIMIT* - override default recursion limit
-  for match in pcre_exec call in textfilecontent(54) probes.
+  for match in pcre_exec/pcre2_match calls in textfilecontent(54) probes.
 
 
 
diff --git a/docs/manual/manual.adoc b/docs/manual/manual.adoc
index d4ecbdd4335..77f7b32e454 100644
--- a/docs/manual/manual.adoc
+++ b/docs/manual/manual.adoc
@@ -1613,7 +1613,7 @@ not considered local by the scanner:
 * `OSCAP_EVALUATION_TARGET` - Change value of target facts `urn:xccdf:fact:identifier` and `urn:xccdf:fact:asset:identifier:ein` in XCCDF results. Used during offline scanning to pass the name of the target system.
 * `OSCAP_FULL_VALIDATION` - If set, XML schema validation will be performed in every step of SCAP content processing.
 * `OSCAP_OVAL_COMMAND_OPTIONS` - Additional command line options for `oscap oval` module. The value of this environment variable is appended to the actual command line options of `oscap` command.
-* `OSCAP_PCRE_EXEC_RECURSION_LIMIT` - Set recursion limit of regular expression matching using `pcre_exec` function.
+* `OSCAP_PCRE_EXEC_RECURSION_LIMIT` - Set recursion limit of regular expression matching using `pcre_exec`/`pcre2_match` functions.
 * `OSCAP_PROBE_ROOT` - Path to a directory which contains mounted filesystem to be evaluated. Used for offline scanning.
 * `SEXP_VALIDATE_DISABLE` - If set, `oscap` will not validate SEXP expressions during its execution.
 * `SOURCE_DATE_EPOCH` - Timestamp in seconds since epoch. This timestamp will be used instead of the current time to populate `timestamp` attributes in SCAP source data streams created by `oscap ds sds-compose` sub-module. This is used for reproducible builds of data streams.
diff --git a/src/CPE/cpename.c b/src/CPE/cpename.c
index e2e9230f75c..9030149846f 100644
--- a/src/CPE/cpename.c
+++ b/src/CPE/cpename.c
@@ -37,11 +37,11 @@
 
 #include <string.h>
 #include <stdio.h>
-#include <pcre.h>
 #include <ctype.h>
 
 #include "cpe_name.h"
 #include "common/util.h"
+#include "common/oscap_pcre.h"
 #include "oscap_helpers.h"
 
 #define CPE_URI_SUPPORTED "2.3"
@@ -549,8 +549,8 @@ cpe_format_t cpe_name_get_format_of_str(const char *str)
 	if (str == NULL)
 		return CPE_FORMAT_UNKNOWN;
 
-	pcre *re;
-	const char *error;
+	oscap_pcre_t *re;
+	char *error;
 	int erroffset;
 	int rc;
 	int ovector[30];
@@ -559,26 +559,38 @@ cpe_format_t cpe_name_get_format_of_str(const char *str)
 	// http://scap.nist.gov/schema/cpe/2.3/cpe-naming_2.3.xsd
 	// [c] was replaced with [cC] here and in the schemas
 
-	re = pcre_compile("^[cC][pP][eE]:/[AHOaho]?(:[A-Za-z0-9\\._\\-~%]*){0,6}$", 0, &error, &erroffset, NULL);
-	rc = pcre_exec(re, NULL, str, strlen(str), 0, 0, ovector, 30);
-	pcre_free(re);
+	re = oscap_pcre_compile("^[cC][pP][eE]:/[AHOaho]?(:[A-Za-z0-9\\._\\-~%]*){0,6}$", 0, &error, &erroffset);
+	if (re == NULL) {
+		oscap_pcre_err_free(error);
+		return CPE_FORMAT_UNKNOWN;
+	}
+	rc = oscap_pcre_exec(re, str, strlen(str), 0, 0, ovector, 30);
+	oscap_pcre_free(re);
 
 	if (rc >= 0)
 		return CPE_FORMAT_URI;
 
 	// The regex was taken from the official XSD at
 	// http://scap.nist.gov/schema/cpe/2.3/cpe-naming_2.3.xsd
-	re = pcre_compile("^cpe:2\\.3:[aho\\*\\-](:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#$$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|}~]))+(\\?*|\\*?))|[\\*\\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\\*\\-]))(:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#$$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|}~]))+(\\?*|\\*?))|[\\*\\-])){4}$", 0, &error, &erroffset, NULL);
-	rc = pcre_exec(re, NULL, str, strlen(str), 0, 0, ovector, 30);
-	pcre_free(re);
+	re = oscap_pcre_compile("^cpe:2\\.3:[aho\\*\\-](:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#$$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|}~]))+(\\?*|\\*?))|[\\*\\-])){5}(:(([a-zA-Z]{2,3}(-([a-zA-Z]{2}|[0-9]{3}))?)|[\\*\\-]))(:(((\\?*|\\*?)([a-zA-Z0-9\\-\\._]|(\\\\[\\\\\\*\\?!\"#$$%&'\\(\\)\\+,/:;<=>@\\[\\]\\^`\\{\\|}~]))+(\\?*|\\*?))|[\\*\\-])){4}$", 0, &error, &erroffset);
+	if (re == NULL) {
+		oscap_pcre_err_free(error);
+		return CPE_FORMAT_UNKNOWN;
+	}
+	rc = oscap_pcre_exec(re, str, strlen(str), 0, 0, ovector, 30);
+	oscap_pcre_free(re);
 
 	if (rc >= 0)
 		return CPE_FORMAT_STRING;
 
 	// FIXME: This should be way more strict
-	re = pcre_compile("^wfn:\\[.+\\]$", PCRE_CASELESS, &error, &erroffset, NULL);
-	rc = pcre_exec(re, NULL, str, strlen(str), 0, 0, ovector, 30);
-	pcre_free(re);
+	re = oscap_pcre_compile("^wfn:\\[.+\\]$", OSCAP_PCRE_OPTS_CASELESS, &error, &erroffset);
+	if (re == NULL) {
+		oscap_pcre_err_free(error);
+		return CPE_FORMAT_UNKNOWN;
+	}
+	rc = oscap_pcre_exec(re, str, strlen(str), 0, 0, ovector, 30);
+	oscap_pcre_free(re);
 
 	if (rc >= 0)
 		return CPE_FORMAT_WFN;
diff --git a/src/OVAL/oval_component.c b/src/OVAL/oval_component.c
index 420de1529db..472cf9782a3 100644
--- a/src/OVAL/oval_component.c
+++ b/src/OVAL/oval_component.c
@@ -55,8 +55,8 @@
 #include "common/debug_priv.h"
 #include "common/_error.h"
 #include "common/oscap_string.h"
+#include "common/oscap_pcre.h"
 #include "oval_glob_to_regex.h"
-#include <pcre.h>
 
 #if !defined(OVAL_PROBES_ENABLED)
 const char *oval_subtype_to_str(oval_subtype_t subtype);
@@ -1980,12 +1980,16 @@ static long unsigned int _parse_fmt_sse(char *dt)
 static bool _match(const char *pattern, const char *string)
 {
 	bool match = false;
-	pcre *re;
-	const char *error;
+	oscap_pcre_t *re;
+	char *error;
 	int erroffset = -1, ovector[60], ovector_len = sizeof (ovector) / sizeof (ovector[0]);
-	re = pcre_compile(pattern, PCRE_UTF8, &error, &erroffset, NULL);
-	match = (pcre_exec(re, NULL, string, strlen(string), 0, 0, ovector, ovector_len) >= 0);
-	pcre_free(re);
+	re = oscap_pcre_compile(pattern, OSCAP_PCRE_OPTS_UTF8, &error, &erroffset);
+	if (re == NULL) {
+		oscap_pcre_err_free(error);
+		return false;
+	}
+	match = (oscap_pcre_exec(re, string, strlen(string), 0, 0, ovector, ovector_len) >= 0);
+	oscap_pcre_free(re);
 	return match;
 }
 
@@ -2208,13 +2212,14 @@ static oval_syschar_collection_flag_t _oval_component_evaluate_REGEX_CAPTURE(ova
 	int rc;
 	char *pattern;
 	int erroffset = -1;
-	pcre *re = NULL;
-	const char *error;
+	oscap_pcre_t *re = NULL;
+	char *error;
 
 	pattern = oval_component_get_regex_pattern(component);
-	re = pcre_compile(pattern, PCRE_UTF8, &error, &erroffset, NULL);
+	re = oscap_pcre_compile(pattern, OSCAP_PCRE_OPTS_UTF8, &error, &erroffset);
 	if (re == NULL) {
-		dE("pcre_compile() failed: \"%s\".", error);
+		dE("oscap_pcre_compile() failed: \"%s\".", error);
+		oscap_pcre_err_free(error);
 		return SYSCHAR_FLAG_ERROR;
 	}
 
@@ -2233,9 +2238,9 @@ static oval_syschar_collection_flag_t _oval_component_evaluate_REGEX_CAPTURE(ova
 			for (i = 0; i < ovector_len; ++i)
 				ovector[i] = -1;
 
-			rc = pcre_exec(re, NULL, text, strlen(text), 0, 0, ovector, ovector_len);
+			rc = oscap_pcre_exec(re, text, strlen(text), 0, 0, ovector, ovector_len);
 			if (rc < -1) {
-				dE("pcre_exec() failed: %d.", rc);
+				dE("oscap_pcre_exec() failed: %d.", rc);
 				flag = SYSCHAR_FLAG_ERROR;
 				break;
 			}
@@ -2263,7 +2268,7 @@ static oval_syschar_collection_flag_t _oval_component_evaluate_REGEX_CAPTURE(ova
 		oval_collection_free_items(subcoll, (oscap_destruct_func) oval_value_free);
 	}
 	oval_component_iterator_free(subcomps);
-	pcre_free(re);
+	oscap_pcre_free(re);
 	return flag;
 }
 
diff --git a/src/OVAL/oval_schema_version.c b/src/OVAL/oval_schema_version.c
index 9f2d332d5a6..9828f3d6328 100644
--- a/src/OVAL/oval_schema_version.c
+++ b/src/OVAL/oval_schema_version.c
@@ -29,12 +29,11 @@
 #include <string.h>
 #include <sys/types.h>
 #include "common/util.h"
+#include "common/oscap_pcre.h"
 #include "common/debug_priv.h"
 #include "public/oval_schema_version.h"
 
-#include <pcre.h>
-
-#define OVECTOR_LEN 30 // must be a multiple of 30
+#define OVECTOR_LEN 60 // must be a multiple of 30
 
 static int _parse_int(const char *substring, size_t substring_length)
 {
@@ -55,16 +54,17 @@ oval_schema_version_t oval_schema_version_from_cstr(const char *ver_str)
 		return version;
 	}
 	const char *pattern = "([0-9]+)\\.([0-9]+)(?:\\.([0-9]+))?(?::([0-9]+)\\.([0-9]+)(?:\\.([0-9]+))?)?";
-	const char *error;
+	char *error;
 	int erroffset;
-	pcre *re = pcre_compile(pattern, 0, &error, &erroffset, NULL);
+	oscap_pcre_t *re = oscap_pcre_compile(pattern, 0, &error, &erroffset);
 	if (re == NULL) {
 		dE("Regular expression compilation failed with %s", pattern);
+		oscap_pcre_err_free(error);
 		return version;
 	}
 	int ovector[OVECTOR_LEN];
-	int rc = pcre_exec(re, NULL, ver_str, strlen(ver_str), 0, 0, ovector, OVECTOR_LEN);
-	pcre_free(re);
+	int rc = oscap_pcre_exec(re, ver_str, strlen(ver_str), 0, 0, ovector, OVECTOR_LEN);
+	oscap_pcre_free(re);
 	if (rc < 0) {
 		dE("Regular expression %s did not match string %s", pattern, ver_str);
 		return version;
diff --git a/src/OVAL/probes/independent/system_info_probe.c b/src/OVAL/probes/independent/system_info_probe.c
index 9f680e14d27..6a239565f80 100644
--- a/src/OVAL/probes/independent/system_info_probe.c
+++ b/src/OVAL/probes/independent/system_info_probe.c
@@ -101,7 +101,6 @@
 #include <net/if.h>
 #include <arpa/inet.h>
 #include <sys/types.h>
-#include <pcre.h>
 #elif defined(OS_FREEBSD)
 #include <arpa/inet.h>
 #include <ifaddrs.h>
@@ -540,21 +539,23 @@ static char *_get_os_release_elem(char *os_release_data, const char *elem_name)
 	char elem_re[128] = {0};
 	snprintf(elem_re, sizeof(elem_re), "%s%s%s", "^", elem_name, "=[\"']?(.*?)[\"']?$");
 
-	const char *error;
+	char *error;
 	int erroffset, ovec[_REGEX_RES_VECSIZE] = {0};
-	pcre *re = pcre_compile(elem_re, PCRE_MULTILINE, &error, &erroffset, NULL);
-	if (re == NULL)
+	oscap_pcre_t *re = oscap_pcre_compile(elem_re, OSCAP_PCRE_OPTS_MULTILINE, &error, &erroffset);
+	if (re == NULL) {
+		oscap_pcre_err_free(error);
 		goto finish;
+	}
 
 	char *ptr = NULL;
-	int rc = pcre_exec(re, NULL, os_release_data, len, 0, 0, ovec, _REGEX_RES_VECSIZE);
+	int rc = oscap_pcre_exec(re, os_release_data, len, 0, 0, ovec, _REGEX_RES_VECSIZE);
 	if (rc >= 0) {
 		/* ovec[0] and ovec[1] - are the start and the end of the whole pattern match (=".....")
 		 * ovec[2] and ovec[3] - are start and end char positions of the capture group (.*?) */
 		ptr = strndup(os_release_data+ovec[2], ovec[3]-ovec[2]);
 		ret = ptr;
 	}
-	pcre_free(re);
+	oscap_pcre_free(re);
 
 finish:
 	return ret;
diff --git a/src/OVAL/probes/independent/textfilecontent54_probe.c b/src/OVAL/probes/independent/textfilecontent54_probe.c
index 322977418ca..af60cdf20a5 100644
--- a/src/OVAL/probes/independent/textfilecontent54_probe.c
+++ b/src/OVAL/probes/independent/textfilecontent54_probe.c
@@ -43,7 +43,6 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <limits.h>
-#include <pcre.h>
 
 #include "_seap.h"
 #include <probe-api.h>
@@ -53,6 +52,7 @@
 #include <oval_fts.h>
 #include "common/debug_priv.h"
 #include "common/util.h"
+#include "common/oscap_pcre.h"
 #include "textfilecontent54_probe.h"
 
 #define FILE_SEPARATOR '/'
@@ -112,10 +112,10 @@ static SEXP_t *create_item(const char *path, const char *filename, char *pattern
 
 struct pfdata {
 	char *pattern;
-	int re_opts;
+	oscap_pcre_options_t re_opts;
 	SEXP_t *instance_ent;
-        probe_ctx *ctx;
-	pcre *compiled_regex;
+	probe_ctx *ctx;
+	oscap_pcre_t *compiled_regex;
 };
 
 static int process_file(const char *prefix, const char *path, const char *file, void *arg, oval_schema_version_t over)
@@ -274,7 +274,7 @@ int textfilecontent54_probe_main(probe_ctx *ctx, void *arg)
 	struct pfdata pfd;
 	int ret = 0;
 	int errorffset = -1;
-	const char *error;
+	char *error;
 	OVAL_FTS    *ofts;
 	OVAL_FTSENT *ofts_ent;
 
@@ -316,38 +316,38 @@ int textfilecontent54_probe_main(probe_ctx *ctx, void *arg)
 
 	pfd.instance_ent = inst_ent;
         pfd.ctx          = ctx;
-	pfd.re_opts = PCRE_UTF8;
+	pfd.re_opts = OSCAP_PCRE_OPTS_UTF8;
 	r0 = probe_ent_getattrval(bh_ent, "ignore_case");
 	if (r0) {
 		val = SEXP_string_getb(r0);
 		SEXP_free(r0);
 		if (val)
-			pfd.re_opts |= PCRE_CASELESS;
+			pfd.re_opts |= OSCAP_PCRE_OPTS_CASELESS;
 	}
 	r0 = probe_ent_getattrval(bh_ent, "multiline");
 	if (r0) {
 		val = SEXP_string_getb(r0);
 		SEXP_free(r0);
 		if (val)
-			pfd.re_opts |= PCRE_MULTILINE;
+			pfd.re_opts |= OSCAP_PCRE_OPTS_MULTILINE;
 	}
 	r0 = probe_ent_getattrval(bh_ent, "singleline");
 	if (r0) {
 		val = SEXP_string_getb(r0);
 		SEXP_free(r0);
 		if (val)
-			pfd.re_opts |= PCRE_DOTALL;
+			pfd.re_opts |= OSCAP_PCRE_OPTS_DOTALL;
 	}
 
-	pfd.compiled_regex = pcre_compile(pfd.pattern, pfd.re_opts, &error,
-					  &errorffset, NULL);
+	pfd.compiled_regex = oscap_pcre_compile(pfd.pattern, pfd.re_opts, &error, &errorffset);
 	if (pfd.compiled_regex == NULL) {
 		SEXP_t *msg;
 
-		msg = probe_msg_creatf(OVAL_MESSAGE_LEVEL_ERROR, "pcre_compile() '%s' %s.", pfd.pattern, error);
+		msg = probe_msg_creatf(OVAL_MESSAGE_LEVEL_ERROR, "oscap_pcre_compile() '%s' %s.", pfd.pattern, error);
 		probe_cobj_add_msg(probe_ctx_getresult(pfd.ctx), msg);
 		SEXP_free(msg);
 		probe_cobj_set_flag(probe_ctx_getresult(pfd.ctx), SYSCHAR_FLAG_ERROR);
+		oscap_pcre_err_free(error);
 		goto cleanup;
 	}
 
@@ -375,6 +375,6 @@ int textfilecontent54_probe_main(probe_ctx *ctx, void *arg)
 	if (pfd.pattern != NULL)
 		free(pfd.pattern);
 	if (pfd.compiled_regex != NULL)
-		pcre_free(pfd.compiled_regex);
+		oscap_pcre_free(pfd.compiled_regex);
 	return ret;
 }
diff --git a/src/OVAL/probes/independent/textfilecontent_probe.c b/src/OVAL/probes/independent/textfilecontent_probe.c
index 988a6471dcf..224f0ea21a3 100644
--- a/src/OVAL/probes/independent/textfilecontent_probe.c
+++ b/src/OVAL/probes/independent/textfilecontent_probe.c
@@ -62,7 +62,6 @@
 #include <sys/types.h>
 #include <fcntl.h>
 #include <limits.h>
-#include <pcre.h>
 
 #include "_seap.h"
 #include <probe-api.h>
@@ -72,6 +71,7 @@
 #include <oval_fts.h>
 #include "common/debug_priv.h"
 #include "common/util.h"
+#include "common/oscap_pcre.h"
 #include "textfilecontent_probe.h"
 
 #define FILE_SEPARATOR '/'
@@ -148,11 +148,12 @@ static int process_file(const char *prefix, const char *path, const char *filena
 
 // todo: move to probe_main()?
 	int erroffset = -1;
-	pcre *re = NULL;
-	const char *error;
+	oscap_pcre_t *re = NULL;
+	char *error;
 
-	re = pcre_compile(pfd->pattern, PCRE_UTF8, &error, &erroffset, NULL);
+	re = oscap_pcre_compile(pfd->pattern, OSCAP_PCRE_OPTS_UTF8, &error, &erroffset);
 	if (re == NULL) {
+		oscap_pcre_err_free(error);
 		return -1;
 	}
 
@@ -218,7 +219,7 @@ static int process_file(const char *prefix, const char *path, const char *filena
 	if (whole_path != NULL)
 		free(whole_path);
 	if (re != NULL)
-		pcre_free(re);
+		oscap_pcre_free(re);
 	free(whole_path_with_prefix);
 
 	return ret;
diff --git a/src/OVAL/probes/independent/yamlfilecontent_probe.c b/src/OVAL/probes/independent/yamlfilecontent_probe.c
index 2d0cac69913..f4698ff4d53 100644
--- a/src/OVAL/probes/independent/yamlfilecontent_probe.c
+++ b/src/OVAL/probes/independent/yamlfilecontent_probe.c
@@ -26,7 +26,6 @@
 
 #include <math.h>
 #include <errno.h>
-#include <pcre.h>
 #include <yaml.h>
 #include <yaml-path.h>
 
@@ -36,6 +35,7 @@
 #include "oval_fts.h"
 #include "list.h"
 #include "probe/probe.h"
+#include "common/oscap_pcre.h"
 
 #define OSCAP_YAML_STRING_TAG "tag:yaml.org,2002:str"
 #define OSCAP_YAML_BOOL_TAG "tag:yaml.org,2002:bool"
@@ -52,17 +52,18 @@ int yamlfilecontent_probe_offline_mode_supported()
 
 static bool match_regex(const char *pattern, const char *value)
 {
-	const char *errptr;
+	char *errptr;
 	int erroroffset;
-	pcre *re = pcre_compile(pattern, 0, &errptr, &erroroffset, NULL);
+	oscap_pcre_t *re = oscap_pcre_compile(pattern, 0, &errptr, &erroroffset);
 	if (re == NULL) {
-		dE("pcre_compile failed on pattern '%s': %s at %d", pattern,
+		dE("oscap_pcre_compile failed on pattern '%s': %s at %d", pattern,
 			errptr, erroroffset);
+		oscap_pcre_err_free(errptr);
 		return false;
 	}
 	int ovector[OVECCOUNT];
-	int rc = pcre_exec(re, NULL, value, strlen(value), 0, 0, ovector, OVECCOUNT);
-	pcre_free(re);
+	int rc = oscap_pcre_exec(re, value, strlen(value), 0, 0, ovector, OVECCOUNT);
+	oscap_pcre_free(re);
 	if (rc > 0) {
 		return true;
 	}
diff --git a/src/OVAL/probes/oval_fts.c b/src/OVAL/probes/oval_fts.c
index c96247b0945..e4106720208 100644
--- a/src/OVAL/probes/oval_fts.c
+++ b/src/OVAL/probes/oval_fts.c
@@ -32,7 +32,6 @@
 #include <sys/stat.h>
 #include <limits.h>
 #include <errno.h>
-#include <pcre.h>
 
 #include "oscap_helpers.h"
 #include "fsdev.h"
@@ -379,150 +378,20 @@ static char *extract_fixed_path_prefix(char *path)
 	return strdup("/");
 }
 
-static int badpartial_check_slash(const char *pattern)
-{
-	pcre *regex;
-	const char *errptr = NULL;
-	int errofs = 0, fb, ret;
-
-	regex = pcre_compile(pattern + 1 /* skip '^' */, 0, &errptr, &errofs, NULL);
-	if (regex == NULL) {
-		dE("Failed to validate the pattern: pcre_compile(): "
-		   "error: '%s', error offset: %d, pattern: '%s'.\n",
-		   errptr, errofs, pattern);
-		return -1;
-	}
-	ret = pcre_fullinfo(regex, NULL, PCRE_INFO_FIRSTBYTE, &fb);
-	pcre_free(regex);
-	regex = NULL;
-	if (ret != 0) {
-		dE("Failed to validate the pattern: pcre_fullinfo(): "
-		   "return code: %d, pattern: '%s'.\n", ret, pattern);
-		return -1;
-	}
-	if (fb != '/') {
-		dE("Failed to validate the pattern: pcre_fullinfo(): "
-		   "first byte: %d '%c', pattern: '%s' - the first "
-		   "byte should be a '/'.\n", fb, fb, pattern);
-		return -2;
-	}
-
-	return 0;
-}
-
 #define TEST_PATH1 "/"
 #define TEST_PATH2 "x"
 
-static int badpartial_transform_pattern(char *pattern, pcre **regex_out)
-{
-	/*
-	  PCREPARTIAL(3)
-	  http://pcre.org/pcre.txt
-	  Last updated: 21 January 2012
-
-	  For releases of PCRE prior to 8.00, because of the way
-	  certain internal optimizations were implemented in the
-	  pcre_exec() function, the PCRE_PARTIAL option (predecessor
-	  of PCRE_PARTIAL_SOFT) could not be used with all patterns.
-
-	  Items that were formerly restricted were repeated single
-	  characters and repeated metasequences. If PCRE_PARTIAL was
-	  set for a pattern that did not conform to the restrictions,
-	  pcre_exec() returned the error code PCRE_ERROR_BADPARTIAL
-	  (-13).
-	*/
-
-	int ret, brkt_lvl = 0, errofs = 0;
-	const char *rchars = "\\[]()*+{"; /* probably incomplete */
-	const char *test_path1 = TEST_PATH1;
-	const char *errptr = NULL;
-	char *s, *brkt_mark;
-	bool bracketed = false, found_regex = false;
-	pcre *regex;
-
-	/* The processing bellow builds upon the assumption that
-	   the pattern has been validated by pcre_compile() */
-	for (s = brkt_mark = pattern; (s = strpbrk(s, rchars)) != NULL; s++) {
-		switch (*s) {
-		case '\\':
-			s++;
-			break;
-		case '[':
-			if (!bracketed) {
-				bracketed = true;
-				if (s[1] == ']')
-					s++;
-			}
-			break;
-		case ']':
-			bracketed = false;
-			break;
-		case '(':
-			if (!bracketed) {
-				if (brkt_lvl++ == 0)
-					brkt_mark = s;
-			}
-			break;
-		case ')':
-			if (!bracketed)
-				brkt_lvl--;
-			break;
-		default:
-			if (!bracketed)
-				found_regex = true;
-			break;
-		}
-		if (found_regex)
-			break;
-	}
-
-	if (s == NULL) {
-		dW("Nonfatal failure: can't transform the pattern for partial "
-		   "match optimization: none of the suspected culprits found, "
-		   "pattern: '%s'.", pattern);
-		return -1;
-	}
-
-	if (brkt_lvl > 0)
-		*brkt_mark = '\0';
-	else
-		*s = '\0';
-
-	regex = pcre_compile(pattern, 0, &errptr, &errofs, NULL);
-	if (regex == NULL) {
-		dW("Nonfatal failure: can't transform the pattern for partial "
-		   "match optimization, error: '%s', error offset: %d, "
-		   "pattern: '%s'.", errptr, errofs, pattern);
-		return -1;
-	}
-
-	ret = pcre_exec(regex, NULL, test_path1, strlen(test_path1), 0,
-		PCRE_PARTIAL, NULL, 0);
-	if (ret != PCRE_ERROR_PARTIAL && ret < 0) {
-		pcre_free(regex);
-		dW("Nonfatal failure: can't transform the pattern for partial "
-		   "match optimization, pcre_exec() return code: %d, pattern: "
-		   "'%s'.", ret, pattern);
-		return -1;
-	}
-
-	if (regex_out != NULL)
-		*regex_out = regex;
-
-	return 0;
-}
-
 /* Verify that the path is usable and try to craft a regex to speed up
    the filesystem traversal. If the path to match is ill-designed, an
    ugly heuristic is employed to obtain something meaningfull. */
-static int process_pattern_match(const char *path, pcre **regex_out)
+static int process_pattern_match(const char *path, oscap_pcre_t **regex_out)
 {
 	int ret, errofs = 0;
 	char *pattern;
 	const char *test_path1 = TEST_PATH1;
 	//const char *test_path2 = TEST_PATH2;
-	const char *errptr = NULL;
-	pcre *regex;
+	char *errptr = NULL;
+	oscap_pcre_t *regex;
 
 	if (path[0] != '^') {
 		/* Matching has to have a fixed starting point and thus
@@ -540,19 +409,20 @@ static int process_pattern_match(const char *path, pcre **regex_out)
 		pattern = strdup(path);
 	}
 
-	regex = pcre_compile(pattern, 0, &errptr, &errofs, NULL);
+	regex = oscap_pcre_compile(pattern, 0, &errptr, &errofs);
 	if (regex == NULL) {
-		dE("Failed to validate the pattern: pcre_compile(): "
+		dE("Failed to validate the pattern: oscap_pcre_compile(): "
 		   "error offset: %d, error: '%s', pattern: '%s'.\n",
 		   errofs, errptr, pattern);
 		free(pattern);
+		oscap_pcre_err_free(errptr);
 		return -1;
 	}
-	ret = pcre_exec(regex, NULL, test_path1, strlen(test_path1), 0,
-		PCRE_PARTIAL, NULL, 0);
+	ret = oscap_pcre_exec(regex, test_path1, strlen(test_path1), 0,
+		OSCAP_PCRE_OPTS_PARTIAL, NULL, 0);
 
 	switch (ret) {
-	case PCRE_ERROR_PARTIAL:
+	case OSCAP_PCRE_ERR_PARTIAL:
 		/* The pattern has matched a prefix of the test path
 		   and probably begins with a slash. Make sure that it
 		   doesn't match an arbitrary prefix. */
@@ -577,35 +447,15 @@ static int process_pattern_match(const char *path, pcre **regex_out)
 		}
 		*/
 		break;
-	case PCRE_ERROR_BADPARTIAL:
-		dD("pcre_exec() returned PCRE_ERROR_BADPARTIAL for pattern "
-		   "'%s' and a test path '%s'. Falling back to "
-		   "pcre_fullinfo().\n", pattern, test_path1);
-		pcre_free(regex);
-		regex = NULL;
-
-		/* Fallback to first byte check to determin if
-		   the pattern begins with a slash. */
-		ret = badpartial_check_slash((const char *) pattern);
-		if (ret != 0) {
-			free(pattern);
-			return ret;
-		}
-		/* The pattern contains features that this version of
-		   PCRE can't handle for partial matching. At least
-		   try to find the longest well-bracketed prefix that
-		   can be handled. */
-		badpartial_transform_pattern(pattern, &regex);
-		break;
-	case PCRE_ERROR_NOMATCH:
+	case OSCAP_PCRE_ERR_NOMATCH:
 		/* The pattern doesn't contain a leading slash (or
 		   some part of this code is broken). Apologise to the
 		   user and fail. */
-		dE("Failed to validate the pattern: pcre_exec() returned "
+		dE("Failed to validate the pattern: oscap_pcre_exec() returned "
 		   "PCRE_ERROR_NOMATCH for pattern '%s' and a test path '%s'. "
 		   "This indicates the pattern doesn't match a leading '/'.\n",
 		   pattern, test_path1);
-		pcre_free(regex);
+		oscap_pcre_free(regex);
 		free(pattern);
 		return -2;
 	default:
@@ -634,10 +484,10 @@ static int process_pattern_match(const char *path, pcre **regex_out)
 			break;
 		}
 		/* Some other error. */
-		dE("Failed to validate the pattern: pcre_exec() return "
+		dE("Failed to validate the pattern: oscap_pcre_exec() return "
 		   "code: %d, pattern '%s', test path '%s'.\n", ret,
 		   pattern, test_path1);
-		pcre_free(regex);
+		oscap_pcre_free(regex);
 		free(pattern);
 		return -1;
 	}
@@ -685,7 +535,7 @@ OVAL_FTS *oval_fts_open_prefixed(const char *prefix, SEXP_t *path, SEXP_t *filen
 
 	uint32_t path_op;
 	bool nilfilename = false;
-	pcre *regex = NULL;
+	oscap_pcre_t *regex = NULL;
 	struct stat st;
 
 	if ((path != NULL || filename != NULL || filepath == NULL)
@@ -845,7 +695,7 @@ OVAL_FTS *oval_fts_open_prefixed(const char *prefix, SEXP_t *path, SEXP_t *filen
 			   errno, strerror(errno));
 		}
 		free((void *) paths[0]);
-		pcre_free(regex);
+		oscap_pcre_free(regex);
 		return NULL;
 	}
 
@@ -861,17 +711,15 @@ OVAL_FTS *oval_fts_open_prefixed(const char *prefix, SEXP_t *path, SEXP_t *filen
 	if (ofts->ofts_match_path_fts == NULL || errno != 0) {
 		dE("fts_open() failed, errno: %d \"%s\".", errno, strerror(errno));
 		OVAL_FTS_free(ofts);
-		pcre_free(regex);
+		oscap_pcre_free(regex);
 		return (NULL);
 	}
 
 	ofts->ofts_recurse_path_fts_opts = rec_fts_options;
 	ofts->ofts_path_op = path_op;
 	if (regex != NULL) {
-		const char *errptr = NULL;
-
 		ofts->ofts_path_regex = regex;
-		ofts->ofts_path_regex_extra = pcre_study(regex, 0, &errptr);
+		oscap_pcre_optimize(regex);
 	}
 
 	if (filesystem == OVAL_RECURSE_FS_LOCAL) {
@@ -997,20 +845,20 @@ static FTSENT *oval_fts_read_match_path(OVAL_FTS *ofts)
 		if (ofts->ofts_path_regex != NULL && fts_ent->fts_info == FTS_D) {
 			int ret, svec[3];
 
-			ret = pcre_exec(ofts->ofts_path_regex, ofts->ofts_path_regex_extra,
-					fts_ent->fts_path+shift, fts_ent->fts_pathlen-shift, 0, PCRE_PARTIAL,
+			ret = oscap_pcre_exec(ofts->ofts_path_regex,
+					fts_ent->fts_path+shift, fts_ent->fts_pathlen-shift, 0, OSCAP_PCRE_OPTS_PARTIAL,
 					svec, sizeof(svec) / sizeof(svec[0]));
 			if (ret < 0) {
 				switch (ret) {
-				case PCRE_ERROR_NOMATCH:
+				case OSCAP_PCRE_ERR_NOMATCH:
 					dD("Partial match optimization: PCRE_ERROR_NOMATCH, skipping.");
 					fts_set(ofts->ofts_match_path_fts, fts_ent, FTS_SKIP);
 					continue;
-				case PCRE_ERROR_PARTIAL:
+				case OSCAP_PCRE_ERR_PARTIAL:
 					dD("Partial match optimization: PCRE_ERROR_PARTIAL, continuing.");
 					continue;
 				default:
-					dE("pcre_exec() error: %d.", ret);
+					dE("oscap_pcre_exec() error: %d.", ret);
 					return NULL;
 				}
 			}
@@ -1384,9 +1232,7 @@ int oval_fts_close(OVAL_FTS *ofts)
 		free(ofts->ofts_recurse_path_pthcpy);
 
 	if (ofts->ofts_path_regex)
-		pcre_free(ofts->ofts_path_regex);
-	if (ofts->ofts_path_regex_extra)
-		pcre_free(ofts->ofts_path_regex_extra);
+		oscap_pcre_free(ofts->ofts_path_regex);
 
 	if (ofts->ofts_spath != NULL)
 		SEXP_free(ofts->ofts_spath);
diff --git a/src/OVAL/probes/oval_fts.h b/src/OVAL/probes/oval_fts.h
index d4d898fea64..0134654f4e4 100644
--- a/src/OVAL/probes/oval_fts.h
+++ b/src/OVAL/probes/oval_fts.h
@@ -30,8 +30,8 @@
 #else
 #include <fts.h>
 #endif
-#include <pcre.h>
 #include "fsdev.h"
+#include "common/oscap_pcre.h"
 
 #define ENT_GET_AREF(ent, dst, attr_name, mandatory)			\
 	do {								\
@@ -78,8 +78,7 @@ typedef struct {
 	char *ofts_recurse_path_curpth;
 	dev_t ofts_recurse_path_devid;
 
-	pcre       *ofts_path_regex;
-	pcre_extra *ofts_path_regex_extra;
+	oscap_pcre_t *ofts_path_regex;
 	uint32_t ofts_path_op;
 
 	SEXP_t *ofts_spath;
diff --git a/src/OVAL/probes/unix/gconf_probe.c b/src/OVAL/probes/unix/gconf_probe.c
index e41d6741401..3ddfb628218 100644
--- a/src/OVAL/probes/unix/gconf_probe.c
+++ b/src/OVAL/probes/unix/gconf_probe.c
@@ -36,7 +36,6 @@
 #include <stdint.h>
 #include <limits.h>
 #include <probe-api.h>
-#include <pcre.h>
 #include <gconf/gconf.h>
 #include "gconf_probe.h"
 
diff --git a/src/OVAL/probes/unix/linux/partition_probe.c b/src/OVAL/probes/unix/linux/partition_probe.c
index be51f2de850..cd0e1041327 100644
--- a/src/OVAL/probes/unix/linux/partition_probe.c
+++ b/src/OVAL/probes/unix/linux/partition_probe.c
@@ -63,9 +63,9 @@
 #include <probe/probe.h>
 #include <probe/option.h>
 #include <mntent.h>
-#include <pcre.h>
 
 #include "common/debug_priv.h"
+#include "common/oscap_pcre.h"
 #include "partition_probe.h"
 
 #ifndef MTAB_PATH
@@ -346,8 +346,8 @@ int partition_probe_main(probe_ctx *ctx, void *probe_arg)
                 char buffer[MTAB_LINE_MAX];
                 struct mntent mnt_ent, *mnt_entp;
 
-                pcre *re = NULL;
-                const char *estr = NULL;
+                oscap_pcre_t *re = NULL;
+                char *estr = NULL;
                 int eoff = -1;
 #if defined(HAVE_BLKID_GET_TAG_VALUE)
                 blkid_cache blkcache;
@@ -358,13 +358,14 @@ int partition_probe_main(probe_ctx *ctx, void *probe_arg)
                 }
 #endif
                 if (mnt_op == OVAL_OPERATION_PATTERN_MATCH) {
-                        re = pcre_compile(mnt_path, PCRE_UTF8, &estr, &eoff, NULL);
+                        re = oscap_pcre_compile(mnt_path, OSCAP_PCRE_OPTS_UTF8, &estr, &eoff);
 
                         if (re == NULL) {
                                 endmntent(mnt_fp);
 #if defined(HAVE_BLKID_GET_TAG_VALUE)
                                 blkid_put_cache(blkcache);
 #endif
+                                oscap_pcre_err_free(estr);
                                 return (PROBE_EINVAL);
                         }
                 }
@@ -398,7 +399,7 @@ int partition_probe_main(probe_ctx *ctx, void *probe_arg)
                         } else if (mnt_op == OVAL_OPERATION_PATTERN_MATCH) {
                                 int rc;
 
-                                rc = pcre_exec(re, NULL, mnt_entp->mnt_dir,
+                                rc = oscap_pcre_exec(re, mnt_entp->mnt_dir,
                                                strlen(mnt_entp->mnt_dir), 0, 0, NULL, 0);
 
                                 if (rc == 0) {
@@ -418,7 +419,7 @@ int partition_probe_main(probe_ctx *ctx, void *probe_arg)
                 endmntent(mnt_fp);
 
                 if (mnt_op == OVAL_OPERATION_PATTERN_MATCH)
-                        pcre_free(re);
+                        oscap_pcre_free(re);
 #if defined(HAVE_BLKID_GET_TAG_VALUE)
                 blkid_put_cache(blkcache);
 #endif
diff --git a/src/OVAL/probes/unix/linux/rpmverify_probe.c b/src/OVAL/probes/unix/linux/rpmverify_probe.c
index 14ee57d89a5..6a8f4b4992e 100644
--- a/src/OVAL/probes/unix/linux/rpmverify_probe.c
+++ b/src/OVAL/probes/unix/linux/rpmverify_probe.c
@@ -39,7 +39,6 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
-#include <pcre.h>
 
 #include "rpm-helper.h"
 
@@ -54,6 +53,7 @@
 
 #include <probe/probe.h>
 #include <probe/option.h>
+#include "common/oscap_pcre.h"
 
 #include "rpmverify_probe.h"
 
@@ -83,18 +83,19 @@ static int rpmverify_collect(probe_ctx *ctx,
 	rpmdbMatchIterator match;
         rpmVerifyAttrs omit = (rpmVerifyAttrs)(flags & RPMVERIFY_RPMATTRMASK);
 	Header pkgh;
-        pcre *re = NULL;
+	oscap_pcre_t *re = NULL;
 	int  ret = -1;
 
         /* pre-compile regex if needed */
         if (file_op == OVAL_OPERATION_PATTERN_MATCH) {
-                const char *errmsg;
+                char *errmsg;
                 int erroff;
 
-                re = pcre_compile(file, PCRE_UTF8, &errmsg,  &erroff, NULL);
+                re = oscap_pcre_compile(file, OSCAP_PCRE_OPTS_UTF8, &errmsg,  &erroff);
 
                 if (re == NULL) {
                         /* TODO */
+                        oscap_pcre_err_free(errmsg);
                         return (-1);
                 }
         }
@@ -213,7 +214,7 @@ static int rpmverify_collect(probe_ctx *ctx,
         ret   = 0;
 ret:
         if (re != NULL)
-                pcre_free(re);
+                oscap_pcre_free(re);
 
         RPMVERIFY_UNLOCK;
         return (ret);
diff --git a/src/OVAL/probes/unix/linux/rpmverifyfile_probe.c b/src/OVAL/probes/unix/linux/rpmverifyfile_probe.c
index a61f44e39b7..12145c411f4 100644
--- a/src/OVAL/probes/unix/linux/rpmverifyfile_probe.c
+++ b/src/OVAL/probes/unix/linux/rpmverifyfile_probe.c
@@ -41,7 +41,6 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
-#include <pcre.h>
 
 #include "rpm-helper.h"
 #include "oscap_helpers.h"
@@ -57,6 +56,8 @@
 
 #include <probe/probe.h>
 #include <probe/option.h>
+#include "common/oscap_pcre.h"
+
 #include "rpmverifyfile_probe.h"
 
 struct rpmverify_res {
@@ -154,25 +155,26 @@ static int _compare_file_with_current_file(oval_operation_t file_op, const char
 		}
 		*result_file = current_file_realpath ? oscap_strdup(current_file_realpath) : oscap_strdup(current_file);
 	} else if (file_op == OVAL_OPERATION_PATTERN_MATCH) {
-		const char *errmsg;
+		char *errmsg;
 		int erroff;
-		pcre *re = pcre_compile(file, PCRE_UTF8, &errmsg,  &erroff, NULL);
+		oscap_pcre_t *re = oscap_pcre_compile(file, OSCAP_PCRE_OPTS_UTF8, &errmsg,  &erroff);
 		if (re == NULL) {
-			dE("pcre_compile pattern='%s': %s", file, errmsg);
+			dE("oscap_pcre_compile pattern='%s': %s", file, errmsg);
 			ret = -1;
+			oscap_pcre_err_free(errmsg);
 			goto cleanup;
 		}
-		int pcre_ret = pcre_exec(re, NULL, current_file, strlen(current_file), 0, 0, NULL, 0);
-		pcre_free(re);
-		if (pcre_ret == 0) {
+		int pcre_ret = oscap_pcre_exec(re, current_file, strlen(current_file), 0, 0, NULL, 0);
+		oscap_pcre_free(re);
+		if (pcre_ret > OSCAP_PCRE_ERR_NOMATCH) {
 			/* match */
 			*result_file = oscap_strdup(current_file);
-		} else if (pcre_ret == -1) {
+		} else if (pcre_ret == OSCAP_PCRE_ERR_NOMATCH) {
 			/* no match */
 			ret = 1;
 			goto cleanup;
 		} else {
-			dE("pcre_exec() failed!");
+			dE("oscap_pcre_exec() failed!");
 			ret = -1;
 			goto cleanup;
 		}
diff --git a/src/OVAL/probes/unix/linux/rpmverifypackage_probe.c b/src/OVAL/probes/unix/linux/rpmverifypackage_probe.c
index b56fd43d11e..87a91240757 100644
--- a/src/OVAL/probes/unix/linux/rpmverifypackage_probe.c
+++ b/src/OVAL/probes/unix/linux/rpmverifypackage_probe.c
@@ -41,7 +41,6 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
-#include <pcre.h>
 
 #include "rpm-helper.h"
 #include "probe-chroot.h"
diff --git a/src/OVAL/results/oval_cmp_basic.c b/src/OVAL/results/oval_cmp_basic.c
index 7b819d5538d..48fbcf121fc 100644
--- a/src/OVAL/results/oval_cmp_basic.c
+++ b/src/OVAL/results/oval_cmp_basic.c
@@ -27,10 +27,10 @@
 
 #include <math.h>
 #include <string.h>
-#include <pcre.h>
 
 #include "oval_types.h"
 #include "common/_error.h"
+#include "common/oscap_pcre.h"
 #include "common/debug_priv.h"
 #include "oval_cmp_basic_impl.h"
 
@@ -124,29 +124,30 @@ static oval_result_t strregcomp(const char *pattern, const char *test_str)
 {
 	int ret;
 	oval_result_t result = OVAL_RESULT_ERROR;
-	pcre *re;
-	const char *err;
+	oscap_pcre_t *re;
+	char *err;
 	int errofs;
 
-	re = pcre_compile(pattern, PCRE_UTF8, &err, &errofs, NULL);
+	re = oscap_pcre_compile(pattern, OSCAP_PCRE_OPTS_UTF8, &err, &errofs);
 	if (re == NULL) {
 		dE("Unable to compile regex pattern '%s', "
-				"pcre_compile() returned error (offset: %d): '%s'.\n", pattern, errofs, err);
+				"oscap_pcre_compile() returned error (offset: %d): '%s'.\n", pattern, errofs, err);
+		oscap_pcre_err_free(err);
 		return OVAL_RESULT_ERROR;
 	}
 
-	ret = pcre_exec(re, NULL, test_str, strlen(test_str), 0, 0, NULL, 0);
-	if (ret > -1 ) {
+	ret = oscap_pcre_exec(re, test_str, strlen(test_str), 0, 0, NULL, 0);
+	if (ret > OSCAP_PCRE_ERR_NOMATCH ) {
 		result = OVAL_RESULT_TRUE;
-	} else if (ret == -1) {
+	} else if (ret == OSCAP_PCRE_ERR_NOMATCH) {
 		result = OVAL_RESULT_FALSE;
 	} else {
 		dE("Unable to match regex pattern '%s' on string '%s', "
-				"pcre_exec() returned error: %d.\n", pattern, test_str, ret);
+				"oscap_pcre_exec() returned error: %d.\n", pattern, test_str, ret);
 		result = OVAL_RESULT_ERROR;
 	}
 
-	pcre_free(re);
+	oscap_pcre_free(re);
 	return result;
 }
 
diff --git a/src/XCCDF/item.c b/src/XCCDF/item.c
index 295e4a7f00a..042d9b3d769 100644
--- a/src/XCCDF/item.c
+++ b/src/XCCDF/item.c
@@ -30,7 +30,6 @@
 #include <string.h>
 #include <time.h>
 #include <math.h>
-#include <pcre.h>
 
 #include <libxml/tree.h>
 #include <libxml/xpath.h>
@@ -41,6 +40,7 @@
 #include "helpers.h"
 #include "xccdf_impl.h"
 #include "common/util.h"
+#include "common/oscap_pcre.h"
 #include "oscap_helpers.h"
 
 /* According to `man 3 pcreapi`, the number passed in ovecsize should always
@@ -768,12 +768,16 @@ void xccdf_item_add_applicable_platform(struct xccdf_item *item, xmlTextReaderPt
 	 * an underscore to workaround the situation that this XCCDF benchmark is
 	 * not applicable.
 	 */
-	const char *pcreerror = NULL;
+	char *pcreerror = NULL;
 	int erroffset = 0;
-	pcre *regex = pcre_compile("^(cpe:/o:microsoft:windows)(7.*)", 0, &pcreerror, &erroffset, NULL);
+	oscap_pcre_t *regex = oscap_pcre_compile("^(cpe:/o:microsoft:windows)(7.*)", 0, &pcreerror, &erroffset);
+	if (regex == NULL) {
+		oscap_pcre_err_free(pcreerror);
+		return;
+	}
 	int ovector[OVECTOR_LEN];
-	int rc = pcre_exec(regex, NULL, platform_idref, strlen(platform_idref), 0, 0, ovector, OVECTOR_LEN);
-	pcre_free(regex);
+	int rc = oscap_pcre_exec(regex, platform_idref, strlen(platform_idref), 0, 0, ovector, OVECTOR_LEN);
+	oscap_pcre_free(regex);
 	/* 1 pattern + 2 groups = 3 */
 	if (rc == 3) {
 		const int first_group_start = ovector[2];
diff --git a/src/XCCDF_POLICY/xccdf_policy_remediate.c b/src/XCCDF_POLICY/xccdf_policy_remediate.c
index c5f81d3f0c6..9d4ba16e663 100644
--- a/src/XCCDF_POLICY/xccdf_policy_remediate.c
+++ b/src/XCCDF_POLICY/xccdf_policy_remediate.c
@@ -38,12 +38,12 @@
 #endif
 
 #include <libxml/tree.h>
-#include <pcre.h>
 
 #include "XCCDF/item.h"
 #include "common/_error.h"
 #include "common/debug_priv.h"
 #include "common/oscap_acquire.h"
+#include "common/oscap_pcre.h"
 #include "xccdf_policy_priv.h"
 #include "xccdf_policy_model_priv.h"
 #include "public/xccdf_policy.h"
@@ -661,12 +661,12 @@ static int _write_fix_missing_warning_to_fd(const char *sys, int output_fd, stru
 struct blueprint_entries {
 	const char *pattern;
 	struct oscap_list *list;
-	pcre *re;
+	oscap_pcre_t *re;
 };
 
 static inline int _parse_blueprint_fix(const char *fix_text, struct oscap_list *generic, struct oscap_list *services_enable, struct oscap_list *services_disable, struct oscap_list *kernel_append)
 {
-	const char *err;
+	char *err;
 	int errofs;
 	int ret = 0;
 
@@ -681,9 +681,10 @@ static inline int _parse_blueprint_fix(const char *fix_text, struct oscap_list *
 	};
 
 	for (int i = 0; tab[i].pattern != NULL; i++) {
-		tab[i].re = pcre_compile(tab[i].pattern, PCRE_UTF8, &err, &errofs, NULL);
+		tab[i].re = oscap_pcre_compile(tab[i].pattern, OSCAP_PCRE_OPTS_UTF8, &err, &errofs);
 		if (tab[i].re == NULL) {
-			dE("Unable to compile /%s/ regex pattern, pcre_compile() returned error (offset: %d): '%s'.\n", tab[i].pattern, errofs, err);
+			dE("Unable to compile /%s/ regex pattern, oscap_pcre_compile() returned error (offset: %d): '%s'.\n", tab[i].pattern, errofs, err);
+			oscap_pcre_err_free(err);
 			ret = 1;
 			goto exit;
 		}
@@ -695,7 +696,7 @@ static inline int _parse_blueprint_fix(const char *fix_text, struct oscap_list *
 
 	for (int i = 0; tab[i].pattern != NULL; i++) {
 		while (true) {
-			const int match = pcre_exec(tab[i].re, NULL, fix_text, fix_text_len, start_offset,
+			const int match = oscap_pcre_exec(tab[i].re, fix_text, fix_text_len, start_offset,
 			                            0, ovector, sizeof(ovector) / sizeof(ovector[0]));
 			if (match == -1)
 				break;
@@ -726,7 +727,7 @@ static inline int _parse_blueprint_fix(const char *fix_text, struct oscap_list *
 
 exit:
 	for (int i = 0; tab[i].pattern != NULL; i++)
-		pcre_free(tab[i].re);
+		oscap_pcre_free(tab[i].re);
 
 	return ret;
 }
@@ -737,13 +738,14 @@ static inline int _parse_ansible_fix(const char *fix_text, struct oscap_list *va
 	const char *pattern =
 		"- name: XCCDF Value [^ ]+ # promote to variable\n  set_fact:\n"
 		"    ([^:]+): (.+)\n  tags:\n    - always\n";
-	const char *err;
+	char *err;
 	int errofs;
 
-	pcre *re = pcre_compile(pattern, PCRE_UTF8, &err, &errofs, NULL);
+	oscap_pcre_t *re = oscap_pcre_compile(pattern, OSCAP_PCRE_OPTS_UTF8, &err, &errofs);
 	if (re == NULL) {
 		dE("Unable to compile regex pattern, "
-				"pcre_compile() returned error (offset: %d): '%s'.\n", errofs, err);
+				"oscap_pcre_compile() returned error (offset: %d): '%s'.\n", errofs, err);
+		oscap_pcre_err_free(err);
 		return 1;
 	}
 
@@ -758,14 +760,14 @@ static inline int _parse_ansible_fix(const char *fix_text, struct oscap_list *va
 	const size_t fix_text_len = strlen(fix_text);
 	int start_offset = 0;
 	while (true) {
-		const int match = pcre_exec(re, NULL, fix_text, fix_text_len, start_offset,
+		const int match = oscap_pcre_exec(re, fix_text, fix_text_len, start_offset,
 				0, ovector, sizeof(ovector) / sizeof(ovector[0]));
 		if (match == -1)
 			break;
 		if (match != 3) {
 			dE("Expected 2 capture group matches per XCCDF variable. Found %i!",
 				match - 1);
-			pcre_free(re);
+			oscap_pcre_free(re);
 			return 1;
 		}
 
@@ -807,7 +809,7 @@ static inline int _parse_ansible_fix(const char *fix_text, struct oscap_list *va
 		oscap_list_add(tasks, remediation_part);
 	}
 
-	pcre_free(re);
+	oscap_pcre_free(re);
 	return 0;
 }
 
diff --git a/src/common/util.c b/src/common/util.c
index 69e059a4c71..52362c098ae 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -31,13 +31,13 @@
 #include <limits.h>
 #include <stdarg.h>
 #include <math.h>
-#include <pcre.h>
 #include <sys/stat.h>
 
 #include "util.h"
 #include "_error.h"
 #include "oscap.h"
 #include "oscap_helpers.h"
+#include "oscap_pcre.h"
 #include "debug_priv.h"
 
 #ifdef OS_WINDOWS
@@ -361,7 +361,7 @@ char *oscap_path_join(const char *path1, const char *path2)
 	return joined_path;
 }
 
-int oscap_get_substrings(char *str, int *ofs, pcre *re, int want_substrs, char ***substrings) {
+int oscap_get_substrings(char *str, int *ofs, oscap_pcre_t *re, int want_substrs, char ***substrings) {
 	int i, ret, rc;
 	int ovector[60], ovector_len = sizeof (ovector) / sizeof (ovector[0]);
 	char **substrs;
@@ -372,30 +372,26 @@ int oscap_get_substrings(char *str, int *ofs, pcre *re, int want_substrs, char *
 		ovector[i] = -1;
 	}
 
-	struct pcre_extra extra;
-	extra.match_limit_recursion = OSCAP_PCRE_EXEC_RECURSION_LIMIT_DEFAULT;
+	unsigned long limit = OSCAP_PCRE_EXEC_RECURSION_LIMIT_DEFAULT;
 	char *limit_str = getenv("OSCAP_PCRE_EXEC_RECURSION_LIMIT");
-	if (limit_str != NULL) {
-		unsigned long limit;
-		if (sscanf(limit_str, "%lu", &limit) == 1) {
-			extra.match_limit_recursion = limit;
-		}
-	}
-	extra.flags = PCRE_EXTRA_MATCH_LIMIT_RECURSION;
+	if (limit_str != NULL)
+		if (sscanf(limit_str, "%lu", &limit) <= 0)
+			dW("Unable to parse OSCAP_PCRE_EXEC_RECURSION_LIMIT value");
+	oscap_pcre_set_match_limit_recursion(re, limit);
 	size_t str_len = strlen(str);
 #if defined(OS_SOLARIS)
-	rc = pcre_exec(re, &extra, str, str_len, *ofs, PCRE_NO_UTF8_CHECK, ovector, ovector_len);
+	rc = oscap_pcre_exec(re, str, str_len, *ofs, OSCAP_PCRE_OPTS_NO_UTF8_CHECK, ovector, ovector_len);
 #else
-	rc = pcre_exec(re, &extra, str, str_len, *ofs, 0, ovector, ovector_len);
+	rc = oscap_pcre_exec(re, str, str_len, *ofs, 0, ovector, ovector_len);
 #endif
 
-	if (rc < -1) {
+	if (rc < OSCAP_PCRE_ERR_NOMATCH) {
 		if (str_len < 100)
-			dE("Function pcre_exec() failed to match a regular expression with return code %d on string '%s'.", rc, str);
+			dE("Function oscap_pcre_exec() failed to match a regular expression with return code %d on string '%s'.", rc, str);
 		else
-			dE("Function pcre_exec() failed to match a regular expression with return code %d on string '%.100s' (truncated, showing first 100 characters).", rc, str);
+			dE("Function oscap_pcre_exec() failed to match a regular expression with return code %d on string '%.100s' (truncated, showing first 100 characters).", rc, str);
 		return rc;
-	} else if (rc == -1) {
+	} else if (rc == OSCAP_PCRE_ERR_NOMATCH) {
 		/* no match */
 		return 0;
 	}
diff --git a/src/common/util.h b/src/common/util.h
index 0db36fc31be..7f84e6436bc 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -32,8 +32,8 @@
 #include "public/oscap.h"
 #include <stdarg.h>
 #include <string.h>
-#include <pcre.h>
 #include "oscap_export.h"
+#include "oscap_pcre.h"
 
 #ifndef __attribute__nonnull__
 #define __attribute__nonnull__(x) assert((x) != NULL)
@@ -498,7 +498,7 @@ char *oscap_strerror_r(int errnum, char *buf, size_t buflen);
  * @return count of matched substrings, 0 if no match
  * negative value on failure
  */
-int oscap_get_substrings(char *str, int *ofs, pcre *re, int want_substrs, char ***substrings);
+int oscap_get_substrings(char *str, int *ofs, oscap_pcre_t *re, int want_substrs, char ***substrings);
 
 
 #ifndef OS_WINDOWS
diff --git a/tests/API/OVAL/schema_version/test_schema_version.c b/tests/API/OVAL/schema_version/test_schema_version.c
index a282ece099d..d968f916a65 100644
--- a/tests/API/OVAL/schema_version/test_schema_version.c
+++ b/tests/API/OVAL/schema_version/test_schema_version.c
@@ -27,6 +27,10 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include "OVAL/public/oval_schema_version.h"
+#include <stddef.h>
+#include <assert.h>
+#include "util.h"
+#include "public/oscap_debug.h"
 
 static const char *cmp_to_cstr(int cmp)
 {
@@ -44,6 +48,7 @@ int main(int argc, char **argv)
 		printf("Not enough arguments\n");
 		return 1;
 	}
+	oscap_set_verbose(NULL, NULL);
 	const char *v1_str = argv[1];
 	const char *v2_str = argv[2];
 	int expected_result = atoi(argv[3]);
diff --git a/tests/API/XCCDF/unittests/CMakeLists.txt b/tests/API/XCCDF/unittests/CMakeLists.txt
index c9c507791ce..95441b40e1c 100644
--- a/tests/API/XCCDF/unittests/CMakeLists.txt
+++ b/tests/API/XCCDF/unittests/CMakeLists.txt
@@ -4,6 +4,7 @@ add_oscap_test_executable(test_oscap_common
 	${CMAKE_SOURCE_DIR}/src/common/list.c
 	${CMAKE_SOURCE_DIR}/src/common/error.c
 	${CMAKE_SOURCE_DIR}/src/common/err_queue.c
+	${CMAKE_SOURCE_DIR}/src/common/oscap_pcre.c
 )
 
 add_oscap_test_executable(test_xccdf_overrides
diff --git a/tests/API/probes/CMakeLists.txt b/tests/API/probes/CMakeLists.txt
index 2ac4081ac27..cd5ca8358d3 100644
--- a/tests/API/probes/CMakeLists.txt
+++ b/tests/API/probes/CMakeLists.txt
@@ -27,6 +27,7 @@ add_oscap_test_executable(oval_fts_list
 	"${CMAKE_SOURCE_DIR}/src/common/err_queue.c"
 	"${CMAKE_SOURCE_DIR}/src/OVAL/probes/probe/entcmp.c"
 	"${CMAKE_SOURCE_DIR}/src/common/util.c"
+	"${CMAKE_SOURCE_DIR}/src/common/oscap_pcre.c"
 	"${OVAL_RESULTS_SOURCES}"
 )
 target_include_directories(oval_fts_list PUBLIC
diff --git a/tests/API/probes/oval_fts_list.c b/tests/API/probes/oval_fts_list.c
index eb1ffc283f0..e01526a89c4 100644
--- a/tests/API/probes/oval_fts_list.c
+++ b/tests/API/probes/oval_fts_list.c
@@ -9,6 +9,10 @@
 #include "sexp.h"
 #include "oval_fts.h"
 #include "probe-api.h"
+#include <stddef.h>
+#include <assert.h>
+#include "util.h"
+#include "public/oscap_debug.h"
 
 static int create_path_sexpr(char *arg_operation, char *arg_argument, SEXP_t **result)
 {
@@ -172,6 +176,8 @@ int main(int argc, char *argv[])
 
 	int ret = 0;
 
+	oscap_set_verbose(NULL, NULL);
+
 	if (argc < 11) {
 		fprintf(stderr, "Invalid usage -- too few arguments supplied.\n");
 		fprintf(stderr, "The following arguments are required, but may be empty:\n\n");
diff --git a/tests/probes/textfilecontent54/test_recursion_limit.sh b/tests/probes/textfilecontent54/test_recursion_limit.sh
index 2619dafdd53..f34585dba60 100755
--- a/tests/probes/textfilecontent54/test_recursion_limit.sh
+++ b/tests/probes/textfilecontent54/test_recursion_limit.sh
@@ -16,7 +16,7 @@ stderr=$(mktemp)
 
 $OSCAP oval eval --results $result $input > $stdout 2> $stderr
 
-grep -q "Function pcre_exec() failed to match a regular expression with return code -21" $stderr
+grep -q "Function oscap_pcre_exec() failed to match a regular expression with return code -21" $stderr
 
 assert_exists 1 '/oval_results/results/system/definitions/definition[@definition_id="oval:x:def:1" and @result="error"]'
 
diff --git a/tests/probes/xinetd/CMakeLists.txt b/tests/probes/xinetd/CMakeLists.txt
index c2525277dde..f1bbbbc8bf2 100644
--- a/tests/probes/xinetd/CMakeLists.txt
+++ b/tests/probes/xinetd/CMakeLists.txt
@@ -4,6 +4,7 @@ if(ENABLE_PROBES_UNIX)
 		"${CMAKE_SOURCE_DIR}/src/common/bfind.c"
 		"${CMAKE_SOURCE_DIR}/src/OVAL/probes/SEAP/generic/rbt/rbt_common.c"
 		"${CMAKE_SOURCE_DIR}/src/OVAL/probes/SEAP/generic/rbt/rbt_str.c"
+		"${CMAKE_SOURCE_DIR}/src/common/oscap_pcre.c"
 	)
 	target_link_libraries(test_probe_xinetd openscap)
 	target_include_directories(test_probe_xinetd PUBLIC

From f653e07627eea34de72ce6315af571ec4c54f10a Mon Sep 17 00:00:00 2001
From: Evgeny Kolesnikov <ekolesni@redhat.com>
Date: Mon, 4 Sep 2023 13:57:19 +0200
Subject: [PATCH 3/4] Add PCRE2 library #3

Move the oscap_get_substring into the oscap_pcre.c module
and rename it into oscap_pcre_get_substring.

The function imposes implicit dependencies on PCRE/PCRE2 symbols
even for utils.c users that won't use PCRE at all (SCE library).
---
 CMakeLists.txt                                |  1 +
 .../independent/textfilecontent54_probe.c     |  2 +-
 .../independent/textfilecontent_probe.c       |  2 +-
 src/common/oscap_pcre.c                       | 71 +++++++++++++++++++
 src/common/oscap_pcre.h                       | 12 ++++
 src/common/util.c                             | 71 -------------------
 src/common/util.h                             | 14 ----
 7 files changed, 86 insertions(+), 87 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index b6e5a825dc1..802f9d9a2a2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -530,6 +530,7 @@ endif()
 
 if (${CMAKE_C_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
 	set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pipe -W -Wall -Wnonnull -Wshadow -Wformat -Wundef -Wno-unused-parameter -Wmissing-prototypes -Wno-unknown-pragmas -Wno-int-conversion -Werror=implicit-function-declaration -D_GNU_SOURCE -std=c99")
+	add_link_options(-Wl,-z,now)
 endif()
 if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
 	add_link_options(-lkvm -lm -lprocstat)
diff --git a/src/OVAL/probes/independent/textfilecontent54_probe.c b/src/OVAL/probes/independent/textfilecontent54_probe.c
index af60cdf20a5..d3a5c6eb22c 100644
--- a/src/OVAL/probes/independent/textfilecontent54_probe.c
+++ b/src/OVAL/probes/independent/textfilecontent54_probe.c
@@ -216,7 +216,7 @@ static int process_file(const char *prefix, const char *path, const char *file,
 			want_instance = 0;
 
 		SEXP_free(next_inst);
-		substr_cnt = oscap_get_substrings(buf, &ofs, pfd->compiled_regex, want_instance, &substrs);
+		substr_cnt = oscap_pcre_get_substrings(buf, &ofs, pfd->compiled_regex, want_instance, &substrs);
 
 		if (substr_cnt < 0) {
 			SEXP_t *msg;
diff --git a/src/OVAL/probes/independent/textfilecontent_probe.c b/src/OVAL/probes/independent/textfilecontent_probe.c
index 224f0ea21a3..d4c1f62bd99 100644
--- a/src/OVAL/probes/independent/textfilecontent_probe.c
+++ b/src/OVAL/probes/independent/textfilecontent_probe.c
@@ -196,7 +196,7 @@ static int process_file(const char *prefix, const char *path, const char *filena
 	int ofs = 0;
 
 	while (fgets(line, sizeof(line), fp) != NULL) {
-		substr_cnt = oscap_get_substrings(line, &ofs, re, 1, &substrs);
+		substr_cnt = oscap_pcre_get_substrings(line, &ofs, re, 1, &substrs);
 		if (substr_cnt > 0) {
 			int k;
 			SEXP_t *item;
diff --git a/src/common/oscap_pcre.c b/src/common/oscap_pcre.c
index d2e45292ab1..65694c44037 100644
--- a/src/common/oscap_pcre.c
+++ b/src/common/oscap_pcre.c
@@ -26,6 +26,8 @@
 
 #include <memory.h>
 
+#define OSCAP_PCRE_EXEC_RECURSION_LIMIT_DEFAULT 3500
+
 #ifdef HAVE_PCRE2
 #define PCRE2_CODE_UNIT_WIDTH 8
 #define PCRE2_ERR_BUF_SIZE 127
@@ -243,6 +245,75 @@ void oscap_pcre_free(oscap_pcre_t *opcre)
 	}
 }
 
+int oscap_pcre_get_substrings(char *str, int *ofs, oscap_pcre_t *re, int want_substrs, char ***substrings) {
+	int i, ret, rc;
+	int ovector[60], ovector_len = sizeof (ovector) / sizeof (ovector[0]);
+	char **substrs;
+
+	// todo: max match count check
+
+	for (i = 0; i < ovector_len; ++i) {
+		ovector[i] = -1;
+	}
+
+	unsigned long limit = OSCAP_PCRE_EXEC_RECURSION_LIMIT_DEFAULT;
+	char *limit_str = getenv("OSCAP_PCRE_EXEC_RECURSION_LIMIT");
+	if (limit_str != NULL)
+		if (sscanf(limit_str, "%lu", &limit) <= 0)
+			dW("Unable to parse OSCAP_PCRE_EXEC_RECURSION_LIMIT value");
+	oscap_pcre_set_match_limit_recursion(re, limit);
+	size_t str_len = strlen(str);
+#if defined(OS_SOLARIS)
+	rc = oscap_pcre_exec(re, str, str_len, *ofs, OSCAP_PCRE_OPTS_NO_UTF8_CHECK, ovector, ovector_len);
+#else
+	rc = oscap_pcre_exec(re, str, str_len, *ofs, 0, ovector, ovector_len);
+#endif
+
+	if (rc < OSCAP_PCRE_ERR_NOMATCH) {
+		if (str_len < 100)
+			dE("Function oscap_pcre_exec() failed to match a regular expression with return code %d on string '%s'.", rc, str);
+		else
+			dE("Function oscap_pcre_exec() failed to match a regular expression with return code %d on string '%.100s' (truncated, showing first 100 characters).", rc, str);
+		return rc;
+	} else if (rc == OSCAP_PCRE_ERR_NOMATCH) {
+		/* no match */
+		return 0;
+	}
+
+	*ofs = (*ofs == ovector[1]) ? ovector[1] + 1 : ovector[1];
+
+	if (!want_substrs) {
+		/* just report successful match */
+		return 1;
+	}
+
+	ret = 0;
+	if (rc == 0) {
+		/* vector too small */
+		// todo: report partial results
+		rc = ovector_len / 3;
+	}
+
+	substrs = malloc(rc * sizeof (char *));
+	for (i = 0; i < rc; ++i) {
+		int len;
+		char *buf;
+
+		if (ovector[2 * i] == -1) {
+			continue;
+		}
+		len = ovector[2 * i + 1] - ovector[2 * i];
+		buf = malloc(len + 1);
+		memcpy(buf, str + ovector[2 * i], len);
+		buf[len] = '\0';
+		substrs[ret] = buf;
+		++ret;
+	}
+
+	*substrings = substrs;
+
+	return ret;
+}
 
 void oscap_pcre_err_free(char *err)
 {
diff --git a/src/common/oscap_pcre.h b/src/common/oscap_pcre.h
index df8acd01543..2f37955d8cb 100644
--- a/src/common/oscap_pcre.h
+++ b/src/common/oscap_pcre.h
@@ -96,6 +96,18 @@ void oscap_pcre_set_match_limit_recursion(oscap_pcre_t *opcre, unsigned long lim
  */
 void oscap_pcre_optimize(oscap_pcre_t *opcre);
 
+/**
+ * Match a regular expression and return substrings.
+ * Caller is responsible for freeing the returned array.
+ * @param str subject string
+ * @param ofs starting offset in str
+ * @param re compiled regular expression
+ * @param want_substrs if non-zero, substrings will be returned
+ * @param substrings contains returned substrings
+ * @return count of matched substrings, 0 if no match
+ * negative value on failure
+ */
+int oscap_pcre_get_substrings(char *str, int *ofs, oscap_pcre_t *re, int want_substrs, char ***substrings);
 
 /**
  * Free the error message returned by oscap_pcre_compile. DON'T USE REGULAR free()!
diff --git a/src/common/util.c b/src/common/util.c
index 52362c098ae..8bce0d6a031 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -49,7 +49,6 @@
 #endif
 
 #define PATH_SEPARATOR '/'
-#define OSCAP_PCRE_EXEC_RECURSION_LIMIT_DEFAULT 3500
 
 int oscap_string_to_enum(const struct oscap_string_map *map, const char *str)
 {
@@ -361,76 +360,6 @@ char *oscap_path_join(const char *path1, const char *path2)
 	return joined_path;
 }
 
-int oscap_get_substrings(char *str, int *ofs, oscap_pcre_t *re, int want_substrs, char ***substrings) {
-	int i, ret, rc;
-	int ovector[60], ovector_len = sizeof (ovector) / sizeof (ovector[0]);
-	char **substrs;
-
-	// todo: max match count check
-
-	for (i = 0; i < ovector_len; ++i) {
-		ovector[i] = -1;
-	}
-
-	unsigned long limit = OSCAP_PCRE_EXEC_RECURSION_LIMIT_DEFAULT;
-	char *limit_str = getenv("OSCAP_PCRE_EXEC_RECURSION_LIMIT");
-	if (limit_str != NULL)
-		if (sscanf(limit_str, "%lu", &limit) <= 0)
-			dW("Unable to parse OSCAP_PCRE_EXEC_RECURSION_LIMIT value");
-	oscap_pcre_set_match_limit_recursion(re, limit);
-	size_t str_len = strlen(str);
-#if defined(OS_SOLARIS)
-	rc = oscap_pcre_exec(re, str, str_len, *ofs, OSCAP_PCRE_OPTS_NO_UTF8_CHECK, ovector, ovector_len);
-#else
-	rc = oscap_pcre_exec(re, str, str_len, *ofs, 0, ovector, ovector_len);
-#endif
-
-	if (rc < OSCAP_PCRE_ERR_NOMATCH) {
-		if (str_len < 100)
-			dE("Function oscap_pcre_exec() failed to match a regular expression with return code %d on string '%s'.", rc, str);
-		else
-			dE("Function oscap_pcre_exec() failed to match a regular expression with return code %d on string '%.100s' (truncated, showing first 100 characters).", rc, str);
-		return rc;
-	} else if (rc == OSCAP_PCRE_ERR_NOMATCH) {
-		/* no match */
-		return 0;
-	}
-
-	*ofs = (*ofs == ovector[1]) ? ovector[1] + 1 : ovector[1];
-
-	if (!want_substrs) {
-		/* just report successful match */
-		return 1;
-	}
-
-	ret = 0;
-	if (rc == 0) {
-		/* vector too small */
-		// todo: report partial results
-		rc = ovector_len / 3;
-	}
-
-	substrs = malloc(rc * sizeof (char *));
-	for (i = 0; i < rc; ++i) {
-		int len;
-		char *buf;
-
-		if (ovector[2 * i] == -1) {
-			continue;
-		}
-		len = ovector[2 * i + 1] - ovector[2 * i];
-		buf = malloc(len + 1);
-		memcpy(buf, str + ovector[2 * i], len);
-		buf[len] = '\0';
-		substrs[ret] = buf;
-		++ret;
-	}
-
-	*substrings = substrs;
-
-	return ret;
-}
-
 #ifndef OS_WINDOWS
 FILE *oscap_fopen_with_prefix(const char *prefix, const char *path)
 {
diff --git a/src/common/util.h b/src/common/util.h
index 7f84e6436bc..0811a7698c1 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -487,20 +487,6 @@ int oscap_strncasecmp(const char *s1, const char *s2, size_t n);
  */
 char *oscap_strerror_r(int errnum, char *buf, size_t buflen);
 
-/**
- * Match a regular expression and return substrings.
- * Caller is responsible for freeing the returned array.
- * @param str subject string
- * @param ofs starting offset in str
- * @param re compiled regular expression
- * @param want_substrs if non-zero, substrings will be returned
- * @param substrings contains returned substrings
- * @return count of matched substrings, 0 if no match
- * negative value on failure
- */
-int oscap_get_substrings(char *str, int *ofs, oscap_pcre_t *re, int want_substrs, char ***substrings);
-
-
 #ifndef OS_WINDOWS
 /**
  * Open file for reading with prefix added to its name.