Skip to content

Commit

Permalink
Merge pull request #3132 from eduar-hte/windows-port
Browse files Browse the repository at this point in the history
Add support to build libModSecurity v3 on Windows
  • Loading branch information
airween authored May 15, 2024
2 parents c805648 + 1b2de5a commit 71a786b
Show file tree
Hide file tree
Showing 64 changed files with 1,333 additions and 662 deletions.
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)
Loading

0 comments on commit 71a786b

Please sign in to comment.