diff --git a/Makefile.am b/Makefile.am index 7ac184b504..460303cb7b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -63,7 +63,7 @@ cppcheck: --enable=warning,style,performance,portability,unusedFunction,missingInclude \ --inconclusive \ --template="warning: {file},{line},{severity},{id},{message}" \ - -I headers -I . -I $(top_srcdir)/others -I $(top_srcdir)/src -I $(top_srcdir)/others/mbedtls/include \ + -I headers -I . -I $(top_srcdir)/others -I $(top_srcdir)/src -I $(top_srcdir)/others/mbedtls/include -I $(top_srcdir)/others/mbedtls/tf-psa-crypto/include -I $(top_srcdir)/others/mbedtls/tf-psa-crypto/drivers/builtin/include \ --error-exitcode=1 \ -i "src/parser/seclang-parser.cc" -i "src/parser/seclang-scanner.cc" \ -i others \ @@ -99,4 +99,3 @@ pkgconfig_DATA = modsecurity.pc EXTRA_DIST = modsecurity.pc.in \ modsecurity.conf-recommended \ unicode.mapping - diff --git a/README.md b/README.md index 50b8833319..7bb6a0aa95 100644 --- a/README.md +++ b/README.md @@ -47,36 +47,51 @@ Keeping these connectors separated allows each project to have different release # Compilation -Before starting the compilation process, make sure that you have all the -dependencies in place. Read the subsection “Dependencies” for further -information. +Before starting the compilation process, make sure that all required dependencies are installed. +See the [Dependencies](#dependencies) and [Git submodules](#Git-submodules) section for further information. -After the compilation make sure that there are no issues on your -build/platform. We strongly recommend the utilization of the unit tests and -regression tests. These test utilities are located under the subfolder ‘tests’. +After compilation, make sure that there are no issues on your build/platform. +We strongly recommend running the unit tests and regression tests. These test utilities are located in the [`tests/`](#testing-your-patch) subfolder. -As a dynamic library, don’t forget that libmodsecurity must be installed to a location (folder) where you OS will be looking for dynamic libraries. +As a dynamic library, `libmodsecurity` must be installed in a location where your operating system can find dynamic libraries. +### Unix (Linux, macOS, FreeBSD, …) +On Unix-like systems, the project uses autotools for the compilation process. -### Unix (Linux, MacOS, FreeBSD, …) +If you are working with a git checkout, make sure to clone the repository recursively or initialize all submodules before building. +See also the [Git submodules](#git-submodules) section. -On unix the project uses autotools to help the compilation process. Please note that if you are working with `git`, don't forget to initialize and update the submodules. Here's a quick how-to: -```shell -$ git clone --recursive https://github.com/owasp-modsecurity/ModSecurity ModSecurity -$ cd ModSecurity +```sh +git clone https://github.com/owasp-modsecurity/ModSecurity ModSecurity +cd ModSecurity +```` + +This repository uses git submodules. After cloning, make sure to initialize and fetch all submodules: + +```sh +git submodule update --init --recursive +``` + +You can verify that all submodules are properly initialized with: + +```sh +git submodule status ``` +Submodules that are correctly initialized show a commit hash. +A leading `-` indicates that the submodule has not been initialized. + You can then start the build process: -```shell -$ ./build.sh -$ ./configure -$ make -$ sudo make install +```sh +./build.sh +./configure +make +sudo make install ``` -Details on distribution specific builds can be found in our Wiki: +Details on distribution-specific builds can be found in our Wiki: [Compilation Recipes](https://github.com/owasp-modsecurity/ModSecurity/wiki/Compilation-recipes) ### Windows @@ -85,15 +100,110 @@ Windows build information can be found [here](build/win32/README.md). ## Dependencies -This library is written in C++ using the C++17 standards. It also uses Flex -and Yacc to produce the “Sec Rules Language” parser. Other, mandatory dependencies include YAJL, as ModSecurity uses JSON for producing logs and its testing framework, libpcre (not yet mandatory) for processing regular expressions in SecRules, and libXML2 (not yet mandatory) which is used for parsing XML requests. +* This library is written in C++ using the C++17 standard. +* It uses Flex and Bison (Yacc) to produce the “Sec Rules Language” parser. +* Mandatory dependencies include YAJL, as ModSecurity uses JSON for logging and its testing framework. +* libXML2 (optional) is used for parsing XML requests. + +### Regular expression engine (PCRE2 / PCRE) + +* Regular expression processing in SecRules is implemented via the `Regex` utility (`src/utils/regex.*`). +* By default, ModSecurity uses **PCRE2** for regex handling. +* This is used by operators such as `@rx`, `@rxGlobal`, and `@verifyCC`. +* Build-time behavior: + + * **Default:** PCRE2 is detected and used. + * **Fallback:** legacy PCRE can be used if `--with-pcre` is explicitly provided (`WITH_PCRE`). +* In other words, current builds expect PCRE2 unless explicitly configured otherwise. + +All other dependencies are related to operators specified within SecRules or configuration directives and may not be required for compilation. + +### Operator-related dependencies + +* `libinjection` is required for the operators `@detectXSS` and `@detectSQL`. +* `curl` is required for the directive `SecRemoteRules`. + +If those libraries are missing, ModSecurity will be compiled without support for the respective operators or directives. + +### Git-submodules + +The repository includes the following submodules: + +* `others/libinjection` – used by `@detectSQLi` and `@detectXSS` operators. + +* `others/mbedtls` (TF-PSA-Crypto subset) – used for cryptographic functions and helpers (e.g. hashing, base64). + + **Note:** The newer mbedTLS v4 layout is not compatible with the older v3 structure. + The internal structure has changed significantly, and many components have been moved into submodules (e.g. TF-PSA-Crypto). + + After merging PR #3532, it is required to run: + + ```sh + git submodule update --init --recursive + ``` + + This ensures that all required submodules are fetched. Without this step, the project will not build successfully. + + You can verify that all submodules are properly initialized with: + + ```sh + git submodule status + ``` + + Example output: + + ```sh + bc625d5... bindings/python + 2117822... others/libinjection (v4.0.0) + 0fe989b... others/mbedtls (v4.1.0) + a3d4405... test/test-cases/secrules-language-tests + ``` + + If a submodule is missing, it will be shown with a leading `-`, for example: + + ```sh + -bc625d5... bindings/python + ``` + + A leading `-` indicates that the submodule has not been initialized or fetched. + +* `test/test-cases/secrules-language-tests` – shared SecRules conformance and regression test suite used by `make check`. + +* `bindings/python` – Python bindings for ModSecurity (not required for core library compilation). + +`others/libinjection` and `others/mbedtls` are effectively required for source builds and must be initialized before building. + +### Optional external dependencies + +Several external libraries are optional and enable additional features, including: + +* `libcurl` – required for `SecRemoteRules` + +* LMDB – persistent storage support + +* Lua – scripting support + +* XML libraries – extended XML processing + +* **GeoIP (legacy) / MaxMind** + + The legacy **GeoIP C API** (libGeoIP) is **deprecated and no longer maintained** by MaxMind. + The upstream repository has been archived and should not be used for new deployments. + + Instead, ModSecurity supports the modern **MaxMind DB API (libmaxminddb)**, which is actively maintained. + + During configuration you may see something like: + + ```sh + + GeoIP/MaxMind ....found + * (MaxMind) v1.12.2 + -lmaxminddb , -I/usr/include/x86_64-linux-gnu + ``` -All others dependencies are related to operators specified within SecRules or configuration directives and may not be required for compilation. A short list of such dependencies is as follows: + This indicates that **libmaxminddb** is being used (recommended). -* libinjection is needed for the operator @detectXSS and @detectSQL -* curl is needed for the directive SecRemoteRules. + It is strongly recommended to use MaxMind DB instead of the legacy GeoIP library. -If those libraries are missing ModSecurity will be compiled without the support for the operator @detectXSS and the configuration directive SecRemoteRules. # Library documentation diff --git a/build/win32/CMakeLists.txt b/build/win32/CMakeLists.txt index 5ee49714d3..399d4727c1 100644 --- a/build/win32/CMakeLists.txt +++ b/build/win32/CMakeLists.txt @@ -51,10 +51,32 @@ target_compile_definitions(libinjection PRIVATE LIBINJECTION_VERSION="${LIBINJEC project(mbedcrypto C) set(MBEDTLS_DIR ${BASE_DIR}/others/mbedtls) +set(TF_PSA_CRYPTO_DIR ${MBEDTLS_DIR}/tf-psa-crypto) + +add_library(mbedcrypto STATIC + ${TF_PSA_CRYPTO_DIR}/utilities/base64.c + ${TF_PSA_CRYPTO_DIR}/utilities/constant_time.c + ${TF_PSA_CRYPTO_DIR}/platform/platform_util.c + ${TF_PSA_CRYPTO_DIR}/extras/md.c + ${TF_PSA_CRYPTO_DIR}/drivers/builtin/src/md5.c + ${TF_PSA_CRYPTO_DIR}/drivers/builtin/src/sha1.c + ${TF_PSA_CRYPTO_DIR}/drivers/builtin/src/sha256.c + ${TF_PSA_CRYPTO_DIR}/drivers/builtin/src/sha512.c + ${TF_PSA_CRYPTO_DIR}/drivers/builtin/src/sha3.c + ${TF_PSA_CRYPTO_DIR}/drivers/builtin/src/ripemd160.c + ${TF_PSA_CRYPTO_DIR}/drivers/builtin/src/psa_util_internal.c +) -add_library(mbedcrypto STATIC ${MBEDTLS_DIR}/library/base64.c ${MBEDTLS_DIR}/library/sha1.c ${MBEDTLS_DIR}/library/md5.c ${MBEDTLS_DIR}/library/platform_util.c ${MBEDTLS_DIR}/library/constant_time.c) - -target_include_directories(mbedcrypto PRIVATE ${MBEDTLS_DIR}/include) +target_include_directories(mbedcrypto PRIVATE + ${MBEDTLS_DIR}/include + ${TF_PSA_CRYPTO_DIR}/include + ${TF_PSA_CRYPTO_DIR}/core + ${TF_PSA_CRYPTO_DIR}/extras + ${TF_PSA_CRYPTO_DIR}/library + ${TF_PSA_CRYPTO_DIR}/utilities + ${TF_PSA_CRYPTO_DIR}/drivers/builtin/include + ${TF_PSA_CRYPTO_DIR}/drivers/builtin/src +) # get mbedtls version with git describe execute_process( @@ -72,7 +94,7 @@ message("-- Detecting Mbed TLS version - ${MBEDTLS_VERSION}") project(libModSecurity VERSION - 3.0.12 + 3.0.14 LANGUAGES CXX ) @@ -137,7 +159,7 @@ file(GLOB_RECURSE libModSecuritySources ${BASE_DIR}/src/*.cc) add_library(libModSecurity SHARED ${libModSecuritySources}) target_compile_definitions(libModSecurity PRIVATE WITH_PCRE2) -target_include_directories(libModSecurity PRIVATE ${BASE_DIR} ${BASE_DIR}/headers ${BASE_DIR}/others ${MBEDTLS_DIR}/include) +target_include_directories(libModSecurity PRIVATE ${BASE_DIR} ${BASE_DIR}/headers ${BASE_DIR}/others ${MBEDTLS_DIR}/include ${TF_PSA_CRYPTO_DIR}/include ${TF_PSA_CRYPTO_DIR}/drivers/builtin/include) target_link_libraries(libModSecurity PRIVATE pcre2::pcre2 libinjection mbedcrypto Poco::Poco Iphlpapi.lib) macro(add_package_dependency project compile_definition link_library flag) diff --git a/build/win32/README.md b/build/win32/README.md index 2c9c11d06f..becabe23c3 100644 --- a/build/win32/README.md +++ b/build/win32/README.md @@ -18,15 +18,15 @@ The Windows build of libModSecurity uses Build Tools for Visual Studio 2022 (for * Windows SDK * CMake * Address Sanitizer - * [Conan package manager 2.10.2](https://github.com/conan-io/conan/releases/download/2.10.2/conan-2.10.2-windows-x86_64-installer.exe) + * [Conan package manager 2.27.1](https://github.com/conan-io/conan/releases/download/2.27.1/conan-2.27.1-windows-x86_64-installer.exe) * Install and then setup the default Conan profile to use the MSVC C++ compiler: 1. Open a command-prompt and set the MSVC C++ compiler environment by executing: `C:\BuildTools\VC\Auxiliary\Build\vcvars64.bat` 2. Execute: `conan profile detect --force` - * [Git for Windows 2.44.0](https://github.com/git-for-windows/git/releases/download/v2.44.0.windows.1/Git-2.44.0-64-bit.exe) + * [Git for Windows 2.53.0](https://github.com/git-for-windows/git/releases/download/v2.53.0.windows.1/Git-2.53.0-64-bit.exe) * To clone the libModSecurity repository. - * NOTE: Make sure to initialize and update submodules (to get `libinjection` and regression tests) - * `git submodule init` - * `git submodule update` + * NOTE: Make sure to initialize and update submodules (to get `libinjection`, `mbedtls` and regression tests) + * `git submodule update --init --recursive` + * `git submodule status` ## Build @@ -65,11 +65,11 @@ NOTE: When building a different configuration, it's recommended to reset: By default the following all the following features are enabled by including the associated third-party library through a Conan package: - * libxml2 2.12.6 for XML processing support - * libcurl 8.6.0 to support http requests from rules - * libmaxminddb 1.9.1 to support reading MaxMind DB files. - * LUA 5.4.6 to enable rules to run scripts in this language for extensibility - * lmdb 0.9.31 in-memory database + * libxml2 2.15.2 for XML processing support + * libcurl 8.19.0 to support http requests from rules + * libmaxminddb 1.12.2 to support reading MaxMind DB files. + * LUA 5.5.0 to enable rules to run scripts in this language for extensibility + * lmdb 0.9.32 in-memory database Each of these can be turned off by updating the associated `HAVE_xxx` variable (setting it to zero) in the beginning of the libModSecurity section of `CMakeLists.txt`. diff --git a/build/win32/conanfile.txt b/build/win32/conanfile.txt index b8f9721d0a..29dbde4aa4 100644 --- a/build/win32/conanfile.txt +++ b/build/win32/conanfile.txt @@ -1,13 +1,13 @@ [requires] yajl/2.1.0 -pcre2/10.42 -libxml2/2.12.6 -lua/5.4.6 -libcurl/8.6.0 -lmdb/0.9.31 -libmaxminddb/1.9.1 +pcre2/10.44 +libxml2/2.15.2 +lua/5.5.0 +libcurl/8.19.0 +lmdb/0.9.32 +libmaxminddb/1.12.2 dirent/1.24 -poco/1.13.3 +poco/1.14.2 [generators] CMakeDeps diff --git a/build/win32/docker/Dockerfile b/build/win32/docker/Dockerfile index e01897904c..b81e0b85f6 100644 --- a/build/win32/docker/Dockerfile +++ b/build/win32/docker/Dockerfile @@ -24,7 +24,7 @@ RUN C:\TEMP\InstallBuildTools.cmd C:\TEMP\vs_buildtools.exe --quiet --wait --nor --installPath C:\BuildTools # download & install GIT -ARG GIT_VERSION=2.44.0 +ARG GIT_VERSION=2.53.0 ARG GIT_BINARY=Git-${GIT_VERSION}-64-bit.exe ARG GIT_URL=https://github.com/git-for-windows/git/releases/download/v${GIT_VERSION}.windows.1/${GIT_BINARY} @@ -35,7 +35,7 @@ RUN %INSTALLER% /SP- /VERYSILENT /SUPPRESSMSGBOXES /NOCANCEL ` /NORESTART /CLOSEAPPLICATIONS /RESTARTAPPLICATIONS /LOADINF=git.inf # download & setup conan -ARG CONAN_VERSION=2.10.2 +ARG CONAN_VERSION=2.27.1 ARG CONAN_BINARY=conan-${CONAN_VERSION}-windows-x86_64-installer.exe ARG CONAN_URL=https://github.com/conan-io/conan/releases/download/${CONAN_VERSION}/${CONAN_BINARY} @@ -87,7 +87,7 @@ RUN cmd.exe /C copy "C:\Program Files\GIT\usr\bin" \bin > NUL RUN cmd.exe /C copy "C:\Program Files\GIT\usr\bin\echo.exe" \bin\echo > NUL # disable tests that don't work on windows -ARG JQ_VERSION=1.7.1 +ARG JQ_VERSION=1.8.1 ARG JQ_BINARY=jq-windows-amd64.exe ARG JQ_URL=https://github.com/jqlang/jq/releases/download/jq-${JQ_VERSION}/${JQ_BINARY} diff --git a/configure.ac b/configure.ac index 03295be063..7bdcca6bc4 100644 --- a/configure.ac +++ b/configure.ac @@ -84,7 +84,7 @@ AC_DEFUN([LIBINJECTION_VERSION], m4_esyscmd_s(cd "others/libinjection" && git de AC_SUBST([LIBINJECTION_VERSION]) # Check for Mbed TLS -if ! test -f "${srcdir}/others/mbedtls/library/base64.c"; then +if ! test -f "${srcdir}/others/mbedtls/tf-psa-crypto/utilities/base64.c"; then AC_MSG_ERROR([\ @@ -532,4 +532,3 @@ if test "$aflFuzzer" = "true"; then echo " $ export CC=afl-clang-fast " echo " " fi - diff --git a/others/Makefile.am b/others/Makefile.am index beba0bfc84..3217fd7af8 100644 --- a/others/Makefile.am +++ b/others/Makefile.am @@ -16,19 +16,25 @@ noinst_HEADERS = \ libinjection/src/libinjection_sqli_data.h \ libinjection/src/libinjection_xss.h \ libinjection/src/libinjection_error.h \ - mbedtls/include/mbedtls/base64.h \ - mbedtls/include/mbedtls/check_config.h \ + mbedtls/tf-psa-crypto/include/mbedtls/base64.h \ + mbedtls/tf-psa-crypto/core/check_crypto_config.h \ mbedtls/include/mbedtls/mbedtls_config.h \ - mbedtls/include/mbedtls/md5.h \ - mbedtls/include/mbedtls/platform.h \ - mbedtls/include/mbedtls/sha1.h + mbedtls/tf-psa-crypto/include/mbedtls/md.h \ + mbedtls/tf-psa-crypto/include/mbedtls/platform.h libmbedtls_la_SOURCES = \ - mbedtls/library/base64.c \ - mbedtls/library/md5.c \ - mbedtls/library/sha1.c \ - mbedtls/library/platform_util.c + mbedtls/tf-psa-crypto/utilities/base64.c \ + mbedtls/tf-psa-crypto/utilities/constant_time.c \ + mbedtls/tf-psa-crypto/platform/platform_util.c \ + mbedtls/tf-psa-crypto/extras/md.c \ + mbedtls/tf-psa-crypto/drivers/builtin/src/md5.c \ + mbedtls/tf-psa-crypto/drivers/builtin/src/sha1.c \ + mbedtls/tf-psa-crypto/drivers/builtin/src/sha256.c \ + mbedtls/tf-psa-crypto/drivers/builtin/src/sha512.c \ + mbedtls/tf-psa-crypto/drivers/builtin/src/sha3.c \ + mbedtls/tf-psa-crypto/drivers/builtin/src/ripemd160.c \ + mbedtls/tf-psa-crypto/drivers/builtin/src/psa_util_internal.c -libmbedtls_la_CFLAGS = -DMBEDTLS_CONFIG_FILE=\"mbedtls/mbedtls_config.h\" -I$(top_srcdir)/others/mbedtls/include +libmbedtls_la_CFLAGS = -DMBEDTLS_CONFIG_FILE=\"mbedtls/mbedtls_config.h\" -I$(top_srcdir)/others/mbedtls/include -I$(top_srcdir)/others/mbedtls/tf-psa-crypto/include -I$(top_srcdir)/others/mbedtls/tf-psa-crypto/core -I$(top_srcdir)/others/mbedtls/tf-psa-crypto/extras -I$(top_srcdir)/others/mbedtls/tf-psa-crypto/library -I$(top_srcdir)/others/mbedtls/tf-psa-crypto/utilities -I$(top_srcdir)/others/mbedtls/tf-psa-crypto/drivers/builtin/include -I$(top_srcdir)/others/mbedtls/tf-psa-crypto/drivers/builtin/src libmbedtls_la_CPPFLAGS = libmbedtls_la_LIBADD = diff --git a/others/mbedtls b/others/mbedtls index 2ca6c285a0..0fe989b6b5 160000 --- a/others/mbedtls +++ b/others/mbedtls @@ -1 +1 @@ -Subproject commit 2ca6c285a0dd3f33982dd57299012dacab1ff206 +Subproject commit 0fe989b6b514192783c469039edd325fd0989806 diff --git a/src/Makefile.am b/src/Makefile.am index 476585eda2..7154215633 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -297,6 +297,8 @@ libmodsecurity_la_CPPFLAGS = \ -g \ -I$(top_srcdir)/others \ -I$(top_srcdir)/others/mbedtls/include \ + -I$(top_srcdir)/others/mbedtls/tf-psa-crypto/include \ + -I$(top_srcdir)/others/mbedtls/tf-psa-crypto/drivers/builtin/include \ -fPIC \ -O3 \ -I$(top_srcdir)/headers \ @@ -344,4 +346,3 @@ libmodsecurity_la_LIBADD = \ $(MAXMIND_LDADD) \ $(SSDEEP_LDADD) \ $(YAJL_LDADD) - diff --git a/src/actions/transformations/base64_decode.cc b/src/actions/transformations/base64_decode.cc index fe910e5012..e71f81350a 100644 --- a/src/actions/transformations/base64_decode.cc +++ b/src/actions/transformations/base64_decode.cc @@ -17,13 +17,21 @@ #include "src/utils/base64.h" +#include + namespace modsecurity::actions::transformations { bool Base64Decode::transform(std::string &value, const Transaction *trans) const { if (value.empty()) return false; - value = Utils::Base64::decode(value); + + std::string transformedValue; + if (!Utils::Base64::decode(value, &transformedValue)) { + return false; + } + + value = std::move(transformedValue); return true; } diff --git a/src/actions/transformations/base64_decode_ext.cc b/src/actions/transformations/base64_decode_ext.cc index e6c13987f6..0053358973 100644 --- a/src/actions/transformations/base64_decode_ext.cc +++ b/src/actions/transformations/base64_decode_ext.cc @@ -17,13 +17,21 @@ #include "src/utils/base64.h" +#include + namespace modsecurity::actions::transformations { bool Base64DecodeExt::transform(std::string &value, const Transaction *trans) const { if (value.empty()) return false; - value = Utils::Base64::decode_forgiven(value); + + std::string transformedValue; + if (!Utils::Base64::decode_forgiven(value, &transformedValue)) { + return false; + } + + value = std::move(transformedValue); return true; } diff --git a/src/actions/transformations/base64_encode.cc b/src/actions/transformations/base64_encode.cc index bf802a1e8d..24751b1910 100644 --- a/src/actions/transformations/base64_encode.cc +++ b/src/actions/transformations/base64_encode.cc @@ -17,13 +17,21 @@ #include "src/utils/base64.h" +#include + namespace modsecurity::actions::transformations { bool Base64Encode::transform(std::string &value, const Transaction *trans) const { if (value.empty()) return false; - value = Utils::Base64::encode(value); + + std::string transformedValue; + if (!Utils::Base64::encode(value, &transformedValue)) { + return false; + } + + value = std::move(transformedValue); return true; } diff --git a/src/actions/transformations/md5.cc b/src/actions/transformations/md5.cc index b1cdc93ece..dbd67061e1 100644 --- a/src/actions/transformations/md5.cc +++ b/src/actions/transformations/md5.cc @@ -21,8 +21,7 @@ namespace modsecurity::actions::transformations { bool Md5::transform(std::string &value, const Transaction *trans) const { - value = Utils::Md5::digest(value); - return true; + return Utils::Md5::digestReplace(&value); } diff --git a/src/actions/transformations/sha1.cc b/src/actions/transformations/sha1.cc index 388633455c..b8b71389ad 100644 --- a/src/actions/transformations/sha1.cc +++ b/src/actions/transformations/sha1.cc @@ -22,8 +22,7 @@ namespace modsecurity::actions::transformations { bool Sha1::transform(std::string &value, const Transaction *trans) const { - value = Utils::Sha1::digest(value); - return true; + return Utils::Sha1::digestReplace(&value); } diff --git a/src/audit_log/writer/parallel.cc b/src/audit_log/writer/parallel.cc index 5f3055f4c7..fdbc3f0277 100644 --- a/src/audit_log/writer/parallel.cc +++ b/src/audit_log/writer/parallel.cc @@ -163,8 +163,14 @@ bool Parallel::write(Transaction *transaction, int parts, std::string *error) { if (m_audit->m_path1.empty() == false && m_audit->m_path2.empty() == false) { + std::string logMd5; + if (!Utils::Md5::hexdigest(log, &logMd5)) { + error->assign("Failed to calculate audit log MD5 digest"); + return false; + } + std::string msg = transaction->toOldAuditLogFormatIndex(fileName, - log.length(), Utils::Md5::hexdigest(log)); + log.length(), logMd5); ret = utils::SharedFiles::getInstance().write(m_audit->m_path2, msg, error); if (ret == false) { @@ -173,8 +179,14 @@ bool Parallel::write(Transaction *transaction, int parts, std::string *error) { } if (m_audit->m_path1.empty() == false && m_audit->m_path2.empty() == true) { + std::string logMd5; + if (!Utils::Md5::hexdigest(log, &logMd5)) { + error->assign("Failed to calculate audit log MD5 digest"); + return false; + } + std::string msg = transaction->toOldAuditLogFormatIndex(fileName, - log.length(), Utils::Md5::hexdigest(log)); + log.length(), logMd5); ret = utils::SharedFiles::getInstance().write(m_audit->m_path1, msg, error); if (ret == false) { @@ -183,8 +195,14 @@ bool Parallel::write(Transaction *transaction, int parts, std::string *error) { } if (m_audit->m_path1.empty() == true && m_audit->m_path2.empty() == false) { + std::string logMd5; + if (!Utils::Md5::hexdigest(log, &logMd5)) { + error->assign("Failed to calculate audit log MD5 digest"); + return false; + } + std::string msg = transaction->toOldAuditLogFormatIndex(fileName, - log.length(), Utils::Md5::hexdigest(log)); + log.length(), logMd5); ret = utils::SharedFiles::getInstance().write(m_audit->m_path2, msg, error); if (ret == false) { diff --git a/src/unique_id.cc b/src/unique_id.cc index 890188e990..c2e47e2032 100644 --- a/src/unique_id.cc +++ b/src/unique_id.cc @@ -55,6 +55,7 @@ #include #endif #include +#include #include "src/utils/sha1.h" @@ -72,7 +73,12 @@ void UniqueId::fillUniqueId() { data = macAddress + name; - this->uniqueId_str = Utils::Sha1::hexdigest(data); + std::string uniqueIdHex; + if (Utils::Sha1::hexdigest(data, &uniqueIdHex)) { + this->uniqueId_str = std::move(uniqueIdHex); + } else { + this->uniqueId_str.clear(); + } } // Based on: diff --git a/src/utils/base64.cc b/src/utils/base64.cc index e27cace943..bb859625e4 100644 --- a/src/utils/base64.cc +++ b/src/utils/base64.cc @@ -18,54 +18,92 @@ #include #include -#include -#include +#include #include "mbedtls/base64.h" -template -inline std::string base64Helper(const char *data, const unsigned int len, Operation op) { // cppcheck-suppress syntaxError ; false positive +namespace { +template +bool base64HelperMbedtls(const char *data, const unsigned int len, + Base64MbedtlsOperation op, std::string *output) { + if (output == nullptr) { + return false; + } + + size_t out_len = 0; + + if (const int sizingRet = op(nullptr, 0, &out_len, + reinterpret_cast(data), len); + sizingRet != 0 && sizingRet != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { + return false; + } + + std::string ret(out_len, {}); + if (out_len > 0) { + if (const int retCode = op(reinterpret_cast(ret.data()), + ret.size(), &out_len, + reinterpret_cast(data), len); + retCode != 0) { + return false; + } + + ret.resize(out_len); + } + + *output = std::move(ret); + return true; +} + +template +bool base64HelperForgiven(const char *data, const unsigned int len, + Base64ForgivenOperation op, std::string *output) { + if (output == nullptr) { + return false; + } + size_t out_len = 0; op(nullptr, 0, &out_len, reinterpret_cast(data), len); std::string ret(out_len, {}); - if(out_len > 0) { + if (out_len > 0) { op(reinterpret_cast(ret.data()), ret.size(), &out_len, reinterpret_cast(data), len); ret.resize(out_len); } - return ret; + *output = std::move(ret); + return true; } +} // namespace namespace modsecurity { namespace Utils { -std::string Base64::encode(const std::string& data) { - return base64Helper(data.c_str(), data.size(), mbedtls_base64_encode); +bool Base64::encode(const std::string& data, std::string *output) { + return base64HelperMbedtls(data.c_str(), data.size(), mbedtls_base64_encode, output); } -std::string Base64::decode(const std::string& data, bool forgiven) { +bool Base64::decode(const std::string& data, std::string *output, bool forgiven) { if (forgiven) { - return decode_forgiven(data); + return decode_forgiven(data, output); } - return decode(data); + return decode(data, output); } -std::string Base64::decode(const std::string& data) { - return base64Helper(data.c_str(), strlen(data.c_str()), mbedtls_base64_decode); +bool Base64::decode(const std::string& data, std::string *output) { + return base64HelperMbedtls(data.c_str(), strlen(data.c_str()), mbedtls_base64_decode, output); } -std::string Base64::decode_forgiven(const std::string& data) { - return base64Helper(data.c_str(), data.size(), decode_forgiven_engine); +bool Base64::decode_forgiven(const std::string& data, std::string *output) { + return base64HelperForgiven(data.c_str(), data.size(), decode_forgiven_engine, output); } diff --git a/src/utils/base64.h b/src/utils/base64.h index 97639a5413..b57beb86bb 100644 --- a/src/utils/base64.h +++ b/src/utils/base64.h @@ -26,11 +26,11 @@ class Base64 { public: Base64() { } - static std::string encode(const std::string& data); + static bool encode(const std::string& data, std::string *output); - static std::string decode(const std::string& data, bool forgiven); - static std::string decode(const std::string& data); - static std::string decode_forgiven(const std::string& data); + static bool decode(const std::string& data, std::string *output, bool forgiven); + static bool decode(const std::string& data, std::string *output); + static bool decode_forgiven(const std::string& data, std::string *output); static void decode_forgiven_engine(unsigned char *plain_text, size_t plain_text_size, size_t *aiming_size, diff --git a/src/utils/md5.h b/src/utils/md5.h index 68f5d748e4..85c21bf1fd 100644 --- a/src/utils/md5.h +++ b/src/utils/md5.h @@ -17,16 +17,15 @@ #define SRC_UTILS_MD5_H_ #include "src/utils/sha1.h" -#include "mbedtls/md5.h" #include namespace modsecurity::Utils { -class Md5 : public DigestImpl<&mbedtls_md5, 16> { +class Md5 : public DigestImpl { }; } // namespace modsecurity::Utils -#endif // SRC_UTILS_MD5_H_ \ No newline at end of file +#endif // SRC_UTILS_MD5_H_ diff --git a/src/utils/sha1.h b/src/utils/sha1.h index a40d7fa1c8..d827d7f656 100644 --- a/src/utils/sha1.h +++ b/src/utils/sha1.h @@ -3,7 +3,7 @@ * Copyright (c) 2015 - 2021 Trustwave Holdings, Inc. (http://www.trustwave.com/) * * You may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -16,60 +16,90 @@ #ifndef SRC_UTILS_SHA1_H_ #define SRC_UTILS_SHA1_H_ +#include +#include #include -#include +#include #include "src/utils/string.h" -#include "mbedtls/sha1.h" +#include "mbedtls/md.h" namespace modsecurity::Utils { -using DigestOp = int (*)(const unsigned char *, size_t, unsigned char []); - - -template +template class DigestImpl { public: - - static std::string digest(const std::string& input) { - return digestHelper(input, [](const auto digest) { - return std::string(digest); - }); + static bool digest(const std::string& input, std::string *output) { + if (output == nullptr) { + return false; + } + + std::array digestBytes; + if (!calculateDigest(input, &digestBytes)) { + return false; + } + + output->assign(digestBytes.begin(), digestBytes.end()); + return true; } - static void digestReplace(std::string& value) { - digestHelper(value, [&value](const auto digest) mutable { - value = digest; - }); - } + static bool digestReplace(std::string *value) { + if (value == nullptr) { + return false; + } - static std::string hexdigest(const std::string &input) { - return digestHelper(input, [](const auto digest) { - return utils::string::string_to_hex(digest); - }); + return digest(*value, value); } -private: + static bool hexdigest(const std::string& input, std::string *output) { + if (output == nullptr) { + return false; + } - template - static auto digestHelper(const std::string &input, - ConvertOp convertOp) -> auto { - char digest[DigestSize]; + std::array digestBytes; + if (!calculateDigest(input, &digestBytes)) { + return false; + } - const auto ret = (*digestOp)(reinterpret_cast(input.c_str()), - input.size(), reinterpret_cast(digest)); - assert(ret == 0); + const auto *digestByteData = + static_cast(static_cast(digestBytes.data())); - return convertOp(std::string_view(digest, DigestSize)); + output->assign(utils::string::string_to_hex(digestByteData, digestBytes.size())); + return true; + } + + private: + static bool calculateDigest(std::string_view input, + std::array *output) { + if (output == nullptr) { + return false; + } + + const mbedtls_md_info_t *mdInfo = mbedtls_md_info_from_type(DigestType); + if (mdInfo == nullptr) { + return false; + } + + const auto *inputBytes = + static_cast(static_cast(input.data())); + + if (const int ret = mbedtls_md( + mdInfo, + inputBytes, + input.size(), + output->data()); ret != 0) { + return false; + } + + return true; } }; -class Sha1 : public DigestImpl<&mbedtls_sha1, 20> { +class Sha1 : public DigestImpl { }; - } // namespace modsecurity::Utils #endif // SRC_UTILS_SHA1_H_ diff --git a/src/utils/string.h b/src/utils/string.h index ca2967aa5f..ac3264aeab 100644 --- a/src/utils/string.h +++ b/src/utils/string.h @@ -16,6 +16,7 @@ #ifndef SRC_UTILS_STRING_H_ #define SRC_UTILS_STRING_H_ +#include #include #include #include @@ -241,6 +242,29 @@ inline unsigned char *c2x(unsigned what, unsigned char *where) { } +inline std::string string_to_hex(const std::byte *input, size_t size) { + static const char* const lut = "0123456789abcdef"; + + std::string a(size*2, 0); + char *d = a.data(); + + for (size_t i = 0; i < size; ++i) { + const std::byte b = input[i]; + *d++ = lut[std::to_integer(b >> 4)]; + *d++ = lut[std::to_integer(b & std::byte{0x0F})]; + } + + return a; +} + + +inline std::string string_to_hex(const unsigned char *input, size_t size) { + return string_to_hex( + static_cast(static_cast(input)), + size); +} + + inline std::string string_to_hex(std::string_view input) { static const char* const lut = "0123456789abcdef"; diff --git a/src/variables/remote_user.cc b/src/variables/remote_user.cc index 550294c70e..216bde4b6a 100644 --- a/src/variables/remote_user.cc +++ b/src/variables/remote_user.cc @@ -42,6 +42,11 @@ void RemoteUser::evaluate(Transaction *transaction, std::vector l2; transaction->m_variableRequestHeaders.resolve("authorization", &l2); + std::vector> l2Owners; + l2Owners.reserve(l2.size()); + for (const auto &a : l2) { + l2Owners.emplace_back(a); + } if (!l2.empty()) { const auto *v = l2[0]; @@ -54,10 +59,13 @@ void RemoteUser::evaluate(Transaction *transaction, base64 = std::string(header, 6, header.length()); } - base64 = Utils::Base64::decode(base64); + std::string decodedAuthorization; + if (!Utils::Base64::decode(base64, &decodedAuthorization)) { + return; + } - if (const auto pos{base64.find(":")}; pos != std::string::npos) { - transaction->m_variableRemoteUser.assign(std::string(base64, 0, pos)); + if (const auto pos{decodedAuthorization.find(":")}; pos != std::string::npos) { + transaction->m_variableRemoteUser.assign(std::string(decodedAuthorization, 0, pos)); auto var = std::make_unique(&v->getKeyWithCollection(), &transaction->m_variableRemoteUser); @@ -69,9 +77,6 @@ void RemoteUser::evaluate(Transaction *transaction, l->push_back(var.release()); } - for (auto &a : l2) { - delete a; - } } } diff --git a/test/test-cases/unit/transformation-hash-base64.json b/test/test-cases/unit/transformation-hash-base64.json new file mode 100644 index 0000000000..6082fd5ca0 --- /dev/null +++ b/test/test-cases/unit/transformation-hash-base64.json @@ -0,0 +1,37 @@ +[ + { + "type": "tfn", + "name": "sha1", + "input": "abc", + "ret": 1, + "output": "\\xa9\\x99\\x3e\\x36\\x47\\x06\\x81\\x6a\\xba\\x3e\\x25\\x71\\x78\\x50\\xc2\\x6c\\x9c\\xd0\\xd8\\x9d" + }, + { + "type": "tfn", + "name": "md5", + "input": "abc", + "ret": 1, + "output": "\\x90\\x01\\x50\\x98\\x3c\\xd2\\x4f\\xb0\\xd6\\x96\\x3f\\x7d\\x28\\xe1\\x7f\\x72" + }, + { + "type": "tfn", + "name": "base64Encode", + "input": "hello", + "ret": 1, + "output": "aGVsbG8=" + }, + { + "type": "tfn", + "name": "base64Decode", + "input": "aGVsbG8=", + "ret": 1, + "output": "hello" + }, + { + "type": "tfn", + "name": "base64Decode", + "input": "!!!!", + "ret": 1, + "output": "!!!!" + } +] diff --git a/test/test-suite.in b/test/test-suite.in index c667bf677f..ebda49fb81 100644 --- a/test/test-suite.in +++ b/test/test-suite.in @@ -199,6 +199,7 @@ TESTS+=test/test-cases/secrules-language-tests/operators/containsWord.json TESTS+=test/test-cases/secrules-language-tests/operators/detectSQLi.json TESTS+=test/test-cases/secrules-language-tests/operators/detectXSS.json TESTS+=test/test-cases/unit/operator-libinjection-error.json +TESTS+=test/test-cases/unit/transformation-hash-base64.json TESTS+=test/test-cases/secrules-language-tests/operators/endsWith.json TESTS+=test/test-cases/secrules-language-tests/operators/eq.json TESTS+=test/test-cases/secrules-language-tests/operators/ge.json