diff --git a/.github/workflows/test-ci-windows.yml b/.github/workflows/test-ci-windows.yml new file mode 100644 index 000000000..fa27757d3 --- /dev/null +++ b/.github/workflows/test-ci-windows.yml @@ -0,0 +1,325 @@ +name: CI/CD for IIS Module + +on: + push: + pull_request: + +jobs: + build: + strategy: + matrix: + arch: [x64, x86] + config: [Release, RelWithDebInfo] + runs-on: windows-latest + + # For Caching + permissions: + actions: read + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Install Apache for x86 + if: matrix.arch == 'x86' + shell: pwsh + run: | + $apachePath = "${{ github.workspace }}\apache-x86" + New-Item -ItemType Directory -Path $apachePath -Force + choco install apache-httpd -y --force --forcex86 --no-progress -r --params="'/installLocation:$apachePath /noService'" + echo "APACHE_ROOT=$apachePath\Apache24" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Set Apache path for x64 + if: matrix.arch == 'x64' + shell: pwsh + run: | + echo "APACHE_ROOT=C:\tools\Apache24" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + + - name: Setup MSYS2 + uses: msys2/setup-msys2@fb197b72ce45fb24f17bf3f807a388985654d1f2 + with: + msystem: ${{ matrix.arch == 'x86' && 'MINGW32' || 'UCRT64' }} + update: true + install: > + git + make + autoconf + automake + libtool + ${{ matrix.arch == 'x86' && 'mingw-w64-i686-gcc' || 'mingw-w64-ucrt-x86_64-gcc' }} + ${{ matrix.arch == 'x86' && 'mingw-w64-i686-pkg-config' || 'mingw-w64-ucrt-x86_64-pkg-config' }} + + - name: Clone and build ssdeep + shell: msys2 {0} + run: | + MSYS2_WORKSPACE=$(cygpath -u '${{ github.workspace }}') + + git clone https://github.com/ssdeep-project/ssdeep.git --depth 1 + cd ssdeep + autoreconf -i + + if [ "${{ matrix.arch }}" = "x86" ]; then + ./configure --enable-shared --disable-static CFLAGS="-O3" CXXFLAGS="-O3" --build=i686-pc-mingw32 + else + ./configure --enable-shared --disable-static CFLAGS="-O3" CXXFLAGS="-O3" + fi + + make dll + + mkdir -p "${MSYS2_WORKSPACE}/ssdeep-install/" + cp -v fuzzy.dll "${MSYS2_WORKSPACE}/ssdeep-install/" + cp -v fuzzy.h "${MSYS2_WORKSPACE}/ssdeep-install/" + cp -v fuzzy.def "${MSYS2_WORKSPACE}/ssdeep-install/" + + - name: Restore vcpkg cache + id: vcpkg-cache + uses: TAServers/vcpkg-cache@e848939f754daf406a06006be2e05eb5b17cc481 + with: + token: ${{ secrets.GITHUB_TOKEN }} + prefix: vcpkg-iis-module-${{ matrix.arch }}/ + + - uses: ammaraskar/msvc-problem-matcher@1ebcb382869bfdc2cc645e8a2a43b6d319ea1cc0 + + - name: Configure CMake for IIS Module + env: + VCPKG_FEATURE_FLAGS: "binarycaching" + VCPKG_BINARY_SOURCES: "clear;files,${{ steps.vcpkg-cache.outputs.path }},readwrite" + VCPKG_DEFAULT_TRIPLET: ${{ matrix.arch }}-windows + run: | + $archFlag = "${{ matrix.arch }}" + $cmakeArch = if ($archFlag -eq "x86") { "Win32" } else { "x64" } + $installDir = if ($archFlag -eq "x86") { "x86" } else { "amd64" } + + cmake ` + -DAPACHE_ROOT="$env:APACHE_ROOT" ` + -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}\iis\release\$installDir" ` + -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT\scripts\buildsystems\vcpkg.cmake" ` + -DSSDEEP_ROOT="${{ github.workspace }}\ssdeep-install" ` + -DWITH_SSDEEP=ON ` + -A $cmakeArch ` + -DWITH_LUA=ON ` + -DWITH_YAJL=ON ` + -S IIS -B "iis\build" + + - name: Build IIS Module + shell: pwsh + run: | + cmake --build "iis\build" --config ${{ matrix.config }} + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: iis-module-${{ matrix.arch }}-${{ matrix.config }} + path: iis/build/${{ matrix.config }}/ + + package: + needs: build + runs-on: windows-latest + strategy: + matrix: + config: [Release, RelWithDebInfo] + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Download x64 artifacts + uses: actions/download-artifact@v4 + with: + name: iis-module-x64-${{ matrix.config }} + path: iis/release/amd64/ + + - name: Download x86 artifacts + uses: actions/download-artifact@v4 + with: + name: iis-module-x86-${{ matrix.config }} + path: iis/release/x86/ + + - name: Generate MSI files + shell: pwsh + run: | + heat dir "iis\release\amd64" -cg ModSec64Components -dr inetsrv64 -gg -sreg -srd -var var.ModSecurityIISRelease64 -out "iis\ModSec64.wxs" + heat dir "iis\release\x86" -cg ModSec32Components -dr inetsrv32 -gg -sreg -srd -var var.ModSecurityIISRelease32 -out "iis\ModSec32.wxs" + candle.exe -ext WixUtilExtension -ext WixUIExtension "iis\installer.wxs" "iis\ModSec64.wxs" -arch x64 -dModSecurityIISRelease64="iis\release\amd64\" -out iis\ + candle.exe -ext WixUtilExtension -ext WixUIExtension "iis\ModSec32.wxs" -arch x86 -dModSecurityIISRelease32="iis\release\x86\" -out iis\ + light.exe -ext WixUtilExtension -ext WixUIExtension "iis\installer.wixobj" "iis\ModSec32.wixobj" "iis\ModSec64.wixobj" -out "iis\modsecurityiis.msi" + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: modsecurityiis-installers-${{ matrix.config }} + path: iis/modsecurityiis.msi + + test: + needs: package + runs-on: windows-latest + strategy: + matrix: + config: [Release, RelWithDebInfo] + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Download MSI files + uses: actions/download-artifact@v4 + with: + name: modsecurityiis-installers-${{ matrix.config }} + path: ${{ github.workspace }}/ + + - name: Install MSI + shell: pwsh + run: | + $msiPath = "${{ github.workspace }}\modsecurityiis.msi" + if (-not (Test-Path $msiPath)) { + Write-Error "MSI file not found at $msiPath" + exit 1 + } + + # Install with logging for debugging + $installLog = "${{ github.workspace }}\install.log" + $installResult = Start-Process -FilePath "msiexec.exe" -ArgumentList @( + "/i", "`"$msiPath`"", + "/qn", + "/norestart", + "/l*", "`"$installLog`"" + ) -Wait -PassThru + + if ($installResult.ExitCode -ne 0) { + Write-Error "MSI installation failed with exit code $($installResult.ExitCode)" + Get-Content $installLog | Write-Host + exit 1 + } + + $installDir = "C:\Program Files\ModSecurity IIS" + $requiredFiles = @( + "modsecurity.conf", + "modsecurity_iis.conf" + ) + + foreach ($file in $requiredFiles) { + $filePath = Join-Path $installDir $file + if (-not (Test-Path $filePath)) { + Write-Error "Required file $file not found in installation directory" + exit 1 + } + } + + - name: Install OWASP Core Rules + shell: pwsh + run: | + $crsVersion = "v4.18.0" + $crsUrl = "https://github.com/coreruleset/coreruleset/archive/refs/tags/$crsVersion.tar.gz" + $crsDir = "C:\Program Files\ModSecurity IIS\coreruleset" + $modSecurityConfigDir = "C:\Program Files\ModSecurity IIS" + + try { + New-Item -ItemType Directory -Path $crsDir -Force + Invoke-WebRequest -Uri $crsUrl -OutFile "$crsDir\$crsVersion.tar.gz" + tar -xzf "$crsDir\$crsVersion.tar.gz" -C $crsDir --strip-components=1 + + Get-ChildItem "$crsDir" -Recurse -Filter "*.example" | ForEach-Object { + $newName = $_.Name.Replace(".example", "") + Rename-Item -Path $_.FullName -NewName $newName + } + + $modSecurityConfigFile = "$modSecurityConfigDir\modsecurity_iis.conf" + + $crsRules = @( + "Include coreruleset/crs-setup.conf", + "Include coreruleset/plugins/*-config.conf", + "Include coreruleset/plugins/*-before.conf", + "Include coreruleset/rules/*.conf", + "Include coreruleset/plugins/*-after.conf" + ) + + Add-Content -Path $modSecurityConfigFile -Value $crsRules + + (Get-Content -Path $modSecurityConfigDir\modsecurity.conf) -replace 'SecRuleEngine DetectionOnly', 'SecRuleEngine On' | Set-Content -Path $modSecurityConfigDir\modsecurity.conf + + } + catch { + Write-Error "Failed to install OWASP Core Rules: $($_.Exception.Message)" + exit 1 + } + + - name: Test IIS Module + shell: pwsh + run: | + $iisConfigDir = "C:\Program Files\ModSecurity IIS\" + + Restart-Service W3SVC -Force + + $modules = & "$env:SystemRoot\system32\inetsrv\appcmd.exe" list modules + Write-Host "IIS modules: $modules" + if ($LASTEXITCODE -ne 0) { + Write-Error "appcmd failed with exit code $LASTEXITCODE" + exit 1 + } + + if (-not ($modules -match "ModSecurity")) { + Write-Error "ModSecurity module not found in IIS modules" + Write-Host "IIS modules: $modules" + exit 1 + } + + $testCases = @( + @{Url = "http://localhost/"; Description = "Normal request"; ExpectedCode = 200}, + @{Url = "http://localhost/?id=1' OR '1'='1"; Description = "SQL injection attempt"; ExpectedCode = 403}, + @{Url = "http://localhost/?q="; Description = "XSS attempt"; ExpectedCode = 403} + ) + + foreach ($test in $testCases) { + try { + $response = Invoke-WebRequest $test.Url -UseBasicParsing -SkipHttpErrorCheck -TimeoutSec 30 + + if ($response.StatusCode -eq $test.ExpectedCode) { + Write-Host "PASS: $($test.Description) - returned $($response.StatusCode)" + } + else { + Write-Host "FAIL: $($test.Description) - expected $($test.ExpectedCode) but got $($response.StatusCode)" + } + } + catch { + Write-Host "ERROR: $($test.Description) - request failed: $($_.Exception.Message)" + } + } + + + # Check event log + $badMessagePattern = 'Failed to find the RegisterModule entrypoint|The description for Event ID|The data is the error|dll failed to load' + + $events = Get-EventLog -LogName Application -Newest 100 | + Where-Object { $_.Message -match $badMessagePattern } | + Where-Object { $_.Source -match 'IIS|W3SVC|mscor|IIS-W3SVC|IIS-W3WP|ModSecurity' } + + if ($events -and $events.Count -gt 0) { + Write-Host '::error:: Found errors in event log' + $events | Select-Object TimeGenerated, Source, EntryType, EventID, Message | Format-List + Exit 1 + } + + Get-EventLog -LogName Application -Source ModSecurity | Format-List + + - name: Install go-ftw + shell: pwsh + run: | + go install github.com/coreruleset/go-ftw@latest + + # Certain rules are disabled due to specific IIS behavior patterns. + # Using go-ftw in cloud mode as the IIS connector does not generate logs in file format. + # Technically, Event logs can be streamed to files, but this requires implementing rate limits to avoid log overflow. + - name: Test ModSecurity Rules + shell: pwsh + run: | + $testRuleDir = "C:\Program Files\ModSecurity IIS\coreruleset\tests\regression\tests" + $goBinPath = "" + if ($env:GOBIN) { + $goBinPath = $env:GOBIN + } elseif ($env:GOPATH) { + $goBinPath = Join-Path $env:GOPATH "bin" + } else { + $goBinPath = Join-Path $env:USERPROFILE "go\bin" + } + + & "$goBinPath\go-ftw.exe" run -d $testRuleDir --cloud -e "920100-2$|920100-4$|920100-8$|920100-12$|920272-5$|920290-1$|920620-1$|920380-1$" --show-failures-only + diff --git a/iis/.gitignore b/iis/.gitignore new file mode 100644 index 000000000..d16386367 --- /dev/null +++ b/iis/.gitignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/iis/CMakeLists.txt b/iis/CMakeLists.txt new file mode 100644 index 000000000..fe5e1d0f5 --- /dev/null +++ b/iis/CMakeLists.txt @@ -0,0 +1,348 @@ +cmake_minimum_required(VERSION 3.15) + +project(ModSecurityIIS C CXX) + +find_package(LibXml2 CONFIG REQUIRED) +find_package(PCRE2 CONFIG REQUIRED) +find_package(CURL CONFIG REQUIRED) +find_package(APR CONFIG REQUIRED) + +set(IIS_MODULE_NAME "modsecurityiis") + +set(IIS_APACHE_SOURCES + ../apache2/mod_security2.c + ../apache2/apache2_config.c + ../apache2/apache2_io.c + ../apache2/apache2_util.c + ../apache2/re.c + ../apache2/re_operators.c + ../apache2/re_actions.c + ../apache2/re_tfns.c + ../apache2/re_variables.c + ../apache2/msc_logging.c + ../apache2/msc_xml.c + ../apache2/msc_multipart.c + ../apache2/modsecurity.c + ../apache2/msc_parsers.c + ../apache2/msc_util.c + ../apache2/msc_pcre.c + ../apache2/persist_dbm.c + ../apache2/msc_reqbody.c + ../apache2/msc_geo.c + ../apache2/msc_gsb.c + ../apache2/msc_crypt.c + ../apache2/msc_tree.c + ../apache2/msc_unicode.c + ../apache2/acmp.c + ../apache2/msc_lua.c + ../apache2/msc_release.c + ../apache2/msc_status_engine.c + ../apache2/msc_remote_rules.c + ../apache2/msc_json.c + ../apache2/libinjection/libinjection_html5.c + ../apache2/libinjection/libinjection_sqli.c + ../apache2/libinjection/libinjection_xss.c +) + +set(IIS_STANDALONE_SOURCES + ../standalone/api.c + ../standalone/buckets.c + ../standalone/config.c + ../standalone/filters.c + ../standalone/hooks.c + ../standalone/regex.c + ../standalone/server.c +) + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(ARCHITECTURE "x64") +else() + set(ARCHITECTURE "x86") +endif() + +set(IIS_RESOURCE_MC "${CMAKE_CURRENT_SOURCE_DIR}/ModSecurityIISMessage.mc") + +set(MC_GENERATED_RC "${CMAKE_CURRENT_BINARY_DIR}/ModSecurityIISMessage.rc") +set(MC_GENERATED_H "${CMAKE_CURRENT_BINARY_DIR}/ModSecurityIISMessage.h") +add_custom_command( + OUTPUT ${MC_GENERATED_RC} ${MC_GENERATED_H} + COMMAND mc.exe + ARGS -U -h "${CMAKE_CURRENT_BINARY_DIR}/" -r "${CMAKE_CURRENT_BINARY_DIR}/" "${IIS_RESOURCE_MC}" + DEPENDS "${IIS_RESOURCE_MC}" + COMMENT "Generating resource files from ${IIS_RESOURCE_MC}" + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) + +# Source files for IIS-specific components +set(IIS_MODULE_SOURCES + main.cpp + moduleconfig.cpp + mymodule.cpp + mymodule.def + ${MC_GENERATED_RC} +) + +set_source_files_properties( + ${MC_GENERATED_RC} + ${MC_GENERATED_H} + PROPERTIES GENERATED TRUE +) + +add_library(${IIS_MODULE_NAME} SHARED + ${IIS_APACHE_SOURCES} + ${IIS_STANDALONE_SOURCES} + ${IIS_MODULE_SOURCES} +) + +# Set the output name and extension +set_target_properties(${IIS_MODULE_NAME} PROPERTIES + OUTPUT_NAME ${IIS_MODULE_NAME} + PREFIX "" + SUFFIX ".dll" +) + +target_include_directories(${IIS_MODULE_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_CURRENT_SOURCE_DIR}/../apache2 + ${CMAKE_CURRENT_SOURCE_DIR}/../apache2/libinjection + ${LIBXML2_INCLUDE_DIR}/libxml + ${CMAKE_CURRENT_SOURCE_DIR}/../standalone + ${PCRE2_INCLUDE_DIRS} + ${CURL_INCLUDE_DIRS} + ${APR_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR} +) + +if(APACHE_ROOT) + if(NOT EXISTS "${APACHE_ROOT}") + message(FATAL_ERROR "APACHE_ROOT is defined but the directory '${APACHE_ROOT}' does not exist. Please set APACHE_ROOT to a valid Apache installation directory.") + endif() + if(NOT EXISTS "${APACHE_ROOT}/lib") + message(FATAL_ERROR "APACHE_ROOT/lib directory does not exist. Expected: '${APACHE_ROOT}/lib'. Please ensure Apache libraries are available.") + endif() + + file(TO_CMAKE_PATH "${APACHE_ROOT}" APACHE_ROOT) + + # Create imported targets for Apache libraries + add_library(Apache::httpd SHARED IMPORTED) + set_target_properties(Apache::httpd PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${APACHE_ROOT}/include" + IMPORTED_IMPLIB "${APACHE_ROOT}/lib/libhttpd.lib" + IMPORTED_LOCATION "${APACHE_ROOT}/bin/libhttpd.dll" + ) + + add_library(Apache::apr SHARED IMPORTED) + set_target_properties(Apache::apr PROPERTIES + IMPORTED_IMPLIB "${APACHE_ROOT}/lib/libapr-1.lib" + IMPORTED_LOCATION "${APACHE_ROOT}/bin/libapr-1.dll" + ) + + add_library(Apache::aprutil SHARED IMPORTED) + set_target_properties(Apache::aprutil PROPERTIES + IMPORTED_IMPLIB "${APACHE_ROOT}/lib/libaprutil-1.lib" + IMPORTED_LOCATION "${APACHE_ROOT}/bin/libaprutil-1.dll" + ) + + add_library(Apache::apriconv SHARED IMPORTED) + set_target_properties(Apache::apriconv PROPERTIES + IMPORTED_IMPLIB "${APACHE_ROOT}/lib/libapriconv-1.lib" + IMPORTED_LOCATION "${APACHE_ROOT}/bin/libapriconv-1.dll" + ) + + target_include_directories(${IIS_MODULE_NAME} PRIVATE + ${APACHE_ROOT}/include + ) +endif() + +set(MODSECURITY_VERSION_FLAG "VERSION_IIS") # Define the version flag string +target_compile_definitions(${IIS_MODULE_NAME} PRIVATE + inline=APR_INLINE + AP_DECLARE_STATIC + WITH_CURL + WITH_REMOTE_RULES + MSC_LARGE_STREAM_INPUT + WITH_YAJL + ${MODSECURITY_VERSION_FLAG} +) + +option(WITH_LUA "Enable Lua support" OFF) +if(WITH_LUA) + find_package(Lua CONFIG REQUIRED) + target_compile_definitions(${IIS_MODULE_NAME} PRIVATE WITH_LUA) + target_include_directories(${IIS_MODULE_NAME} PRIVATE ${LUA_INCLUDE_DIR}) +endif() + +option(WITH_YAJL "Enable YAJL support" OFF) +if(WITH_YAJL) + # Manually find YAJL if config.cmake is not available (e.g., from vcpkg) + find_path(YAJL_INCLUDE_DIR yajl/yajl_common.h + PATHS "${CMAKE_CURRENT_SOURCE_DIR}/build/vcpkg_installed/${ARCHITECTURE}-windows/include" + NO_DEFAULT_PATH + ) + find_library(YAJL_LIBRARY NAMES yajl + PATHS "${CMAKE_CURRENT_SOURCE_DIR}/build/vcpkg_installed/${ARCHITECTURE}-windows/lib" + NO_DEFAULT_PATH + ) + + if(YAJL_INCLUDE_DIR AND YAJL_LIBRARY) + set(YAJL_INCLUDE_DIRS ${YAJL_INCLUDE_DIR}) + set(YAJL_LIBRARIES ${YAJL_LIBRARY}) + target_compile_definitions(${IIS_MODULE_NAME} PRIVATE WITH_YAJL) + target_include_directories(${IIS_MODULE_NAME} PRIVATE ${YAJL_INCLUDE_DIRS}) + else() + message(WARNING "YAJL not found. YAJL_INCLUDE_DIR: '${YAJL_INCLUDE_DIR}', YAJL_LIBRARY: '${YAJL_LIBRARY}'. Please ensure yajl is installed via vcpkg in the vcpkg_installed directory. Disabling YAJL support.") + option(WITH_YAJL "Enable YAJL support" OFF) + endif() +endif() + +option(WITH_SSDEEP "Enable SSDEEP support" OFF) +if(WITH_SSDEEP) + + if(NOT EXISTS "${SSDEEP_ROOT}") + message(WARNING "SSDEEP_ROOT is not defined or path does not exist. Current SSDEEP_ROOT: '${SSDEEP_ROOT}'. Please set SSDEEP_ROOT to the ssdeep installation directory. Disabling SSDEEP support.") + set(WITH_SSDEEP OFF CACHE BOOL "Enable SSDEEP support" FORCE) + else() + + file(TO_CMAKE_PATH "${SSDEEP_ROOT}" SSDEEP_ROOT) + + find_path(SSDEEP_INCLUDE_DIR fuzzy.h + PATHS "${SSDEEP_ROOT}" + NO_DEFAULT_PATH + ) + + if(SSDEEP_INCLUDE_DIR) + target_compile_definitions(${IIS_MODULE_NAME} PRIVATE WITH_SSDEEP) + target_include_directories(${IIS_MODULE_NAME} PRIVATE ${SSDEEP_INCLUDE_DIR}) + + set(SSDEEP_DEF_FILE "${SSDEEP_ROOT}/fuzzy.def") + if(NOT EXISTS "${SSDEEP_DEF_FILE}") + message(WARNING "fuzzy.def not found at ${SSDEEP_DEF_FILE}. Disabling SSDEEP support.") + set(WITH_SSDEEP OFF CACHE BOOL "Enable SSDEEP support" FORCE) + else() + set(SSDEEP_GENERATED_LIB "${CMAKE_CURRENT_BINARY_DIR}/fuzzy.lib") + set(SSDEEP_GENERATED_dll "${CMAKE_CURRENT_BINARY_DIR}/fuzzy.dll") + + add_custom_command( + OUTPUT ${SSDEEP_GENERATED_LIB} + COMMAND lib.exe /machine:${ARCHITECTURE} /def:${SSDEEP_DEF_FILE} /out:${SSDEEP_GENERATED_LIB} + DEPENDS "${SSDEEP_DEF_FILE}" + COMMENT "Generating SSDEEP .lib from .def for MSVC" + VERBATIM + ) + + set_source_files_properties(${SSDEEP_GENERATED_LIB} PROPERTIES GENERATED TRUE) + + add_custom_target(generate_ssdeep_lib ALL + DEPENDS ${SSDEEP_GENERATED_LIB} + COMMENT "Ensuring ssdeep lib is generated" + ) + + add_dependencies(${IIS_MODULE_NAME} generate_ssdeep_lib) + + add_library(SSDEEP::fuzzy SHARED IMPORTED) + set_target_properties(SSDEEP::fuzzy PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${SSDEEP_INCLUDE_DIR}" + IMPORTED_LOCATION "${SSDEEP_GENERATED_dll}" + IMPORTED_IMPLIB "${SSDEEP_GENERATED_LIB}" + ) + + endif() + else() + message(WARNING "fuzzy.h not found at ${SSDEEP_INCLUDE_DIR}. Disabling SSDEEP support.") + set(WITH_SSDEEP OFF CACHE BOOL "Enable SSDEEP support" FORCE) + endif() + endif() +endif() + +if(MSVC) + target_compile_options(${IIS_MODULE_NAME} PRIVATE + /nologo + /W3 + /wd4244 + /wd4018 + ) +endif() + +target_link_libraries(${IIS_MODULE_NAME} PRIVATE + LibXml2::LibXml2 + PCRE2::8BIT + CURL::libcurl + ws2_32 + iphlpapi +) + +if(APACHE_ROOT) + target_link_libraries(${IIS_MODULE_NAME} PRIVATE + Apache::httpd + Apache::apr + Apache::aprutil + Apache::apriconv + ) +else() + message(WARNING "APACHE_ROOT is not defined or path does not exist. Current APACHE_ROOT: '${APACHE_ROOT}'. Please set APACHE_ROOT to the Apache installation directory.") +endif() + +if(WITH_LUA) + target_link_libraries(${IIS_MODULE_NAME} PRIVATE ${LUA_LIBRARIES}) +endif() + +if(WITH_YAJL) + target_link_libraries(${IIS_MODULE_NAME} PRIVATE ${YAJL_LIBRARIES}) +endif() + +if(WITH_SSDEEP AND SSDEEP_INCLUDE_DIR AND SSDEEP_GENERATED_LIB) + target_link_libraries(${IIS_MODULE_NAME} PRIVATE SSDEEP::fuzzy) +endif() + +if(APACHE_ROOT AND EXISTS "${APACHE_ROOT}/bin") + add_custom_command(TARGET ${IIS_MODULE_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${APACHE_ROOT}/bin/libhttpd.dll" + $ + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${APACHE_ROOT}/bin/libaprutil-1.dll" + $ + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${APACHE_ROOT}/bin/libapriconv-1.dll" + $ + COMMENT "Copying Apache DLLs to output directory" + ) +else() + message(WARNING "APACHE_ROOT is not defined or path does not exist. Current APACHE_ROOT: '${APACHE_ROOT}'. Please set APACHE_ROOT to the Apache installation directory.") +endif() + +if(WITH_SSDEEP AND SSDEEP_ROOT AND EXISTS "${SSDEEP_ROOT}/fuzzy.dll") + add_custom_command(TARGET ${IIS_MODULE_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${SSDEEP_ROOT}/fuzzy.dll" + $ + COMMENT "Copying SSDEEP DLL to output directory" + ) +endif() + + +# Install target - copy to release files directory +install(TARGETS ${IIS_MODULE_NAME} + RUNTIME DESTINATION . + LIBRARY DESTINATION . +) + +if(APACHE_ROOT AND EXISTS "${APACHE_ROOT}/bin") + install(FILES + "${APACHE_ROOT}/bin/libhttpd.dll" + "${APACHE_ROOT}/bin/libaprutil-1.dll" + "${APACHE_ROOT}/bin/libapriconv-1.dll" + DESTINATION . + ) +endif() + +if(WITH_SSDEEP AND SSDEEP_ROOT AND EXISTS "${SSDEEP_ROOT}/fuzzy.dll") + install(FILES + "${SSDEEP_ROOT}/fuzzy.dll" + DESTINATION . + ) +endif() + +# Also install the PDB file if it's generated +install(FILES $ DESTINATION . OPTIONAL) \ No newline at end of file diff --git a/iis/installer.wxs b/iis/installer.wxs index 9197a733b..3f1744332 100644 --- a/iis/installer.wxs +++ b/iis/installer.wxs @@ -17,17 +17,19 @@ - + - + + + @@ -87,24 +89,28 @@ + + + - - VersionNT64 - - NOT VersionNT64 - - - - - + + + VersionNT64 + + NOT VersionNT64 + + + + + @@ -121,7 +127,7 @@ - + @@ -129,437 +135,112 @@ - + - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + - + + - + (NOT &ModSec64=3) AND (NOT &ModSec32=3) &ModSec64=3 OR &ModSec32=3 - + (NOT &ModSec32=3) &ModSec32=3 - + 1 @@ -649,9 +330,9 @@ &ModSec64=3 OR &ModSec32=3 (NOT &ModSec64=3) AND (NOT &ModSec32=3) - + &ModSec32=3 - + NOT Installed OR WixUI_InstallMode = "Change" NOT Installed OR WixUI_InstallMode = "Change" Installed AND PATCH @@ -686,12 +367,12 @@ - + - + @@ -701,45 +382,45 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/iis/mymodule.cpp b/iis/mymodule.cpp index e9d5ce376..dfaee4b2c 100644 --- a/iis/mymodule.cpp +++ b/iis/mymodule.cpp @@ -17,6 +17,8 @@ #undef inline #define inline inline +#include "winsock2.h" + // IIS7 Server API header file #include #include @@ -30,8 +32,6 @@ #include "api.h" #include "moduleconfig.h" -#include "winsock2.h" - class REQUEST_STORED_CONTEXT : public IHttpStoredContext { @@ -90,63 +90,66 @@ class REQUEST_STORED_CONTEXT : public IHttpStoredContext char *GetIpAddr(apr_pool_t *pool, PSOCKADDR pAddr) { - const char *format = "%15[0-9.]:%5[0-9]"; - char ip[16] = { 0 }; // ip4 addresses have max len 15 - char port[6] = { 0 }; // port numbers are 16bit, ie 5 digits max - - DWORD len = 50; - char *buf = (char *)apr_palloc(pool, len); - - if(buf == NULL) - return ""; - - buf[0] = 0; - - WSAAddressToString(pAddr, sizeof(SOCKADDR), NULL, buf, &len); - - // test for IPV4 with port on the end - if (sscanf(buf, format, ip, port) == 2) { - // IPV4 but with port - remove the port - char* input = ":"; - char* ipv4 = strtok(buf, input); - return ipv4; - } - - return buf; + if (pAddr == nullptr) { + return apr_pstrdup(pool, ""); + } + + DWORD addrSize = pAddr->sa_family == AF_INET ? sizeof(SOCKADDR_IN) : sizeof(SOCKADDR_IN6); + auto buf = (char*)apr_palloc(pool, NI_MAXHOST); + if (buf == nullptr) { + return apr_pstrdup(pool, ""); + } + buf[0] = '\0'; + + if (GetNameInfo(pAddr, addrSize, buf, NI_MAXHOST, nullptr, 0, NI_NUMERICHOST) != 0) { + return apr_pstrdup(pool, ""); + } + + return buf; } apr_sockaddr_t *CopySockAddr(apr_pool_t *pool, PSOCKADDR pAddr) { - apr_sockaddr_t *addr = (apr_sockaddr_t *)apr_palloc(pool, sizeof(apr_sockaddr_t)); - int adrlen = 16, iplen = 4; + apr_sockaddr_t *addr = (apr_sockaddr_t *)apr_palloc(pool, sizeof(apr_sockaddr_t)); - if(pAddr->sa_family == AF_INET6) - { - adrlen = 46; - iplen = 16; + addr->pool = pool; + addr->hostname = "unknown"; + addr->servname = addr->hostname; + addr->family = AF_UNSPEC; + addr->addr_str_len = 0; + addr->ipaddr_len = 0; + addr->ipaddr_ptr = nullptr; + addr->salen = 0; + addr->port = 0; + + if (pAddr == nullptr) { + return addr; } - addr->addr_str_len = adrlen; addr->family = pAddr->sa_family; - addr->hostname = "unknown"; -#ifdef WIN32 - addr->ipaddr_len = sizeof(IN_ADDR); -#else - addr->ipaddr_len = sizeof(struct in_addr); -#endif - addr->ipaddr_ptr = &addr->sa.sin.sin_addr; - addr->pool = pool; - addr->port = 80; -#ifdef WIN32 - memcpy(&addr->sa.sin.sin_addr.S_un.S_addr, pAddr->sa_data, iplen); -#else - memcpy(&addr->sa.sin.sin_addr.s_addr, pAddr->sa_data, iplen); -#endif - addr->sa.sin.sin_family = pAddr->sa_family; - addr->sa.sin.sin_port = 80; - addr->salen = sizeof(addr->sa); - addr->servname = addr->hostname; + if (pAddr->sa_family == AF_INET) { + auto sin = (SOCKADDR_IN *)pAddr; + addr->addr_str_len = INET_ADDRSTRLEN; + addr->ipaddr_len = sizeof(struct in_addr); + addr->ipaddr_ptr = &addr->sa.sin.sin_addr; + addr->sa.sin.sin_family = AF_INET; + addr->sa.sin.sin_port = sin->sin_port; /* keep network byte order */ + /* copy address */ + memcpy(&addr->sa.sin.sin_addr, &sin->sin_addr, sizeof(struct in_addr)); + addr->salen = sizeof(addr->sa); + addr->port = ntohs(sin->sin_port); + } else if (pAddr->sa_family == AF_INET6) { + auto sin6 = (SOCKADDR_IN6 *)pAddr; + addr->addr_str_len = INET6_ADDRSTRLEN; + addr->ipaddr_len = sizeof(struct in6_addr); + addr->ipaddr_ptr = &addr->sa.sin6.sin6_addr; + addr->sa.sin6.sin6_family = AF_INET6; + addr->sa.sin6.sin6_port = sin6->sin6_port; + memcpy(&addr->sa.sin6.sin6_addr, &sin6->sin6_addr, sizeof(struct in6_addr)); + addr->salen = sizeof(addr->sa); + addr->port = ntohs(sin6->sin6_port); + } return addr; } diff --git a/iis/vcpkg.json b/iis/vcpkg.json new file mode 100644 index 000000000..3abb499b3 --- /dev/null +++ b/iis/vcpkg.json @@ -0,0 +1,10 @@ +{ + "dependencies": [ + "curl", + "libxml2", + "lua", + "pcre2", + "yajl", + "apr" + ] +} \ No newline at end of file