Skip to content

Commit

Permalink
Added Windows build scripts using Build Tools for Visual Studio 2022 …
Browse files Browse the repository at this point in the history
…(MSVC compiler & CMake) and Conan package manager

- Included Dockerfile to automate the setup process of prerequisites
  and build of libModSecurity binaries.
  • Loading branch information
eduar-hte committed Apr 25, 2024
1 parent 70a8bbd commit 9c57c0c
Show file tree
Hide file tree
Showing 9 changed files with 583 additions and 0 deletions.
196 changes: 196 additions & 0 deletions build/win32/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
cmake_minimum_required(VERSION 3.15)

set(BASE_DIR ${CMAKE_CURRENT_LIST_DIR}/../..)

set(USE_ASAN OFF CACHE BOOL "Build with Address Sanitizer")

# common compiler settings

# NOTE: MBEDTLS_CONFIG_FILE is not only required to compile the mbedtls subset in others, but also
# when their headers are included while compiling libModSecurity
add_compile_definitions(WIN32 _CRT_SECURE_NO_WARNINGS MBEDTLS_CONFIG_FILE="mbed-tls-config.h")

# set standards conformance preprocessor & compiler to align with cross-compiled codebase
# NOTE: otherwise visual c++'s default compiler/preprocessor behaviour generates C4067 warnings
# (unexpected tokens following preprocessor directive - expected a newline)
add_compile_options(/Zc:preprocessor /permissive-)

if(USE_ASAN)
add_compile_options(/fsanitize=address)
add_link_options(/INFERASANLIBS /INCREMENTAL:no)
endif()

# libinjection

project(libinjection C)

add_library(libinjection STATIC ${BASE_DIR}/others/libinjection/src/libinjection_sqli.c ${BASE_DIR}/others/libinjection/src/libinjection_xss.c ${BASE_DIR}/others/libinjection/src/libinjection_html5.c)

# mbedtls

project(mbedtls C)

add_library(mbedtls STATIC ${BASE_DIR}/others/mbedtls/base64.c ${BASE_DIR}/others/mbedtls/sha1.c ${BASE_DIR}/others/mbedtls/md5.c)

target_include_directories(mbedtls PRIVATE ${BASE_DIR}/others)

#
# libModSecurity
#

project(libModSecurity
VERSION
3.0.12
LANGUAGES
CXX
)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED On)
set(CMAKE_CXX_EXTENSIONS Off)

set(PACKAGE_BUGREPORT "security@modsecurity.org")
set(PACKAGE_NAME "modsecurity")
set(PACKAGE_VERSION "${PROJECT_VERSION}")
set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}")
set(PACKAGE_TARNAME "${PACKAGE_NAME}")

set(HAVE_GEOIP 0) # should always be zero, no conan package available
set(HAVE_LMDB 1)
set(HAVE_LUA 1)
set(HAVE_LIBXML2 1)
set(HAVE_MAXMIND 1)
set(HAVE_SSDEEP 0) # should always be zero, no conan package available
set(HAVE_YAJL 1) # should always be one, mandatory dependency
set(HAVE_CURL 1)

include(${CMAKE_CURRENT_LIST_DIR}/ConfigureChecks.cmake)

configure_file(config.h.cmake ${BASE_DIR}/src/config.h)

find_package(PCRE2 REQUIRED)
find_package(PThreads4W REQUIRED)
find_package(Poco REQUIRED)
find_package(dirent REQUIRED) # used only by tests (check dirent::dirent refernces)

macro(include_package package flag)
if(${flag})
find_package(${package} REQUIRED)
endif()
endmacro()

include_package(yajl HAVE_YAJL)
include_package(libxml2 HAVE_LIBXML2)
include_package(lua HAVE_LUA)
include_package(CURL HAVE_CURL)
include_package(lmdb HAVE_LMDB)
include_package(maxminddb HAVE_MAXMIND)

# library
#

# NOTE: required to generate libModSecurity's import library (libModSecurity.lib), used by tests to link with shared library
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)

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)
target_link_libraries(libModSecurity PRIVATE pcre2::pcre2 pthreads4w::pthreads4w libinjection mbedtls Poco::Poco Iphlpapi.lib)

macro(add_package_dependency project compile_definition link_library flag)
if(${flag})
target_compile_definitions(${project} PRIVATE ${compile_definition})
target_link_libraries(${project} PRIVATE ${link_library})
endif()
endmacro()

add_package_dependency(libModSecurity WITH_YAJL yajl::yajl HAVE_YAJL)
add_package_dependency(libModSecurity WITH_LIBXML2 LibXml2::LibXml2 HAVE_LIBXML2)
add_package_dependency(libModSecurity WITH_LUA lua::lua HAVE_LUA)
if(HAVE_LUA)
target_compile_definitions(libModSecurity PRIVATE WITH_LUA_5_4)
endif()
add_package_dependency(libModSecurity MSC_WITH_CURL CURL::libcurl HAVE_CURL)
add_package_dependency(libModSecurity WITH_LMDB lmdb::lmdb HAVE_LMDB)
add_package_dependency(libModSecurity WITH_MAXMIND maxminddb::maxminddb HAVE_MAXMIND)

# tests
#

project(libModSecurityTests)

function(setTestTargetProperties executable)
target_compile_definitions(${executable} PRIVATE WITH_PCRE2)
target_include_directories(${executable} PRIVATE ${BASE_DIR} ${BASE_DIR}/headers)
target_link_libraries(${executable} PRIVATE libModSecurity pcre2::pcre2 dirent::dirent)
add_package_dependency(${executable} WITH_YAJL yajl::yajl HAVE_YAJL)
endfunction()

# unit tests
file(GLOB unitTestSources ${BASE_DIR}/test/unit/*.cc)
add_executable(unit_tests ${unitTestSources})
setTestTargetProperties(unit_tests)
target_compile_options(unit_tests PRIVATE /wd4805)

# regression tests
file(GLOB regressionTestsSources ${BASE_DIR}/test/regression/*.cc)
add_executable(regression_tests ${regressionTestsSources})
setTestTargetProperties(regression_tests)

macro(add_regression_test_capability compile_definition flag)
if(${flag})
target_compile_definitions(regression_tests PRIVATE ${compile_definition})
endif()
endmacro()

add_regression_test_capability(WITH_LUA HAVE_LUA)
add_regression_test_capability(WITH_CURL HAVE_CURL)
add_regression_test_capability(WITH_LMDB HAVE_LMDB)
add_regression_test_capability(WITH_MAXMIND HAVE_MAXMIND)

# benchmark
add_executable(benchmark ${BASE_DIR}/test/benchmark/benchmark.cc)
setTestTargetProperties(benchmark)

# rules_optimization
add_executable(rules_optimization ${BASE_DIR}/test/optimization/optimization.cc)
setTestTargetProperties(rules_optimization)


# examples
#

project(libModSecurityExamples)

function(setExampleTargetProperties executable)
target_include_directories(${executable} PRIVATE ${BASE_DIR} ${BASE_DIR}/headers)
target_link_libraries(${executable} PRIVATE libModSecurity)
endfunction()

# simple_example_using_c
add_executable(simple_example_using_c ${BASE_DIR}/examples/simple_example_using_c/test.c)
setExampleTargetProperties(simple_example_using_c)

# using_bodies_in_chunks
add_executable(using_bodies_in_chunks ${BASE_DIR}/examples/using_bodies_in_chunks/simple_request.cc)
setExampleTargetProperties(using_bodies_in_chunks)

# reading_logs_via_rule_message
add_executable(reading_logs_via_rule_message ${BASE_DIR}/examples/reading_logs_via_rule_message/simple_request.cc)
setExampleTargetProperties(reading_logs_via_rule_message)
target_link_libraries(reading_logs_via_rule_message PRIVATE libModSecurity pthreads4w::pthreads4w)

# reading_logs_with_offset
add_executable(reading_logs_with_offset ${BASE_DIR}/examples/reading_logs_with_offset/read.cc)
setExampleTargetProperties(reading_logs_with_offset)

# tools
#

# rules_check
add_executable(rules_check ${BASE_DIR}/tools/rules-check/rules-check.cc)
target_include_directories(rules_check PRIVATE ${BASE_DIR} ${BASE_DIR}/headers)
target_link_libraries(rules_check PRIVATE libModSecurity)
18 changes: 18 additions & 0 deletions build/win32/ConfigureChecks.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
include(CheckIncludeFile)
include(CheckIncludeFiles)

check_include_file("dlfcn.h" HAVE_DLFCN_H)
check_include_file("inttypes.h" HAVE_INTTYPES_H)
check_include_file("stdint.h" HAVE_STDINT_H)
check_include_file("stdio.h" HAVE_STDIO_H)
check_include_file("stdlib.h" HAVE_STDLIB_H)
check_include_file("string" HAVE_STRING)
check_include_file("strings.h" HAVE_STRINGS_H)
check_include_file("string.h" HAVE_STRING_H)
check_include_file("sys/stat.h" HAVE_SYS_STAT_H)
check_include_file("sys/types.h" HAVE_SYS_TYPES_H)
check_include_file("sys/utsname.h" HAVE_SYS_UTSNAME_H)
check_include_file("unistd.h" HAVE_UNISTD_H)

#/* Define to 1 if you have the ANSI C header files. */
check_include_files("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS)
111 changes: 111 additions & 0 deletions build/win32/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# libModSecurity Windows build information <!-- omit from toc -->

The Windows build of libModSecurity uses Build Tools for Visual Studio 2022 (for Visual C++ & CMake) and Conan package manager.

## Contents <!-- omit from toc -->

- [Prerequisites](#prerequisites)
- [Build](#build)
- [Optional features](#optional-features)
- [Address Sanitizer](#address-sanitizer)
- [Docker container](#docker-container)

## Prerequisites

* [Build Tools for Visual Studio 2022](https://aka.ms/vs/17/release/vs_buildtools.exe)
* Install *Desktop development with C++* workload, which includes:
* MSVC C++ compiler
* Windows SDK
* CMake
* Address Sanitizer
* [Conan package manager 2.2.2](https://github.com/conan-io/conan/releases/download/2.2.2/conan-2.2.2-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)
* 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`

## Build

Install the prerequisites listsed in the previous section, checkout libModSecurity and from the directory where it's located execute:

```
vcbuild.bat [build_configuration] [arch] [USE_ASAN]
```

where `[build_configuration]` can be: `Release` (default), `RelWithDebInfo`, `MinSizeRel` or `Debug`, and `[arch]` can be: `x86_64` (default) or `x86`.

Built files will be located in the directory: `build\win32\build\[build_configuration]` and include:

* `libModSecurity.dll`
* Executable files for test projects
* `unit_tests.exe`
* `regression_tests.exe`
* `benchmark.exe`
* `rules_optimization.exe`
* Executable files for examples
* `simple_example_using_c.exe`
* `using_bodies_in_chunks.exe`
* `reading_logs_via_rule_message.exe`
* `reading_logs_with_offset.exe`
* Executable files for tools
* `rules_check.exe`

NOTE: When building a different configuration, it's recommended to reset:

* the build directory: `build\win32\build`
* previously built conan packages executing the command:
* `conan remove * -c`

### Optional features

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

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`.

### Address Sanitizer

[AddressSanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer) (aka ASan) is a memory error detector for C/C++.

To generate a build with *Address Sanitizer*, add the `USE_ASAN` optional third argument to `vcbuild.bat`. For example:
* `vcbuild.bat Debug x86_64 USE_ASAN`

NOTE: `USE_ASAN` does not work with `Release` & `MinSizeRel` configurations because they do not include debug info (it is only compatible with `Debug` & `RelWithDebInfo` builds).

* References
* [AddressSanitizer | Microsoft Learn](https://learn.microsoft.com/en-us/cpp/sanitizers/asan?view=msvc-170)
* [AddressSanitizer for Windows: x64 and Debug Build Support - C++ Team Blog (microsoft.com)](https://devblogs.microsoft.com/cppblog/asan-for-windows-x64-and-debug-build-support/)
* [AddressSanitizer language, build, and debugging reference | Microsoft Learn](https://learn.microsoft.com/en-us/cpp/sanitizers/asan-building?view=msvc-170)

### Docker container

A `Dockerfile` configuration file is provided in the `docker` subdir that creates a Windows container image which installs the [prerequisites](#prerequisites) and builds libModSecurity and other binaries.

NOTE: Windows containers are supported in Docker Desktop for Windows, using the *Switch to Windows containers...* option on the context menu of the system tray icon.

To build the docker image, execute the following command (from the `build\win32\docker` directory):

* `docker build -t buildtools_libmodsecurity:latest -m 4GB .`
* Build type, architecture and build with Address Sanitizer can be configured through build arguments (`BUILD_TYPE`, `ARCH` & `USE_ASAN` respectively). For example, to generate a debug build, add the following argument:
* `--build-arg BUILD_TYPE=Debug`

Once the image is generated, the library and associated binaries (tests & examples) are located in the `C:\src\ModSecurity\build\win32\build\[build_type]` directory.

To extract the library (`libModSecurity.dll`) from the image, you can execute the following commands:

* `docker container create --name [container_name] buildtools_libmodsecurity`
* `docker cp [container_name]:C:\src\ModSecurity\build\win32\build\[build_type]\libModSecurity.dll .`
* NOTE: If you leave out the `libModSecurity.dll` filename out, you can copy all the built binaries (including examples & tests).

Additionally, the image can be used interactively for additional development work by executing:

* `docker run -it buildtools_libmodsecurity`
15 changes: 15 additions & 0 deletions build/win32/conanfile.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[requires]
yajl/2.1.0
pcre2/10.42
pthreads4w/3.0.0
libxml2/2.12.6
lua/5.4.6
libcurl/8.6.0
lmdb/0.9.31
libmaxminddb/1.9.1
dirent/1.24
poco/1.13.3

[generators]
CMakeDeps
CMakeToolchain
Loading

0 comments on commit 9c57c0c

Please sign in to comment.