Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to build libModSecurity v3 on Windows #3132

Merged
merged 20 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a488568
Updated included headers to support compilation on Windows (using Vis…
eduar-hte Apr 23, 2024
942c8ba
Replaced usage of usleep (not available in Visual C++) with C++11's s…
eduar-hte Apr 23, 2024
abbd7b2
Replaced usage of apr_snprintf with snprintf (already in Windows excl…
eduar-hte Apr 23, 2024
3594917
setenv is not available in Windows build, replaced with _putenv_s
eduar-hte Apr 23, 2024
373633f
mkstemp is not available in Windows build, replaced with _mktemp_s pl…
eduar-hte Apr 24, 2024
91a7366
Minor changes to debug_log_writer
eduar-hte Apr 23, 2024
ebf1f8f
On Windows use the operating system's native CA store for certificate…
eduar-hte Apr 23, 2024
10c6ee7
Added support for expandEnv, createDir & cpu_seconds on Windows
eduar-hte Apr 23, 2024
fef419f
Minor changes related to std::shared_ptr usage in RuleWithActions
eduar-hte Apr 23, 2024
7bff2f7
Updated references to coreruleset repository
eduar-hte Apr 23, 2024
50c3534
Fixed use after free in ModSecurity::processContentOffset
eduar-hte Apr 23, 2024
50e7833
Updated Env::evaluate to support case-insensitive environment variabl…
eduar-hte Apr 23, 2024
d7c49ed
Added support to lock files on Windows and major rewrite to reintrodu…
eduar-hte Apr 23, 2024
faae58e
Added Windows build scripts using Build Tools for Visual Studio 2022 …
eduar-hte Apr 24, 2024
411bbb2
Updated case of winsock header files
eduar-hte Apr 26, 2024
b69405a
Use default keyword to implement constructor/destructor
eduar-hte Apr 26, 2024
a8e132f
Replaced the use of "new" in find_resource
eduar-hte Apr 26, 2024
e6e2989
Configure test fixture using CTest for Windows build
eduar-hte May 11, 2024
6bf78f2
Added GitHub workflow to build libModSecurity on Windows.
eduar-hte May 13, 2024
1b2de5a
Add support to turn 3rd party dependencies off
eduar-hte May 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
52 changes: 52 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,55 @@ jobs:
run: make -j `sysctl -n hw.logicalcpu`
- name: check
run: make check

build-windows:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-2022]
platform: [x86_64]
configuration: [Release]
configure:
- {label: "full", opt: "" }
- {label: "wo lmdb", opt: "-DWITHOUT_LMDB=ON" }
- {label: "wo lua", opt: "-DWITHOUT_LUA=ON" }
- {label: "wo maxmind", opt: "-DWITHOUT_MAXMIND=ON" }
- {label: "wo curl", opt: "-DWITHOUT_CURL=ON" }
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Install Conan
run: |
pip3 install conan --upgrade
conan profile detect
- uses: ammaraskar/msvc-problem-matcher@master
- name: Build ${{ matrix.configuration }} ${{ matrix.platform }} ${{ matrix.configure.label }}
shell: cmd
run: vcbuild.bat ${{ matrix.configuration }} ${{ matrix.platform }} NO_ASAN "${{ matrix.configure.opt }}"
- name: Set up test environment
working-directory: build\win32\build\${{ matrix.configuration }}
env:
BASE_DIR: ..\..\..\..
shell: cmd
run: |
copy unit_tests.exe %BASE_DIR%\test
copy regression_tests.exe %BASE_DIR%\test
copy libModSecurity.dll %BASE_DIR%\test
copy %BASE_DIR%\unicode.mapping %BASE_DIR%\test
md \tmp
md \bin
copy "C:\Program Files\Git\usr\bin\echo.exe" \bin
copy "C:\Program Files\Git\usr\bin\echo.exe" \bin\echo
- name: Disable tests that don't work on Windows
working-directory: test\test-cases\regression
shell: cmd
run: |
jq "map(if .title == \"Test match variable (1/n)\" then .enabled = 0 else . end)" issue-2423-msg-in-chain.json > tmp.json && move /Y tmp.json issue-2423-msg-in-chain.json
jq "map(if .title == \"Test match variable (2/n)\" then .enabled = 0 else . end)" issue-2423-msg-in-chain.json > tmp.json && move /Y tmp.json issue-2423-msg-in-chain.json
jq "map(if .title == \"Test match variable (3/n)\" then .enabled = 0 else . end)" issue-2423-msg-in-chain.json > tmp.json && move /Y tmp.json issue-2423-msg-in-chain.json
jq "map(if .title == \"Variable offset - FILES_NAMES\" then .enabled = 0 else . end)" offset-variable.json > tmp.json && move /Y tmp.json offset-variable.json
- name: Run tests
working-directory: build\win32\build
run: |
ctest -C ${{ matrix.configuration }} --output-on-failure
257 changes: 1 addition & 256 deletions Makefile.am

Large diffs are not rendered by default.

244 changes: 244 additions & 0 deletions build/win32/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
cmake_minimum_required(VERSION 3.24)

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

option(WITHOUT_LMDB "Include LMDB support" OFF)
option(WITHOUT_LUA "Include LUA support" OFF)
option(WITHOUT_LIBXML2 "Include LibXML2 support" OFF)
option(WITHOUT_MAXMIND "Include MaxMind support" OFF)
option(WITHOUT_CURL "Include CURL support" OFF)

option(USE_ASAN "Build with Address Sanitizer" OFF)

# 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_YAJL 1) # should always be one, mandatory dependency
set(HAVE_GEOIP 0) # should always be zero, no conan package available
set(HAVE_SSDEEP 0) # should always be zero, no conan package available

macro(enable_feature flag option)
if(${option})
set(${flag} 0)
else()
set(${flag} 1)
endif()
endmacro()

enable_feature(HAVE_LMDB ${WITHOUT_LMDB})
enable_feature(HAVE_LUA ${WITHOUT_LUA})
enable_feature(HAVE_LIBXML2 ${WITHOUT_LIBXML2})
enable_feature(HAVE_MAXMIND ${WITHOUT_MAXMIND})
enable_feature(HAVE_CURL ${WITHOUT_CURL})

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)

enable_testing()

file(READ ${BASE_DIR}/test/test-suite.in TEST_FILES_RAW)
string(REPLACE "\n" ";" TEST_FILES ${TEST_FILES_RAW})

foreach(TEST_FILE ${TEST_FILES})
# ignore comment lines
string(FIND ${TEST_FILE} "#" is_comment)
if(NOT is_comment EQUAL 0)
string(FIND ${TEST_FILE} "TESTS+=" is_valid_prefix)
if(NOT is_valid_prefix EQUAL 0)
message(FATAL_ERROR "Invalid prefix in line: ${TEST_FILE}")
endif()

# remove 'TESTS+=' prefix and 'test/' too because tests are launched
# from that directory
string(SUBSTRING ${TEST_FILE} 12 -1 TEST_FILE)

# test name
get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE)

# determine test runner based on test path prefix
string(FIND ${TEST_FILE} "test-cases/regression/" is_regression_test)
if(is_regression_test EQUAL 0)
set(TEST_RUNNER "regression_tests")
else()
set(TEST_RUNNER "unit_tests")
endif()

add_test(NAME ${TEST_NAME} COMMAND ${TEST_RUNNER} ${TEST_FILE} WORKING_DIRECTORY ${BASE_DIR}/test)
endif()
endforeach()

# 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)