diff --git a/.github/ISSUE_TEMPLATE/bug-report-for-version-2-x.md b/.github/ISSUE_TEMPLATE/bug-report-for-version-2-x.md new file mode 100644 index 0000000000..b39db8633f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report-for-version-2-x.md @@ -0,0 +1,46 @@ +--- +name: Bug report for version 2.x +about: Create a report to help us improve + +--- + +**Describe the bug** + +A clear and concise description of what the bug is. + +**Logs and dumps** + +Output of: + 1. DebugLogs (level 9) + 2. AuditLogs + 3. Error logs + 4. If there is a crash, the core dump file. + +_Notice:_ Be carefully to not leak any confidential information. + +**To Reproduce** + +Steps to reproduce the behavior: + +A **curl** command line that mimics the original request and reproduces the problem. Or a ModSecurity v3 test case. + +[e.g: curl "modsec-full/ca/..\\..\\..\\..\\..\\..\\/\\etc/\\passwd" or [issue-394.json](https://github.com/SpiderLabs/ModSecurity/blob/v3/master/test/test-cases/regression/issue-394.json)] + + +**Expected behavior** + +A clear and concise description of what you expected to happen. + +**Server (please complete the following information):** + - ModSecurity version (and connector): [e.g. ModSecurity v3.0.1 with nginx-connector v1.0.0] + - WebServer: [e.g. nginx-1.15.5] + - OS (and distro): [e.g. Linux, archlinux] + + +**Rule Set (please complete the following information):** + - Running any public or commercial rule set? [e.g. SpiderLabs commercial rules] + - What is the version number? [e.g. 2018-08-11] + +**Additional context** + +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/bug-report-for-version-3-x.md b/.github/ISSUE_TEMPLATE/bug-report-for-version-3-x.md new file mode 100644 index 0000000000..58874dfb42 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report-for-version-3-x.md @@ -0,0 +1,47 @@ +--- +name: Bug report for version 3.x +about: Create a report to help us improve. If you don't know a specific detail or + piece of information leave it blank, if necessary we will help you to figure out. + +--- + +**Describe the bug** + +A clear and concise description of what the bug is. + +**Logs and dumps** + +Output of: + 1. DebugLogs (level 9) + 2. AuditLogs + 3. Error logs + 4. If there is a crash, the core dump file. + +_Notice:_ Be carefully to not leak any confidential information. + +**To Reproduce** + +Steps to reproduce the behavior: + +A **curl** command line that mimics the original request and reproduces the problem. Or a ModSecurity v3 test case. + +[e.g: curl "modsec-full/ca/..\\..\\..\\..\\..\\..\\/\\etc/\\passwd" or [issue-394.json](https://github.com/SpiderLabs/ModSecurity/blob/v3/master/test/test-cases/regression/issue-394.json)] + + +**Expected behavior** + +A clear and concise description of what you expected to happen. + +**Server (please complete the following information):** + - ModSecurity version (and connector): [e.g. ModSecurity v3.0.1 with nginx-connector v1.0.0] + - WebServer: [e.g. nginx-1.15.5] + - OS (and distro): [e.g. Linux, archlinux] + + +**Rule Set (please complete the following information):** + - Running any public or commercial rule set? [e.g. SpiderLabs commercial rules] + - What is the version number? [e.g. 2018-08-11] + +**Additional context** + +Add any other context about the problem here. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..4d274644a4 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,24 @@ + + + +## what + + + +## why + + + +## references + + diff --git a/.github/security2.conf b/.github/security2.conf new file mode 100644 index 0000000000..d9051b007c --- /dev/null +++ b/.github/security2.conf @@ -0,0 +1,8 @@ +LoadModule security2_module /usr/lib/apache2/modules/mod_security2.so + + + SecDataDir /var/cache/modsecurity + Include /etc/apache2/modsecurity.conf + + +SecAuditLog /var/log/apache2/modsec_audit.log diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..8ba565f099 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,112 @@ +name: Quality Assurance + +on: + push: + pull_request: + +jobs: + build-linux: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-22.04] + platform: [x32, x64] + compiler: [gcc, clang] + configure: + - {label: "with pcre2, no study, no jit", opt: "--enable-pcre-study=no" } + - {label: "with pcre2, with study, no jit", opt: "--enable-pcre-study=yes" } + - {label: "with pcre2, no study, with jit", opt: "--enable-pcre-study=no --enable-pcre-jit" } + - {label: "with pcre2, with study, with jit", opt: "--enable-pcre-study=yes --enable-pcre-jit" } + - {label: "with pcre", opt: "--with-pcre --enable-pcre-study=no" } + - {label: "with pcre, with study, no jit", opt: "--with-pcre --enable-pcre-study=yes" } + - {label: "with pcre, no study, with jit", opt: "--with-pcre --enable-pcre-study=no --enable-pcre-jit" } + - {label: "with pcre, with study, with jit", opt: "--with-pcre --enable-pcre-study=yes --enable-pcre-jit" } + - {label: "with lua", opt: "--with-lua" } + - {label: "wo lua", opt: "--without-lua" } + steps: + - name: Setup Dependencies + run: | + sudo apt-get update -y -qq + sudo apt-get install -y apache2-dev libxml2-dev liblua5.1-0-dev libcurl4-gnutls-dev libpcre2-dev pkg-config libyajl-dev apache2 apache2-bin apache2-data + - uses: actions/checkout@v2 + - name: autogen.sh + run: ./autogen.sh + - name: configure ${{ matrix.configure.label }} + run: ./configure --enable-assertions ${{ matrix.configure.opt }} 'CFLAGS=-Werror=format-security' + - uses: ammaraskar/gcc-problem-matcher@master + - name: make + run: make -j `nproc` + - name: install module + run: sudo make install + - name: prepare config + run: | + sudo cp .github/security2.conf /etc/apache2/mods-enabled/ + sudo cp modsecurity.conf-recommended /etc/apache2/modsecurity.conf + sudo cp unicode.mapping /etc/apache2/ + sudo mkdir -p /var/cache/modsecurity + sudo chown -R www-data:www-data /var/cache/modsecurity + - name: first check config (to get syntax errors) + run: sudo apachectl configtest + - name: start apache with module + run: sudo systemctl restart apache2.service + - name: Search for errors/warnings in error log + run: | + # '|| :' handles the case grep doesn't match, otherwise the script exits with 1 (error) + errors=$(grep -E ':(?error|warn)[]]' /var/log/apache2/error.log) || : + if [[ -z "${errors}" ]]; then exit 0; fi + echo "::error:: Found errors/warnings in error.log" + echo "${errors}" + exit 1 + - name: Check error.log + run: | + # Send requests & check log format + # Valid request + curl -s http://127.0.01/ > /dev/null || echo $? + # Invalid request + curl -s http://127.0.01/%2e%2f > /dev/null || echo $? + # Check log format + grep -F ModSecurity < /var/log/apache2/error.log | grep -vP "^\[[^\]]+\] \[security2:[a-z]+\] \[pid [0-9]+:tid [0-9]+\] (?:\[client [0-9.:]+\] )?ModSecurity" || exit 0 + # grep -v succeeded => found some lines with invalid format + exit 1 + - name: Show httpd error log + if: always() + run: sudo cat /var/log/apache2/error.log + - name: Show mod_security2 audit log + if: always() + run: sudo cat /var/log/apache2/modsec_audit.log + + test-linux: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-22.04] + platform: [x32, x64] + compiler: [gcc, clang] + configure: + - {label: "with pcre2, no study, no jit", opt: "--enable-pcre-study=no" } + - {label: "with pcre2, with study, no jit", opt: "--enable-pcre-study=yes" } + - {label: "with pcre2, no study, with jit", opt: "--enable-pcre-study=no --enable-pcre-jit" } + - {label: "with pcre2, with study, with jit", opt: "--enable-pcre-study=yes --enable-pcre-jit" } + - {label: "with pcre", opt: "--with-pcre --enable-pcre-study=no" } + - {label: "with pcre, with study, no jit", opt: "--with-pcre --enable-pcre-study=yes" } + - {label: "with pcre, no study, with jit", opt: "--with-pcre --enable-pcre-study=no --enable-pcre-jit" } + - {label: "with pcre, with study, with jit", opt: "--with-pcre --enable-pcre-study=yes --enable-pcre-jit" } + - {label: "with lua", opt: "--with-lua" } + - {label: "wo lua", opt: "--without-lua" } + steps: + - name: Setup Dependencies + run: | + sudo apt-get update -y -qq + sudo apt-get install -y --no-install-recommends apache2-dev libxml2-dev liblua5.1-0-dev libcurl4-gnutls-dev libpcre2-dev pkg-config libyajl-dev apache2 apache2-bin apache2-data + - uses: actions/checkout@v2 + - name: autogen.sh + run: ./autogen.sh + - name: configure ${{ matrix.configure.label }} + run: ./configure ${{ matrix.configure.opt }} 'CFLAGS=-Werror=format-security' + - uses: ammaraskar/gcc-problem-matcher@master + - name: make + run: make -j `nproc` + - name: install module + run: sudo make install + - name: run tests + run: make test diff --git a/.github/workflows/test-ci-windows.yml b/.github/workflows/test-ci-windows.yml new file mode 100644 index 0000000000..fa27757d33 --- /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/.gitignore b/.gitignore new file mode 100644 index 0000000000..d8d6784f63 --- /dev/null +++ b/.gitignore @@ -0,0 +1,119 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Backup files +*~ + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# http://www.gnu.org/software/automake + +Makefile.in +/ar-lib +/mdate-sh +/py-compile +/test-driver +/ylwrap +.deps/ +.dirstamp + +# http://www.gnu.org/software/autoconf + +autom4te.cache +/autoscan.log +/autoscan-*.log +/aclocal.m4 +/compile +/config.cache +/config.guess +/config.h.in +/config.log +/config.status +/config.sub +/configure +/configure.scan +/depcomp +/install-sh +/missing +/stamp-h1 + +# https://www.gnu.org/software/libtool/ + +/ltmain.sh + +# http://www.gnu.org/software/texinfo + +/texinfo.tex + +# http://www.gnu.org/software/m4/ + +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 + +# Generated Makefile +# (meta build system like autotools, +# can automatically generate from config.status script +# (which is called by configure script)) +Makefile + +# IDEs +.idea + +# tests +tests/regression/server_root/** +tests/*.pl +tests/*.trs +tests/*.log + + diff --git a/CHANGES b/CHANGES index c51479a650..4cf9c09356 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,362 @@ -DD MMM YYYY - 2.9.2 - To be released ------------------------------------- - +05 Aug 2025 - 2.9.12 +-------------------- + + * fix: Improper error handling + [PR from private repo - @orangetw, @pgajdos, @ylavic, @theseion, @fzipi, @airween + fixed CVE-2025-54571] + * fix: mod_security2's regression tests [Issue #3425 - @airween] + * fix: remove unused condition from msc_status_engine.c [Issue #3412 - @airween] + * fix: remove unwanted '\0' string terminator from argument's value [Issue #3411 - @airween] + +01 Jul 2025 - 2.9.11 +-------------------- + + * fix: prevent segmentation fault if the XML node is empty + [PR from private repo - @theseion, @fzipi, @RedXanadu, @airween; fixed CVE-2025-52891] + * Plug memory leak when msre_op_validateSchema_execute() exits normally (validateSchema) + [Issue #3401 - @nic-prgs] + * chore: bump version in MSI installer.wxs + [Issue #3400 - @airween] + * Fix resource leaks in `msc_status_engine_mac_address` + [Issue #3391 - @amezin] + +02 Jun 2025 - 2.9.10 +-------------------- + + * fix: DoS vulnerability + [PR from private repo - @theseion, @fzipi, @airween; fixed CVE-2025-48866] + +21 May 2025 - 2.9.9 +------------------- + + * fix: DoS vulnerability + [PR from private repo - @theseion, @fzipi, @airween; fixed CVE-2025-47947] + * chore: log error codes for global mutex failure modes. + [Issue #3387 - @airween] + * chore: refactor build system to use PCRE2 + [Issue #3383 - @airween] + * feat: add 'make test' to v2's workflow + [Issue #3379 - @airween] + * fix: 'make test' is able to run again + [Issue #3378 - @airween] + * fix: add PCRE2 capability to standalone module + [Issue #3377 - @airween] + * chore: remove unnecessary @LIBXML2_CFLAGS@ from linker flags + [Issue #3376 - @airween] + * fix: add msc_fullinfo() to check JIT compilation + [Issue #3375 - @airween] + * Fix error logging for standalone module + [Issue #3374 - @RedXanadu] + * Fix compiler warnings from GCC + [Issue #3372 - @notroj] + * feat: improved XMLArgs processing + [Issue #3358 - @airween] + * Incorrect utf8toUnicode transformation for 00xx + [Issue #3284 - @marcstern] + * Fixed PCRE2 error message + [Issue #3279 - @marcstern] + * make rootpath and incpath consts for apr_filepath_root + [Issue #3270 - @Marcool04] + * Fix apr_global_mutex_create() usage + [Issue #3269 - @marcstern] + * chore: add 'log' action to rule 200005 (v2/master) + [Issue #3267 - @airween] + * Move id_log() to msc_util to fix unit tests; it is declared on msc_ut… + [Issue #3265 - @rainerjung] + * Missing #include + [Issue #3262 - @marcstern] + * Fixed apr_global_mutex_create() usage (no filename) + [PR #3269 - @marcstern] + * handle errors from apr_global_mutex_lock + [PR #3257 - @marcstern] + +03 Sep 2024 - 2.9.8 +------------------- + + * Fixed ap_log_perror() usage + [PR #3241 - @marcstern] + * Memory leaks + enhanced logging + [PR #3191 - @marcstern] + * CI improvement: First check syntax & always display error/audit logs + [PR #3190 - @marcstern] + * Fixed assert() usage + [PR #3202 - @marcstern] + * Removed useless code + [PR #3193 - @marcstern] + * feat: Check if the MP header contains invalid character + [PR #3226 - @airween] + * Use standard httpd logging format in error log + [PR #3192 - @marcstern] + * fix msc_regexec() != PCRE_ERROR_NOMATCH strict check + [PR #3194 - @marcstern] + * Move xmlFree() call to the right place + [Issue #3199 - @airween] + * Add collection size in log in case of writing error + [Issue #3198 - @marcstern] + * Passing address of lock instead of lock in acquire_global_lock() + [Issue #3188 - @marcstern] + * Invalid pointer access in case rule id == NOT_SET_P + [Issue #3187 - @marcstern] + * Show error.log after httpd start in CI + [Issue #3171 - @marcstern] + * chore: add pull request template + [Issue #3159 - @fzipi] + * chore: add gitignore file + [Issue #3158 - @fzipi] + * Possible double free + [Issue #3155 - @marcstern] + * Set 'jit' variable's initial value + [Issue #3154 - @marcstern] + * Missing null byte + optimization + [Issue #3153 - @marcstern] + * fix: remove usage of insecure tmpname + [Issue #3149 - @fzipi] + * docs: update copyright + [Issue #3148 - @fzipi] + * Enhanced logging [Issue #3107] + [Issue #3139 - @marcstern] + * Check for null pointer dereference (almost) everywhere + [Issue #3120 - @marcstern] + * Fix possible segfault in collection_unpack + [Issue #3099 - @twouters] + * fix: Replace obsolete macros + [Issue #3094 - @airween] + * chore: update bug-report-for-version-2-x.md + [Issue #3087 - @fzipi] + * feat: Add more steps: install built module and restart the server + [Issue #3078 - @airween] + * Add new flag: --without-lua + [Issue #3076 - @airween] + * Initial release of CI worklow + [Issue #3075 - @airween] + * V2/fixbuildissue + [Issue #3074 - @airween] + * ; incorrectly replaced by space in cmdline + [Issue #3051 - @marcstern] + * Detailed error message when writing collections + [Issue #3050 - @marcstern] + * docs: Fix organization name in references and security e-mail (v2) + [Issue #3043 - @airween] + * ctl:ruleRemoveByTag isn't executed if no rule id is present in the rule + [Issue #3012 - @marcstern] + * Suppress useless loop on tag matching + [Issue #3009 - @marcstern] + * Optimization: Avoid last loop and storing an empty value in case nothing + after last %{..} macro + [Issue #3004 - @marcstern] + * Ignore (consistently) empty actions + [Issue #3003 - @marcstern] + * Add context info to error message + [Issue #2997 - @marcstern] + * Implement msre_action_phase_validate() + [Issue #2994 - @marcstern] + * Avoid some useless code and memory allocation in case no macro is present + [Issue #2992 - @marcstern] + * 'jit' variable not initialized when WITH_PCRE2 is defined + [Issue #2987 - @marcstern] + * Configure: do not check for pcre1 if pcre2 requested + [Issue #2975 - @martinhsv] + * Double memory allocation + [Issue #2969 - @marcstern] + * Fix for DEBUG_CONF compile flag + [Issue #2963 - @marcstern] + * Enhance logging + [Issue #3107 - @marcstern] + * Fix possible segfault in collection_unpack + [Issue #3072 - @twouters] + * Set the minimum security protocol version for SecRemoteRules + [Issue security/code-scanning/2 - @airween] + * Allow lua version 5.4 + [Issue #2996 - @3eka, @martinhsv] + * Configure: do not check for pcre1 if pcre2 requested + [Issue #2975 - @zhaoshikui, @martinhsv] + * Check return code of apr_procattr_io_set() + [Issue #2958 - @marcstern] + * Do not escape special chars in rx pattern with macro + [Issue #2357 - @marcstern, @martinhsv] + * Substitute two equals-equals operators in build + [Issue #2883 - @Polynomial-C] + +04 Jan 2023 - 2.9.7 +------------------- + + * Fix: FILES_TMP_CONTENT may sometimes lack complete content + [Issue #2857 - gieltje, @airween, @dune73, @martinhsv] + * Support configurable limit on number of arguments processed + [Issue #2844 - @jleproust, @martinhsv] + * Silence compiler warning about discarded const + [Issue #2843 - @Steve8291, @martinhsv] + * Support for JIT option for PCRE2 + [Issue #2840 - @martinhsv] + * Use uid for user if apr_uid_name_get() fails + [Issue #2046 - @arminabf, @marcstern] + * Fix: handle error with SecConnReadStateLimit configuration + [Issue #2815, #2834 - @marcstern, @martinhsv] + * Only check for pcre2 install if required + [Issue #2833 - @martinhsv] + * Adjustment of previous fix for log messages + [Issue #2832 - @marcstern, @erkia] + * Mark apache error log messages as from mod_security2 + [Issue #2781 - @erkia] + * Use pkg-config to find libxml2 first + [Issue #2818 - @hughmcmaster] + * Support for PCRE2 in mlogc + [Issue #2737, #2827 - @martinhsv] + * Support for PCRE2 + [Issue #2737 - @martinhsv] + +07 Sep 2022 - 2.9.6 +------------------- + + * Adjust parser activation rules in modsecurity.conf-recommended + [Issue #2799 - @terjanq, @martinhsv] + * Multipart parsing fixes and new MULTIPART_PART_HEADERS collection + [Issue #2797 - @terjanq, @martinhsv] + * Limit rsub null termination to where necessary + [Issue #2794 - @marcstern, @martinhsv] + * IIS: Update dependencies for next planned release + [@martinhsv] + * XML parser cleanup: NULL duplicate pointer + [Issue #2760 - @martinhsv] + * Properly cleanup XML parser contexts upon completion + [Issue #2239 - @argenet] + * Fix memory leak in streams + [Issue #2208 - @marcstern, @vloup, @JamesColeman-LW] + * Fix: negative usec on log line when data type long is 32b + [Issue #2753 - @ABrauer-CPT, @martinhsv] + * mlogc log-line parsing fails due to enhanced timestamp + [Issue #2682 - @bozhinov, @ABrauer-CPT, @martinhsv] + * Allow no-key, single-value JSON body + [Issue #2735 - @marcstern, @martinhsv] + * Set SecStatusEngine Off in modsecurity.conf-recommended + [Issue #2717 - @un99known99, @martinhsv] + * Fix memory leak that occurs on JSON parsing error + [Issue #2236 @argenet, @vloup, @martinhsv] + * Multipart names/filenames may include single quote if double-quote enclosed + [Issue #2352 @martinhsv] + * Add SecRequestBodyJsonDepthLimit to modsecurity.conf-recommended + [Issue #2647 @theMiddleBlue, @airween, @877509395 ,@martinhsv] + * IIS: Update dependencies for Windows build as of v2.9.5 + [@martinhsv] + +22 Nov 2021 - 2.9.5 +------------------- + + * Support configurable limit on depth of JSON parsing + [@theMiddleBlue, @airween, @dune73, @martinhsv] + +21 Jun 2021 - 2.9.4 +------------------- + + * Add microsec timestamp resolution to the formatted log timestamp + [Issue #2095 - @rainerjung] + * Store temporaries in the request pool for regexes compiled per-request. + [Issue #890, #2049 - @lightsey] + * Fix other usage of the global pool for request temporaries in re_operators.c + [Issue #890, #2049 - @lightsey] + * Adds a sanity check before use ctl:ruleRemoveTargetById and ctl:ruleRemoveTargetByMsg. + [Issue #2033 - @studersi] + * Fix the order of error_msg validation + [Issue #2128 - @marcstern, @zimmerle] + * Added missing Geo Countries + [Issue #2123, #2124 - @emphazer] + * When the input filter finishes, check whether we returned data + [Issue #2091, #2092 - @rainerjung] + * fix: care non-null terminated chunk data + [Issue #2097 - @orisano] + * Fix for apr_global_mutex_create() crashes with mod_security + [Issue #1957 - @blappm] + * Fix inet addr handling on 64 bit big endian systems + [Issue #1980 - @zimmerle, @airween] + + +05 Dec 2018 - 2.9.3 +------------------- + + * Enable optimization for large stream input by default on IIS + [Issue #1299 - @victorhora, @zimmerle] + * Allow 0 length JSON requests. + [Issue #1822 - @allanbomsft, @zimmerle, @victorhora, @marcstern] + * Include unanmed JSON values in unnamed ARGS + [Issue #1577, #1576 - @marcstern, @victorhora, @zimmerle] + * Fix buffer size for utf8toUnicode transformation + [Issue #1208 - @katef, @victorhora] + * Fix sanitizing JSON request bodies in native audit log format + [p0pr0ck5, @victorhora] + * IIS: Update Wix installer to bundle a supported CRS version (3.0) + [@victorhora, @zimmerle] + * IIS: Update dependencies for Windows build + [Issue #1848 - @victorhora, @hsluoyz] + * IIS: Set SecStreamInBodyInspection by default on IIS builds (#1299) + [Issue #1299 - @victorhora] + * IIS: Update modsecurity.conf + [Issue #788 - @victorhora, @brianclark] + * Add sanity check for a couple malloc() and make code more resilient + [Issue #979 - @dogbert2, @victorhora, @zimmerl] + * Fix NetBSD build by renaming the hmac function to avoid conflicts + [Issue #1241 - @victorhora, @joerg, @sevan] + * IIS: Windows build, fix duplicate YAJL dir in script + [Issue #1612 - @allanbomsft, @victorhora] + * IIS: Remove body prebuffering due to no locking in modsecProcessRequest + [Issue #1917 - @allanbomsft, @victorhora] + * Fix mpm-itk / mod_ruid2 compatibility + [Issue #712 - @ju5t , @derhansen, @meatlayer, @victorhora] + * Code cosmetics: checks if actionset is not null before use it + [Issue #1556 - @marcstern, @zimmerle, @victorhora] + * Only generate SecHashKey when SecHashEngine is On + [Issue #1671 - @dmuey, @monkburger, @zimmerle] + * Docs: Reformat README to Markdown and update dependencies + [Issue #1857 - @hsluoyz, @victorhora] + * IIS: no lock on ProcessRequest. No reload of config. + [Issue #1826 - @allanbomsft] + * IIS: buffer request body before taking lock + [Issue #1651 - @allanbomsft] + * good practices: Initialize variables before use it + [Issue #1889 - Marc Stern] + * Let body parsers observe SecRequestBodyNoFilesLimit + [Issue #1613 - @allanbomsft] + * potential off by one in parse_arguments + [Issue #1799 - @tinselcity, @zimmerle] + * Fix utf-8 character encoding conversion + [Issue #1794 - @tinselcity, @zimmerle] + * Fix ip tree lookup on netmask content + [Issue #1793 - @tinselcity, @zimmerle] + * IIS: set overrideModeDefault to Allow so that individual websites can + add to their web.config file + [Issue #1781 - @default-kramer] + * modsecurity.conf-recommended: Fix spelling + [Issue #1721 - @padraigdoran] + * build: fix when multiple lines for curl version + [Issue #1771 - @Artistan] + * Fix arabic charset in unicode_mapping file + [Issue #1619 - @alaa-ahmed-a] + * Optionally preallocates memory when SecStreamInBodyInspection is on + [Issue #1366 - @allanbomsft, @zimmerle] + * Fixed typo in build_yajl.bat + [Issue #1366 - @allanbomsft] + * Fixes SecConnWriteStateLimit + [Issue #1545 - @nicjansma] + * Added "empy chunk" check + [Issue #1347, #1446 - @gravagli, @bostrt, @zimmerle] + * Add capture action to @detectXSS operator + [Issue #1488, #1482 - @victorhora] + * Fix for wildcard operator when loading conf files on Nginx / IIS + [Issue #1486, #1285 - @victorhora and @thierry-f-78] + * Set of fixies to make windows build workable with the buildbots + [Commit 94fe3 - @zimmerle] + * Uses LOG_NO_STOPWATCH instead of DLOG_NO_STOPWATCH + [Issue #1510 - @marcstern] + * Adds missing headers + [Issue #1454 - @devnexen] + + +18 Jul 2017 - 2.9.2 +------------------- + + * IIS build refactoring and dependencies update + [Issue #1487 - @victorhora] + * Best practice: Initialize msre_var pointers + [Commit fbd57 - Allan Boll] * nginx: Obtain port from r->connection->local_sockaddr. [Commit 51314 - @defanator] * Updates libinjection to v3.10.0 @@ -42,6 +398,7 @@ DD MMM YYYY - 2.9.2 - To be released [Issue #1067 - Marc Stern] * {dis|en}able-dechunk-logging: Option to disable logging of dechunking in audit log when log level < 9. + [Issue #1068 - Marc Stern] * Updates libinjection to: da027ab52f9cf14401dd92e34e6683d183bdb3b4 [ModSecurity team] * {dis|en}able-handler-logging: Option to disable logging of Apache handler @@ -90,6 +447,15 @@ DD MMM YYYY - 2.9.2 - To be released * Treat APR_INCOMPLETE as APR_EOF while receiving the request body. [Issue #1060, #334 - Alexey Sintsov] + +Security issues + + * Allan Boll reported an uninitialized variable that may lead to a crash on + Windows platform. + * Brian Adeloye reported an infinite loop on the version of libinjection used + on ModSecurity 2.9.1. + + 09 Mar 2016 - 2.9.1 ------------------- diff --git a/README.TXT b/README.TXT deleted file mode 100644 index 03767e345f..0000000000 --- a/README.TXT +++ /dev/null @@ -1,110 +0,0 @@ -ModSecurity for Apache 2.x, http://www.modsecurity.org/ -Copyright (c) 2004-2013 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 - -    http://www.apache.org/licenses/LICENSE-2.0 - -If any of the files related to licensing are missing or if you have any -other questions related to licensing please contact Trustwave Holdings, Inc. -directly using the email address security@modsecurity.org. - - -DOCUMENTATION - -Please refer to the documentation folder (/doc) for -the reference manual. - - -############################################## ----------------------------------- -OWASP ModSecurity Core Rule Set (CRS) - - -Project Site: -https://www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project - - -Download: -https://github.com/SpiderLabs/owasp-modsecurity-crs - ----------------------------------- - -ModSecurity™ is a web application firewall engine that provides very -little protection on its own. In order to become useful, ModSecurity™ must -be configured with rules. In order to enable users to take full advantage -of ModSecurity™ out of the box, Trustwave's SpiderLabs is providing a free -certified rule set for ModSecurity™ 2.x. Unlike intrusion detection and -prevention systems, which rely on signatures specific to known -vulnerabilities, the Core Rules provide generic protection from unknown -vulnerabilities often found in web applications, which are in most cases -custom coded. The Core Rules are heavily commented to allow it to be used -as a step-by-step deployment guide for ModSecurity™. -Core Rules Content - -In order to provide generic web applications protection, the Core Rules -use the following techniques: - -* HTTP Protection - detecting violations of the HTTP protocol and a -locally defined usage policy. -* Real-time Blacklist Lookups - utilizes 3rd Party IP Reputation -* Web-based Malware Detection - identifies malicious web content by check -against the Google Safe Browsing API. -* HTTP Denial of Service Protections - defense against HTTP Flooding and -Slow HTTP DoS Attacks. -* Common Web Attacks Protection - detecting common web application -security attack. -* Automation Detection - Detecting bots, crawlers, scanners and other -surface malicious activity. -* Integration with AV Scanning for File Uploads - detects malicious files -uploaded through the web application. -* Tracking Sensitive Data - Tracks Credit Card usage and blocks leakages. -* Trojan Protection - Detecting access to Trojans horses. -* Identification of Application Defects - alerts on application -misconfigurations. -* Error Detection and Hiding - Disguising error messages sent by the -server. - - ----------------------------------- -ModSecurity Rules from Trustwave SpiderLabs - -Project Site: -https://www.trustwave.com/modsecurity-rules-support.php - -Download: -https://ssl.trustwave.com/web-application-firewall - ----------------------------------- - - - -Trustwave now provides a commercial certified rule set for ModSecurity 2.x -that protects against known attacks that target vulnerabilities in public -software and are based on intelligence gathered from real-world -investigations, honeypot data and research. - -1. More than 16,000 specific rules, broken out into the following attack -categories: - * SQL injection - * Cross-site Scripting (XSS) - * Local File Include - * Remote File Include - -2. User option for application specific rules, covering the same -vulnerability classes for applications such as: - * WordPress - * cPanel - * osCommerce - * Joomla - * For a complete listing of application coverage, please refer to this -link (which is updated daily). -https://modsecurity.org/application_coverage.html - -3. Complements and integrates with the OWASP Core Rule Set -4. IP Reputation capabilities which provide protection against malicious -clients identified by the Trustwave SpiderLabs Distributed Web Honeypots -5. Malware Detection capabilities which prevent your web site from -distributing malicious code to clients. -############################################## diff --git a/README.md b/README.md new file mode 100644 index 0000000000..a823585172 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# ModSecurity 2 + +https://www.modsecurity.org/ + +Copyright (c) 2004-2024 Trustwave Holdings, Inc. (https://www.trustwave.com/) +Copyright (c) 2024-2024 OWASP ModSecurity Project (https://www.owasp.org/) + +You may not use this file except in compliance with the License. You may obtain a copy of the License at: https://www.apache.org/licenses/LICENSE-2.0 + +If any of the files related to licensing are missing or if you have any other questions related to licensing please contact us here: modsecurity@owasp.org. + +## Documentation + +Please refer to: [the documentation folder](https://github.com/owasp-modsecurity/ModSecurity/tree/v2/master/doc) for the reference manual. + +## Sponsor Note + +Original Development of ModSecurity was sponsored by Trustwave. In 2024, [stewardship was transferred to OWASP](https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/trustwave-transfers-modsecurity-custodianship-to-the-open-worldwide-application-security-project/). + +Contact us for sponsorship! + +You can also send us donations using the [OWASP donations page](https://owasp.org/donate/?reponame=www-project-modsecurity&title=OWASP+ModSecurity). diff --git a/README_WINDOWS.TXT b/README_WINDOWS.TXT deleted file mode 100644 index 94c2bc9db9..0000000000 --- a/README_WINDOWS.TXT +++ /dev/null @@ -1,192 +0,0 @@ -===================================================================== -MOD_SECURITY 2.6 Command-line Build notes for Windows 4/2/2011 -by Tom Donovam -===================================================================== - -PREREQUISITES: - - Microsoft Visual Studio C++ tested with Visual Studio 2008 (aka VC9) - - CMake build system from: http://www.cmake.org/ tested with CMake v2.8.0 - - Apache 2.2.x from: http://httpd.apache.org/ tested with Apache 2.2.17 - Apache must be built from source using the same Visual Studio compiler as mod_security. - - PCRE Perl Compatible Regular Expression library from: http://www.pcre.org/ tested with PCRE v8.12 - - LibXML2 from: http://xmlsoft.org/ tested with LibXML2 v2.7.7 - Note that LibXML2 v2.7.8 does not build correctly for Windows - - Lua Scripting Language from: http://www.lua.org/ tested with Lua v5.1.4 - - cURL multiprotocol file transfer library from: http://curl.haxx.se/ tested with cURL v7.21.4 - - -BEFORE BUILDING - -The directory where you build software from source ( C:\work in this exmaple) -must contain the Apache source you used to build the Apache web serverand the mod_security source - - Apache source is in C:\work\httpd-2.2.17 in this example. - Apache has been installed to C:\Apache2217 in this example. - Mod_security source is in C:\work\mod_security in this example. - -Download and untar the prerequite library sources: - - Download pcre-8.12.tar.gz from ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/ - untar it into C:\work\ creating C:\work\pcre-8.12 - - Download libxml2-2.7.7.tar.gz from ftp://xmlsoft.org/libxml2/ - untar it into C:\work\ creating C:\work\libxml2-2.7.7 - - Download lua-5.1.4.tar.gz from http://www.lua.org/ftp/ - untar it into C:\work\ creating C:\work\lua-5.1.4 - - Download curl-7.21.4.tar.gz from http://curl.haxx.se/download.html - untar it into C:\work\ creating C:\work\curl-7.21.4 - -Setup your build environment: - - The PATH environment variable must include the Visual Studio variables as set by vsvars32.bat - The PATH environment variable must also include the CMAKE bin\ directory - - Set an environment variable to the Apache source code directory: - - SET HTTPD_BUILD=C:\work\httpd-2.2.17 - - If OpenSSL and Zlib support were included when you built Apache 2.2, and you want them available to LIBXML2 and CURL - - Ensure that cURL and libXML2 can find the OpenSSL and Zlib includes and libraries that Apache was built with. - - SET INCLUDE=%INCLUDE%;%HTTPD_BUILD%\srclib\openssl\inc32;%HTTPD_BUILD%\srclib\zlib - SET LIB=%LIB%;%HTTPD_BUILD%\srclib\openssl\out32dll;%HTTPD_BUILD%\srclib\zlib - - Ensure that cURL and libXML2 don't use the static zlib library: zlib.lib. - Force cURL and libXML2 to use zdll.lib instead, requiring zlib1.dll at runtime: - - IF EXIST %HTTPD_BUILD%\srclib\zlib\zlib.lib DEL %HTTPD_BUILD%\srclib\zlib\zlib.lib - -BUILD PCRE-8.12 - - CD C:\work\pcre-8.12 - CMAKE -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=True - NMAKE - -BUILD LIBXML2-2.7.7 (note: the more recent version: 2.7.8 does not build correctly on Windows) - - CD C:\work\libxml2-2.7.7\win32 - CSCRIPT configure.js iconv=no vcmanifest=yes zlib=yes - NMAKE -f Makefile.msvc - -BUILD LUA-5.1.4 - - CD C:\work\lua-5.1.4\src - CL /Ox /arch:SSE2 /GF /GL /Gy /FD /EHsc /MD /Zi /TC /wd4005 /D "_MBCS" /D "LUA_CORE" /D "LUA_BUILD_AS_DLL" /D "_CRT_SECURE_NO_WARNINGS" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_WIN32" /D "_WINDLL" /c *.c - DEL lua.obj luac.obj - LINK /DLL /LTCG /DEBUG /OUT:lua5.1.dll *.obj - IF EXIST lua5.1.dll.manifest MT -manifest lua5.1.dll.manifest -outputresource:lua5.1.dll;2 - -BUILD CURL-7.21.4 - - CD C:\work\curl-7.21.4 - CMAKE -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=True -DCURL_ZLIB=True - NMAKE - -BUILD MOD_SECURITY-2.6 - - CD C:\work\mod_security\apache2 - NMAKE -f Makefile.win APACHE=C:\Apache2217 PCRE=C:\work\pcre-8.12 LIBXML2=C:\work\libxml2-2.7.7 LUA=C:\work\lua-5.1.4\src - -INSTALL MOD_SECURITY AND RUN APACHE - -Copy these five files to C:\Apache2217\bin: - C:\work\pcre-8.12\pcre.dll C:\Apache2217\bin\ - C:\work\lua-5.1.4\src\lua5.1.dll C:\Apache2217\bin\ - C:\work\libxml2-2.7.7\win32\bin.msvc\libxml2.dll C:\Apache2217\bin\ - C:\work\curl-7.21.4\libcurl.dll C:\Apache2217\bin\ - C:\work\mod_security\apache2\mlogc-src\mlogc.exe - -Copy this one file to C:\Apache2217\modules: - - C:\work\mod_security\apache2\mod_security2.so - -You may also copy C:\work\curl-7.21.4\curl.exe to C:\Apache2217\bin, if you want to use the cURL command-line program. - -Download the core rules from http://sourceforge.net/projects/mod-security/files/modsecurity-crs/0-CURRENT/ -and unzip them into C:\Apache2217\conf\modsecurity_crs - -Add configuration directives to your Apache conf\httpd.conf: - - # mod_security requires mod_unique_id - LoadModule unique_id_module modules/mod_unique_id.so - - # mod_security - LoadModule security2_module modules/mod_security2.so - - SecRuleEngine On - SecDataDir logs - Include conf/modsecurity_crs/*.conf - Include conf/modsecurity_crs/base_rules/*.conf - SecAuditEngine RelevantOnly - SecAuditLogRelevantStatus "^(?:5|4\d[^4])" - SecAuditLogType Serial - SecAuditLogParts ABCDEFGHZ - SecAuditLog logs/modsecurity.log - - - -============================================================================================== -OPTIONAL: BUILD AND CONFIGURE THE MOD_SECURITY-2.6 MLOGC piped-logging program - -Edit the top of C:\work\mod_security\apache2\mlogc-src\Makefile.win and set your local paths - - # Path to Apache httpd installation - BASE = C:\Apache2217 - - # Paths to required libraries - PCRE = C:\work\pcre-8.12 - CURL = C:\work\curl-7.21.4 - - # Linking libraries - LIBS = $(BASE)\lib\libapr-1.lib \ - $(BASE)\lib\libaprutil-1.lib \ - $(PCRE)\pcre.lib \ - $(CURL)\libcurl_imp.lib \ - wsock32.lib - -Build the mlogc.exe program: - - CD C:\work\mod_security_trunk\mlogc - NMAKE -f Makefile.win - -Copy mlocg.exe to C:\Apache2217\bin\ - -Create a new command file C:\Apache2217\bin\mlogc.bat with one line: - - C:\Apache2217\bin\mlogc.exe C:\Apache2217\conf\mlogc.conf - -Create a new configuration file C:\Apache2217\conf\mlogc.conf to control the piped-logging program mlogc.exe. -Here is an example conf\mlogc.conf: - - CollectorRoot "C:/Apache2217/logs" - ConsoleURI "https://localhost:8888/rpc/auditLogReceiver" - SensorUsername "test" - SensorPassword "testtest" - LogStorageDir "data" - TransactionLog "mlogc-transaction.log" - QueuePath "mlogc-queue.log" - ErrorLog "mlogc-error.log" - LockFile "mlogc.lck" - KeepEntries 0 - ErrorLogLevel 2 - MaxConnections 10 - MaxWorkerRequests 1000 - TransactionDelay 50 - StartupDelay 5000 - CheckpointInterval 15 - ServerErrorTimeout 60 - -Change the SecAuditLog directive in conf\httpd.conf to pipe the log data to mlogc -instead of writing them to a file: - - SecAuditLog |C:/Apache2217/bin/mlogc.bat diff --git a/README_WINDOWS.md b/README_WINDOWS.md new file mode 100644 index 0000000000..dcb7e0db3a --- /dev/null +++ b/README_WINDOWS.md @@ -0,0 +1,194 @@ + +## ModSecurity 2.x Command-line build notes for Windows + +by Tom Donovam, 4/2/2011 + + +## Prerequisites: + +Dependency | Tested with | Note +----|------|---- +Microsoft Visual Studio C++ | Visual Studio 2013 (aka VC12) | +[CMake build system](http://www.cmake.org/) | CMake v3.8.2 | +[Apache 2.4.x](http://httpd.apache.org/) | Apache 2.4.27 | Apache must be built from source using the same Visual Studio compiler as mod_security. +[PCRE, Perl Compatible Regular Expression library](http://www.pcre.org/) | PCRE v8.40 +[LibXML2](http://xmlsoft.org/) | LibXML2 v2.9.4 | +[Lua Scripting Language](http://www.lua.org/) | Lua v5.3.4 +[cURL multiprotocol file transfer library](http://curl.haxx.se/) | cURL v7.54.0 + + +## Before building + +The directory where you build software from source ( ``C:\work`` in this exmaple) +must contain the Apache source you used to build the Apache web serverand the mod_security source + + Apache source is in C:\work\httpd-2.4.27 in this example. + Apache has been installed to C:\Apache2427 in this example. + Mod_security source is in C:\work\mod_security in this example. + +## Download and untar the prerequisite library sources: + + Download pcre-8.40.tar.gz from ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/ + untar it into C:\work\ creating C:\work\pcre-8.40 + + Download libxml2-2.9.4.tar.gz from ftp://xmlsoft.org/libxml2/ + untar it into C:\work\ creating C:\work\libxml2-2.9.4 + + Download lua-5.3.4.tar.gz from http://www.lua.org/ftp/ + untar it into C:\work\ creating C:\work\lua-5.3.4 + + Download curl-7.54.0.tar.gz from http://curl.haxx.se/download.html + untar it into C:\work\ creating C:\work\curl-7.54.0 + +## Setup your build environment: + +1. The ``PATH`` environment variable must include the Visual Studio variables as set by ``vsvars32.bat`` + +2. The ``PATH`` environment variable must also include the CMAKE ``bin\`` directory + +3. Set an environment variable to the Apache source code directory: + +``` + SET HTTPD_BUILD=C:\work\httpd-2.4.27 +``` + +### Optional: + +If OpenSSL and zlib support were included when you built Apache 2.4, and you want them available to LibXML2 and cURL + +1. Ensure that cURL and LibXML2 can find the OpenSSL and zlib includes and libraries that Apache was built with. + +``` + SET INCLUDE=%INCLUDE%;%HTTPD_BUILD%\srclib\openssl\inc32;%HTTPD_BUILD%\srclib\zlib + SET LIB=%LIB%;%HTTPD_BUILD%\srclib\openssl\out32dll;%HTTPD_BUILD%\srclib\zlib +``` + +2. Ensure that cURL and libXML2 don't use the static zlib library: ``zlib.lib``. Force cURL and libXML2 to use ``zdll.lib`` instead, requiring ``zlib1.dll`` at runtime: + +``` + IF EXIST %HTTPD_BUILD%\srclib\zlib\zlib.lib DEL %HTTPD_BUILD%\srclib\zlib\zlib.lib +``` + +## Build + +### PCRE-8.40 + + CD C:\work\pcre-8.40 + CMAKE -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=True + NMAKE + +### LibXML2-2.9.4 + + CD C:\work\libxml2-2.9.4\win32 + CSCRIPT configure.js iconv=no vcmanifest=yes zlib=yes + NMAKE -f Makefile.msvc + +### Lua-5.3.4 + + CD C:\work\lua-5.3.4\src + CL /Ox /arch:SSE2 /GF /GL /Gy /FD /EHsc /MD /Zi /TC /wd4005 /D "_MBCS" /D "LUA_CORE" /D "LUA_BUILD_AS_DLL" /D "_CRT_SECURE_NO_WARNINGS" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_WIN32" /D "_WINDLL" /c *.c + DEL lua.obj luac.obj + LINK /DLL /LTCG /DEBUG /OUT:lua5.1.dll *.obj + IF EXIST lua5.1.dll.manifest MT -manifest lua5.1.dll.manifest -outputresource:lua5.1.dll;2 + +### cURL-7.54.0 + + CD C:\work\curl-7.54.0 + CMAKE -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=True -DCURL_ZLIB=True + NMAKE + +### ModSecurity-2.9.x + + CD C:\work\mod_security\apache2 + NMAKE -f Makefile.win APACHE=C:\Apache2427 PCRE=C:\work\pcre-8.40 LIBXML2=C:\work\libxml2-2.9.4 LUA=C:\work\lua-5.3.4\src + +## Install ModSecurity and run Apache + +Copy these five files to ``C:\Apache2427\bin``: + + C:\work\pcre-8.40\pcre.dll C:\Apache2427\bin\ + C:\work\lua-5.3.4\src\lua5.1.dll C:\Apache2427\bin\ + C:\work\libxml2-2.9.4\win32\bin.msvc\libxml2.dll C:\Apache2427\bin\ + C:\work\curl-7.54.0\libcurl.dll C:\Apache2427\bin\ + C:\work\mod_security\apache2\mlogc-src\mlogc.exe + +Copy this one file to ``C:\Apache2427\modules``: + + C:\work\mod_security\apache2\mod_security2.so + +You may also copy ``C:\work\curl-7.54.0\curl.exe`` to ``C:\Apache2427\bin``, if you want to use the cURL command-line program. + +Download the core rules from http://sourceforge.net/projects/mod-security/files/modsecurity-crs/0-CURRENT/ and unzip them into ``C:\Apache2427\conf\modsecurity_crs`` + +Add configuration directives to your Apache conf\httpd.conf: + + # mod_security requires mod_unique_id + LoadModule unique_id_module modules/mod_unique_id.so + + # mod_security + LoadModule security2_module modules/mod_security2.so + + SecRuleEngine On + SecDataDir logs + Include conf/modsecurity_crs/*.conf + Include conf/modsecurity_crs/base_rules/*.conf + SecAuditEngine RelevantOnly + SecAuditLogRelevantStatus "^(?:5|4\d[^4])" + SecAuditLogType Serial + SecAuditLogParts ABCDEFGHZ + SecAuditLog logs/modsecurity.log + + +## Optional: Build and configure the ModSecurity-2.x MLOGC piped-logging program + +Edit the top of ``C:\work\mod_security\apache2\mlogc-src\Makefile.win`` and set your local paths + + # Path to Apache httpd installation + BASE = C:\Apache2427 + + # Paths to required libraries + PCRE = C:\work\pcre-8.40 + CURL = C:\work\curl-7.54.0 + + # Linking libraries + LIBS = $(BASE)\lib\libapr-1.lib \ + $(BASE)\lib\libaprutil-1.lib \ + $(PCRE)\pcre.lib \ + $(CURL)\libcurl_imp.lib \ + wsock32.lib + +Build the ``mlogc.exe`` program: + + CD C:\work\mod_security_trunk\mlogc + NMAKE -f Makefile.win + +Copy ``mlocg.exe`` to ``C:\Apache2427\bin\`` + +Create a new command file ``C:\Apache2427\bin\mlogc.bat`` with one line: + + C:\Apache2427\bin\mlogc.exe C:\Apache2427\conf\mlogc.conf + +Create a new configuration file ``C:\Apache2427\conf\mlogc.conf`` to control the piped-logging program ``mlogc.exe``. +Here is an example ``conf\mlogc.conf``: + + CollectorRoot "C:/Apache2427/logs" + ConsoleURI "https://localhost:8888/rpc/auditLogReceiver" + SensorUsername "test" + SensorPassword "testtest" + LogStorageDir "data" + TransactionLog "mlogc-transaction.log" + QueuePath "mlogc-queue.log" + ErrorLog "mlogc-error.log" + LockFile "mlogc.lck" + KeepEntries 0 + ErrorLogLevel 2 + MaxConnections 10 + MaxWorkerRequests 1000 + TransactionDelay 50 + StartupDelay 5000 + CheckpointInterval 15 + ServerErrorTimeout 60 + +Change the SecAuditLog directive in ``conf\httpd.conf`` to pipe the log data to mlogc instead of writing them to a file: + + SecAuditLog |C:/Apache2427/bin/mlogc.bat diff --git a/apache2/Makefile.am b/apache2/Makefile.am index e7bf787bdc..50246829bd 100644 --- a/apache2/Makefile.am +++ b/apache2/Makefile.am @@ -42,6 +42,7 @@ mod_security2_la_CFLAGS = @APR_CFLAGS@ \ @LUA_CFLAGS@ \ @MODSEC_EXTRA_CFLAGS@ \ @PCRE_CFLAGS@ \ + @PCRE2_CFLAGS@ \ @YAJL_CFLAGS@ \ @SSDEEP_CFLAGS@ @@ -50,15 +51,16 @@ mod_security2_la_CPPFLAGS = @APR_CPPFLAGS@ \ @CURL_CPPFLAGS@ \ @LIBXML2_CFLAGS@ \ @LIBXML2_CPPFLAGS@ \ - @PCRE_CPPFLAGS@ + @PCRE_CPPFLAGS@ \ + @PCRE2_CPPFLAGS@ mod_security2_la_LIBADD = @APR_LDADD@ \ @APU_LDADD@ \ @CURL_LDADD@ \ - @LIBXML2_CFLAGS@ \ @LIBXML2_LDADD@ \ @LUA_LDADD@ \ @PCRE_LDADD@ \ + @PCRE2_LDADD@ \ @YAJL_LDADD@ if AIX @@ -67,10 +69,10 @@ mod_security2_la_LDFLAGS = -module -avoid-version \ @APU_LDFLAGS@ \ @APXS_LDFLAGS@ \ @CURL_LDFLAGS@ \ - @LIBXML2_CFLAGS@ \ @LIBXML2_LDFLAGS@ \ @LUA_LDFLAGS@ \ @PCRE_LDFLAGS@ \ + @PCRE2_LDFLAGS@ \ @YAJL_LDFLAGS@ \ @SSDEEP_LDFLAGS@ endif @@ -81,10 +83,10 @@ mod_security2_la_LDFLAGS = -module -avoid-version \ @APU_LDFLAGS@ \ @APXS_LDFLAGS@ \ @CURL_LDFLAGS@ \ - @LIBXML2_CFLAGS@ \ @LIBXML2_LDFLAGS@ \ @LUA_LDFLAGS@ \ @PCRE_LDFLAGS@ \ + @PCRE2_LDFLAGS@ \ @YAJL_LDFLAGS@ \ @SSDEEP_LDFLAGS@ endif @@ -95,10 +97,10 @@ mod_security2_la_LDFLAGS = -module -avoid-version \ @APU_LDFLAGS@ \ @APXS_LDFLAGS@ \ @CURL_LDFLAGS@ \ - @LIBXML2_CFLAGS@ \ @LIBXML2_LDFLAGS@ \ @LUA_LDFLAGS@ \ @PCRE_LDFLAGS@ \ + @PCRE2_LDFLAGS@ \ @YAJL_LDFLAGS@ \ @SSDEEP_LDFLAGS@ endif @@ -109,10 +111,10 @@ mod_security2_la_LDFLAGS = -module -avoid-version \ @APU_LDFLAGS@ \ @APXS_LDFLAGS@ \ @CURL_LDFLAGS@ \ - @LIBXML2_CFLAGS@ \ @LIBXML2_LDFLAGS@ \ @LUA_LDFLAGS@ \ @PCRE_LDFLAGS@ \ + @PCRE2_LDFLAGS@ \ @YAJL_LDFLAGS@ \ @SSDEEP_LDFLAGS@ endif @@ -123,10 +125,10 @@ mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version -R @PCRE_LD_PATH @APU_LDFLAGS@ \ @APXS_LDFLAGS@ \ @CURL_LDFLAGS@ \ - @LIBXML2_CFLAGS@ \ @LIBXML2_LDFLAGS@ \ @LUA_LDFLAGS@ \ @PCRE_LDFLAGS@ \ + @PCRE2_LDFLAGS@ \ @YAJL_LDFLAGS@ \ @SSDEEP_LDFLAGS@ endif @@ -137,10 +139,10 @@ mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version \ @APU_LDFLAGS@ \ @APXS_LDFLAGS@ \ @CURL_LDFLAGS@ \ - @LIBXML2_CFLAGS@ \ @LIBXML2_LDFLAGS@ \ @LUA_LDFLAGS@ \ @PCRE_LDFLAGS@ \ + @PCRE2_LDFLAGS@ \ @YAJL_LDFLAGS@ \ @SSDEEP_LDFLAGS@ endif @@ -151,10 +153,10 @@ mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version \ @APU_LDFLAGS@ \ @APXS_LDFLAGS@ \ @CURL_LDFLAGS@ \ - @LIBXML2_CFLAGS@ \ @LIBXML2_LDFLAGS@ \ @LUA_LDFLAGS@ \ @PCRE_LDFLAGS@ \ + @PCRE2_LDFLAGS@ \ @YAJL_LDFLAGS@ \ @SSDEEP_LDFLAGS@ endif @@ -165,10 +167,10 @@ mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version \ @APU_LDFLAGS@ \ @APXS_LDFLAGS@ \ @CURL_LDFLAGS@ \ - @LIBXML2_CFLAGS@ \ @LIBXML2_LDFLAGS@ \ @LUA_LDFLAGS@ \ @PCRE_LDFLAGS@ \ + @PCRE2_LDFLAGS@ \ @YAJL_LDFLAGS@ \ @SSDEEP_LDFLAGS@ endif diff --git a/apache2/acmp.c b/apache2/acmp.c index 6e796b38db..3691dd12e5 100644 --- a/apache2/acmp.c +++ b/apache2/acmp.c @@ -251,18 +251,6 @@ static void acmp_add_node_to_parent(acmp_node_t *parent, acmp_node_t *child) { } } -/** - * Copies values from one node to another, without child/sibling/fail pointers - * and without state variables. - */ -static void acmp_clone_node_no_state(acmp_node_t *from, acmp_node_t *to) { - memcpy(to, from, sizeof(acmp_node_t)); - to->child = NULL; - to->sibling = NULL; - to->fail = NULL; - to->hit_count = 0; -} - static inline acmp_node_t *acmp_btree_find(acmp_node_t *node, acmp_utf8_char_t letter) { acmp_btree_node_t *bnode = node->btree; for (;;) { diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index ce97950f54..f410f40ed5 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* Copyright (c) 2004-2022 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 @@ -26,6 +26,9 @@ #include "msc_lua.h" #endif +#ifdef APLOG_USE_MODULE + APLOG_USE_MODULE(security2); +#endif /* -- Directory context creation and initialisation -- */ @@ -50,6 +53,8 @@ void *create_directory_config(apr_pool_t *mp, char *path) dcfg->reqbody_inmemory_limit = NOT_SET; dcfg->reqbody_limit = NOT_SET; dcfg->reqbody_no_files_limit = NOT_SET; + dcfg->reqbody_json_depth_limit = NOT_SET; + dcfg->arguments_limit = NOT_SET; dcfg->resbody_access = NOT_SET; dcfg->debuglog_name = NOT_SET_P; @@ -161,6 +166,7 @@ void *create_directory_config(apr_pool_t *mp, char *path) /* xml external entity */ dcfg->xml_external_entity = NOT_SET; + dcfg->parse_xml_into_args = NOT_SET; return dcfg; } @@ -174,6 +180,9 @@ static void copy_rules_phase(apr_pool_t *mp, apr_array_header_t *child_phase_arr, apr_array_header_t *exceptions_arr) { + assert(parent_phase_arr != NULL); + assert(child_phase_arr != NULL); + assert(exceptions_arr != NULL); rule_exception **exceptions; msre_rule **rules; int i, j; @@ -182,11 +191,14 @@ static void copy_rules_phase(apr_pool_t *mp, rules = (msre_rule **)parent_phase_arr->elts; for(i = 0; i < parent_phase_arr->nelts; i++) { msre_rule *rule = (msre_rule *)rules[i]; + assert(rule != NULL); + assert(rule->actionset != NULL); int copy = 1; if (mode == 0) { /* First rule in the chain. */ exceptions = (rule_exception **)exceptions_arr->elts; + assert(exceptions != NULL); for(j = 0; j < exceptions_arr->nelts; j++) { /* Process exceptions. */ @@ -234,7 +246,7 @@ static void copy_rules_phase(apr_pool_t *mp, if (copy > 0) { #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Copy rule %pp [id \"%s\"]", rule, rule->actionset->id); + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Copy rule %pp [id \"%s\"]", rule, id_log(rule)); #endif /* Copy the rule. */ @@ -246,7 +258,7 @@ static void copy_rules_phase(apr_pool_t *mp, } else { if (mode == 2) { #ifdef DEBUG_CONF - ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Copy chain %pp for rule %pp [id \"%s\"]", rule, rule->chain_starter, rule->chain_starter->actionset->id); + ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Copy chain %pp for rule %pp [id \"%s\"]", rule, rule->chain_starter, id_log(rule->chain_starter)); #endif /* Copy the rule (it belongs to the chain we want to include. */ @@ -272,18 +284,21 @@ static void copy_rules_phase(apr_pool_t *mp, * @retval -1 Something went wrong. * */ -static int copy_rules(apr_pool_t *mp, msre_ruleset *parent_ruleset, +static void copy_rules(apr_pool_t *mp, msre_ruleset *parent_ruleset, msre_ruleset *child_ruleset, apr_array_header_t *exceptions_arr) { - int ret = 0; - - if (parent_ruleset == NULL || child_ruleset == NULL || - exceptions_arr == NULL) { - ret = -1; - goto failed; - } - + assert(parent_ruleset != NULL); + assert(child_ruleset != NULL); + assert(exceptions_arr != NULL); + // Normally useless code, left to be safe for the moment + if (parent_ruleset == NULL || child_ruleset == NULL || exceptions_arr == NULL) { + if (parent_ruleset == NULL) ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, mp, "copy_rules: parent_ruleset is NULL"); + if (child_ruleset == NULL) ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, mp, "copy_rules: child_ruleset is NULL"); + if (exceptions_arr == NULL) ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, mp, "copy_rules: exceptions_arr is NULL"); + return; + } + copy_rules_phase(mp, parent_ruleset->phase_request_headers, child_ruleset->phase_request_headers, exceptions_arr); copy_rules_phase(mp, parent_ruleset->phase_request_body, @@ -294,9 +309,6 @@ static int copy_rules(apr_pool_t *mp, msre_ruleset *parent_ruleset, child_ruleset->phase_response_body, exceptions_arr); copy_rules_phase(mp, parent_ruleset->phase_logging, child_ruleset->phase_logging, exceptions_arr); - -failed: - return ret; } /** @@ -304,6 +316,8 @@ static int copy_rules(apr_pool_t *mp, msre_ruleset *parent_ruleset, */ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) { + assert(_parent != NULL); + assert(_child != NULL); directory_config *parent = (directory_config *)_parent; directory_config *child = (directory_config *)_child; directory_config *merged = create_directory_config(mp, NULL); @@ -332,6 +346,10 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) ? parent->reqbody_limit : child->reqbody_limit); merged->reqbody_no_files_limit = (child->reqbody_no_files_limit == NOT_SET ? parent->reqbody_no_files_limit : child->reqbody_no_files_limit); + merged->reqbody_json_depth_limit = (child->reqbody_json_depth_limit == NOT_SET + ? parent->reqbody_json_depth_limit : child->reqbody_json_depth_limit); + merged->arguments_limit = (child->arguments_limit == NOT_SET + ? parent->arguments_limit : child->arguments_limit); merged->resbody_access = (child->resbody_access == NOT_SET ? parent->resbody_access : child->resbody_access); @@ -415,7 +433,6 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) /* Copy the rules from the parent context. */ merged->ruleset = msre_ruleset_create(parent->ruleset->engine, mp); - /* TODO: copy_rules return code should be taken into consideration. */ copy_rules(mp, parent->ruleset, merged->ruleset, child->rule_exceptions); } else if (parent->ruleset == NULL) { @@ -442,7 +459,6 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) /* Copy parent rules, then add child rules to it. */ merged->ruleset = msre_ruleset_create(parent->ruleset->engine, mp); - /* TODO: copy_rules return code should be taken into consideration. */ copy_rules(mp, parent->ruleset, merged->ruleset, child->rule_exceptions); apr_array_cat(merged->ruleset->phase_request_headers, @@ -625,6 +641,8 @@ void *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child) /* xml external entity */ merged->xml_external_entity = (child->xml_external_entity == NOT_SET ? parent->xml_external_entity : child->xml_external_entity); + merged->parse_xml_into_args = (child->parse_xml_into_args == NOT_SET + ? parent->parse_xml_into_args : child->parse_xml_into_args); return merged; } @@ -648,6 +666,8 @@ void init_directory_config(directory_config *dcfg) dcfg->reqbody_inmemory_limit = REQUEST_BODY_DEFAULT_INMEMORY_LIMIT; if (dcfg->reqbody_limit == NOT_SET) dcfg->reqbody_limit = REQUEST_BODY_DEFAULT_LIMIT; if (dcfg->reqbody_no_files_limit == NOT_SET) dcfg->reqbody_no_files_limit = REQUEST_BODY_NO_FILES_DEFAULT_LIMIT; + if (dcfg->reqbody_json_depth_limit == NOT_SET) dcfg->reqbody_json_depth_limit = REQUEST_BODY_JSON_DEPTH_DEFAULT_LIMIT; + if (dcfg->arguments_limit == NOT_SET) dcfg->arguments_limit = ARGUMENTS_LIMIT; if (dcfg->resbody_access == NOT_SET) dcfg->resbody_access = 0; if (dcfg->of_limit == NOT_SET) dcfg->of_limit = RESPONSE_BODY_DEFAULT_LIMIT; if (dcfg->if_limit_action == NOT_SET) dcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_REJECT; @@ -732,8 +752,13 @@ void init_directory_config(directory_config *dcfg) if (dcfg->col_timeout == NOT_SET) dcfg->col_timeout = 3600; /* Hash */ - if (dcfg->crypto_key == NOT_SET_P) dcfg->crypto_key = getkey(dcfg->mp); - if (dcfg->crypto_key_len == NOT_SET) dcfg->crypto_key_len = strlen(dcfg->crypto_key); + if (dcfg->hash_is_enabled == HASH_ENABLED) { + if (dcfg->crypto_key == NOT_SET_P) dcfg->crypto_key = getkey(dcfg->mp); + if (dcfg->crypto_key_len == NOT_SET) dcfg->crypto_key_len = strlen(dcfg->crypto_key); + } else { + if (dcfg->crypto_key == NOT_SET_P) dcfg->crypto_key = ""; + if (dcfg->crypto_key_len == NOT_SET) dcfg->crypto_key_len = 0; + } if (dcfg->crypto_key_add == NOT_SET) dcfg->crypto_key_add = HASH_KEYONLY; if (dcfg->crypto_param_name == NOT_SET_P) dcfg->crypto_param_name = "crypt"; if (dcfg->hash_is_enabled == NOT_SET) dcfg->hash_is_enabled = HASH_DISABLED; @@ -751,6 +776,7 @@ void init_directory_config(directory_config *dcfg) /* xml external entity */ if (dcfg->xml_external_entity == NOT_SET) dcfg->xml_external_entity = 0; + if (dcfg->parse_xml_into_args == NOT_SET) dcfg->parse_xml_into_args = 0; } @@ -760,15 +786,17 @@ void init_directory_config(directory_config *dcfg) static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, int type, const char *p1, const char *p2, const char *p3) { + assert(cmd != NULL); + assert(dcfg != NULL); char *my_error_msg = NULL; //msre_rule *rule = NULL, *tmp_rule = NULL; char *rid = NULL; msre_rule *rule = NULL; extern msc_engine *modsecurity; + assert(modsecurity != NULL); int type_with_lua = 1; int type_rule; int rule_actionset; - int offset = 0; #ifdef DEBUG_CONF ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, @@ -890,14 +918,14 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, int type, */ rule->actionset = msre_actionset_merge(modsecurity->msre, cmd->pool, dcfg->tmp_default_actionset, rule->actionset, 1); + if (rule->actionset == NULL) return apr_psprintf(cmd->pool, "ModSecurity: cannot merge actionset (memory full?)."); /* Keep track of the parent action for "block" */ rule->actionset->parent_intercept_action_rec = dcfg->tmp_default_actionset->intercept_action_rec; rule->actionset->parent_intercept_action = dcfg->tmp_default_actionset->intercept_action; /* Must NOT specify a disruptive action in logging phase. */ - if ((rule->actionset != NULL) - && (rule->actionset->phase == PHASE_LOGGING) + if ( (rule->actionset->phase == PHASE_LOGGING) && (rule->actionset->intercept_action != ACTION_ALLOW) && (rule->actionset->intercept_action != ACTION_ALLOW_REQUEST) && (rule->actionset->intercept_action != ACTION_NONE) @@ -947,8 +975,7 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, int type, #ifdef DEBUG_CONF ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, - "Adding rule %pp phase=%d id=\"%s\".", rule, rule->actionset->phase, (rule->actionset->id == NOT_SET_P - ? "(none)" : rule->actionset->id)); + "Adding rule %pp phase=%d id=\"%s\".", rule, rule->actionset->phase, id_log(rule)); #endif /* Add rule to the recipe. */ @@ -993,9 +1020,12 @@ static const char *add_rule(cmd_parms *cmd, directory_config *dcfg, int type, static const char *add_marker(cmd_parms *cmd, directory_config *dcfg, const char *p1, const char *p2, const char *p3) { + assert(cmd != NULL); + assert(dcfg != NULL); char *my_error_msg = NULL; msre_rule *rule = NULL; extern msc_engine *modsecurity; + assert(modsecurity != NULL); int p; #ifdef DEBUG_CONF @@ -1022,8 +1052,7 @@ static const char *add_marker(cmd_parms *cmd, directory_config *dcfg, for (p = PHASE_FIRST; p <= PHASE_LAST; p++) { #ifdef DEBUG_CONF ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, - "Adding marker %pp phase=%d id=\"%s\".", rule, p, (rule->actionset->id == NOT_SET_P - ? "(none)" : rule->actionset->id)); + "Adding marker %pp phase=%d id=\"%s\".", rule, p, id_log(rule)); #endif if (msre_ruleset_rule_add(dcfg->ruleset, rule, p) < 0) { @@ -1045,11 +1074,14 @@ static const char *add_marker(cmd_parms *cmd, directory_config *dcfg, static const char *update_rule_action(cmd_parms *cmd, directory_config *dcfg, const char *p1, const char *p2, int offset) { + assert(cmd != NULL); + assert(dcfg != NULL); char *my_error_msg = NULL; msre_rule *rule = NULL; msre_actionset *new_actionset = NULL; msre_ruleset *ruleset = dcfg->ruleset; extern msc_engine *modsecurity; + assert(modsecurity != NULL); /* Get the ruleset if one exists */ if ((ruleset == NULL)||(ruleset == NOT_SET_P)) { @@ -1071,11 +1103,7 @@ static const char *update_rule_action(cmd_parms *cmd, directory_config *dcfg, return NULL; } - /* Check the rule actionset */ - /* ENH: Can this happen? */ - if (rule->actionset == NULL) { - return apr_psprintf(cmd->pool, "ModSecurity: Attempt to update action for rule \"%s\" failed: Rule does not have an actionset.", p1); - } + assert(rule->actionset != NULL); /* Create a new actionset */ new_actionset = msre_actionset_create(modsecurity->msre, cmd->pool, p2, &my_error_msg); @@ -1097,9 +1125,7 @@ static const char *update_rule_action(cmd_parms *cmd, directory_config *dcfg, char *actions = msre_actionset_generate_action_string(ruleset->mp, rule->actionset); ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Update rule %pp id=\"%s\" old action: \"%s\"", - rule, - (rule->actionset->id == NOT_SET_P ? "(none)" : rule->actionset->id), - actions); + rule, id_log(rule), actions); } #endif @@ -1107,6 +1133,7 @@ static const char *update_rule_action(cmd_parms *cmd, directory_config *dcfg, /* ENH: Will this leak the old actionset? */ rule->actionset = msre_actionset_merge(modsecurity->msre, cmd->pool, rule->actionset, new_actionset, 1); + if (rule->actionset == NULL) return apr_psprintf(cmd->pool, "ModSecurity: cannot merge actionset (memory full?)."); msre_actionset_set_defaults(rule->actionset); /* Update the unparsed rule */ @@ -1117,9 +1144,7 @@ static const char *update_rule_action(cmd_parms *cmd, directory_config *dcfg, char *actions = msre_actionset_generate_action_string(ruleset->mp, rule->actionset); ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Update rule %pp id=\"%s\" new action: \"%s\"", - rule, - (rule->actionset->id == NOT_SET_P ? "(none)" : rule->actionset->id), - actions); + rule, id_log(rule), actions); } #endif @@ -1135,6 +1160,12 @@ static const char *cmd_action(cmd_parms *cmd, void *_dcfg, const char *p1) static const char *cmd_marker(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(_dcfg != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_marker: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; const char *action = apr_pstrcat(dcfg->mp, SECMARKER_BASE_ACTIONS, p1, NULL); return add_marker(cmd, (directory_config *)_dcfg, SECMARKER_TARGETS, SECMARKER_ARGS, action); @@ -1143,6 +1174,14 @@ static const char *cmd_marker(cmd_parms *cmd, void *_dcfg, const char *p1) static const char *cmd_cookiev0_separator(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_cookiev0_separator: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; if (strlen(p1) != 1) { @@ -1157,6 +1196,14 @@ static const char *cmd_cookiev0_separator(cmd_parms *cmd, void *_dcfg, static const char *cmd_argument_separator(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_argument_separator: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; if (strlen(p1) != 1) { @@ -1170,6 +1217,9 @@ static const char *cmd_argument_separator(cmd_parms *cmd, void *_dcfg, static const char *cmd_audit_engine(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); directory_config *dcfg = _dcfg; if (strcasecmp(p1, "On") == 0) dcfg->auditlog_flag = AUDITLOG_ON; @@ -1186,6 +1236,9 @@ static const char *cmd_audit_engine(cmd_parms *cmd, void *_dcfg, const char *p1) static const char *cmd_audit_log(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); directory_config *dcfg = _dcfg; dcfg->auditlog_name = (char *)p1; @@ -1223,6 +1276,9 @@ static const char *cmd_audit_log(cmd_parms *cmd, void *_dcfg, const char *p1) static const char *cmd_audit_log2(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); directory_config *dcfg = _dcfg; if (dcfg->auditlog_name == NOT_SET_P) { @@ -1265,6 +1321,9 @@ static const char *cmd_audit_log2(cmd_parms *cmd, void *_dcfg, const char *p1) static const char *cmd_audit_log_parts(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); directory_config *dcfg = _dcfg; if (is_valid_parts_specification((char *)p1) != 1) { @@ -1278,9 +1337,16 @@ static const char *cmd_audit_log_parts(cmd_parms *cmd, void *_dcfg, static const char *cmd_audit_log_relevant_status(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); directory_config *dcfg = _dcfg; +#ifndef WITH_PCRE + dcfg->auditlog_relevant_regex = msc_pregcomp(cmd->pool, p1, PCRE2_DOTALL, NULL, NULL); +#else dcfg->auditlog_relevant_regex = msc_pregcomp(cmd->pool, p1, PCRE_DOTALL, NULL, NULL); +#endif if (dcfg->auditlog_relevant_regex == NULL) { return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p1); } @@ -1291,6 +1357,9 @@ static const char *cmd_audit_log_relevant_status(cmd_parms *cmd, void *_dcfg, static const char *cmd_audit_log_type(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); directory_config *dcfg = _dcfg; if (strcasecmp(p1, "Serial") == 0) dcfg->auditlog_type = AUDITLOG_SERIAL; @@ -1307,6 +1376,9 @@ static const char *cmd_audit_log_type(cmd_parms *cmd, void *_dcfg, static const char *cmd_audit_log_mode(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); directory_config *dcfg = _dcfg; if (strcasecmp(p1, "JSON") == 0) dcfg->auditlog_format = AUDITLOGFORMAT_JSON; @@ -1323,10 +1395,16 @@ static const char *cmd_audit_log_mode(cmd_parms *cmd, void *_dcfg, static const char *cmd_audit_log_dirmode(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_audit_log_dirmode: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - if (strcasecmp(p1, "default") == 0) { dcfg->auditlog_dirperms = NOT_SET; } @@ -1345,10 +1423,16 @@ static const char *cmd_audit_log_dirmode(cmd_parms *cmd, void *_dcfg, static const char *cmd_audit_log_filemode(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_audit_log_filemode: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - if (strcasecmp(p1, "default") == 0) { dcfg->auditlog_fileperms = NOT_SET; } @@ -1367,6 +1451,9 @@ static const char *cmd_audit_log_filemode(cmd_parms *cmd, void *_dcfg, static const char *cmd_audit_log_storage_dir(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); directory_config *dcfg = _dcfg; dcfg->auditlog_storage_dir = ap_server_root_relative(cmd->pool, p1); @@ -1377,6 +1464,9 @@ static const char *cmd_audit_log_storage_dir(cmd_parms *cmd, void *_dcfg, static const char *cmd_cookie_format(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); directory_config *dcfg = (directory_config *)_dcfg; if (strcmp(p1, "0") == 0) dcfg->cookie_format = COOKIES_V0; @@ -1391,6 +1481,8 @@ static const char *cmd_cookie_format(cmd_parms *cmd, void *_dcfg, static const char *cmd_chroot_dir(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(p1 != NULL); char cwd[1025] = ""; if (cmd->server->is_virtual) { @@ -1422,6 +1514,13 @@ static const char *cmd_chroot_dir(cmd_parms *cmd, void *_dcfg, const char *p1) static const char *cmd_component_signature(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_component_signature: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; /* ENH Enforce "Name/VersionX.Y.Z (comment)" format. */ @@ -1432,14 +1531,22 @@ static const char *cmd_component_signature(cmd_parms *cmd, void *_dcfg, static const char *cmd_content_injection(cmd_parms *cmd, void *_dcfg, int flag) { + assert(_dcfg != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_content_injection: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; dcfg->content_injection_enabled = flag; return NULL; } static const char *cmd_data_dir(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); directory_config *dcfg = (directory_config *)_dcfg; if (cmd->server->is_virtual) { @@ -1453,6 +1560,9 @@ static const char *cmd_data_dir(cmd_parms *cmd, void *_dcfg, const char *p1) static const char *cmd_debug_log(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); directory_config *dcfg = (directory_config *)_dcfg; apr_status_t rc; @@ -1483,6 +1593,9 @@ static const char *cmd_debug_log(cmd_parms *cmd, void *_dcfg, const char *p1) static const char *cmd_collection_timeout(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); directory_config *dcfg = (directory_config *)_dcfg; dcfg->col_timeout = atoi(p1); @@ -1495,6 +1608,9 @@ static const char *cmd_collection_timeout(cmd_parms *cmd, void *_dcfg, static const char *cmd_debug_log_level(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); directory_config *dcfg = (directory_config *)_dcfg; dcfg->debuglog_level = atoi(p1); @@ -1506,6 +1622,8 @@ static const char *cmd_debug_log_level(cmd_parms *cmd, void *_dcfg, static const char *cmd_default_action(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); directory_config *dcfg = (directory_config *)_dcfg; extern msc_engine *modsecurity; char *my_error_msg = NULL; @@ -1582,8 +1700,13 @@ static const char *cmd_default_action(cmd_parms *cmd, void *_dcfg, static const char *cmd_disable_backend_compression(cmd_parms *cmd, void *_dcfg, int flag) { - directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; + assert(_dcfg != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_disable_backend_compression: _dcfg is NULL"); + return NULL; + } + directory_config* dcfg = (directory_config*)_dcfg; dcfg->disable_backend_compression = flag; return NULL; } @@ -1591,6 +1714,8 @@ static const char *cmd_disable_backend_compression(cmd_parms *cmd, void *_dcfg, static const char *cmd_guardian_log(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2) { + assert(cmd != NULL); + assert(p1 != NULL); extern char *guardianlog_name; extern apr_file_t *guardianlog_fd; extern char *guardianlog_condition; @@ -1651,8 +1776,13 @@ static const char *cmd_guardian_log(cmd_parms *cmd, void *_dcfg, */ static const char *cmd_stream_inbody_inspection(cmd_parms *cmd, void *_dcfg, int flag) { + assert(_dcfg != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_stream_inbody_inspection: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; dcfg->stream_inbody_inspection = flag; return NULL; } @@ -1670,8 +1800,13 @@ static const char *cmd_stream_inbody_inspection(cmd_parms *cmd, void *_dcfg, int */ static const char *cmd_stream_outbody_inspection(cmd_parms *cmd, void *_dcfg, int flag) { + assert(_dcfg != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_stream_outbody_inspection: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; dcfg->stream_outbody_inspection = flag; return NULL; } @@ -1688,11 +1823,17 @@ static const char *cmd_stream_outbody_inspection(cmd_parms *cmd, void *_dcfg, in static const char *cmd_rule_perf_time(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_rule_perf_time: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; long int limit; - if (dcfg == NULL) return NULL; - limit = strtol(p1, NULL, 10); if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRulePerfTime: %s", p1); @@ -1707,15 +1848,28 @@ char *parser_conn_limits_operator(apr_pool_t *mp, const char *p2, TreeRoot **whitelist, TreeRoot **suspicious_list, const char *filename) { + assert(p2 != NULL); + assert(whitelist != NULL); + assert(suspicious_list != NULL); + assert(filename != NULL); int res = 0; char *config_orig_path; char *param = strchr(p2, ' '); char *file = NULL; char *error_msg = NULL; + + if (param == NULL) { + return apr_psprintf(mp, "ModSecurity: Space character between operator " \ + "and parameter not found with ConnReadStateLimit: %s", p2); + } + param++; config_orig_path = apr_pstrndup(mp, filename, strlen(filename) - strlen(apr_filepath_name_get(filename))); + if (config_orig_path == NULL) { + return apr_psprintf(mp, "ModSecurity: failed to duplicate filename in parser_conn_limits_operator"); + } apr_filepath_merge(&file, config_orig_path, param, APR_FILEPATH_TRUENAME, mp); @@ -1772,11 +1926,16 @@ char *parser_conn_limits_operator(apr_pool_t *mp, const char *p2, static const char *cmd_conn_read_state_limit(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2) { - directory_config *dcfg = (directory_config *)_dcfg; + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_conn_read_state_limit: _dcfg is NULL"); + return NULL; + } long int limit; - if (dcfg == NULL) return NULL; - limit = strtol(p1, NULL, 10); if ((limit == LONG_MAX) || (limit == LONG_MIN) || (limit <= 0)) { return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for " \ @@ -1800,6 +1959,7 @@ static const char *cmd_conn_read_state_limit(cmd_parms *cmd, void *_dcfg, static const char *cmd_read_state_limit(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2) { + assert(cmd != NULL); ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "SecReadStateLimit is depricated, use SecConnReadStateLimit " \ "instead."); @@ -1821,11 +1981,16 @@ static const char *cmd_read_state_limit(cmd_parms *cmd, void *_dcfg, static const char *cmd_conn_write_state_limit(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2) { - directory_config *dcfg = (directory_config *)_dcfg; + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_conn_write_state_limit: _dcfg is NULL"); + return NULL; + } long int limit; - if (dcfg == NULL) return NULL; - limit = strtol(p1, NULL, 10); if ((limit == LONG_MAX) || (limit == LONG_MIN) || (limit <= 0)) { return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for " \ @@ -1848,6 +2013,7 @@ static const char *cmd_conn_write_state_limit(cmd_parms *cmd, void *_dcfg, static const char *cmd_write_state_limit(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2) { + assert(cmd != NULL); ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "SecWriteStateLimit is depricated, use SecConnWriteStateLimit " \ "instead."); @@ -1860,11 +2026,17 @@ static const char *cmd_write_state_limit(cmd_parms *cmd, void *_dcfg, static const char *cmd_request_body_inmemory_limit(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_request_body_inmemory_limit: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; long int limit; - if (dcfg == NULL) return NULL; - limit = strtol(p1, NULL, 10); if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyInMemoryLimit: %s", p1); @@ -1878,11 +2050,17 @@ static const char *cmd_request_body_inmemory_limit(cmd_parms *cmd, void *_dcfg, static const char *cmd_request_body_limit(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_request_body_limit: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; long int limit; - if (dcfg == NULL) return NULL; - limit = strtol(p1, NULL, 10); if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyLimit: %s", p1); @@ -1896,11 +2074,17 @@ static const char *cmd_request_body_limit(cmd_parms *cmd, void *_dcfg, static const char *cmd_request_body_no_files_limit(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_request_body_no_files_limit: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; long int limit; - if (dcfg == NULL) return NULL; - limit = strtol(p1, NULL, 10); if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyNoFilesLimit: %s", p1); @@ -1911,12 +2095,67 @@ static const char *cmd_request_body_no_files_limit(cmd_parms *cmd, void *_dcfg, return NULL; } +static const char *cmd_request_body_json_depth_limit(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_request_body_json_depth_limit: _dcfg is NULL"); + return NULL; + } + directory_config *dcfg = (directory_config *)_dcfg; + long int limit; + + limit = strtol(p1, NULL, 10); + if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyJsonDepthLimit: %s", p1); + } + + dcfg->reqbody_json_depth_limit = limit; + + return NULL; +} + +static const char *cmd_arguments_limit(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_arguments_limit: _dcfg is NULL"); + return NULL; + } + directory_config *dcfg = (directory_config *)_dcfg; + long int limit; + + limit = strtol(p1, NULL, 10); + if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecArgumentsLimit: %s", p1); + } + + dcfg->arguments_limit = limit; + + return NULL; +} + static const char *cmd_request_body_access(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_request_body_access: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - + if (strcasecmp(p1, "on") == 0) dcfg->reqbody_access = 1; else if (strcasecmp(p1, "off") == 0) dcfg->reqbody_access = 0; @@ -1939,9 +2178,16 @@ static const char *cmd_request_body_access(cmd_parms *cmd, void *_dcfg, static const char *cmd_request_intercept_on_error(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_request_intercept_on_error: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - + if (strcasecmp(p1, "on") == 0) dcfg->reqintercept_oe = 1; else if (strcasecmp(p1, "off") == 0) dcfg->reqintercept_oe = 0; @@ -1955,11 +2201,15 @@ static const char *cmd_request_intercept_on_error(cmd_parms *cmd, void *_dcfg, static const char *cmd_request_encoding(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(_dcfg != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_request_encoding: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - + /* ENH Validate encoding */ - dcfg->request_encoding = p1; return NULL; @@ -1968,9 +2218,16 @@ static const char *cmd_request_encoding(cmd_parms *cmd, void *_dcfg, static const char *cmd_response_body_access(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_response_body_access: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - + if (strcasecmp(p1, "on") == 0) dcfg->resbody_access = 1; else if (strcasecmp(p1, "off") == 0) dcfg->resbody_access = 0; @@ -1983,6 +2240,14 @@ static const char *cmd_response_body_access(cmd_parms *cmd, void *_dcfg, static const char *cmd_response_body_limit(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_response_body_limit: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; long int limit; @@ -2003,9 +2268,16 @@ static const char *cmd_response_body_limit(cmd_parms *cmd, void *_dcfg, static const char *cmd_response_body_limit_action(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_response_body_limit_action: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - + if (dcfg->is_enabled == MODSEC_DETECTION_ONLY) { dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_PARTIAL; return NULL; @@ -2033,9 +2305,16 @@ static const char *cmd_response_body_limit_action(cmd_parms *cmd, void *_dcfg, static const char *cmd_resquest_body_limit_action(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_resquest_body_limit_action: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - + if (dcfg->is_enabled == MODSEC_DETECTION_ONLY) { dcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_PARTIAL; return NULL; @@ -2053,6 +2332,14 @@ static const char *cmd_resquest_body_limit_action(cmd_parms *cmd, void *_dcfg, static const char *cmd_response_body_mime_type(cmd_parms *cmd, void *_dcfg, const char *_p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(_p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_response_body_mime_type: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; char *p1 = apr_pstrdup(cmd->pool, _p1); @@ -2071,9 +2358,14 @@ static const char *cmd_response_body_mime_type(cmd_parms *cmd, void *_dcfg, static const char *cmd_response_body_mime_types_clear(cmd_parms *cmd, void *_dcfg) { + assert(_dcfg != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_response_body_mime_types_clear: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - + dcfg->of_mime_types_cleared = 1; if ((dcfg->of_mime_types != NULL)&&(dcfg->of_mime_types != NOT_SET_P)) { @@ -2097,10 +2389,16 @@ static const char *cmd_response_body_mime_types_clear(cmd_parms *cmd, static const char *cmd_rule_update_target_by_id(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2, const char *p3) { + assert(cmd != NULL); + assert(_dcfg != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_rule_update_target_by_id: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); - if (dcfg == NULL) return NULL; - + if(p1 == NULL) { return apr_psprintf(cmd->pool, "Updating target by ID with no ID"); } @@ -2134,10 +2432,16 @@ static const char *cmd_rule_update_target_by_id(cmd_parms *cmd, void *_dcfg, static const char *cmd_rule_update_target_by_tag(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2, const char *p3) { + assert(cmd != NULL); + assert(_dcfg != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_rule_update_target_by_tag: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); - if (dcfg == NULL) return NULL; - + if(p1 == NULL) { return apr_psprintf(cmd->pool, "Updating target by tag with no tag"); } @@ -2169,10 +2473,17 @@ static const char *cmd_rule_update_target_by_tag(cmd_parms *cmd, void *_dcfg, static const char *cmd_rule_update_target_by_msg(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2, const char *p3) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_rule_update_target_by_msg: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); - if (dcfg == NULL) return NULL; - + if(p1 == NULL) { return apr_psprintf(cmd->pool, "Updating target by message with no message"); } @@ -2197,9 +2508,14 @@ static const char *cmd_rule(cmd_parms *cmd, void *_dcfg, static const char *cmd_sever_conn_filters_engine(cmd_parms *cmd, void *_dcfg, const char *p1) { - directory_config *dcfg = (directory_config *)_dcfg; - - if (dcfg == NULL) return NULL; + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_sever_conn_filters_engine: _dcfg is NULL"); + return NULL; + } if (strcasecmp(p1, "on") == 0) { @@ -2224,10 +2540,16 @@ static const char *cmd_sever_conn_filters_engine(cmd_parms *cmd, void *_dcfg, static const char *cmd_rule_engine(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_rule_engine: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - if (strcasecmp(p1, "on") == 0) { dcfg->is_enabled = MODSEC_ENABLED; @@ -2253,8 +2575,15 @@ static const char *cmd_rule_engine(cmd_parms *cmd, void *_dcfg, const char *p1) static const char *cmd_remote_rules_fail(cmd_parms *cmd, void *_dcfg, const char *p1) { - directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_remote_rules_fail: _dcfg is NULL"); + return NULL; + } + if (strncasecmp(p1, "warn", 4) == 0) { remote_rules_fail_action = REMOTE_RULES_WARN_ON_FAIL; @@ -2275,7 +2604,15 @@ static const char *cmd_remote_rules_fail(cmd_parms *cmd, void *_dcfg, const char static const char *cmd_remote_rules(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2, const char *p3) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); char *error_msg = NULL; + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_remote_rules: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; #ifdef WITH_REMOTE_RULES int crypto = 0; @@ -2283,8 +2620,6 @@ static const char *cmd_remote_rules(cmd_parms *cmd, void *_dcfg, const char *p1, const char *key = p1; #endif - if (dcfg == NULL) return NULL; - #ifdef WITH_REMOTE_RULES if (strncasecmp(p1, "crypto", 6) == 0) { @@ -2347,6 +2682,8 @@ static const char *cmd_remote_rules(cmd_parms *cmd, void *_dcfg, const char *p1, static const char *cmd_status_engine(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(p1 != NULL); if (strcasecmp(p1, "on") == 0) { status_engine_state = STATUS_ENGINE_ENABLED; } @@ -2364,8 +2701,13 @@ static const char *cmd_status_engine(cmd_parms *cmd, void *_dcfg, const char *p1 static const char *cmd_rule_inheritance(cmd_parms *cmd, void *_dcfg, int flag) { + assert(_dcfg != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_rule_inheritance: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; dcfg->rule_inheritance = flag; return NULL; } @@ -2373,7 +2715,9 @@ static const char *cmd_rule_inheritance(cmd_parms *cmd, void *_dcfg, int flag) static const char *cmd_rule_script(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2) { - #if defined(WITH_LUA) + assert(cmd != NULL); + assert(p1 != NULL); +#if defined(WITH_LUA) const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1); return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_LUA, filename, p2, NULL); #else @@ -2385,9 +2729,20 @@ static const char *cmd_rule_script(cmd_parms *cmd, void *_dcfg, static const char *cmd_rule_remove_by_id(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_rule_remove_by_id: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); - if (dcfg == NULL) return NULL; + rule_exception* re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); + if (re == NULL) { + ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_NOERRNO, 0, cmd->pool, "cmd_rule_remove_by_id: Cannot allocate memory"); + return NULL; + } re->type = RULE_EXCEPTION_REMOVE_ID; re->param = p1; @@ -2412,10 +2767,21 @@ static const char *cmd_rule_remove_by_id(cmd_parms *cmd, void *_dcfg, static const char *cmd_rule_remove_by_tag(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_rule_remove_by_tag: _dcfg is NULL"); + return NULL; + } + if (p1 == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_rule_remove_by_tag: p1 is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); - if (dcfg == NULL) return NULL; - + re->type = RULE_EXCEPTION_REMOVE_TAG; re->param = p1; re->param_data = msc_pregcomp(cmd->pool, p1, 0, NULL, NULL); @@ -2437,10 +2803,17 @@ static const char *cmd_rule_remove_by_tag(cmd_parms *cmd, void *_dcfg, static const char *cmd_rule_remove_by_msg(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_rule_remove_by_msg: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; rule_exception *re = apr_pcalloc(cmd->pool, sizeof(rule_exception)); - if (dcfg == NULL) return NULL; - + re->type = RULE_EXCEPTION_REMOVE_MSG; re->param = p1; re->param_data = msc_pregcomp(cmd->pool, p1, 0, NULL, NULL); @@ -2462,6 +2835,8 @@ static const char *cmd_rule_remove_by_msg(cmd_parms *cmd, void *_dcfg, static const char *cmd_rule_update_action_by_id(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2) { + assert(cmd != NULL); + assert(p1 != NULL); int offset = 0, rule_id = atoi(p1); char *opt = strchr(p1,':'); char *savedptr = NULL; @@ -2484,6 +2859,8 @@ static const char *cmd_rule_update_action_by_id(cmd_parms *cmd, void *_dcfg, static const char *cmd_server_signature(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(p1 != NULL); if (cmd->server->is_virtual) { return "ModSecurity: SecServerSignature not allowed in VirtualHost"; } @@ -2493,10 +2870,16 @@ static const char *cmd_server_signature(cmd_parms *cmd, void *_dcfg, static const char *cmd_tmp_dir(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_tmp_dir: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - if (strcasecmp(p1, "none") == 0) dcfg->tmp_dir = NULL; else dcfg->tmp_dir = ap_server_root_relative(cmd->pool, p1); @@ -2505,10 +2888,16 @@ static const char *cmd_tmp_dir(cmd_parms *cmd, void *_dcfg, const char *p1) static const char *cmd_upload_dir(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_upload_dir: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - if (strcasecmp(p1, "none") == 0) dcfg->upload_dir = NULL; else dcfg->upload_dir = ap_server_root_relative(cmd->pool, p1); @@ -2518,10 +2907,16 @@ static const char *cmd_upload_dir(cmd_parms *cmd, void *_dcfg, const char *p1) static const char *cmd_upload_file_limit(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_upload_file_limit: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - if (strcasecmp(p1, "default") == 0) { dcfg->upload_file_limit = NOT_SET; } @@ -2535,10 +2930,16 @@ static const char *cmd_upload_file_limit(cmd_parms *cmd, void *_dcfg, static const char *cmd_upload_filemode(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_upload_filemode: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - if (strcasecmp(p1, "default") == 0) { dcfg->upload_filemode = NOT_SET; } @@ -2557,10 +2958,16 @@ static const char *cmd_upload_filemode(cmd_parms *cmd, void *_dcfg, static const char *cmd_upload_keep_files(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_upload_keep_files: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - if (strcasecmp(p1, "on") == 0) { dcfg->upload_keep_files = KEEP_FILES_ON; } else @@ -2579,10 +2986,16 @@ static const char *cmd_upload_keep_files(cmd_parms *cmd, void *_dcfg, static const char *cmd_upload_save_tmp_files(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_upload_save_tmp_files: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - if (strcasecmp(p1, "on") == 0) { dcfg->upload_validates_files = 1; @@ -2602,6 +3015,14 @@ static const char *cmd_upload_save_tmp_files(cmd_parms *cmd, void *_dcfg, static const char *cmd_web_app_id(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_web_app_id: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; /* ENH enforce format (letters, digits, ., _, -) */ @@ -2612,6 +3033,14 @@ static const char *cmd_web_app_id(cmd_parms *cmd, void *_dcfg, const char *p1) static const char *cmd_sensor_id(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_sensor_id: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; /* ENH enforce format (letters, digits, ., _, -) */ @@ -2632,9 +3061,15 @@ static const char *cmd_sensor_id(cmd_parms *cmd, void *_dcfg, const char *p1) */ static const char *cmd_xml_external_entity(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_xml_external_entity: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - if (strcasecmp(p1, "on") == 0) { dcfg->xml_external_entity = 1; } @@ -2659,9 +3094,15 @@ static const char *cmd_xml_external_entity(cmd_parms *cmd, void *_dcfg, const ch */ static const char *cmd_hash_engine(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_hash_engine: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - if (strcasecmp(p1, "on") == 0) { dcfg->hash_is_enabled = HASH_ENABLED; dcfg->hash_enforcement = HASH_ENABLED; @@ -2676,7 +3117,7 @@ static const char *cmd_hash_engine(cmd_parms *cmd, void *_dcfg, const char *p1) } /** -* \brief Add SecHashPram configuration option +* \brief Add SecHashParam configuration option * * \param cmd Pointer to configuration data * \param _dcfg Pointer to directory configuration @@ -2686,11 +3127,20 @@ static const char *cmd_hash_engine(cmd_parms *cmd, void *_dcfg, const char *p1) */ static const char *cmd_hash_param(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_hash_param: _dcfg is NULL"); + return NULL; + } + if (p1 == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_hash_param: p1 is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - - if (p1 == NULL) return NULL; dcfg->crypto_param_name = p1; return NULL; @@ -2708,12 +3158,26 @@ static const char *cmd_hash_param(cmd_parms *cmd, void *_dcfg, const char *p1) */ static const char *cmd_hash_key(cmd_parms *cmd, void *_dcfg, const char *_p1, const char *_p2) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(_p1 != NULL); + assert(_p2 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_hash_key: _dcfg is NULL"); + return NULL; + } + if (_p1 == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_hash_key: _p1 is NULL"); + return NULL; + } + if (_p2 == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_hash_key: _p2 is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; char *p1 = NULL; - if (dcfg == NULL) return NULL; - if (_p1 == NULL) return NULL; - if (strcasecmp(_p1, "Rand") == 0) { p1 = apr_pstrdup(cmd->pool, getkey(cmd->pool)); dcfg->crypto_key = p1; @@ -2724,16 +3188,13 @@ static const char *cmd_hash_key(cmd_parms *cmd, void *_dcfg, const char *_p1, co dcfg->crypto_key_len = strlen(p1); } - if(_p2 == NULL) { - return NULL; - } else { - if (strcasecmp(_p2, "KeyOnly") == 0) - dcfg->crypto_key_add = HASH_KEYONLY; - else if (strcasecmp(_p2, "SessionID") == 0) - dcfg->crypto_key_add = HASH_SESSIONID; - else if (strcasecmp(_p2, "RemoteIP") == 0) - dcfg->crypto_key_add = HASH_REMOTEIP; - } + if (strcasecmp(_p2, "KeyOnly") == 0) + dcfg->crypto_key_add = HASH_KEYONLY; + else if (strcasecmp(_p2, "SessionID") == 0) + dcfg->crypto_key_add = HASH_SESSIONID; + else if (strcasecmp(_p2, "RemoteIP") == 0) + dcfg->crypto_key_add = HASH_REMOTEIP; + return NULL; } @@ -2751,6 +3212,19 @@ static const char *cmd_hash_key(cmd_parms *cmd, void *_dcfg, const char *_p1, co static const char *cmd_hash_method_pm(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + assert(p2 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_hash_method_pm: _dcfg is NULL"); + return NULL; + } + if (p1 == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_hash_method_pm: p1 is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; rule_exception *re = apr_pcalloc(cmd->pool, sizeof(hash_method)); const char *_p2 = apr_pstrdup(cmd->pool, p2); @@ -2758,8 +3232,6 @@ static const char *cmd_hash_method_pm(cmd_parms *cmd, void *_dcfg, const char *phrase = NULL; const char *next = NULL; - if (dcfg == NULL) return NULL; - p = acmp_create(0, cmd->pool); if (p == NULL) return NULL; @@ -2842,11 +3314,19 @@ static const char *cmd_hash_method_pm(cmd_parms *cmd, void *_dcfg, static const char *cmd_hash_method_rx(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + assert(p2 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_hash_method_rx: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; rule_exception *re = apr_pcalloc(cmd->pool, sizeof(hash_method)); const char *_p2 = apr_pstrdup(cmd->pool, p2); - if (dcfg == NULL) return NULL; - + if (strcasecmp(p1, "HashHref") == 0) { re->type = HASH_URL_HREF_HASH_RX; re->param = _p2; @@ -2909,11 +3389,20 @@ static const char *cmd_hash_method_rx(cmd_parms *cmd, void *_dcfg, */ static const char *cmd_httpBl_key(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_httpBl_key: _dcfg is NULL"); + return NULL; + } + if (p1 == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_httpBl_key: p1 is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - - if (p1 == NULL) return NULL; dcfg->httpBlkey = p1; return NULL; @@ -2924,6 +3413,13 @@ static const char *cmd_httpBl_key(cmd_parms *cmd, void *_dcfg, const char *p1) static const char *cmd_pcre_match_limit(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (p1 == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_pcre_match_limit: p1 is NULL"); + return NULL; + } long val; if (cmd->server->is_virtual) { @@ -2943,6 +3439,13 @@ static const char *cmd_pcre_match_limit(cmd_parms *cmd, static const char *cmd_pcre_match_limit_recursion(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (p1 == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_pcre_match_limit_recursion: p1 is NULL"); + return NULL; + } long val; if (cmd->server->is_virtual) { @@ -2965,11 +3468,22 @@ static const char *cmd_pcre_match_limit_recursion(cmd_parms *cmd, static const char *cmd_geo_lookup_db(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(p1 != NULL); + assert(_dcfg != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_geo_lookup_db: _dcfg is NULL"); + return NULL; + } + if (p1 == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_geo_lookup_db: p1 is NULL"); + return NULL; + } const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1); char *error_msg; directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - + if (geo_init(dcfg, filename, &error_msg) <= 0) { return error_msg; } @@ -2991,6 +3505,8 @@ static const char *cmd_geo_lookup_db(cmd_parms *cmd, void *_dcfg, static const char *cmd_unicode_codepage(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(p1 != NULL); long val; val = atol(p1); @@ -3016,12 +3532,19 @@ static const char *cmd_unicode_codepage(cmd_parms *cmd, static const char *cmd_unicode_map(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2) { + assert(cmd != NULL); + assert(p1 != NULL); + assert(p2 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_unicode_map: _dcfg is NULL"); + return NULL; + } const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1); char *error_msg; long val = 0; directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - + if(p2 != NULL) { val = atol(p2); if (val <= 0) { @@ -3051,11 +3574,12 @@ static const char *cmd_unicode_map(cmd_parms *cmd, void *_dcfg, static const char *cmd_gsb_lookup_db(cmd_parms *cmd, void *_dcfg, const char *p1) { + assert(cmd != NULL); + assert(p1 != NULL); const char *filename = resolve_relative_path(cmd->pool, cmd->directive->filename, p1); char *error_msg; directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - + if (gsb_db_init(dcfg, filename, &error_msg) <= 0) { return error_msg; } @@ -3068,10 +3592,16 @@ static const char *cmd_gsb_lookup_db(cmd_parms *cmd, void *_dcfg, static const char *cmd_cache_transformations(cmd_parms *cmd, void *_dcfg, const char *p1, const char *p2) { + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_cache_transformations: _dcfg is NULL"); + return NULL; + } directory_config *dcfg = (directory_config *)_dcfg; - if (dcfg == NULL) return NULL; - if (strcasecmp(p1, "on") == 0) dcfg->cache_trans = MODSEC_CACHE_ENABLED; else if (strcasecmp(p1, "off") == 0) @@ -3167,6 +3697,34 @@ static const char *cmd_cache_transformations(cmd_parms *cmd, void *_dcfg, return NULL; } +/** +* \brief Add SecParseXmlIntoArgs configuration option +* +* \param cmd Pointer to configuration data +* \param _dcfg Pointer to directory configuration +* \param p1 Pointer to configuration option +* +* \retval NULL On Success +* \retval apr_psprintf On error +*/ +static const char *cmd_parse_xml_into_args(cmd_parms *cmd, void *_dcfg, const char *p1) +{ + assert(cmd != NULL); + assert(_dcfg != NULL); + assert(p1 != NULL); + // Normally useless code, left to be safe for the moment + if (_dcfg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, cmd->pool, "cmd_parse_xml_into_args: _dcfg is NULL"); + return NULL; + } + directory_config *dcfg = (directory_config *)_dcfg; + if (strcasecmp(p1, "on") == 0) { dcfg->parse_xml_into_args = MSC_XML_ARGS_ON; } + else if (strcasecmp(p1, "off") == 0) { dcfg->parse_xml_into_args = MSC_XML_ARGS_OFF; } + else if (strcasecmp(p1, "onlyargs") == 0) { dcfg->parse_xml_into_args = MSC_XML_ARGS_ONLYARGS; } + else return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecParseXmlIntoArgs: %s", p1); + + return NULL; +} /* -- Configuration directives definitions -- */ @@ -3544,6 +4102,22 @@ const command_rec module_directives[] = { "maximum request body size ModSecurity will accept, but excluding the size of uploaded files." ), + AP_INIT_TAKE1 ( + "SecRequestBodyJsonDepthLimit", + cmd_request_body_json_depth_limit, + NULL, + CMD_SCOPE_ANY, + "maximum request body JSON parsing depth ModSecurity will accept." + ), + + AP_INIT_TAKE1 ( + "SecArgumentsLimit", + cmd_arguments_limit, + NULL, + CMD_SCOPE_ANY, + "maximum number of ARGS that ModSecurity will accept." + ), + AP_INIT_TAKE1 ( "SecRequestEncoding", cmd_request_encoding, @@ -3919,5 +4493,13 @@ const command_rec module_directives[] = { "Set Hash parameter" ), + AP_INIT_TAKE1 ( + "SecParseXmlIntoArgs", + cmd_parse_xml_into_args, + NULL, + CMD_SCOPE_ANY, + "On, Off or OnlyArgs" + ), + { NULL } }; diff --git a/apache2/apache2_io.c b/apache2/apache2_io.c index c14dd41318..8deeb01c9a 100644 --- a/apache2/apache2_io.c +++ b/apache2/apache2_io.c @@ -18,6 +18,10 @@ #include "apache2.h" #include "msc_crypt.h" +#ifdef APLOG_USE_MODULE + APLOG_USE_MODULE(security2); +#endif + /* -- Input filter -- */ #if 0 @@ -36,6 +40,7 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, msc_data_chunk *chunk = NULL; apr_bucket *bucket; apr_status_t rc; + int no_data = 1; char *my_error_msg = NULL; if (msr == NULL) { @@ -85,10 +90,11 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, return APR_EGENERAL; } - if (chunk && (!msr->txcfg->stream_inbody_inspection || (msr->txcfg->stream_inbody_inspection && msr->if_stream_changed == 0))) { - /* Copy the data we received in the chunk */ - bucket = apr_bucket_heap_create(chunk->data, chunk->length, NULL, - f->r->connection->bucket_alloc); + if (chunk && chunk->length > 0) { + if (chunk && (!msr->txcfg->stream_inbody_inspection || (msr->txcfg->stream_inbody_inspection && msr->if_stream_changed == 0))) { + /* Copy the data we received in the chunk */ + bucket = apr_bucket_heap_create(chunk->data, chunk->length, NULL, + f->r->connection->bucket_alloc); #if 0 @@ -107,33 +113,36 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, #endif - if (bucket == NULL) return APR_EGENERAL; - APR_BRIGADE_INSERT_TAIL(bb_out, bucket); + if (bucket == NULL) return APR_EGENERAL; + APR_BRIGADE_INSERT_TAIL(bb_out, bucket); + no_data = 0; - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input filter: Forwarded %" APR_SIZE_T_FMT " bytes.", chunk->length); - } - } else if (msr->stream_input_data != NULL) { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Input filter: Forwarded %" APR_SIZE_T_FMT " bytes.", chunk->length); + } + } else if (msr->stream_input_data != NULL) { - msr->if_stream_changed = 0; + msr->if_stream_changed = 0; - bucket = apr_bucket_heap_create(msr->stream_input_data, msr->stream_input_length, NULL, - f->r->connection->bucket_alloc); + bucket = apr_bucket_heap_create(msr->stream_input_data, msr->stream_input_length, NULL, + f->r->connection->bucket_alloc); - if (msr->txcfg->stream_inbody_inspection) { - if(msr->stream_input_data != NULL) { - free(msr->stream_input_data); - msr->stream_input_data = NULL; + if (msr->txcfg->stream_inbody_inspection) { + if(msr->stream_input_data != NULL) { + free(msr->stream_input_data); + msr->stream_input_data = NULL; + } } - } - if (bucket == NULL) return APR_EGENERAL; - APR_BRIGADE_INSERT_TAIL(bb_out, bucket); + if (bucket == NULL) return APR_EGENERAL; + APR_BRIGADE_INSERT_TAIL(bb_out, bucket); + no_data = 0; + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Input stream filter: Forwarded %" APR_SIZE_T_FMT " bytes.", msr->stream_input_length); + } - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "Input stream filter: Forwarded %" APR_SIZE_T_FMT " bytes.", msr->stream_input_length); } - } if (rc == 0) { @@ -143,6 +152,7 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, bucket = apr_bucket_eos_create(f->r->connection->bucket_alloc); if (bucket == NULL) return APR_EGENERAL; APR_BRIGADE_INSERT_TAIL(bb_out, bucket); + no_data = 0; if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Input filter: Sent EOS."); @@ -156,6 +166,10 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Input filter: Input forwarding complete."); } + + if (no_data) { + return ap_get_brigade(f->next, bb_out, mode, block, nbytes); + } } return APR_SUCCESS; @@ -165,40 +179,42 @@ apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out, * Reads request body from a client. */ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { + assert(msr != NULL); + assert(error_msg!= NULL); request_rec *r = msr->r; unsigned int finished_reading; apr_bucket_brigade *bb_in; apr_bucket *bucket; - if (error_msg == NULL) return -1; *error_msg = NULL; if (msr->reqbody_should_exist != 1) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Input filter: This request does not have a body."); } - return 0; + return APR_SUCCESS; } if (msr->txcfg->reqbody_access != 1) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Input filter: Request body access not enabled."); } - return 0; + return APR_SUCCESS; } if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Input filter: Reading request body."); } - if (modsecurity_request_body_start(msr, error_msg) < 0) { - return -1; + return HTTP_INTERNAL_SERVER_ERROR; } finished_reading = 0; msr->if_seen_eos = 0; bb_in = apr_brigade_create(msr->mp, r->connection->bucket_alloc); - if (bb_in == NULL) return -1; + if (bb_in == NULL) { + return HTTP_INTERNAL_SERVER_ERROR; + } do { apr_status_t rc; @@ -208,25 +224,17 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { * too large and APR_EGENERAL when the client disconnects. */ switch(rc) { - case APR_INCOMPLETE : - *error_msg = apr_psprintf(msr->mp, "Error reading request body: %s", get_apr_error(msr->mp, rc)); - return -7; - case APR_EOF : - *error_msg = apr_psprintf(msr->mp, "Error reading request body: %s", get_apr_error(msr->mp, rc)); - return -6; - case APR_TIMEUP : - *error_msg = apr_psprintf(msr->mp, "Error reading request body: %s", get_apr_error(msr->mp, rc)); - return -4; case AP_FILTER_ERROR : *error_msg = apr_psprintf(msr->mp, "Error reading request body: HTTP Error 413 - Request entity too large. (Most likely.)"); - return -3; + break; case APR_EGENERAL : *error_msg = apr_psprintf(msr->mp, "Error reading request body: Client went away."); - return -2; + break; default : *error_msg = apr_psprintf(msr->mp, "Error reading request body: %s", get_apr_error(msr->mp, rc)); - return -1; + break; } + return ap_map_http_request_error(rc, HTTP_BAD_REQUEST); } /* Loop through the buckets in the brigade in order @@ -242,7 +250,7 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { rc = apr_bucket_read(bucket, &buf, &buflen, APR_BLOCK_READ); if (rc != APR_SUCCESS) { *error_msg = apr_psprintf(msr->mp, "Failed reading input / bucket (%d): %s", rc, get_apr_error(msr->mp, rc)); - return -1; + return HTTP_INTERNAL_SERVER_ERROR; } if (msr->txcfg->debuglog_level >= 9) { @@ -255,7 +263,7 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) { *error_msg = apr_psprintf(msr->mp, "Request body is larger than the " "configured limit (%ld).", msr->txcfg->reqbody_limit); - return -5; + return HTTP_REQUEST_ENTITY_TOO_LARGE; } else if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)) { *error_msg = apr_psprintf(msr->mp, "Request body is larger than the " @@ -276,13 +284,19 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { *error_msg = apr_psprintf(msr->mp, "Request body is larger than the " "configured limit (%ld).", msr->txcfg->reqbody_limit); - return -5; + return HTTP_REQUEST_ENTITY_TOO_LARGE; } } if (msr->txcfg->stream_inbody_inspection == 1) { +#ifndef MSC_LARGE_STREAM_INPUT msr->stream_input_length+=buflen; modsecurity_request_body_to_stream(msr, buf, buflen, error_msg); +#else + if (modsecurity_request_body_to_stream(msr, buf, buflen, error_msg) < 0) { + return HTTP_INTERNAL_SERVER_ERROR; + } +#endif } msr->reqbody_length += buflen; @@ -299,7 +313,7 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) { *error_msg = apr_psprintf(msr->mp, "Request body no files data length is larger than the " "configured limit (%ld).", msr->txcfg->reqbody_no_files_limit); - return -5; + return HTTP_REQUEST_ENTITY_TOO_LARGE; } else if ((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)) { *error_msg = apr_psprintf(msr->mp, "Request body no files data length is larger than the " "configured limit (%ld).", msr->txcfg->reqbody_no_files_limit); @@ -309,12 +323,12 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { } else { *error_msg = apr_psprintf(msr->mp, "Request body no files data length is larger than the " "configured limit (%ld).", msr->txcfg->reqbody_no_files_limit); - return -5; + return HTTP_REQUEST_ENTITY_TOO_LARGE; } } if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) - return -1; + return HTTP_INTERNAL_SERVER_ERROR; } } @@ -328,8 +342,7 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { apr_brigade_cleanup(bb_in); } while(!finished_reading); - // TODO: Why ignore the return code here? - modsecurity_request_body_end(msr, error_msg); + apr_status_t rcbe = modsecurity_request_body_end(msr, error_msg); if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Input filter: Completed receiving request body (length %" APR_SIZE_T_FMT ").", @@ -338,7 +351,13 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { msr->if_status = IF_STATUS_WANTS_TO_RUN; - return 1; + if (rcbe == -5) { + return HTTP_REQUEST_ENTITY_TOO_LARGE; + } + if (rcbe < 0) { + return HTTP_INTERNAL_SERVER_ERROR; + } + return APR_SUCCESS; } @@ -350,6 +369,8 @@ apr_status_t read_request_body(modsec_rec *msr, char **error_msg) { * run or not. */ static int output_filter_should_run(modsec_rec *msr, request_rec *r) { + assert(msr != NULL); + assert(r != NULL); char *content_type = NULL; /* Check configuration. */ @@ -411,10 +432,13 @@ static int output_filter_should_run(modsec_rec *msr, request_rec *r) { static apr_status_t output_filter_init(modsec_rec *msr, ap_filter_t *f, apr_bucket_brigade *bb_in) { + assert(msr != NULL); + assert(f != NULL); request_rec *r = f->r; const char *s_content_length = NULL; apr_status_t rc; + assert(msr != NULL); msr->of_brigade = apr_brigade_create(msr->mp, f->c->bucket_alloc); if (msr->of_brigade == NULL) { msr_log(msr, 1, "Output filter: Failed to create brigade."); @@ -478,6 +502,8 @@ static apr_status_t output_filter_init(modsec_rec *msr, ap_filter_t *f, * and to the client. */ static apr_status_t send_of_brigade(modsec_rec *msr, ap_filter_t *f) { + assert(msr != NULL); + assert(f != NULL); apr_status_t rc; rc = ap_pass_brigade(f->next, msr->of_brigade); @@ -519,6 +545,8 @@ static apr_status_t send_of_brigade(modsec_rec *msr, ap_filter_t *f) { * */ static void inject_content_to_of_brigade(modsec_rec *msr, ap_filter_t *f) { + assert(msr != NULL); + assert(f != NULL); apr_bucket *b; if (msr->txcfg->content_injection_enabled && msr->stream_output_data != NULL) { @@ -545,6 +573,8 @@ static void inject_content_to_of_brigade(modsec_rec *msr, ap_filter_t *f) { * */ static void prepend_content_to_of_brigade(modsec_rec *msr, ap_filter_t *f) { + assert(msr != NULL); + assert(f != NULL); if ((msr->txcfg->content_injection_enabled) && (msr->content_prepend) && (!msr->of_skipping)) { apr_bucket *bucket_ci = NULL; @@ -599,7 +629,6 @@ static int flatten_response_body(modsec_rec *msr) { return -1; } - memset(msr->stream_output_data, 0, msr->stream_output_length+1); memcpy(msr->stream_output_data, msr->resbody_data, msr->stream_output_length); msr->stream_output_data[msr->stream_output_length] = '\0'; } else if (msr->txcfg->stream_outbody_inspection && msr->txcfg->hash_is_enabled == HASH_ENABLED) { @@ -632,7 +661,6 @@ static int flatten_response_body(modsec_rec *msr) { return -1; } - memset(msr->stream_output_data, 0, msr->stream_output_length+1); memcpy(msr->stream_output_data, msr->resbody_data, msr->stream_output_length); msr->stream_output_data[msr->stream_output_length] = '\0'; } @@ -990,6 +1018,12 @@ apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) { /* Now send data down the filter stream * (full-buffering only). */ + if (!eos_bucket) { + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, f->r->server, + "ModSecurity: Internal Error: eos_bucket is NULL."); + return APR_EGENERAL; + } + if ((msr->of_skipping == 0)&&(!msr->of_partial)) { if(msr->of_stream_changed == 1) { inject_content_to_of_brigade(msr,f); diff --git a/apache2/apache2_util.c b/apache2/apache2_util.c index 24bba0cee9..e13c64b905 100644 --- a/apache2/apache2_util.c +++ b/apache2/apache2_util.c @@ -17,10 +17,16 @@ #include "http_core.h" #include "util_script.h" +#ifdef APLOG_USE_MODULE + APLOG_USE_MODULE(security2); +#endif + /** * Sends a brigade with an error bucket down the filter chain. */ apr_status_t send_error_bucket(modsec_rec *msr, ap_filter_t *f, int status) { + assert(msr != NULL); + assert(f != NULL); apr_bucket_brigade *brigade = NULL; apr_bucket *bucket = NULL; @@ -57,6 +63,9 @@ apr_status_t send_error_bucket(modsec_rec *msr, ap_filter_t *f, int status) { * the "output" parameter. */ int apache2_exec(modsec_rec *msr, const char *command, const char **argv, char **output) { + assert(msr != NULL); + assert(command != NULL); + apr_procattr_t *procattr = NULL; apr_proc_t *procnew = NULL; apr_status_t rc = APR_SUCCESS; @@ -95,7 +104,12 @@ int apache2_exec(modsec_rec *msr, const char *command, const char **argv, char * return -1; } - apr_procattr_io_set(procattr, APR_NO_PIPE, APR_FULL_BLOCK, APR_NO_PIPE); + rc = apr_procattr_io_set(procattr, APR_NO_PIPE, APR_FULL_BLOCK, APR_NO_PIPE); + if (rc != APR_SUCCESS) { + msr_log(msr, 1, "Exec: apr_procattr_io_set failed: %d (%s)", rc, get_apr_error(r->pool, rc)); + return -1; + } + apr_procattr_cmdtype_set(procattr, APR_SHELLCMD); if (msr->txcfg->debuglog_level >= 9) { @@ -195,13 +209,12 @@ char *get_env_var(request_rec *r, char *name) { static void internal_log_ex(request_rec *r, directory_config *dcfg, modsec_rec *msr, int level, int fixup, const char *text, va_list ap) { + assert(r != NULL); + assert(msr != NULL); + assert(text != NULL); apr_size_t nbytes, nbytes_written; apr_file_t *debuglog_fd = NULL; int filter_debug_level = 0; - char *remote = NULL; - char *parse_remote = NULL; - char *saved = NULL; - char *str = NULL; char str1[1024] = ""; char str2[1256] = ""; @@ -267,21 +280,33 @@ static void internal_log_ex(request_rec *r, directory_config *dcfg, modsec_rec * } else hostname = ""; -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 +/* Non-standalone modules use amended format string for logging */ +#if !(defined(VERSION_STANDALONE)) + #if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 + ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r, + "ModSecurity: %s%s [uri \"%s\"]%s", str1, hostname, log_escape(msr->mp, r->uri), unique_id); + #else + ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server, + "ModSecurity: %s%s [uri \"%s\"]%s", str1, hostname, log_escape(msr->mp, r->uri), unique_id); + #endif +/* Standalone module must use original format string for logging with explicit + * "[client %s]" to log client IP address (no Apache to implicitly add this) */ +#else + #if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r, "[client %s] ModSecurity: %s%s [uri \"%s\"]%s", r->useragent_ip ? r->useragent_ip : r->connection->client_ip, str1, hostname, log_escape(msr->mp, r->uri), unique_id); -#else + #else ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server, - "[client %s] ModSecurity: %s%s [uri \"%s\"]%s", msr->remote_addr ? msr->remote_addr : r->connection->remote_ip, str1, - hostname, log_escape(msr->mp, r->uri), unique_id); + "[client %s] ModSecurity: %s%s [uri \"%s\"]%s", msr->remote_addr ? msr->remote_addr : r->connection->remote_ip, str1, + hostname, log_escape(msr->mp, r->uri), unique_id); + #endif #endif /* Add this message to the list. */ if (msr != NULL) { /* Force relevency if this is an alert */ msr->is_relevant++; - *(const char **)apr_array_push(msr->alerts) = apr_pstrdup(msr->mp, str1); } } @@ -294,6 +319,8 @@ static void internal_log_ex(request_rec *r, directory_config *dcfg, modsec_rec * * Apache error log if the message is important enough. */ void msr_log(modsec_rec *msr, int level, const char *text, ...) { + assert(msr != NULL); + assert(text != NULL); va_list ap; va_start(ap, text); @@ -307,6 +334,8 @@ void msr_log(modsec_rec *msr, int level, const char *text, ...) { * Apache error log. This is intended for error callbacks. */ void msr_log_error(modsec_rec *msr, const char *text, ...) { + assert(msr != NULL); + assert(text != NULL); va_list ap; va_start(ap, text); @@ -321,6 +350,8 @@ void msr_log_error(modsec_rec *msr, const char *text, ...) { * The 'text' will first be escaped. */ void msr_log_warn(modsec_rec *msr, const char *text, ...) { + assert(msr != NULL); + assert(text != NULL); va_list ap; va_start(ap, text); diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index b6e98e9dcd..97c49646bc 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* Copyright (c) 2004-2022 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 @@ -25,12 +25,6 @@ #include "apr_optional.h" #include "mod_log_config.h" -/* - * #ifdef APLOG_USE_MODULE - * APLOG_USE_MODULE(security2); - * #endif - */ - #include "msc_logging.h" #include "msc_util.h" @@ -52,6 +46,10 @@ #include #endif /* WITH_YAJL */ +#ifdef APLOG_USE_MODULE + APLOG_USE_MODULE(security2); +#endif + /* ModSecurity structure */ msc_engine DSOLOCAL *modsecurity = NULL; @@ -105,39 +103,52 @@ static int server_limit, thread_limit; * * \param mp Pointer to memory pool */ -static void version(apr_pool_t *mp) { +static void version(apr_pool_t *mp, server_rec* s) { char *pcre_vrs = NULL; + const char *pcre_loaded_vrs = NULL; + char pcre2_loaded_vrs_buffer[80] ={0}; - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, "ModSecurity: APR compiled version=\"%s\"; " "loaded version=\"%s\"", APR_VERSION_STRING, apr_version_string()); if (strstr(apr_version_string(), APR_VERSION_STRING) == NULL) { - ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "ModSecurity: Loaded APR do not match with compiled!"); + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, "ModSecurity: Loaded APR do not match with compiled!"); } +#ifndef WITH_PCRE + pcre_vrs = apr_psprintf(mp,"%d.%d ", PCRE2_MAJOR, PCRE2_MINOR); + pcre_loaded_vrs = pcre2_loaded_vrs_buffer; + pcre2_config(PCRE2_CONFIG_VERSION, pcre2_loaded_vrs_buffer); +#else pcre_vrs = apr_psprintf(mp,"%d.%d ", PCRE_MAJOR, PCRE_MINOR); + pcre_loaded_vrs = pcre_version(); +#endif ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, +#ifndef WITH_PCRE + "ModSecurity: PCRE2 compiled version=\"%s\"; " +#else "ModSecurity: PCRE compiled version=\"%s\"; " - "loaded version=\"%s\"", pcre_vrs, pcre_version()); +#endif + "loaded version=\"%s\"", pcre_vrs, pcre_loaded_vrs); - if (strstr(pcre_version(),pcre_vrs) == NULL) { - ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "ModSecurity: Loaded PCRE do not match with compiled!"); + if (strstr(pcre_loaded_vrs,pcre_vrs) == NULL) { + ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, "ModSecurity: Loaded PCRE do not match with compiled!"); } /* Lua version function was removed in current 5.1. Need to check in future versions if it's back */ #if defined(WITH_LUA) - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, "ModSecurity: LUA compiled version=\"%s\"", LUA_VERSION); #endif /* WITH_LUA */ #ifdef WITH_YAJL - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, "ModSecurity: YAJL compiled version=\"%d.%d.%d\"", YAJL_MAJOR, YAJL_MINOR, YAJL_MICRO); #endif /* WITH_YAJL */ - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, "ModSecurity: LIBXML compiled version=\"%s\"", LIBXML_DOTTED_VERSION); } @@ -464,6 +475,8 @@ static modsec_rec *retrieve_tx_context(request_rec *r) { * phases, redirections, or subrequests. */ static void store_tx_context(modsec_rec *msr, request_rec *r) { + assert(msr != NULL); + assert(r != NULL); apr_table_setn(r->notes, NOTE_MSR, (void *)msr); } @@ -480,7 +493,10 @@ static modsec_rec *create_tx_context(request_rec *r) { apr_allocator_create(&allocator); apr_allocator_max_free_set(allocator, 1024); apr_pool_create_ex(&msr->mp, r->pool, NULL, allocator); - if (msr->mp == NULL) return NULL; + if (msr->mp == NULL) { + apr_allocator_destroy(allocator); + return NULL; + } apr_allocator_owner_set(allocator, msr->mp); msr->modsecurity = modsecurity; @@ -762,7 +778,7 @@ static int hook_post_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_t ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s, "%s configured.", MODSEC_MODULE_NAME_FULL); - version(mp); + version(mp, s); /* If we've changed the server signature make note of the original. */ if (new_server_signature != NULL) { @@ -852,6 +868,9 @@ static int hook_request_early(request_rec *r) { */ msr = create_tx_context(r); if (msr == NULL) return DECLINED; + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Context created after request failure."); + } #ifdef REQUEST_EARLY @@ -1013,56 +1032,15 @@ static int hook_request_late(request_rec *r) { } rc = read_request_body(msr, &my_error_msg); - if (rc < 0) { - switch(rc) { - case -1 : - if (my_error_msg != NULL) { - msr_log(msr, 1, "%s", my_error_msg); - } - return HTTP_INTERNAL_SERVER_ERROR; - break; - case -4 : /* Timeout. */ - if (my_error_msg != NULL) { - msr_log(msr, 4, "%s", my_error_msg); - } - r->connection->keepalive = AP_CONN_CLOSE; - return HTTP_REQUEST_TIME_OUT; - break; - case -5 : /* Request body limit reached. */ - msr->inbound_error = 1; - if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) { - r->connection->keepalive = AP_CONN_CLOSE; - if (my_error_msg != NULL) { - msr_log(msr, 1, "%s. Deny with code (%d)", my_error_msg, HTTP_REQUEST_ENTITY_TOO_LARGE); - } - return HTTP_REQUEST_ENTITY_TOO_LARGE; - } else { - if (my_error_msg != NULL) { - msr_log(msr, 1, "%s", my_error_msg); - } - } - break; - case -6 : /* EOF when reading request body. */ - if (my_error_msg != NULL) { - msr_log(msr, 4, "%s", my_error_msg); - } - r->connection->keepalive = AP_CONN_CLOSE; - return HTTP_BAD_REQUEST; - break; - case -7 : /* Partial recieved */ - if (my_error_msg != NULL) { - msr_log(msr, 4, "%s", my_error_msg); - } - r->connection->keepalive = AP_CONN_CLOSE; - return HTTP_BAD_REQUEST; - break; - default : - /* allow through */ - break; + if (rc != OK) { + if (my_error_msg != NULL) { + msr_log(msr, 1, "%s", my_error_msg); } - - msr->msc_reqbody_error = 1; - msr->msc_reqbody_error_msg = my_error_msg; + if (rc == HTTP_REQUEST_ENTITY_TOO_LARGE) { + msr->inbound_error = 1; + } + r->connection->keepalive = AP_CONN_CLOSE; + return rc; } /* Update the request headers. They might have changed after @@ -1139,17 +1117,12 @@ static void hook_error_log(const char *file, int line, int level, apr_status_t s #endif if (msr_ap_server) { #if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - msr = create_tx_context((request_rec *)info->r); + msr = create_tx_context((request_rec*)info->r); #else - msr = create_tx_context((request_rec *)r); + msr = create_tx_context((request_rec*)r); #endif - if (msr != NULL && msr->txcfg->debuglog_level >= 9) { - if (msr == NULL) { - msr_log(msr, 9, "Failed to create context after request failure."); - } - else { - msr_log(msr, 9, "Context created after request failure."); - } + if (msr && msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Context created after request failure."); } } if (msr == NULL) return; @@ -1597,7 +1570,7 @@ static int hook_connection_early(conn_rec *conn) "Possible DoS Consumption Attack [Rejected]", ip_count_w, conn_write_state_limit, client_ip); - if (!conn_limits_filter_state == MODSEC_ENABLED) + if (conn_limits_filter_state == MODSEC_ENABLED) return OK; } } diff --git a/apache2/modsecurity.c b/apache2/modsecurity.c index dcdb48590c..29ec5167c6 100644 --- a/apache2/modsecurity.c +++ b/apache2/modsecurity.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* Copyright (c) 2004-2022 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 @@ -27,6 +27,10 @@ #include #endif +#ifdef APLOG_USE_MODULE + APLOG_USE_MODULE(security2); +#endif + unsigned long int DSOLOCAL unicode_codepage = 0; int DSOLOCAL *unicode_map_table = NULL; @@ -37,6 +41,8 @@ int DSOLOCAL *unicode_map_table = NULL; const char * msc_alert_message(modsec_rec *msr, msre_actionset *actionset, const char *action_message, const char *rule_message) { + assert(msr != NULL); + assert(actionset != NULL); const char *message = NULL; if (rule_message == NULL) rule_message = "Unknown error."; @@ -59,6 +65,8 @@ const char * msc_alert_message(modsec_rec *msr, msre_actionset *actionset, const void msc_alert(modsec_rec *msr, int level, msre_actionset *actionset, const char *action_message, const char *rule_message) { + assert(msr != NULL); + assert(actionset != NULL); const char *message = msc_alert_message(msr, actionset, action_message, rule_message); msr_log(msr, level, "%s", message); @@ -114,80 +122,96 @@ msc_engine *modsecurity_create(apr_pool_t *mp, int processing_mode) { return msce; } -/** - * Initialise the modsecurity engine. This function must be invoked - * after configuration processing is complete as Apache needs to know the - * username it is running as. - */ -int modsecurity_init(msc_engine *msce, apr_pool_t *mp) { - apr_status_t rc; - - /** - * Notice that curl is initialized here but never cleaned up. First version - * of this implementation curl was initialized and cleaned for every - * utilization. Turns out that it was not only cleaning stuff that was - * utilized by Curl but also other OpenSSL stuff that was utilized by - * mod_ssl leading the SSL support to crash. - */ -#ifdef WITH_CURL - curl_global_init(CURL_GLOBAL_ALL); -#endif - /* Serial audit log mutext */ - rc = apr_global_mutex_create(&msce->auditlog_lock, NULL, APR_LOCK_DEFAULT, mp); +int acquire_global_lock(apr_global_mutex_t **lock, apr_pool_t *mp) { + apr_status_t rc = apr_global_mutex_create(lock, NULL, APR_LOCK_DEFAULT, mp); if (rc != APR_SUCCESS) { - //ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, "mod_security: Could not create modsec_auditlog_lock"); - //return HTTP_INTERNAL_SERVER_ERROR; + ap_log_perror(APLOG_MARK, APLOG_ERR, rc, mp, " ModSecurity: Could not create global mutex"); return -1; } - #if !defined(MSC_TEST) #ifdef __SET_MUTEX_PERMS #if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - rc = ap_unixd_set_global_mutex_perms(msce->auditlog_lock); + rc = ap_unixd_set_global_mutex_perms(*lock); #else - rc = unixd_set_global_mutex_perms(msce->auditlog_lock); + rc = unixd_set_global_mutex_perms(*lock); #endif if (rc != APR_SUCCESS) { - // ap_log_error(APLOG_MARK, APLOG_ERR, rc, s, "mod_security: Could not set permissions on modsec_auditlog_lock; check User and Group directives"); - // return HTTP_INTERNAL_SERVER_ERROR; + ap_log_perror(APLOG_MARK, APLOG_ERR, rc, mp, " ModSecurity: Could not set permissions on global mutex"); return -1; } #endif /* SET_MUTEX_PERMS */ +#endif /* MSC_TEST */ + return APR_SUCCESS; +} - rc = apr_global_mutex_create(&msce->geo_lock, NULL, APR_LOCK_DEFAULT, mp); - if (rc != APR_SUCCESS) { +/** + * handle errors from apr_global_mutex_lock + */ +int msr_global_mutex_lock(modsec_rec* msr, apr_global_mutex_t* lock, const char* fct) { + assert(msr); + assert(msr->modsecurity); // lock is msr->modsecurity->..._lock + assert(msr->mp); + if (!lock) { + msr_log(msr, 1, "%s: Global mutex was not created", fct); return -1; } -#ifdef __SET_MUTEX_PERMS -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - rc = ap_unixd_set_global_mutex_perms(msce->geo_lock); -#else - rc = unixd_set_global_mutex_perms(msce->geo_lock); -#endif - if (rc != APR_SUCCESS) { + int rc = apr_global_mutex_lock(lock); + if (rc != APR_SUCCESS) msr_log(msr, 1, "Audit log: Failed to lock global mutex: %s", get_apr_error(msr->mp, rc)); + return rc; +} +/** + * handle errors from apr_global_mutex_unlock + */ +int msr_global_mutex_unlock(modsec_rec* msr, apr_global_mutex_t* lock, const char* fct) { + assert(msr); + assert(msr->modsecurity); // lock is msr->modsecurity->..._lock + assert(msr->mp); + if (!lock) { + msr_log(msr, 1, "%s: Global mutex was not created", fct); return -1; } -#endif /* SET_MUTEX_PERMS */ -#ifdef GLOBAL_COLLECTION_LOCK - rc = apr_global_mutex_create(&msce->dbm_lock, NULL, APR_LOCK_DEFAULT, mp); - if (rc != APR_SUCCESS) { - return -1; - } + int rc = apr_global_mutex_unlock(lock); + // We should have get the warning at lock time, so ignore it here + // if (rc != APR_SUCCESS) msr_log(msr, 1, "Audit log: Failed to unlock global mutex: %s", get_apr_error(msr->mp, rc)); + return rc; +} -#ifdef __SET_MUTEX_PERMS -#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 - rc = ap_unixd_set_global_mutex_perms(msce->dbm_lock); -#else - rc = unixd_set_global_mutex_perms(msce->dbm_lock); -#endif - if (rc != APR_SUCCESS) { - return -1; - } -#endif /* SET_MUTEX_PERMS */ +/** + * Initialise the modsecurity engine. This function must be invoked + * after configuration processing is complete as Apache needs to know the + * username it is running as. + */ +int modsecurity_init(msc_engine *msce, apr_pool_t *mp) { + apr_status_t rc; + + msce->auditlog_lock = msce->geo_lock = NULL; +#ifdef GLOBAL_COLLECTION_LOCK + msce->dbm_lock = NULL; #endif + + /** + * Notice that curl is initialized here but never cleaned up. First version + * of this implementation curl was initialized and cleaned for every + * utilization. Turns out that it was not only cleaning stuff that was + * utilized by Curl but also other OpenSSL stuff that was utilized by + * mod_ssl leading the SSL support to crash. + */ +#ifdef WITH_CURL + curl_global_init(CURL_GLOBAL_ALL); #endif + /* Serial audit log mutex */ + rc = acquire_global_lock(&msce->auditlog_lock, mp); + if (rc != APR_SUCCESS) return -1; + + rc = acquire_global_lock(&msce->geo_lock, mp); + if (rc != APR_SUCCESS) return -1; + +#ifdef GLOBAL_COLLECTION_LOCK + rc = acquire_global_lock(&msce->dbm_lock, mp); + if (rc != APR_SUCCESS) return -1; +#endif /* GLOBAL_COLLECTION_LOCK */ return 1; } @@ -314,6 +338,7 @@ static apr_status_t modsecurity_tx_cleanup(void *data) { msr->msc_full_request_buffer != NULL) { msr->msc_full_request_length = 0; free(msr->msc_full_request_buffer); + msr->msc_full_request_buffer = NULL; } #if defined(WITH_LUA) @@ -322,6 +347,21 @@ static apr_status_t modsecurity_tx_cleanup(void *data) { #endif #endif + /* Streams cleanup. */ + if (msr->stream_input_data != NULL) { + free(msr->stream_input_data); + msr->stream_input_data = NULL; + msr->stream_input_length = 0; +#ifdef MSC_LARGE_STREAM_INPUT + msr->stream_input_allocated_length = 0; +#endif + } + if (msr->stream_output_data != NULL) { + free(msr->stream_output_data); + msr->stream_output_data = NULL; + msr->stream_output_length = 0; + } + return APR_SUCCESS; } @@ -525,6 +565,7 @@ apr_status_t modsecurity_tx_init(modsec_rec *msr) { * */ static int is_response_status_relevant(modsec_rec *msr, int status) { + assert(msr != NULL); char *my_error_msg = NULL; apr_status_t rc; char buf[32]; @@ -543,7 +584,11 @@ static int is_response_status_relevant(modsec_rec *msr, int status) { rc = msc_regexec(msr->txcfg->auditlog_relevant_regex, buf, strlen(buf), &my_error_msg); if (rc >= 0) return 1; +#ifndef WITH_PCRE + if (rc == PCRE2_ERROR_NOMATCH) return 0; +#else if (rc == PCRE_ERROR_NOMATCH) return 0; +#endif msr_log(msr, 1, "Regex processing failed (rc %d): %s", rc, my_error_msg); @@ -637,6 +682,7 @@ static apr_status_t modsecurity_process_phase_response_headers(modsec_rec *msr) * */ static apr_status_t modsecurity_process_phase_response_body(modsec_rec *msr) { + assert(msr != NULL); apr_time_t time_before; apr_status_t rc = 0; @@ -668,6 +714,7 @@ static apr_status_t modsecurity_process_phase_response_body(modsec_rec *msr) { * */ static apr_status_t modsecurity_process_phase_logging(modsec_rec *msr) { + assert(msr != NULL); apr_time_t time_before, time_after; if (msr->txcfg->debuglog_level >= 4) { @@ -754,6 +801,7 @@ static apr_status_t modsecurity_process_phase_logging(modsec_rec *msr) { * in the modsec_rec structure. */ apr_status_t modsecurity_process_phase(modsec_rec *msr, unsigned int phase) { + assert(msr != NULL); /* Check if we should run. */ if ((msr->was_intercepted)&&(phase != PHASE_LOGGING)) { if (msr->txcfg->debuglog_level >= 4) { diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index f170034c99..2894717031 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* Copyright (c) 2004-2022 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 @@ -95,6 +95,8 @@ typedef struct msc_parm msc_parm; #define REQUEST_BODY_DEFAULT_INMEMORY_LIMIT 131072 #define REQUEST_BODY_DEFAULT_LIMIT 134217728 #define REQUEST_BODY_NO_FILES_DEFAULT_LIMIT 1048576 +#define REQUEST_BODY_JSON_DEPTH_DEFAULT_LIMIT 10000 +#define ARGUMENTS_LIMIT 1000 #define RESPONSE_BODY_DEFAULT_LIMIT 524288 #define RESPONSE_BODY_HARD_LIMIT 1073741824L @@ -241,6 +243,10 @@ extern DSOLOCAL int *unicode_map_table; #define RULE_EXCEPTION_REMOVE_MSG 4 #define RULE_EXCEPTION_REMOVE_TAG 5 +#define MSC_XML_ARGS_OFF 0 +#define MSC_XML_ARGS_ON 1 +#define MSC_XML_ARGS_ONLYARGS 2 + #define NBSP 160 struct rule_exception { @@ -287,6 +293,10 @@ struct modsec_rec { unsigned int resbody_contains_html; apr_size_t stream_input_length; +#ifdef MSC_LARGE_STREAM_INPUT + apr_size_t stream_input_allocated_length; +#endif + char *stream_input_data; apr_size_t stream_output_length; char *stream_output_data; @@ -488,6 +498,8 @@ struct directory_config { long int reqbody_inmemory_limit; long int reqbody_limit; long int reqbody_no_files_limit; + long int reqbody_json_depth_limit; + long int arguments_limit; int resbody_access; long int of_limit; @@ -635,6 +647,7 @@ struct directory_config { /* xml */ int xml_external_entity; + int parse_xml_into_args; /* This will be used whenever ModSecurity will be ready * to ask the server for newer rules. @@ -680,6 +693,7 @@ struct msc_arg { unsigned int value_origin_offset; unsigned int value_origin_len; const char *origin; + unsigned int marked_for_sanitization; }; struct msc_string { @@ -695,6 +709,11 @@ struct msc_parm { int pad_2; }; +/* Reusable functions */ +int acquire_global_lock(apr_global_mutex_t **lock, apr_pool_t *mp); +int msr_global_mutex_lock(modsec_rec* msr, apr_global_mutex_t* lock, const char* fct); +int msr_global_mutex_unlock(modsec_rec* msr, apr_global_mutex_t* lock, const char* fct); + /* Engine functions */ msc_engine DSOLOCAL *modsecurity_create(apr_pool_t *mp, int processing_mode); diff --git a/apache2/msc_crypt.c b/apache2/msc_crypt.c index cf6a4a7705..bd3061a3c6 100644 --- a/apache2/msc_crypt.c +++ b/apache2/msc_crypt.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) + * Copyright (c) 2004-2022 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 @@ -32,14 +32,12 @@ * \retval NULL on fail */ char *normalize_path(modsec_rec *msr, char *input) { + assert(msr != NULL); + assert(input != NULL); xmlURI *uri = NULL; char *parsed_content = NULL; char *content = NULL; - if(msr == NULL) return NULL; - - if(input == NULL) return NULL; - uri = xmlParseURI(input); if(uri != NULL && uri->path) { @@ -66,7 +64,6 @@ char *normalize_path(modsec_rec *msr, char *input) { if(uri->path) { char *Uri = NULL; - int bytes = 0; /*int i;*/ char *abs_link = NULL; char *filename = NULL; @@ -188,8 +185,15 @@ char *getkey(apr_pool_t *mp) { * * \retval hex_digest The MAC */ +#ifdef __NetBSD__ +char *mschmac(modsec_rec *msr, const char *key, int key_len, + unsigned char *msg, int msglen) { +#else char *hmac(modsec_rec *msr, const char *key, int key_len, unsigned char *msg, int msglen) { +#endif + assert(msr != NULL); + assert(msg != NULL); apr_sha1_ctx_t ctx; unsigned char digest[APR_SHA1_DIGESTSIZE]; unsigned char hmac_ipad[HMAC_PAD_SIZE], hmac_opad[HMAC_PAD_SIZE]; @@ -341,6 +345,8 @@ int init_response_body_html_parser(modsec_rec *msr) { * \retval -1 on fail */ int do_hash_method(modsec_rec *msr, char *link, int type) { + assert(msr != NULL); + assert(link != NULL); hash_method **em = NULL; int i = 0; char *error_msg = NULL; @@ -381,7 +387,11 @@ int do_hash_method(modsec_rec *msr, char *link, int type) { case HASH_URL_HREF_HASH_RX: if(em[i]->type == HASH_URL_HREF_HASH_RX) { rc = msc_regexec_capture(em[i]->param_data, link, strlen(link), ovector, 30, &my_error_msg); +#ifndef WITH_PCRE + if ((rc == PCRE2_ERROR_MATCHLIMIT) || (rc == PCRE2_ERROR_RECURSIONLIMIT)) { +#else if ((rc == PCRE_ERROR_MATCHLIMIT) || (rc == PCRE_ERROR_RECURSIONLIMIT)) { +#endif msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); if (s == NULL) return -1; @@ -410,7 +420,11 @@ int do_hash_method(modsec_rec *msr, char *link, int type) { msr_log(msr, 4, "%s.", error_msg); return -1; } +#ifndef WITH_PCRE + if (rc != PCRE2_ERROR_NOMATCH) { /* Match. */ +#else if (rc != PCRE_ERROR_NOMATCH) { /* Match. */ +#endif return 1; } } @@ -436,7 +450,11 @@ int do_hash_method(modsec_rec *msr, char *link, int type) { case HASH_URL_FACTION_HASH_RX: if(em[i]->type == HASH_URL_FACTION_HASH_RX) { rc = msc_regexec_capture(em[i]->param_data, link, strlen(link), ovector, 30, &my_error_msg); +#ifndef WITH_PCRE + if ((rc == PCRE2_ERROR_MATCHLIMIT) || (rc == PCRE2_ERROR_RECURSIONLIMIT)) { +#else if ((rc == PCRE_ERROR_MATCHLIMIT) || (rc == PCRE_ERROR_RECURSIONLIMIT)) { +#endif msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); if (s == NULL) return -1; @@ -465,7 +483,11 @@ int do_hash_method(modsec_rec *msr, char *link, int type) { msr_log(msr, 4, "%s.", error_msg); return -1; } +#ifndef WITH_PCRE + if (rc != PCRE2_ERROR_NOMATCH) { /* Match. */ +#else if (rc != PCRE_ERROR_NOMATCH) { /* Match. */ +#endif return 1; } } @@ -491,7 +513,11 @@ int do_hash_method(modsec_rec *msr, char *link, int type) { case HASH_URL_LOCATION_HASH_RX: if(em[i]->type == HASH_URL_LOCATION_HASH_RX) { rc = msc_regexec_capture(em[i]->param_data, link, strlen(link), ovector, 30, &my_error_msg); +#ifndef WITH_PCRE + if ((rc == PCRE2_ERROR_MATCHLIMIT) || (rc == PCRE2_ERROR_RECURSIONLIMIT)) { +#else if ((rc == PCRE_ERROR_MATCHLIMIT) || (rc == PCRE_ERROR_RECURSIONLIMIT)) { +#endif msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); if (s == NULL) return -1; @@ -520,7 +546,11 @@ int do_hash_method(modsec_rec *msr, char *link, int type) { msr_log(msr, 4, "%s.", error_msg); return -1; } +#ifndef WITH_PCRE + if (rc != PCRE2_ERROR_NOMATCH) { /* Match. */ +#else if (rc != PCRE_ERROR_NOMATCH) { /* Match. */ +#endif return 1; } } @@ -546,7 +576,11 @@ int do_hash_method(modsec_rec *msr, char *link, int type) { case HASH_URL_IFRAMESRC_HASH_RX: if(em[i]->type == HASH_URL_IFRAMESRC_HASH_RX) { rc = msc_regexec_capture(em[i]->param_data, link, strlen(link), ovector, 30, &my_error_msg); +#ifndef WITH_PCRE + if ((rc == PCRE2_ERROR_MATCHLIMIT) || (rc == PCRE2_ERROR_RECURSIONLIMIT)) { +#else if ((rc == PCRE_ERROR_MATCHLIMIT) || (rc == PCRE_ERROR_RECURSIONLIMIT)) { +#endif msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); if (s == NULL) return -1; @@ -575,7 +609,11 @@ int do_hash_method(modsec_rec *msr, char *link, int type) { msr_log(msr, 4, "%s.", error_msg); return -1; } +#ifndef WITH_PCRE + if (rc != PCRE2_ERROR_NOMATCH) { /* Match. */ +#else if (rc != PCRE_ERROR_NOMATCH) { /* Match. */ +#endif return 1; } } @@ -601,7 +639,11 @@ int do_hash_method(modsec_rec *msr, char *link, int type) { case HASH_URL_FRAMESRC_HASH_RX: if(em[i]->type == HASH_URL_FRAMESRC_HASH_RX) { rc = msc_regexec_capture(em[i]->param_data, link, strlen(link), ovector, 30, &my_error_msg); +#ifndef WITH_PCRE + if ((rc == PCRE2_ERROR_MATCHLIMIT) || (rc == PCRE2_ERROR_RECURSIONLIMIT)) { +#else if ((rc == PCRE_ERROR_MATCHLIMIT) || (rc == PCRE_ERROR_RECURSIONLIMIT)) { +#endif msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); if (s == NULL) return -1; @@ -630,7 +672,11 @@ int do_hash_method(modsec_rec *msr, char *link, int type) { msr_log(msr, 4, "%s.", error_msg); return -1; } +#ifndef WITH_PCRE + if (rc != PCRE2_ERROR_NOMATCH) { /* Match. */ +#else if (rc != PCRE_ERROR_NOMATCH) { /* Match. */ +#endif return 1; } } @@ -1006,6 +1052,7 @@ int hash_response_body_links(modsec_rec *msr) { * \retval -1 On fail */ int inject_hashed_response_body(modsec_rec *msr, int elts) { + assert(msr != NULL); xmlOutputBufferPtr output_buf = NULL; xmlCharEncodingHandlerPtr handler = NULL; char *p = NULL; @@ -1031,31 +1078,35 @@ int inject_hashed_response_body(modsec_rec *msr, int elts) { if (ctype && encoding == NULL) { if (ctype && (p = m_strcasestr(ctype, "charset=") , p != NULL)) { p += 8 ; - if (encoding = apr_pstrndup(msr->mp, p, strcspn(p, " ;") ), encoding) { - xmlCharEncoding enc; - enc = xmlParseCharEncoding(encoding); - handler = xmlFindCharEncodingHandler(encoding); - } + encoding = apr_pstrndup(msr->mp, p, strcspn(p, " ;")); + handler = xmlFindCharEncodingHandler(encoding); } } else { if(encoding != NULL) { - xmlCharEncoding enc; - enc = xmlParseCharEncoding(encoding); handler = xmlFindCharEncodingHandler(encoding); } } if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "inject_hashed_response_body: Detected encoding type [%s].", encoding); + msr_log(msr, 4, "inject_hashed_response_body: Detected encoding type [%s].", + encoding ? encoding : "(none)"); - if (handler == NULL) + if (handler == NULL) { handler = xmlFindCharEncodingHandler("UTF-8"); - if (handler == NULL) + encoding = apr_pstrdup(msr->mp, "UTF-8"); + } + if (handler == NULL) { handler = xmlFindCharEncodingHandler("ISO-8859-1"); - if (handler == NULL) + encoding = apr_pstrdup(msr->mp, "ISO-8859-1"); + } + if (handler == NULL) { handler = xmlFindCharEncodingHandler("HTML"); - if (handler == NULL) + encoding = apr_pstrdup(msr->mp, "HTML"); + } + if (handler == NULL) { handler = xmlFindCharEncodingHandler("ascii"); + encoding = apr_pstrdup(msr->mp, "ascii"); + } if(handler == NULL) { xmlFreeDoc(msr->crypto_html_tree); @@ -1063,11 +1114,11 @@ int inject_hashed_response_body(modsec_rec *msr, int elts) { } apr_table_unset(msr->r->headers_out,"Content-Type"); - new_ct = (char*)apr_psprintf(msr->mp, "text/html;%s",handler->name); + new_ct = (char*)apr_psprintf(msr->mp, "text/html;%s",encoding); apr_table_set(msr->r->err_headers_out,"Content-Type",new_ct); if (msr->txcfg->debuglog_level >= 4) - msr_log(msr, 4, "inject_hashed_response_body: Using content-type [%s].", handler->name); + msr_log(msr, 4, "inject_hashed_response_body: Using content-type [%s].", encoding); output_buf = xmlAllocOutputBuffer(handler); if (output_buf == NULL) { @@ -1108,8 +1159,8 @@ int inject_hashed_response_body(modsec_rec *msr, int elts) { return -1; } - memset(msr->stream_output_data, 0x0, msr->stream_output_length+1); memcpy(msr->stream_output_data, xmlOutputBufferGetContent(output_buf), msr->stream_output_length); + msr->stream_output_data[msr->stream_output_length] = '\0'; if (msr->txcfg->debuglog_level >= 4) msr_log(msr, 4, "inject_hashed_response_body: Copying XML tree from CONTENT to stream buffer [%zu] bytes.", xmlOutputBufferGetSize(output_buf)); @@ -1139,8 +1190,8 @@ int inject_hashed_response_body(modsec_rec *msr, int elts) { return -1; } - memset(msr->stream_output_data, 0x0, msr->stream_output_length+1); memcpy(msr->stream_output_data, xmlOutputBufferGetContent(output_buf), msr->stream_output_length); + msr->stream_output_data[msr->stream_output_length] = '\0'; if (msr->txcfg->debuglog_level >= 4) msr_log(msr, 4, "inject_hashed_response_body: Copying XML tree from CONV to stream buffer [%zu] bytes.", xmlOutputBufferGetSize(output_buf)); @@ -1174,9 +1225,9 @@ int inject_hashed_response_body(modsec_rec *msr, int elts) { return -1; } - memset(msr->stream_output_data, 0x0, msr->stream_output_length+1); memcpy(msr->stream_output_data, (char *)xmlBufferContent(output_buf->buffer), msr->stream_output_length); //memcpy(msr->stream_output_data, output_buf->buffer->content, msr->stream_output_length); + msr->stream_output_data[msr->stream_output_length] = '\0'; if (msr->txcfg->debuglog_level >= 4) msr_log(msr, 4, "inject_hashed_response_body: Copying XML tree from CONTENT to stream buffer [%d] bytes.", msr->stream_output_length); @@ -1206,9 +1257,9 @@ int inject_hashed_response_body(modsec_rec *msr, int elts) { return -1; } - memset(msr->stream_output_data, 0x0, msr->stream_output_length+1); memcpy(msr->stream_output_data, (char *)xmlBufferContent(output_buf->conv), msr->stream_output_length); //memcpy(msr->stream_output_data, output_buf->conv->content, msr->stream_output_length); + msr->stream_output_data[msr->stream_output_length] = '\0'; if (msr->txcfg->debuglog_level >= 4) msr_log(msr, 4, "inject_hashed_response_body: Copying XML tree from CONV to stream buffer [%d] bytes.", msr->stream_output_length); @@ -1245,13 +1296,13 @@ int inject_hashed_response_body(modsec_rec *msr, int elts) { * \retval NULL on fail */ char *do_hash_link(modsec_rec *msr, char *link, int type) { + assert(msr != NULL); + assert(link != NULL); char *mac_link = NULL; char *path_chunk = NULL; char *hash_value = NULL; char *qm = NULL; - if(msr == NULL) return NULL; - if(strlen(link) > 7 && strncmp("http:",(char*)link,5)==0){ path_chunk = strchr(link+7,'/'); if(path_chunk != NULL) { @@ -1260,8 +1311,11 @@ char *do_hash_link(modsec_rec *msr, char *link, int type) { } if(msr->txcfg->crypto_key_add == HASH_KEYONLY) +#ifdef __NetBSD__ + hash_value = mschmac(msr, msr->txcfg->crypto_key, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); +#else hash_value = hmac(msr, msr->txcfg->crypto_key, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); - +#endif if(msr->txcfg->crypto_key_add == HASH_SESSIONID) { if(msr->sessionid == NULL || strlen(msr->sessionid) == 0) { #if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 @@ -1272,13 +1326,21 @@ char *do_hash_link(modsec_rec *msr, char *link, int type) { if (msr->txcfg->debuglog_level >= 4) msr_log(msr, 4, "Session id is empty. Using REMOTE_IP"); msr->txcfg->crypto_key_len = strlen(new_pwd); +#ifdef __NetBSD__ + hash_value = mschmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); +#else hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); +#endif } else { const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->sessionid); if (msr->txcfg->debuglog_level >= 4) msr_log(msr, 4, "Using session id [%s]", msr->sessionid); msr->txcfg->crypto_key_len = strlen(new_pwd); +#ifdef __NetBSD__ + hash_value = mschmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); +#else hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); +#endif } } @@ -1289,7 +1351,11 @@ char *do_hash_link(modsec_rec *msr, char *link, int type) { const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->remote_ip); #endif msr->txcfg->crypto_key_len = strlen(new_pwd); +#ifdef __NetBSD__ + hash_value = mschmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); +#else hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); +#endif } } else { return NULL; @@ -1303,8 +1369,11 @@ char *do_hash_link(modsec_rec *msr, char *link, int type) { } if(msr->txcfg->crypto_key_add == HASH_KEYONLY) +#ifdef __NetBSD__ + hash_value = mschmac(msr, msr->txcfg->crypto_key, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); +#else hash_value = hmac(msr, msr->txcfg->crypto_key, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); - +#endif if(msr->txcfg->crypto_key_add == HASH_SESSIONID) { if(msr->sessionid == NULL || strlen(msr->sessionid) == 0) { #if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 @@ -1315,13 +1384,21 @@ char *do_hash_link(modsec_rec *msr, char *link, int type) { if (msr->txcfg->debuglog_level >= 4) msr_log(msr, 4, "Session id is empty. Using REMOTE_IP"); msr->txcfg->crypto_key_len = strlen(new_pwd); +#ifdef __NetBSD__ + hash_value = mschmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); +#else hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); +#endif } else { const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->sessionid); if (msr->txcfg->debuglog_level >= 4) msr_log(msr, 4, "Using session id [%s]", msr->sessionid); msr->txcfg->crypto_key_len = strlen(new_pwd); +#ifdef __NetBSD__ + hash_value = mschmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); +#else hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); +#endif } } @@ -1332,7 +1409,11 @@ char *do_hash_link(modsec_rec *msr, char *link, int type) { const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->remote_ip); #endif msr->txcfg->crypto_key_len = strlen(new_pwd); +#ifdef __NetBSD__ + hash_value = mschmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); +#else hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) path_chunk+1, strlen((char*)path_chunk)-1); +#endif } } else { return NULL; @@ -1344,8 +1425,11 @@ char *do_hash_link(modsec_rec *msr, char *link, int type) { } if(msr->txcfg->crypto_key_add == HASH_KEYONLY) +#ifdef __NetBSD__ + hash_value = mschmac(msr, msr->txcfg->crypto_key, msr->txcfg->crypto_key_len, (unsigned char *) link+1, strlen((char*)link)-1); +#else hash_value = hmac(msr, msr->txcfg->crypto_key, msr->txcfg->crypto_key_len, (unsigned char *) link+1, strlen((char*)link)-1); - +#endif if(msr->txcfg->crypto_key_add == HASH_SESSIONID) { if(msr->sessionid == NULL || strlen(msr->sessionid) == 0) { #if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 @@ -1356,13 +1440,21 @@ char *do_hash_link(modsec_rec *msr, char *link, int type) { if (msr->txcfg->debuglog_level >= 4) msr_log(msr, 4, "Session id is empty. Using REMOTE_IP"); msr->txcfg->crypto_key_len = strlen(new_pwd); +#ifdef __NetBSD__ + hash_value = mschmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) link+1, strlen((char*)link)-1); +#else hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) link+1, strlen((char*)link)-1); +#endif } else { const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->sessionid); if (msr->txcfg->debuglog_level >= 4) msr_log(msr, 4, "Using session id [%s]", msr->sessionid); msr->txcfg->crypto_key_len = strlen(new_pwd); +#ifdef __NetBSD__ + hash_value = mschmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) link+1, strlen((char*)link)-1); +#else hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) link+1, strlen((char*)link)-1); +#endif } } @@ -1373,7 +1465,11 @@ char *do_hash_link(modsec_rec *msr, char *link, int type) { const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->remote_ip); #endif msr->txcfg->crypto_key_len = strlen(new_pwd); +#ifdef __NetBSD__ + hash_value = mschmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) link+1, strlen((char*)link)-1); +#else hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) link+1, strlen((char*)link)-1); +#endif } } @@ -1398,7 +1494,11 @@ char *do_hash_link(modsec_rec *msr, char *link, int type) { } if(msr->txcfg->crypto_key_add == HASH_KEYONLY) +#ifdef __NetBSD__ + hash_value = mschmac(msr, msr->txcfg->crypto_key, msr->txcfg->crypto_key_len, (unsigned char *) relative_link, strlen((char*)relative_link)); +#else hash_value = hmac(msr, msr->txcfg->crypto_key, msr->txcfg->crypto_key_len, (unsigned char *) relative_link, strlen((char*)relative_link)); +#endif if(msr->txcfg->crypto_key_add == HASH_SESSIONID) { if(msr->sessionid == NULL || strlen(msr->sessionid) == 0) { @@ -1410,13 +1510,21 @@ char *do_hash_link(modsec_rec *msr, char *link, int type) { if (msr->txcfg->debuglog_level >= 4) msr_log(msr, 4, "Session id is empty. Using REMOTE_IP"); msr->txcfg->crypto_key_len = strlen(new_pwd); +#ifdef __NetBSD__ + hash_value = mschmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) relative_link, strlen((char*)relative_link)); +#else hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) relative_link, strlen((char*)relative_link)); +#endif } else { const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->sessionid); if (msr->txcfg->debuglog_level >= 4) msr_log(msr, 4, "Using session id [%s]", msr->sessionid); msr->txcfg->crypto_key_len = strlen(new_pwd); +#ifdef __NetBSD__ + hash_value = mschmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) relative_link, strlen((char*)relative_link)); +#else hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) relative_link, strlen((char*)relative_link)); +#endif } } @@ -1427,7 +1535,11 @@ char *do_hash_link(modsec_rec *msr, char *link, int type) { const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->remote_ip); #endif msr->txcfg->crypto_key_len = strlen(new_pwd); +#ifdef __NetBSD__ + hash_value = mschmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) relative_link, strlen((char*)relative_link)); +#else hash_value = hmac(msr, new_pwd, msr->txcfg->crypto_key_len, (unsigned char *) relative_link, strlen((char*)relative_link)); +#endif } link = relative_uri; diff --git a/apache2/msc_crypt.h b/apache2/msc_crypt.h index 3b3aa6da44..fb3d515112 100644 --- a/apache2/msc_crypt.h +++ b/apache2/msc_crypt.h @@ -27,8 +27,13 @@ #define INT32_MAX (2147483647) #endif +#ifdef __NetBSD__ +char DSOLOCAL *mschmac(modsec_rec *msr, const char *key, int key_len, + unsigned char *msg, int msglen); +#else char DSOLOCAL *hmac(modsec_rec *msr, const char *key, int key_len, unsigned char *msg, int msglen); +#endif char DSOLOCAL *do_hash_link(modsec_rec *msr, char *link, int type); char DSOLOCAL *getkey(apr_pool_t *mp); diff --git a/apache2/msc_geo.c b/apache2/msc_geo.c index 134f40fa21..77f3403708 100644 --- a/apache2/msc_geo.c +++ b/apache2/msc_geo.c @@ -12,6 +12,7 @@ * directly using the email address security@modsecurity.org. */ +#include #include "msc_geo.h" @@ -43,7 +44,8 @@ static const char geo_country_code[GEO_COUNTRY_LAST + 1][4] = { "TJ","TK","TM","TN","TO","TL","TR","TT","TV","TW", "TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE", "VG","VI","VN","VU","WF","WS","YE","YT","RS","ZA", - "ZM","ME","ZW","A1","A2","O1","AX","GG","IM","JE" + "ZM","ME","ZW","A1","A2","O1","AX","GG","IM","JE", + "BL","BQ","CW","MF","SS","SX" }; static const char geo_country_code3[GEO_COUNTRY_LAST + 1][4] = { @@ -72,7 +74,8 @@ static const char geo_country_code3[GEO_COUNTRY_LAST + 1][4] = { "TJK","TKL","TKM","TUN","TON","TLS","TUR","TTO","TUV","TWN", "TZA","UKR","UGA","UM","USA","URY","UZB","VAT","VCT","VEN", "VGB","VIR","VNM","VUT","WLF","WSM","YEM","YT","SRB","ZAF", - "ZMB","MNE","ZWE","A1","A2","O1","ALA","GGY","IMN","JEY" + "ZMB","MNE","ZWE","A1","A2","O1","ALA","GGY","IMN","JEY", + "BLM","BES","CUW","MAF","SSD","SXM" }; static const char *const geo_country_name[GEO_COUNTRY_LAST + 1] = { @@ -101,7 +104,8 @@ static const char *const geo_country_name[GEO_COUNTRY_LAST + 1] = { "Tajikistan","Tokelau","Turkmenistan","Tunisia","Tonga","Timor-Leste","Turkey","Trinidad and Tobago","Tuvalu","Taiwan", "Tanzania, United Republic of","Ukraine","Uganda","United States Minor Outlying Islands","United States","Uruguay","Uzbekistan","Holy See (Vatican City State)","Saint Vincent and the Grenadines","Venezuela", "Virgin Islands, British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Serbia","South Africa", - "Zambia","Montenegro","Zimbabwe","Anonymous Proxy","Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man","Jersey" + "Zambia","Montenegro","Zimbabwe","Anonymous Proxy","Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man","Jersey", + "Saint Barthélemy","Bonaire, Sint Eustatius and Saba","Curaçao","Saint Martin (French part)","South Sudan","Sint Maarten (Dutch part)" }; static const char geo_country_continent[GEO_COUNTRY_LAST + 1][4] = { @@ -130,7 +134,8 @@ static const char geo_country_continent[GEO_COUNTRY_LAST + 1][4] = { "AS","OC","AS","AF","OC","AS","AS","SA","OC","AS", "AF","EU","AF","OC","NA","SA","AS","EU","SA","SA", "SA","SA","AS","OC","OC","OC","AS","AF","EU","AF", - "AF","EU","AF","--","--","--","EU","EU","EU","EU" + "AF","EU","AF","--","--","--","EU","EU","EU","EU", + "--","--","--","--","AF","--" }; typedef enum { @@ -240,6 +245,7 @@ static int field_length(const char *field, int maxlen) */ int geo_init(directory_config *dcfg, const char *dbfn, char **error_msg) { + assert(dcfg != NULL); *error_msg = NULL; if ((dcfg->geo == NULL) || (dcfg->geo == NOT_SET_P)) { @@ -259,6 +265,10 @@ int geo_init(directory_config *dcfg, const char *dbfn, char **error_msg) */ int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **error_msg) { + assert(msr != NULL); + assert(georec != NULL); + assert(target != NULL); + assert(error_msg != NULL); apr_sockaddr_t *addr; long ipnum = 0; char *targetip = NULL; @@ -269,7 +279,6 @@ int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **erro apr_size_t nbytes; unsigned int rec_val = 0; apr_off_t seekto = 0; - apr_status_t ret; int rc; int country = 0; int level; @@ -315,11 +324,7 @@ int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **erro msr_log(msr, 9, "GEO: Using address \"%s\" (0x%08lx). %lu", targetip, ipnum, ipnum); } - ret = apr_global_mutex_lock(msr->modsecurity->geo_lock); - if (ret != APR_SUCCESS) { - msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s", - get_apr_error(msr->mp, ret)); - } + msr_global_mutex_lock(msr, msr->modsecurity->geo_lock, "Geo lookup"); for (level = 31; level >= 0; level--) { /* Read the record */ @@ -351,13 +356,7 @@ int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **erro if (rec_val == geo->ctry_offset) { *error_msg = apr_psprintf(msr->mp, "No geo data for \"%s\").", log_escape(msr->mp, target)); msr_log(msr, 4, "%s", *error_msg); - - ret = apr_global_mutex_unlock(msr->modsecurity->geo_lock); - if (ret != APR_SUCCESS) { - msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s", - get_apr_error(msr->mp, ret)); - } - + msr_global_mutex_unlock(msr, msr->modsecurity->geo_lock, "Geo Lookup"); return 0; } @@ -367,13 +366,7 @@ int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **erro if ((country <= 0) || (country > GEO_COUNTRY_LAST)) { *error_msg = apr_psprintf(msr->mp, "No geo data for \"%s\" (country %d).", log_escape(msr->mp, target), country); msr_log(msr, 4, "%s", *error_msg); - - ret = apr_global_mutex_unlock(msr->modsecurity->geo_lock); - if (ret != APR_SUCCESS) { - msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s", - get_apr_error(msr->mp, ret)); - } - + msr_global_mutex_unlock(msr, msr->modsecurity->geo_lock, "Geo Lookup"); return 0; } @@ -398,13 +391,7 @@ int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **erro if ((country <= 0) || (country > GEO_COUNTRY_LAST)) { *error_msg = apr_psprintf(msr->mp, "No geo data for \"%s\" (country %d).", log_escape(msr->mp, target), country); msr_log(msr, 4, "%s", *error_msg); - - ret = apr_global_mutex_unlock(msr->modsecurity->geo_lock); - if (ret != APR_SUCCESS) { - msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s", - get_apr_error(msr->mp, ret)); - } - + msr_global_mutex_unlock(msr, msr->modsecurity->geo_lock, "Geo Lookup"); return 0; } if (msr->txcfg->debuglog_level >= 9) { @@ -493,13 +480,7 @@ int geo_lookup(modsec_rec *msr, geo_rec *georec, const char *target, char **erro } *error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" succeeded.", log_escape(msr->mp, target)); - - ret = apr_global_mutex_unlock(msr->modsecurity->geo_lock); - if (ret != APR_SUCCESS) { - msr_log(msr, 1, "Geo Lookup: Failed to lock proc mutex: %s", - get_apr_error(msr->mp, ret)); - } - + msr_global_mutex_unlock(msr, msr->modsecurity->geo_lock, "Geo Lookup"); return 1; } diff --git a/apache2/msc_geo.h b/apache2/msc_geo.h index 1293614608..afc8e72c74 100644 --- a/apache2/msc_geo.h +++ b/apache2/msc_geo.h @@ -25,7 +25,7 @@ #define GEO_COUNTRY_DATABASE 1 #define GEO_CITY_DATABASE_0 6 #define GEO_CITY_DATABASE_1 2 -#define GEO_COUNTRY_LAST 250 +#define GEO_COUNTRY_LAST 256 #define GEO_SEGMENT_RECORD_LENGTH 3 #define GEO_STATE_BEGIN_REV0 16700000 #define GEO_STATE_BEGIN_REV1 16000000 diff --git a/apache2/msc_json.c b/apache2/msc_json.c index 0f9a4645e2..cae7bf4f70 100644 --- a/apache2/msc_json.c +++ b/apache2/msc_json.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ - * Copyright (c) 2004-2011 Trustwave Holdings, Inc. (http://www.trustwave.com/) + * Copyright (c) 2004-2022 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 @@ -16,8 +16,12 @@ #ifdef WITH_YAJL +const char *base_offset=NULL; + int json_add_argument(modsec_rec *msr, const char *value, unsigned length) { + assert(msr != NULL); + assert(msr->json != NULL); msc_arg *arg = (msc_arg *) NULL; /** @@ -25,8 +29,7 @@ int json_add_argument(modsec_rec *msr, const char *value, unsigned length) * to reference this argument; for now we simply ignore these */ if (!msr->json->current_key) { - msr_log(msr, 3, "Cannot add scalar value without an associated key"); - return 1; + msr->json->current_key = ""; } arg = (msc_arg *) apr_pcalloc(msr->mp, sizeof(msc_arg)); @@ -48,13 +51,26 @@ int json_add_argument(modsec_rec *msr, const char *value, unsigned length) */ arg->value = apr_pstrmemdup(msr->mp, value, length); arg->value_len = length; + arg->value_origin_len = length; + arg->value_origin_offset = value-base_offset; arg->origin = "JSON"; if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "Adding JSON argument '%s' with value '%s'", arg->name, arg->value); } + if (apr_table_elts(msr->arguments)->nelts >= msr->txcfg->arguments_limit) { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Skipping request argument, over limit (%s): name \"%s\", value \"%s\"", + arg->origin, log_escape_ex(msr->mp, arg->name, arg->name_len), + log_escape_ex(msr->mp, arg->value, arg->value_len)); + } + msr->msc_reqbody_error = 1; + msr->json->yajl_error = apr_psprintf(msr->mp, "More than %ld JSON keys", msr->txcfg->arguments_limit); + return 0; + } + arg->marked_for_sanitization = 0; apr_table_addn(msr->arguments, log_escape_nq_ex(msr->mp, arg->name, arg->name_len), (void *) arg); @@ -74,6 +90,8 @@ int json_add_argument(modsec_rec *msr, const char *value, unsigned length) static int yajl_map_key(void *ctx, const unsigned char *key, size_t length) { modsec_rec *msr = (modsec_rec *) ctx; + assert(msr != NULL); + assert(msr->json != NULL); unsigned char *safe_key = (unsigned char *) NULL; /** @@ -105,6 +123,7 @@ static int yajl_map_key(void *ctx, const unsigned char *key, size_t length) static int yajl_null(void *ctx) { modsec_rec *msr = (modsec_rec *) ctx; + assert(msr != NULL); return json_add_argument(msr, "", 0); } @@ -115,6 +134,7 @@ static int yajl_null(void *ctx) static int yajl_boolean(void *ctx, int value) { modsec_rec *msr = (modsec_rec *) ctx; + assert(msr != NULL); if (value) { return json_add_argument(msr, "true", strlen("true")); @@ -130,6 +150,7 @@ static int yajl_boolean(void *ctx, int value) static int yajl_string(void *ctx, const unsigned char *value, size_t length) { modsec_rec *msr = (modsec_rec *) ctx; + assert(msr != NULL); return json_add_argument(msr, value, length); } @@ -142,10 +163,75 @@ static int yajl_string(void *ctx, const unsigned char *value, size_t length) static int yajl_number(void *ctx, const char *value, size_t length) { modsec_rec *msr = (modsec_rec *) ctx; + assert(msr != NULL); return json_add_argument(msr, value, length); } +static int yajl_start_array(void *ctx) { + modsec_rec *msr = (modsec_rec *) ctx; + assert(msr != NULL); + assert(msr->json != NULL); + + if (!msr->json->current_key && !msr->json->prefix) { + msr->json->prefix = apr_pstrdup(msr->mp, "array"); + msr->json->current_key = apr_pstrdup(msr->mp, "array"); + } + else if (msr->json->prefix) { + msr->json->prefix = apr_psprintf(msr->mp, "%s.%s", msr->json->prefix, + msr->json->current_key); + } + else { + msr->json->prefix = apr_pstrdup(msr->mp, msr->json->current_key); + } + msr->json->current_depth++; + if (msr->json->current_depth > msr->txcfg->reqbody_json_depth_limit) { + msr->json->depth_limit_exceeded = 1; + return 0; + } + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "New JSON hash context (prefix '%s')", msr->json->prefix); + } + + + return 1; +} + + +static int yajl_end_array(void *ctx) { + modsec_rec *msr = (modsec_rec *) ctx; + assert(msr != NULL); + assert(msr->json != NULL); + unsigned char *separator = (unsigned char *) NULL; + + /** + * If we have no prefix, then this is the end of a top-level hash and + * we don't do anything + */ + if (msr->json->prefix == NULL) return 1; + + /** + * Current prefix might or not include a separator character; top-level + * hash keys do not have separators in the variable name + */ + separator = strrchr(msr->json->prefix, '.'); + + if (separator) { + msr->json->prefix = apr_pstrmemdup(msr->mp, msr->json->prefix, + separator - msr->json->prefix); + } + else { + /** + * TODO: Check if it is safe to do this kind of pointer tricks + */ + msr->json->prefix = (unsigned char *) NULL; + } + msr->json->current_depth--; + + return 1; +} + /** * Callback for a new hash, which indicates a new subtree, labeled as the current * argument name, is being created @@ -153,6 +239,8 @@ static int yajl_number(void *ctx, const char *value, size_t length) static int yajl_start_map(void *ctx) { modsec_rec *msr = (modsec_rec *) ctx; + assert(msr != NULL); + assert(msr->json != NULL); /** * If we do not have a current_key, this is a top-level hash, so we do not @@ -171,6 +259,11 @@ static int yajl_start_map(void *ctx) else { msr->json->prefix = apr_pstrdup(msr->mp, msr->json->current_key); } + msr->json->current_depth++; + if (msr->json->current_depth > msr->txcfg->reqbody_json_depth_limit) { + msr->json->depth_limit_exceeded = 1; + return 0; + } if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "New JSON hash context (prefix '%s')", msr->json->prefix); @@ -186,6 +279,8 @@ static int yajl_start_map(void *ctx) static int yajl_end_map(void *ctx) { modsec_rec *msr = (modsec_rec *) ctx; + assert(msr != NULL); + assert(msr->json != NULL); unsigned char *separator = (unsigned char *) NULL; /** @@ -212,6 +307,7 @@ static int yajl_end_map(void *ctx) msr->json->current_key = msr->json->prefix; msr->json->prefix = (unsigned char *) NULL; } + msr->json->current_depth--; return 1; } @@ -220,6 +316,8 @@ static int yajl_end_map(void *ctx) * Initialise JSON parser. */ int json_init(modsec_rec *msr, char **error_msg) { + assert(msr != NULL); + assert(error_msg != NULL); /** * yajl configuration and callbacks */ @@ -233,11 +331,10 @@ int json_init(modsec_rec *msr, char **error_msg) { yajl_start_map, yajl_map_key, yajl_end_map, - NULL /* yajl_start_array */, - NULL /* yajl_end_array */ + yajl_start_array, + yajl_end_array }; - if (error_msg == NULL) return -1; *error_msg = NULL; msr_log(msr, 4, "JSON parser initialization"); @@ -250,6 +347,9 @@ int json_init(modsec_rec *msr, char **error_msg) { msr->json->prefix = (unsigned char *) NULL; msr->json->current_key = (unsigned char *) NULL; + msr->json->current_depth = 0; + msr->json->depth_limit_exceeded = 0; + /** * yajl initialization * @@ -271,14 +371,25 @@ int json_init(modsec_rec *msr, char **error_msg) { * Feed one chunk of data to the JSON parser. */ int json_process_chunk(modsec_rec *msr, const char *buf, unsigned int size, char **error_msg) { - if (error_msg == NULL) return -1; + assert(msr != NULL); + assert(msr->json != NULL); + assert(error_msg != NULL); *error_msg = NULL; + base_offset=buf; /* Feed our parser and catch any errors */ msr->json->status = yajl_parse(msr->json->handle, buf, size); if (msr->json->status != yajl_status_ok) { - /* We need to free the yajl error message later, how to do this? */ - *error_msg = yajl_get_error(msr->json->handle, 0, buf, size); + if (msr->json->depth_limit_exceeded) { + *error_msg = "JSON depth limit exceeded"; + } else { + if (msr->json->yajl_error) *error_msg = msr->json->yajl_error; + else { + char* yajl_err = yajl_get_error(msr->json->handle, 0, buf, size); + *error_msg = apr_pstrdup(msr->mp, yajl_err); + yajl_free_error(msr->json->handle, yajl_err); + } + } return -1; } @@ -289,16 +400,23 @@ int json_process_chunk(modsec_rec *msr, const char *buf, unsigned int size, char * Finalise JSON parsing. */ int json_complete(modsec_rec *msr, char **error_msg) { - char *json_data = (char *) NULL; + assert(msr != NULL); + assert(msr->json != NULL); + assert(error_msg != NULL); - if (error_msg == NULL) return -1; *error_msg = NULL; /* Wrap up the parsing process */ msr->json->status = yajl_complete_parse(msr->json->handle); if (msr->json->status != yajl_status_ok) { - /* We need to free the yajl error message later, how to do this? */ - *error_msg = yajl_get_error(msr->json->handle, 0, NULL, 0); + if (msr->json->depth_limit_exceeded) { + *error_msg = "JSON depth limit exceeded"; + } else { + char *yajl_err = yajl_get_error(msr->json->handle, 0, NULL, 0); + *error_msg = apr_pstrdup(msr->mp, yajl_err); + yajl_free_error(msr->json->handle, yajl_err); + } + return -1; } @@ -309,6 +427,8 @@ int json_complete(modsec_rec *msr, char **error_msg) { * Frees the resources used for JSON parsing. */ apr_status_t json_cleanup(modsec_rec *msr) { + assert(msr != NULL); + assert(msr->json != NULL); msr_log(msr, 4, "JSON: Cleaning up JSON results"); if (msr->json->handle != NULL) { yajl_free(msr->json->handle); diff --git a/apache2/msc_json.h b/apache2/msc_json.h index 02326ec03d..089dab4763 100644 --- a/apache2/msc_json.h +++ b/apache2/msc_json.h @@ -39,7 +39,9 @@ struct json_data { /* prefix is used to create data hierarchy (i.e., 'parent.child.value') */ unsigned char *prefix; - unsigned char *current_key; + const unsigned char *current_key; + long int current_depth; + int depth_limit_exceeded; }; /* Functions */ diff --git a/apache2/msc_logging.c b/apache2/msc_logging.c index c2274c7f57..8c7667ef5b 100644 --- a/apache2/msc_logging.c +++ b/apache2/msc_logging.c @@ -35,6 +35,7 @@ * the size counters, update the hash context. */ static int sec_auditlog_write(modsec_rec *msr, const char *data, unsigned int len) { + assert(msr != NULL); apr_size_t nbytes_written, nbytes = len; apr_status_t rc; @@ -86,6 +87,8 @@ static int sec_auditlog_write(modsec_rec *msr, const char *data, unsigned int le * some of the fields to make the log line shorter than _limit bytes. */ char *construct_log_vcombinedus_limited(modsec_rec *msr, int _limit, int *was_limited) { + assert(msr != NULL); + assert(was_limited != NULL); char *hostname; char *local_user, *remote_user; char *referer, *user_agent, *uniqueid; @@ -230,10 +233,24 @@ static char *construct_auditlog_filename(apr_pool_t *mp, const char *uniqueid) { char tstr[300]; apr_size_t len; + /** + * This is required for mpm-itk & mod_ruid2, though should be harmless for other implementations + * It also changes the return statement. + */ + char *userinfo; + apr_status_t rc; + apr_uid_t uid; + apr_gid_t gid; + apr_uid_current(&uid, &gid, mp); + rc = apr_uid_name_get(&userinfo, uid, mp); + if (rc != APR_SUCCESS) { + userinfo = apr_psprintf(mp, "%u", uid); + } + apr_time_exp_lt(&t, apr_time_now()); apr_strftime(tstr, &len, 299, "/%Y%m%d/%Y%m%d-%H%M/%Y%m%d-%H%M%S", &t); - return apr_psprintf(mp, "%s-%s", tstr, uniqueid); + return apr_psprintf(mp, "/%s%s-%s", userinfo, tstr, uniqueid); } /** @@ -391,6 +408,7 @@ static void sec_auditlog_write_producer_header(modsec_rec *msr) { * Ouput the Producer header into a JSON generator */ static void sec_auditlog_write_producer_header_json(modsec_rec *msr, yajl_gen g) { + assert(msr != NULL); char **signatures = NULL; int i; @@ -506,6 +524,7 @@ static msre_rule *return_chained_rule(const msre_rule *current, modsec_rec *msr) * \retval 1 On Success */ static int chained_is_matched(modsec_rec *msr, const msre_rule *next_rule) { + assert(msr != NULL); int i = 0; const msre_rule *rule = NULL; @@ -524,6 +543,7 @@ static int chained_is_matched(modsec_rec *msr, const msre_rule *next_rule) { * Write detailed information about performance metrics into a JSON generator */ static void format_performance_variables_json(modsec_rec *msr, yajl_gen g) { + assert(msr != NULL); yajl_string(g, "stopwatch"); yajl_gen_map_open(g); @@ -544,6 +564,8 @@ static void format_performance_variables_json(modsec_rec *msr, yajl_gen g) { * Write detailed information about a rule and its actionset into a JSON generator */ static void write_rule_json(modsec_rec *msr, const msre_rule *rule, yajl_gen g) { + assert(msr != NULL); + assert(rule != NULL); const apr_array_header_t *tarr; const apr_table_entry_t *telts; int been_opened = 0; @@ -632,6 +654,7 @@ static void write_rule_json(modsec_rec *msr, const msre_rule *rule, yajl_gen g) * Produce an audit log entry in JSON format. */ void sec_audit_logger_json(modsec_rec *msr) { + assert(msr != NULL); const apr_array_header_t *arr = NULL; apr_table_entry_t *te = NULL; const apr_array_header_t *tarr_pattern = NULL; @@ -734,11 +757,7 @@ void sec_audit_logger_json(modsec_rec *msr) { /* Lock the mutex, but only if we are using serial format. */ if (msr->txcfg->auditlog_type != AUDITLOG_CONCURRENT) { - rc = apr_global_mutex_lock(msr->modsecurity->auditlog_lock); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Audit log: Failed to lock global mutex: %s", - get_apr_error(msr->mp, rc)); - } + msr_global_mutex_lock(msr, msr->modsecurity->auditlog_lock, "Audit log"); } /** @@ -878,7 +897,7 @@ void sec_audit_logger_json(modsec_rec *msr) { for(i = 0; i < tarr->nelts; i++) { msc_arg *arg = (msc_arg *)telts[i].val; if (arg->origin != NULL && - strcmp(arg->origin, "BODY") != 0) + ( strcmp(arg->origin, "BODY") != 0 && strcmp(arg->origin, "JSON") !=0) ) continue; if (last_offset == 0) { /* The first time we're here. */ @@ -982,6 +1001,7 @@ void sec_audit_logger_json(modsec_rec *msr) { /* Write the sanitized chunk to the log * and advance to the next chunk. */ + chunk->data[chunk->length] = 0; yajl_string(g, chunk->data); chunk_offset += chunk->length; } @@ -1114,8 +1134,6 @@ void sec_audit_logger_json(modsec_rec *msr) { /* AUDITLOG_PART_TRAILER */ if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_TRAILER) != NULL) { - apr_time_t now = apr_time_now(); - /* Messages */ been_opened = 0; if (msr->alerts->nelts > 0) { @@ -1165,7 +1183,7 @@ void sec_audit_logger_json(modsec_rec *msr) { /* Stopwatch2 */ -#ifdef DLOG_NO_STOPWATCH +#ifdef LOG_NO_STOPWATCH if (msr->txcfg->debuglog_level >= 9) #endif format_performance_variables_json(msr, g); @@ -1444,14 +1462,8 @@ void sec_audit_logger_json(modsec_rec *msr) { * as it does not need an index file. */ if (msr->txcfg->auditlog_type != AUDITLOG_CONCURRENT) { - /* Unlock the mutex we used to serialise access to the audit log file. */ - rc = apr_global_mutex_unlock(msr->modsecurity->auditlog_lock); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Audit log: Failed to unlock global mutex: %s", - get_apr_error(msr->mp, rc)); - } - + msr_global_mutex_unlock(msr, msr->modsecurity->auditlog_lock, "Audit log"); return; } @@ -1521,6 +1533,7 @@ void sec_audit_logger_json(modsec_rec *msr) { * Produce an audit log entry in native format. */ void sec_audit_logger_native(modsec_rec *msr) { + assert(msr != NULL); const apr_array_header_t *arr = NULL; apr_table_entry_t *te = NULL; const apr_array_header_t *tarr_pattern = NULL; @@ -1621,11 +1634,7 @@ void sec_audit_logger_native(modsec_rec *msr) { /* Lock the mutex, but only if we are using serial format. */ if (msr->txcfg->auditlog_type != AUDITLOG_CONCURRENT) { - rc = apr_global_mutex_lock(msr->modsecurity->auditlog_lock); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Audit log: Failed to lock global mutex: %s", - get_apr_error(msr->mp, rc)); - } + msr_global_mutex_lock(msr, msr->modsecurity->auditlog_lock, "Audit log"); } @@ -1739,7 +1748,7 @@ void sec_audit_logger_native(modsec_rec *msr) { for(i = 0; i < tarr->nelts; i++) { msc_arg *arg = (msc_arg *)telts[i].val; if (arg->origin != NULL && - strcmp(arg->origin, "BODY") != 0) + ( strcmp(arg->origin, "BODY") != 0 && strcmp(arg->origin, "JSON") != 0) ) continue; if (last_offset == 0) { /* The first time we're here. */ @@ -1998,7 +2007,7 @@ void sec_audit_logger_native(modsec_rec *msr) { } /* Stopwatch; left in for compatibility reasons */ -#ifdef DLOG_NO_STOPWATCH +#ifdef LOG_NO_STOPWATCH if (msr->txcfg->debuglog_level >= 9) { #endif text = apr_psprintf(msr->mp, "Stopwatch: %" APR_TIME_T_FMT " %" APR_TIME_T_FMT " (- - -)\n", @@ -2013,7 +2022,7 @@ void sec_audit_logger_native(modsec_rec *msr) { "; %s\n", msr->request_time, (now - msr->request_time), perf_all); sec_auditlog_write(msr, text, strlen(text)); } -#ifdef DLOG_NO_STOPWATCH +#ifdef LOG_NO_STOPWATCH } #endif @@ -2209,7 +2218,7 @@ void sec_audit_logger_native(modsec_rec *msr) { sec_auditlog_write(msr, text, strlen(text)); } else { if ((rule != NULL) && (rule->actionset != NULL) && !rule->actionset->is_chained && (rule->chain_starter == NULL)) { - text = apr_psprintf(msr->mp, "%s\n\n", rule->unparsed); + text = apr_psprintf(msr->mp, "%s\n", rule->unparsed); sec_auditlog_write(msr, text, strlen(text)); } } @@ -2224,14 +2233,8 @@ void sec_audit_logger_native(modsec_rec *msr) { */ if (msr->txcfg->auditlog_type != AUDITLOG_CONCURRENT) { sec_auditlog_write(msr, "\n", 1); - /* Unlock the mutex we used to serialise access to the audit log file. */ - rc = apr_global_mutex_unlock(msr->modsecurity->auditlog_lock); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "Audit log: Failed to unlock global mutex: %s", - get_apr_error(msr->mp, rc)); - } - + msr_global_mutex_unlock(msr, msr->modsecurity->auditlog_lock, "Audit log"); return; } @@ -2301,6 +2304,7 @@ void sec_audit_logger_native(modsec_rec *msr) { */ void sec_audit_logger(modsec_rec *msr) { #ifdef WITH_YAJL + assert(msr != NULL); if (msr->txcfg->auditlog_format == AUDITLOGFORMAT_JSON) { sec_audit_logger_json(msr); } else { diff --git a/apache2/msc_lua.c b/apache2/msc_lua.c index 51be1745b2..e1715c03b5 100644 --- a/apache2/msc_lua.c +++ b/apache2/msc_lua.c @@ -154,6 +154,8 @@ static int l_log(lua_State *L) { * */ static apr_array_header_t *resolve_tfns(lua_State *L, int idx, modsec_rec *msr, apr_pool_t *mp) { + assert(msr != NULL); + assert(mp != NULL); apr_array_header_t *tfn_arr = NULL; msre_tfn_metadata *tfn = NULL; char *name = NULL; @@ -406,11 +408,13 @@ static const struct luaL_Reg mylib[] = { * */ int lua_execute(msc_script *script, char *param, modsec_rec *msr, msre_rule *rule, char **error_msg) { + assert(script != NULL); + assert(msr != NULL); + assert(error_msg != NULL); apr_time_t time_before; lua_State *L = NULL; int rc = 0; - if (error_msg == NULL) return -1; *error_msg = NULL; if (msr->txcfg->debuglog_level >= 8) { @@ -429,12 +433,12 @@ int lua_execute(msc_script *script, char *param, modsec_rec *msr, msre_rule *rul #else /* Create new state. */ -#if LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 || LUA_VERSION_NUM == 501 +#if LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 || LUA_VERSION_NUM == 504 || LUA_VERSION_NUM == 501 L = luaL_newstate(); #elif LUA_VERSION_NUM == 500 L = lua_open(); #else -#error We are only tested under Lua 5.0, 5.1, 5.2, or 5.3. +#error We are only tested under Lua 5.0, 5.1, 5.2, 5.3, or 5.4. #endif luaL_openlibs(L); @@ -459,10 +463,10 @@ int lua_execute(msc_script *script, char *param, modsec_rec *msr, msre_rule *rul /* Register functions. */ #if LUA_VERSION_NUM == 500 || LUA_VERSION_NUM == 501 luaL_register(L, "m", mylib); -#elif LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 +#elif LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 || LUA_VERSION_NUM == 504 luaL_setfuncs(L, mylib, 0); #else -#error We are only tested under Lua 5.0, 5.1, 5.2, or 5.3. +#error We are only tested under Lua 5.0, 5.1, 5.2, 5.3, or 5.4. #endif lua_setglobal(L, "m"); diff --git a/apache2/msc_multipart.c b/apache2/msc_multipart.c index 9bf327d2bc..dc24248de7 100644 --- a/apache2/msc_multipart.c +++ b/apache2/msc_multipart.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* Copyright (c) 2004-2022 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 @@ -20,18 +20,22 @@ #include "msc_util.h" #include "msc_parsers.h" -void validate_quotes(modsec_rec *msr, char *data) { +void validate_quotes(modsec_rec *msr, char *data, char quote) { + assert(msr != NULL); int i, len; - if(msr == NULL) - return; - if(msr->mpd == NULL) return; if(data == NULL) return; + // if the value was enclosed in double quotes, then we don't care about + // a single quote character within the name. + if (quote == '"') { + return; + } + len = strlen(data); for(i = 0; i < len; i++) { @@ -78,6 +82,8 @@ static char *multipart_construct_filename(modsec_rec *msr) { * */ static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value) { + assert(msr != NULL); + assert(c_d_value != NULL); char *p = NULL, *t = NULL; /* accept only what we understand */ @@ -123,9 +129,10 @@ static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value) * set so the user can deal with it in the rules if they so wish. */ + char quote = '\0'; if ((*p == '"') || (*p == '\'')) { /* quoted */ - char quote = *p; + quote = *p; // remember which quote character was used for the value if (quote == '\'') { msr->mpd->flag_invalid_quoting = 1; @@ -182,7 +189,7 @@ static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value) if (strcmp(name, "name") == 0) { - validate_quotes(msr, value); + validate_quotes(msr, value, quote); msr->multipart_name = apr_pstrdup(msr->mp, value); @@ -201,7 +208,7 @@ static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value) else if (strcmp(name, "filename") == 0) { - validate_quotes(msr, value); + validate_quotes(msr, value, quote); msr->multipart_filename = apr_pstrdup(msr->mp, value); @@ -248,9 +255,10 @@ static int multipart_parse_content_disposition(modsec_rec *msr, char *c_d_value) * */ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) { + assert(msr != NULL); + assert(error_msg != NULL); int i, len, rc; - if (error_msg == NULL) return -1; *error_msg = NULL; /* Check for nul bytes. */ @@ -318,7 +326,14 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) { } msr->mpd->mpp_state = 1; + msr->mpd->mpp_substate_part_data_read = 0; msr->mpd->mpp->last_header_name = NULL; + + /* Record the last part header line in the collection */ + if (msr->mpd->mpp->last_header_line != NULL) { + *(char **)apr_array_push(msr->mpd->mpp->header_lines) = msr->mpd->mpp->last_header_line; + msr_log(msr, 9, "Multipart: Added part header line \"%s\"", msr->mpd->mpp->last_header_line); + } } else { /* Header line. */ @@ -372,12 +387,28 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) { *error_msg = apr_psprintf(msr->mp, "Multipart: Part header too long."); return -1; } + if ((msr->mpd->mpp->last_header_line != NULL) && (msr->mpd->mpp->last_header_name != NULL) + && (new_value != NULL)) { + msr->mpd->mpp->last_header_line = apr_psprintf(msr->mp, + "%s: %s", msr->mpd->mpp->last_header_name, new_value); + } + } else { char *header_name, *header_value, *data; /* new header */ + /* Record the most recently-seen part header line in the collection */ + if (msr->mpd->mpp->last_header_line != NULL) { + *(char **)apr_array_push(msr->mpd->mpp->header_lines) = msr->mpd->mpp->last_header_line; + msr_log(msr, 9, "Multipart: Added part header line \"%s\"", msr->mpd->mpp->last_header_line); + } + data = msr->mpd->buf; + + msr->mpd->mpp->last_header_line = apr_pstrdup(msr->mp, data); + remove_lf_crlf_inplace(msr->mpd->mpp->last_header_line); + while((*data != ':') && (*data != '\0')) data++; if (*data == '\0') { *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid part header (colon missing): %s.", @@ -393,6 +424,16 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) { return -1; } + /* check if multipart header contains any invalid characters */ + char *ch = header_name; + while(*ch != '\0') { + if (*ch < 33 || *ch > 126) { + *error_msg = apr_psprintf(msr->mp, "Multipart: Invalid part header (contains invalid character)."); + return -1; + } + ch++; + } + /* extract the value value */ data++; while((*data == '\t') || (*data == ' ')) data++; @@ -424,13 +465,16 @@ static int multipart_process_part_header(modsec_rec *msr, char **error_msg) { * */ static int multipart_process_part_data(modsec_rec *msr, char **error_msg) { + assert(msr != NULL); + assert(error_msg != NULL); char *p = msr->mpd->buf + (MULTIPART_BUF_SIZE - msr->mpd->bufleft); char localreserve[2] = { '\0', '\0' }; /* initialized to quiet warning */ int bytes_reserved = 0; - if (error_msg == NULL) return -1; *error_msg = NULL; + msr->mpd->mpp_substate_part_data_read = 1; + /* Preserve some bytes for later. */ if ( ((MULTIPART_BUF_SIZE - msr->mpd->bufleft) >= 1) && (*(p - 1) == '\n') ) @@ -596,6 +640,8 @@ static int multipart_process_part_data(modsec_rec *msr, char **error_msg) { * */ static char *multipart_combine_value_parts(modsec_rec *msr, apr_array_header_t *value_parts) { + assert(msr != NULL); + assert(value_parts != NULL); value_part_t **parts = NULL; char *rval = apr_palloc(msr->mp, msr->mpd->mpp->length + 1); unsigned long int offset; @@ -620,6 +666,7 @@ static char *multipart_combine_value_parts(modsec_rec *msr, apr_array_header_t * * */ static int multipart_process_boundary(modsec_rec *msr, int last_part, char **error_log) { + assert(msr != NULL); /* if there was a part being built finish it */ if (msr->mpd->mpp != NULL) { /* close the temp file */ @@ -673,10 +720,14 @@ static int multipart_process_boundary(modsec_rec *msr, int last_part, char **err if (msr->mpd->mpp == NULL) return -1; msr->mpd->mpp->type = MULTIPART_FORMDATA; msr->mpd->mpp_state = 0; + msr->mpd->mpp_substate_part_data_read = 0; msr->mpd->mpp->headers = apr_table_make(msr->mp, 10); if (msr->mpd->mpp->headers == NULL) return -1; msr->mpd->mpp->last_header_name = NULL; + msr->mpd->mpp->last_header_line = NULL; + msr->mpd->mpp->header_lines = apr_array_make(msr->mp, 2, sizeof(char *)); + if (msr->mpd->mpp->header_lines == NULL) return -1; msr->mpd->reserve[0] = 0; msr->mpd->reserve[1] = 0; @@ -695,42 +746,29 @@ static int multipart_boundary_characters_valid(char *boundary) { if (p == NULL) return -1; - while((c = *p) != '\0') { - /* Control characters and space not allowed. */ - if (c < 32) { - return 0; - } - - /* Non-ASCII characters not allowed. */ - if (c > 126) { + while ((c = *p) != '\0') { + // Check against allowed list defined in RFC2046 page 21 + if (!( + ('0' <= c && c <= '9') + || ('A' <= c && c <= 'Z') + || ('a' <= c && c <= 'z') + || (c == ' ' && *(p + 1) != '\0') // space allowed, but not as last character + || c == '\'' + || c == '(' + || c == ')' + || c == '+' + || c == '_' + || c == ',' + || c == '-' + || c == '.' + || c == '/' + || c == ':' + || c == '=' + || c == '?' + )) { return 0; } - switch(c) { - /* Special characters not allowed. */ - case '(' : - case ')' : - case '<' : - case '>' : - case '@' : - case ',' : - case ';' : - case ':' : - case '\\' : - case '"' : - case '/' : - case '[' : - case ']' : - case '?' : - case '=' : - return 0; - break; - - default : - /* Do nothing. */ - break; - } - p++; } @@ -765,7 +803,8 @@ static int multipart_count_boundary_params(apr_pool_t *mp, const char *header_va * */ int multipart_init(modsec_rec *msr, char **error_msg) { - if (error_msg == NULL) return -1; + assert(msr != NULL); + assert(error_msg != NULL); *error_msg = NULL; msr->mpd = (multipart_data *)apr_pcalloc(msr->mp, sizeof(multipart_data)); @@ -929,6 +968,8 @@ int multipart_init(modsec_rec *msr, char **error_msg) { * is clear that there is no more data to be processed. */ int multipart_complete(modsec_rec *msr, char **error_msg) { + assert(msr != NULL); + assert(error_msg != NULL); if (msr->mpd == NULL) return 1; if (msr->txcfg->debuglog_level >= 4) { @@ -989,6 +1030,19 @@ int multipart_complete(modsec_rec *msr, char **error_msg) { && (*(msr->mpd->buf + 2 + strlen(msr->mpd->boundary)) == '-') && (*(msr->mpd->buf + 2 + strlen(msr->mpd->boundary) + 1) == '-') ) { + if ((msr->mpd->crlf_state_buf_end == 2) && (msr->mpd->flag_lf_line != 1)) { + msr->mpd->flag_lf_line = 1; + if (msr->mpd->flag_crlf_line) { + msr_log(msr, 4, "Multipart: Warning: mixed line endings used (CRLF/LF)."); + } else { + msr_log(msr, 4, "Multipart: Warning: incorrect line endings used (LF)."); + } + } + if (msr->mpd->mpp_substate_part_data_read == 0) { + /* it looks like the final boundary, but it's where part data should begin */ + msr->mpd->flag_invalid_part = 1; + msr_log(msr, 4, "Multipart: Warning: Invalid part (data contains final boundary)"); + } /* Looks like the final boundary - process it. */ if (multipart_process_boundary(msr, 1 /* final */, error_msg) < 0) { msr->mpd->flag_error = 1; @@ -1019,10 +1073,12 @@ int multipart_complete(modsec_rec *msr, char **error_msg) { int multipart_process_chunk(modsec_rec *msr, const char *buf, unsigned int size, char **error_msg) { + assert(msr != NULL); + assert(buf != NULL); + assert(error_msg != NULL); char *inptr = (char *)buf; unsigned int inleft = size; - if (error_msg == NULL) return -1; *error_msg = NULL; if (size == 0) return 1; @@ -1081,54 +1137,63 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf, if ( (strlen(msr->mpd->buf) >= strlen(msr->mpd->boundary) + 2) && (strncmp(msr->mpd->buf + 2, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) ) { - char *boundary_end = msr->mpd->buf + 2 + strlen(msr->mpd->boundary); - int is_final = 0; + if (msr->mpd->crlf_state_buf_end == 2) { + msr->mpd->flag_lf_line = 1; + } + if ((msr->mpd->mpp_substate_part_data_read == 0) && (msr->mpd->boundary_count > 0)) { + /* string matches our boundary, but it's where part data should begin */ + msr->mpd->flag_invalid_part = 1; + msr_log(msr, 4, "Multipart: Warning: Invalid part (data contains boundary)"); + } else { + char *boundary_end = msr->mpd->buf + 2 + strlen(msr->mpd->boundary); + int is_final = 0; + + /* Is this the final boundary? */ + if ((*boundary_end == '-') && (*(boundary_end + 1)== '-')) { + is_final = 1; + boundary_end += 2; + + if (msr->mpd->is_complete != 0) { + msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, + "Multipart: Invalid boundary (final duplicate)."); + return -1; + } + } - /* Is this the final boundary? */ - if ((*boundary_end == '-') && (*(boundary_end + 1)== '-')) { - is_final = 1; - boundary_end += 2; + /* Allow for CRLF and LF line endings. */ + if ( ( (*boundary_end == '\r') + && (*(boundary_end + 1) == '\n') + && (*(boundary_end + 2) == '\0') ) + || ( (*boundary_end == '\n') + && (*(boundary_end + 1) == '\0') ) ) + { + if (*boundary_end == '\n') { + msr->mpd->flag_lf_line = 1; + } else { + msr->mpd->flag_crlf_line = 1; + } - if (msr->mpd->is_complete != 0) { - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, - "Multipart: Invalid boundary (final duplicate)."); - return -1; - } - } + if (multipart_process_boundary(msr, (is_final ? 1 : 0), error_msg) < 0) { + msr->mpd->flag_error = 1; + return -1; + } - /* Allow for CRLF and LF line endings. */ - if ( ( (*boundary_end == '\r') - && (*(boundary_end + 1) == '\n') - && (*(boundary_end + 2) == '\0') ) - || ( (*boundary_end == '\n') - && (*(boundary_end + 1) == '\0') ) ) - { - if (*boundary_end == '\n') { - msr->mpd->flag_lf_line = 1; - } else { - msr->mpd->flag_crlf_line = 1; - } + if (is_final) { + msr->mpd->is_complete = 1; + } - if (multipart_process_boundary(msr, (is_final ? 1 : 0), error_msg) < 0) { + processed_as_boundary = 1; + msr->mpd->boundary_count++; + } + else { + /* error */ msr->mpd->flag_error = 1; + *error_msg = apr_psprintf(msr->mp, + "Multipart: Invalid boundary: %s", + log_escape_nq(msr->mp, msr->mpd->buf)); return -1; } - - if (is_final) { - msr->mpd->is_complete = 1; - } - - processed_as_boundary = 1; - msr->mpd->boundary_count++; - } - else { - /* error */ - msr->mpd->flag_error = 1; - *error_msg = apr_psprintf(msr->mp, - "Multipart: Invalid boundary: %s", - log_escape_nq(msr->mp, msr->mpd->buf)); - return -1; } } else { /* It looks like a boundary but we couldn't match it. */ char *p = NULL; @@ -1227,6 +1292,21 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf, msr->mpd->bufptr = msr->mpd->buf; msr->mpd->bufleft = MULTIPART_BUF_SIZE; msr->mpd->buf_contains_line = (c == 0x0a) ? 1 : 0; + + if (c == 0x0a) { + if (msr->mpd->crlf_state == 1) { + msr->mpd->crlf_state = 3; + } else { + msr->mpd->crlf_state = 2; + } + } + msr->mpd->crlf_state_buf_end = msr->mpd->crlf_state; + } + + if (c == 0x0d) { + msr->mpd->crlf_state = 1; + } else if (c != 0x0a) { + msr->mpd->crlf_state = 0; } if ((msr->mpd->is_complete) && (inleft != 0)) { @@ -1247,6 +1327,7 @@ int multipart_process_chunk(modsec_rec *msr, const char *buf, * */ apr_status_t multipart_cleanup(modsec_rec *msr) { + assert(msr != NULL); int keep_files = 0; if (msr->mpd == NULL) return -1; @@ -1327,14 +1408,14 @@ apr_status_t multipart_cleanup(modsec_rec *msr) { } else { /* Move file to the upload dir. */ if (parts[i]->tmp_file_name != NULL) { + const char *new_filename = NULL; + const char *new_basename = NULL; + if (strcmp(msr->txcfg->upload_dir, msr->txcfg->tmp_dir) == 0) { msr_log(msr, 4, "Not moving part to identical location"); continue; } - const char *new_filename = NULL; - const char *new_basename = NULL; - /* make sure it is closed first */ if (parts[i]->tmp_file_fd > 0) { close(parts[i]->tmp_file_fd); @@ -1373,6 +1454,7 @@ apr_status_t multipart_cleanup(modsec_rec *msr) { * */ int multipart_get_arguments(modsec_rec *msr, char *origin, apr_table_t *arguments) { + assert(msr != NULL); multipart_part **parts; int i; diff --git a/apache2/msc_multipart.h b/apache2/msc_multipart.h index a0f6a08ddf..a9c20b9b19 100644 --- a/apache2/msc_multipart.h +++ b/apache2/msc_multipart.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* Copyright (c) 2004-2022 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 @@ -55,6 +55,8 @@ struct multipart_part { char *last_header_name; apr_table_t *headers; + char *last_header_line; + apr_array_header_t *header_lines; unsigned int offset; unsigned int length; @@ -81,6 +83,15 @@ struct multipart_data { char *bufptr; int bufleft; + /* line ending status seen immediately before current position. + * 0 = neither LF nor CR; 1 = prev char CR; 2 = prev char LF alone; + * 3 = prev two chars were CRLF + */ + int crlf_state; + + /* crlf_state at end of previous buffer */ + int crlf_state_buf_end; + unsigned int buf_offset; /* pointer that keeps track of a part while @@ -94,6 +105,14 @@ struct multipart_data { */ int mpp_state; + /* part parsing substate; if mpp_state is 1 (collecting + * data), then for this variable: + * 0 means we have not yet read any data between the + * post-headers blank line and the next boundary + * 1 means we have read at some data after that blank line + */ + int mpp_substate_part_data_read; + /* because of the way this parsing algorithm * works we hold back the last two bytes of * each data chunk so that we can discard it diff --git a/apache2/msc_parsers.c b/apache2/msc_parsers.c index 57f291ec2a..793549a5f6 100644 --- a/apache2/msc_parsers.c +++ b/apache2/msc_parsers.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* Copyright (c) 2004-2022 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 @@ -21,6 +21,9 @@ int parse_cookies_v0(modsec_rec *msr, char *_cookie_header, apr_table_t *cookies, const char *delim) { + assert(msr != NULL); + assert(cookies != NULL); + assert(delim != NULL); char *attr_name = NULL, *attr_value = NULL; char *cookie_header; char *saveptr = NULL; @@ -95,6 +98,8 @@ int parse_cookies_v0(modsec_rec *msr, char *_cookie_header, int parse_cookies_v1(modsec_rec *msr, char *_cookie_header, apr_table_t *cookies) { + assert(msr != NULL); + assert(cookies != NULL); char *attr_name = NULL, *attr_value = NULL, *p = NULL; char *prev_attr_name = NULL; char *cookie_header = NULL; @@ -239,6 +244,8 @@ int parse_arguments(modsec_rec *msr, const char *s, apr_size_t inputlength, int argument_separator, const char *origin, apr_table_t *arguments, int *invalid_count) { + assert(msr != NULL); + assert(invalid_count != NULL); msc_arg *arg; apr_size_t i, j; char *value = NULL; @@ -266,7 +273,7 @@ int parse_arguments(modsec_rec *msr, const char *s, apr_size_t inputlength, if (status == 0) { /* parameter name */ arg->name_origin_offset = i; - while ((s[i] != '=') && (s[i] != argument_separator) && (i < inputlength)) { + while ((i < inputlength) && (s[i] != '=') && (s[i] != argument_separator)) { buf[j] = s[i]; j++; i++; @@ -340,12 +347,31 @@ int parse_arguments(modsec_rec *msr, const char *s, apr_size_t inputlength, */ void add_argument(modsec_rec *msr, apr_table_t *arguments, msc_arg *arg) { + assert(msr != NULL); + assert(arguments != NULL); + assert(arg != NULL); + arg->marked_for_sanitization = 0; if (msr->txcfg->debuglog_level >= 5) { msr_log(msr, 5, "Adding request argument (%s): name \"%s\", value \"%s\"", arg->origin, log_escape_ex(msr->mp, arg->name, arg->name_len), log_escape_ex(msr->mp, arg->value, arg->value_len)); } - apr_table_addn(arguments, log_escape_nq_ex(msr->mp, arg->name, arg->name_len), (void *)arg); + if (apr_table_elts(arguments)->nelts >= msr->txcfg->arguments_limit) { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Skipping request argument, over limit (%s): name \"%s\", value \"%s\"", + arg->origin, log_escape_ex(msr->mp, arg->name, arg->name_len), + log_escape_ex(msr->mp, arg->value, arg->value_len)); + } + if (msr->msc_reqbody_error != 1) { + char *error_msg = apr_psprintf(msr->mp, "SecArgumentsLimit exceeded"); + msr->msc_reqbody_error = 1; + if (error_msg != NULL) { + msr->msc_reqbody_error_msg = error_msg; + } + } + } else { + apr_table_addn(arguments, log_escape_nq_ex(msr->mp, arg->name, arg->name_len), (void *)arg); + } } diff --git a/apache2/msc_pcre.c b/apache2/msc_pcre.c index 8534a20914..edaac3ff8f 100644 --- a/apache2/msc_pcre.c +++ b/apache2/msc_pcre.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* Copyright (c) 2004-2022 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 @@ -20,18 +20,25 @@ */ static apr_status_t msc_pcre_cleanup(msc_regex_t *regex) { if (regex != NULL) { +#ifndef WITH_PCRE + if (regex->match_context != NULL) { + pcre2_match_context_free(regex->match_context); + regex->match_context = NULL; + } + if (regex->re != NULL) { + pcre2_code_free(regex->re); + regex->re = NULL; + } +#else if (regex->pe != NULL) { -#if defined(VERSION_NGINX) pcre_free(regex->pe); -#else - free(regex->pe); -#endif regex->pe = NULL; } if (regex->re != NULL) { pcre_free(regex->re); regex->re = NULL; } +#endif } return APR_SUCCESS; @@ -48,6 +55,88 @@ static apr_status_t msc_pcre_cleanup(msc_regex_t *regex) { void *msc_pregcomp_ex(apr_pool_t *pool, const char *pattern, int options, const char **_errptr, int *_erroffset, int match_limit, int match_limit_recursion) +#ifndef WITH_PCRE +{ + msc_regex_t *regex = NULL; + PCRE2_SPTR pcre2_pattern; + uint32_t pcre2_options; + int error_number = 0; + PCRE2_SIZE error_offset = 0; + pcre2_match_context *match_context = NULL; + + regex = apr_pcalloc(pool, sizeof(msc_regex_t)); + if (regex == NULL) return NULL; + regex->pattern = pattern; + + pcre2_pattern = (PCRE2_SPTR)pattern; + pcre2_options = (uint32_t)options; + + regex->re = pcre2_compile(pcre2_pattern, PCRE2_ZERO_TERMINATED, + pcre2_options, &error_number, &error_offset, NULL); + if (regex->re == NULL) { + if (_erroffset != NULL) { + *_erroffset = (int)error_offset; + } + PCRE2_UCHAR buffer[256]; + // Get the error message from the error code + pcre2_get_error_message(error_number, buffer, sizeof(buffer)); +#ifdef DEBUG_CONF + * _errptr = apr_psprintf(pool, "%s - pattern = %s", buffer, pattern); +#else + * _errptr = apr_pstrdup(pool, buffer); +#endif + return NULL; + } + +#ifdef WITH_PCRE_JIT + regex->jit_compile_rc = pcre2_jit_compile(regex->re, PCRE2_JIT_COMPLETE); +#endif + + /* Setup the pcre2 match context */ + regex->match_context = NULL; + match_context = pcre2_match_context_create(NULL); + if (match_context == NULL) { + return NULL; + } + + /* Prefer the match limit passed as an arg; else use compilation default */ + { + uint32_t final_match_limit = 0; + if (match_limit > 0) { + final_match_limit = match_limit; + pcre2_set_match_limit(match_context, final_match_limit); + } +#ifdef MODSEC_PCRE_MATCH_LIMIT + else { + final_match_limit = MODSEC_PCRE_MATCH_LIMIT; + pcre2_set_match_limit(match_context, final_match_limit); + } +#endif /* MODSEC_PCRE_MATCH_LIMIT */ + } + + /* Prefer the depth limit passed as an arg; else use compilation default */ + { + uint32_t final_match_limit_recursion = 0; + if (match_limit_recursion > 0) { + final_match_limit_recursion = match_limit_recursion; + pcre2_set_depth_limit(match_context, final_match_limit_recursion); + } +#ifdef MODSEC_PCRE_MATCH_LIMIT_RECURSION + else { + final_match_limit_recursion = MODSEC_PCRE_MATCH_LIMIT_RECURSION; + pcre2_set_depth_limit(match_context, final_match_limit_recursion); + } +#endif /* MODSEC_PCRE_MATCH_LIMIT_RECURSION */ + } + + regex->match_context = match_context; + + apr_pool_cleanup_register(pool, (void *)regex, + (apr_status_t (*)(void *))msc_pcre_cleanup, apr_pool_cleanup_null); + + return regex; +} +#else /* not WITH_PCRE2 */ { const char *errptr = NULL; int erroffset; @@ -75,11 +164,7 @@ void *msc_pregcomp_ex(apr_pool_t *pool, const char *pattern, int options, /* Setup the pcre_extra record if pcre_study did not already do it */ if (pe == NULL) { -#if defined(VERSION_NGINX) - pe = pcre_malloc(sizeof(pcre_extra)); -#else - pe = malloc(sizeof(pcre_extra)); -#endif + pe = (pcre_extra*)pcre_malloc(sizeof(pcre_extra)); if (pe == NULL) { return NULL; } @@ -131,6 +216,7 @@ void *msc_pregcomp_ex(apr_pool_t *pool, const char *pattern, int options, return regex; } +#endif /* WITH_PCRE2 */ /** * Compiles the provided regular expression pattern. Calls msc_pregcomp_ex() @@ -143,9 +229,9 @@ void *msc_pregcomp(apr_pool_t *pool, const char *pattern, int options, } /** - * Executes regular expression with extended options. - * Returns PCRE_ERROR_NOMATCH when there is no match, error code < -1 - * on errors, and a value > 0 when there is a match. + * Executes regular expression with extended options (or match context) + * Returns PCRE_ERROR_NOMATCH (or PCRE2_ERROR_NOMATCH), + * error code < -1 on errors, and a value > 0 when there is a match. */ int msc_regexec_ex(msc_regex_t *regex, const char *s, unsigned int slen, int startoffset, int options, int *ovector, int ovecsize, char **error_msg) @@ -153,7 +239,52 @@ int msc_regexec_ex(msc_regex_t *regex, const char *s, unsigned int slen, if (error_msg == NULL) return -1000; /* To differentiate from PCRE as it already uses -1. */ *error_msg = NULL; +#ifndef WITH_PCRE + { + PCRE2_SPTR pcre2_s; + int pcre2_ret; + pcre2_match_data *match_data; + PCRE2_SIZE *pcre2_ovector = NULL; + + pcre2_s = (PCRE2_SPTR)s; + match_data = pcre2_match_data_create_from_pattern(regex->re, NULL); + +#ifdef WITH_PCRE_JIT + if (regex->jit_compile_rc == 0) { + pcre2_ret = pcre2_jit_match(regex->re, pcre2_s, slen, + (PCRE2_SIZE)(startoffset), (uint32_t)options, match_data, regex->match_context); + } + if (regex->jit_compile_rc != 0 || pcre2_ret == PCRE2_ERROR_JIT_STACKLIMIT) { + pcre2_ret = pcre2_match(regex->re, pcre2_s, slen, + (PCRE2_SIZE)(startoffset), (PCRE2_NO_JIT | (uint32_t)options), match_data, regex->match_context); + } +#else + pcre2_ret = pcre2_match(regex->re, pcre2_s, slen, + (PCRE2_SIZE)(startoffset), (uint32_t)options, match_data, regex->match_context); +#endif + if (match_data != NULL) { + if (ovector != NULL) { + pcre2_ovector = pcre2_get_ovector_pointer(match_data); + if (pcre2_ovector != NULL) { + for (int i = 0; ((i < pcre2_ret) && ((i*2) <= ovecsize)); i++) { + if ((i*2) < ovecsize) { + ovector[2*i] = pcre2_ovector[2*i]; + ovector[2*i+1] = pcre2_ovector[2*i+1]; + } + } + } + } + pcre2_match_data_free(match_data); + } + if ((pcre2_ret*2) > ovecsize) { + return 0; + } else { + return pcre2_ret; + } + } +#else return pcre_exec(regex->re, regex->pe, s, slen, startoffset, options, ovector, ovecsize); +#endif } /** @@ -188,6 +319,10 @@ int msc_regexec(msc_regex_t *regex, const char *s, unsigned int slen, */ int msc_fullinfo(msc_regex_t *regex, int what, void *where) { +#ifndef WITH_PCRE + return pcre2_pattern_info(regex->re, (uint32_t)what, where); +#else return pcre_fullinfo(regex->re, regex->pe, what, where); +#endif } diff --git a/apache2/msc_pcre.h b/apache2/msc_pcre.h index bbaa818b29..e693236e3d 100644 --- a/apache2/msc_pcre.h +++ b/apache2/msc_pcre.h @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* Copyright (c) 2004-2022 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 @@ -17,7 +17,14 @@ typedef struct msc_regex_t msc_regex_t; +#ifndef WITH_PCRE +#define PCRE2_CODE_UNIT_WIDTH 8 +#include "pcre2.h" +#else #include "pcre.h" +#endif + +#ifndef WITH_PCRE2 #ifndef PCRE_ERROR_MATCHLIMIT /* Define for compile, but not valid in this version of PCRE. */ @@ -29,12 +36,23 @@ typedef struct msc_regex_t msc_regex_t; #define PCRE_ERROR_RECURSIONLIMIT (-21) #endif /* PCRE_ERROR_RECURSIONLIMIT */ +#endif + #include "apr_general.h" #include "modsecurity.h" struct msc_regex_t { +#ifndef WITH_PCRE + pcre2_code *re; + pcre2_match_context *match_context; +#ifdef WITH_PCRE_JIT + int jit_compile_rc; +#endif + +#else void *re; void *pe; +#endif const char *pattern; }; diff --git a/apache2/msc_release.h b/apache2/msc_release.h index f2fe898d5d..e496dc6659 100644 --- a/apache2/msc_release.h +++ b/apache2/msc_release.h @@ -38,7 +38,7 @@ #define MODSEC_VERSION_MAJOR "2" #define MODSEC_VERSION_MINOR "9" -#define MODSEC_VERSION_MAINT "1" +#define MODSEC_VERSION_MAINT "12" #define MODSEC_VERSION_TYPE "" #define MODSEC_VERSION_RELEASE "" diff --git a/apache2/msc_remote_rules.c b/apache2/msc_remote_rules.c index 8a6df9e0ab..a3cf688923 100644 --- a/apache2/msc_remote_rules.c +++ b/apache2/msc_remote_rules.c @@ -312,6 +312,11 @@ int msc_remote_download_content(apr_pool_t *mp, const char *uri, const char *key struct curl_slist *headers_chunk = NULL; #ifdef WIN32 char *buf = malloc(sizeof(TCHAR) * (2048 + 1)); + if (buf == NULL) { /* malloc failed... */ + *error_msg = apr_psprintf(mp, "Unable to allocate memory"); + ret = -2; + goto failed; + } char *ptr = NULL; DWORD res_len; #endif @@ -326,8 +331,8 @@ int msc_remote_download_content(apr_pool_t *mp, const char *uri, const char *key headers_chunk = curl_slist_append(headers_chunk, header_key); } - /* Make it TLS 1.x only. */ - curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1); + /* Make it TLS 1.2 at least. */ + curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); #ifdef WIN32 res_len = SearchPathA(NULL, "curl-ca-bundle.crt", NULL, (2048 + 1), buf, &ptr); @@ -714,9 +719,7 @@ int msc_remote_add_rules_from_uri(cmd_parms *orig_parms, if (plain_text[len] == '\n') { const char *rule = NULL; - int tmp = len; char *cmd_name = NULL; - char *word = NULL; const command_rec *cmd; ap_directive_t *newdir; @@ -787,6 +790,7 @@ int msc_remote_add_rules_from_uri(cmd_parms *orig_parms, { msc_remote_clean_chunk(&downloaded_content); } + return 0; #else *error_msg = "SecRemoteRules was not enabled during ModSecurity " \ "compilation."; diff --git a/apache2/msc_reqbody.c b/apache2/msc_reqbody.c index a8411fa04d..0877c82598 100644 --- a/apache2/msc_reqbody.c +++ b/apache2/msc_reqbody.c @@ -25,6 +25,7 @@ void msre_engine_reqbody_processor_register(msre_engine *engine, const char *name, void *fn_init, void *fn_process, void *fn_complete) { + assert(engine != NULL); msre_reqbody_processor_metadata *metadata = (msre_reqbody_processor_metadata *)apr_pcalloc(engine->mp, sizeof(msre_reqbody_processor_metadata)); @@ -41,6 +42,8 @@ void msre_engine_reqbody_processor_register(msre_engine *engine, * Prepare to accept the request body (part 2). */ static apr_status_t modsecurity_request_body_start_init(modsec_rec *msr, char **error_msg) { + assert(msr != NULL); + assert(error_msg != NULL); *error_msg = NULL; if(msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) { @@ -80,6 +83,8 @@ static apr_status_t modsecurity_request_body_start_init(modsec_rec *msr, char ** * Prepare to accept the request body (part 1). */ apr_status_t modsecurity_request_body_start(modsec_rec *msr, char **error_msg) { + assert(msr != NULL); + assert(error_msg != NULL); *error_msg = NULL; msr->msc_reqbody_length = 0; msr->stream_input_length = 0; @@ -161,6 +166,8 @@ apr_status_t modsecurity_request_body_start(modsec_rec *msr, char **error_msg) { static apr_status_t modsecurity_request_body_store_disk(modsec_rec *msr, const char *data, apr_size_t length, char **error_msg) { + assert(msr != NULL); + assert(error_msg != NULL); apr_size_t i; *error_msg = NULL; @@ -181,6 +188,8 @@ static apr_status_t modsecurity_request_body_store_disk(modsec_rec *msr, static apr_status_t modsecurity_request_body_store_memory(modsec_rec *msr, const char *data, apr_size_t length, char **error_msg) { + assert(msr != NULL); + assert(error_msg != NULL); *error_msg = NULL; /* Would storing this chunk mean going over the limit? */ @@ -309,6 +318,8 @@ static apr_status_t modsecurity_request_body_store_memory(modsec_rec *msr, apr_status_t modsecurity_request_body_store(modsec_rec *msr, const char *data, apr_size_t length, char **error_msg) { + assert(msr != NULL); + assert(error_msg != NULL); *error_msg = NULL; /* If we have a processor for this request body send @@ -396,13 +407,14 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, /* Check that we are not over the request body no files limit. */ if (msr->msc_reqbody_no_files_length >= (unsigned long) msr->txcfg->reqbody_no_files_limit) { - *error_msg = apr_psprintf(msr->mp, "Request body no files data length is larger than the " "configured limit (%ld).", msr->txcfg->reqbody_no_files_limit); if (msr->txcfg->debuglog_level >= 1) { msr_log(msr, 1, "%s", *error_msg); } + msr->msc_reqbody_error = 1; + if ((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) { return -5; } else if (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL) { @@ -411,7 +423,6 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, } } - /* Store data. */ if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) { return modsecurity_request_body_store_memory(msr, data, length, error_msg); @@ -428,10 +439,19 @@ apr_status_t modsecurity_request_body_store(modsec_rec *msr, } apr_status_t modsecurity_request_body_to_stream(modsec_rec *msr, const char *buffer, int buflen, char **error_msg) { + assert(msr != NULL); + assert(error_msg != NULL); + assert(buffer != NULL || buflen == 0); +#ifndef MSC_LARGE_STREAM_INPUT char *stream_input_body = NULL; char *data = NULL; int first_pkt = 0; +#else + apr_size_t allocate_length = 0; + char* allocated = NULL; +#endif +#ifndef MSC_LARGE_STREAM_INPUT if(msr->stream_input_data == NULL) { msr->stream_input_data = (char *)calloc(sizeof(char), msr->stream_input_length + 1); first_pkt = 1; @@ -443,8 +463,8 @@ apr_status_t modsecurity_request_body_to_stream(modsec_rec *msr, const char *buf if(data == NULL) return -1; - memset(data, 0, msr->stream_input_length + 1 - buflen); memcpy(data, msr->stream_input_data, msr->stream_input_length - buflen); + data[msr->stream_input_length - buflen] = '\0'; stream_input_body = (char *)realloc(msr->stream_input_data, msr->stream_input_length + 1); @@ -452,35 +472,84 @@ apr_status_t modsecurity_request_body_to_stream(modsec_rec *msr, const char *buf } if (msr->stream_input_data == NULL) { - if(data) { - free(data); - data = NULL; - } + if (data) free(data); *error_msg = apr_psprintf(msr->mp, "Unable to allocate memory to hold request body on stream. Asked for %" APR_SIZE_T_FMT " bytes.", msr->stream_input_length + 1); return -1; } - memset(msr->stream_input_data, 0, msr->stream_input_length+1); - if(first_pkt) { memcpy(msr->stream_input_data, buffer, msr->stream_input_length); } else { memcpy(msr->stream_input_data, data, msr->stream_input_length - buflen); memcpy(msr->stream_input_data+(msr->stream_input_length - buflen), buffer, buflen); } + msr->stream_input_data[msr->stream_input_length] = '\0'; - if(data) { - free(data); - data = NULL; + if (data) free(data); +#else + if (msr->stream_input_data == NULL) { + // Is the request body length known beforehand? (requests that are not Transfer-Encoding: chunked) + if (msr->request_content_length > 0) { + // Use min of Content-Length and SecRequestBodyLimit + allocate_length = msr->request_content_length < msr->txcfg->reqbody_limit ? msr->request_content_length : msr->txcfg->reqbody_limit; + } + else { + // We don't know how this request is going to be, so hope for just buflen to begin with (requests that are Transfer-Encoding: chunked) + allocate_length = buflen; + } + + allocated = (char*) calloc(allocate_length, sizeof(char)); + if (allocated) { + msr->stream_input_data = allocated; + msr->stream_input_allocated_length = allocate_length; + } + else { + *error_msg = apr_psprintf( + msr->mp, + "Unable to allocate memory to hold request body on stream. Asked for %" APR_SIZE_T_FMT " bytes.", + allocate_length); + return -1; + } + } + else { + // Do we need to expand the space we have previously allocated? + if ((msr->stream_input_length + buflen) > msr->stream_input_allocated_length) { + // If this becomes a hotspot again, consider increasing by some percent extra each time, for fewer reallocs + allocate_length = msr->stream_input_length + buflen; + + allocated = (char*) realloc(msr->stream_input_data, allocate_length); + if (allocated) { + msr->stream_input_data = allocated; + msr->stream_input_allocated_length = allocate_length; + } + else { + *error_msg = apr_psprintf( + msr->mp, + "Unable to reallocate memory to hold request body on stream. Asked for %" APR_SIZE_T_FMT " bytes.", + allocate_length); + free(msr->stream_input_data); + msr->stream_input_data = NULL; + msr->stream_input_length = 0; + msr->stream_input_allocated_length = 0; + return -1; + } + } } + // Append buffer to msr->stream_input_data + memcpy(msr->stream_input_data + msr->stream_input_length, buffer, buflen); + msr->stream_input_length += buflen; +#endif return 1; } + /** * Replace a bunch of chunks holding a request body with a single large chunk. */ static apr_status_t modsecurity_request_body_end_raw(modsec_rec *msr, char **error_msg) { + assert(msr != NULL); + assert(error_msg != NULL); msc_data_chunk **chunks, *one_chunk; char *d; int i, sofar; @@ -554,6 +623,8 @@ static apr_status_t modsecurity_request_body_end_raw(modsec_rec *msr, char **err * */ static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr, char **error_msg) { + assert(msr != NULL); + assert(error_msg != NULL); int invalid_count = 0; *error_msg = NULL; @@ -583,6 +654,8 @@ static apr_status_t modsecurity_request_body_end_urlencoded(modsec_rec *msr, cha * Stops receiving the request body. */ apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) { + assert(msr != NULL); + assert(error_msg != NULL); *error_msg = NULL; /* Close open file descriptors, if any. */ @@ -596,6 +669,19 @@ apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) { /* Note that we've read the body. */ msr->msc_reqbody_read = 1; + + /* Check that we are not over the request body no files limit. */ + if (msr->msc_reqbody_no_files_length >= (unsigned long)msr->txcfg->reqbody_no_files_limit) { + *error_msg = apr_psprintf(msr->mp, "Request body no files data length is larger than the " + "configured limit (%ld).", msr->txcfg->reqbody_no_files_limit); + if (msr->txcfg->debuglog_level >= 1) { + msr_log(msr, 1, "%s", *error_msg); + } + + return -5; + } + + /* Finalise body processing. */ if ((msr->msc_reqbody_processor != NULL) && (msr->msc_reqbody_error == 0)) { char *my_error_msg = NULL; @@ -637,7 +723,7 @@ apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) { } else if (strcmp(msr->msc_reqbody_processor, "JSON") == 0) { #ifdef WITH_YAJL - if (json_complete(msr, &my_error_msg) < 0) { + if (json_complete(msr, &my_error_msg) < 0 && msr->msc_reqbody_length > 0) { *error_msg = apr_psprintf(msr->mp, "JSON parser error: %s", my_error_msg); msr->msc_reqbody_error = 1; msr->msc_reqbody_error_msg = *error_msg; @@ -680,6 +766,8 @@ apr_status_t modsecurity_request_body_end(modsec_rec *msr, char **error_msg) { * Prepares to forward the request body. */ apr_status_t modsecurity_request_body_retrieve_start(modsec_rec *msr, char **error_msg) { + assert(msr != NULL); + assert(error_msg != NULL); *error_msg = NULL; if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) { @@ -726,6 +814,7 @@ apr_status_t modsecurity_request_body_retrieve_start(modsec_rec *msr, char **err * */ apr_status_t modsecurity_request_body_retrieve_end(modsec_rec *msr) { + assert(msr != NULL); if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) { if (msr->msc_reqbody_fd > 0) { close(msr->msc_reqbody_fd); @@ -748,6 +837,8 @@ apr_status_t modsecurity_request_body_retrieve_end(modsec_rec *msr) { apr_status_t modsecurity_request_body_retrieve(modsec_rec *msr, msc_data_chunk **chunk, long int nbytes, char **error_msg) { + assert(msr != NULL); + assert(error_msg != NULL); msc_data_chunk **chunks; *error_msg = NULL; @@ -849,6 +940,8 @@ apr_status_t modsecurity_request_body_retrieve(modsec_rec *msr, * */ apr_status_t modsecurity_request_body_clear(modsec_rec *msr, char **error_msg) { + assert(msr != NULL); + assert(error_msg != NULL); *error_msg = NULL; /* Release memory we used to store request body data. */ @@ -884,15 +977,15 @@ apr_status_t modsecurity_request_body_clear(modsec_rec *msr, char **error_msg) { if (msr->msc_reqbody_filename != NULL) { if (keep_body) { + /* Move request body (which is a file) to the storage area. */ + const char *put_filename = NULL; + const char *put_basename = NULL; + if (strcmp(msr->txcfg->upload_dir, msr->txcfg->tmp_dir) == 0) { msr_log(msr, 4, "Not moving file to identical location."); goto nullify; } - /* Move request body (which is a file) to the storage area. */ - const char *put_filename = NULL; - const char *put_basename = NULL; - /* Construct the new filename. */ put_basename = file_basename(msr->msc_reqbody_mp, msr->msc_reqbody_filename); if (put_basename == NULL) { diff --git a/apache2/msc_status_engine.c b/apache2/msc_status_engine.c index 834ecc3ed9..be4150bc23 100644 --- a/apache2/msc_status_engine.c +++ b/apache2/msc_status_engine.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* Copyright (c) 2004-2022 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 @@ -19,6 +19,9 @@ #ifdef WIN32 #include #include +#else +#include +#include #endif #ifdef DARWIN @@ -42,6 +45,10 @@ #include #endif +#ifdef APLOG_USE_MODULE + APLOG_USE_MODULE(security2); +#endif + // Bese32 encode, based on: // https://code.google.com/p/google-authenticator/source/browse/libpam/base32.c int DSOLOCAL msc_status_engine_base32_encode(char *encoded, @@ -157,7 +164,6 @@ int DSOLOCAL msc_status_engine_mac_address (unsigned char *mac) #ifdef DARWIN struct ifaddrs* ifaphead; struct ifaddrs* ifap; - int i = 0; if ( getifaddrs( &ifaphead ) != 0 ) { goto failed; @@ -167,7 +173,7 @@ int DSOLOCAL msc_status_engine_mac_address (unsigned char *mac) for ( ifap = ifaphead; ifap; ifap = ifap->ifa_next ) { struct sockaddr_dl* sdl = (struct sockaddr_dl*)ifap->ifa_addr; if ( sdl && ( sdl->sdl_family == AF_LINK ) && ( sdl->sdl_type == IFT_ETHER ) - && mac[0] && mac[1] && mac[2] && i < 6) { + && mac[0] && mac[1] && mac[2]) { apr_snprintf(mac, MAC_ADDRESS_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x", (unsigned char)LLADDR(sdl)[0], @@ -176,7 +182,7 @@ int DSOLOCAL msc_status_engine_mac_address (unsigned char *mac) (unsigned char)LLADDR(sdl)[3], (unsigned char)LLADDR(sdl)[4], (unsigned char)LLADDR(sdl)[5]); - goto end; + break; } } @@ -206,7 +212,6 @@ int DSOLOCAL msc_status_engine_mac_address (unsigned char *mac) } if ( ioctl( sock, SIOCGIFHWADDR, ifr ) == 0 ) { - int i = 0; if (!ifr->ifr_addr.sa_data[0] && !ifr->ifr_addr.sa_data[1] && !ifr->ifr_addr.sa_data[2]) { continue; @@ -220,7 +225,7 @@ int DSOLOCAL msc_status_engine_mac_address (unsigned char *mac) (unsigned char)ifr->ifr_addr.sa_data[4], (unsigned char)ifr->ifr_addr.sa_data[5]); - goto end; + break; } } close( sock ); @@ -263,7 +268,7 @@ int DSOLOCAL msc_status_engine_mac_address (unsigned char *mac) (unsigned char)pAdapter->Address[3], (unsigned char)pAdapter->Address[4], (unsigned char)pAdapter->Address[5]); - goto end; + break; } pAdapter = pAdapter->Next; } @@ -271,7 +276,6 @@ int DSOLOCAL msc_status_engine_mac_address (unsigned char *mac) free(pAdapterInfo); #endif -end: return 0; failed: return -1; @@ -280,7 +284,7 @@ int DSOLOCAL msc_status_engine_mac_address (unsigned char *mac) int DSOLOCAL msc_status_engine_unique_id (unsigned char *digest) { unsigned char hex_digest[APR_SHA1_DIGESTSIZE]; - unsigned char *mac_address = NULL; + unsigned char *mac_address = NULL, *digptr; char *machine_name = NULL; int ret = 0; int i = 0; @@ -314,9 +318,10 @@ int DSOLOCAL msc_status_engine_unique_id (unsigned char *digest) apr_sha1_update(&context, mac_address, strlen(mac_address)); apr_sha1_final(hex_digest, &context); - for (i = 0; i < APR_SHA1_DIGESTSIZE; i++) + for (i = 0, digptr = digest; i < APR_SHA1_DIGESTSIZE; i++) { - sprintf(digest, "%s%02x", digest, hex_digest[i]); + sprintf(digptr, "%02x", hex_digest[i]); + digptr += 2; } failed_set_machine_name: @@ -342,8 +347,13 @@ int DSOLOCAL msc_beacon_string (char *beacon_string, int beacon_string_max_len) apr = APR_VERSION_STRING; apr_loaded = apr_version_string(); +#ifndef WITH_PCRE + apr_snprintf(pcre, 7, "%d.%d", PCRE2_MAJOR, PCRE2_MINOR); + pcre_loaded = ""; /* complete this if/when status reactivated */ +#else apr_snprintf(pcre, 7, "%d.%d", PCRE_MAJOR, PCRE_MINOR); pcre_loaded = pcre_version(); +#endif #ifdef WITH_LUA lua = LUA_VERSION; #endif @@ -360,7 +370,7 @@ int DSOLOCAL msc_beacon_string (char *beacon_string, int beacon_string_max_len) /* 6 represents: strlen("(null)") */ beacon_string_len = (modsec ? strlen(modsec) : 6) + (apache ? strlen(apache) : 6) + (apr ? strlen(apr) : 6) + - (apr_loaded ? strlen(apr_loaded) : 6) + (pcre ? strlen(pcre) : 6) + + (apr_loaded ? strlen(apr_loaded) : 6) + strlen(pcre) + (pcre_loaded ? strlen(pcre_loaded) : 6) + (lua ? strlen(lua) : 6) + (libxml ? strlen(libxml) : 6) + (APR_SHA1_DIGESTSIZE * 2); @@ -496,4 +506,3 @@ int msc_status_engine_call (void) { return ret; } - diff --git a/apache2/msc_tree.c b/apache2/msc_tree.c index c672bd5fd4..feabdd0e03 100644 --- a/apache2/msc_tree.c +++ b/apache2/msc_tree.c @@ -14,6 +14,7 @@ #include #include +#include #if APR_HAVE_STDINT_H #include #endif @@ -534,6 +535,8 @@ int TreeCheckData(TreePrefix *prefix, CPTData *prefix_data, unsigned int netmask } int TreePrefixNetmask(modsec_rec *msr, TreePrefix *prefix, unsigned int netmask, int flag) { + // msr can be NULL; + assert(!msr || msr->txcfg != NULL); CPTData *prefix_data = NULL; int ret = 0; @@ -573,6 +576,8 @@ int TreePrefixNetmask(modsec_rec *msr, TreePrefix *prefix, unsigned int netmask, } TreeNode *CPTRetriveNode(modsec_rec *msr, unsigned char *buffer, unsigned int ip_bitmask, TreeNode *node) { + // msr can be NULL; + assert(!msr || msr->txcfg != NULL); unsigned int x, y; if(node == NULL) { @@ -619,6 +624,8 @@ TreeNode *CPTRetriveParentNode(TreeNode *node) { } TreeNode *CPTFindElementIPNetblock(modsec_rec *msr, unsigned char *ipdata, unsigned char ip_bitmask, TreeNode *node) { + // msr can be NULL; + assert(!msr || msr->txcfg != NULL); TreeNode *netmask_node = NULL; int mask = 0, bytes = 0; int i = 0, j = 0; @@ -655,15 +662,22 @@ TreeNode *CPTFindElementIPNetblock(modsec_rec *msr, unsigned char *ipdata, unsig } node = CPTRetriveNode(msr, ipdata, ip_bitmask, node); + if (!node) { + if (msr && msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "CPTFindElementIPNetblock: No tree node found."); + } + return NULL; + } - if (node && node->bit != ip_bitmask) { + + if (node->bit != ip_bitmask) { if (msr && msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "CPTFindElementIPNetblock: Found a tree node but netmask is different."); } return NULL; } - if (node && node->prefix == NULL) { + if (node->prefix == NULL) { if (msr && msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "CPTFindElementIPNetblock: Found a tree node but prefix is NULL."); } @@ -699,6 +713,8 @@ TreeNode *CPTFindElementIPNetblock(modsec_rec *msr, unsigned char *ipdata, unsig } TreeNode *CPTFindElement(modsec_rec *msr, unsigned char *ipdata, unsigned int ip_bitmask, CPTTree *tree) { + // msr can be NULL; + assert(!msr || msr->txcfg != NULL); TreeNode *node = NULL; int mask = 0, bytes = 0; unsigned char temp_data[NETMASK_256-1]; @@ -754,7 +770,7 @@ TreeNode *CPTFindElement(modsec_rec *msr, unsigned char *ipdata, unsigned int ip return node; } - if (memcmp(node->prefix->buffer, temp_data, bytes) == 0) { + if ((node->netmasks == NULL) && (memcmp(node->prefix->buffer, temp_data, bytes) == 0)) { mask = SHIFT_LEFT_MASK(8 - ip_bitmask % 8); if ((ip_bitmask % 8) == 0) { @@ -780,6 +796,8 @@ TreeNode *CPTFindElement(modsec_rec *msr, unsigned char *ipdata, unsigned int ip } TreeNode *CPTIpMatch(modsec_rec *msr, unsigned char *ipdata, CPTTree *tree, int type) { + // msr can be NULL; + assert(!msr || msr->txcfg != NULL); if(tree == NULL) { if (msr && msr->txcfg->debuglog_level >= 9) { @@ -815,7 +833,7 @@ TreeNode *CPTIpMatch(modsec_rec *msr, unsigned char *ipdata, CPTTree *tree, int } TreeNode *TreeAddIP(const char *buffer, CPTTree *tree, int type) { - unsigned long ip, ret; + unsigned long ret; unsigned char netmask_v4 = NETMASK_32, netmask_v6 = NETMASK_128; char ip_strv4[NETMASK_32], ip_strv6[NETMASK_128]; struct in_addr addr4; @@ -831,13 +849,14 @@ TreeNode *TreeAddIP(const char *buffer, CPTTree *tree, int type) { switch(type) { case IPV4_TREE: - memset(&addr4, 0, sizeof(addr4)); + memset(&(addr4.s_addr), 0, sizeof(addr4.s_addr)); memset(ip_strv4, 0x0, NETMASK_32); strncpy(ip_strv4, buffer, sizeof(ip_strv4)); *(ip_strv4 + (sizeof(ip_strv4) - 1)) = '\0'; ptr = strdup(ip_strv4); + if (ptr == NULL) return NULL; // No way to return a clean error message netmask_v4 = is_netmask_v4(ptr); if (netmask_v4 > NETMASK_32) { @@ -858,26 +877,23 @@ TreeNode *TreeAddIP(const char *buffer, CPTTree *tree, int type) { ip_strv4[pos] = '\0'; } - ret = inet_pton(AF_INET, ip_strv4, &addr4); + ret = inet_pton(AF_INET, ip_strv4, &(addr4.s_addr)); if (ret <= 0) { return NULL; } - - ip = addr4.s_addr; - tree->count++; - - return CPTAddElement((unsigned char *)&ip, NETMASK_32, tree, netmask_v4); + return CPTAddElement((unsigned char *)&(addr4.s_addr), NETMASK_32, tree, netmask_v4); case IPV6_TREE: - memset(&addr6, 0, sizeof(addr6)); + memset(&(addr6.s6_addr), 0, sizeof(addr6.s6_addr)); memset(ip_strv6, 0x0, NETMASK_128); strncpy(ip_strv6, buffer, sizeof(ip_strv6)); *(ip_strv6 + sizeof(ip_strv6) - 1) = '\0'; ptr = strdup(ip_strv6); + if (ptr == NULL) return NULL; // No way to return a clean error message netmask_v6 = is_netmask_v6(ptr); if (netmask_v6 > NETMASK_128) { @@ -898,7 +914,7 @@ TreeNode *TreeAddIP(const char *buffer, CPTTree *tree, int type) { ip_strv6[pos] = '\0'; } - ret = inet_pton(AF_INET6, ip_strv6, &addr6); + ret = inet_pton(AF_INET6, ip_strv6, &(addr6.s6_addr)); if (ret <= 0) { @@ -907,7 +923,7 @@ TreeNode *TreeAddIP(const char *buffer, CPTTree *tree, int type) { tree->count++; - return CPTAddElement((unsigned char *)&addr6.s6_addr, NETMASK_128, tree, netmask_v6); + return CPTAddElement((unsigned char *)&(addr6.s6_addr), NETMASK_128, tree, netmask_v6); default: return NULL; } diff --git a/apache2/msc_util.c b/apache2/msc_util.c index 33447ba0d0..471a98a80b 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* Copyright (c) 2004-2022 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 @@ -22,6 +22,10 @@ #include "msc_release.h" #include "msc_util.h" +#include +#if APR_HAVE_ARPA_INET_H +#include +#endif #include #include #include "modsecurity_config.h" @@ -100,82 +104,44 @@ int swap_int32(int x) { * \retval rval On Success */ char *utf8_unicode_inplace_ex(apr_pool_t *mp, unsigned char *input, long int input_len, int *changed) { - int unicode_len = 0, length = 0; - unsigned int d = 0, count = 0; + int unicode_len = 0; + unsigned int d = 0; unsigned char c, *utf; char *rval, *data; - unsigned int i, len, j; + unsigned int i, len; unsigned int bytes_left = input_len; - unsigned char *unicode = NULL; - *changed = 0; + assert(input != NULL); - len = input_len * 7 + 1; + *changed = 0; + /* RFC3629 states that UTF-8 are encoded using sequences of 1 to 4 octets. */ + /* Max size per character should fit in 4 bytes (%u01020304) */ + len = input_len * 10 + 1; data = rval = apr_palloc(mp, len); if (rval == NULL) return NULL; - - if (input == NULL) return NULL; - - for(i = 0; i < bytes_left;) { + for (i = 0; i < bytes_left;) { unicode_len = 0; d = 0; utf = (unsigned char *)&input[i]; - c = *utf; - /* If first byte begins with binary 0 it is single byte encoding */ + /* If first byte begins with binary 0 it may be single byte encoding */ if ((c & 0x80) == 0) { - /* single byte unicode (7 bit ASCII equivilent) has no validation */ - count++; - if(count <= len) { - if(c == 0) - *data = x2c(&c); - else - *data++ = c; + if (c == 0) { + unicode_len = 2; + d = utf[1]; } - } /* If first byte begins with binary 110 it is two byte encoding*/ else if ((c & 0xE0) == 0xC0) { /* check we have at least two bytes */ if (bytes_left < 2) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING; /* check second byte starts with binary 10 */ - else if (((*(utf + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; + else if ((utf[1] & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; else { unicode_len = 2; - count+=6; - if(count <= len) { - /* compute character number */ - d = ((c & 0x1F) << 6) | (*(utf + 1) & 0x3F); - *data++ = '%'; - *data++ = 'u'; - unicode = apr_psprintf(mp, "%x", d); - length = strlen(unicode); - - switch(length) { - case 1: - *data++ = '0'; - *data++ = '0'; - *data++ = '0'; - break; - case 2: - *data++ = '0'; - *data++ = '0'; - break; - case 3: - *data++ = '0'; - break; - case 4: - case 5: - break; - } - - for(j=0; j= 0xF5) { - *data++ = c; - } + if (c >= 0xF5) unicode_len = UNICODE_ERROR_RESTRICTED_CHARACTER; /* check we have at least four bytes */ - if (bytes_left < 4) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING; + else if (bytes_left < 4) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING; /* check second byte starts with binary 10 */ - else if (((*(utf + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; + else if ((utf[1] & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; /* check third byte starts with binary 10 */ else if (((*(utf + 2)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; /* check forth byte starts with binary 10 */ else if (((*(utf + 3)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING; else { unicode_len = 4; - count+=7; - if(count <= len) { - /* compute character number */ - d = ((c & 0x07) << 18) | ((*(utf + 1) & 0x3F) << 12) | ((*(utf + 2) & 0x3F) << 6) | (*(utf + 3) & 0x3F); - *data++ = '%'; - *data++ = 'u'; - unicode = apr_psprintf(mp, "%x", d); - length = strlen(unicode); - - switch(length) { - case 1: - *data++ = '0'; - *data++ = '0'; - *data++ = '0'; - break; - case 2: - *data++ = '0'; - *data++ = '0'; - break; - case 3: - *data++ = '0'; - break; - case 4: - case 5: - break; - } - - for(j=0; j= 0xD800) && (d <= 0xDFFF)) { - count++; - if(count <= len) - *data++ = c; - } - + if ((d >= 0xD800) && (d <= 0xDFFF)) unicode_len = UNICODE_ERROR_RESTRICTED_CHARACTER; /* check for overlong */ - if ((unicode_len == 4) && (d < 0x010000)) { - /* four byte could be represented with less bytes */ - count++; - if(count <= len) - *data++ = c; - } - else if ((unicode_len == 3) && (d < 0x0800)) { - /* three byte could be represented with less bytes */ - count++; - if(count <= len) - *data++ = c; - } - else if ((unicode_len == 2) && (d < 0x80)) { - /* two byte could be represented with less bytes */ - count++; - if(count <= len) - *data++ = c; - } + if ((unicode_len == 4) && (d < 0x010000)) unicode_len = UNICODE_ERROR_OVERLONG_CHARACTER; + /* three byte could be represented with less bytes */ + if ((unicode_len == 3) && (d < 0x0800)) unicode_len = UNICODE_ERROR_OVERLONG_CHARACTER; + /* two byte could be represented with less bytes */ + if ((unicode_len == 2) && (d < 0x80)) unicode_len = UNICODE_ERROR_OVERLONG_CHARACTER; - if(unicode_len > 0) { + if (unicode_len > 0) { i += unicode_len; - } else { + sprintf(data, "%%u%04x", d); + data += 6; + *changed = 1; + } + else { + /* any other first byte is invalid (RFC 3629), so assume it's an ASCII character */ + *data++ = c; i++; } } - *data ='\0'; - + *data = '\0'; return rval; } @@ -401,7 +281,8 @@ char *parse_pm_content(const char *op_parm, unsigned short int op_len, msre_rule char converted = 0; int i, x; unsigned char bin = 0, esc = 0, bin_offset = 0; - unsigned char bin_parm[3], c = 0; + unsigned char c = 0; + unsigned char bin_parm[3] = { 0 }; char *processed = NULL; content = apr_pstrdup(rule->ruleset->mp, op_parm); @@ -661,6 +542,7 @@ int convert_to_int(const char c) * \retval 0 On Sucess|Fail */ int set_match_to_tx(modsec_rec *msr, int capture, const char *match, int tx_n) { + assert(msr != NULL); if (capture) { msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); @@ -1123,10 +1005,12 @@ char *current_logtime(apr_pool_t *mp) { char tstr[100]; apr_size_t len; - apr_time_exp_lt(&t, apr_time_now()); + apr_time_t now = apr_time_now(); + apr_time_exp_lt(&t, now); - apr_strftime(tstr, &len, 80, "%d/%b/%Y:%H:%M:%S ", &t); - apr_snprintf(tstr + strlen(tstr), 80 - strlen(tstr), "%c%.2d%.2d", + apr_strftime(tstr, &len, 80, "%d/%b/%Y:%H:%M:%S.", &t); + apr_snprintf(tstr + strlen(tstr), 80 - strlen(tstr), "%06ld %c%.2d%.2d", + (long)apr_time_usec(now), t.tm_gmtoff < 0 ? '-' : '+', t.tm_gmtoff / (60 * 60), (t.tm_gmtoff / 60) % 60); return apr_pstrdup(mp, tstr); @@ -2370,6 +2254,7 @@ apr_fileperms_t mode2fileperms(int mode) { * Generate a single variable. */ char *construct_single_var(modsec_rec *msr, char *name) { + assert(msr != NULL); char *varname = NULL; char *param = NULL; msre_var *var = NULL; @@ -2378,6 +2263,7 @@ char *construct_single_var(modsec_rec *msr, char *name) { /* Extract variable name and its parameter from the script. */ varname = apr_pstrdup(msr->mp, name); + if (varname == NULL) return NULL; param = strchr(varname, '.'); if (param != NULL) { *param = '\0'; @@ -2424,9 +2310,11 @@ int msc_headers_to_buffer(const apr_array_header_t *arr, char *buffer, int write_to_buffer = 0; int i = 0; const apr_table_entry_t *te = NULL; + char *ptr = NULL; if (buffer != NULL && buffer_length > 0) { write_to_buffer = 1; + ptr = buffer; } te = (apr_table_entry_t *)arr->elts; @@ -2442,7 +2330,9 @@ int msc_headers_to_buffer(const apr_array_header_t *arr, char *buffer, goto not_enough_memory; } - sprintf(buffer, "%s%s: %s\n", buffer, key, value); + assert(ptr && ptr < buffer + buffer_length); + sprintf(ptr, "%s: %s\n", key, value); + ptr = buffer + headers_length; /* for the next entry. */ } } @@ -2462,28 +2352,16 @@ int msc_headers_to_buffer(const apr_array_header_t *arr, char *buffer, int read_line(char *buf, int len, FILE *fp) { - char *tmp; - - if (buf == NULL) - { - return -1; - } - - memset(buf, '\0', len*sizeof(char)); + if (buf == NULL) return -1; - if (fgets(buf, len, fp) == NULL) - { + if (fgets(buf, len, fp) == NULL) { *buf = '\0'; return 0; } - else - { - if ((tmp = strrchr(buf, '\n')) != NULL) - { - *tmp = '\0'; - } - } - + + char* tmp; + if ((tmp = strrchr(buf, '\n')) != NULL) *tmp = '\0'; + return 1; } @@ -2622,10 +2500,7 @@ int ip_tree_from_uri(TreeRoot **rtree, char *uri, apr_pool_t *mp, char **error_msg) { TreeNode *tnode = NULL; - apr_status_t rc; int line = 0; - apr_file_t *fd; - char *start; int res; struct msc_curl_memory_buffer_t chunk; @@ -2695,6 +2570,10 @@ int ip_tree_from_uri(TreeRoot **rtree, char *uri, int tree_contains_ip(apr_pool_t *mp, TreeRoot *rtree, const char *value, modsec_rec *msr, char **error_msg) { + assert(mp != NULL); + assert(value != NULL); + // msr can be NULL; + assert(error_msg != NULL); struct in_addr in; #if APR_HAVE_IPV6 struct in6_addr in6; @@ -2706,26 +2585,26 @@ int tree_contains_ip(apr_pool_t *mp, TreeRoot *rtree, } if (strchr(value, ':') == NULL) { - if (inet_pton(AF_INET, value, &in) <= 0) { + if (inet_pton(AF_INET, value, &(in.s_addr)) <= 0) { *error_msg = apr_psprintf(mp, "IPmatch: bad IPv4 " \ "specification \"%s\".", value); return -1; } - if (CPTIpMatch(msr, (unsigned char *)&in.s_addr, rtree->ipv4_tree, + if (CPTIpMatch(msr, (unsigned char *)&(in.s_addr), rtree->ipv4_tree, IPV4_TREE) != NULL) { return 1; } } #if APR_HAVE_IPV6 else { - if (inet_pton(AF_INET6, value, &in6) <= 0) { + if (inet_pton(AF_INET6, value, &(in6.s6_addr)) <= 0) { *error_msg = apr_psprintf(mp, "IPmatch: bad IPv6 " \ "specification \"%s\".", value); return -1; } - if (CPTIpMatch(msr, (unsigned char *)&in6.s6_addr, rtree->ipv6_tree, + if (CPTIpMatch(msr, (unsigned char *)&(in6.s6_addr), rtree->ipv6_tree, IPV6_TREE) != NULL) { return 1; } @@ -2783,6 +2662,9 @@ size_t msc_curl_write_memory_cb(void *contents, size_t size, if (mem->size == 0) { mem->memory = malloc(realsize + 1); + if (mem->memory == NULL) { + return 0; + } memset(mem->memory, '\0', sizeof(realsize + 1)); } else @@ -2832,3 +2714,24 @@ char* strtok_r( } #endif +// we cannot log an error message as this happens much too often +char* get_username(apr_pool_t* mp) { + char* username; + apr_uid_t uid; + apr_gid_t gid; + int rc = apr_uid_current(&uid, &gid, mp); + if (rc != APR_SUCCESS) return "apache"; + rc = apr_uid_name_get(&username, uid, mp); + if (rc != APR_SUCCESS) return "apache"; + return username; +} + +// Returns the rule id if existing, otherwise the file name & line number +const char* id_log(msre_rule* rule) { + assert(rule != NULL); + assert(rule->actionset != NULL); + assert(rule->ruleset != NULL); + const char* id = rule->actionset->id; + if (!id || id == NOT_SET_P || !*id) id = apr_psprintf(rule->ruleset->mp, "%s (%d)", rule->filename, rule->line_num); + return id; +} diff --git a/apache2/msc_util.h b/apache2/msc_util.h index f7e1280f21..afff3e7f64 100644 --- a/apache2/msc_util.h +++ b/apache2/msc_util.h @@ -15,6 +15,7 @@ #ifndef _UTIL_H_ #define _UTIL_H_ +#include #include #include @@ -164,6 +165,9 @@ int ip_tree_from_uri(TreeRoot **rtree, char *uri, apr_pool_t *mp, char **error_msg); #endif +char DSOLOCAL *get_username(apr_pool_t* mp); +const char* id_log(msre_rule* rule); + int read_line(char *buff, int size, FILE *fp); size_t msc_curl_write_memory_cb(void *contents, size_t size, diff --git a/apache2/msc_xml.c b/apache2/msc_xml.c index a31decb5e1..4f0e07ca05 100644 --- a/apache2/msc_xml.c +++ b/apache2/msc_xml.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* Copyright (c) 2004-2022 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 @@ -14,6 +14,133 @@ #include "msc_xml.h" +static void msc_xml_on_start_elementns( + void *ctx, + const xmlChar *localname, + const xmlChar *prefix, + const xmlChar *URI, + int nb_namespaces, + const xmlChar **namespaces, + int nb_attributes, + int nb_defaulted, + const xmlChar **attributes +) { + + // get the length of XML tag (localname) + size_t taglen = strlen((const char *)localname); + modsec_rec * msr = (modsec_rec *)ctx; + msc_xml_parser_state * xml_parser_state = msr->xml->xml_parser_state; + + // pathlen contains the concatenated strings of tags with '.' + // eg xml.root.level1.leaf + xml_parser_state->pathlen += (taglen + 1); + char *newpath = apr_pstrcat(msr->mp, xml_parser_state->currpath, ".", (char *)localname, NULL); + xml_parser_state->currpath = newpath; + xml_parser_state->currpathbufflen += taglen + 1; // +1 for the '.' character here too + + int *new_stack_item = (int *)apr_array_push(xml_parser_state->has_child_stack); + *new_stack_item = 0; + xml_parser_state->depth++; + // set the current value to null + // this is necessary because if there is any text between the tags (new line, etc) + // it will be added to the current value + xml_parser_state->currval = NULL; + xml_parser_state->currvalbufflen = 0; + + // if there is an item before the current one we set that has a child + if (xml_parser_state->depth > 1) { + int *parent_stack_item = &((int *)xml_parser_state->has_child_stack->elts)[xml_parser_state->has_child_stack->nelts - 2]; + *parent_stack_item = 1; + } + +} + +static void msc_xml_on_end_elementns( + void* ctx, + const xmlChar* localname, + const xmlChar* prefix, + const xmlChar* URI +) { + + size_t taglen = strlen((const char *)localname); + modsec_rec * msr = (modsec_rec *)ctx; + msc_xml_parser_state * xml_parser_state = msr->xml->xml_parser_state; + + // if the node is a leaf we add it as argument + // get the top item from the stack which tells this info + int * top_stack_item = apr_array_pop(xml_parser_state->has_child_stack); + if (*top_stack_item == 0) { + + if (apr_table_elts(msr->arguments)->nelts >= msr->txcfg->arguments_limit) { + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Skipping request argument, over limit (XML): name \"%s\", value \"%s\"", + log_escape_ex(msr->mp, xml_parser_state->currpath, xml_parser_state->currpathbufflen), + log_escape_ex(msr->mp, + (xml_parser_state->currval == NULL ? apr_pstrndup(msr->mp, "", 0) : xml_parser_state->currval), + xml_parser_state->currvalbufflen + ) + ); + } + msr->msc_reqbody_error = 1; + msr->xml->xml_error = apr_psprintf(msr->mp, "More than %ld ARGS (GET + XML)", msr->txcfg->arguments_limit); + xmlStopParser((xmlParserCtxtPtr)msr->xml->parsing_ctx_arg); + } + else { + + msc_arg * arg = (msc_arg *) apr_pcalloc(msr->mp, sizeof(msc_arg)); + + arg->name = xml_parser_state->currpath; + arg->name_len = xml_parser_state->currpathbufflen; + arg->value = (xml_parser_state->currval == NULL) ? apr_pstrndup(msr->mp, "", 0) : xml_parser_state->currval; + arg->value_len = xml_parser_state->currvalbufflen; + arg->value_origin_len = arg->value_len; + arg->origin = "XML"; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "Adding XML argument '%s' with value '%s'", + arg->name, arg->value); + } + + apr_table_addn(msr->arguments, + log_escape_nq_ex(msr->mp, arg->name, arg->name_len), (void *) arg); + } // end else + } // end top_stack_item == 0 + + // decrease the length of current path length - +1 because of the '\0' + xml_parser_state->pathlen -= (taglen + 1); + + // -1 is needed because we don't need the last '.' + char * newpath = apr_pstrndup(msr->mp, xml_parser_state->currpath, xml_parser_state->pathlen - 1); + xml_parser_state->currpath = newpath; + xml_parser_state->currpathbufflen = xml_parser_state->pathlen - 1; + + xml_parser_state->depth--; + xml_parser_state->currval = NULL; + xml_parser_state->currvalbufflen = 0; +} + +static void msc_xml_on_characters(void *ctx, const xmlChar *ch, int len) { + + modsec_rec * msr = (modsec_rec *)ctx; + msc_xml_parser_state * xml_parser_state = msr->xml->xml_parser_state; + + // libxml2 SAX parser will call this function multiple times + // during the parsing of a single node, if the value has multibyte + // characters, so we need to concatenate the values + xml_parser_state->currval = apr_pstrcat(msr->mp, + ((xml_parser_state->currval != NULL) ? xml_parser_state->currval : ""), + apr_pstrndup(msr->mp, (const char *)ch, len), + NULL); + xml_parser_state->currvalbufflen += len; + // check if the memory allocation was successful + if (xml_parser_state->currval == NULL) { + msr->xml->xml_error = apr_psprintf(msr->mp, "Failed to allocate memory for XML value."); + xmlStopParser((xmlParserCtxtPtr)msr->xml->parsing_ctx_arg); + } + +} + + static xmlParserInputBufferPtr xml_unload_external_entity(const char *URI, xmlCharEncoding enc) { return NULL; @@ -24,9 +151,10 @@ xml_unload_external_entity(const char *URI, xmlCharEncoding enc) { * Initialise XML parser. */ int xml_init(modsec_rec *msr, char **error_msg) { + assert(msr != NULL); + assert(error_msg != NULL); xmlParserInputBufferCreateFilenameFunc entity; - if (error_msg == NULL) return -1; *error_msg = NULL; msr->xml = apr_pcalloc(msr->mp, sizeof(xml_data)); @@ -36,6 +164,34 @@ int xml_init(modsec_rec *msr, char **error_msg) { entity = xmlParserInputBufferCreateFilenameDefault(xml_unload_external_entity); } + if (msr->txcfg->parse_xml_into_args != MSC_XML_ARGS_OFF) { + + msr->xml->sax_handler = (xmlSAXHandler *)apr_pcalloc(msr->mp, sizeof(xmlSAXHandler)); + memset(msr->xml->sax_handler, 0, sizeof(xmlSAXHandler)); + if (msr->xml->sax_handler == NULL) { + *error_msg = apr_psprintf(msr->mp, "XML: Failed to create SAX handler."); + return -1; + } + + msr->xml->sax_handler->initialized = XML_SAX2_MAGIC; + msr->xml->sax_handler->startElementNs = msc_xml_on_start_elementns; + msr->xml->sax_handler->endElementNs = msc_xml_on_end_elementns; + msr->xml->sax_handler->characters = msc_xml_on_characters; + + // set the parser state struct + msr->xml->xml_parser_state = apr_pcalloc(msr->mp, sizeof(msc_xml_parser_state)); + msr->xml->xml_parser_state->depth = 0; + msr->xml->xml_parser_state->pathlen = 4; // "xml\0" + msr->xml->xml_parser_state->currpath = apr_pstrdup(msr->mp, "xml"); + msr->xml->xml_parser_state->currpathbufflen = 3; // "xml" + msr->xml->xml_parser_state->currval = NULL; + msr->xml->xml_parser_state->currvalbufflen = 0; + // initialize the stack with item of 10 + // this will store the information about nodes + // 10 is just an initial value, it can be automatically incremented + msr->xml->xml_parser_state->has_child_stack = apr_array_make(msr->mp, 10, sizeof(int)); + } + return 1; } @@ -59,14 +215,15 @@ static void xml_receive_sax_error(void *data, const char *msg, ...) { * Feed one chunk of data to the XML parser. */ int xml_process_chunk(modsec_rec *msr, const char *buf, unsigned int size, char **error_msg) { - if (error_msg == NULL) return -1; + assert(msr != NULL); + assert(error_msg != NULL); *error_msg = NULL; /* We want to initialise our parsing context here, to * enable us to pass it the first chunk of data so that * it can attempt to auto-detect the encoding. */ - if (msr->xml->parsing_ctx == NULL) { + if (msr->xml->parsing_ctx == NULL && msr->xml->parsing_ctx_arg == NULL) { /* First invocation. */ @@ -84,18 +241,52 @@ int xml_process_chunk(modsec_rec *msr, const char *buf, unsigned int size, char */ - msr->xml->parsing_ctx = xmlCreatePushParserCtxt(NULL, NULL, buf, size, "body.xml"); - if (msr->xml->parsing_ctx == NULL) { - *error_msg = apr_psprintf(msr->mp, "XML: Failed to create parsing context."); - return -1; + if (msr->txcfg->parse_xml_into_args != MSC_XML_ARGS_ONLYARGS) { + msr->xml->parsing_ctx = xmlCreatePushParserCtxt(NULL, NULL, buf, size, "body.xml"); + if (msr->xml->parsing_ctx == NULL) { + *error_msg = apr_psprintf(msr->mp, "XML: Failed to create parsing context."); + return -1; + } + } + if (msr->txcfg->parse_xml_into_args != MSC_XML_ARGS_OFF) { + msr->xml->parsing_ctx_arg = xmlCreatePushParserCtxt( + msr->xml->sax_handler, + msr, + buf, + size, + NULL); + if (msr->xml->parsing_ctx_arg == NULL) { + *error_msg = apr_psprintf(msr->mp, "XML: Failed to create parsing context for ARGS."); + return -1; + } } } else { /* Not a first invocation. */ + msr_log(msr, 4, "XML: Continue parsing."); + if (msr->xml->parsing_ctx != NULL && + msr->txcfg->parse_xml_into_args != MSC_XML_ARGS_ONLYARGS) { + xmlParseChunk(msr->xml->parsing_ctx, buf, size, 0); + if (msr->xml->parsing_ctx->wellFormed != 1) { + *error_msg = apr_psprintf(msr->mp, "XML: Failed to parse document."); + return -1; + } + } - xmlParseChunk(msr->xml->parsing_ctx, buf, size, 0); - if (msr->xml->parsing_ctx->wellFormed != 1) { - *error_msg = apr_psprintf(msr->mp, "XML: Failed parsing document."); + if (msr->xml->parsing_ctx_arg != NULL && + msr->txcfg->parse_xml_into_args != MSC_XML_ARGS_OFF) { + if (xmlParseChunk(msr->xml->parsing_ctx_arg, buf, size, 0) != 0) { + if (msr->xml->xml_error) { + *error_msg = msr->xml->xml_error; + } + else { + *error_msg = apr_psprintf(msr->mp, "XML: Failed to parse document for ARGS."); + } + return -1; + } + } + if (msr->xml->xml_error) { + *error_msg = msr->xml->xml_error; return -1; } } @@ -107,27 +298,49 @@ int xml_process_chunk(modsec_rec *msr, const char *buf, unsigned int size, char * Finalise XML parsing. */ int xml_complete(modsec_rec *msr, char **error_msg) { - if (error_msg == NULL) return -1; + assert(msr != NULL); + assert(error_msg != NULL); *error_msg = NULL; /* Only if we have a context, meaning we've done some work. */ - if (msr->xml->parsing_ctx != NULL) { - /* This is how we signalise the end of parsing to libxml. */ - xmlParseChunk(msr->xml->parsing_ctx, NULL, 0, 1); + if (msr->xml->parsing_ctx != NULL || msr->xml->parsing_ctx_arg != NULL) { + if (msr->xml->parsing_ctx != NULL && + msr->txcfg->parse_xml_into_args != MSC_XML_ARGS_ONLYARGS) { + /* This is how we signal the end of parsing to libxml. */ + xmlParseChunk(msr->xml->parsing_ctx, NULL, 0, 1); - /* Preserve the results for our reference. */ - msr->xml->well_formed = msr->xml->parsing_ctx->wellFormed; - msr->xml->doc = msr->xml->parsing_ctx->myDoc; + /* Preserve the results for our reference. */ + msr->xml->well_formed = msr->xml->parsing_ctx->wellFormed; + msr->xml->doc = msr->xml->parsing_ctx->myDoc; - /* Clean up everything else. */ - xmlFreeParserCtxt(msr->xml->parsing_ctx); - msr->xml->parsing_ctx = NULL; - msr_log(msr, 4, "XML: Parsing complete (well_formed %u).", msr->xml->well_formed); + /* Clean up everything else. */ + xmlFreeParserCtxt(msr->xml->parsing_ctx); + msr->xml->parsing_ctx = NULL; + msr_log(msr, 4, "XML: Parsing complete (well_formed %u).", msr->xml->well_formed); - if (msr->xml->well_formed != 1) { - *error_msg = apr_psprintf(msr->mp, "XML: Failed parsing document."); - return -1; + if (msr->xml->well_formed != 1) { + *error_msg = apr_psprintf(msr->mp, "XML: Failed to parse document."); + return -1; + } + } + + if (msr->xml->parsing_ctx_arg != NULL && + msr->txcfg->parse_xml_into_args != MSC_XML_ARGS_OFF) { + if (xmlParseChunk(msr->xml->parsing_ctx_arg, NULL, 0, 1) != 0) { + if (msr->xml->xml_error) { + *error_msg = msr->xml->xml_error; + } + else { + *error_msg = apr_psprintf(msr->mp, "XML: Failed to parse document for ARGS."); + } + xmlFreeParserCtxt(msr->xml->parsing_ctx_arg); + msr->xml->parsing_ctx_arg = NULL; + return -1; + } + xmlFreeParserCtxt(msr->xml->parsing_ctx_arg); + msr->xml->parsing_ctx_arg = NULL; } + } return 1; @@ -137,6 +350,27 @@ int xml_complete(modsec_rec *msr, char **error_msg) { * Frees the resources used for XML parsing. */ apr_status_t xml_cleanup(modsec_rec *msr) { + assert(msr != NULL); + assert(msr->xml != NULL); + if (msr->xml->parsing_ctx != NULL) { + if (msr->xml->parsing_ctx->myDoc) { + xmlFreeDoc(msr->xml->parsing_ctx->myDoc); + if (msr->xml->parsing_ctx->myDoc == msr->xml->doc) { + msr->xml->doc = NULL; + } + } + xmlFreeParserCtxt(msr->xml->parsing_ctx); + msr->xml->parsing_ctx = NULL; + } + if (msr->xml->parsing_ctx_arg != NULL) { + + if (msr->xml->parsing_ctx_arg->myDoc) { + xmlFreeDoc(msr->xml->parsing_ctx_arg->myDoc); + } + + xmlFreeParserCtxt(msr->xml->parsing_ctx_arg); + msr->xml->parsing_ctx_arg = NULL; + } if (msr->xml->doc != NULL) { xmlFreeDoc(msr->xml->doc); msr->xml->doc = NULL; diff --git a/apache2/msc_xml.h b/apache2/msc_xml.h index f239068672..73999443a2 100644 --- a/apache2/msc_xml.h +++ b/apache2/msc_xml.h @@ -20,15 +20,37 @@ typedef struct xml_data xml_data; #include "modsecurity.h" #include #include +#include /* Structures */ +struct msc_xml_parser_state { + apr_array_header_t * has_child_stack; + unsigned int depth; + unsigned int pathlen; + char * currpath; + char * currval; + size_t currpathbufflen; + size_t currvalbufflen; + apr_pool_t * mp; +}; + +typedef struct msc_xml_parser_state msc_xml_parser_state; + struct xml_data { xmlSAXHandler *sax_handler; xmlParserCtxtPtr parsing_ctx; xmlDocPtr doc; unsigned int well_formed; + + /* error reporting and XML array flag */ + char *xml_error; + + /* additional parser context for arguments */ + xmlParserCtxtPtr parsing_ctx_arg; + /* parser state for SAX parser */ + msc_xml_parser_state *xml_parser_state; }; /* Functions */ diff --git a/apache2/persist_dbm.c b/apache2/persist_dbm.c index 597d5b8fc7..ba8475cc59 100644 --- a/apache2/persist_dbm.c +++ b/apache2/persist_dbm.c @@ -1,806 +1,800 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 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 -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include "persist_dbm.h" -#include "apr_sdbm.h" - -/** - * - */ -static apr_table_t *collection_unpack(modsec_rec *msr, const unsigned char *blob, unsigned int blob_size, - int log_vars) -{ - apr_table_t *col = NULL; - unsigned int blob_offset; - - col = apr_table_make(msr->mp, 32); - if (col == NULL) return NULL; - - /* ENH verify the first 3 bytes (header) */ - - blob_offset = 3; - while (blob_offset + 1 < blob_size) { - msc_string *var = apr_pcalloc(msr->mp, sizeof(msc_string)); - - var->name_len = (blob[blob_offset] << 8) + blob[blob_offset + 1]; - if (var->name_len == 0) { - /* Is the length a name length, or just the end of the blob? */ - if (blob_offset < blob_size - 2) { - /* This should never happen as the name length - * includes the terminating NUL and should be 1 for "" - */ - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collection_unpack: BLOB[%d]: %s", blob_offset, log_escape_hex(msr->mp, blob + blob_offset, blob_size - blob_offset)); - } - msr_log(msr, 4, "collection_unpack: Possibly corrupted database: var name length = 0 at blob offset %u-%u.", blob_offset, blob_offset + 1); - } - break; - } - else if (var->name_len > 65536) { - /* This should never happen as the length is restricted on store - * to 65536. - */ - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collection_unpack: BLOB[%d]: %s", blob_offset, log_escape_hex(msr->mp, blob + blob_offset, blob_size - blob_offset)); - } - msr_log(msr, 4, "collection_unpack: Possibly corrupted database: var name length > 65536 (0x%04x) at blob offset %u-%u.", var->name_len, blob_offset, blob_offset + 1); - break; - } - - blob_offset += 2; - if (blob_offset + var->name_len > blob_size) return NULL; - var->name = apr_pstrmemdup(msr->mp, (const char *)blob + blob_offset, var->name_len - 1); - blob_offset += var->name_len; - var->name_len--; - - var->value_len = (blob[blob_offset] << 8) + blob[blob_offset + 1]; - blob_offset += 2; - - if (blob_offset + var->value_len > blob_size) return NULL; - var->value = apr_pstrmemdup(msr->mp, (const char *)blob + blob_offset, var->value_len - 1); - blob_offset += var->value_len; - var->value_len--; - - if (log_vars && (msr->txcfg->debuglog_level >= 9)) { - msr_log(msr, 9, "collection_unpack: Read variable: name \"%s\", value \"%s\".", - log_escape_ex(msr->mp, var->name, var->name_len), - log_escape_ex(msr->mp, var->value, var->value_len)); - } - - apr_table_addn(col, var->name, (void *)var); - } - - return col; -} - -/** - * - */ -static apr_table_t *collection_retrieve_ex(apr_sdbm_t *existing_dbm, modsec_rec *msr, const char *col_name, - const char *col_key, int col_key_len) -{ - char *dbm_filename = NULL; - apr_status_t rc; - apr_sdbm_datum_t key; - apr_sdbm_datum_t *value = NULL; - apr_sdbm_t *dbm = NULL; - apr_table_t *col = NULL; - const apr_array_header_t *arr; - apr_table_entry_t *te; - int expired = 0; - int i; - - - if (msr->txcfg->data_dir == NULL) { - msr_log(msr, 1, "collection_retrieve_ex: Unable to retrieve collection (name \"%s\", key \"%s\"). Use " - "SecDataDir to define data directory first.", log_escape(msr->mp, col_name), - log_escape_ex(msr->mp, col_key, col_key_len)); - goto cleanup; - } - - dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", col_name, NULL); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collection_retrieve_ex: collection_retrieve_ex: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, col_name), - log_escape(msr->mp, dbm_filename)); - } - - key.dptr = (char *)col_key; - key.dsize = col_key_len + 1; - - if (existing_dbm == NULL) { -#ifdef GLOBAL_COLLECTION_LOCK - rc = apr_global_mutex_lock(msr->modsecurity->dbm_lock); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collection_retrieve_ex: Failed to lock proc mutex: %s", - get_apr_error(msr->mp, rc)); - goto cleanup; - } -#endif - rc = apr_sdbm_open(&dbm, dbm_filename, APR_READ | APR_SHARELOCK, - CREATEMODE, msr->mp); - if (rc != APR_SUCCESS) { - dbm = NULL; -#ifdef GLOBAL_COLLECTION_LOCK - apr_global_mutex_unlock(msr->modsecurity->dbm_lock); -#endif - goto cleanup; - } - } - else { - dbm = existing_dbm; - } - - value = (apr_sdbm_datum_t *)apr_pcalloc(msr->mp, sizeof(apr_sdbm_datum_t)); - rc = apr_sdbm_fetch(dbm, value, key); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collection_retrieve_ex: Failed to read from DBM file \"%s\": %s", log_escape(msr->mp, - dbm_filename), get_apr_error(msr->mp, rc)); - goto cleanup; - } - - if (value->dptr == NULL) { /* Key not found in DBM file. */ - goto cleanup; - } - - /* ENH Need expiration (and perhaps other metadata) accessible in blob - * form to determine if converting to a table is needed. This will - * save some cycles. - */ - - /* Transform raw data into a table. */ - col = collection_unpack(msr, (const unsigned char *)value->dptr, value->dsize, 1); - if (col == NULL) { - goto cleanup; - } - - /* Close after "value" used from fetch or memory may be overwritten. */ - if (existing_dbm == NULL) { - apr_sdbm_close(dbm); -#ifdef GLOBAL_COLLECTION_LOCK - apr_global_mutex_unlock(msr->modsecurity->dbm_lock); -#endif - dbm = NULL; - } - - /* Remove expired variables. */ - do { - arr = apr_table_elts(col); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - if (strncmp(te[i].key, "__expire_", 9) == 0) { - msc_string *var = (msc_string *)te[i].val; - int expiry_time = atoi(var->value); - - if (expiry_time <= apr_time_sec(msr->request_time)) { - char *key_to_expire = te[i].key; - - /* Done early if the col expired */ - if (strcmp(key_to_expire, "__expire_KEY") == 0) { - expired = 1; - } - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collection_retrieve_ex: Removing key \"%s\" from collection.", key_to_expire + 9); - msr_log(msr, 9, "collection_retrieve_ex: Removing key \"%s\" from collection.", key_to_expire); - } - - apr_table_unset(col, key_to_expire + 9); - apr_table_unset(col, key_to_expire); - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "collection_retrieve_ex: Removed expired variable \"%s\".", key_to_expire + 9); - } - - break; - } - } - } - } while(!expired && (i != arr->nelts)); - - /* Delete the collection if the variable "KEY" does not exist. - * - * ENH It would probably be more efficient to hold the DBM - * open until determined if it needs deleted than to open a second - * time. - */ - if (apr_table_get(col, "KEY") == NULL) { - if (existing_dbm == NULL) { -#ifdef GLOBAL_COLLECTION_LOCK - rc = apr_global_mutex_lock(msr->modsecurity->dbm_lock); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collection_retrieve_ex: Failed to lock proc mutex: %s", - get_apr_error(msr->mp, rc)); - goto cleanup; - } -#endif - rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK, - CREATEMODE, msr->mp); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collection_retrieve_ex: Failed to access DBM file \"%s\": %s", - log_escape(msr->mp, dbm_filename), get_apr_error(msr->mp, rc)); - dbm = NULL; -#ifdef GLOBAL_COLLECTION_LOCK - apr_global_mutex_unlock(msr->modsecurity->dbm_lock); -#endif - goto cleanup; - } - } - else { - dbm = existing_dbm; - } - - rc = apr_sdbm_delete(dbm, key); - if (rc != APR_SUCCESS) { -#ifdef LOG_NO_COLL_DELET_PB - if (msr->txcfg->debuglog_level >= 9) -#endif - msr_log(msr, 1, "collection_retrieve_ex: Failed deleting collection (name \"%s\", " - "key \"%s\"): %s", log_escape(msr->mp, col_name), - log_escape_ex(msr->mp, col_key, col_key_len), get_apr_error(msr->mp, rc)); - msr->msc_sdbm_delete_error = 1; - goto cleanup; - } - - - if (existing_dbm == NULL) { - apr_sdbm_close(dbm); -#ifdef GLOBAL_COLLECTION_LOCK - apr_global_mutex_unlock(msr->modsecurity->dbm_lock); -#endif - dbm = NULL; - } - - if (expired && (msr->txcfg->debuglog_level >= 9)) { - msr_log(msr, 9, "collection_retrieve_ex: Collection expired (name \"%s\", key \"%s\").", col_name, - log_escape_ex(msr->mp, col_key, col_key_len)); - } - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "collection_retrieve_ex: Deleted collection (name \"%s\", key \"%s\").", - log_escape(msr->mp, col_name), log_escape_ex(msr->mp, col_key, col_key_len)); - } - goto cleanup; - } - - /* Update UPDATE_RATE */ - { - msc_string *var; - int create_time, counter; - - var = (msc_string *)apr_table_get(col, "CREATE_TIME"); - if (var == NULL) { - /* Error. */ - } else { - create_time = atoi(var->value); - var = (msc_string *)apr_table_get(col, "UPDATE_COUNTER"); - if (var == NULL) { - /* Error. */ - } else { - apr_time_t td; - counter = atoi(var->value); - - /* UPDATE_RATE is removed on store, so add it back here */ - var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = "UPDATE_RATE"; - var->name_len = strlen(var->name); - apr_table_setn(col, var->name, (void *)var); - - /* NOTE: No rate if there has been no time elapsed */ - td = (apr_time_sec(apr_time_now()) - create_time); - if (td == 0) { - var->value = apr_psprintf(msr->mp, "%d", 0); - } - else { - var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, - (apr_time_t)((60 * counter)/td)); - } - var->value_len = strlen(var->value); - } - } - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "collection_retrieve_ex: Retrieved collection (name \"%s\", key \"%s\").", - log_escape(msr->mp, col_name), log_escape_ex(msr->mp, col_key, col_key_len)); - } - - if ((existing_dbm == NULL) && dbm) { - /* Should not ever get here */ - msr_log(msr, 1, "collection_retrieve_ex: Internal Error: Collection remained open (name \"%s\", key \"%s\").", - log_escape(msr->mp, col_name), log_escape_ex(msr->mp, col_key, col_key_len)); - - apr_sdbm_close(dbm); -#ifdef GLOBAL_COLLECTION_LOCK - apr_global_mutex_unlock(msr->modsecurity->dbm_lock); -#endif - } - - return col; - -cleanup: - - if ((existing_dbm == NULL) && dbm) { - apr_sdbm_close(dbm); -#ifdef GLOBAL_COLLECTION_LOCK - apr_global_mutex_unlock(msr->modsecurity->dbm_lock); -#endif - } - - return NULL; -} - -/** - * - */ -apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, - const char *col_key, int col_key_len) -{ - apr_time_t time_before = apr_time_now(); - apr_table_t *rtable = NULL; - - rtable = collection_retrieve_ex(NULL, msr, col_name, col_key, col_key_len); - - msr->time_storage_read += apr_time_now() - time_before; - - return rtable; -} - -/** - * - */ -int collection_store(modsec_rec *msr, apr_table_t *col) { - char *dbm_filename = NULL; - msc_string *var_name = NULL, *var_key = NULL; - unsigned char *blob = NULL; - unsigned int blob_size, blob_offset; - apr_status_t rc; - apr_sdbm_datum_t key; - apr_sdbm_datum_t value; - apr_sdbm_t *dbm = NULL; - const apr_array_header_t *arr; - apr_table_entry_t *te; - int i; - const apr_table_t *stored_col = NULL; - const apr_table_t *orig_col = NULL; - - var_name = (msc_string *)apr_table_get(col, "__name"); - if (var_name == NULL) { - goto error; - } - - var_key = (msc_string *)apr_table_get(col, "__key"); - if (var_key == NULL) { - goto error; - } - - if (msr->txcfg->data_dir == NULL) { - msr_log(msr, 1, "collection_store: Unable to store collection (name \"%s\", key \"%s\"). Use " - "SecDataDir to define data directory first.", log_escape_ex(msr->mp, var_name->value, var_name->value_len), - log_escape_ex(msr->mp, var_key->value, var_key->value_len)); - goto error; - } - - // ENH: lowercase the var name in the filename - dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", var_name->value, NULL); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collection_store: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, var_name->value), - log_escape(msr->mp, dbm_filename)); - } - -#ifdef GLOBAL_COLLECTION_LOCK - /* Need to lock to pull in the stored data again and apply deltas. */ - rc = apr_global_mutex_lock(msr->modsecurity->dbm_lock); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collection_store: Failed to lock proc mutex: %s", - get_apr_error(msr->mp, rc)); - goto error; - } -#endif - - /* Delete IS_NEW on store. */ - apr_table_unset(col, "IS_NEW"); - - /* Delete UPDATE_RATE on store to save space as it is calculated */ - apr_table_unset(col, "UPDATE_RATE"); - - /* Update the timeout value. */ - { - msc_string *var = (msc_string *)apr_table_get(col, "TIMEOUT"); - if (var != NULL) { - int timeout = atoi(var->value); - var = (msc_string *)apr_table_get(col, "__expire_KEY"); - if (var != NULL) { - var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(apr_time_now()) + timeout)); - var->value_len = strlen(var->value); - } - } - } - - /* LAST_UPDATE_TIME */ - { - msc_string *var = (msc_string *)apr_table_get(col, "LAST_UPDATE_TIME"); - if (var == NULL) { - var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = "LAST_UPDATE_TIME"; - var->name_len = strlen(var->name); - apr_table_setn(col, var->name, (void *)var); - } - var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(apr_time_now()))); - var->value_len = strlen(var->value); - } - - /* UPDATE_COUNTER */ - { - msc_string *var = (msc_string *)apr_table_get(col, "UPDATE_COUNTER"); - int counter = 0; - if (var == NULL) { - var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); - var->name = "UPDATE_COUNTER"; - var->name_len = strlen(var->name); - apr_table_setn(col, var->name, (void *)var); - } else { - counter = atoi(var->value); - } - var->value = apr_psprintf(msr->mp, "%d", counter + 1); - var->value_len = strlen(var->value); - } - - /* ENH Make the expiration timestamp accessible in blob form so that - * it is easier/faster to determine expiration without having to - * convert back to table form - */ - - rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK, - CREATEMODE, msr->mp); - if (rc != APR_SUCCESS) { -#ifdef GLOBAL_COLLECTION_LOCK - apr_global_mutex_unlock(msr->modsecurity->dbm_lock); -#endif - msr_log(msr, 1, "collection_store: Failed to access DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename), - get_apr_error(msr->mp, rc)); - dbm = NULL; - goto error; - } - -#ifndef GLOBAL_COLLECTION_LOCK - /* Need to lock to pull in the stored data again and apply deltas. */ - rc = apr_sdbm_lock(dbm, APR_FLOCK_EXCLUSIVE); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collection_store: Failed to exclusivly lock DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename), - get_apr_error(msr->mp, rc)); - goto error; - } -#endif - - /* If there is an original value, then create a delta and - * apply the delta to the current value */ - orig_col = (const apr_table_t *)apr_table_get(msr->collections_original, var_name->value); - if (orig_col != NULL) { - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collection_store: Re-retrieving collection prior to store: %s", - apr_psprintf(msr->mp, "%.*s", var_name->value_len, var_name->value)); - } - - stored_col = (const apr_table_t *)collection_retrieve_ex(dbm, msr, var_name->value, var_key->value, var_key->value_len); - } - - /* Merge deltas and calculate the size first. */ - blob_size = 3 + 2; - arr = apr_table_elts(col); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_string *var = (msc_string *)te[i].val; - int len; - - /* If there is an original value, then apply the delta - * to the latest stored value */ - if (stored_col != NULL) { - const msc_string *orig_var = (const msc_string *)apr_table_get(orig_col, var->name); - if (orig_var != NULL) { - const msc_string *stored_var = (const msc_string *)apr_table_get(stored_col, var->name); - if (stored_var != NULL) { - int origval = atoi(orig_var->value); - int ourval = atoi(var->value); - int storedval = atoi(stored_var->value); - int delta = ourval - origval; - int newval = storedval + delta; - - if (newval < 0) newval = 0; /* Counters never go below zero. */ - - var->value = apr_psprintf(msr->mp, "%d", newval); - var->value_len = strlen(var->value); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collection_store: Delta applied for %s.%s %d->%d (%d): %d + (%d) = %d [%s,%d]", - log_escape_ex(msr->mp, var_name->value, var_name->value_len), - log_escape_ex(msr->mp, var->name, var->name_len), - origval, ourval, delta, storedval, delta, newval, var->value, var->value_len); - } - } - } - } - - // Allocate blob_size for keys - len = var->name_len + 1; - if (len >= 65536) len = 65536; - blob_size += len + 2; - - // Allocate blob_size for values - len = var->value_len + 1; - if (len >= 65536) len = 65536; - blob_size += len + 2; - } - - /* Now generate the binary object. */ - blob = apr_pcalloc(msr->mp, blob_size); - if (blob == NULL) { - if (dbm != NULL) { -#ifdef GLOBAL_COLLECTION_LOCK - apr_sdbm_close(dbm); - apr_global_mutex_unlock(msr->modsecurity->dbm_lock); -#else - apr_sdbm_unlock(dbm); - apr_sdbm_close(dbm); -#endif - } - - return -1; - } - - blob[0] = 0x49; - blob[1] = 0x52; - blob[2] = 0x01; - - blob_offset = 3; - arr = apr_table_elts(col); - te = (apr_table_entry_t *)arr->elts; - for (i = 0; i < arr->nelts; i++) { - msc_string *var = (msc_string *)te[i].val; - int len; - - len = var->name_len + 1; - if (len >= 65536) len = 65536; - - blob[blob_offset + 0] = (len & 0xff00) >> 8; - blob[blob_offset + 1] = len & 0x00ff; - memcpy(blob + blob_offset + 2, var->name, len - 1); - blob[blob_offset + 2 + len - 1] = '\0'; - blob_offset += 2 + len; - - len = var->value_len + 1; - if (len >= 65536) len = 65536; - - blob[blob_offset + 0] = (len & 0xff00) >> 8; - blob[blob_offset + 1] = len & 0x00ff; - memcpy(blob + blob_offset + 2, var->value, len - 1); - blob[blob_offset + 2 + len - 1] = '\0'; - blob_offset += 2 + len; - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collection_store: Wrote variable: name \"%s\", value \"%s\".", - log_escape_ex(msr->mp, var->name, var->name_len), - log_escape_ex(msr->mp, var->value, var->value_len)); - } - } - - blob[blob_offset] = 0; - blob[blob_offset + 1] = 0; - - /* And, finally, store it. */ - key.dptr = var_key->value; - key.dsize = var_key->value_len + 1; - - value.dptr = (char *)blob; - value.dsize = blob_size; - - rc = apr_sdbm_store(dbm, key, value, APR_SDBM_REPLACE); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collection_store: Failed to write to DBM file \"%s\": %s", dbm_filename, - get_apr_error(msr->mp, rc)); - if (dbm != NULL) { -#ifdef GLOBAL_COLLECTION_LOCK - apr_sdbm_close(dbm); - apr_global_mutex_unlock(msr->modsecurity->dbm_lock); -#else - apr_sdbm_unlock(dbm); - apr_sdbm_close(dbm); -#endif - } - - return -1; - } - -#ifdef GLOBAL_COLLECTION_LOCK - apr_sdbm_close(dbm); - apr_global_mutex_unlock(msr->modsecurity->dbm_lock); -#else - apr_sdbm_unlock(dbm); - apr_sdbm_close(dbm); -#endif - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "collection_store: Persisted collection (name \"%s\", key \"%s\").", - log_escape_ex(msr->mp, var_name->value, var_name->value_len), - log_escape_ex(msr->mp, var_key->value, var_key->value_len)); - } - - return 0; - -error: - return -1; -} - -/** - * - */ -int collections_remove_stale(modsec_rec *msr, const char *col_name) { - char *dbm_filename = NULL; - apr_sdbm_datum_t key, value; - apr_sdbm_t *dbm = NULL; - apr_status_t rc; - apr_array_header_t *keys_arr; - char **keys; - apr_time_t now = apr_time_sec(msr->request_time); - int i; - - if (msr->txcfg->data_dir == NULL) { - /* The user has been warned about this problem enough times already by now. - * msr_log(msr, 1, "Unable to access collection file (name \"%s\"). Use SecDataDir to " - * "define data directory first.", log_escape(msr->mp, col_name)); - */ - goto error; - } - - if(strstr(col_name,"USER") || strstr(col_name,"SESSION") || strstr(col_name, "RESOURCE")) - dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", msr->txcfg->webappid, "_", col_name, NULL); - else - dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", col_name, NULL); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collections_remove_stale: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, col_name), - log_escape(msr->mp, dbm_filename)); - } - -#ifdef GLOBAL_COLLECTION_LOCK - rc = apr_global_mutex_lock(msr->modsecurity->dbm_lock); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collections_remove_stale: Failed to lock proc mutex: %s", - get_apr_error(msr->mp, rc)); - goto error; - } -#endif - - rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK, - CREATEMODE, msr->mp); - if (rc != APR_SUCCESS) { -#ifdef GLOBAL_COLLECTION_LOCK - apr_global_mutex_unlock(msr->modsecurity->dbm_lock); -#endif - msr_log(msr, 1, "collections_remove_stale: Failed to access DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename), - get_apr_error(msr->mp, rc)); - dbm = NULL; - goto error; - } - - /* First get a list of all keys. */ - keys_arr = apr_array_make(msr->mp, 256, sizeof(char *)); - -#ifndef GLOBAL_COLLECTION_LOCK - rc = apr_sdbm_lock(dbm, APR_FLOCK_SHARED); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collections_remove_stale: Failed to lock DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename), - get_apr_error(msr->mp, rc)); - goto error; - } -#endif - - /* No one can write to the file while doing this so - * do it as fast as possible. - */ - rc = apr_sdbm_firstkey(dbm, &key); - while(rc == APR_SUCCESS) { - if (key.dsize) { - char *s = apr_pstrmemdup(msr->mp, key.dptr, key.dsize - 1); - *(char **)apr_array_push(keys_arr) = s; - } - rc = apr_sdbm_nextkey(dbm, &key); - } -#ifndef GLOBAL_COLLECTION_LOCK - apr_sdbm_unlock(dbm); -#endif - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collections_remove_stale: Found %d record(s) in file \"%s\".", keys_arr->nelts, - log_escape(msr->mp, dbm_filename)); - } - - /* Now retrieve the entires one by one. */ - keys = (char **)keys_arr->elts; - for (i = 0; i < keys_arr->nelts; i++) { - key.dptr = keys[i]; - key.dsize = strlen(key.dptr) + 1; - - rc = apr_sdbm_fetch(dbm, &value, key); - if (rc != APR_SUCCESS) { - msr_log(msr, 1, "collections_remove_stale: Failed reading DBM file \"%s\": %s", - log_escape(msr->mp, dbm_filename), get_apr_error(msr->mp, rc)); - goto error; - } - - if (value.dptr != NULL) { - apr_table_t *col = NULL; - msc_string *var = NULL; - - col = collection_unpack(msr, (const unsigned char *)value.dptr, value.dsize, 0); - if (col == NULL) { - goto error; - } - - var = (msc_string *)apr_table_get(col, "__expire_KEY"); - if (var == NULL) { - msr_log(msr, 1, "collections_remove_stale: Collection cleanup discovered entry with no " - "__expire_KEY (name \"%s\", key \"%s\").", - log_escape(msr->mp, col_name), log_escape_ex(msr->mp, key.dptr, key.dsize - 1)); - } else { - unsigned int expiry_time = atoi(var->value); - - if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "collections_remove_stale: Record (name \"%s\", key \"%s\") set to expire in %" APR_TIME_T_FMT " seconds.", - log_escape(msr->mp, col_name), log_escape_ex(msr->mp, key.dptr, key.dsize - 1), - expiry_time - now); - } - - if (expiry_time <= now) { - rc = apr_sdbm_delete(dbm, key); - if (rc != APR_SUCCESS) { -#ifdef LOG_NO_COLL_DELET_PB - if (msr->txcfg->debuglog_level >= 9) -#endif - msr_log(msr, 1, "collections_remove_stale: Failed deleting collection (name \"%s\", " - "key \"%s\"): %s", log_escape(msr->mp, col_name), - log_escape_ex(msr->mp, key.dptr, key.dsize - 1), get_apr_error(msr->mp, rc)); - msr->msc_sdbm_delete_error = 1; - goto error; - } - - if (msr->txcfg->debuglog_level >= 4) { - msr_log(msr, 4, "collections_remove_stale: Removed stale collection (name \"%s\", " - "key \"%s\").", log_escape(msr->mp, col_name), - log_escape_ex(msr->mp, key.dptr, key.dsize - 1)); - } - } - } - } else { - /* Ignore entry not found - it may have been removed in the meantime. */ - } - } - - apr_sdbm_close(dbm); -#ifdef GLOBAL_COLLECTION_LOCK - apr_global_mutex_unlock(msr->modsecurity->dbm_lock); -#endif - return 1; - -error: - - if (dbm) { - apr_sdbm_close(dbm); -#ifdef GLOBAL_COLLECTION_LOCK - apr_global_mutex_unlock(msr->modsecurity->dbm_lock); -#endif - } - - return -1; -} +/* +* ModSecurity for Apache 2.x, http://www.modsecurity.org/ +* Copyright (c) 2004-2013 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 +* +*     http://www.apache.org/licenses/LICENSE-2.0 +* +* If any of the files related to licensing are missing or if you have any +* other questions related to licensing please contact Trustwave Holdings, Inc. +* directly using the email address security@modsecurity.org. +*/ + +#include "persist_dbm.h" +#include "apr_sdbm.h" + +/** + * + */ +static apr_table_t *collection_unpack(modsec_rec *msr, const unsigned char *blob, unsigned int blob_size, + int log_vars) +{ + assert(msr != NULL); + assert(blob != NULL); + apr_table_t *col = NULL; + unsigned int blob_offset; + + col = apr_table_make(msr->mp, 32); + if (col == NULL) return NULL; + + /* ENH verify the first 3 bytes (header) */ + + blob_offset = 3; + while (blob_offset + 1 < blob_size) { + msc_string *var = apr_pcalloc(msr->mp, sizeof(msc_string)); + + var->name_len = (blob[blob_offset] << 8) + blob[blob_offset + 1]; + if (var->name_len == 0) { + /* Is the length a name length, or just the end of the blob? */ + if (blob_offset < blob_size - 2) { + /* This should never happen as the name length + * includes the terminating NUL and should be 1 for "" + */ + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "collection_unpack: BLOB[%d]: %s", blob_offset, log_escape_hex(msr->mp, blob + blob_offset, blob_size - blob_offset)); + } + msr_log(msr, 4, "collection_unpack: Possibly corrupted database: var name length = 0 at blob offset %u-%u.", blob_offset, blob_offset + 1); + } + break; + } + else if (var->name_len > 65536) { + /* This should never happen as the length is restricted on store + * to 65536. + */ + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "collection_unpack: BLOB[%d]: %s", blob_offset, log_escape_hex(msr->mp, blob + blob_offset, blob_size - blob_offset)); + } + msr_log(msr, 4, "collection_unpack: Possibly corrupted database: var name length > 65536 (0x%04x) at blob offset %u-%u.", var->name_len, blob_offset, blob_offset + 1); + break; + } + + blob_offset += 2; + if (blob_offset + var->name_len > blob_size) return NULL; + var->name = apr_pstrmemdup(msr->mp, (const char *)blob + blob_offset, var->name_len - 1); + blob_offset += var->name_len; + var->name_len--; + + var->value_len = (blob[blob_offset] << 8) + blob[blob_offset + 1]; + blob_offset += 2; + + if (blob_offset + var->value_len > blob_size) return NULL; + var->value = apr_pstrmemdup(msr->mp, (const char *)blob + blob_offset, var->value_len - 1); + blob_offset += var->value_len; + var->value_len--; + + if (log_vars && (msr->txcfg->debuglog_level >= 9)) { + msr_log(msr, 9, "collection_unpack: Read variable: name \"%s\", value \"%s\".", + log_escape_ex(msr->mp, var->name, var->name_len), + log_escape_ex(msr->mp, var->value, var->value_len)); + } + + apr_table_addn(col, var->name, (void *)var); + } + + return col; +} + +/** + * + */ +static apr_table_t *collection_retrieve_ex(apr_sdbm_t *existing_dbm, modsec_rec *msr, const char *col_name, + const char *col_key, int col_key_len) +{ + assert(msr != NULL); + assert(col_name != NULL); + char *dbm_filename = NULL; + apr_status_t rc; + apr_sdbm_datum_t key; + apr_sdbm_datum_t *value = NULL; + apr_sdbm_t *dbm = NULL; + apr_table_t *col = NULL; + const apr_array_header_t *arr; + apr_table_entry_t *te; + int expired = 0; + int i; + char *userinfo = get_username(msr->mp); + + if (msr->txcfg->data_dir == NULL) { + msr_log(msr, 1, "collection_retrieve_ex: Unable to retrieve collection (name \"%s\", key \"%s\"). Use " + "SecDataDir to define data directory first.", log_escape(msr->mp, col_name), + log_escape_ex(msr->mp, col_key, col_key_len)); + goto cleanup; + } + + dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", userinfo, "-", col_name, NULL); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "collection_retrieve_ex: collection_retrieve_ex: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, col_name), + log_escape(msr->mp, dbm_filename)); + } + + key.dptr = (char *)col_key; + key.dsize = col_key_len + 1; + + if (existing_dbm == NULL) { +#ifdef GLOBAL_COLLECTION_LOCK + rc = msr_global_mutex_lock(msr, msr->modsecurity->dbm_lock, "collection_retrieve_ex"); + if (rc != APR_SUCCESS) goto cleanup; +#endif + rc = apr_sdbm_open(&dbm, dbm_filename, APR_READ | APR_SHARELOCK, + CREATEMODE, msr->mp); + if (rc != APR_SUCCESS) { + dbm = NULL; +#ifdef GLOBAL_COLLECTION_LOCK + msr_global_mutex_unlock(msr, msr->modsecurity->dbm_lock, "collection_retrieve_ex"); +#endif + goto cleanup; + } + } + else { + dbm = existing_dbm; + } + + value = (apr_sdbm_datum_t *)apr_pcalloc(msr->mp, sizeof(apr_sdbm_datum_t)); + rc = apr_sdbm_fetch(dbm, value, key); + if (rc != APR_SUCCESS) { + msr_log(msr, 1, "collection_retrieve_ex: Failed to read from DBM file \"%s\": %s", log_escape(msr->mp, + dbm_filename), get_apr_error(msr->mp, rc)); + goto cleanup; + } + + if (value->dptr == NULL) { /* Key not found in DBM file. */ + goto cleanup; + } + + /* ENH Need expiration (and perhaps other metadata) accessible in blob + * form to determine if converting to a table is needed. This will + * save some cycles. + */ + + /* Transform raw data into a table. */ + col = collection_unpack(msr, (const unsigned char *)value->dptr, value->dsize, 1); + if (col == NULL) { + goto cleanup; + } + + /* Close after "value" used from fetch or memory may be overwritten. */ + if (existing_dbm == NULL) { + apr_sdbm_close(dbm); +#ifdef GLOBAL_COLLECTION_LOCK + msr_global_mutex_unlock(msr, msr->modsecurity->dbm_lock, "collection_retrieve_ex"); +#endif + dbm = NULL; + } + + /* Remove expired variables. */ + do { + arr = apr_table_elts(col); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + if (strncmp(te[i].key, "__expire_", 9) == 0) { + msc_string *var = (msc_string *)te[i].val; + int expiry_time = atoi(var->value); + + if (expiry_time <= apr_time_sec(msr->request_time)) { + char *key_to_expire = te[i].key; + + /* Done early if the col expired */ + if (strcmp(key_to_expire, "__expire_KEY") == 0) { + expired = 1; + } + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "collection_retrieve_ex: Removing key \"%s\" from collection.", key_to_expire + 9); + msr_log(msr, 9, "collection_retrieve_ex: Removing key \"%s\" from collection.", key_to_expire); + } + + apr_table_unset(col, key_to_expire + 9); + apr_table_unset(col, key_to_expire); + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "collection_retrieve_ex: Removed expired variable \"%s\".", key_to_expire + 9); + } + + break; + } + } + } + } while(!expired && (i != arr->nelts)); + + /* Delete the collection if the variable "KEY" does not exist. + * + * ENH It would probably be more efficient to hold the DBM + * open until determined if it needs deleted than to open a second + * time. + */ + if (apr_table_get(col, "KEY") == NULL) { + if (existing_dbm == NULL) { +#ifdef GLOBAL_COLLECTION_LOCK + rc = msr_global_mutex_lock(msr, msr->modsecurity->dbm_lock, "collection_retrieve_ex"); + if (rc != APR_SUCCESS) goto cleanup; +#endif + rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK, + CREATEMODE, msr->mp); + if (rc != APR_SUCCESS) { + msr_log(msr, 1, "collection_retrieve_ex: Failed to access DBM file \"%s\": %s", + log_escape(msr->mp, dbm_filename), get_apr_error(msr->mp, rc)); + dbm = NULL; +#ifdef GLOBAL_COLLECTION_LOCK + msr_global_mutex_unlock(msr, msr->modsecurity->dbm_lock, "collection_retrieve_ex"); +#endif + goto cleanup; + } + } + else { + dbm = existing_dbm; + } + + rc = apr_sdbm_delete(dbm, key); + if (rc != APR_SUCCESS) { +#ifdef LOG_NO_COLL_DELET_PB + if (msr->txcfg->debuglog_level >= 9) +#endif + msr_log(msr, 1, "collection_retrieve_ex: Failed deleting collection (name \"%s\", " + "key \"%s\"): %s", log_escape(msr->mp, col_name), + log_escape_ex(msr->mp, col_key, col_key_len), get_apr_error(msr->mp, rc)); + msr->msc_sdbm_delete_error = 1; + goto cleanup; + } + + + if (existing_dbm == NULL) { + apr_sdbm_close(dbm); +#ifdef GLOBAL_COLLECTION_LOCK + msr_global_mutex_unlock(msr, msr->modsecurity->dbm_lock, "collection_retrieve_ex"); +#endif + dbm = NULL; + } + + if (expired && (msr->txcfg->debuglog_level >= 9)) { + msr_log(msr, 9, "collection_retrieve_ex: Collection expired (name \"%s\", key \"%s\").", col_name, + log_escape_ex(msr->mp, col_key, col_key_len)); + } + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "collection_retrieve_ex: Deleted collection (name \"%s\", key \"%s\").", + log_escape(msr->mp, col_name), log_escape_ex(msr->mp, col_key, col_key_len)); + } + goto cleanup; + } + + /* Update UPDATE_RATE */ + { + msc_string *var; + int create_time, counter; + + var = (msc_string *)apr_table_get(col, "CREATE_TIME"); + if (var == NULL) { + /* Error. */ + } else { + create_time = atoi(var->value); + var = (msc_string *)apr_table_get(col, "UPDATE_COUNTER"); + if (var == NULL) { + /* Error. */ + } else { + apr_time_t td; + counter = atoi(var->value); + + /* UPDATE_RATE is removed on store, so add it back here */ + var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + var->name = "UPDATE_RATE"; + var->name_len = strlen(var->name); + apr_table_setn(col, var->name, (void *)var); + + /* NOTE: No rate if there has been no time elapsed */ + td = (apr_time_sec(apr_time_now()) - create_time); + if (td == 0) { + var->value = apr_psprintf(msr->mp, "%d", 0); + } + else { + var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, + (apr_time_t)((60 * counter)/td)); + } + var->value_len = strlen(var->value); + } + } + } + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "collection_retrieve_ex: Retrieved collection (name \"%s\", key \"%s\").", + log_escape(msr->mp, col_name), log_escape_ex(msr->mp, col_key, col_key_len)); + } + + if ((existing_dbm == NULL) && dbm) { + /* Should not ever get here */ + msr_log(msr, 1, "collection_retrieve_ex: Internal Error: Collection remained open (name \"%s\", key \"%s\").", + log_escape(msr->mp, col_name), log_escape_ex(msr->mp, col_key, col_key_len)); + + apr_sdbm_close(dbm); +#ifdef GLOBAL_COLLECTION_LOCK + msr_global_mutex_unlock(msr, msr->modsecurity->dbm_lock, "collection_retrieve_ex"); +#endif + } + + return col; + +cleanup: + + if ((existing_dbm == NULL) && dbm) { + apr_sdbm_close(dbm); +#ifdef GLOBAL_COLLECTION_LOCK + msr_global_mutex_unlock(msr, msr->modsecurity->dbm_lock, "collection_retrieve_ex"); +#endif + } + + return NULL; +} + +/** + * + */ +apr_table_t *collection_retrieve(modsec_rec *msr, const char *col_name, + const char *col_key, int col_key_len) +{ + assert(msr != NULL); + apr_time_t time_before = apr_time_now(); + apr_table_t *rtable = NULL; + + rtable = collection_retrieve_ex(NULL, msr, col_name, col_key, col_key_len); + + msr->time_storage_read += apr_time_now() - time_before; + + return rtable; +} + +/** + * + */ +int collection_store(modsec_rec *msr, apr_table_t *col) { + assert(msr != NULL); + char *dbm_filename = NULL; + msc_string *var_name = NULL, *var_key = NULL; + unsigned char *blob = NULL; + unsigned int blob_size, blob_offset; + apr_status_t rc; + apr_sdbm_datum_t key; + apr_sdbm_datum_t value; + apr_sdbm_t *dbm = NULL; + const apr_array_header_t *arr; + apr_table_entry_t *te; + int i; + const apr_table_t *stored_col = NULL; + const apr_table_t *orig_col = NULL; + char *userinfo = get_username(msr->mp); + + var_name = (msc_string *)apr_table_get(col, "__name"); + if (var_name == NULL) { + goto error; + } + + var_key = (msc_string *)apr_table_get(col, "__key"); + if (var_key == NULL) { + goto error; + } + + if (msr->txcfg->data_dir == NULL) { + msr_log(msr, 1, "collection_store: Unable to store collection (name \"%s\", key \"%s\"). Use " + "SecDataDir to define data directory first.", log_escape_ex(msr->mp, var_name->value, var_name->value_len), + log_escape_ex(msr->mp, var_key->value, var_key->value_len)); + goto error; + } + + // ENH: lowercase the var name in the filename + dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", userinfo, "-", var_name->value, NULL); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "collection_store: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, var_name->value), + log_escape(msr->mp, dbm_filename)); + } + +#ifdef GLOBAL_COLLECTION_LOCK + /* Need to lock to pull in the stored data again and apply deltas. */ + int ret = msr_global_mutex_lock(msr, msr->modsecurity->dbm_lock, "collection_store"); + if (ret != APR_SUCCESS) goto error; +#endif + + /* Delete IS_NEW on store. */ + apr_table_unset(col, "IS_NEW"); + + /* Delete UPDATE_RATE on store to save space as it is calculated */ + apr_table_unset(col, "UPDATE_RATE"); + + /* Update the timeout value. */ + { + msc_string *var = (msc_string *)apr_table_get(col, "TIMEOUT"); + if (var != NULL) { + int timeout = atoi(var->value); + var = (msc_string *)apr_table_get(col, "__expire_KEY"); + if (var != NULL) { + var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(apr_time_now()) + timeout)); + var->value_len = strlen(var->value); + } + } + } + + /* LAST_UPDATE_TIME */ + { + msc_string *var = (msc_string *)apr_table_get(col, "LAST_UPDATE_TIME"); + if (var == NULL) { + var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + var->name = "LAST_UPDATE_TIME"; + var->name_len = strlen(var->name); + apr_table_setn(col, var->name, (void *)var); + } + var->value = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (apr_time_t)(apr_time_sec(apr_time_now()))); + var->value_len = strlen(var->value); + } + + /* UPDATE_COUNTER */ + { + msc_string *var = (msc_string *)apr_table_get(col, "UPDATE_COUNTER"); + int counter = 0; + if (var == NULL) { + var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + var->name = "UPDATE_COUNTER"; + var->name_len = strlen(var->name); + apr_table_setn(col, var->name, (void *)var); + } else { + counter = atoi(var->value); + } + var->value = apr_psprintf(msr->mp, "%d", counter + 1); + var->value_len = strlen(var->value); + } + + /* ENH Make the expiration timestamp accessible in blob form so that + * it is easier/faster to determine expiration without having to + * convert back to table form + */ + + rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK, + CREATEMODE, msr->mp); + if (rc != APR_SUCCESS) { +#ifdef GLOBAL_COLLECTION_LOCK + msr_global_mutex_unlock(msr, msr->modsecurity->dbm_lock, "collection_store"); +#endif + msr_log(msr, 1, "collection_store: Failed to access DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename), + get_apr_error(msr->mp, rc)); + dbm = NULL; + goto error; + } + +#ifndef GLOBAL_COLLECTION_LOCK + /* Need to lock to pull in the stored data again and apply deltas. */ + rc = apr_sdbm_lock(dbm, APR_FLOCK_EXCLUSIVE); + if (rc != APR_SUCCESS) { + msr_log(msr, 1, "collection_store: Failed to exclusivly lock DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename), + get_apr_error(msr->mp, rc)); + goto error; + } +#endif + + /* If there is an original value, then create a delta and + * apply the delta to the current value */ + orig_col = (const apr_table_t *)apr_table_get(msr->collections_original, var_name->value); + if (orig_col != NULL) { + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "collection_store: Re-retrieving collection prior to store: %s", + apr_psprintf(msr->mp, "%.*s", var_name->value_len, var_name->value)); + } + + stored_col = (const apr_table_t *)collection_retrieve_ex(dbm, msr, var_name->value, var_key->value, var_key->value_len); + } + + /* Merge deltas and calculate the size first. */ + blob_size = 3 + 2; + arr = apr_table_elts(col); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msc_string *var = (msc_string *)te[i].val; + int len; + + /* If there is an original value, then apply the delta + * to the latest stored value */ + if (stored_col != NULL) { + const msc_string *orig_var = (const msc_string *)apr_table_get(orig_col, var->name); + if (orig_var != NULL) { + const msc_string *stored_var = (const msc_string *)apr_table_get(stored_col, var->name); + if (stored_var != NULL) { + int origval = atoi(orig_var->value); + int ourval = atoi(var->value); + int storedval = atoi(stored_var->value); + int delta = ourval - origval; + int newval = storedval + delta; + + if (newval < 0) newval = 0; /* Counters never go below zero. */ + + var->value = apr_psprintf(msr->mp, "%d", newval); + var->value_len = strlen(var->value); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "collection_store: Delta applied for %s.%s %d->%d (%d): %d + (%d) = %d [%s,%d]", + log_escape_ex(msr->mp, var_name->value, var_name->value_len), + log_escape_ex(msr->mp, var->name, var->name_len), + origval, ourval, delta, storedval, delta, newval, var->value, var->value_len); + } + } + } + } + + // Allocate blob_size for keys + len = var->name_len + 1; + if (len >= 65536) len = 65536; + blob_size += len + 2; + + // Allocate blob_size for values + len = var->value_len + 1; + if (len >= 65536) len = 65536; + blob_size += len + 2; + } + + /* Now generate the binary object. */ + blob = apr_pcalloc(msr->mp, blob_size); + if (blob == NULL) { + if (dbm != NULL) { +#ifdef GLOBAL_COLLECTION_LOCK + apr_sdbm_close(dbm); + msr_global_mutex_unlock(msr, msr->modsecurity->dbm_lock, "collection_store"); +#else + apr_sdbm_unlock(dbm); + apr_sdbm_close(dbm); +#endif + } + + return -1; + } + + blob[0] = 0x49; + blob[1] = 0x52; + blob[2] = 0x01; + + blob_offset = 3; + arr = apr_table_elts(col); + te = (apr_table_entry_t *)arr->elts; + for (i = 0; i < arr->nelts; i++) { + msc_string *var = (msc_string *)te[i].val; + int len; + + len = var->name_len + 1; + if (len >= 65536) len = 65536; + + blob[blob_offset + 0] = (len & 0xff00) >> 8; + blob[blob_offset + 1] = len & 0x00ff; + memcpy(blob + blob_offset + 2, var->name, len - 1); + blob[blob_offset + 2 + len - 1] = '\0'; + blob_offset += 2 + len; + + len = var->value_len + 1; + if (len >= 65536) len = 65536; + + blob[blob_offset + 0] = (len & 0xff00) >> 8; + blob[blob_offset + 1] = len & 0x00ff; + memcpy(blob + blob_offset + 2, var->value, len - 1); + blob[blob_offset + 2 + len - 1] = '\0'; + blob_offset += 2 + len; + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "collection_store: Wrote variable: name \"%s\", value \"%s\".", + log_escape_ex(msr->mp, var->name, var->name_len), + log_escape_ex(msr->mp, var->value, var->value_len)); + } + } + + blob[blob_offset] = 0; + blob[blob_offset + 1] = 0; + + /* And, finally, store it. */ + key.dptr = var_key->value; + key.dsize = var_key->value_len + 1; + + value.dptr = (char *)blob; + value.dsize = blob_size; + + rc = apr_sdbm_store(dbm, key, value, APR_SDBM_REPLACE); + if (rc != APR_SUCCESS) { + msr_log(msr, 1, "collection_store: Failed to write to DBM file \"%s\": %s", dbm_filename, + get_apr_error(msr->mp, rc)); + if (dbm != NULL) { +#ifdef GLOBAL_COLLECTION_LOCK + apr_sdbm_close(dbm); + msr_global_mutex_unlock(msr, msr->modsecurity->dbm_lock, "collection_store"); +#else + apr_sdbm_unlock(dbm); + apr_sdbm_close(dbm); +#endif + } + + return -1; + } + +#ifdef GLOBAL_COLLECTION_LOCK + apr_sdbm_close(dbm); + msr_global_mutex_unlock(msr, msr->modsecurity->dbm_lock, "collection_store"); +#else + apr_sdbm_unlock(dbm); + apr_sdbm_close(dbm); +#endif + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "collection_store: Persisted collection (name \"%s\", key \"%s\", length=%d).", + log_escape_ex(msr->mp, var_name->value, var_name->value_len), + log_escape_ex(msr->mp, var_key->value, var_key->value_len), value.dsize); + } + + return 0; + +error: + return -1; +} + +/** + * + */ +int collections_remove_stale(modsec_rec *msr, const char *col_name) { + assert(msr != NULL); + assert(col_name != NULL); + char *dbm_filename = NULL; + apr_sdbm_datum_t key, value; + apr_sdbm_t *dbm = NULL; + apr_status_t rc; + apr_array_header_t *keys_arr; + char **keys; + apr_time_t now = apr_time_sec(msr->request_time); + int i; + char *userinfo = get_username(msr->mp); + + if (msr->txcfg->data_dir == NULL) { + /* The user has been warned about this problem enough times already by now. + * msr_log(msr, 1, "Unable to access collection file (name \"%s\"). Use SecDataDir to " + * "define data directory first.", log_escape(msr->mp, col_name)); + */ + goto error; + } + + if(strstr(col_name,"USER") || strstr(col_name,"SESSION") || strstr(col_name, "RESOURCE")) + dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", userinfo, "-", msr->txcfg->webappid, "_", col_name, NULL); + else + dbm_filename = apr_pstrcat(msr->mp, msr->txcfg->data_dir, "/", userinfo, "-", col_name, NULL); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "collections_remove_stale: Retrieving collection (name \"%s\", filename \"%s\")",log_escape(msr->mp, col_name), + log_escape(msr->mp, dbm_filename)); + } + +#ifdef GLOBAL_COLLECTION_LOCK + rc = msr_global_mutex_lock(msr, msr->modsecurity->dbm_lock, "collections_remove_stale"); + if (rc != APR_SUCCESS) goto error; +#endif + + rc = apr_sdbm_open(&dbm, dbm_filename, APR_CREATE | APR_WRITE | APR_SHARELOCK, + CREATEMODE, msr->mp); + if (rc != APR_SUCCESS) { +#ifdef GLOBAL_COLLECTION_LOCK + msr_global_mutex_unlock(msr, msr->modsecurity->dbm_lock, "collections_remove_stale"); +#endif + msr_log(msr, 1, "collections_remove_stale: Failed to access DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename), + get_apr_error(msr->mp, rc)); + dbm = NULL; + goto error; + } + + /* First get a list of all keys. */ + keys_arr = apr_array_make(msr->mp, 256, sizeof(char *)); + +#ifndef GLOBAL_COLLECTION_LOCK + rc = apr_sdbm_lock(dbm, APR_FLOCK_SHARED); + if (rc != APR_SUCCESS) { + msr_log(msr, 1, "collections_remove_stale: Failed to lock DBM file \"%s\": %s", log_escape(msr->mp, dbm_filename), + get_apr_error(msr->mp, rc)); + goto error; + } +#endif + + /* No one can write to the file while doing this so + * do it as fast as possible. + */ + rc = apr_sdbm_firstkey(dbm, &key); + while(rc == APR_SUCCESS) { + if (key.dsize) { + char *s = apr_pstrmemdup(msr->mp, key.dptr, key.dsize - 1); + *(char **)apr_array_push(keys_arr) = s; + } + rc = apr_sdbm_nextkey(dbm, &key); + } +#ifndef GLOBAL_COLLECTION_LOCK + apr_sdbm_unlock(dbm); +#endif + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "collections_remove_stale: Found %d record(s) in file \"%s\".", keys_arr->nelts, + log_escape(msr->mp, dbm_filename)); + } + + /* Now retrieve the entires one by one. */ + keys = (char **)keys_arr->elts; + for (i = 0; i < keys_arr->nelts; i++) { + key.dptr = keys[i]; + key.dsize = strlen(key.dptr) + 1; + + rc = apr_sdbm_fetch(dbm, &value, key); + if (rc != APR_SUCCESS) { + msr_log(msr, 1, "collections_remove_stale: Failed reading DBM file \"%s\": %s", + log_escape(msr->mp, dbm_filename), get_apr_error(msr->mp, rc)); + goto error; + } + + if (value.dptr != NULL) { + apr_table_t *col = NULL; + msc_string *var = NULL; + + col = collection_unpack(msr, (const unsigned char *)value.dptr, value.dsize, 0); + if (col == NULL) { + goto error; + } + + var = (msc_string *)apr_table_get(col, "__expire_KEY"); + if (var == NULL) { + msr_log(msr, 1, "collections_remove_stale: Collection cleanup discovered entry with no " + "__expire_KEY (name \"%s\", key \"%s\").", + log_escape(msr->mp, col_name), log_escape_ex(msr->mp, key.dptr, key.dsize - 1)); + } else { + unsigned int expiry_time = atoi(var->value); + + if (msr->txcfg->debuglog_level >= 9) { + msr_log(msr, 9, "collections_remove_stale: Record (name \"%s\", key \"%s\") set to expire in %" APR_TIME_T_FMT " seconds.", + log_escape(msr->mp, col_name), log_escape_ex(msr->mp, key.dptr, key.dsize - 1), + expiry_time - now); + } + + if (expiry_time <= now) { + rc = apr_sdbm_delete(dbm, key); + if (rc != APR_SUCCESS) { +#ifdef LOG_NO_COLL_DELET_PB + if (msr->txcfg->debuglog_level >= 9) +#endif + msr_log(msr, 1, "collections_remove_stale: Failed deleting collection (name \"%s\", " + "key \"%s\"): %s", log_escape(msr->mp, col_name), + log_escape_ex(msr->mp, key.dptr, key.dsize - 1), get_apr_error(msr->mp, rc)); + msr->msc_sdbm_delete_error = 1; + goto error; + } + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "collections_remove_stale: Removed stale collection (name \"%s\", " + "key \"%s\").", log_escape(msr->mp, col_name), + log_escape_ex(msr->mp, key.dptr, key.dsize - 1)); + } + } + } + } else { + /* Ignore entry not found - it may have been removed in the meantime. */ + } + } + + apr_sdbm_close(dbm); +#ifdef GLOBAL_COLLECTION_LOCK + msr_global_mutex_unlock(msr, msr->modsecurity->dbm_lock, "collections_remove_stale"); +#endif + return 1; + +error: + + if (dbm) { + apr_sdbm_close(dbm); +#ifdef GLOBAL_COLLECTION_LOCK + msr_global_mutex_unlock(msr, msr->modsecurity->dbm_lock, "collections_remove_stale"); +#endif + } + + return -1; +} \ No newline at end of file diff --git a/apache2/re.c b/apache2/re.c index abc6e4d5a5..d067c68419 100644 --- a/apache2/re.c +++ b/apache2/re.c @@ -20,6 +20,10 @@ #include "msc_lua.h" #endif +#ifdef APLOG_USE_MODULE + APLOG_USE_MODULE(security2); +#endif + static const char *const severities[] = { "EMERGENCY", "ALERT", @@ -53,6 +57,7 @@ static apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr); * \param targets Exception list. */ static int fetch_target_exception(msre_rule *rule, modsec_rec *msr, msre_var *var, const char *exceptions) { + assert(msr != NULL); const char *targets = NULL; char *savedptr = NULL, *target = NULL; char *c = NULL, *name = NULL, *value = NULL; @@ -60,25 +65,23 @@ static int fetch_target_exception(msre_rule *rule, modsec_rec *msr, msre_var *va char *myvalue = NULL, *myname = NULL; int match = 0; - if(msr == NULL) - return 0; - - if(var == NULL) + if (var == NULL) return 0; - if(rule == NULL) + if (rule == NULL) return 0; - if(rule->actionset == NULL) + if (rule->actionset == NULL) return 0; - if(rule->actionset->id !=NULL) { + assert(exceptions != NULL); + { myvar = apr_pstrdup(msr->mp, var->name); c = strchr(myvar,':'); - if(c != NULL) { + if (c != NULL) { myname = apr_strtok(myvar,":",&myvalue); } else { myname = myvar; @@ -88,9 +91,9 @@ static int fetch_target_exception(msre_rule *rule, modsec_rec *msr, msre_var *va targets = apr_pstrdup(msr->mp, exceptions); - if(targets != NULL) { + if (targets != NULL) { if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "fetch_target_exception: Found exception target list [%s] for rule id %s", targets, rule->actionset->id); + msr_log(msr, 9, "fetch_target_exception: Found exception target list [%s] for rule id %s", targets, id_log(rule)); } target = apr_strtok((char *)targets, ",", &savedptr); @@ -100,18 +103,18 @@ static int fetch_target_exception(msre_rule *rule, modsec_rec *msr, msre_var *va c = strchr(variable,':'); - if(c != NULL) { + if (c != NULL) { name = apr_strtok(variable,":",&value); } else { name = variable; value = NULL; } - if((strlen(myname) == strlen(name)) && + if ((strlen(myname) == strlen(name)) && (strncasecmp(myname, name,strlen(myname)) == 0)) { - if(value != NULL && myvalue != NULL) { - if((strlen(myvalue) == strlen(value)) && + if (value != NULL && myvalue != NULL) { + if ((strlen(myvalue) == strlen(value)) && strncasecmp(myvalue,value,strlen(myvalue)) == 0) { if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "fetch_target_exception: Target %s will not be processed.", target); @@ -135,14 +138,14 @@ static int fetch_target_exception(msre_rule *rule, modsec_rec *msr, msre_var *va } } else { if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "fetch_target_exception: No exception target found for rule id %s.", rule->actionset->id); + msr_log(msr, 9, "fetch_target_exception: No exception target found for rule id %s.", id_log(rule)); } } } - if(match == 1) + if (match == 1) return 1; return 0; @@ -160,10 +163,10 @@ static int fetch_target_exception(msre_rule *rule, modsec_rec *msr, msre_var *va char *msre_ruleset_rule_update_target_matching_exception(modsec_rec *msr, msre_ruleset *ruleset, rule_exception *re, const char *p2, const char *p3) { char *err; - if(ruleset == NULL) + if (ruleset == NULL) return NULL; - if(p2 == NULL) { + if (p2 == NULL) { return apr_psprintf(ruleset->mp, "Trying to update without a target"); } @@ -199,20 +202,22 @@ char *msre_ruleset_phase_rule_update_target_matching_exception(modsec_rec *msr, apr_array_header_t *phase_arr, const char *p2, const char *p3) { + assert(ruleset != NULL); + assert(phase_arr != NULL); msre_rule **rules; - int i, j, mode; + int i, mode; char *err; - j = 0; mode = 0; rules = (msre_rule **)phase_arr->elts; for (i = 0; i < phase_arr->nelts; i++) { msre_rule *rule = (msre_rule *)rules[i]; + assert(rule != NULL); if (mode == 0) { /* Looking for next rule. */ + assert(rule->actionset != NULL); if (msre_ruleset_rule_matches_exception(rule, re)) { - - err = update_rule_target_ex(NULL, ruleset, rule, p2, p3); + err = update_rule_target_ex(msr, ruleset, rule, p2, p3); if (err) return err; if (rule->actionset->is_chained) mode = 2; /* Match all rules in this chain. */ } else { @@ -234,38 +239,36 @@ char *msre_ruleset_phase_rule_update_target_matching_exception(modsec_rec *msr, char *update_rule_target_ex(modsec_rec *msr, msre_ruleset *ruleset, msre_rule *rule, const char *p2, const char *p3) { + assert(ruleset != NULL); msre_var **targets = NULL; const char *current_targets = NULL; char *my_error_msg = NULL, *target = NULL; char *p = NULL, *savedptr = NULL; unsigned int is_negated = 0, is_counting = 0; - int name_len = 0, value_len = 0; char *name = NULL, *value = NULL; char *opt = NULL, *param = NULL; char *target_list = NULL, *replace = NULL; int i, rc, match = 0, var_appended = 0; - if(rule != NULL) { - + if (rule != NULL) { target_list = strdup(p2); - if(target_list == NULL) - return apr_psprintf(ruleset->mp, "Error to update target - memory allocation");; + if (target_list == NULL) { + my_error_msg = apr_psprintf(ruleset->mp, "Error to update target - memory allocation"); + goto end; + } - if(p3 != NULL) { + if (p3 != NULL) { replace = strdup(p3); - if(replace == NULL) { - free(target_list); - target_list = NULL; - return apr_psprintf(ruleset->mp, "Error to update target - memory allocation");; + if (replace == NULL) { + my_error_msg = apr_psprintf(ruleset->mp, "Error to update target - memory allocation"); + goto end; } } - if(replace != NULL) { - + if (replace != NULL) { opt = strchr(replace,'!'); - - if(opt != NULL) { + if (opt != NULL) { *opt = '\0'; opt++; param = opt; @@ -281,56 +284,36 @@ char *update_rule_target_ex(modsec_rec *msr, msre_ruleset *ruleset, msre_rule *r opt = strchr(param,':'); - if(opt != NULL) { + if (opt != NULL) { name = apr_strtok(param,":",&value); } else { name = param; } - if(apr_table_get(ruleset->engine->variables, name) == NULL) { - if(target_list != NULL) - free(target_list); - if(replace != NULL) - free(replace); - if(msr) { - msr_log(msr, 9, "Error to update target - [%s] is not valid target", name); - } - return apr_psprintf(ruleset->mp, "Error to update target - [%s] is not valid target", name); - } - - name_len = strlen(name); - - if(value != NULL) - value_len = strlen(value); - - if(msr) { - msr_log(msr, 9, "Trying to replace by variable name [%s] value [%s]", name, value); - } -#if !defined(MSC_TEST) - else { - ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, " ModSecurity: Trying to replace by variable name [%s] value [%s]", name, value); + if (apr_table_get(ruleset->engine->variables, name) == NULL) { + my_error_msg = apr_psprintf(ruleset->mp, "Error to update target - [%s] is not valid target", name); + goto end; } -#endif targets = (msre_var **)rule->targets->elts; // TODO need a good way to remove the element from array, maybe change array by tables or rings for (i = 0; i < rule->targets->nelts; i++) { - if((strlen(targets[i]->name) == strlen(name)) && + if ((strlen(targets[i]->name) == strlen(name)) && (strncasecmp(targets[i]->name,name,strlen(targets[i]->name)) == 0) && (targets[i]->is_negated == is_negated) && (targets[i]->is_counting == is_counting)) { - if(value != NULL && targets[i]->param != NULL) { - if((strlen(targets[i]->param) == strlen(value)) && + if (value != NULL && targets[i]->param != NULL) { + if ((strlen(targets[i]->param) == strlen(value)) && strncasecmp(targets[i]->param,value,strlen(targets[i]->param)) == 0) { - memset(targets[i]->name,0,strlen(targets[i]->name)); - memset(targets[i]->param,0,strlen(targets[i]->param)); + targets[i]->name[0] = '\0'; + targets[i]->param[0] = '\0'; targets[i]->is_counting = 0; targets[i]->is_negated = 1; match = 1; } } else if (value == NULL && targets[i]->param == NULL){ - memset(targets[i]->name,0,strlen(targets[i]->name)); + targets[i]->name[0] = '\0'; targets[i]->is_counting = 0; targets[i]->is_negated = 1; match = 1; @@ -343,22 +326,16 @@ char *update_rule_target_ex(modsec_rec *msr, msre_ruleset *ruleset, msre_rule *r p = apr_strtok(target_list, ",", &savedptr); - while(p != NULL) { - if(replace != NULL) { - if(match == 1) { + while (p != NULL) { + if (replace != NULL) { + if (match == 1) { rc = msre_parse_targets(ruleset, p, rule->targets, &my_error_msg); if (rc < 0) { - if(msr) { - msr_log(msr, 9, "Error parsing rule targets to replace variable"); - } -#if !defined(MSC_TEST) - else { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, " ModSecurity: Error parsing rule targets to replace variable"); - } -#endif + if (my_error_msg) my_error_msg = apr_psprintf(ruleset->mp, "Error parsing rule targets to replace variable: %s", my_error_msg); + else my_error_msg = apr_psprintf(ruleset->mp, "Error parsing rule targets to replace variable"); goto end; } - if(msr) { + if (msr) { msr_log(msr, 9, "Successfully replaced variable"); } #if !defined(MSC_TEST) @@ -369,28 +346,24 @@ char *update_rule_target_ex(modsec_rec *msr, msre_ruleset *ruleset, msre_rule *r var_appended = 1; } else { - if(msr) { - msr_log(msr, 9, "Cannot find variable to replace"); - } -#if !defined(MSC_TEST) - else { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, " ModSecurity: Cannot find varibale to replace"); - } -#endif + my_error_msg = apr_psprintf(ruleset->mp, "Cannot find variable to replace"); goto end; } - } else { + } + else { target = strdup(p); - if(target == NULL) - return NULL; + if (target == NULL) { + my_error_msg = apr_psprintf(ruleset->mp, "Error to update target - memory allocation"); + goto end; + } is_negated = is_counting = 0; param = name = value = NULL; opt = strchr(target,'!'); - if(opt != NULL) { + if (opt != NULL) { *opt = '\0'; opt++; param = opt; @@ -405,30 +378,18 @@ char *update_rule_target_ex(modsec_rec *msr, msre_ruleset *ruleset, msre_rule *r } opt = strchr(param,':'); - - if(opt != NULL) { + if (opt != NULL) { name = apr_strtok(param,":",&value); } else { name = param; } - if(apr_table_get(ruleset->engine->variables, name) == NULL) { - if(target_list != NULL) - free(target_list); - if(replace != NULL) - free(replace); - if(msr) { - msr_log(msr, 9, "Error to update target - [%s] is not valid target", name); - } - return apr_psprintf(ruleset->mp, "Error to update target - [%s] is not valid target", name); + if (apr_table_get(ruleset->engine->variables, name) == NULL) { + my_error_msg = apr_psprintf(ruleset->mp, "Error to update target - [%s] is not valid target", name); + goto end; } - name_len = strlen(name); - - if(value != NULL) - value_len = strlen(value); - - if(msr) { + if (msr) { msr_log(msr, 9, "Trying to append variable name [%s] value [%s]", name, value); } #if !defined(MSC_TEST) @@ -440,45 +401,37 @@ char *update_rule_target_ex(modsec_rec *msr, msre_ruleset *ruleset, msre_rule *r targets = (msre_var **)rule->targets->elts; for (i = 0; i < rule->targets->nelts; i++) { - if((strlen(targets[i]->name) == strlen(name)) && + if ((strlen(targets[i]->name) == strlen(name)) && (strncasecmp(targets[i]->name,name,strlen(targets[i]->name)) == 0) && (targets[i]->is_negated == is_negated) && (targets[i]->is_counting == is_counting)) { - if(value != NULL && targets[i]->param != NULL) { - if((strlen(targets[i]->param) == strlen(value)) && + if (value != NULL && targets[i]->param != NULL) { + if ((strlen(targets[i]->param) == strlen(value)) && strncasecmp(targets[i]->param,value,strlen(targets[i]->param)) == 0) { match = 1; } } else if (value == NULL && targets[i]->param == NULL){ match = 1; - } else - continue; + } else continue; } } - if(target != NULL) { + if (target != NULL) { free(target); target = NULL; } - if(match == 0 ) { + if (match == 0 ) { rc = msre_parse_targets(ruleset, p, rule->targets, &my_error_msg); if (rc < 0) { - if(msr) { - msr_log(msr, 9, "Error parsing rule targets to append variable"); - } -#if !defined(MSC_TEST) - else { - ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, " ModSecurity: Error parsing rule targets to append variable"); - } -#endif + my_error_msg = apr_psprintf(ruleset->mp, "Error parsing rule targets to append variable"); goto end; } var_appended = 1; } else { - if(msr) { + if (msr) { msr_log(msr, 9, "Skipping variable, already appended"); } #if !defined(MSC_TEST) @@ -492,11 +445,11 @@ char *update_rule_target_ex(modsec_rec *msr, msre_ruleset *ruleset, msre_rule *r p = apr_strtok(NULL,",",&savedptr); } - if(var_appended == 1) { + if (var_appended == 1) { current_targets = msre_generate_target_string(ruleset->mp, rule); rule->unparsed = msre_rule_generate_unparsed(ruleset->mp, rule, current_targets, NULL, NULL); rule->p1 = apr_pstrdup(ruleset->mp, current_targets); - if(msr) { + if (msr) { msr_log(msr, 9, "Successfully appended variable"); } #if !defined(MSC_TEST) @@ -508,26 +461,23 @@ char *update_rule_target_ex(modsec_rec *msr, msre_ruleset *ruleset, msre_rule *r } end: - if(target_list != NULL) { - free(target_list); - target_list = NULL; - } - if(replace != NULL) { - free(replace); - replace = NULL; - } - if(target != NULL) { - free(target); - target = NULL; - } - return NULL; + if (my_error_msg) { + if (msr) msr_log(msr, 9, "%s", my_error_msg); + else ap_log_error(APLOG_MARK, APLOG_INFO, 0, NULL, "%s", my_error_msg); + } + if (target_list != NULL) free(target_list); + if (replace != NULL) free(replace); + if (target != NULL) free(target); + return my_error_msg; } int msre_ruleset_rule_matches_exception(msre_rule *rule, rule_exception *re) { + assert(rule != NULL); int match = 0; /* Only remove non-placeholder rules */ if (rule->placeholder == RULE_PH_NONE) { + assert(re != NULL); switch(re->type) { case RULE_EXCEPTION_REMOVE_ID : if ((rule->actionset != NULL)&&(rule->actionset->id != NULL)) { @@ -564,7 +514,7 @@ int msre_ruleset_rule_matches_exception(msre_rule *rule, rule_exception *re) { for (act = 0; act < tarr->nelts; act++) { msre_action *action = (msre_action *)telts[act].val; - if((action != NULL) && (action->metadata != NULL) && (strcmp("tag", action->metadata->name) == 0)) { + if ((action != NULL) && (action->metadata != NULL) && (strcmp("tag", action->metadata->name) == 0)) { int rc = msc_regexec(re->param_data, action->param, strlen(action->param), @@ -615,7 +565,7 @@ static char *msre_generate_target_string(apr_pool_t *pool, msre_rule *rule) { for (i = 0; i < rule->targets->nelts; i++) { - if(targets[i]->name != NULL && strlen(targets[i]->name) > 0) { + if (targets[i]->name != NULL && strlen(targets[i]->name) > 0) { target_str = apr_pstrcat(pool, (target_str == NULL) ? "" : apr_psprintf(pool, "%s|", target_str), (targets[i]->is_negated == 0) ? "" : "!", @@ -633,7 +583,10 @@ static char *msre_generate_target_string(apr_pool_t *pool, msre_rule *rule) { /** * Generate an action string from an actionset. */ -static char *msre_actionset_generate_action_string(apr_pool_t *pool, const msre_actionset *actionset) { +#ifndef DEBUG_CONF +static +#endif +char *msre_actionset_generate_action_string(apr_pool_t *pool, const msre_actionset *actionset) { const apr_array_header_t *tarr = NULL; const apr_table_entry_t *telts = NULL; char *actions = NULL; @@ -860,6 +813,7 @@ static msre_action_metadata *msre_resolve_action(msre_engine *engine, const char msre_var *msre_create_var_ex(apr_pool_t *pool, msre_engine *engine, const char *name, const char *param, modsec_rec *msr, char **error_msg) { + // msr can be NULL const char *varparam = param; msre_var *var = apr_pcalloc(pool, sizeof(msre_var)); if (var == NULL) return NULL; @@ -940,6 +894,9 @@ msre_var *msre_create_var_ex(apr_pool_t *pool, msre_engine *engine, const char * static msre_var *msre_create_var(msre_ruleset *ruleset, const char *name, const char *param, modsec_rec *msr, char **error_msg) { + // msr can be NULL + assert(ruleset != NULL); + assert(error_msg != NULL); msre_var *var = msre_create_var_ex(ruleset->mp, ruleset->engine, name, param, msr, error_msg); if (var == NULL) return NULL; @@ -1458,6 +1415,7 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) rules = (msre_rule **)arr->elts; for (i = 0; i < arr->nelts; i++) { msre_rule *rule = rules[i]; + assert(rule != NULL); rule->execution_time = 0; } @@ -1470,6 +1428,7 @@ apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) rules = (msre_rule **)arr->elts; for (i = 0; i < arr->nelts; i++) { msre_rule *rule = rules[i]; + assert(rule != NULL); /* Ignore markers, which are never processed. */ if (rule->placeholder == RULE_PH_MARKER) continue; @@ -1488,6 +1447,8 @@ static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_re #else apr_status_t msre_ruleset_process_phase(msre_ruleset *ruleset, modsec_rec *msr) { #endif + assert(ruleset != NULL); + assert(msr != NULL); apr_array_header_t *arr = NULL; msre_rule **rules; apr_status_t rc; @@ -1532,6 +1493,8 @@ static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_re rules = (msre_rule **)arr->elts; for (i = 0; i < arr->nelts; i++) { msre_rule *rule = rules[i]; + assert(rule != NULL); + assert(rule->actionset != NULL); #if defined(PERFORMANCE_MEASUREMENT) apr_time_t time1 = 0; #endif @@ -1547,12 +1510,12 @@ static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_re if ((rule->placeholder == RULE_PH_NONE) || (rule->actionset->id == NULL) || (strcmp(skip_after, rule->actionset->id) != 0)) { - if(i-1 >=0) + if (i-1 >=0) last_rule = rules[i-1]; else last_rule = rules[0]; - if((last_rule != NULL) && (last_rule->actionset != NULL) && last_rule->actionset->is_chained && (saw_starter == 1)) { + if ((last_rule != NULL) && (last_rule->actionset != NULL) && last_rule->actionset->is_chained && (saw_starter == 1)) { mode = NEXT_RULE; skipped = 1; --i; @@ -1562,7 +1525,7 @@ static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_re saw_starter = 0; if (msr->txcfg->debuglog_level >= 9) { - msr_log(msr, 9, "Current rule is id=\"%s\" [chained %d] is trying to find the SecMarker=\"%s\" [stater %d]",rule->actionset->id,last_rule->actionset->is_chained,skip_after,saw_starter); + msr_log(msr, 9, "Current rule is id=\"%s\" [chained %d] is trying to find the SecMarker=\"%s\" [stater %d]", id_log(rule),last_rule->actionset->is_chained,skip_after,saw_starter); } } @@ -1644,7 +1607,7 @@ static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_re for(j = 0; j < msr->removed_rules_msg->nelts; j++) { re = ((rule_exception **)msr->removed_rules_msg->elts)[j]; - if(rule->actionset->msg !=NULL) { + if (rule->actionset->msg !=NULL) { if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "Checking removal of rule msg=\"%s\" against: %s", rule->actionset->msg, re->param); @@ -1663,7 +1626,7 @@ static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_re for(j = 0; j < msr->removed_rules->nelts; j++) { range = ((const char**)msr->removed_rules->elts)[j]; - if(rule->actionset->id !=NULL) { + if (rule->actionset->id !=NULL) { if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "Checking removal of rule id=\"%s\" against: %s", rule->actionset->id, range); @@ -1682,13 +1645,13 @@ static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_re for (act = 0; act < tag_tarr->nelts; act++) { msre_action *action = (msre_action *)tag_telts[act].val; - if((action != NULL) && (action->metadata != NULL ) && strcmp("tag", action->metadata->name) == 0) { + if ((action != NULL) && (action->metadata != NULL ) && strcmp("tag", action->metadata->name) == 0) { for(j = 0; j < msr->removed_rules_tag->nelts; j++) { re = ((rule_exception **)msr->removed_rules_tag->elts)[j]; - if(action->param != NULL) { + if (action->param != NULL) { /* Expand variables in the tag argument. */ msc_string *var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); @@ -1719,7 +1682,7 @@ static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_re msr_log(msr, 5, "Not processing %srule id=\"%s\": " "removed by ctl action", rule->actionset->is_chained ? "chained " : "", - rule->actionset->id); + id_log(rule)); } /* Skip the whole chain, if this is a chained rule */ @@ -1734,7 +1697,7 @@ static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_re } } - if(msr->txcfg->is_enabled == MODSEC_DISABLED) { + if (msr->txcfg->is_enabled == MODSEC_DISABLED) { saw_starter = 0; skipped = 0; skip_after = NULL; @@ -1753,11 +1716,11 @@ static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_re fn = apr_psprintf(p, " [file \"%s\"] [line \"%d\"]", rule->filename, rule->line_num); } - if (rule->actionset != NULL && rule->actionset->id != NULL) { + if (rule->actionset->id != NULL) { id = apr_psprintf(p, " [id \"%s\"]", rule->actionset->id); } - if (rule->actionset != NULL && rule->actionset->rev != NULL) { + if (rule->actionset->rev != NULL) { rev = apr_psprintf(p, " [rev \"%s\"]", rule->actionset->rev); } @@ -1814,12 +1777,12 @@ static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_re msr_log(msr, 9, "Match, intercepted -> returning."); } - if(i-1 >= 0) + if (i-1 >= 0) last_rule = rules[i-1]; else last_rule = rules[0]; - if((last_rule != NULL) && (last_rule->actionset != NULL) && last_rule->actionset->is_chained) { + if ((last_rule != NULL) && (last_rule->actionset != NULL) && last_rule->actionset->is_chained) { int st = 0; @@ -1827,8 +1790,8 @@ static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_re rule_starter = rules[st]; - if(rule_starter != NULL && rule_starter->chain_starter != NULL) { - if((msr != NULL) && (msr->intercept_actionset != NULL) && (rule_starter->actionset != NULL)) + if (rule_starter != NULL && rule_starter->chain_starter != NULL) { + if ((msr != NULL) && (msr->intercept_actionset != NULL) && (rule_starter->actionset != NULL)) msr->intercept_actionset->intercept_uri = rule_starter->actionset->intercept_uri; break; } @@ -1852,7 +1815,7 @@ static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_re continue; } - if(skipped == 1) { + if (skipped == 1) { mode = SKIP_RULES; continue; } @@ -1889,17 +1852,11 @@ static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_re } } else if (rc < 0) { - const char *id = ""; const char *msg = ""; - if (rule->actionset) { - if (rule->actionset->id) { - id = rule->actionset->id; - } - if (rule->actionset->msg) { - msg = rule->actionset->msg; - } + if (rule->actionset->msg) { + msg = rule->actionset->msg; } - msr_log(msr, 1, "Rule processing failed (id=%s, msg=%s).", id, msg); + msr_log(msr, 1, "Rule processing failed (id=%s, msg=%s).", id_log(rule), msg); if (msr->txcfg->reqintercept_oe == 1) { apr_table_clear(msr->matched_vars); @@ -1929,17 +1886,11 @@ static apr_status_t msre_ruleset_process_phase_(msre_ruleset *ruleset, modsec_re } } else { - const char *id = ""; const char *msg = ""; - if (rule->actionset) { - if (rule->actionset->id) { - id = rule->actionset->id; - } - if (rule->actionset->msg) { - msg = rule->actionset->msg; - } + if (rule->actionset->msg) { + msg = rule->actionset->msg; } - msr_log(msr, 1, "Rule processing failed with unknown return code: %d (id=%s, msg=%s).", rc, id, msg); + msr_log(msr, 1, "Rule processing failed with unknown return code: %d (id=%s, msg=%s).", rc, id_log(rule), msg); apr_table_clear(msr->matched_vars); return -1; } @@ -1975,6 +1926,9 @@ msre_ruleset *msre_ruleset_create(msre_engine *engine, apr_pool_t *mp) { * Adds one rule to the given phase of the ruleset. */ int msre_ruleset_rule_add(msre_ruleset *ruleset, msre_rule *rule, int phase) { + assert(ruleset != NULL); + assert(rule != NULL); + assert(rule->actionset != NULL); apr_array_header_t *arr = NULL; switch (phase) { @@ -2012,6 +1966,8 @@ int msre_ruleset_rule_add(msre_ruleset *ruleset, msre_rule *rule, int phase) { static msre_rule * msre_ruleset_fetch_phase_rule(const msre_ruleset *ruleset, const char *id, const apr_array_header_t *phase_arr, int offset) { + assert(id != NULL); + assert(phase_arr != NULL); msre_rule **rules = (msre_rule **)phase_arr->elts; int i; @@ -2025,7 +1981,7 @@ static msre_rule * msre_ruleset_fetch_phase_rule(const msre_ruleset *ruleset, co && (strcmp(rule->actionset->id, id) == 0)) { /* Return rule that matched unless it is a placeholder */ - if(offset == 0) { + if (offset == 0) { return (rule->placeholder == RULE_PH_NONE) ? rule : NULL; } else { @@ -2068,6 +2024,7 @@ msre_rule * msre_ruleset_fetch_rule(msre_ruleset *ruleset, const char *id, int o static int msre_ruleset_phase_rule_remove_with_exception(msre_ruleset *ruleset, rule_exception *re, apr_array_header_t *phase_arr) { + assert(phase_arr != NULL); msre_rule **rules; int i, j, mode, removed_count; @@ -2077,15 +2034,18 @@ static int msre_ruleset_phase_rule_remove_with_exception(msre_ruleset *ruleset, rules = (msre_rule **)phase_arr->elts; for (i = 0; i < phase_arr->nelts; i++) { msre_rule *rule = (msre_rule *)rules[i]; + assert(rule != NULL); + assert(rule->actionset != NULL); if (mode == 0) { /* Looking for next rule. */ int remove_rule = 0; /* Only remove non-placeholder rules */ if (rule->placeholder == RULE_PH_NONE) { + assert(re != NULL); switch(re->type) { case RULE_EXCEPTION_REMOVE_ID : - if ((rule->actionset != NULL)&&(rule->actionset->id != NULL)) { + if (rule->actionset->id != NULL) { int ruleid = atoi(rule->actionset->id); if (rule_id_in_range(ruleid, re->param)) { @@ -2120,7 +2080,7 @@ static int msre_ruleset_phase_rule_remove_with_exception(msre_ruleset *ruleset, for (act = 0; act < tarr->nelts; act++) { msre_action *action = (msre_action *)telts[act].val; - if((action != NULL) && (action->metadata != NULL) && (strcmp("tag", action->metadata->name) == 0)) { + if ((action != NULL) && (action->metadata != NULL) && (strcmp("tag", action->metadata->name) == 0)) { int rc = msc_regexec(re->param_data, action->param, strlen(action->param), @@ -2197,6 +2157,7 @@ static const char *msre_format_severity(int severity) { * Creates a string containing the metadata of the supplied rule. */ char *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset) { + assert(msr != NULL); const apr_array_header_t *tarr; const apr_table_entry_t *telts; char *id = ""; @@ -2302,6 +2263,7 @@ char *msre_format_metadata(modsec_rec *msr, msre_actionset *actionset) { char * msre_rule_generate_unparsed(apr_pool_t *pool, const msre_rule *rule, const char *targets, const char *args, const char *actions) { + assert(rule != NULL); char *unparsed = NULL; const char *r_targets = targets; const char *r_args = args; @@ -2361,12 +2323,19 @@ msre_rule *msre_rule_create(msre_ruleset *ruleset, int type, const char *fn, int line, const char *targets, const char *args, const char *actions, char **error_msg) { + assert(ruleset != NULL); + assert(args != NULL); + assert(error_msg != NULL); + // Normally useless code, left to be safe for the moment + if (error_msg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, ruleset->mp, "msre_rule_create: error_msg is NULL"); + return NULL; + } msre_rule *rule; char *my_error_msg; const char *argsp; int rc; - if (error_msg == NULL) return NULL; *error_msg = NULL; rule = (msre_rule *)apr_pcalloc(ruleset->mp, sizeof(msre_rule)); @@ -2493,6 +2462,8 @@ msre_rule *msre_rule_lua_create(msre_ruleset *ruleset, static void msre_perform_nondisruptive_actions(modsec_rec *msr, msre_rule *rule, msre_actionset *actionset, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(actionset != NULL); const apr_array_header_t *tarr; const apr_table_entry_t *telts; int i; @@ -2515,6 +2486,10 @@ static void msre_perform_nondisruptive_actions(modsec_rec *msr, msre_rule *rule, static void msre_perform_disruptive_actions(modsec_rec *msr, msre_rule *rule, msre_actionset *actionset, apr_pool_t *mptmp, const char *message) { + assert(msr != NULL); + assert(actionset != NULL); + assert(actionset->intercept_action_rec != NULL); + assert(actionset->intercept_action_rec->metadata != NULL); const apr_array_header_t *tarr; const apr_table_entry_t *telts; int i; @@ -2528,6 +2503,7 @@ static void msre_perform_disruptive_actions(modsec_rec *msr, msre_rule *rule, telts = (const apr_table_entry_t*)tarr->elts; for (i = 0; i < tarr->nelts; i++) { msre_action *action = (msre_action *)telts[i].val; + assert(action->metadata != NULL); if (action->metadata->type == ACTION_DISRUPTIVE) { if (action->metadata->execute != NULL) { action->metadata->execute(msr, mptmp, rule, action); @@ -2599,6 +2575,14 @@ static void msre_perform_disruptive_actions(modsec_rec *msr, msre_rule *rule, static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, msre_actionset *acting_actionset, apr_pool_t *mptmp) { + assert(var != NULL); + assert(rule != NULL); + assert(rule->actionset != NULL); + assert(rule->op_metadata != NULL); + assert(rule->op_metadata->execute != NULL); + assert(msr != NULL); + assert(acting_actionset != NULL); + assert(mptmp != NULL); apr_time_t time_before_op = 0; char *my_error_msg = NULL; const char *full_varname = NULL; @@ -2639,7 +2623,7 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, if (rc > 0) { rc = fetch_target_exception(rule, msr, var, exceptions); - if(rc > 0) { + if (rc > 0) { if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Executing operator \"%s%s\" with param \"%s\" against %s skipped.", @@ -2687,22 +2671,22 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, msr_log(msr, 4, "Operator completed in %" APR_TIME_T_FMT " usec.", (t1 - time_before_op)); } - if(msr->txcfg->max_rule_time > 0) { + if (msr->txcfg->max_rule_time > 0) { apr_time_t t1 = apr_time_now(); apr_time_t rule_time = 0; const char *rt_time = NULL; - if(rule->actionset->id != NULL) { + if (rule->actionset->id != NULL) { rt_time = apr_table_get(msr->perf_rules, rule->actionset->id); - if(rt_time == NULL) { + if (rt_time == NULL) { rt_time = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, (t1 - time_before_op)); rule_time = (apr_time_t)atoi(rt_time); - if(rule_time >= msr->txcfg->max_rule_time) + if (rule_time >= msr->txcfg->max_rule_time) apr_table_setn(msr->perf_rules, rule->actionset->id, rt_time); } else { rule_time = (apr_time_t)atoi(rt_time); rule_time += (t1 - time_before_op); - if(rule_time >= msr->txcfg->max_rule_time) { + if (rule_time >= msr->txcfg->max_rule_time) { rt_time = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT, rule_time); apr_table_setn(msr->perf_rules, rule->actionset->id, rt_time); } @@ -2740,7 +2724,7 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, *(const msre_rule **)apr_array_push(msr->matched_rules) = rule; /* Save the last matched var data */ - if(var != NULL && msr != NULL) { + if (var != NULL && msr != NULL) { msc_string *mvar = NULL; msr->matched_var->name = apr_pstrdup(msr->mp, var->name); @@ -2783,6 +2767,11 @@ static int execute_operator(msre_var *var, msre_rule *rule, modsec_rec *msr, * Executes rule against the given transaction. */ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { + assert(rule != NULL); + assert(rule->actionset != NULL); + assert(rule->targets != NULL); + assert(msr != NULL); + assert(msr->txcfg != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; msre_actionset *acting_actionset = NULL; @@ -3107,6 +3096,8 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { /* Perform transformations. */ tarr = apr_table_elts(normtab); + /* if no transformation, multi_match makes no sense and breaks the logic */ + if (tarr->nelts == 0) multi_match = 0; /* Execute transformations in a loop. */ @@ -3327,6 +3318,8 @@ static apr_status_t msre_rule_process_normal(msre_rule *rule, modsec_rec *msr) { * */ static apr_status_t msre_rule_process_lua(msre_rule *rule, modsec_rec *msr) { + assert(rule != NULL); + assert(msr != NULL); msre_actionset *acting_actionset = NULL; char *my_error_msg = NULL; int rc; @@ -3364,6 +3357,7 @@ static apr_status_t msre_rule_process_lua(msre_rule *rule, modsec_rec *msr) { * */ static apr_status_t msre_rule_process(msre_rule *rule, modsec_rec *msr) { + assert(msr != NULL); /* Use a fresh memory sub-pool for processing each rule */ if (msr->msc_rule_mptmp == NULL) { if (apr_pool_create(&msr->msc_rule_mptmp, msr->mp) != APR_SUCCESS) { diff --git a/apache2/re.h b/apache2/re.h index c0c5433965..db6c190455 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -75,6 +75,10 @@ int DSOLOCAL rule_id_in_range(int ruleid, const char *range); msre_var DSOLOCAL *generate_single_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, msre_rule *rule, apr_pool_t *mptmp); +#ifdef DEBUG_CONF +char DSOLOCAL* msre_actionset_generate_action_string(apr_pool_t* pool, const msre_actionset* actionset); +#endif + #if defined(WITH_LUA) apr_table_t DSOLOCAL *generate_multi_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, msre_rule *rule, apr_pool_t *mptmp); diff --git a/apache2/re_actions.c b/apache2/re_actions.c index f81ddc87c7..febc4759e1 100644 --- a/apache2/re_actions.c +++ b/apache2/re_actions.c @@ -27,6 +27,8 @@ static void msre_engine_action_register(msre_engine *engine, const char *name, unsigned int cardinality_group, fn_action_validate_t validate, fn_action_init_t init, fn_action_execute_t execute) { + assert(engine != NULL); + assert(name != NULL); msre_action_metadata *metadata = (msre_action_metadata *)apr_pcalloc(engine->mp, sizeof(msre_action_metadata)); if (metadata == NULL) return; @@ -51,6 +53,7 @@ static void msre_engine_action_register(msre_engine *engine, const char *name, msre_var *generate_single_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, msre_rule *rule, apr_pool_t *mptmp) { + assert(msr != NULL); apr_table_t *vartab = NULL; const apr_table_entry_t *te = NULL; const apr_array_header_t *arr = NULL; @@ -92,6 +95,8 @@ msre_var *generate_single_var(modsec_rec *msr, msre_var *var, apr_array_header_t rvar->value = rval; rvar->value_len = rval_len; + assert(msr != NULL); + assert(msr->txcfg != NULL); if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "T (%d) %s: \"%s\"", rc, tfn->name, log_escape_nq_ex(mptmp, rvar->value, rvar->value_len)); @@ -108,6 +113,7 @@ msre_var *generate_single_var(modsec_rec *msr, msre_var *var, apr_array_header_t apr_table_t *generate_multi_var(modsec_rec *msr, msre_var *var, apr_array_header_t *tfn_arr, msre_rule *rule, apr_pool_t *mptmp) { + assert(msr != NULL); const apr_array_header_t *tarr; const apr_table_entry_t *telts; apr_table_t *vartab = NULL, *tvartab = NULL; @@ -169,6 +175,9 @@ apr_table_t *generate_multi_var(modsec_rec *msr, msre_var *var, apr_array_header * in the given variable. */ int expand_macros(modsec_rec *msr, msc_string *var, msre_rule *rule, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->txcfg != NULL); + assert(var != NULL); char *data = NULL; apr_array_header_t *arr = NULL; char *p = NULL, *q = NULL, *t = NULL; @@ -316,6 +325,8 @@ int expand_macros(modsec_rec *msr, msc_string *var, msre_rule *rule, apr_pool_t * value that is set. */ apr_status_t collection_original_setvar(modsec_rec *msr, const char *col_name, const msc_string *orig_var) { + assert(msr != NULL); + assert(msr->txcfg != NULL); apr_table_t *table = NULL; msc_string *var = NULL; const char *var_name = NULL; @@ -374,6 +385,8 @@ apr_status_t collection_original_setvar(modsec_rec *msr, const char *col_name, c static apr_status_t msre_action_marker_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); actionset->id = action->param; return 1; } @@ -383,6 +396,8 @@ static apr_status_t msre_action_marker_init(msre_engine *engine, apr_pool_t *mp, static apr_status_t msre_action_id_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); actionset->id = action->param; return 1; } @@ -409,6 +424,8 @@ static char *msre_action_id_validate(msre_engine *engine, apr_pool_t *mp, msre_a static apr_status_t msre_action_rev_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); actionset->rev = action->param; return 1; } @@ -418,6 +435,8 @@ static apr_status_t msre_action_rev_init(msre_engine *engine, apr_pool_t *mp, ms static apr_status_t msre_action_msg_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); actionset->msg = action->param; return 1; } @@ -427,6 +446,8 @@ static apr_status_t msre_action_msg_init(msre_engine *engine, apr_pool_t *mp, ms static apr_status_t msre_action_logdata_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); actionset->logdata = action->param; return 1; } @@ -436,6 +457,8 @@ static apr_status_t msre_action_logdata_init(msre_engine *engine, apr_pool_t *mp static apr_status_t msre_action_sanitizeMatchedBytes_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); char *parse_parm = NULL; char *ac_param = NULL; char *savedptr = NULL; @@ -464,6 +487,8 @@ static apr_status_t msre_action_sanitizeMatchedBytes_init(msre_engine *engine, a static apr_status_t msre_action_accuracy_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); actionset->accuracy = atoi(action->param); return 1; } @@ -473,6 +498,8 @@ static apr_status_t msre_action_accuracy_init(msre_engine *engine, apr_pool_t *m static apr_status_t msre_action_maturity_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); actionset->maturity = atoi(action->param); return 1; } @@ -482,6 +509,8 @@ static apr_status_t msre_action_maturity_init(msre_engine *engine, apr_pool_t *m static apr_status_t msre_action_ver_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); actionset->version = action->param; return 1; } @@ -491,6 +520,8 @@ static apr_status_t msre_action_ver_init(msre_engine *engine, apr_pool_t *mp, static apr_status_t msre_action_severity_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); if (strcasecmp(action->param, "emergency") == 0) { actionset->severity = 0; } else if (strcasecmp(action->param, "alert") == 0) { @@ -518,6 +549,7 @@ static apr_status_t msre_action_severity_init(msre_engine *engine, apr_pool_t *m static apr_status_t msre_action_chain_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); actionset->is_chained = 1; return 1; } @@ -526,6 +558,7 @@ static apr_status_t msre_action_chain_init(msre_engine *engine, apr_pool_t *mp, static apr_status_t msre_action_log_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); actionset->log = 1; return 1; } @@ -534,6 +567,7 @@ static apr_status_t msre_action_log_init(msre_engine *engine, apr_pool_t *mp, ms static apr_status_t msre_action_nolog_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); actionset->log = 0; actionset->auditlog = 0; return 1; @@ -543,6 +577,7 @@ static apr_status_t msre_action_nolog_init(msre_engine *engine, apr_pool_t *mp, static apr_status_t msre_action_auditlog_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); actionset->auditlog = 1; return 1; } @@ -551,6 +586,7 @@ static apr_status_t msre_action_auditlog_init(msre_engine *engine, apr_pool_t *m static apr_status_t msre_action_noauditlog_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); actionset->auditlog = 0; return 1; } @@ -559,6 +595,7 @@ static apr_status_t msre_action_noauditlog_init(msre_engine *engine, apr_pool_t static apr_status_t msre_action_block_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); /* Right now we just set a flag and inherit the real disruptive action */ actionset->block = 1; return 1; @@ -568,6 +605,7 @@ static apr_status_t msre_action_block_init(msre_engine *engine, apr_pool_t *mp, static apr_status_t msre_action_deny_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); actionset->intercept_action = ACTION_DENY; actionset->intercept_action_rec = action; return 1; @@ -582,6 +620,8 @@ static char *msre_action_status_validate(msre_engine *engine, apr_pool_t *mp, ms static apr_status_t msre_action_status_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); actionset->intercept_status = atoi(action->param); return 1; } @@ -590,6 +630,8 @@ static apr_status_t msre_action_status_init(msre_engine *engine, apr_pool_t *mp, static apr_status_t msre_action_drop_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); actionset->intercept_action = ACTION_DROP; actionset->intercept_action_rec = action; return 1; @@ -604,6 +646,8 @@ static char *msre_action_pause_validate(msre_engine *engine, apr_pool_t *mp, msr static apr_status_t msre_action_pause_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); actionset->intercept_action = ACTION_PAUSE; actionset->intercept_pause = action->param; return 1; @@ -619,6 +663,8 @@ static char *msre_action_redirect_validate(msre_engine *engine, apr_pool_t *mp, static apr_status_t msre_action_redirect_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); actionset->intercept_action = ACTION_REDIRECT; actionset->intercept_uri = action->param; actionset->intercept_action_rec = action; @@ -628,6 +674,10 @@ static apr_status_t msre_action_redirect_init(msre_engine *engine, apr_pool_t *m static apr_status_t msre_action_redirect_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { + assert(msr != NULL); + assert(rule != NULL); + assert(rule->actionset != NULL); + assert(action != NULL); msc_string *var = NULL; var = apr_pcalloc(mptmp, sizeof(msc_string)); @@ -651,6 +701,8 @@ static char *msre_action_proxy_validate(msre_engine *engine, apr_pool_t *mp, msr static apr_status_t msre_action_proxy_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); actionset->intercept_action = ACTION_PROXY; actionset->intercept_uri = action->param; actionset->intercept_action_rec = action; @@ -660,6 +712,9 @@ static apr_status_t msre_action_proxy_init(msre_engine *engine, apr_pool_t *mp, static apr_status_t msre_action_proxy_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { + assert(msr != NULL); + assert(rule != NULL); + assert(action != NULL); msc_string *var = NULL; var = apr_pcalloc(mptmp, sizeof(msc_string)); @@ -683,6 +738,8 @@ static apr_status_t msre_action_proxy_execute(modsec_rec *msr, apr_pool_t *mptmp static apr_status_t msre_action_pass_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); actionset->intercept_action = ACTION_NONE; actionset->intercept_action_rec = action; return 1; @@ -698,6 +755,8 @@ static char *msre_action_skip_validate(msre_engine *engine, apr_pool_t *mp, msre static apr_status_t msre_action_skip_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); actionset->skip_count = atoi(action->param); if (actionset->skip_count <= 0) actionset->skip_count = 1; return 1; @@ -713,6 +772,8 @@ static char *msre_action_skipAfter_validate(msre_engine *engine, apr_pool_t *mp, static apr_status_t msre_action_skipAfter_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); actionset->skip_after = action->param; return 1; } @@ -722,6 +783,8 @@ static apr_status_t msre_action_skipAfter_init(msre_engine *engine, apr_pool_t * static apr_status_t msre_action_allow_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); actionset->intercept_action = ACTION_ALLOW; actionset->intercept_action_rec = action; @@ -738,6 +801,7 @@ static apr_status_t msre_action_allow_init(msre_engine *engine, apr_pool_t *mp, } static char *msre_action_allow_validate(msre_engine *engine, apr_pool_t *mp, msre_action *action) { + assert(action != NULL); if (action->param != NULL) { if (strcasecmp(action->param, "phase") == 0) { return NULL; @@ -762,6 +826,8 @@ static char *msre_action_phase_validate(msre_engine *engine, apr_pool_t *mp, msr static apr_status_t msre_action_phase_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(actionset != NULL); + assert(action != NULL); if(strcasecmp(action->param,"request") == 0) actionset->phase = 2; else if(strcasecmp(action->param,"response") == 0) @@ -777,6 +843,7 @@ static apr_status_t msre_action_phase_init(msre_engine *engine, apr_pool_t *mp, /* t */ static char *msre_action_t_validate(msre_engine *engine, apr_pool_t *mp, msre_action *action) { + assert(action != NULL); msre_tfn_metadata *metadata = NULL; metadata = msre_engine_tfn_resolve(engine, action->param); if (metadata == NULL) return apr_psprintf(mp, "Invalid transformation function: %s", @@ -788,6 +855,7 @@ static char *msre_action_t_validate(msre_engine *engine, apr_pool_t *mp, msre_ac static apr_status_t msre_action_t_init(msre_engine *engine, apr_pool_t *mp, msre_actionset *actionset, msre_action *action) { + assert(action != NULL); msre_tfn_metadata *metadata = (msre_tfn_metadata *)action->param_data; action->param_data = metadata; return 1; @@ -795,6 +863,7 @@ static apr_status_t msre_action_t_init(msre_engine *engine, apr_pool_t *mp, msre /* ctl */ static char *msre_action_ctl_validate(msre_engine *engine, apr_pool_t *mp, msre_action *action) { + assert(action != NULL); char *name = NULL; char *value = NULL; @@ -953,6 +1022,13 @@ static char *msre_action_ctl_validate(msre_engine *engine, apr_pool_t *mp, msre_ if (strcasecmp(value, "on") == 0) return NULL; if (strcasecmp(value, "off") == 0) return NULL; return apr_psprintf(mp, "Invalid setting for ctl name HashEngine: %s", value); + } + else + if (strcasecmp(name, "parseXmlIntoArgs") == 0) { + if (strcasecmp(value, "on") == 0) return NULL; + if (strcasecmp(value, "off") == 0) return NULL; + if (strcasecmp(value, "onlyargs") == 0) return NULL; + return apr_psprintf(mp, "Invalid setting for ctl name parseXmlIntoArgs: %s", value); } else { return apr_psprintf(mp, "Invalid ctl name setting: %s", name); } @@ -968,6 +1044,8 @@ static apr_status_t msre_action_ctl_init(msre_engine *engine, apr_pool_t *mp, ms static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { + assert(msr != NULL); + assert(action != NULL); char *name = NULL; char *value = NULL; @@ -1235,11 +1313,24 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Ctl: ruleRemoveTargetById id=%s targets=%s", p1, p2); } - re = apr_pcalloc(msr->mp, sizeof(rule_exception)); - re->type = RULE_EXCEPTION_REMOVE_ID; - re->param = (const char *)apr_pstrdup(msr->mp, p1); - apr_table_addn(msr->removed_targets, apr_pstrdup(msr->mp, p2), (void *)re); - return 1; + if (p2 == NULL) { + msr_log(msr, 1, "Ctl: ruleRemoveTargetById: Missing target for id \"%s\"", p1); + return -1; + } + + re = apr_pcalloc(msr->mp, sizeof(rule_exception)); + if (re == NULL) { + msr_log(msr, 1, "Ctl: Memory allocation error"); + return -1; + } + re->type = RULE_EXCEPTION_REMOVE_ID; + re->param = (const char *)apr_pstrdup(msr->mp, p1); + if (re->param == NULL) { + msr_log(msr, 1, "Ctl: Memory allocation error"); + return -1; + } + apr_table_addn(msr->removed_targets, apr_pstrdup(msr->mp, p2), (void *)re); + return 1; } else if (strcasecmp(name, "ruleRemoveTargetByTag") == 0) { rule_exception *re = NULL; @@ -1247,27 +1338,26 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, char *savedptr = NULL; p1 = apr_strtok(value,";",&savedptr); - p2 = apr_strtok(NULL,";",&savedptr); if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Ctl: ruleRemoveTargetByTag tag=%s targets=%s", p1, p2); } - if (p2 == NULL) { + if (p2 == NULL) { msr_log(msr, 1, "ModSecurity: Missing target for tag \"%s\"", p1); - return -1; - } - - re = apr_pcalloc(msr->mp, sizeof(rule_exception)); - re->type = RULE_EXCEPTION_REMOVE_TAG; - re->param = (const char *)apr_pstrdup(msr->mp, p1); - re->param_data = msc_pregcomp(msr->mp, p1, 0, NULL, NULL); - if (re->param_data == NULL) { - msr_log(msr, 1, "ModSecurity: Invalid regular expression \"%s\"", p1); - return -1; - } - apr_table_addn(msr->removed_targets, apr_pstrdup(msr->mp, p2), (void *)re); - return 1; + return -1; + } + + re = apr_pcalloc(msr->mp, sizeof(rule_exception)); + re->type = RULE_EXCEPTION_REMOVE_TAG; + re->param = (const char *)apr_pstrdup(msr->mp, p1); + re->param_data = msc_pregcomp(msr->mp, p1, 0, NULL, NULL); + if (re->param_data == NULL) { + msr_log(msr, 1, "ModSecurity: Invalid regular expression \"%s\"", p1); + return -1; + } + apr_table_addn(msr->removed_targets, apr_pstrdup(msr->mp, p2), (void *)re); + return 1; } else if (strcasecmp(name, "ruleRemoveTargetByMsg") == 0) { rule_exception *re = NULL; @@ -1275,35 +1365,59 @@ static apr_status_t msre_action_ctl_execute(modsec_rec *msr, apr_pool_t *mptmp, char *savedptr = NULL; p1 = apr_strtok(value,";",&savedptr); - p2 = apr_strtok(NULL,";",&savedptr); if (msr->txcfg->debuglog_level >= 4) { msr_log(msr, 4, "Ctl: ruleRemoveTargetByMsg msg=%s targets=%s", p1, p2); } + if (p2 == NULL) { + msr_log(msr, 1, "ModSecurity: Missing target for msg \"%s\"", p1); + return -1; + } - re = apr_pcalloc(msr->mp, sizeof(rule_exception)); - re->type = RULE_EXCEPTION_REMOVE_MSG; - re->param = apr_pstrdup(msr->mp, p1); - re->param_data = msc_pregcomp(msr->mp, p1, 0, NULL, NULL); - if (re->param_data == NULL) { - msr_log(msr, 1, "ModSecurity: Invalid regular expression \"%s\"", p1); - return -1; - } - apr_table_addn(msr->removed_targets, apr_pstrdup(msr->mp, p2), (void *)re); - return 1; - } - else { - /* Should never happen, but log if it does. */ - msr_log(msr, 1, "Internal Error: Unknown ctl action \"%s\".", name); - return -1; + re = apr_pcalloc(msr->mp, sizeof(rule_exception)); + re->type = RULE_EXCEPTION_REMOVE_MSG; + re->param = apr_pstrdup(msr->mp, p1); + re->param_data = msc_pregcomp(msr->mp, p1, 0, NULL, NULL); + if (re->param_data == NULL) { + msr_log(msr, 1, "ModSecurity: Invalid regular expression \"%s\"", p1); + return -1; + } + apr_table_addn(msr->removed_targets, apr_pstrdup(msr->mp, p2), (void *)re); + return 1; + } else + if (strcasecmp(name, "parseXmlIntoArgs") == 0) { + + if (strcasecmp(value, "on") == 0) { + msr->txcfg->parse_xml_into_args = MSC_XML_ARGS_ON; + msr->usercfg->parse_xml_into_args = MSC_XML_ARGS_ON; + } + else + if (strcasecmp(value, "off") == 0) { + msr->txcfg->parse_xml_into_args = MSC_XML_ARGS_OFF; + msr->usercfg->parse_xml_into_args = MSC_XML_ARGS_OFF; + } + else + if (strcasecmp(value, "onlyargs") == 0) { + msr->txcfg->parse_xml_into_args = MSC_XML_ARGS_ONLYARGS; + msr->usercfg->parse_xml_into_args = MSC_XML_ARGS_ONLYARGS; + } + + if (msr->txcfg->debuglog_level >= 4) { + msr_log(msr, 4, "Ctl: Set parseXmlIntoArgs to %s.", value); + } + + return 1; } + /* Should never happen, but log if it does. */ + msr_log(msr, 1, "Internal Error: Unknown ctl action \"%s\".", name); return -1; } /* xmlns */ static char *msre_action_xmlns_validate(msre_engine *engine, apr_pool_t *mp, msre_action *action) { + assert(action != NULL); char *name = NULL; char *value = NULL; @@ -1327,6 +1441,8 @@ static char *msre_action_xmlns_validate(msre_engine *engine, apr_pool_t *mp, msr static apr_status_t msre_action_sanitizeArg_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { + assert(msr != NULL); + assert(action != NULL); const char *sargname = NULL; const apr_array_header_t *tarr; const apr_table_entry_t *telts; @@ -1339,8 +1455,9 @@ static apr_status_t msre_action_sanitizeArg_execute(modsec_rec *msr, apr_pool_t for (i = 0; i < tarr->nelts; i++) { msc_arg *arg = (msc_arg *)telts[i].val; - if (strcasecmp(sargname, arg->name) == 0) { + if (arg->marked_for_sanitization == 0 && strcasecmp(sargname, arg->name) == 0) { apr_table_addn(msr->arguments_to_sanitize, arg->name, (void *)arg); + arg->marked_for_sanitization = 1; } } @@ -1355,17 +1472,21 @@ static apr_status_t msre_action_sanitizeArg_execute(modsec_rec *msr, apr_pool_t static apr_status_t msre_action_sanitizeMatched_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { + assert(msr != NULL); + assert(action != NULL); const char *sargname = NULL; const apr_array_header_t *tarr; const apr_table_entry_t *telts; int i, type = 0; msc_string *mvar = msr->matched_var; + assert(mvar != NULL); if (mvar->name_len == 0) return 0; /* IMP1 We need to extract the variable name properly here, * taking into account it may have been escaped. */ + assert(mvar->name != NULL); if ((mvar->name_len > 5) && (strncmp(mvar->name, "ARGS:", 5) == 0)) { sargname = apr_pstrdup(msr->mp, mvar->name + 5); type = SANITISE_ARG; @@ -1400,21 +1521,27 @@ static apr_status_t msre_action_sanitizeMatched_execute(modsec_rec *msr, apr_poo switch(type) { case SANITISE_ARG : + assert(msr->arguments_to_sanitize != NULL); tarr = apr_table_elts(msr->arguments); + assert(tarr != NULL); telts = (const apr_table_entry_t*)tarr->elts; for (i = 0; i < tarr->nelts; i++) { msc_arg *arg = (msc_arg *)telts[i].val; - if (strcasecmp(sargname, arg->name) == 0) { + assert(arg != NULL); + if (arg->marked_for_sanitization == 0 && strcasecmp(sargname, arg->name) == 0) { apr_table_addn(msr->arguments_to_sanitize, arg->name, (void *)arg); + arg->marked_for_sanitization = 1; } } break; case SANITISE_REQUEST_HEADER : + assert(msr->request_headers_to_sanitize != NULL); apr_table_set(msr->request_headers_to_sanitize, sargname, "1"); break; case SANITISE_RESPONSE_HEADER : + assert(msr->response_headers_to_sanitize != NULL); apr_table_set(msr->response_headers_to_sanitize, sargname, "1"); break; @@ -1430,6 +1557,10 @@ static apr_status_t msre_action_sanitizeMatched_execute(modsec_rec *msr, apr_poo static apr_status_t msre_action_sanitizeRequestHeader_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { + assert(msr != NULL); + assert(msr->request_headers_to_sanitize != NULL); + assert(action != NULL); + assert(action->param != NULL); apr_table_set(msr->request_headers_to_sanitize, action->param, "1"); return 1; } @@ -1438,6 +1569,10 @@ static apr_status_t msre_action_sanitizeRequestHeader_execute(modsec_rec *msr, a static apr_status_t msre_action_sanitizeResponseHeader_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { + assert(msr != NULL); + assert(msr->response_headers_to_sanitize != NULL); + assert(action != NULL); + assert(action->param != NULL); apr_table_set(msr->response_headers_to_sanitize, action->param, "1"); return 1; } @@ -1446,6 +1581,9 @@ static apr_status_t msre_action_sanitizeResponseHeader_execute(modsec_rec *msr, static apr_status_t msre_action_setenv_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { + assert(msr != NULL); + assert(action != NULL); + assert(action->param != NULL); char *data = apr_pstrdup(mptmp, action->param); char *env_name = NULL, *env_value = NULL; char *s = NULL; @@ -1463,6 +1601,7 @@ static apr_status_t msre_action_setenv_execute(modsec_rec *msr, apr_pool_t *mptm *s = '\0'; } + assert(msr->txcfg != NULL); if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "Setting env variable: %s=%s", env_name, env_value); } @@ -1481,6 +1620,7 @@ static apr_status_t msre_action_setenv_execute(modsec_rec *msr, apr_pool_t *mptm /* Execute the requested action. */ if (env_name != NULL && env_name[0] == '!') { /* Delete */ + assert(msr->r != NULL); apr_table_unset(msr->r->subprocess_env, env_name + 1); if (msr->txcfg->debuglog_level >= 9) { @@ -1501,6 +1641,7 @@ static apr_status_t msre_action_setenv_execute(modsec_rec *msr, apr_pool_t *mptm expand_macros(msr, val, rule, mptmp); /* To be safe, we escape NULs as it goes in subprocess_env. */ + assert(msr->mp != NULL); val_value = log_escape_nul(msr->mp, (const unsigned char *)val->value, val->value_len); apr_table_set(msr->r->subprocess_env, env_name, val_value); @@ -1519,6 +1660,9 @@ static apr_status_t msre_action_setenv_execute(modsec_rec *msr, apr_pool_t *mptm apr_status_t msre_action_setvar_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, char *var_name, char *var_value) { + assert(msr != NULL); + assert(var_name != NULL); + assert(var_value != NULL); char *col_name = NULL; char *s = NULL; apr_table_t *target_col = NULL; @@ -1540,9 +1684,13 @@ apr_status_t msre_action_setvar_execute(modsec_rec *msr, apr_pool_t *mptmp, var->value_len = strlen(var->value); expand_macros(msr, var, rule, mptmp); var_name = log_escape_nq_ex(msr->mp, var->value, var->value_len); + if (var_name == NULL) { + msr_log(msr, 1, "Failed to allocate space to expand name macros"); + return -1; + } /* Handle the exclamation mark. */ - if (var_name != NULL && var_name[0] == '!') { + if (var_name[0] == '!') { var_name = var_name + 1; is_negated = 1; } @@ -1702,6 +1850,9 @@ apr_status_t msre_action_setvar_execute(modsec_rec *msr, apr_pool_t *mptmp, static apr_status_t msre_action_setvar_parse(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { + assert(msr != NULL); + assert(action != NULL); + assert(action->param != NULL); char *data = apr_pstrdup(mptmp, action->param); char *var_name = NULL, *var_value = NULL; char *s = NULL; @@ -1717,7 +1868,7 @@ static apr_status_t msre_action_setvar_parse(modsec_rec *msr, apr_pool_t *mptmp, var_value = s + 1; *s = '\0'; - while ((*var_value != '\0')&&(isspace(*var_value))) var_value++; + while (isspace(*var_value)) var_value++; } return msre_action_setvar_execute(msr,mptmp,rule,var_name,var_value); @@ -1727,6 +1878,9 @@ static apr_status_t msre_action_setvar_parse(modsec_rec *msr, apr_pool_t *mptmp, static apr_status_t msre_action_expirevar_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { + assert(msr != NULL); + assert(action != NULL); + assert(action->param != NULL); char *data = apr_pstrdup(mptmp, action->param); char *col_name = NULL, *var_name = NULL, *var_value = NULL; char *s = NULL; @@ -1824,6 +1978,9 @@ static apr_status_t msre_action_expirevar_execute(modsec_rec *msr, apr_pool_t *m static apr_status_t msre_action_deprecatevar_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { + assert(msr != NULL); + assert(action != NULL); + assert(action->param != NULL); char *data = apr_pstrdup(mptmp, action->param); char *col_name = NULL, *var_name = NULL, *var_value = NULL; char *s = NULL; @@ -1958,6 +2115,10 @@ static apr_status_t msre_action_deprecatevar_execute(modsec_rec *msr, apr_pool_t static apr_status_t init_collection(modsec_rec *msr, const char *real_col_name, const char *col_name, const char *col_key, unsigned int col_key_len) { + assert(msr != NULL); + assert(msr->collections != NULL); + assert(msr->txcfg != NULL); + assert(real_col_name != NULL); apr_table_t *table = NULL; msc_string *var = NULL; @@ -1971,7 +2132,6 @@ static apr_status_t init_collection(modsec_rec *msr, const char *real_col_name, /* Init collection from storage. */ table = collection_retrieve(msr, real_col_name, col_key, col_key_len); - if (table == NULL) { /* Does not exist yet - create new. */ @@ -2092,6 +2252,9 @@ static apr_status_t init_collection(modsec_rec *msr, const char *real_col_name, static apr_status_t msre_action_initcol_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { + assert(msr != NULL); + assert(action != NULL); + assert(action->param != NULL); char *data = apr_pstrdup(msr->mp, action->param); char *col_name = NULL, *col_key = NULL; unsigned int col_key_len; @@ -2123,6 +2286,9 @@ static apr_status_t msre_action_initcol_execute(modsec_rec *msr, apr_pool_t *mpt static apr_status_t msre_action_setsid_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { + assert(msr != NULL); + assert(action != NULL); + assert(action->param != NULL); msc_string *var = NULL; char *real_col_name = NULL, *col_key = NULL; unsigned int col_key_len; @@ -2147,6 +2313,9 @@ static apr_status_t msre_action_setsid_execute(modsec_rec *msr, apr_pool_t *mptm static apr_status_t msre_action_setuid_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { + assert(msr != NULL); + assert(action != NULL); + assert(action->param != NULL); msc_string *var = NULL; char *real_col_name = NULL, *col_key = NULL; unsigned int col_key_len; @@ -2171,6 +2340,9 @@ static apr_status_t msre_action_setuid_execute(modsec_rec *msr, apr_pool_t *mptm static apr_status_t msre_action_setrsc_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { + assert(msr != NULL); + assert(action != NULL); + assert(action->param != NULL); msc_string *var = NULL; char *real_col_name = NULL, *col_key = NULL; unsigned int col_key_len; @@ -2192,6 +2364,8 @@ static apr_status_t msre_action_setrsc_execute(modsec_rec *msr, apr_pool_t *mptm /* exec */ static char *msre_action_exec_validate(msre_engine *engine, apr_pool_t *mp, msre_action *action) { + assert(action != NULL); + assert(action->param != NULL); #if defined(WITH_LUA) char *filename = (char *)action->param; @@ -2219,7 +2393,9 @@ static char *msre_action_exec_validate(msre_engine *engine, apr_pool_t *mp, msre static apr_status_t msre_action_exec_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { - #if defined(WITH_LUA) + assert(msr != NULL); + assert(action != NULL); +#if defined(WITH_LUA) if (action->param_data != NULL) { /* Lua */ msc_script *script = (msc_script *)action->param_data; char *my_error_msg = NULL; @@ -2247,6 +2423,9 @@ static apr_status_t msre_action_exec_execute(modsec_rec *msr, apr_pool_t *mptmp, static apr_status_t msre_action_prepend_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { + assert(msr != NULL); + assert(action != NULL); + assert(action->param != NULL); msc_string *var = NULL; /* Expand any macros in the text */ @@ -2267,6 +2446,9 @@ static apr_status_t msre_action_prepend_execute(modsec_rec *msr, apr_pool_t *mpt static apr_status_t msre_action_append_execute(modsec_rec *msr, apr_pool_t *mptmp, msre_rule *rule, msre_action *action) { + assert(msr != NULL); + assert(action != NULL); + assert(action->param != NULL); msc_string *var = NULL; /* Expand any macros in the text */ diff --git a/apache2/re_operators.c b/apache2/re_operators.c index e54a540700..aa678c0ab5 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* Copyright (c) 2004-2023 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 @@ -37,6 +37,27 @@ #include "libinjection/libinjection.h" +#ifdef WITH_PCRE_STUDY +#ifdef WITH_PCRE_JIT +#ifndef WITH_PCRE +/** + * @brief Set the JIT compile return code and JIT compile status. + * \param regex regex structure + * \param rc return code of the JIT compile + * \param jit JIT compile status + * \return void + */ +static void msc_op_set_jitrc(msc_regex_t *regex, int *rc, int *jit) { + *rc = regex->jit_compile_rc; + if (*rc == 0) { + msc_fullinfo(regex, PCRE2_INFO_JITSIZE, jit); + *jit = (*jit > 0) ? 1 : 0; + } + return; +} +#endif +#endif +#endif /** * @@ -44,6 +65,8 @@ void msre_engine_op_register(msre_engine *engine, const char *name, fn_op_param_init_t fn1, fn_op_execute_t fn2) { + assert(engine != NULL); + assert(name != NULL); msre_op_metadata *metadata = (msre_op_metadata *)apr_pcalloc(engine->mp, sizeof(msre_op_metadata)); if (metadata == NULL) return; @@ -58,6 +81,7 @@ void msre_engine_op_register(msre_engine *engine, const char *name, * */ msre_op_metadata *msre_engine_op_resolve(msre_engine *engine, const char *name) { + assert(engine != NULL); return (msre_op_metadata *)apr_table_get(engine->operators, name); } @@ -70,6 +94,7 @@ msre_op_metadata *msre_engine_op_resolve(msre_engine *engine, const char *name) static int msre_op_unconditionalmatch_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(error_msg != NULL); *error_msg = "Unconditional match in SecAction."; /* Always match. */ @@ -81,6 +106,7 @@ static int msre_op_unconditionalmatch_execute(modsec_rec *msr, msre_rule *rule, static int msre_op_nomatch_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(error_msg != NULL); *error_msg = "No match."; /* Never match. */ @@ -99,13 +125,17 @@ static int msre_op_nomatch_execute(modsec_rec *msr, msre_rule *rule, * \retval 0 On Fail */ static int msre_op_ipmatch_param_init(msre_rule *rule, char **error_msg) { + assert(rule != NULL); + assert(error_msg != NULL); + // Normally useless code, left to be safe for the moment + if (error_msg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, rule->ruleset->mp, "msre_op_ipmatch_param_init: error_msg is NULL"); + return -1; + } char *param = NULL; int res = 0; - if (error_msg == NULL) - return -1; - else - *error_msg = NULL; + *error_msg = NULL; param = apr_pstrdup(rule->ruleset->mp, rule->op_param); @@ -131,13 +161,13 @@ static int msre_op_ipmatch_param_init(msre_rule *rule, char **error_msg) { * \retval 0 On No Match */ static int msre_op_ipmatch_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(var != NULL); + assert(error_msg != NULL); TreeRoot *rtree = NULL; int res = 0; - if (error_msg == NULL) - return -1; - else - *error_msg = NULL; + *error_msg = NULL; if (rule == NULL || rule->ip_op == NULL) { msr_log(msr, 1, "ipMatch Internal Error: ipmatch value is null."); @@ -171,6 +201,8 @@ static int msre_op_ipmatch_execute(modsec_rec *msr, msre_rule *rule, msre_var *v * \retval 0 On Fail */ static int msre_op_ipmatchFromFile_param_init(msre_rule *rule, char **error_msg) { + assert(rule != NULL); + assert(error_msg != NULL); const char *rootpath = NULL; const char *filepath = NULL; const char *ipfile_path = NULL; @@ -258,14 +290,14 @@ static int msre_op_ipmatchFromFile_param_init(msre_rule *rule, char **error_msg) */ static int msre_op_ipmatchFromFile_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); TreeRoot *rtree = (TreeRoot *)rule->op_param_data; int res = 0; - if (error_msg == NULL) - return -1; - else - *error_msg = NULL; + *error_msg = NULL; if (rtree == NULL) { @@ -298,6 +330,8 @@ static int msre_op_ipmatchFromFile_execute(modsec_rec *msr, msre_rule *rule, /* rsub */ static char *param_remove_escape(msre_rule *rule, char *str, int len) { + assert(rule != NULL); + assert(str != NULL); char *parm = apr_pcalloc(rule->ruleset->mp, len); char *ret = parm; @@ -330,6 +364,14 @@ static char *param_remove_escape(msre_rule *rule, char *str, int len) { */ #if !defined(MSC_TEST) static int msre_op_rsub_param_init(msre_rule *rule, char **error_msg) { + assert(rule != NULL); + assert(rule->ruleset != NULL); + assert(error_msg != NULL); + // Normally useless code, left to be safe for the moment + if (error_msg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, rule->ruleset->mp, "msre_op_rsub_param_init: error_msg is NULL"); + return -1; + } #if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 0 ap_regex_t *regex; #else @@ -347,7 +389,6 @@ static int msre_op_rsub_param_init(msre_rule *rule, char **error_msg) { int ignore_case = 0; unsigned short int op_len = 0; - if (error_msg == NULL) return -1; *error_msg = NULL; line = rule->op_param; @@ -469,8 +510,20 @@ static int msre_op_rsub_param_init(msre_rule *rule, char **error_msg) { * \retval 0 On No Match */ static int msre_op_rsub_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + if (!str) { + msr_log(msr, 1, "rsub: Memory allocation error"); + return -1; + } msc_string *re_pattern = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + if (!re_pattern) { + msr_log(msr, 1, "rsub: Memory allocation error"); + return -1; + } char *offset = NULL; char *data = NULL, *pattern = NULL; char *data_out = NULL; @@ -483,7 +536,6 @@ static int msre_op_rsub_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, regmatch_t pmatch[AP_MAX_REG_MATCH]; #endif - if (error_msg == NULL) return -1; *error_msg = NULL; if(strcmp(var->name,"STREAM_OUTPUT_BODY") == 0 ) { @@ -604,24 +656,15 @@ static int msre_op_rsub_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, size+=sl; *data_out=0; - if(msr->stream_output_data != NULL && output_body == 1) { - - memset(msr->stream_output_data, 0x0, msr->stream_output_length); + if (msr->stream_output_data != NULL && output_body == 1) { free(msr->stream_output_data); msr->stream_output_data = NULL; msr->stream_output_length = 0; - msr->stream_output_data = (char *)malloc(size+1); - - if(msr->stream_output_data == NULL) { - return -1; - } + if (msr->stream_output_data == NULL) return -1; msr->stream_output_length = size; - memset(msr->stream_output_data, 0x0, size+1); - msr->of_stream_changed = 1; - memcpy(msr->stream_output_data, data, size); msr->stream_output_data[size] = '\0'; @@ -629,21 +672,20 @@ static int msre_op_rsub_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, var->value = msr->stream_output_data; } - if(msr->stream_input_data != NULL && input_body == 1) { - memset(msr->stream_input_data, 0x0, msr->stream_input_length); + if (msr->stream_input_data != NULL && input_body == 1) { free(msr->stream_input_data); msr->stream_input_data = NULL; msr->stream_input_length = 0; - +#ifdef MSC_LARGE_STREAM_INPUT + msr->stream_input_allocated_length = 0; +#endif msr->stream_input_data = (char *)malloc(size+1); - - if(msr->stream_input_data == NULL) { - return -1; - } + if(msr->stream_input_data == NULL) return -1; msr->stream_input_length = size; - memset(msr->stream_input_data, 0x0, size+1); - +#ifdef MSC_LARGE_STREAM_INPUT + msr->stream_input_allocated_length = size; +#endif msr->if_stream_changed = 1; memcpy(msr->stream_input_data, data, size); @@ -671,13 +713,15 @@ static int msre_op_rsub_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, * \retval 0 On fail */ static int msre_op_validateHash_param_init(msre_rule *rule, char **error_msg) { + assert(rule != NULL); + assert(error_msg != NULL); const char *errptr = NULL; int erroffset; msc_regex_t *regex; const char *pattern = rule->op_param; #ifdef WITH_PCRE_STUDY #ifdef WITH_PCRE_JIT - int rc, jit; + int rc, jit = 0; #endif #endif @@ -686,7 +730,12 @@ static int msre_op_validateHash_param_init(msre_rule *rule, char **error_msg) { /* Compile pattern */ if(strstr(pattern,"%{") == NULL) { - regex = msc_pregcomp_ex(rule->ruleset->mp, pattern, PCRE_DOTALL | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); +#ifndef WITH_PCRE + int options = PCRE2_DOTALL | PCRE2_DOLLAR_ENDONLY; +#else + int options = PCRE_DOTALL | PCRE_DOLLAR_ENDONLY; +#endif + regex = msc_pregcomp_ex(rule->ruleset->mp, pattern, options, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); if (regex == NULL) { *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s", erroffset, errptr); @@ -695,7 +744,11 @@ static int msre_op_validateHash_param_init(msre_rule *rule, char **error_msg) { #ifdef WITH_PCRE_STUDY #ifdef WITH_PCRE_JIT + #ifndef WITH_PCRE + msc_op_set_jitrc(regex, &rc, &jit); + #else rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit); + #endif if ((rc != 0) || (jit != 1)) { *error_msg = apr_psprintf(rule->ruleset->mp, "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - " @@ -731,24 +784,30 @@ static int msre_op_validateHash_param_init(msre_rule *rule, char **error_msg) { * \retval 0 On fail */ static int msre_op_validateHash_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); msc_regex_t *regex = (msc_regex_t *)rule->op_param_data; msc_string *re_pattern = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + if (!re_pattern) { + msr_log(msr, 1, "validateHash: Memory allocation error"); + return -1; + } const char *target; const char *errptr = NULL; int erroffset; + int options = 0; unsigned int target_length; char *my_error_msg = NULL; int ovector[33]; int rc; - const char *pattern = NULL; #ifdef WITH_PCRE_STUDY #ifdef WITH_PCRE_JIT - int jit; + int jit = 0; #endif #endif - - if (error_msg == NULL) return -1; *error_msg = NULL; if (msr->txcfg->hash_enforcement == HASH_DISABLED || msr->txcfg->hash_is_enabled == HASH_DISABLED) @@ -770,15 +829,20 @@ static int msre_op_validateHash_execute(modsec_rec *msr, msre_rule *rule, msre_v expand_macros(msr, re_pattern, rule, msr->mp); - pattern = log_escape_re(msr->mp, re_pattern->value); + const char *pattern = log_escape_re(msr->mp, re_pattern->value); if (msr->txcfg->debuglog_level >= 6) { msr_log(msr, 6, "Escaping pattern [%s]",pattern); } - regex = msc_pregcomp_ex(rule->ruleset->mp, pattern, PCRE_DOTALL | PCRE_DOLLAR_ENDONLY, &errptr, +#ifndef WITH_PCRE + options = PCRE2_DOTALL | PCRE2_DOLLAR_ENDONLY; +#else + options = PCRE_DOTALL | PCRE_DOLLAR_ENDONLY; +#endif + regex = msc_pregcomp_ex(msr->mp, pattern, options, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); if (regex == NULL) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s", + *error_msg = apr_psprintf(msr->mp, "Error compiling pattern (offset %d): %s", erroffset, errptr); return 0; } @@ -786,9 +850,13 @@ static int msre_op_validateHash_execute(modsec_rec *msr, msre_rule *rule, msre_v #ifdef WITH_PCRE_STUDY #ifdef WITH_PCRE_JIT if (msr->txcfg->debuglog_level >= 4) { + #ifndef WITH_PCRE + msc_op_set_jitrc(regex, &rc, &jit); + #else rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit); + #endif if ((rc != 0) || (jit != 1)) { - *error_msg = apr_psprintf(rule->ruleset->mp, + *error_msg = apr_psprintf(msr->mp, "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - " "Execution error - " "Does not support JIT (%d)", @@ -820,7 +888,11 @@ static int msre_op_validateHash_execute(modsec_rec *msr, msre_rule *rule, msre_v * and no memory has to be allocated for any backreferences. */ rc = msc_regexec_capture(regex, target, target_length, ovector, 30, &my_error_msg); +#ifndef WITH_PCRE + if ((rc == PCRE2_ERROR_MATCHLIMIT) || (rc == PCRE2_ERROR_RECURSIONLIMIT)) { +#else if ((rc == PCRE_ERROR_MATCHLIMIT) || (rc == PCRE_ERROR_RECURSIONLIMIT)) { +#endif msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); if (s == NULL) return -1; @@ -850,7 +922,11 @@ static int msre_op_validateHash_execute(modsec_rec *msr, msre_rule *rule, msre_v return -1; } +#ifndef WITH_PCRE + if (rc != PCRE2_ERROR_NOMATCH) { /* Match. */ +#else if (rc != PCRE_ERROR_NOMATCH) { /* Match. */ +#endif /* We no longer escape the pattern here as it is done when logging */ char *pattern = apr_pstrdup(msr->mp, log_escape(msr->mp, regex->pattern ? regex->pattern : "")); char *hmac = NULL, *valid = NULL; @@ -914,13 +990,15 @@ static int msre_op_validateHash_execute(modsec_rec *msr, msre_rule *rule, msre_v /* rx */ static int msre_op_rx_param_init(msre_rule *rule, char **error_msg) { + assert(rule != NULL); + assert(error_msg != NULL); const char *errptr = NULL; int erroffset; msc_regex_t *regex; const char *pattern = rule->op_param; #ifdef WITH_PCRE_STUDY #ifdef WITH_PCRE_JIT - int rc, jit; + int rc, jit = 0; #endif #endif @@ -929,7 +1007,12 @@ static int msre_op_rx_param_init(msre_rule *rule, char **error_msg) { /* Compile pattern */ if(strstr(pattern,"%{") == NULL) { - regex = msc_pregcomp_ex(rule->ruleset->mp, pattern, PCRE_DOTALL | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); +#ifndef WITH_PCRE + int options = PCRE2_DOTALL | PCRE2_DOLLAR_ENDONLY; +#else + int options = PCRE_DOTALL | PCRE_DOLLAR_ENDONLY; +#endif + regex = msc_pregcomp_ex(rule->ruleset->mp, pattern, options, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); if (regex == NULL) { *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s", erroffset, errptr); @@ -938,7 +1021,11 @@ static int msre_op_rx_param_init(msre_rule *rule, char **error_msg) { #ifdef WITH_PCRE_STUDY #ifdef WITH_PCRE_JIT + #ifndef WITH_PCRE + msc_op_set_jitrc(regex, &rc, &jit); + #else rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit); + #endif if ((rc != 0) || (jit != 1)) { *error_msg = apr_psprintf(rule->ruleset->mp, "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - " @@ -963,11 +1050,21 @@ static int msre_op_rx_param_init(msre_rule *rule, char **error_msg) { } static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(rule->actionset != NULL); + assert(var != NULL); + assert(error_msg != NULL); msc_regex_t *regex = (msc_regex_t *)rule->op_param_data; msc_string *re_pattern = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + if (!re_pattern) { + msr_log(msr, 1, "rx: Memory allocation error"); + return -1; + } const char *target; const char *errptr = NULL; int erroffset; + int options = 0; unsigned int target_length; char *my_error_msg = NULL; int ovector[33]; @@ -976,16 +1073,14 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c int matched = 0; int rc; char *qspos = NULL; - const char *parm = NULL, *pattern = NULL; + const char *parm = NULL; msc_parm *mparm = NULL; #ifdef WITH_PCRE_STUDY #ifdef WITH_PCRE_JIT - int jit; + int jit = 0; #endif #endif - - if (error_msg == NULL) return -1; *error_msg = NULL; if (regex == NULL) { @@ -1004,14 +1099,19 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c expand_macros(msr, re_pattern, rule, msr->mp); - pattern = log_escape_re(msr->mp, re_pattern->value); if (msr->txcfg->debuglog_level >= 6) { - msr_log(msr, 6, "Escaping pattern [%s]",pattern); + char *pattern = log_escape_re(msr->mp, re_pattern->value); + msr_log(msr, 6, "Expanded-macro pattern [%s]",pattern); } - regex = msc_pregcomp_ex(rule->ruleset->mp, pattern, PCRE_DOTALL | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); +#ifndef WITH_PCRE + options = PCRE2_DOTALL | PCRE2_DOLLAR_ENDONLY; +#else + options = PCRE_DOTALL | PCRE_DOLLAR_ENDONLY; +#endif + regex = msc_pregcomp_ex(msr->mp, re_pattern->value, options, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); if (regex == NULL) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s", + *error_msg = apr_psprintf(msr->mp, "Error compiling pattern (offset %d): %s", erroffset, errptr); return 0; } @@ -1019,9 +1119,13 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c #ifdef WITH_PCRE_STUDY #ifdef WITH_PCRE_JIT if (msr->txcfg->debuglog_level >= 4) { + #ifndef WITH_PCRE + msc_op_set_jitrc(regex, &rc, &jit); + #else rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit); + #endif if ((rc != 0) || (jit != 1)) { - *error_msg = apr_psprintf(rule->ruleset->mp, + *error_msg = apr_psprintf(msr->mp, "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - " "Execution error - " "Does not support JIT (%d)", @@ -1051,20 +1155,27 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c target_length = var->value_len; } - /* Are we supposed to capture subexpressions? */ - capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; - matched_bytes = apr_table_get(rule->actionset->actions, "sanitizeMatchedBytes") ? 1 : 0; - if(!matched_bytes) - matched_bytes = apr_table_get(rule->actionset->actions, "sanitiseMatchedBytes") ? 1 : 0; + if (rule->actionset->actions) { + /* Are we supposed to capture subexpressions? */ + capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; + matched_bytes = apr_table_get(rule->actionset->actions, "sanitizeMatchedBytes") ? 1 : 0; + if (!matched_bytes) + matched_bytes = apr_table_get(rule->actionset->actions, "sanitiseMatchedBytes") ? 1 : 0; - matched = apr_table_get(rule->actionset->actions, "sanitizeMatched") ? 1 : 0; - if(!matched) - matched = apr_table_get(rule->actionset->actions, "sanitiseMatched") ? 1 : 0; + matched = apr_table_get(rule->actionset->actions, "sanitizeMatched") ? 1 : 0; + if (!matched) + matched = apr_table_get(rule->actionset->actions, "sanitiseMatched") ? 1 : 0; + } + else capture = 0; /* Show when the regex captures but "capture" is not set */ if (msr->txcfg->debuglog_level >= 6) { int capcount = 0; +#ifndef WITH_PCRE + rc = msc_fullinfo(regex, PCRE2_INFO_CAPTURECOUNT, &capcount); +#else rc = msc_fullinfo(regex, PCRE_INFO_CAPTURECOUNT, &capcount); +#endif if (msr->txcfg->debuglog_level >= 6) { if ((capture == 0) && (capcount > 0)) { msr_log(msr, 6, "Ignoring regex captures since \"capture\" action is not enabled."); @@ -1076,7 +1187,11 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c * and no memory has to be allocated for any backreferences. */ rc = msc_regexec_capture(regex, target, target_length, ovector, 30, &my_error_msg); +#ifndef WITH_PCRE + if ((rc == PCRE2_ERROR_MATCHLIMIT) || (rc == PCRE2_ERROR_RECURSIONLIMIT)) { +#else if ((rc == PCRE_ERROR_MATCHLIMIT) || (rc == PCRE_ERROR_RECURSIONLIMIT)) { +#endif msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); if (s == NULL) return -1; @@ -1167,7 +1282,11 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c } } +#ifndef WITH_PCRE + if (rc != PCRE2_ERROR_NOMATCH) { /* Match. */ +#else if (rc != PCRE_ERROR_NOMATCH) { /* Match. */ +#endif /* We no longer escape the pattern here as it is done when logging */ char *pattern = apr_pstrdup(msr->mp, log_escape(msr->mp, regex->pattern ? regex->pattern : "")); @@ -1190,6 +1309,9 @@ static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c /* pm */ static int msre_op_pm_param_init(msre_rule *rule, char **error_msg) { + assert(rule != NULL); + assert(rule->ruleset != NULL); + assert(error_msg != NULL); ACMP *p; const char *phrase; const char *next; @@ -1228,6 +1350,9 @@ static int msre_op_pm_param_init(msre_rule *rule, char **error_msg) { /* pmFromFile */ static int msre_op_pmFromFile_param_init(msre_rule *rule, char **error_msg) { + assert(rule != NULL); + assert(rule->ruleset != NULL); + assert(error_msg != NULL); char errstr[1024]; char buf[HUGE_STRING_LEN + 1]; char *fn = NULL; @@ -1387,6 +1512,11 @@ static int msre_op_pmFromFile_param_init(msre_rule *rule, char **error_msg) { } static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(rule->actionset != NULL); + assert(var != NULL); + assert(error_msg != NULL); const char *match = NULL; apr_status_t rc = 0; int capture; @@ -1396,7 +1526,9 @@ static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c if ((var->value == NULL) || (var->value_len == 0)) return 0; /* Are we supposed to capture subexpressions? */ - capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; + if (rule->actionset->actions) + capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; + else capture = 0; if (rule->op_param_data == NULL) { @@ -1468,18 +1600,17 @@ static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, c * \retval url On Success */ static const char *gsb_replace_tpath(apr_pool_t *pool, const char *domain, int len) { - + assert(domain != NULL); char *pos = NULL, *data = NULL; char *url = NULL; int match = 0; url = apr_palloc(pool, len + 1); + if (!url) return NULL; data = apr_palloc(pool, len + 1); + if (!data) return NULL; - memset(data, 0, len+1); - memset(url, 0, len+1); - - memcpy(url, domain, len); + url[len] = '\0'; while(( pos = strstr(url , "/./" )) != NULL) { match = 1; @@ -1490,8 +1621,7 @@ static const char *gsb_replace_tpath(apr_pool_t *pool, const char *domain, int l strncpy(url , data, len); } - if(match == 0) - return domain; + if (match == 0) return domain; return url; } @@ -1506,7 +1636,7 @@ static const char *gsb_replace_tpath(apr_pool_t *pool, const char *domain, int l * \retval reduced On Success */ static const char *gsb_reduce_char(apr_pool_t *pool, const char *domain) { - + assert(domain != NULL); char *ptr = apr_pstrdup(pool, domain); char *data = NULL; char *reduced = NULL; @@ -1573,14 +1703,15 @@ static const char *gsb_reduce_char(apr_pool_t *pool, const char *domain) { * \retval 0 On No Match */ static int verify_gsb(gsb_db *gsb, modsec_rec *msr, const char *match, unsigned int match_length) { + assert(gsb != NULL); + assert(msr != NULL); + assert(match != NULL); apr_md5_ctx_t ctx; apr_status_t rc; unsigned char digest[APR_MD5_DIGESTSIZE]; const char *hash = NULL; const char *search = NULL; - memset(digest, 0, sizeof(digest)); - apr_md5_init(&ctx); if ((rc = apr_md5_update(&ctx, match, match_length)) != APR_SUCCESS) @@ -1588,7 +1719,7 @@ static int verify_gsb(gsb_db *gsb, modsec_rec *msr, const char *match, unsigned apr_md5_final(digest, &ctx); - hash = apr_psprintf(msr->mp, "%s", bytes2hex(msr->mp, digest, 16)); + hash = apr_psprintf(msr->mp, "%s", bytes2hex(msr->mp, digest, APR_MD5_DIGESTSIZE)); if ((hash != NULL) && (gsb->gsb_table != NULL)) { search = apr_hash_get(gsb->gsb_table, hash, APR_HASH_KEY_STRING); @@ -1610,15 +1741,23 @@ static int verify_gsb(gsb_db *gsb, modsec_rec *msr, const char *match, unsigned * \retval 0 On Fail */ static int msre_op_gsbLookup_param_init(msre_rule *rule, char **error_msg) { + assert(rule != NULL); + assert(error_msg != NULL); const char *errptr = NULL; int erroffset; + int options = 0; msc_regex_t *regex; if (error_msg == NULL) return -1; *error_msg = NULL; /* Compile rule->op_param */ - regex = msc_pregcomp_ex(rule->ruleset->mp, rule->op_param, PCRE_DOTALL | PCRE_MULTILINE, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); +#ifndef WITH_PCRE + options = PCRE2_DOTALL | PCRE2_MULTILINE; +#else + options = PCRE_DOTALL | PCRE_MULTILINE; +#endif + regex = msc_pregcomp_ex(rule->ruleset->mp, rule->op_param, options, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); if (regex == NULL) { *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s", @@ -1644,10 +1783,15 @@ static int msre_op_gsbLookup_param_init(msre_rule *rule, char **error_msg) { * \retval 0 On No Match */ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); msc_regex_t *regex = (msc_regex_t *)rule->op_param_data; char *my_error_msg = NULL; int ovector[33]; unsigned int offset = 0; + int options = 0; gsb_db *gsb = msr->txcfg->gsb; const char *match = NULL; unsigned int match_length; @@ -1675,7 +1819,7 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var return 0; } - data = apr_pcalloc(rule->ruleset->mp, var->value_len+1); + data = apr_pcalloc(msr->mp, var->value_len+1); if(data == NULL) { *error_msg = "Internal Error: cannot allocate memory for data."; @@ -1686,22 +1830,27 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var memcpy(data,var->value,var->value_len); - while (offset < size && (rv = msc_regexec_ex(regex, data, size, offset, PCRE_NOTEMPTY, ovector, 30, &my_error_msg)) >= 0) +#ifndef WITH_PCRE + options = PCRE2_NOTEMPTY; +#else + options = PCRE_NOTEMPTY; +#endif + while (offset < size && (rv = msc_regexec_ex(regex, data, size, offset, options, ovector, 30, &my_error_msg)) >= 0) { for(i = 0; i < rv; ++i) { - match = apr_psprintf(rule->ruleset->mp, "%.*s", ovector[2*i+1] - ovector[2*i], data + ovector[2*i]); + match = apr_psprintf(msr->mp, "%.*s", ovector[2*i+1] - ovector[2*i], data + ovector[2*i]); if (match == NULL) { *error_msg = "Internal Error: cannot allocate memory for match."; return -1; } - match = remove_escape(rule->ruleset->mp, match, strlen(match)); + match = remove_escape(msr->mp, match, strlen(match)); - match = gsb_replace_tpath(rule->ruleset->mp, match, strlen(match)); + match = gsb_replace_tpath(msr->mp, match, strlen(match)); - match = gsb_reduce_char(rule->ruleset->mp, match); + match = gsb_reduce_char(msr->mp, match); match_length = strlen(match); @@ -1723,7 +1872,7 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var log_escape_nq(msr->mp, match)); } - str = apr_pstrdup(rule->ruleset->mp,match); + str = apr_pstrdup(msr->mp,match); base = apr_strtok(str,"/",&savedptr); if(base != NULL) @@ -1735,7 +1884,7 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var /* append / in the end of full url */ if ((match[match_length -1] != '/') && (strchr(match,'?') == NULL)) { - canon = apr_psprintf(rule->ruleset->mp, "%s/", match); + canon = apr_psprintf(msr->mp, "%s/", match); if (canon != NULL) { canon_length = strlen(canon); @@ -1748,7 +1897,7 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var log_escape_nq(msr->mp, canon)); } - str = apr_pstrdup(rule->ruleset->mp,match); + str = apr_pstrdup(msr->mp,match); base = apr_strtok(str,"/",&savedptr); if(base != NULL) @@ -1761,7 +1910,7 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var /* Parsing full url */ - domain = apr_pstrdup(rule->ruleset->mp, match); + domain = apr_pstrdup(msr->mp, match); domain_len = strlen(domain); @@ -1776,7 +1925,7 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var dot = strchr(domain,'.'); if(dot != NULL) { - canon = apr_pstrdup(rule->ruleset->mp, domain); + canon = apr_pstrdup(msr->mp, domain); ret = verify_gsb(gsb, msr, canon, strlen(canon)); @@ -1787,7 +1936,7 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var log_escape_nq(msr->mp, canon)); } - str = apr_pstrdup(rule->ruleset->mp,match); + str = apr_pstrdup(msr->mp,match); base = apr_strtok(str,"/",&savedptr); if(base != NULL) @@ -1809,7 +1958,7 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var log_escape_nq(msr->mp, base)); } - str = apr_pstrdup(rule->ruleset->mp,match); + str = apr_pstrdup(msr->mp,match); base = apr_strtok(str,"/",&savedptr); if(base != NULL) @@ -1820,13 +1969,13 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var } - url = apr_palloc(rule->ruleset->mp, strlen(canon)); + url = apr_palloc(msr->mp, strlen(canon)); count_slash = 0; while(*canon != '\0') { switch (*canon) { case '/': - ptr = apr_psprintf(rule->ruleset->mp,"%s/",url); + ptr = apr_psprintf(msr->mp,"%s/",url); ret = verify_gsb(gsb, msr, ptr, strlen(ptr)); if(ret > 0) { set_match_to_tx(msr, capture, ptr, 0); @@ -1835,7 +1984,7 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var log_escape_nq(msr->mp, ptr)); } - str = apr_pstrdup(rule->ruleset->mp,match); + str = apr_pstrdup(msr->mp,match); base = apr_strtok(str,"/",&savedptr); if(base != NULL) @@ -1862,7 +2011,7 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var } - str = apr_pstrdup(rule->ruleset->mp, match); + str = apr_pstrdup(msr->mp, match); while (*str != '\0') { @@ -1887,7 +2036,7 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var dot = strchr(domain,'.'); if(dot != NULL) { - canon = apr_pstrdup(rule->ruleset->mp, domain); + canon = apr_pstrdup(msr->mp, domain); ret = verify_gsb(gsb, msr, canon, strlen(canon)); @@ -1897,7 +2046,7 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var *error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.", log_escape_nq(msr->mp, canon)); } - str = apr_pstrdup(rule->ruleset->mp,match); + str = apr_pstrdup(msr->mp,match); base = apr_strtok(str,"/",&savedptr); if(base != NULL) @@ -1917,7 +2066,7 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var *error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.", log_escape_nq(msr->mp, base)); } - str = apr_pstrdup(rule->ruleset->mp,match); + str = apr_pstrdup(msr->mp,match); base = apr_strtok(str,"/",&savedptr); if(base != NULL) @@ -1927,13 +2076,13 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var } - url = apr_palloc(rule->ruleset->mp, strlen(canon)); + url = apr_palloc(msr->mp, strlen(canon)); count_slash = 0; while(*canon != '\0') { switch (*canon) { case '/': - ptr = apr_psprintf(rule->ruleset->mp,"%s/",url); + ptr = apr_psprintf(msr->mp,"%s/",url); ret = verify_gsb(gsb, msr, ptr, strlen(ptr)); if(ret > 0) { set_match_to_tx(msr, capture, ptr, 0); @@ -1941,7 +2090,7 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var *error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.", log_escape_nq(msr->mp, ptr)); } - str = apr_pstrdup(rule->ruleset->mp,match); + str = apr_pstrdup(msr->mp,match); base = apr_strtok(str,"/",&savedptr); if(base != NULL) @@ -1977,6 +2126,10 @@ static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var /* within */ static int msre_op_within_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); const char *match = NULL; const char *target; @@ -1984,6 +2137,8 @@ static int msre_op_within_execute(modsec_rec *msr, msre_rule *rule, msre_var *va unsigned int target_length = 0; unsigned int i, i_max; + *error_msg = NULL; + str->value = (char *)rule->op_param; if (str->value == NULL) { @@ -1993,9 +2148,6 @@ static int msre_op_within_execute(modsec_rec *msr, msre_rule *rule, msre_var *va str->value_len = strlen(str->value); - if (error_msg == NULL) return -1; - *error_msg = NULL; - expand_macros(msr, str, rule, msr->mp); match = (const char *)str->value; @@ -2047,15 +2199,22 @@ static int msre_op_within_execute(modsec_rec *msr, msre_rule *rule, msre_var *va /* contains */ static int msre_op_contains_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); const char *match = NULL; const char *target; unsigned int match_length; unsigned int target_length = 0; unsigned int i, i_max; + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + if (str == NULL) { + *error_msg = "Internal Error: cannot allocate memory."; + return -1; + } str->value = (char *)rule->op_param; - if (str->value == NULL) { *error_msg = "Internal Error: match string is null."; return -1; @@ -2126,7 +2285,12 @@ static int msre_op_contains_execute(modsec_rec *msr, msre_rule *rule, msre_var * */ static int msre_op_detectSQLi_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - + assert(msr != NULL); + assert(rule != NULL); + assert(rule->actionset != NULL); + assert(rule->actionset->actions != NULL); + assert(var != NULL); + assert(error_msg != NULL); char fingerprint[8]; int issqli; int capture; @@ -2158,12 +2322,20 @@ static int msre_op_detectSQLi_execute(modsec_rec *msr, msre_rule *rule, msre_var */ static int msre_op_detectXSS_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - + assert(msr != NULL); + assert(rule != NULL); + assert(rule->actionset != NULL); + assert(rule->actionset->actions != NULL); + assert(var != NULL); + assert(error_msg != NULL); + int capture; int is_xss; is_xss = libinjection_xss(var->value, var->value_len); + capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; if (is_xss) { + set_match_to_tx(msr, capture, var->value, 0); *error_msg = apr_psprintf(msr->mp, "detected XSS using libinjection."); if (msr->txcfg->debuglog_level >= 9) { @@ -2182,16 +2354,23 @@ static int msre_op_detectXSS_execute(modsec_rec *msr, msre_rule *rule, msre_var /* containsWord */ static int msre_op_containsWord_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); const char *match = NULL; const char *target; unsigned int match_length; unsigned int target_length = 0; unsigned int i, i_max; int rc = 0; + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + if (str == NULL) { + *error_msg = "Internal Error: cannot allocate memory."; + return -1; + } str->value = (char *)rule->op_param; - if (str->value == NULL) { *error_msg = "Internal Error: match string is null."; return -1; @@ -2199,7 +2378,6 @@ static int msre_op_containsWord_execute(modsec_rec *msr, msre_rule *rule, msre_v str->value_len = strlen(str->value); - if (error_msg == NULL) return -1; *error_msg = NULL; expand_macros(msr, str, rule, msr->mp); @@ -2277,14 +2455,21 @@ static int msre_op_containsWord_execute(modsec_rec *msr, msre_rule *rule, msre_v /* streq */ static int msre_op_streq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + if (!str) { + msr_log(msr, 1, "streq: Memory allocation error"); + return -1; + } const char *match = NULL; const char *target; unsigned int match_length; unsigned int target_length; str->value = (char *)rule->op_param; - if (str->value == NULL) { *error_msg = "Internal Error: match string is null."; return -1; @@ -2292,7 +2477,6 @@ static int msre_op_streq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var str->value_len = strlen(str->value); - if (error_msg == NULL) return -1; *error_msg = NULL; expand_macros(msr, str, rule, msr->mp); @@ -2333,14 +2517,21 @@ static int msre_op_streq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var /* beginsWith */ static int msre_op_beginsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); const char *match = NULL; const char *target; unsigned int match_length; unsigned int target_length; + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + if (str == NULL) { + *error_msg = "Internal Error: cannot allocate memory."; + return -1; + } str->value = (char *)rule->op_param; - if (str->value == NULL) { *error_msg = "Internal Error: match string is null."; return -1; @@ -2396,14 +2587,26 @@ static int msre_op_beginsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var /* endsWith */ static int msre_op_endsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); + // Normally useless code, left to be safe for the moment + if (error_msg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, rule->ruleset->mp, "msre_op_endsWith_execute: error_msg is NULL"); + return -1; + } const char *match = NULL; const char *target; unsigned int match_length; unsigned int target_length; + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + if (str == NULL) { + *error_msg = "Internal Error: cannot allocate memory."; + return -1; + } str->value = (char *)rule->op_param; - if (str->value == NULL) { *error_msg = "Internal Error: match string is null."; return -1; @@ -2411,7 +2614,6 @@ static int msre_op_endsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var * str->value_len = strlen(str->value); - if (error_msg == NULL) return -1; *error_msg = NULL; expand_macros(msr, str, rule, msr->mp); @@ -2459,12 +2661,19 @@ static int msre_op_endsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var * /* strmatch */ static int msre_op_strmatch_param_init(msre_rule *rule, char **error_msg) { + assert(rule != NULL); + assert(rule->ruleset != NULL); + assert(error_msg != NULL); + // Normally useless code, left to be safe for the moment + if (error_msg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, rule->ruleset->mp, "msre_op_strmatch_param_init: error_msg is NULL"); + return -1; + } const apr_strmatch_pattern *compiled_pattern; char *processed = NULL; const char *pattern = rule->op_param; unsigned short int op_len; - if (error_msg == NULL) return -1; *error_msg = NULL; op_len = strlen(pattern); @@ -2488,12 +2697,20 @@ static int msre_op_strmatch_param_init(msre_rule *rule, char **error_msg) { } static int msre_op_strmatch_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); + // Normally useless code, left to be safe for the moment + if (error_msg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, rule->ruleset->mp, "msre_op_strmatch_execute: error_msg is NULL"); + return -1; + } apr_strmatch_pattern *compiled_pattern = (apr_strmatch_pattern *)rule->op_param_data; const char *target; unsigned int target_length; const char *rc; - if (error_msg == NULL) return -1; *error_msg = NULL; if (compiled_pattern == NULL) { @@ -2536,6 +2753,10 @@ static int msre_op_validateDTD_init(msre_rule *rule, char **error_msg) { static int msre_op_validateDTD_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); xmlValidCtxtPtr cvp; xmlDtdPtr dtd; @@ -2606,6 +2827,10 @@ static int msre_op_validateSchema_init(msre_rule *rule, char **error_msg) { static int msre_op_validateSchema_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); xmlSchemaParserCtxtPtr parserCtx; xmlSchemaValidCtxtPtr validCtx; xmlSchemaPtr schema; @@ -2673,6 +2898,7 @@ static int msre_op_validateSchema_execute(modsec_rec *msr, msre_rule *rule, msre xmlSchemaFree(schema); xmlSchemaFreeValidCtxt(validCtx); + xmlSchemaFreeParserCtxt(parserCtx); return 0; } @@ -2683,6 +2909,7 @@ static int msre_op_validateSchema_execute(modsec_rec *msr, msre_rule *rule, msre * Luhn Mod-10 Method (ISO 2894/ANSI 4.13) */ static int luhn_verify(const char *ccnumber, int len) { + assert(ccnumber != NULL); int sum[2] = { 0, 0 }; int odd = 0; int digits = 0; @@ -2716,15 +2943,24 @@ static int luhn_verify(const char *ccnumber, int len) { } static int msre_op_verifyCC_init(msre_rule *rule, char **error_msg) { + assert(rule != NULL); + assert(rule->ruleset != NULL); + assert(error_msg != NULL); const char *errptr = NULL; int erroffset; + int options = 0; msc_regex_t *regex; if (error_msg == NULL) return -1; *error_msg = NULL; +#ifndef WITH_PCRE + options = PCRE2_DOTALL | PCRE2_MULTILINE; +#else + options = PCRE_DOTALL | PCRE_MULTILINE; +#endif /* Compile rule->op_param */ - regex = msc_pregcomp_ex(rule->ruleset->mp, rule->op_param, PCRE_DOTALL | PCRE_MULTILINE, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); + regex = msc_pregcomp_ex(rule->ruleset->mp, rule->op_param, options, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); if (regex == NULL) { *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s", erroffset, errptr); @@ -2737,6 +2973,11 @@ static int msre_op_verifyCC_init(msre_rule *rule, char **error_msg) { } static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(rule->actionset != NULL); + assert(var != NULL); + assert(error_msg != NULL); msc_regex_t *regex = (msc_regex_t *)rule->op_param_data; const char *target; unsigned int target_length; @@ -2745,17 +2986,17 @@ static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var * int rc; int is_cc = 0; int offset; + int options = 0; int matched_bytes = 0; char *qspos = NULL; const char *parm = NULL; msc_parm *mparm = NULL; #ifdef WITH_PCRE_STUDY #ifdef WITH_PCRE_JIT - int jit; + int jit = 0; #endif #endif - if (error_msg == NULL) return -1; *error_msg = NULL; if (regex == NULL) { @@ -2763,14 +3004,20 @@ static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var * return -1; } + assert(rule->actionset != NULL); + memset(ovector, 0, sizeof(ovector)); #ifdef WITH_PCRE_STUDY #ifdef WITH_PCRE_JIT if (msr->txcfg->debuglog_level >= 4) { + #ifndef WITH_PCRE + msc_op_set_jitrc(regex, &rc, &jit); + #else rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit); + #endif if ((rc != 0) || (jit != 1)) { - *error_msg = apr_psprintf(rule->ruleset->mp, + *error_msg = apr_psprintf(msr->mp, "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - " "Execution error - " "Does not support JIT (%d)", @@ -2804,10 +3051,19 @@ static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var * } } - rc = msc_regexec_ex(regex, target, target_length, offset, PCRE_NOTEMPTY, ovector, 30, &my_error_msg); +#ifndef WITH_PCRE + options = PCRE2_NOTEMPTY; +#else + options = PCRE_NOTEMPTY; +#endif + rc = msc_regexec_ex(regex, target, target_length, offset, options, ovector, 30, &my_error_msg); /* If there was no match, then we are done. */ +#ifndef WITH_PCRE + if (rc == PCRE2_ERROR_NOMATCH) { +#else if (rc == PCRE_ERROR_NOMATCH) { +#endif break; } @@ -2844,7 +3100,6 @@ static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var * if(!matched_bytes) matched_bytes = apr_table_get(rule->actionset->actions, "sanitiseMatchedBytes") ? 1 : 0; - if (apr_table_get(rule->actionset->actions, "capture")) { for(; i < rc; i++) { msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); @@ -2924,7 +3179,7 @@ static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var * * \retval 1 On Valid CPF */ static int cpf_verify(const char *cpfnumber, int len) { - + assert(cpfnumber != NULL); int factor, part_1, part_2, var_len = len; unsigned int sum = 0, i = 0, cpf_len = 11, c; int cpf[11]; @@ -3012,15 +3267,27 @@ static int cpf_verify(const char *cpfnumber, int len) { * \retval 1 On Success */ static int msre_op_verifyCPF_init(msre_rule *rule, char **error_msg) { + assert(rule != NULL); + assert(rule->ruleset != NULL); + assert(error_msg != NULL); + if (error_msg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, rule->ruleset->mp, "msre_op_verifyCPF_init: error_msg is NULL"); + return -1; + } const char *errptr = NULL; int erroffset; + int options = 0; msc_regex_t *regex; - if (error_msg == NULL) return -1; *error_msg = NULL; +#ifndef WITH_PCRE + options = PCRE2_DOTALL | PCRE2_MULTILINE; +#else + options = PCRE_DOTALL | PCRE_MULTILINE; +#endif /* Compile rule->op_param */ - regex = msc_pregcomp_ex(rule->ruleset->mp, rule->op_param, PCRE_DOTALL | PCRE_MULTILINE, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); + regex = msc_pregcomp_ex(rule->ruleset->mp, rule->op_param, options, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); if (regex == NULL) { *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s", erroffset, errptr); @@ -3045,6 +3312,11 @@ static int msre_op_verifyCPF_init(msre_rule *rule, char **error_msg) { * \retval 0 On No Match */ static int msre_op_verifyCPF_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(rule->actionset != NULL); + assert(var != NULL); + assert(error_msg != NULL); msc_regex_t *regex = (msc_regex_t *)rule->op_param_data; const char *target; unsigned int target_length; @@ -3053,18 +3325,17 @@ static int msre_op_verifyCPF_execute(modsec_rec *msr, msre_rule *rule, msre_var int rc; int is_cpf = 0; int offset; + int options = 0; int matched_bytes = 0; char *qspos = NULL; const char *parm = NULL; msc_parm *mparm = NULL; #ifdef WITH_PCRE_STUDY #ifdef WITH_PCRE_JIT - int jit; + int jit = 0; #endif #endif - - if (error_msg == NULL) return -1; *error_msg = NULL; if (regex == NULL) { @@ -3072,14 +3343,20 @@ static int msre_op_verifyCPF_execute(modsec_rec *msr, msre_rule *rule, msre_var return -1; } + assert(rule->actionset != NULL); + memset(ovector, 0, sizeof(ovector)); #ifdef WITH_PCRE_STUDY #ifdef WITH_PCRE_JIT if (msr->txcfg->debuglog_level >= 4) { + #ifndef WITH_PCRE + msc_op_set_jitrc(regex, &rc, &jit); + #else rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit); + #endif if ((rc != 0) || (jit != 1)) { - *error_msg = apr_psprintf(rule->ruleset->mp, + *error_msg = apr_psprintf(msr->mp, "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - " "Execution error - " "Does not support JIT (%d)", @@ -3112,10 +3389,19 @@ static int msre_op_verifyCPF_execute(modsec_rec *msr, msre_rule *rule, msre_var } } - rc = msc_regexec_ex(regex, target, target_length, offset, PCRE_NOTEMPTY, ovector, 30, &my_error_msg); +#ifndef WITH_PCRE + options = PCRE2_NOTEMPTY; +#else + options = PCRE_NOTEMPTY; +#endif + rc = msc_regexec_ex(regex, target, target_length, offset, options, ovector, 30, &my_error_msg); /* If there was no match, then we are done. */ +#ifndef WITH_PCRE + if (rc == PCRE2_ERROR_NOMATCH) { +#else if (rc == PCRE_ERROR_NOMATCH) { +#endif break; } @@ -3126,11 +3412,11 @@ static int msre_op_verifyCPF_execute(modsec_rec *msr, msre_rule *rule, msre_var /* Verify a match. */ if (rc > 0) { - const char *match = target + ovector[0]; + const char* match = target + ovector[0]; int length = ovector[1] - ovector[0]; int i = 0; - offset = ovector[2*i]; + offset = ovector[2 * i]; /* Check CPF using the match string */ is_cpf = cpf_verify(match, length); @@ -3149,12 +3435,12 @@ static int msre_op_verifyCPF_execute(modsec_rec *msr, msre_rule *rule, msre_var */ matched_bytes = apr_table_get(rule->actionset->actions, "sanitizeMatchedBytes") ? 1 : 0; - if(!matched_bytes) + if (!matched_bytes) matched_bytes = apr_table_get(rule->actionset->actions, "sanitiseMatchedBytes") ? 1 : 0; if (apr_table_get(rule->actionset->actions, "capture")) { - for(; i < rc; i++) { - msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + for (; i < rc; i++) { + msc_string* s = (msc_string*)apr_pcalloc(msr->mp, sizeof(msc_string)); if (s == NULL) return -1; s->name = apr_psprintf(msr->mp, "%d", i); if (s->name == NULL) return -1; @@ -3163,33 +3449,34 @@ static int msre_op_verifyCPF_execute(modsec_rec *msr, msre_rule *rule, msre_var if (s->value == NULL) return -1; s->value_len = length; - apr_table_setn(msr->tx_vars, s->name, (void *)s); + apr_table_setn(msr->tx_vars, s->name, (void*)s); if (msr->txcfg->debuglog_level >= 9) { msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i, log_escape_nq_ex(msr->mp, s->value, s->value_len)); } - if((matched_bytes == 1) && (var != NULL) && (var->name != NULL)) { + if ((matched_bytes == 1) && (var != NULL) && (var->name != NULL)) { qspos = apr_psprintf(msr->mp, "%s", var->name); parm = strstr(qspos, ":"); - if (parm != NULL) { + if (parm != NULL) { parm++; mparm = apr_palloc(msr->mp, sizeof(msc_parm)); if (mparm == NULL) continue; - mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len); + mparm->value = apr_pstrmemdup(msr->mp, s->value, s->value_len); mparm->pad_1 = rule->actionset->arg_min; mparm->pad_2 = rule->actionset->arg_max; - apr_table_addn(msr->pattern_to_sanitize, parm, (void *)mparm); - } else { + apr_table_addn(msr->pattern_to_sanitize, parm, (void*)mparm); + } + else { mparm = apr_palloc(msr->mp, sizeof(msc_parm)); if (mparm == NULL) continue; - mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len); - apr_table_addn(msr->pattern_to_sanitize, qspos, (void *)mparm); + mparm->value = apr_pstrmemdup(msr->mp, s->value, s->value_len); + apr_table_addn(msr->pattern_to_sanitize, qspos, (void*)mparm); } } @@ -3197,7 +3484,7 @@ static int msre_op_verifyCPF_execute(modsec_rec *msr, msre_rule *rule, msre_var } /* Unset the remaining TX vars (from previous invocations). */ - for(; i <= 9; i++) { + for (; i <= 9; i++) { char buf[24]; apr_snprintf(buf, sizeof(buf), "%i", i); apr_table_unset(msr->tx_vars, buf); @@ -3232,6 +3519,8 @@ static int msre_op_verifyCPF_execute(modsec_rec *msr, msre_rule *rule, msre_var * \retval 1 On Valid SSN */ static int ssn_verify(modsec_rec *msr, const char *ssnumber, int len) { + assert(msr != NULL); + assert(ssnumber != NULL); int i; int num[9]; int digits = 0; @@ -3304,15 +3593,24 @@ static int ssn_verify(modsec_rec *msr, const char *ssnumber, int len) { * \retval 1 On Success */ static int msre_op_verifySSN_init(msre_rule *rule, char **error_msg) { + assert(rule != NULL); + assert(rule->ruleset != NULL); + assert(error_msg != NULL); const char *errptr = NULL; int erroffset; + int options = 0; msc_regex_t *regex; if (error_msg == NULL) return -1; *error_msg = NULL; +#ifndef WITH_PCRE + options = PCRE2_DOTALL | PCRE2_MULTILINE; +#else + options = PCRE_DOTALL | PCRE_MULTILINE; +#endif /* Compile rule->op_param */ - regex = msc_pregcomp_ex(rule->ruleset->mp, rule->op_param, PCRE_DOTALL | PCRE_MULTILINE, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); + regex = msc_pregcomp_ex(rule->ruleset->mp, rule->op_param, options, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion); if (regex == NULL) { *error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s", erroffset, errptr); @@ -3337,6 +3635,11 @@ static int msre_op_verifySSN_init(msre_rule *rule, char **error_msg) { * \retval 0 On No Match */ static int msre_op_verifySSN_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(rule->actionset != NULL); + assert(var != NULL); + assert(error_msg != NULL); msc_regex_t *regex = (msc_regex_t *)rule->op_param_data; const char *target; unsigned int target_length; @@ -3345,13 +3648,14 @@ static int msre_op_verifySSN_execute(modsec_rec *msr, msre_rule *rule, msre_var int rc; int is_ssn = 0; int offset; + int options = 0; int matched_bytes = 0; char *qspos = NULL; const char *parm = NULL; msc_parm *mparm = NULL; #ifdef WITH_PCRE_STUDY #ifdef WITH_PCRE_JIT - int jit; + int jit = 0; #endif #endif @@ -3364,14 +3668,20 @@ static int msre_op_verifySSN_execute(modsec_rec *msr, msre_rule *rule, msre_var return -1; } + assert(rule->actionset != NULL); + memset(ovector, 0, sizeof(ovector)); #ifdef WITH_PCRE_STUDY #ifdef WITH_PCRE_JIT if (msr->txcfg->debuglog_level >= 4) { + #ifndef WITH_PCRE + msc_op_set_jitrc(regex, &rc, &jit); + #else rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit); + #endif if ((rc != 0) || (jit != 1)) { - *error_msg = apr_psprintf(rule->ruleset->mp, + *error_msg = apr_psprintf(msr->mp, "Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - " "Execution error - " "Does not support JIT (%d)", @@ -3404,10 +3714,19 @@ static int msre_op_verifySSN_execute(modsec_rec *msr, msre_rule *rule, msre_var } } - rc = msc_regexec_ex(regex, target, target_length, offset, PCRE_NOTEMPTY, ovector, 30, &my_error_msg); +#ifndef WITH_PCRE + options = PCRE2_NOTEMPTY; +#else + options = PCRE_NOTEMPTY; +#endif + rc = msc_regexec_ex(regex, target, target_length, offset, options, ovector, 30, &my_error_msg); /* If there was no match, then we are done. */ +#ifndef WITH_PCRE + if (rc == PCRE2_ERROR_NOMATCH) { +#else if (rc == PCRE_ERROR_NOMATCH) { +#endif break; } @@ -3519,6 +3838,9 @@ static int msre_op_verifySSN_execute(modsec_rec *msr, msre_rule *rule, msre_var static int msre_op_geoLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(var != NULL); + assert(error_msg != NULL); geo_rec rec; geo_db *geo = msr->txcfg->geo; const char *geo_host = var->value; @@ -3645,6 +3967,11 @@ static int msre_op_geoLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var /* rbl */ static int msre_op_rbl_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(rule->actionset != NULL); + assert(var != NULL); + assert(error_msg != NULL); unsigned int h0, h1, h2, h3; unsigned int high8bits = 0; char *name_to_check = NULL; @@ -3653,10 +3980,11 @@ static int msre_op_rbl_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, apr_status_t rc; int capture = 0; - if (error_msg == NULL) return -1; *error_msg = NULL; - capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; + if (rule->actionset->actions) + capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0; + else capture = 0; /* ENH Add IPv6 support. */ @@ -3826,6 +4154,13 @@ static int msre_op_rbl_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, /* fuzzyHash */ static int msre_op_fuzzy_hash_init(msre_rule *rule, char **error_msg) { + assert(rule != NULL); + assert(rule->ruleset != NULL); + assert(error_msg != NULL); + if (error_msg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, rule->ruleset->mp, "msre_op_fuzzy_hash_init error_msg is NULL"); + return -1; + } #ifdef WITH_SSDEEP struct fuzzy_hash_param_data *param_data; struct fuzzy_hash_chunk *chunk, *t; @@ -3846,11 +4181,6 @@ static int msre_op_fuzzy_hash_init(msre_rule *rule, char **error_msg) threshold_str = data; #endif - if (error_msg == NULL) - { - return -1; - } - *error_msg = NULL; #ifdef WITH_SSDEEP @@ -3933,24 +4263,22 @@ static int msre_op_fuzzy_hash_init(msre_rule *rule, char **error_msg) static int msre_op_fuzzy_hash_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); #ifdef WITH_SSDEEP char result[FUZZY_MAX_RESULT]; struct fuzzy_hash_param_data *param = rule->op_param_data; struct fuzzy_hash_chunk *chunk = param->head; #endif - if (error_msg == NULL) - { - return -1; - } - *error_msg = NULL; #ifdef WITH_SSDEEP if (fuzzy_hash_buf(var->value, var->value_len, result)) { - *error_msg = apr_psprintf(rule->ruleset->mp, "Problems generating " \ + *error_msg = apr_psprintf(msr->mp, "Problems generating " \ "fuzzy hash."); return -1; @@ -3970,7 +4298,7 @@ static int msre_op_fuzzy_hash_execute(modsec_rec *msr, msre_rule *rule, chunk = chunk->next; } #else - *error_msg = apr_psprintf(rule->ruleset->mp, "ModSecurity was not " \ + *error_msg = apr_psprintf(msr->mp, "ModSecurity was not " \ "compiled with ssdeep support."); return -1; @@ -3984,9 +4312,15 @@ static int msre_op_fuzzy_hash_execute(modsec_rec *msr, msre_rule *rule, /* inspectFile */ static int msre_op_inspectFile_init(msre_rule *rule, char **error_msg) { + assert(rule != NULL); + assert(rule->ruleset != NULL); + assert(error_msg != NULL); + if (error_msg == NULL) { + ap_log_perror(APLOG_MARK, APLOG_EMERG, 0, rule->ruleset->mp, "msre_op_inspectFile_init: error_msg is NULL"); + return -1; + } char *filename = (char *)rule->op_param; - if (error_msg == NULL) return -1; *error_msg = NULL; if ((filename == NULL)||(is_empty_string(filename))) { @@ -4025,7 +4359,10 @@ static int msre_op_inspectFile_init(msre_rule *rule, char **error_msg) { static int msre_op_inspectFile_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { - if (error_msg == NULL) return -1; + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); *error_msg = NULL; if (rule->op_param_data == NULL) { @@ -4086,6 +4423,9 @@ static int msre_op_inspectFile_execute(modsec_rec *msr, msre_rule *rule, msre_va /* validateByteRange */ static int msre_op_validateByteRange_init(msre_rule *rule, char **error_msg) { + assert(rule != NULL); + assert(rule->ruleset != NULL); + assert(error_msg != NULL); char *p = NULL, *saveptr = NULL; char *table = NULL, *data = NULL; @@ -4149,10 +4489,13 @@ static int msre_op_validateByteRange_init(msre_rule *rule, char **error_msg) { static int msre_op_validateByteRange_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); char *table = rule->op_param_data; unsigned int i, count; - if (error_msg == NULL) return -1; *error_msg = NULL; if (table == NULL) { @@ -4223,6 +4566,9 @@ static int validate_url_encoding(const char *input, long int input_length) { static int msre_op_validateUrlEncoding_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(var != NULL); + assert(error_msg != NULL); int rc = validate_url_encoding(var->value, var->value_len); switch(rc) { case 1 : @@ -4310,7 +4656,7 @@ static int detect_utf8_character(const unsigned char *p_read, unsigned int lengt else { unicode_len = 4; /* compute character number */ - d = ((c & 0x07) << 18) | ((*(p_read + 1) & 0x3F) << 12) | ((*(p_read + 2) & 0x3F) < 6) | (*(p_read + 3) & 0x3F); + d = ((c & 0x07) << 18) | ((*(p_read + 1) & 0x3F) << 12) | ((*(p_read + 2) & 0x3F) << 6) | (*(p_read + 3) & 0x3F); } } /* any other first byte is invalid (RFC 3629) */ @@ -4343,6 +4689,9 @@ static int detect_utf8_character(const unsigned char *p_read, unsigned int lengt static int msre_op_validateUtf8Encoding_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(var != NULL); + assert(error_msg != NULL); unsigned int i, bytes_left; bytes_left = var->value_len; @@ -4400,6 +4749,10 @@ static int msre_op_validateUtf8Encoding_execute(modsec_rec *msr, msre_rule *rule static int msre_op_eq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); msc_string str; int left, right; char *target = NULL; @@ -4438,6 +4791,10 @@ static int msre_op_eq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, static int msre_op_gt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); msc_string str; int left, right; char *target = NULL; @@ -4481,16 +4838,14 @@ static int msre_op_gt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, static int msre_op_lt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); msc_string str; int left, right; char *target = NULL; - if ((var->value == NULL)||(rule->op_param == NULL)) { - /* NULL values do not match anything. */ - return 0; - } - - if (error_msg == NULL) return -1; *error_msg = NULL; if ((var->value == NULL)||(rule->op_param == NULL)) { @@ -4524,6 +4879,10 @@ static int msre_op_lt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, static int msre_op_ge_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); msc_string str; int left, right; char *target = NULL; @@ -4567,6 +4926,10 @@ static int msre_op_ge_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, static int msre_op_le_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + assert(msr != NULL); + assert(rule != NULL); + assert(var != NULL); + assert(error_msg != NULL); msc_string str; int left, right; char *target = NULL; @@ -4576,7 +4939,6 @@ static int msre_op_le_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, return 0; } - if (error_msg == NULL) return -1; *error_msg = NULL; if ((var->value == NULL)||(rule->op_param == NULL)) { diff --git a/apache2/re_variables.c b/apache2/re_variables.c index c69085feef..c4c4b0d895 100644 --- a/apache2/re_variables.c +++ b/apache2/re_variables.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* Copyright (c) 2004-2022 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 @@ -12,6 +12,7 @@ * directly using the email address security@modsecurity.org. */ +#include #include "http_core.h" #include "modsecurity.h" @@ -21,12 +22,18 @@ #include "libxml/xpathInternals.h" +#ifndef WITH_PCRE +#define PCRE_ERROR_NOMATCH PCRE2_ERROR_NOMATCH +#endif /** * Generates a variable from a string and a length. */ static int var_simple_generate_ex(msre_var *var, apr_table_t *vartab, apr_pool_t *mptmp, const char *value, int value_len) { + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); msre_var *rvar = NULL; if (value == NULL) return 0; @@ -54,6 +61,9 @@ static int var_simple_generate(msre_var *var, apr_table_t *vartab, apr_pool_t *m * care of the case when the parameter is a regular expression. */ static char *var_generic_list_validate(msre_ruleset *ruleset, msre_var *var) { + assert(ruleset != NULL); + assert(var != NULL); + /* It's OK if there's no parameter. */ if (var->param == NULL) return NULL; @@ -64,12 +74,18 @@ static char *var_generic_list_validate(msre_ruleset *ruleset, msre_var *var) { msc_regex_t *regex = NULL; const char *errptr = NULL; const char *pattern = NULL; + int options = 0; int erroffset; pattern = apr_pstrmemdup(ruleset->mp, var->param + 1, strlen(var->param + 1) - 1); if (pattern == NULL) return FATAL_ERROR; - regex = msc_pregcomp(ruleset->mp, pattern, PCRE_DOTALL | PCRE_CASELESS | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset); +#ifndef WITH_PCRE + options = PCRE2_DOTALL | PCRE2_CASELESS | PCRE2_DOLLAR_ENDONLY; +#else + options = PCRE_DOTALL | PCRE_CASELESS | PCRE_DOLLAR_ENDONLY; +#endif + regex = msc_pregcomp(ruleset->mp, pattern, options, &errptr, &erroffset); if (regex == NULL) { return apr_psprintf(ruleset->mp, "Error compiling pattern (offset %d): %s", erroffset, errptr); @@ -90,6 +106,10 @@ static char *var_generic_list_validate(msre_ruleset *ruleset, msre_var *var) { static int var_args_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; @@ -99,6 +119,7 @@ static int var_args_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { msc_arg *arg = (msc_arg *)te[i].val; + assert(arg != NULL); int match = 0; /* Figure out if we want to include this argument. */ @@ -107,8 +128,7 @@ static int var_args_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; /* Run the regex against the argument name. */ - if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, - arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, arg->name, arg->name_len, &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(arg->name, var->param) == 0) match = 1; } @@ -135,6 +155,10 @@ static int var_args_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, static int var_args_combined_size_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; unsigned int combined_size = 0; @@ -145,6 +169,7 @@ static int var_args_combined_size_generate(modsec_rec *msr, msre_var *var, msre_ te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { msc_arg *arg = (msc_arg *)te[i].val; + assert(arg != NULL); combined_size += arg->name_len; combined_size += arg->value_len; } @@ -162,6 +187,10 @@ static int var_args_combined_size_generate(modsec_rec *msr, msre_var *var, msre_ static int var_args_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; @@ -170,6 +199,7 @@ static int var_args_names_generate(modsec_rec *msr, msre_var *var, msre_rule *ru te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { msc_arg *arg = (msc_arg *)te[i].val; + assert(arg != NULL); int match = 0; /* Figure out if we want to include this variable. */ @@ -177,8 +207,7 @@ static int var_args_names_generate(modsec_rec *msr, msre_var *var, msre_rule *ru else { if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, - arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, arg->name, arg->name_len, &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(arg->name, var->param) == 0) match = 1; } @@ -205,6 +234,10 @@ static int var_args_names_generate(modsec_rec *msr, msre_var *var, msre_rule *ru static int var_args_get_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; @@ -214,6 +247,7 @@ static int var_args_get_generate(modsec_rec *msr, msre_var *var, msre_rule *rule te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { msc_arg *arg = (msc_arg *)te[i].val; + assert(arg != NULL); int match = 0; /* Only QUERY_STRING arguments */ @@ -225,8 +259,7 @@ static int var_args_get_generate(modsec_rec *msr, msre_var *var, msre_rule *rule if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; /* Run the regex against the argument name. */ - if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, - arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, arg->name, arg->name_len, &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(arg->name, var->param) == 0) match = 1; } @@ -253,6 +286,10 @@ static int var_args_get_generate(modsec_rec *msr, msre_var *var, msre_rule *rule static int var_args_get_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; @@ -261,6 +298,7 @@ static int var_args_get_names_generate(modsec_rec *msr, msre_var *var, msre_rule te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { msc_arg *arg = (msc_arg *)te[i].val; + assert(arg != NULL); int match = 0; /* Only QUERY_STRING arguments */ @@ -271,8 +309,7 @@ static int var_args_get_names_generate(modsec_rec *msr, msre_var *var, msre_rule else { if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, - arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, arg->name, arg->name_len, &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(arg->name, var->param) == 0) match = 1; } @@ -299,6 +336,10 @@ static int var_args_get_names_generate(modsec_rec *msr, msre_var *var, msre_rule static int var_args_post_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; @@ -308,6 +349,7 @@ static int var_args_post_generate(modsec_rec *msr, msre_var *var, msre_rule *rul te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { msc_arg *arg = (msc_arg *)te[i].val; + assert(arg != NULL); int match = 0; /* Only BODY arguments */ @@ -319,8 +361,7 @@ static int var_args_post_generate(modsec_rec *msr, msre_var *var, msre_rule *rul if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; /* Run the regex against the argument name. */ - if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, - arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, arg->name, arg->name_len, &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(arg->name, var->param) == 0) match = 1; } @@ -347,6 +388,10 @@ static int var_args_post_generate(modsec_rec *msr, msre_var *var, msre_rule *rul static int var_args_post_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; @@ -355,6 +400,7 @@ static int var_args_post_names_generate(modsec_rec *msr, msre_var *var, msre_rul te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { msc_arg *arg = (msc_arg *)te[i].val; + assert(arg != NULL); int match = 0; /* Only BODY arguments */ @@ -365,8 +411,7 @@ static int var_args_post_names_generate(modsec_rec *msr, msre_var *var, msre_rul else { if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, arg->name, - arg->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, arg->name, arg->name_len, &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(arg->name, var->param) == 0) match = 1; } @@ -393,10 +438,14 @@ static int var_args_post_names_generate(modsec_rec *msr, msre_var *var, msre_rul static int var_rule_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(rule != NULL); + assert(rule->actionset != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); msre_actionset *actionset = NULL; - if (rule == NULL) return 0; - actionset = rule->actionset; if (rule->chain_starter != NULL) actionset = rule->chain_starter->actionset; @@ -428,13 +477,14 @@ static int var_rule_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, return var_simple_generate(var, vartab, mptmp, value); } - return 0; } /* ENV */ static char *var_env_validate(msre_ruleset *ruleset, msre_var *var) { + assert(ruleset != NULL); + assert(var != NULL); if (var->param == NULL) { return apr_psprintf(ruleset->mp, "Parameter required for ENV."); } @@ -449,6 +499,9 @@ static char *var_env_validate(msre_ruleset *ruleset, msre_var *var) { static int var_env_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->r != NULL); + assert(var != NULL); char *value = get_env_var(msr->r, (char *)var->param); if (value != NULL) { return var_simple_generate(var, vartab, mptmp, value); @@ -461,6 +514,8 @@ static int var_env_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, static int var_request_uri_raw_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->r != NULL); return var_simple_generate(var, vartab, mptmp, msr->r->unparsed_uri); } @@ -469,6 +524,8 @@ static int var_request_uri_raw_generate(modsec_rec *msr, msre_var *var, msre_rul static int var_uniqueid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->r != NULL); char *value = get_env_var(msr->r, "UNIQUE_ID"); if (value != NULL) { return var_simple_generate(var, vartab, mptmp, value); @@ -483,10 +540,18 @@ static int var_uniqueid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule static int var_request_uri_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) /* dynamic */ { + assert(msr != NULL); + assert(msr->r != NULL); char *value = NULL; if (msr->r->parsed_uri.query == NULL) value = msr->r->parsed_uri.path; - else value = apr_pstrcat(mptmp, msr->r->parsed_uri.path, "?", msr->r->parsed_uri.query, NULL); + else { + value = apr_pstrcat(mptmp, msr->r->parsed_uri.path, "?", msr->r->parsed_uri.query, NULL); + if (!value) { + msr_log(msr, 1, "REQUEST_URI: Memory allocation error"); + return -1; + } + } return var_simple_generate(var, vartab, mptmp, value); } @@ -496,7 +561,14 @@ static int var_request_uri_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_reqbody_processor_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "REQBODY_PROCESSOR: Memory allocation error"); + return -1; + } if (msr->msc_reqbody_processor == NULL) { rvar->value = apr_pstrdup(mptmp, ""); @@ -515,9 +587,21 @@ static int var_reqbody_processor_generate(modsec_rec *msr, msre_var *var, msre_r static int var_sdbm_delete_error_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "SDBM_DELETE_ERROR: Memory allocation error"); + return -1; + } rvar->value = apr_psprintf(mptmp, "%d", msr->msc_sdbm_delete_error); + if (!rvar->value) { + msr_log(msr, 1, "SDBM_DELETE_ERROR: Memory allocation error"); + return -1; + } rvar->value_len = strlen(rvar->value); apr_table_addn(vartab, rvar->name, (void *)rvar); @@ -529,7 +613,15 @@ static int var_sdbm_delete_error_generate(modsec_rec *msr, msre_var *var, msre_r static int var_reqbody_processor_error_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "REQBODY_ERROR: Memory allocation error"); + return -1; + } rvar->value = apr_psprintf(mptmp, "%d", msr->msc_reqbody_error); rvar->value_len = strlen(rvar->value); @@ -543,7 +635,15 @@ static int var_reqbody_processor_error_generate(modsec_rec *msr, msre_var *var, static int var_reqbody_processor_error_msg_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "REQBODY_ERROR_MSG: Memory allocation error"); + return -1; + } if (msr->msc_reqbody_error_msg == NULL) { rvar->value = apr_pstrdup(mptmp, ""); @@ -561,6 +661,8 @@ static int var_reqbody_processor_error_msg_generate(modsec_rec *msr, msre_var *v /* XML */ static char *var_xml_validate(msre_ruleset *ruleset, msre_var *var) { + assert(var != NULL); + /* It's OK if there's no parameter. */ if (var->param == NULL) return NULL; @@ -572,6 +674,13 @@ static char *var_xml_validate(msre_ruleset *ruleset, msre_var *var) { static int var_xml_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(var->name != NULL); + assert(rule != NULL); + assert(rule->actionset != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *tarr; const apr_table_entry_t *telts; xmlXPathContextPtr xpathCtx; @@ -652,22 +761,35 @@ static int var_xml_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, } /* Create one variable for each node in the result. */ + char* content = NULL; for(i = 0; i < nodes->nodeNr; i++) { msre_var *rvar = NULL; - char *content = NULL; content = (char *)xmlNodeGetContent(nodes->nodeTab[i]); if (content != NULL) { rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "XML: Memory allocation error"); + count = -1; + goto var_xml_generate_Error; + } rvar->value = apr_pstrdup(mptmp, content); - xmlFree(content); + if (!rvar->value) { + msr_log(msr, 1, "XML: Memory allocation error"); + count = -1; + goto var_xml_generate_Error; + } rvar->value_len = strlen(rvar->value); apr_table_addn(vartab, rvar->name, (void *)rvar); + xmlFree(content); + content = NULL; count++; } } +var_xml_generate_Error: + if (content != NULL) xmlFree(content); xmlXPathFreeObject(xpathObj); xmlXPathFreeContext(xpathCtx); @@ -679,6 +801,11 @@ static int var_xml_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, static int var_webserver_error_log_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(var->name != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); msre_var *rvar = NULL; int i, count = 0; @@ -689,7 +816,15 @@ static int var_webserver_error_log_generate(modsec_rec *msr, msre_var *var, msre fem = format_error_log_message(mptmp, em); if (fem != NULL) { rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "WEBSERVER_ERROR_LOG: Memory allocation error"); + return -1; + } rvar->value = apr_pstrdup(mptmp, fem); + if (!rvar->value) { + msr_log(msr, 1, "WEBSERVER_ERROR_LOG: Memory allocation error"); + return -1; + } rvar->value_len = strlen(rvar->value); apr_table_addn(vartab, rvar->name, (void *)rvar); @@ -704,6 +839,7 @@ static int var_webserver_error_log_generate(modsec_rec *msr, msre_var *var, msre static int var_useragent_ip_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return var_simple_generate(var, vartab, mptmp, msr->useragent_ip ? msr->useragent_ip : "0.0.0.0"); } #endif @@ -713,9 +849,12 @@ static int var_useragent_ip_generate(modsec_rec *msr, msre_var *var, msre_rule * static int var_remote_addr_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); #if !defined(MSC_TEST) #if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 3 if (ap_find_linked_module("mod_remoteip.c") != NULL) { + assert(msr->r != NULL); + assert(msr->r->useragent_ip != NULL); if(msr->r->useragent_ip != NULL) msr->remote_addr = apr_pstrdup(msr->mp, msr->r->useragent_ip); return var_simple_generate(var, vartab, mptmp, msr->remote_addr); } @@ -730,6 +869,8 @@ static int var_remote_addr_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_remote_host_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->r != NULL); const char *value1 = ap_get_remote_host(msr->r->connection, msr->r->per_dir_config, REMOTE_NAME, NULL); return var_simple_generate(var, vartab, mptmp, value1); @@ -740,6 +881,7 @@ static int var_remote_host_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_remote_port_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); char *value = apr_psprintf(mptmp, "%u", msr->remote_port); return var_simple_generate(var, vartab, mptmp, value); } @@ -749,6 +891,7 @@ static int var_remote_port_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_remote_user_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return var_simple_generate(var, vartab, mptmp, msr->remote_user); } @@ -757,14 +900,20 @@ static int var_remote_user_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_tx_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; arr = apr_table_elts(msr->tx_vars); + assert(arr != NULL); te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { msc_string *str = (msc_string *)te[i].val; + assert(str != NULL); int match; /* Figure out if we want to include this variable. */ @@ -773,8 +922,7 @@ static int var_tx_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, else { if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, str->name, str->name_len, &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(str->name, var->param) == 0) match = 1; } @@ -783,10 +931,18 @@ static int var_tx_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, /* If we had a match add this argument to the collection. */ if (match) { msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "TX: Memory allocation error"); + return -1; + } rvar->value = str->value; rvar->value_len = str->value_len; rvar->name = apr_psprintf(mptmp, "TX:%s", log_escape_nq_ex(mptmp, str->name, str->name_len)); + if (!rvar->name) { + msr_log(msr, 1, "TX: Memory allocation error"); + return -1; + } apr_table_addn(vartab, rvar->name, (void *)rvar); count++; @@ -801,14 +957,20 @@ static int var_tx_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, static int var_geo_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; arr = apr_table_elts(msr->geo_vars); + assert(arr != NULL); te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { msc_string *str = (msc_string *)te[i].val; + assert(str != NULL); int match; /* Figure out if we want to include this variable. */ @@ -817,8 +979,7 @@ static int var_geo_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, else { if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, str->name, str->name_len, &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(str->name, var->param) == 0) match = 1; } @@ -845,6 +1006,7 @@ static int var_geo_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, static int var_highest_severity_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return var_simple_generate(var, vartab, mptmp, apr_psprintf(mptmp, "%d", msr->highest_severity)); } @@ -854,6 +1016,11 @@ static int var_highest_severity_generate(modsec_rec *msr, msre_var *var, msre_ru static int var_ip_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->collections != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; @@ -863,9 +1030,11 @@ static int var_ip_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, if (target_col == NULL) return 0; arr = apr_table_elts(target_col); + assert(arr != NULL); te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { msc_string *str = (msc_string *)te[i].val; + assert(str != NULL); int match; /* Figure out if we want to include this variable. */ @@ -874,8 +1043,7 @@ static int var_ip_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, else { if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, str->name, str->name_len, &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(str->name, var->param) == 0) match = 1; } @@ -902,6 +1070,8 @@ static int var_ip_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, static int var_matched_var_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->matched_var != NULL); return var_simple_generate_ex(var, vartab, mptmp, apr_pmemdup(mptmp, msr->matched_var->value, @@ -914,6 +1084,8 @@ static int var_matched_var_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_matched_var_name_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->matched_var != NULL); return var_simple_generate_ex(var, vartab, mptmp, apr_pmemdup(mptmp, msr->matched_var->name, @@ -926,6 +1098,11 @@ static int var_matched_var_name_generate(modsec_rec *msr, msre_var *var, msre_ru static int var_session_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->collections != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; @@ -938,6 +1115,7 @@ static int var_session_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { msc_string *str = (msc_string *)te[i].val; + assert(str != NULL); int match; /* Figure out if we want to include this variable. */ @@ -946,8 +1124,7 @@ static int var_session_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, else { if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, str->name, str->name_len, &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(str->name, var->param) == 0) match = 1; } @@ -956,6 +1133,10 @@ static int var_session_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, /* If we had a match add this argument to the collection. */ if (match) { msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "SESSION: Memory allocation error"); + return -1; + } rvar->value = str->value; rvar->value_len = str->value_len; @@ -974,6 +1155,11 @@ static int var_session_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, static int var_user_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->collections != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; @@ -986,6 +1172,7 @@ static int var_user_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { msc_string *str = (msc_string *)te[i].val; + assert(str != NULL); int match; /* Figure out if we want to include this variable. */ @@ -994,8 +1181,7 @@ static int var_user_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, else { if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, str->name, str->name_len, &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(str->name, var->param) == 0) match = 1; } @@ -1004,10 +1190,18 @@ static int var_user_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, /* If we had a match add this argument to the collection. */ if (match) { msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "USER: Memory allocation error"); + return -1; + } rvar->value = str->value; rvar->value_len = str->value_len; rvar->name = apr_psprintf(mptmp, "USER:%s", log_escape_nq_ex(mptmp, str->name, str->name_len)); + if (!rvar->name) { + msr_log(msr, 1, "USER: Memory allocation error"); + return -1; + } apr_table_addn(vartab, rvar->name, (void *)rvar); count++; @@ -1022,6 +1216,11 @@ static int var_user_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, static int var_global_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->collections != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; @@ -1034,6 +1233,7 @@ static int var_global_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { msc_string *str = (msc_string *)te[i].val; + assert(str != NULL); int match; /* Figure out if we want to include this variable. */ @@ -1042,8 +1242,7 @@ static int var_global_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, else { if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, str->name, str->name_len, &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(str->name, var->param) == 0) match = 1; } @@ -1070,6 +1269,11 @@ static int var_global_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, static int var_resource_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->collections != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; @@ -1082,6 +1286,7 @@ static int var_resource_generate(modsec_rec *msr, msre_var *var, msre_rule *rule te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { msc_string *str = (msc_string *)te[i].val; + assert(str != NULL); int match; /* Figure out if we want to include this variable. */ @@ -1090,8 +1295,7 @@ static int var_resource_generate(modsec_rec *msr, msre_var *var, msre_rule *rule else { if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - str->name_len, &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, str->name, str->name_len, &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(str->name, var->param) == 0) match = 1; } @@ -1100,6 +1304,10 @@ static int var_resource_generate(modsec_rec *msr, msre_var *var, msre_rule *rule /* If we had a match add this argument to the collection. */ if (match) { msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "RESOURCE: Memory allocation error"); + return -1; + } rvar->value = str->value; rvar->value_len = str->value_len; @@ -1118,44 +1326,32 @@ static int var_resource_generate(modsec_rec *msr, msre_var *var, msre_rule *rule static int var_files_tmp_contents_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); multipart_part **parts = NULL; int i, count = 0; if (msr->mpd == NULL) return 0; + assert(msr->mpd->parts != NULL); parts = (multipart_part **)msr->mpd->parts->elts; - for (i = 0; i < msr->mpd->parts->nelts; i++) - { - if ((parts[i]->type == MULTIPART_FILE) && - (parts[i]->tmp_file_name != NULL)) - { + for (i = 0; i < msr->mpd->parts->nelts; i++) { + if ((parts[i]->type == MULTIPART_FILE) && (parts[i]->tmp_file_name != NULL)) { int match = 0; /* Figure out if we want to include this variable. */ - if (var->param == NULL) - { - match = 1; - } - else - { - if (var->param_data != NULL) - { + if (var->param == NULL)match = 1; + else { + if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, - parts[i]->name, strlen(parts[i]->name), - &my_error_msg) == PCRE_ERROR_NOMATCH)) - { - match = 1; - } + if (msc_regexec((msc_regex_t*)var->param_data, parts[i]->name, strlen(parts[i]->name), &my_error_msg) >= 0) match = 1; } - else - { + else { /* Simple comparison. */ - if (strcasecmp(parts[i]->name, var->param) == 0) - { - match = 1; - } + if (strcasecmp(parts[i]->name, var->param) == 0)match = 1; } } /* If we had a match add this argument to the collection. */ @@ -1164,28 +1360,30 @@ static int var_files_tmp_contents_generate(modsec_rec *msr, msre_var *var, FILE *file; size_t nread; char *full_content = NULL; + char *full_content_tmp_ptr = NULL; size_t total_lenght = 0; msre_var *rvar = NULL; file = fopen(parts[i]->tmp_file_name, "r"); - if (file == NULL) - { - continue; + if (file == NULL) continue; + + full_content = (char *)apr_pcalloc(mptmp, (sizeof(char)*parts[i]->length) + 1); + if (full_content == NULL) { + if (msr->txcfg->debuglog_level >= 3) { + msr_log(msr, 3, "Variable FILES_TMP_CONTENT will not be created, not " \ + "enough memory available."); + } + goto files_tmp_content_not_enough_mem; } + full_content_tmp_ptr = full_content; while ((nread = fread(buf, 1, 1023, file)) > 0) { - total_lenght += nread; - buf[nread] = '\0'; - if (full_content == NULL) - { - full_content = apr_psprintf(mptmp, "%s", buf); - } - else - { - full_content = apr_psprintf(mptmp, "%s%s", full_content, buf); - } + full_content_tmp_ptr = memcpy(full_content_tmp_ptr, buf, nread); + full_content_tmp_ptr += nread; + total_lenght += nread; } + full_content_tmp_ptr[total_lenght] = '\0'; fclose(file); rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); @@ -1200,6 +1398,7 @@ static int var_files_tmp_contents_generate(modsec_rec *msr, msre_var *var, } } +files_tmp_content_not_enough_mem: return count; } @@ -1209,13 +1408,19 @@ static int var_files_tmp_contents_generate(modsec_rec *msr, msre_var *var, static int var_files_tmpnames_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); multipart_part **parts = NULL; int i, count = 0; if (msr->mpd == NULL) return 0; + assert(msr->mpd->parts != NULL); parts = (multipart_part **)msr->mpd->parts->elts; for(i = 0; i < msr->mpd->parts->nelts; i++) { + assert(parts[i] != NULL); if ((parts[i]->type == MULTIPART_FILE)&&(parts[i]->tmp_file_name != NULL)) { int match = 0; @@ -1224,8 +1429,7 @@ static int var_files_tmpnames_generate(modsec_rec *msr, msre_var *var, msre_rule else { if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, parts[i]->name, - strlen(parts[i]->name), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, parts[i]->name, strlen(parts[i]->name), &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(parts[i]->name, var->param) == 0) match = 1; } @@ -1254,13 +1458,19 @@ static int var_files_tmpnames_generate(modsec_rec *msr, msre_var *var, msre_rule static int var_files_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); multipart_part **parts = NULL; int i, count = 0; if (msr->mpd == NULL) return 0; + assert(msr->mpd->parts != NULL); parts = (multipart_part **)msr->mpd->parts->elts; for(i = 0; i < msr->mpd->parts->nelts; i++) { + assert(parts[i] != NULL); if (parts[i]->type == MULTIPART_FILE) { int match = 0; @@ -1269,8 +1479,7 @@ static int var_files_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, else { if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, parts[i]->name, - strlen(parts[i]->name), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, parts[i]->name, strlen(parts[i]->name), &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(parts[i]->name, var->param) == 0) match = 1; } @@ -1299,13 +1508,19 @@ static int var_files_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, static int var_files_sizes_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); multipart_part **parts = NULL; int i, count = 0; if (msr->mpd == NULL) return 0; + assert(msr->mpd->parts != NULL); parts = (multipart_part **)msr->mpd->parts->elts; for(i = 0; i < msr->mpd->parts->nelts; i++) { + assert(parts[i] != NULL); if (parts[i]->type == MULTIPART_FILE) { int match = 0; @@ -1314,8 +1529,7 @@ static int var_files_sizes_generate(modsec_rec *msr, msre_var *var, msre_rule *r else { if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, parts[i]->name, - strlen(parts[i]->name), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, parts[i]->name, strlen(parts[i]->name), &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(parts[i]->name, var->param) == 0) match = 1; } @@ -1344,15 +1558,25 @@ static int var_files_sizes_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_files_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); multipart_part **parts = NULL; int i, count = 0; if (msr->mpd == NULL) return 0; + assert(msr->mpd->parts != NULL); parts = (multipart_part **)msr->mpd->parts->elts; for(i = 0; i < msr->mpd->parts->nelts; i++) { + assert(parts[i] != NULL); if (parts[i]->type == MULTIPART_FILE) { msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "FILES_NAMES: Memory allocation error"); + return count; + } rvar->value = parts[i]->name; rvar->value_len = strlen(rvar->value); @@ -1372,6 +1596,9 @@ static int var_files_names_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_files_combined_size_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); multipart_part **parts = NULL; msre_var *rvar = NULL; unsigned int combined_size = 0; @@ -1380,6 +1607,7 @@ static int var_files_combined_size_generate(modsec_rec *msr, msre_var *var, msre if (msr->mpd != NULL) { parts = (multipart_part **)msr->mpd->parts->elts; for(i = 0; i < msr->mpd->parts->nelts; i++) { + assert(parts[i] != NULL); if (parts[i]->type == MULTIPART_FILE) { combined_size += parts[i]->tmp_file_size; } @@ -1387,6 +1615,10 @@ static int var_files_combined_size_generate(modsec_rec *msr, msre_var *var, msre } rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "FILES_NAMES: Memory allocation error"); + return -1; + } rvar->value = apr_psprintf(mptmp, "%u", combined_size); rvar->value_len = strlen(rvar->value); apr_table_addn(vartab, rvar->name, (void *)rvar); @@ -1394,6 +1626,59 @@ static int var_files_combined_size_generate(modsec_rec *msr, msre_var *var, msre return 1; } +/* MULTIPART_PART_HEADERS */ + +static int var_multipart_part_headers_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, + apr_table_t *vartab, apr_pool_t *mptmp) +{ + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); + multipart_part **parts = NULL; + int i, j, count = 0; + + if (msr->mpd == NULL) return 0; + assert(msr->mpd->parts != NULL); + + parts = (multipart_part **)msr->mpd->parts->elts; + for(i = 0; i < msr->mpd->parts->nelts; i++) { + assert(parts[i] != NULL); + int match = 0; + + /* Figure out if we want to include this variable. */ + if (var->param == NULL) match = 1; + else { + if (var->param_data != NULL) { /* Regex. */ + char *my_error_msg = NULL; + if (msc_regexec((msc_regex_t *)var->param_data, parts[i]->name, strlen(parts[i]->name), &my_error_msg) >= 0) match = 1; + } else { /* Simple comparison. */ + if (strcasecmp(parts[i]->name, var->param) == 0) match = 1; + } + } + + /* If we had a match add this argument to the collection. */ + if (match) { + if (parts[i]->header_lines) { /* this NULL check shouldn't be necessary */ + for (j = 0; j < parts[i]->header_lines->nelts; j++) { + char *header_line = ((char **)parts[i]->header_lines->elts)[j]; + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + + rvar->value = header_line; + rvar->value_len = strlen(rvar->value); + rvar->name = apr_psprintf(mptmp, "MULTIPART_PART_HEADERS:%s", + log_escape_nq(mptmp, parts[i]->name)); + apr_table_addn(vartab, rvar->name, (void *)rvar); + + count++; + } + } + } + } + + return count; +} + /* MODSEC_BUILD */ static int var_modsec_build_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, @@ -1407,6 +1692,7 @@ static int var_modsec_build_generate(modsec_rec *msr, msre_var *var, msre_rule * static int var_multipart_filename_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return var_simple_generate(var, vartab, mptmp, msr->multipart_filename); } @@ -1415,6 +1701,7 @@ static int var_multipart_filename_generate(modsec_rec *msr, msre_var *var, msre_ static int var_multipart_name_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return var_simple_generate(var, vartab, mptmp, msr->multipart_name); } @@ -1423,6 +1710,7 @@ static int var_multipart_name_generate(modsec_rec *msr, msre_var *var, msre_rule static int var_multipart_boundary_quoted_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if ((msr->mpd != NULL)&&(msr->mpd->flag_boundary_quoted != 0)) { return var_simple_generate(var, vartab, mptmp, "1"); } else { @@ -1435,6 +1723,7 @@ static int var_multipart_boundary_quoted_generate(modsec_rec *msr, msre_var *var static int var_multipart_boundary_whitespace_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if ((msr->mpd != NULL)&&(msr->mpd->flag_boundary_whitespace != 0)) { return var_simple_generate(var, vartab, mptmp, "1"); } else { @@ -1447,6 +1736,7 @@ static int var_multipart_boundary_whitespace_generate(modsec_rec *msr, msre_var static int var_multipart_data_after_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if ((msr->mpd != NULL)&&(msr->mpd->flag_data_after != 0)) { return var_simple_generate(var, vartab, mptmp, "1"); } else { @@ -1459,6 +1749,7 @@ static int var_multipart_data_after_generate(modsec_rec *msr, msre_var *var, msr static int var_multipart_data_before_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if ((msr->mpd != NULL)&&(msr->mpd->flag_data_before != 0)) { return var_simple_generate(var, vartab, mptmp, "1"); } else { @@ -1471,6 +1762,7 @@ static int var_multipart_data_before_generate(modsec_rec *msr, msre_var *var, ms static int var_multipart_header_folding_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if ((msr->mpd != NULL)&&(msr->mpd->flag_header_folding != 0)) { return var_simple_generate(var, vartab, mptmp, "1"); } else { @@ -1483,6 +1775,7 @@ static int var_multipart_header_folding_generate(modsec_rec *msr, msre_var *var, static int var_multipart_crlf_line_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if ((msr->mpd != NULL)&&(msr->mpd->flag_crlf_line != 0)) { return var_simple_generate(var, vartab, mptmp, "1"); } else { @@ -1495,6 +1788,7 @@ static int var_multipart_crlf_line_generate(modsec_rec *msr, msre_var *var, msre static int var_multipart_crlf_lf_lines_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if ((msr->mpd != NULL)&&(msr->mpd->flag_lf_line != 0)&&(msr->mpd->flag_crlf_line != 0)) { return var_simple_generate(var, vartab, mptmp, "1"); } else { @@ -1507,6 +1801,7 @@ static int var_multipart_crlf_lf_lines_generate(modsec_rec *msr, msre_var *var, static int var_multipart_lf_line_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if ((msr->mpd != NULL)&&(msr->mpd->flag_lf_line != 0)) { return var_simple_generate(var, vartab, mptmp, "1"); } else { @@ -1519,6 +1814,7 @@ static int var_multipart_lf_line_generate(modsec_rec *msr, msre_var *var, msre_r static int var_multipart_missing_semicolon_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if ((msr->mpd != NULL)&&(msr->mpd->flag_missing_semicolon != 0)) { return var_simple_generate(var, vartab, mptmp, "1"); } else { @@ -1531,6 +1827,7 @@ static int var_multipart_missing_semicolon_generate(modsec_rec *msr, msre_var *v static int var_multipart_invalid_part_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if ((msr->mpd != NULL)&&(msr->mpd->flag_invalid_part != 0)) { return var_simple_generate(var, vartab, mptmp, "1"); } else { @@ -1543,6 +1840,7 @@ static int var_multipart_invalid_part_generate(modsec_rec *msr, msre_var *var, m static int var_multipart_invalid_quoting_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if ((msr->mpd != NULL)&&(msr->mpd->flag_invalid_quoting != 0)) { return var_simple_generate(var, vartab, mptmp, "1"); } else { @@ -1555,6 +1853,7 @@ static int var_multipart_invalid_quoting_generate(modsec_rec *msr, msre_var *var static int var_multipart_invalid_header_folding_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if ((msr->mpd != NULL)&&(msr->mpd->flag_invalid_header_folding != 0)) { return var_simple_generate(var, vartab, mptmp, "1"); } else { @@ -1567,6 +1866,7 @@ static int var_multipart_invalid_header_folding_generate(modsec_rec *msr, msre_v static int var_multipart_file_limit_exceeded_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if ((msr->mpd != NULL)&&(msr->mpd->flag_file_limit_exceeded != 0)) { return var_simple_generate(var, vartab, mptmp, "1"); } else { @@ -1579,6 +1879,7 @@ static int var_multipart_file_limit_exceeded_generate(modsec_rec *msr, msre_var static int var_multipart_strict_error_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if (msr->mpd != NULL) { /* Respond positive if at least one of the multipart flags is raised. */ if ( (msr->mpd->flag_error) @@ -1606,6 +1907,7 @@ static int var_multipart_strict_error_generate(modsec_rec *msr, msre_var *var, m static int var_multipart_unmatched_boundary_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if ((msr->mpd != NULL)&&(msr->mpd->flag_unmatched_boundary != 0)) { return var_simple_generate(var, vartab, mptmp, "1"); } else { @@ -1618,6 +1920,7 @@ static int var_multipart_unmatched_boundary_generate(modsec_rec *msr, msre_var * static int var_urlencoded_error_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if (msr->urlencoded_error) { return var_simple_generate(var, vartab, mptmp, "1"); } else { @@ -1630,6 +1933,7 @@ static int var_urlencoded_error_generate(modsec_rec *msr, msre_var *var, msre_ru static int var_inbound_error_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if (msr->inbound_error) { return var_simple_generate(var, vartab, mptmp, "1"); } else { @@ -1642,6 +1946,7 @@ static int var_inbound_error_generate(modsec_rec *msr, msre_var *var, msre_rule static int var_outbound_error_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if (msr->outbound_error) { return var_simple_generate(var, vartab, mptmp, "1"); } else { @@ -1650,12 +1955,15 @@ static int var_outbound_error_generate(modsec_rec *msr, msre_var *var, msre_rule } static apr_time_t calculate_perf_combined(modsec_rec *msr) { + assert(msr != NULL); return msr->time_phase1 + msr->time_phase2 + msr->time_phase3 + msr->time_phase4 + msr->time_phase5 + msr->time_storage_write /* time_storage_read is already included in phases */ + msr->time_logging + msr->time_gc; } char *format_all_performance_variables(modsec_rec *msr, apr_pool_t *mp) { + assert(msr != NULL); + assert(mp != NULL); return apr_psprintf(mp, "combined=%" APR_TIME_T_FMT ", p1=%" APR_TIME_T_FMT ", p2=%" APR_TIME_T_FMT ", p3=%" APR_TIME_T_FMT ", p4=%" APR_TIME_T_FMT ", p5=%" APR_TIME_T_FMT ", sr=%" APR_TIME_T_FMT ", sw=%" APR_TIME_T_FMT @@ -1668,6 +1976,9 @@ char *format_all_performance_variables(modsec_rec *msr, apr_pool_t *mp) { static int generate_performance_variable(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp, apr_time_t value) { + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); msre_var *rvar = NULL; rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); @@ -1684,6 +1995,9 @@ static int generate_performance_variable(modsec_rec *msr, msre_var *var, msre_ru static int var_perf_all_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); msre_var *rvar = NULL; rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); @@ -1708,6 +2022,7 @@ static int var_perf_combined_generate(modsec_rec *msr, msre_var *var, msre_rule static int var_perf_gc_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_gc); } @@ -1716,6 +2031,7 @@ static int var_perf_gc_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, static int var_perf_phase1_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_phase1); } @@ -1724,6 +2040,7 @@ static int var_perf_phase1_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_perf_phase2_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_phase2); } @@ -1732,6 +2049,7 @@ static int var_perf_phase2_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_perf_phase3_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_phase3); } @@ -1740,6 +2058,7 @@ static int var_perf_phase3_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_perf_phase4_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_phase4); } @@ -1748,6 +2067,7 @@ static int var_perf_phase4_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_perf_phase5_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_phase5); } @@ -1756,6 +2076,7 @@ static int var_perf_phase5_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_perf_sread_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_storage_read); } @@ -1764,6 +2085,7 @@ static int var_perf_sread_generate(modsec_rec *msr, msre_var *var, msre_rule *ru static int var_perf_swrite_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_storage_write); } @@ -1772,6 +2094,7 @@ static int var_perf_swrite_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_perf_logging_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return generate_performance_variable(msr, var, rule, vartab, mptmp, msr->time_logging); } @@ -1781,11 +2104,16 @@ static int var_perf_logging_generate(modsec_rec *msr, msre_var *var, msre_rule * static int var_perf_rules_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; arr = apr_table_elts(msr->perf_rules); + assert(arr != NULL); te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { int match = 0; @@ -1795,8 +2123,7 @@ static int var_perf_rules_generate(modsec_rec *msr, msre_var *var, msre_rule *ru else { if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, - strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, te[i].key, strlen(te[i].key), &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(te[i].key, var->param) == 0) match = 1; } @@ -1824,9 +2151,16 @@ static int var_perf_rules_generate(modsec_rec *msr, msre_var *var, msre_rule *ru static int var_duration_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { - msre_var *rvar = NULL; + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); + msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "DURATION: Memory allocation error"); + return -1; + } - rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); rvar->value = apr_psprintf(mptmp, "%" APR_TIME_T_FMT, (apr_time_now() - msr->r->request_time)); rvar->value_len = strlen(rvar->value); @@ -1840,6 +2174,10 @@ static int var_duration_generate(modsec_rec *msr, msre_var *var, msre_rule *rule static int var_time_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); msre_var *rvar = NULL; struct tm *tm; time_t tc; @@ -1847,10 +2185,18 @@ static int var_time_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, tc = time(NULL); tm = localtime(&tc); rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "TIME: Memory allocation error"); + return -1; + } rvar->value = apr_psprintf(mptmp, "%02d%02d%02d%02d%02d%02d%02d", (tm->tm_year / 100) + 19, (tm->tm_year % 100), tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + if (!rvar->value) { + msr_log(msr, 1, "TIME: Memory allocation error"); + return -1; + } rvar->value_len = strlen(rvar->value); apr_table_addn(vartab, rvar->name, (void *)rvar); @@ -1862,6 +2208,10 @@ static int var_time_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, static int var_time_year_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); msre_var *rvar = NULL; struct tm *tm; time_t tc; @@ -1869,9 +2219,17 @@ static int var_time_year_generate(modsec_rec *msr, msre_var *var, msre_rule *rul tc = time(NULL); tm = localtime(&tc); rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "TIME_YEAR: Memory allocation error"); + return -1; + } rvar->value = apr_psprintf(mptmp, "%02d%02d", (tm->tm_year / 100) + 19, tm->tm_year % 100); + if (!rvar->value) { + msr_log(msr, 1, "TIME_YEAR: Memory allocation error"); + return -1; + } rvar->value_len = strlen(rvar->value); apr_table_addn(vartab, rvar->name, (void *)rvar); @@ -1883,6 +2241,10 @@ static int var_time_year_generate(modsec_rec *msr, msre_var *var, msre_rule *rul static int var_time_wday_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); msre_var *rvar = NULL; struct tm *tm; time_t tc; @@ -1890,7 +2252,15 @@ static int var_time_wday_generate(modsec_rec *msr, msre_var *var, msre_rule *rul tc = time(NULL); tm = localtime(&tc); rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "TIME_WDAY: Memory allocation error"); + return -1; + } rvar->value = apr_psprintf(mptmp, "%d", tm->tm_wday); + if (!rvar->value) { + msr_log(msr, 1, "TIME_WDAY: Memory allocation error"); + return -1; + } rvar->value_len = strlen(rvar->value); apr_table_addn(vartab, rvar->name, (void *)rvar); @@ -1902,6 +2272,10 @@ static int var_time_wday_generate(modsec_rec *msr, msre_var *var, msre_rule *rul static int var_time_sec_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); msre_var *rvar = NULL; struct tm *tm; time_t tc; @@ -1909,7 +2283,15 @@ static int var_time_sec_generate(modsec_rec *msr, msre_var *var, msre_rule *rule tc = time(NULL); tm = localtime(&tc); rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "TIME_SEC: Memory allocation error"); + return -1; + } rvar->value = apr_psprintf(mptmp, "%02d", tm->tm_sec); + if (!rvar->value) { + msr_log(msr, 1, "TIME_SEC: Memory allocation error"); + return -1; + } rvar->value_len = strlen(rvar->value); apr_table_addn(vartab, rvar->name, (void *)rvar); @@ -1921,6 +2303,10 @@ static int var_time_sec_generate(modsec_rec *msr, msre_var *var, msre_rule *rule static int var_time_min_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); msre_var *rvar = NULL; struct tm *tm; time_t tc; @@ -1928,7 +2314,15 @@ static int var_time_min_generate(modsec_rec *msr, msre_var *var, msre_rule *rule tc = time(NULL); tm = localtime(&tc); rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "TIME_MIN: Memory allocation error"); + return -1; + } rvar->value = apr_psprintf(mptmp, "%02d", tm->tm_min); + if (!rvar->value) { + msr_log(msr, 1, "TIME_MIN: Memory allocation error"); + return -1; + } rvar->value_len = strlen(rvar->value); apr_table_addn(vartab, rvar->name, (void *)rvar); @@ -1939,6 +2333,10 @@ static int var_time_min_generate(modsec_rec *msr, msre_var *var, msre_rule *rule static int var_time_hour_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); msre_var *rvar = NULL; struct tm *tm; time_t tc; @@ -1946,7 +2344,15 @@ static int var_time_hour_generate(modsec_rec *msr, msre_var *var, msre_rule *rul tc = time(NULL); tm = localtime(&tc); rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "TIME_HOUR: Memory allocation error"); + return -1; + } rvar->value = apr_psprintf(mptmp, "%02d", tm->tm_hour); + if (!rvar->value) { + msr_log(msr, 1, "TIME_HOUR: Memory allocation error"); + return -1; + } rvar->value_len = strlen(rvar->value); apr_table_addn(vartab, rvar->name, (void *)rvar); @@ -1958,6 +2364,10 @@ static int var_time_hour_generate(modsec_rec *msr, msre_var *var, msre_rule *rul static int var_time_mon_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); msre_var *rvar = NULL; struct tm *tm; time_t tc; @@ -1965,7 +2375,16 @@ static int var_time_mon_generate(modsec_rec *msr, msre_var *var, msre_rule *rule tc = time(NULL); tm = localtime(&tc); rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + assert(msr != NULL); + if (!rvar) { + msr_log(msr, 1, "TIME_MON: Memory allocation error"); + return -1; + } rvar->value = apr_psprintf(mptmp, "%02d", tm->tm_mon + 1); + if (!rvar->value) { + msr_log(msr, 1, "TIME_MON: Memory allocation error"); + return -1; + } rvar->value_len = strlen(rvar->value); apr_table_addn(vartab, rvar->name, (void *)rvar); @@ -1977,6 +2396,10 @@ static int var_time_mon_generate(modsec_rec *msr, msre_var *var, msre_rule *rule static int var_time_day_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); msre_var *rvar = NULL; struct tm *tm; time_t tc; @@ -1984,7 +2407,15 @@ static int var_time_day_generate(modsec_rec *msr, msre_var *var, msre_rule *rule tc = time(NULL); tm = localtime(&tc); rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "TIME_DAY: Memory allocation error"); + return -1; + } rvar->value = apr_psprintf(mptmp, "%02d", tm->tm_mday); + if (!rvar->value) { + msr_log(msr, 1, "TIME_DAY: Memory allocation error"); + return -1; + } rvar->value_len = strlen(rvar->value); apr_table_addn(vartab, rvar->name, (void *)rvar); @@ -1996,12 +2427,24 @@ static int var_time_day_generate(modsec_rec *msr, msre_var *var, msre_rule *rule static int var_time_epoch_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); msre_var *rvar = NULL; time_t tc; tc = time(NULL); rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "TIME_EPOCH: Memory allocation error"); + return -1; + } rvar->value = apr_psprintf(mptmp, "%ld", (long)tc); + if (!rvar->value) { + msr_log(msr, 1, "TIME_EPOCH: Memory allocation error"); + return -1; + } rvar->value_len = strlen(rvar->value); apr_table_addn(vartab, rvar->name, (void *)rvar); @@ -2013,6 +2456,7 @@ static int var_time_epoch_generate(modsec_rec *msr, msre_var *var, msre_rule *ru static int var_query_string_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return var_simple_generate(var, vartab, mptmp, msr->query_string); } @@ -2021,6 +2465,8 @@ static int var_query_string_generate(modsec_rec *msr, msre_var *var, msre_rule * static int var_request_basename_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->r != NULL); char *value = file_basename(mptmp, msr->r->parsed_uri.path); return var_simple_generate(var, vartab, mptmp, value); } @@ -2030,6 +2476,10 @@ static int var_request_basename_generate(modsec_rec *msr, msre_var *var, msre_ru static int var_full_request_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; char *full_request = NULL; int full_request_length = 0; @@ -2056,7 +2506,7 @@ static int var_full_request_generate(modsec_rec *msr, msre_var *var, } goto failed_not_enough_mem; } - memset(full_request, '\0', sizeof(char)*msr->msc_full_request_length); + full_request[0] = '\0'; msr->msc_full_request_buffer = full_request; msr->msc_full_request_length = full_request_length; @@ -2092,6 +2542,10 @@ static int var_full_request_generate(modsec_rec *msr, msre_var *var, static int var_full_request_length_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; char *value = NULL; int headers_length = 0; @@ -2110,6 +2564,7 @@ static int var_full_request_length_generate(modsec_rec *msr, msre_var *var, msre static int var_request_body_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if (msr->msc_reqbody_buffer != NULL) { return var_simple_generate_ex(var, vartab, mptmp, msr->msc_reqbody_buffer, msr->msc_reqbody_length); @@ -2122,6 +2577,7 @@ static int var_request_body_generate(modsec_rec *msr, msre_var *var, msre_rule * static int var_request_body_length_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); char *value = apr_psprintf(mptmp, "%d", msr->msc_reqbody_length); return var_simple_generate(var, vartab, mptmp, value); } @@ -2131,6 +2587,10 @@ static int var_request_body_length_generate(modsec_rec *msr, msre_var *var, msre static int var_matched_vars_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; @@ -2140,14 +2600,14 @@ static int var_matched_vars_names_generate(modsec_rec *msr, msre_var *var, msre_ for (i = 0; i < arr->nelts; i++) { int match = 0; msc_string *str = (msc_string *)te[i].val; + assert(str != NULL); /* Figure out if we want to include this variable. */ if (var->param == NULL) match = 1; else { if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - strlen(str->name), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, str->name, strlen(str->name), &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(str->name, var->param) == 0) match = 1; } @@ -2158,6 +2618,11 @@ static int var_matched_vars_names_generate(modsec_rec *msr, msre_var *var, msre_ msre_var *rvar = apr_palloc(mptmp, sizeof(msre_var)); + rvar->param = NULL; + rvar->param_data = NULL; + rvar->metadata = NULL; + rvar->param_regex = NULL; + rvar->value = apr_pstrndup(mptmp, str->name, strlen(str->name)); rvar->value_len = strlen(rvar->value); rvar->name = apr_psprintf(mptmp, "%s", @@ -2191,23 +2656,28 @@ static int var_matched_vars_names_generate(modsec_rec *msr, msre_var *var, msre_ static int var_matched_vars_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; arr = apr_table_elts(msr->matched_vars); + assert(arr != NULL); te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { int match = 0; msc_string *str = (msc_string *)te[i].val; + assert(str != NULL); /* Figure out if we want to include this variable. */ if (var->param == NULL) match = 1; else { if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, str->name, - strlen(str->name), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, str->name, strlen(str->name), &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(str->name, var->param) == 0) match = 1; } @@ -2218,6 +2688,11 @@ static int var_matched_vars_generate(modsec_rec *msr, msre_var *var, msre_rule * msre_var *rvar = apr_palloc(mptmp, sizeof(msre_var)); + rvar->param = NULL; + rvar->param_data = NULL; + rvar->metadata = NULL; + rvar->param_regex = NULL; + rvar->value = apr_pstrndup(mptmp, str->value, str->value_len); rvar->value_len = str->value_len; rvar->name = apr_psprintf(mptmp, "%s", @@ -2251,11 +2726,16 @@ static int var_matched_vars_generate(modsec_rec *msr, msre_var *var, msre_rule * static int var_request_cookies_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; arr = apr_table_elts(msr->request_cookies); + assert(arr != NULL); te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { int match = 0; @@ -2263,10 +2743,10 @@ static int var_request_cookies_generate(modsec_rec *msr, msre_var *var, msre_rul /* Figure out if we want to include this variable. */ if (var->param == NULL) match = 1; else { + assert(te[i].key != NULL); if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, - strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, te[i].key, strlen(te[i].key), &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(te[i].key, var->param) == 0) match = 1; } @@ -2275,11 +2755,19 @@ static int var_request_cookies_generate(modsec_rec *msr, msre_var *var, msre_rul /* If we had a match add this argument to the collection. */ if (match) { msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "REQUEST_COOKIES: Memory allocation error"); + return -1; + } rvar->value = te[i].val; rvar->value_len = strlen(rvar->value); rvar->name = apr_psprintf(mptmp, "REQUEST_COOKIES:%s", log_escape_nq(mptmp, te[i].key)); + if (!rvar->name) { + msr_log(msr, 1, "REQUEST_COOKIES: Memory allocation error"); + return -1; + } apr_table_addn(vartab, rvar->name, (void *)rvar); count++; @@ -2294,11 +2782,16 @@ static int var_request_cookies_generate(modsec_rec *msr, msre_var *var, msre_rul static int var_request_cookies_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; arr = apr_table_elts(msr->request_cookies); + assert(arr != NULL); te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { int match = 0; @@ -2306,10 +2799,10 @@ static int var_request_cookies_names_generate(modsec_rec *msr, msre_var *var, ms /* Figure out if we want to include this variable. */ if (var->param == NULL) match = 1; else { + assert(te[i].key != NULL); if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, - strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, te[i].key, strlen(te[i].key), &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(te[i].key, var->param) == 0) match = 1; } @@ -2318,11 +2811,19 @@ static int var_request_cookies_names_generate(modsec_rec *msr, msre_var *var, ms /* If we had a match add this argument to the collection. */ if (match) { msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "REQUEST_COOKIES_NAMES: Memory allocation error"); + return -1; + } rvar->value = te[i].key; rvar->value_len = strlen(rvar->value); rvar->name = apr_psprintf(mptmp, "REQUEST_COOKIES_NAMES:%s", log_escape_nq(mptmp, te[i].key)); + if (!rvar->name) { + msr_log(msr, 1, "REQUEST_COOKIES_NAMES: Memory allocation error"); + return -1; + } apr_table_addn(vartab, rvar->name, (void *)rvar); count++; @@ -2337,11 +2838,16 @@ static int var_request_cookies_names_generate(modsec_rec *msr, msre_var *var, ms static int var_request_headers_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; arr = apr_table_elts(msr->request_headers); + assert(arr != NULL); te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { int match = 0; @@ -2349,10 +2855,10 @@ static int var_request_headers_generate(modsec_rec *msr, msre_var *var, msre_rul /* Figure out if we want to include this variable. */ if (var->param == NULL) match = 1; else { + assert(te[i].key != NULL); if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, - strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, te[i].key, strlen(te[i].key), &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(te[i].key, var->param) == 0) match = 1; } @@ -2361,11 +2867,19 @@ static int var_request_headers_generate(modsec_rec *msr, msre_var *var, msre_rul /* If we had a match add this argument to the collection. */ if (match) { msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "REQUEST_HEADERS: Memory allocation error"); + return -1; + } rvar->value = te[i].val; rvar->value_len = strlen(rvar->value); rvar->name = apr_psprintf(mptmp, "REQUEST_HEADERS:%s", log_escape_nq(mptmp, te[i].key)); + if (!rvar->name) { + msr_log(msr, 1, "REQUEST_HEADERS: Memory allocation error"); + return -1; + } apr_table_addn(vartab, rvar->name, (void *)rvar); count++; @@ -2380,11 +2894,16 @@ static int var_request_headers_generate(modsec_rec *msr, msre_var *var, msre_rul static int var_request_headers_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; arr = apr_table_elts(msr->request_headers); + assert(arr != NULL); te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { int match = 0; @@ -2392,10 +2911,10 @@ static int var_request_headers_names_generate(modsec_rec *msr, msre_var *var, ms /* Figure out if we want to include this variable. */ if (var->param == NULL) match = 1; else { + assert(te[i].key != NULL); if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, - strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, te[i].key, strlen(te[i].key), &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(te[i].key, var->param) == 0) match = 1; } @@ -2404,11 +2923,19 @@ static int var_request_headers_names_generate(modsec_rec *msr, msre_var *var, ms /* If we had a match add this argument to the collection. */ if (match) { msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "REQUEST_HEADERS_NAMES: Memory allocation error"); + return -1; + } rvar->value = te[i].key; rvar->value_len = strlen(rvar->value); rvar->name = apr_psprintf(mptmp, "REQUEST_HEADERS_NAMES:%s", log_escape_nq(mptmp, te[i].key)); + if (!rvar->name) { + msr_log(msr, 1, "REQUEST_HEADERS_NAMES: Memory allocation error"); + return -1; + } apr_table_addn(vartab, rvar->name, (void *)rvar); count++; @@ -2423,6 +2950,8 @@ static int var_request_headers_names_generate(modsec_rec *msr, msre_var *var, ms static int var_request_filename_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->r != NULL); return var_simple_generate(var, vartab, mptmp, msr->r->parsed_uri.path); } @@ -2431,6 +2960,7 @@ static int var_request_filename_generate(modsec_rec *msr, msre_var *var, msre_ru static int var_request_line_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return var_simple_generate(var, vartab, mptmp, msr->request_line); } @@ -2439,6 +2969,7 @@ static int var_request_line_generate(modsec_rec *msr, msre_var *var, msre_rule * static int var_request_method_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return var_simple_generate(var, vartab, mptmp, msr->request_method); } @@ -2447,6 +2978,7 @@ static int var_request_method_generate(modsec_rec *msr, msre_var *var, msre_rule static int var_request_protocol_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return var_simple_generate(var, vartab, mptmp, msr->request_protocol); } @@ -2455,6 +2987,7 @@ static int var_request_protocol_generate(modsec_rec *msr, msre_var *var, msre_ru static int var_server_addr_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return var_simple_generate(var, vartab, mptmp, msr->local_addr); } @@ -2463,6 +2996,7 @@ static int var_server_addr_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_server_name_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); return var_simple_generate(var, vartab, mptmp, msr->hostname); } @@ -2471,7 +3005,12 @@ static int var_server_name_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_server_port_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); char *value = apr_psprintf(mptmp, "%u", msr->local_port); + if (!value) { + msr_log(msr, 1, "SERVER_PORT: Memory allocation error"); + return -1; + } return var_simple_generate(var, vartab, mptmp, value); } @@ -2480,6 +3019,8 @@ static int var_server_port_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_script_basename_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->r != NULL); char *value = file_basename(mptmp, msr->r->filename); return var_simple_generate(var, vartab, mptmp, value); } @@ -2489,6 +3030,8 @@ static int var_script_basename_generate(modsec_rec *msr, msre_var *var, msre_rul static int var_script_filename_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->r != NULL); char *value = msr->r->filename; return var_simple_generate(var, vartab, mptmp, value); } @@ -2498,7 +3041,13 @@ static int var_script_filename_generate(modsec_rec *msr, msre_var *var, msre_rul static int var_script_gid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->r != NULL); char *value = apr_psprintf(mptmp, "%ld", (long)msr->r->finfo.group); + if (!value) { + msr_log(msr, 1, "SCRIPT_GID: Memory allocation error"); + return -1; + } return var_simple_generate(var, vartab, mptmp, value); } @@ -2507,6 +3056,9 @@ static int var_script_gid_generate(modsec_rec *msr, msre_var *var, msre_rule *ru static int var_script_groupname_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->r != NULL); + assert(mptmp != NULL); char *value = NULL; if (apr_gid_name_get(&value, msr->r->finfo.group, mptmp) == APR_SUCCESS) { return var_simple_generate(var, vartab, mptmp, value); @@ -2519,6 +3071,9 @@ static int var_script_groupname_generate(modsec_rec *msr, msre_var *var, msre_ru static int var_script_mode_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->r != NULL); + assert(mptmp != NULL); char *value = apr_psprintf(mptmp, "%04x", msr->r->finfo.protection); return var_simple_generate(var, vartab, mptmp, value); } @@ -2528,6 +3083,9 @@ static int var_script_mode_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_script_uid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->r != NULL); + assert(mptmp != NULL); char *value = apr_psprintf(mptmp, "%ld", (long)msr->r->finfo.user); return var_simple_generate(var, vartab, mptmp, value); } @@ -2537,6 +3095,9 @@ static int var_script_uid_generate(modsec_rec *msr, msre_var *var, msre_rule *ru static int var_script_username_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->r != NULL); + assert(mptmp != NULL); char *value = NULL; if (apr_uid_name_get(&value, msr->r->finfo.user, mptmp) == APR_SUCCESS) { return var_simple_generate(var, vartab, mptmp, value); @@ -2549,6 +3110,8 @@ static int var_script_username_generate(modsec_rec *msr, msre_var *var, msre_rul static int var_auth_type_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->r != NULL); char *value = msr->r->ap_auth_type; return var_simple_generate(var, vartab, mptmp, value); } @@ -2558,6 +3121,9 @@ static int var_auth_type_generate(modsec_rec *msr, msre_var *var, msre_rule *rul static int var_path_info_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->r != NULL); + assert(mptmp != NULL); const char *value = msr->r->path_info; return var_simple_generate(var, vartab, mptmp, value); } @@ -2567,6 +3133,7 @@ static int var_path_info_generate(modsec_rec *msr, msre_var *var, msre_rule *rul static int var_stream_output_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if (msr->stream_output_data != NULL) { return var_simple_generate_ex(var, vartab, mptmp, msr->stream_output_data, msr->stream_output_length); @@ -2580,6 +3147,7 @@ static int var_stream_output_generate(modsec_rec *msr, msre_var *var, msre_rule static int var_stream_input_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if (msr->stream_input_data != NULL) { return var_simple_generate_ex(var, vartab, mptmp, msr->stream_input_data, msr->stream_input_length); @@ -2593,6 +3161,7 @@ static int var_stream_input_generate(modsec_rec *msr, msre_var *var, msre_rule * static int var_response_body_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); if (msr->resbody_data != NULL) { return var_simple_generate_ex(var, vartab, mptmp, msr->resbody_data, msr->resbody_length); @@ -2606,6 +3175,10 @@ static int var_response_body_generate(modsec_rec *msr, msre_var *var, msre_rule static int var_response_headers_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; @@ -2613,6 +3186,7 @@ static int var_response_headers_generate(modsec_rec *msr, msre_var *var, msre_ru if (msr->response_headers == NULL) return 0; arr = apr_table_elts(msr->response_headers); + assert(arr != NULL); te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { int match = 0; @@ -2620,10 +3194,10 @@ static int var_response_headers_generate(modsec_rec *msr, msre_var *var, msre_ru /* Figure out if we want to include this variable. */ if (var->param == NULL) match = 1; else { + assert(te[i].key != NULL); if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, - strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, te[i].key, strlen(te[i].key), &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(te[i].key, var->param) == 0) match = 1; } @@ -2632,11 +3206,19 @@ static int var_response_headers_generate(modsec_rec *msr, msre_var *var, msre_ru /* If we had a match add this argument to the collection. */ if (match) { msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "RESPONSE_HEADERS: Memory allocation error"); + return -1; + } rvar->value = te[i].val; rvar->value_len = strlen(rvar->value); rvar->name = apr_psprintf(mptmp, "RESPONSE_HEADERS:%s", log_escape_nq(mptmp, te[i].key)); + if (!rvar->name) { + msr_log(msr, 1, "RESPONSE_HEADERS: Memory allocation error"); + return -1; + } apr_table_addn(vartab, rvar->name, (void *)rvar); count++; @@ -2651,11 +3233,16 @@ static int var_response_headers_generate(modsec_rec *msr, msre_var *var, msre_ru static int var_response_headers_names_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(var != NULL); + assert(vartab != NULL); + assert(mptmp != NULL); const apr_array_header_t *arr = NULL; const apr_table_entry_t *te = NULL; int i, count = 0; arr = apr_table_elts(msr->response_headers); + assert(arr != NULL); te = (apr_table_entry_t *)arr->elts; for (i = 0; i < arr->nelts; i++) { int match = 0; @@ -2663,10 +3250,10 @@ static int var_response_headers_names_generate(modsec_rec *msr, msre_var *var, m /* Figure out if we want to include this variable. */ if (var->param == NULL) match = 1; else { + assert(te[i].key != NULL); if (var->param_data != NULL) { /* Regex. */ char *my_error_msg = NULL; - if (!(msc_regexec((msc_regex_t *)var->param_data, te[i].key, - strlen(te[i].key), &my_error_msg) == PCRE_ERROR_NOMATCH)) match = 1; + if (msc_regexec((msc_regex_t *)var->param_data, te[i].key, strlen(te[i].key), &my_error_msg) >= 0) match = 1; } else { /* Simple comparison. */ if (strcasecmp(te[i].key, var->param) == 0) match = 1; } @@ -2675,11 +3262,19 @@ static int var_response_headers_names_generate(modsec_rec *msr, msre_var *var, m /* If we had a match add this argument to the collection. */ if (match) { msre_var *rvar = apr_pmemdup(mptmp, var, sizeof(msre_var)); + if (!rvar) { + msr_log(msr, 1, "RESPONSE_HEADERS_NAMES: Memory allocation error"); + return -1; + } rvar->value = te[i].key; rvar->value_len = strlen(rvar->value); rvar->name = apr_psprintf(mptmp, "RESPONSE_HEADERS_NAMES:%s", log_escape_nq(mptmp, te[i].key)); + if (!rvar) { + msr_log(msr, 1, "RESPONSE_HEADERS_NAMES: Memory allocation error"); + return -1; + } apr_table_addn(vartab, rvar->name, (void *)rvar); count++; @@ -2694,6 +3289,7 @@ static int var_response_headers_names_generate(modsec_rec *msr, msre_var *var, m static int var_status_line_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); const char *value = msr->status_line; return var_simple_generate(var, vartab, mptmp, value); } @@ -2703,6 +3299,7 @@ static int var_status_line_generate(modsec_rec *msr, msre_var *var, msre_rule *r static int var_response_protocol_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); const char *value = msr->response_protocol; return var_simple_generate(var, vartab, mptmp, value); } @@ -2712,6 +3309,7 @@ static int var_response_protocol_generate(modsec_rec *msr, msre_var *var, msre_r static int var_response_status_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); const char *value = apr_psprintf(mptmp, "%u", msr->response_status); return var_simple_generate(var, vartab, mptmp, value); } @@ -2721,6 +3319,8 @@ static int var_response_status_generate(modsec_rec *msr, msre_var *var, msre_rul static int var_response_content_type(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->r != NULL); return var_simple_generate(var, vartab, mptmp, msr->r->content_type); } @@ -2729,6 +3329,8 @@ static int var_response_content_type(modsec_rec *msr, msre_var *var, msre_rule * static int var_response_content_length(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->r != NULL); const char *value = apr_psprintf(mptmp, "%" APR_OFF_T_FMT, msr->r->clength); return var_simple_generate(var, vartab, mptmp, value); } @@ -2738,6 +3340,7 @@ static int var_response_content_length(modsec_rec *msr, msre_var *var, msre_rule static int var_userid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); const char *value = msr->userid; return var_simple_generate(var, vartab, mptmp, value); } @@ -2747,6 +3350,7 @@ static int var_userid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, static int var_sessionid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); const char *value = msr->sessionid; return var_simple_generate(var, vartab, mptmp, value); } @@ -2756,6 +3360,8 @@ static int var_sessionid_generate(modsec_rec *msr, msre_var *var, msre_rule *rul static int var_webappid_generate(modsec_rec *msr, msre_var *var, msre_rule *rule, apr_table_t *vartab, apr_pool_t *mptmp) { + assert(msr != NULL); + assert(msr->txcfg != NULL); const char *value = msr->txcfg->webappid; return var_simple_generate(var, vartab, mptmp, value); } @@ -2770,6 +3376,7 @@ void msre_engine_variable_register(msre_engine *engine, const char *name, fn_var_validate_t validate, fn_var_generate_t generate, unsigned int is_cacheable, unsigned int availability) { + assert(engine != NULL); msre_var_metadata *metadata = (msre_var_metadata *)apr_pcalloc(engine->mp, sizeof(msre_var_metadata)); if (metadata == NULL) return; @@ -2790,6 +3397,7 @@ void msre_engine_variable_register(msre_engine *engine, const char *name, * */ void msre_engine_register_default_variables(msre_engine *engine) { + assert(engine != NULL); /* ARGS */ msre_engine_variable_register(engine, @@ -2956,6 +3564,17 @@ void msre_engine_register_default_variables(msre_engine *engine) { PHASE_REQUEST_BODY ); + /* MULTIPART_PART_HEADERS */ + msre_engine_variable_register(engine, + "MULTIPART_PART_HEADERS", + VAR_LIST, + 0, 1, + var_generic_list_validate, + var_multipart_part_headers_generate, + VAR_CACHE, + PHASE_REQUEST_BODY + ); + /* GEO */ msre_engine_variable_register(engine, "GEO", diff --git a/build/compile b/build/compile index 1b1d232169..49b3d05fde 100755 --- a/build/compile +++ b/build/compile @@ -1,9 +1,9 @@ #! /bin/sh -# Wrapper for compilers which do not understand `-c -o'. +# Wrapper for compilers which do not understand '-c -o'. -scriptversion=2005-05-14.22 +scriptversion=2024-06-19.01; # UTC -# Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. +# Copyright (C) 1999-2024 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify @@ -17,8 +17,7 @@ scriptversion=2005-05-14.22 # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -29,35 +28,244 @@ scriptversion=2005-05-14.22 # bugs to or send patches to # . +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN* | MSYS*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/* | msys/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.lo | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + case $1 in '') - echo "$0: No command. Try \`$0 --help' for more information." 1>&2 + echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] -Wrapper for compilers which do not understand `-c -o'. -Remove `-o dest.o' from ARGS, run PROGRAM with the remaining +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the -right script to run: please start by reading the file `INSTALL'. +right script to run: please start by reading the file 'INSTALL'. Report bugs to . +GNU Automake home page: . +General help using GNU software: . EOF exit $? ;; -v | --v*) - echo "compile $scriptversion" + echo "compile (GNU Automake) $scriptversion" exit $? ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ + clang-cl | *[/\\]clang-cl | clang-cl.exe | *[/\\]clang-cl.exe | \ + icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; esac ofile= cfile= -eat= for arg do @@ -66,8 +274,8 @@ do else case $1 in -o) - # configure might choose to run compile as `compile cc -o foo foo.c'. - # So we strip `-o arg' only if arg is an object. + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) @@ -94,22 +302,22 @@ do done if test -z "$ofile" || test -z "$cfile"; then - # If no `-o' option was seen then we might have been invoked from a + # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no - # `.c' file was seen then we are probably linking. That is also + # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. -cofile=`echo "$cfile" | sed -e 's|^.*/||' -e 's/\.c$/.o/'` +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. -# Note: use `[/.-]' here to ensure that we don't use the same name +# Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. -lockdir=`echo "$cofile" | sed -e 's|[/.-]|_|g'`.d +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break @@ -124,9 +332,9 @@ trap "rmdir '$lockdir'; exit 1" 1 2 15 ret=$? if test -f "$cofile"; then - mv "$cofile" "$ofile" + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then - mv "${cofile}bj" "$ofile" + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" @@ -135,8 +343,9 @@ exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-end: "$" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" # End: diff --git a/build/find_apr.m4 b/build/find_apr.m4 index 5524b66285..a303696586 100644 --- a/build/find_apr.m4 +++ b/build/find_apr.m4 @@ -18,7 +18,7 @@ AC_DEFUN([CHECK_APR], AC_ARG_WITH( apr, - [AC_HELP_STRING([--with-apr=PATH],[Path to apr prefix or config script])], + [AS_HELP_STRING([--with-apr=PATH],[Path to apr prefix or config script])], [test_paths="${with_apr}"], [test_paths="/usr/local/libapr /usr/local/apr /usr/local /opt/libapr /opt/apr /opt /usr"]) diff --git a/build/find_apu.m4 b/build/find_apu.m4 index 4a5e6e5549..956a159cb7 100644 --- a/build/find_apu.m4 +++ b/build/find_apu.m4 @@ -18,7 +18,7 @@ AC_DEFUN([CHECK_APU], AC_ARG_WITH( apu, - [AC_HELP_STRING([--with-apu=PATH],[Path to apu prefix or config script])], + [AS_HELP_STRING([--with-apu=PATH],[Path to apu prefix or config script])], [test_paths="${with_apu}"], [test_paths="/usr/local/libapr-util /usr/local/apr-util /usr/local/libapu /usr/local/apu /usr/local/apr /usr/local /opt/libapr-util /opt/apr-util /opt/libapu /opt/apu /opt /usr"]) diff --git a/build/find_curl.m4 b/build/find_curl.m4 index 3310e40494..1cee35bba2 100644 --- a/build/find_curl.m4 +++ b/build/find_curl.m4 @@ -18,7 +18,7 @@ AC_DEFUN([CHECK_CURL], AC_ARG_WITH( curl, - [AC_HELP_STRING([--with-curl=PATH],[Path to curl prefix or config script])], + [AS_HELP_STRING([--with-curl=PATH],[Path to curl prefix or config script])], [test_paths="${with_curl}"], [test_paths="/usr/local/libcurl /usr/local/curl /usr/local /opt/libcurl /opt/curl /opt /usr"]) @@ -54,7 +54,7 @@ if test -n "${curl_path}"; then CURL_CONFIG="${curl_path}/${CURL_CONFIG}" fi AC_MSG_RESULT([${CURL_CONFIG}]) - CURL_VERSION=`${CURL_CONFIG} --version | sed 's/^[[^0-9]][[^[:space:]]][[^[:space:]]]*[[[:space:]]]*//'` + CURL_VERSION=`${CURL_CONFIG} --version | sed 's/^[[^0-9]][[^[:space:]]][[^[:space:]]]*[[[:space:]]]*//' | tr '\r\n' ' '` if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(curl VERSION: $CURL_VERSION); fi CURL_CFLAGS="`${CURL_CONFIG} --cflags`" if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(curl CFLAGS: $CURL_CFLAGS); fi diff --git a/build/find_lua.m4 b/build/find_lua.m4 index acb903e5fa..664bc3e3fa 100644 --- a/build/find_lua.m4 +++ b/build/find_lua.m4 @@ -21,7 +21,7 @@ LUA_SONAMES="so la sl dll dylib a" AC_ARG_WITH( lua, - [AC_HELP_STRING([--with-lua=PATH],[Path to lua prefix or config script])] + [AS_HELP_STRING([--with-lua=PATH],[Path to lua prefix or config script])] ,, with_lua=yes) AS_CASE(["${with_lua}"], diff --git a/build/find_pcre.m4 b/build/find_pcre.m4 index f5da40a327..9b222f67fc 100644 --- a/build/find_pcre.m4 +++ b/build/find_pcre.m4 @@ -17,74 +17,76 @@ AC_DEFUN([CHECK_PCRE], AC_ARG_WITH( pcre, - [AC_HELP_STRING([--with-pcre=PATH],[Path to pcre prefix or config script])], + [AS_HELP_STRING([--with-pcre=PATH],[Path to pcre prefix or config script])], [test_paths="${with_pcre}"], - [test_paths="/usr/local/libpcre /usr/local/pcre /usr/local /opt/libpcre /opt/pcre /opt /usr"]) + [with_pcre="no"]) -AC_MSG_CHECKING([for libpcre config script]) +AS_CASE(["${with_pcre}"], + [no], [test_paths=], + [yes], [test_paths="/usr/local/libpcre /usr/local/pcre /usr/local /opt/libpcre /opt/pcre /opt /usr"], + [test_paths="${with_pcre}"]) -dnl # Determine pcre lib directory -if test -z "${with_pcre}"; then - test_paths="/usr/local/pcre /usr/local /usr" +if test "x${with_pcre}" = "x" || test "x${with_pcre}" = "xno"; then + AC_MSG_NOTICE([pcre not specified; omitting check for pcre]) else - test_paths="${with_pcre}" -fi - -for x in ${test_paths}; do - dnl # Determine if the script was specified and use it directly - if test ! -d "$x" -a -e "$x"; then - PCRE_CONFIG=$x - pcre_path="no" - break - fi + AC_MSG_CHECKING([for libpcre config script]) - dnl # Try known config script names/locations - for PCRE_CONFIG in pcre-config; do - if test -e "${x}/bin/${PCRE_CONFIG}"; then - pcre_path="${x}/bin" + for x in ${test_paths}; do + dnl # Determine if the script was specified and use it directly + if test ! -d "$x" -a -e "$x"; then + PCRE_CONFIG=$x + pcre_path="no" break - elif test -e "${x}/${PCRE_CONFIG}"; then - pcre_path="${x}" + fi + + dnl # Try known config script names/locations + for PCRE_CONFIG in pcre-config; do + if test -e "${x}/bin/${PCRE_CONFIG}"; then + pcre_path="${x}/bin" + break + elif test -e "${x}/${PCRE_CONFIG}"; then + pcre_path="${x}" + break + else + pcre_path="" + fi + done + if test -n "$pcre_path"; then break - else - pcre_path="" fi done - if test -n "$pcre_path"; then - break - fi -done -if test -n "${pcre_path}"; then - if test "${pcre_path}" != "no"; then - PCRE_CONFIG="${pcre_path}/${PCRE_CONFIG}" + if test -n "${pcre_path}"; then + if test "${pcre_path}" != "no"; then + PCRE_CONFIG="${pcre_path}/${PCRE_CONFIG}" + fi + AC_MSG_RESULT([${PCRE_CONFIG}]) + PCRE_VERSION="`${PCRE_CONFIG} --version`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre VERSION: $PCRE_VERSION); fi + PCRE_CFLAGS="`${PCRE_CONFIG} --cflags`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre CFLAGS: $PCRE_CFLAGS); fi + PCRE_LDADD="`${PCRE_CONFIG} --libs`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre LDADD: $PCRE_LDADD); fi + PCRE_LD_PATH="/`${PCRE_CONFIG} --libs | cut -d'/' -f2,3,4,5,6 | cut -d ' ' -f1`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre PCRE_LD_PATH: $PCRE_LD_PATH); fi + else + AC_MSG_RESULT([no]) fi - AC_MSG_RESULT([${PCRE_CONFIG}]) - PCRE_VERSION="`${PCRE_CONFIG} --version`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre VERSION: $PCRE_VERSION); fi - PCRE_CFLAGS="`${PCRE_CONFIG} --cflags`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre CFLAGS: $PCRE_CFLAGS); fi - PCRE_LDADD="`${PCRE_CONFIG} --libs`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre LDADD: $PCRE_LDADD); fi - PCRE_LD_PATH="/`${PCRE_CONFIG} --libs | cut -d'/' -f2,3,4,5,6 | cut -d ' ' -f1`" - if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre PCRE_LD_PATH: $PCRE_LD_PATH); fi -else - AC_MSG_RESULT([no]) -fi -AC_SUBST(PCRE_CONFIG) -AC_SUBST(PCRE_VERSION) -AC_SUBST(PCRE_CPPFLAGS) -AC_SUBST(PCRE_CFLAGS) -AC_SUBST(PCRE_LDFLAGS) -AC_SUBST(PCRE_LDADD) -AC_SUBST(PCRE_LD_PATH) + AC_SUBST(PCRE_CONFIG) + AC_SUBST(PCRE_VERSION) + AC_SUBST(PCRE_CPPFLAGS) + AC_SUBST(PCRE_CFLAGS) + AC_SUBST(PCRE_LDFLAGS) + AC_SUBST(PCRE_LDADD) + AC_SUBST(PCRE_LD_PATH) -if test -z "${PCRE_VERSION}"; then - AC_MSG_NOTICE([*** pcre library not found.]) - ifelse([$2], , AC_MSG_ERROR([pcre library is required]), $2) -else - AC_MSG_NOTICE([using pcre v${PCRE_VERSION}]) - ifelse([$1], , , $1) -fi + if test -z "${PCRE_VERSION}"; then + AC_MSG_NOTICE([*** pcre library not found.]) + else + AC_MSG_NOTICE([using pcre v${PCRE_VERSION}]) + PCRE_CFLAGS="-DWITH_PCRE ${PCRE_CFLAGS}" + ifelse([$1], , , $1) + fi +fi ]) diff --git a/build/find_pcre2.m4 b/build/find_pcre2.m4 new file mode 100644 index 0000000000..58cb79c442 --- /dev/null +++ b/build/find_pcre2.m4 @@ -0,0 +1,93 @@ +dnl Check for PCRE2 Libraries +dnl CHECK_PCRE2(ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]) +dnl Sets: +dnl PCRE2_CFLAGS +dnl PCRE2_LIBS + +PCRE2_CONFIG="" +PCRE2_VERSION="" +PCRE2_CPPFLAGS="" +PCRE2_CFLAGS="" +PCRE2_LDFLAGS="" +PCRE2_LDADD="" +PCRE_LD_PATH="" + +AC_DEFUN([CHECK_PCRE2], +[dnl + +AC_ARG_WITH( + pcre2, + [AS_HELP_STRING([--with-pcre2=PATH],[Path to pcre2 prefix or config script])], + [test_paths="${with_pcre2}"], + [with_pcre2="yes"]) + +AS_CASE(["${with_pcre2}"], + [no], [test_paths=], + [yes], [test_paths="/usr/local/libpcre2 /usr/local/pcre2 /usr/local /opt/libpcre2 /opt/pcre2 /opt /usr"], + [test_paths="${with_pcre2}"]) + +if test "x${with_pcre}" != "x" && test "x${with_pcre}" != "xno"; then + AC_MSG_NOTICE([pcre specified; omitting check for pcre2]) +else + AC_MSG_CHECKING([for libpcre2 config script]) + + for x in ${test_paths}; do + dnl # Determine if the script was specified and use it directly + if test ! -d "$x" -a -e "$x"; then + PCRE2_CONFIG=$x + pcre2_path="no" + break + fi + + dnl # Try known config script names/locations + for PCRE2_CONFIG in pcre2-config; do + if test -e "${x}/bin/${PCRE2_CONFIG}"; then + pcre2_path="${x}/bin" + break + elif test -e "${x}/${PCRE2_CONFIG}"; then + pcre2_path="${x}" + break + else + pcre2_path="" + fi + done + if test -n "$pcre2_path"; then + break + fi + done + + if test -n "${pcre2_path}"; then + if test "${pcre2_path}" != "no"; then + PCRE2_CONFIG="${pcre2_path}/${PCRE2_CONFIG}" + fi + AC_MSG_RESULT([${PCRE2_CONFIG}]) + PCRE2_VERSION="`${PCRE2_CONFIG} --version`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre2 VERSION: $PCRE2_VERSION); fi + PCRE2_CFLAGS="`${PCRE2_CONFIG} --cflags`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre2 CFLAGS: $PCRE2_CFLAGS); fi + PCRE2_LDADD="`${PCRE2_CONFIG} --libs8`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre2 LDADD: $PCRE2_LDADD); fi + PCRE_LD_PATH="/`${PCRE2_CONFIG} --libs8 | cut -d'/' -f2,3,4,5,6 | cut -d ' ' -f1`" + if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(pcre2 PCRE_LD_PATH: $PCRE_LD_PATH); fi + else + AC_MSG_RESULT([no]) + fi + + AC_SUBST(PCRE2_CONFIG) + AC_SUBST(PCRE2_VERSION) + AC_SUBST(PCRE2_CPPFLAGS) + AC_SUBST(PCRE2_CFLAGS) + AC_SUBST(PCRE2_LDFLAGS) + AC_SUBST(PCRE2_LDADD) + AC_SUBST(PCRE_LD_PATH) + + if test -z "${PCRE2_VERSION}"; then + AC_MSG_NOTICE([*** pcre2 library not found.]) + ifelse([$2], , AC_MSG_ERROR([pcre2 library is required]), $2) + else + AC_MSG_NOTICE([using pcre2 v${PCRE2_VERSION}]) + PCRE2_CFLAGS="${PCRE2_CFLAGS}" + ifelse([$1], , , $1) + fi +fi +]) diff --git a/build/find_ssdeep.m4 b/build/find_ssdeep.m4 index 4b0c9aa970..08d3ef959b 100644 --- a/build/find_ssdeep.m4 +++ b/build/find_ssdeep.m4 @@ -13,7 +13,7 @@ SSDEEP_LDADD="" AC_ARG_WITH( ssdeep, - [AC_HELP_STRING([--with-ssdeep=PATH],[Path to ssdeep prefix])] + [AS_HELP_STRING([--with-ssdeep=PATH],[Path to ssdeep prefix])] ,, with_ssdeep=yes) AS_CASE(["${with_ssdeep}"], diff --git a/build/find_xml.m4 b/build/find_xml.m4 index 691679e467..cd410faf4d 100644 --- a/build/find_xml.m4 +++ b/build/find_xml.m4 @@ -4,21 +4,7 @@ dnl Sets: dnl LIBXML2_CFLAGS dnl LIBXML2_LIBS -LIBXML2_CONFIG="" -LIBXML2_VERSION="" -LIBXML2_CFLAGS="" -LIBXML2_CPPFLAGS="" -LIBXML2_LDADD="" -LIBXML2_LDFLAGS="" - -AC_DEFUN([CHECK_LIBXML2], -[dnl - -AC_ARG_WITH( - libxml, - [AC_HELP_STRING([--with-libxml=PATH],[Path to libxml2 prefix or config script])], - [test_paths="${with_libxml}"], - [test_paths="/usr/local/libxml2 /usr/local/xml2 /usr/local/xml /usr/local /opt/libxml2 /opt/libxml /opt/xml2 /opt/xml /opt /usr"]) +AC_DEFUN([CHECK_XML2CONFIG], [ AC_MSG_CHECKING([for libxml2 config script]) @@ -59,19 +45,56 @@ if test -n "${libxml2_path}"; then LIBXML2_LDADD="`${LIBXML2_CONFIG} --libs`" if test "$verbose_output" -eq 1; then AC_MSG_NOTICE(xml LDADD: $LIBXML2_LDADD); fi - AC_MSG_CHECKING([if libxml2 is at least v2.6.29]) - libxml2_min_ver=`echo 2.6.29 | awk -F. '{print (\$ 1 * 1000000) + (\$ 2 * 1000) + \$ 3}'` + AC_MSG_CHECKING([if libxml2 is at least v${LIBXML2_MIN_VERSION}]) + libxml2_min_ver=`echo ${LIBXML2_MIN_VERSION} | awk -F. '{print (\$ 1 * 1000000) + (\$ 2 * 1000) + \$ 3}'` libxml2_ver=`echo ${LIBXML2_VERSION} | awk -F. '{print (\$ 1 * 1000000) + (\$ 2 * 1000) + \$ 3}'` if test "$libxml2_ver" -ge "$libxml2_min_ver"; then AC_MSG_RESULT([yes, $LIBXML2_VERSION]) else AC_MSG_RESULT([no, $LIBXML2_VERSION]) - AC_MSG_ERROR([NOTE: libxml2 library must be at least 2.6.29]) + AC_MSG_ERROR([NOTE: libxml2 library must be at least ${LIBXML2_MIN_VERSION}]) fi else AC_MSG_RESULT([no]) fi +]) + +AC_DEFUN([CHECK_LIBXML2], [ + +AC_ARG_WITH( + libxml, + [AS_HELP_STRING([--with-libxml=PATH],[Path to libxml2 prefix or config script])], + [test_paths="${with_libxml}"], + [test_paths="/usr/local/libxml2 /usr/local/xml2 /usr/local/xml /usr/local /opt/libxml2 /opt/libxml /opt/xml2 /opt/xml /opt /usr"]) + +LIBXML2_MIN_VERSION="2.6.29" +LIBXML2_PKG_NAME="libxml-2.0" +LIBXML2_CONFIG="" +LIBXML2_VERSION="" +LIBXML2_CFLAGS="" +LIBXML2_CPPFLAGS="" +LIBXML2_LDADD="" +LIBXML2_LDFLAGS="" + +if test "x${with_libxml}" != "xno"; then + if test -n "${PKG_CONFIG}"; then + AC_MSG_CHECKING([for libxml2 >= ${LIBXML2_MIN_VERSION} via pkg-config]) + if `${PKG_CONFIG} --exists "${LIBXML2_PKG_NAME} >= ${LIBXML2_MIN_VERSION}"`; then + LIBXML2_VERSION="`${PKG_CONFIG} --modversion ${LIBXML2_PKG_NAME}`" + LIBXML2_CFLAGS="`${PKG_CONFIG} --cflags ${LIBXML2_PKG_NAME}`" + LIBXML2_LDADD="`${PKG_CONFIG} --libs-only-l ${LIBXML2_PKG_NAME}`" + LIBXML2_LDFLAGS="`${PKG_CONFIG} --libs-only-L --libs-only-other ${LIBXML2_PKG_NAME}`" + AC_MSG_RESULT([found version ${LIBXML2_VERSION}]) + else + AC_MSG_RESULT([not found]) + fi + fi + + if test -z "${LIBXML2_VERSION}"; then + CHECK_XML2CONFIG + fi +fi AC_SUBST(LIBXML2_CONFIG) AC_SUBST(LIBXML2_VERSION) diff --git a/build/find_yajl.m4 b/build/find_yajl.m4 index 132a8a8a90..e0934e8496 100644 --- a/build/find_yajl.m4 +++ b/build/find_yajl.m4 @@ -23,7 +23,7 @@ YAJL_SONAMES="so la sl dll dylib" AC_ARG_WITH( yajl, - [AC_HELP_STRING([--with-yajl=PATH],[Path to yajl prefix or config script])] + [AS_HELP_STRING([--with-yajl=PATH],[Path to yajl prefix or config script])] ,, with_yajl=yes) AS_CASE(["${with_yajl}"], @@ -89,10 +89,14 @@ else yajl_lib_path="${x}/" yajl_lib_name="yajl" break - else + elif test -e "${x}/lib/libyajl.${y}"; then + yajl_lib_path="${x}/lib/" + yajl_lib_name="yajl" + break + else yajl_lib_path="" yajl_lib_name="" - fi + fi done if test -n "$yajl_lib_path"; then break diff --git a/configure.ac b/configure.ac index 3180547cc5..7103729163 100644 --- a/configure.ac +++ b/configure.ac @@ -30,9 +30,9 @@ AC_PROG_MAKE_SET AC_PROG_GREP AC_PATH_PROGS(PERL, [perl perl5], ) AC_PATH_PROGS(ENV_CMD, [env printenv], ) +PKG_PROG_PKG_CONFIG # Checks for header files. -AC_HEADER_STDC AC_CHECK_HEADERS([fcntl.h limits.h stdlib.h string.h unistd.h sys/types.h sys/stat.h sys/utsname.h]) # Checks for typedefs, structures, and compiler characteristics. @@ -244,7 +244,7 @@ AC_ARG_ENABLE(mlogc, CHECK_CURL() if test -z "${CURL_VERSION}"; then - AC_MSG_NOTICE([NOTE: mlgoc compilation was disabled.]) + AC_MSG_NOTICE([NOTE: mlogc compilation was disabled.]) build_mlogc=0 fi @@ -305,6 +305,22 @@ if test "$build_docs" -eq 1; then fi +# Add assert() usage + +AC_ARG_ENABLE(assertions, + AS_HELP_STRING([--enable-assertions], + [Turn on assertions checks (undefine NDEBUG, define _GLIBCXX_ASSERTIONS & _FORTIFY_SOURCE)]), +[ + if test "${enableval}" = "yes"; then + assertions='-UNDEBUG -D_FORTIFY_SOURCE=3 -D_GLIBCXX_ASSERTIONS' + else + assertions='-DNDEBUG' + fi +], +[ + assertions='-DNDEBUG' +]) + # Add PCRE Studying AC_ARG_ENABLE(pcre-study, @@ -690,6 +706,22 @@ AC_ARG_ENABLE(modsec-api, modsec_api= ]) +# MSC_LARGE_STREAM_INPUT +AC_ARG_ENABLE(large-stream-input, + AS_HELP_STRING([--enable-large-stream-input], + [Enable optimization for large stream input]), +[ + if test "$enableval" = "yes"; then + large_stream_input="-DMSC_LARGE_STREAM_INPUT" + MODSEC_EXTRA_CFLAGS="$MODSEC_EXTRA_CFLAGS $large_stream_input" + else + large_stream_input= + fi +], +[ + large_stream_input= +]) + # Find apxs AC_MSG_NOTICE(looking for Apache module support via DSO through APXS) AC_ARG_WITH(apxs, @@ -812,7 +844,7 @@ else fi fi -MODSEC_EXTRA_CFLAGS="$pcre_study $pcre_match_limit $pcre_match_limit_recursion $pcre_jit $request_early $htaccess_config $lua_cache $debug_conf $debug_cache $debug_acmp $debug_mem $perf_meas $modsec_api $cpu_type $unique_id $log_filename $log_server $log_collection_delete_problem $log_dechunk $log_stopwatch $log_handler $log_server_contex $collection_global_lock" +MODSEC_EXTRA_CFLAGS="$pcre_study $pcre_match_limit $pcre_match_limit_recursion $pcre_jit $request_early $htaccess_config $lua_cache $debug_conf $debug_cache $debug_acmp $debug_mem $perf_meas $modsec_api $cpu_type $unique_id $log_filename $log_server $log_collection_delete_problem $log_dechunk $log_stopwatch $log_handler $log_server_context $collection_global_lock $large_stream_input $assertions" APXS_WRAPPER=build/apxs-wrapper APXS_EXTRA_CFLAGS="" @@ -849,6 +881,7 @@ AC_SUBST(APXS_MODULES) AC_SUBST(APXS_HTTPD) CHECK_PCRE() +CHECK_PCRE2() if test "$build_apache2_module" -ne 0 -o "$build_mlogc" -ne 0; then CHECK_APR() CHECK_APU() @@ -871,13 +904,13 @@ ORIG_CFLAGS="$CFLAGS $APU_CFLAGS" ORIG_CPPFLAGS="$CPPFLAGS" CFLAGS="$CFLAGS $APR_CFLAGS" CPPFLAGS="$CPPFLAGS $APR_CPPFLAGS" -AC_TRY_COMPILE( - [#include ], - [ +AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ #include ]], + [[ #if APU_HAVE_CRYPTO == 0 #error APR util was not compiled with crypto support. #endif - ], + ]])], [ AC_DEFINE([WITH_APU_CRYPTO], [1], [APR util was compiled with crypto support]) MODSEC_EXTRA_CFLAGS="$MODSEC_EXTRA_CFLAGS -DWITH_APU_CRYPTO" ], @@ -888,7 +921,7 @@ AC_TRY_COMPILE( CFLAGS="$ORIG_CFLAGS" CPPFLAGS="$ORIG_CPPFLAGS" -# Current our unique download backend is curl, furhter we can support more. +# Currently our unique download backend is curl, further we can support more. if test ! -z "${CURL_VERSION}"; then AC_DEFINE([WITH_REMOTE_RULES], [1], [Enables SecRemoteRules support]) MODSEC_EXTRA_CFLAGS="$MODSEC_EXTRA_CFLAGS -DWITH_REMOTE_RULES" @@ -913,9 +946,6 @@ AC_CONFIG_FILES([build/apxs-wrapper], [chmod +x build/apxs-wrapper]) if test -e "$PERL"; then if test "$build_mlogc" -ne 0; then AC_CONFIG_FILES([mlogc/mlogc-batch-load.pl], [chmod +x mlogc/mlogc-batch-load.pl]) - AC_CONFIG_FILES([tests/regression/misc/40-secRemoteRules.t]) - AC_CONFIG_FILES([tests/regression/misc/50-ipmatchfromfile-external.t]) - AC_CONFIG_FILES([tests/regression/misc/60-pmfromfile-external.t]) fi AC_CONFIG_FILES([tests/run-unit-tests.pl], [chmod +x tests/run-unit-tests.pl]) AC_CONFIG_FILES([tests/run-regression-tests.pl], [chmod +x tests/run-regression-tests.pl]) diff --git a/design.md b/design.md new file mode 100644 index 0000000000..a8e56e128e --- /dev/null +++ b/design.md @@ -0,0 +1,29 @@ +Design notes for source code +== +This file give some explanations and guidelines regarding ModSecurity v2 source code. +The goal is to discuss topics that are not related to a specific location in the code, so that cannot be best explained by comments. +The goal is not to replace comments where it is probably better. +It's quite short for the moment, but the goal is to extend it from time to time. + +## Null pointer check +The default behaviour is to check for null pointer dereference everywhere it may be needed. +In case a pointer cannot be null, it has to be explained with a comment at the beginning of the function of when dereferencing the pointer. +On top of that, an explicit check should be done when compiling in debug mode with the following code: +``` + assert(mypointer); +``` +In case a pointer that cannot be null is used at several locations (say more than 3 times), +the explanation could be given globally in this file. + +### Pointers never null +The following pointers can never be null: + +#### msr + +msr is assigned at the following places: +- mod_security2.c (14 x): initialization +In all the above calls, and all calling functions, it immediately returns (with an error code) in case msr is null, up to a place where no mod_security2 processing at all occurs. +In subsequent calls, there's thus no possibility to have msr null. +- apache2_io.c (2 x): assign a previously initialized msr +- msc_json (9 x): assign a previously initialized msr +- msc_lua.c (4 x): assign a previously initialized msr diff --git a/doc/README.txt b/doc/README.txt index ec0ed3572d..75bfac094c 100644 --- a/doc/README.txt +++ b/doc/README.txt @@ -8,4 +8,4 @@ Please access the ModSecurity Github space to access the below documentation. * Reference Manual * RoadMap -https://github.com/SpiderLabs/ModSecurity/wiki/ +https://github.com/owasp-modsecurity/ModSecurity/wiki/ diff --git a/iis/.gitignore b/iis/.gitignore new file mode 100644 index 0000000000..d16386367f --- /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 0000000000..fe5e1d0f51 --- /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/Makefile.win b/iis/Makefile.win index 8c2cdbd7ee..04df4fb430 100644 --- a/iis/Makefile.win +++ b/iis/Makefile.win @@ -10,12 +10,12 @@ LIBS = $(APACHE)\lib\libapr-1.lib \ $(APACHE)\lib\libaprutil-1.lib \ $(PCRE)\pcre.lib \ - $(CURL)\libcurl.lib \ + $(CURL)\libcurl.lib \ $(LIBXML2)\win32\bin.msvc\libxml2.lib \ "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" \ "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" "ws2_32.lib" \ "iphlpapi.lib" -# $(SSDEEP)\fuzzy.lib \ +# $(SSDEEP)\fuzzy.lib \ ########################################################################### ########################################################################### @@ -24,12 +24,12 @@ LINK = link.exe MT = mt -DEFS = /nologo /O2 /LD /W3 /wd4244 /wd4018 -DWITH_YAJL -DWIN32 -DWINNT -Dinline=APR_INLINE -DAP_DECLARE_STATIC -D_MBCS -D$(VERSION) +DEFS = /nologo /O2 /LD /W3 /wd4244 /wd4018 -DWITH_YAJL -DWIN32 -DWINNT -Dinline=APR_INLINE -DAP_DECLARE_STATIC -D_MBCS -D$(VERSION) DLL = ModSecurityIIS.dll INCLUDES = -I. -I.. \ - -I$(YAJL)\.. \ + -I$(YAJL)\.. \ -I$(PCRE)\include -I$(PCRE) \ -I$(LIBXML2)\include \ -I$(CURL)\include -I$(CURL) \ @@ -37,10 +37,10 @@ INCLUDES = -I. -I.. \ -I..\apache2 \ -I..\standalone -# Enables support for SecRemoteRules and external resources. -DEFS=$(DEFS) -DWITH_CURL -DWITH_REMOTE_RULES - -# -I$(SSDEEP) \ +# Enables support for SecRemoteRules, external resources and enable optimization for large stream input by default on IIS. +DEFS=$(DEFS) -DWITH_CURL -DWITH_REMOTE_RULES -DMSC_LARGE_STREAM_INPUT + +# -I$(SSDEEP) \ # Lua is optional !IF "$(LUA)" != "" LIBS = $(LIBS) $(LUA)\lua5.1.lib @@ -54,16 +54,16 @@ LIBS = $(LIBS) $(YAJL)\lib\yajl.lib DEFS=$(DEFS) -DWITH_YAJL INCLUDES = $(INCLUDES) -I$(YAJL)\include -I$(YAJL) \ !ENDIF - + # ssdeep is optional # !IF "$(SSDEEP)" != "" # LIBS = $(LIBS) $(SSDEEP)\fuzzy.lib # DEFS=$(DEFS) -DWITH_SSDEEP # INCLUDES = $(INCLUDES) -I$(SSDEEP)\include -I$(SSDEEP) \ # !ENDIF - - - + + + CFLAGS= -MD /Zi $(INCLUDES) $(DEFS) @@ -75,16 +75,18 @@ OBJS1 = mod_security2.obj apache2_config.obj apache2_io.obj apache2_util.obj \ msc_parsers.obj msc_util.obj msc_pcre.obj persist_dbm.obj \ msc_reqbody.obj msc_geo.obj msc_gsb.obj msc_unicode.obj acmp.obj msc_lua.obj \ msc_release.obj msc_crypt.obj msc_tree.obj \ - msc_status_engine.obj \ + msc_status_engine.obj \ msc_json.obj \ - msc_remote_rules.obj - + msc_remote_rules.obj + OBJS2 = api.obj buckets.obj config.obj filters.obj hooks.obj regex.obj server.obj OBJS3 = main.obj moduleconfig.obj mymodule.obj OBJS4 = libinjection_html5.obj \ libinjection_sqli.obj \ libinjection_xss.obj +OBJS5 = ModSecurityIISMessage.res + all: $(DLL) dll: $(DLL) @@ -101,12 +103,21 @@ $(OBJS2): ..\standalone\$*.c .cpp.obj: $(CC) $(CFLAGS) -c $< -Fo$@ -$(DLL): $(OBJS1) $(OBJS2) $(OBJS3) $(OBJS4) - $(LINK) $(LDFLAGS) $(OBJS1) $(OBJS2) $(OBJS3) $(OBJS4) $(LIBS) +$(DLL): $(OBJS1) $(OBJS2) $(OBJS3) $(OBJS4) $(OBJS5) + $(LINK) $(LDFLAGS) $(OBJS1) $(OBJS2) $(OBJS3) $(OBJS4) $(OBJS5) $(LIBS) IF EXIST $(DLL).manifest $(MT) -manifest $(DLL).manifest -outputresource:$(DLL);#1 +$(OBJS5): ModSecurityIISMessage.rc + rc /r ModSecurityIISMessage.rc + +ModSecurityIISMessage.rc: ModSecurityIISMessage.mc + mc -U ModSecurityIISMessage.mc + clean: del /f *.obj del /f *.dll del /f *.exp del /f *.lib + del /f *.rc + del /f *.bin + del /f *.res diff --git a/iis/ModSecurityIISMessage.mc b/iis/ModSecurityIISMessage.mc new file mode 100644 index 0000000000..49106bfa74 --- /dev/null +++ b/iis/ModSecurityIISMessage.mc @@ -0,0 +1,5 @@ +MessageId=0x1 +Language=English +%1 +. + diff --git a/iis/build_dependencies.bat b/iis/build_dependencies.bat old mode 100644 new mode 100755 index fc0d6095d6..e131cd15cd --- a/iis/build_dependencies.bat +++ b/iis/build_dependencies.bat @@ -1,4 +1,4 @@ -:: Those variable should be edited as needed. +:: Those variables should be edited as needed. :: Use full paths. :: General paths @@ -6,23 +6,28 @@ @set OUTPUT_DIR=%cd%\dependencies\release_files @set SOURCE_DIR=%USERPROFILE%\Downloads -:: Aditional paths. -@set PATH=%PATH%;c:\work\cmake-2.8.7-win32-x86\bin;"c:\program files\7-zip" +:: Dependencies +@set CMAKE=cmake-3.12.4-win32-x86.zip +@set PCRE=pcre-8.45.zip +@set ZLIB=zlib-1.2.12.tar.gz +@set LIBXML2=libxml2-2.9.14.tar.gz +@set LUA=lua-5.3.6.tar.gz +@set CURL=curl-7.83.1.zip +@set APACHE_SRC=httpd-2.4.54.tar.gz +@set APACHE_BIN32=httpd-2.4.54-win32-VS16.zip +@set APACHE_BIN64=httpd-2.4.54-win64-VS16.zip +@set YAJL=yajl-2.1.0.zip +@set SSDEEP=ssdeep-2.14.1.tar.gz +@set SSDEEP_BIN=ssdeep-2.14.1.zip + +@set CMAKE_DIR=%WORK_DIR%\%CMAKE:~0,-4%\bin -@set PCRE=pcre-8.33.zip -@set ZLIB=zlib-1.2.8.tar.gz -@set LIBXML2=libxml2-2.9.1.tar.gz -@set LUA=lua-5.1.5.tar.gz -@set CURL=curl-7.39.0.zip -@set APACHE_SRC=httpd-2.4.6.tar.gz -@set APACHE_BIN32=httpd-2.4.6-win32-VC11.zip -@set APACHE_BIN64=httpd-2.4.6-win64-VC11.zip -@set YAJL=lloyd-yajl-f4b2b1a.zip -@set SSDEEP=ssdeep-2.10.tar.gz -@set SSDEEP_BIN=ssdeep-2.10.zip +:: Aditional paths. +@set "DIR_7ZIP=c:\program files\7-zip" +@set PATH=%PATH%;%CMAKE_DIR%;%DIR_7ZIP% -:: @set VCARGS32="C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\vcvars32.bat" -:: @set VCARGS64="C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\x86_amd64\vcvarsx86_amd64.bat" +:: @set VCARGS32="C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat" +:: @set VCARGS64="C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsx86_amd64.bat" :: Do not edit bellow this line. @@ -45,6 +50,12 @@ call cl 2>&1 | findstr /C:"x64" @if (%ERRORLEVEL%) == (0) set APACHE_BIN=%APACHE_BIN64% @echo Starting with the depdendencies... +@echo # CMake. - %CMAKE% +@call dependencies/build_cmake.bat +@if NOT (%ERRORLEVEL%) == (0) goto build_failed_cmake +@cd "%CURRENT_DIR%" + + @echo # Apache - %HTTPD%/%APACHE24_ZIP% @call dependencies/build_apache.bat @if NOT (%ERRORLEVEL%) == (0) goto build_failed_apache @@ -53,7 +64,7 @@ call cl 2>&1 | findstr /C:"x64" @echo # pcre. - %PCRE% @call dependencies/build_pcre.bat @if NOT (%ERRORLEVEL%) == (0) goto build_failed_pcre -@cd "%CURRENT_DIR%" +@cd "%CURRENT_DIR% @echo # zlib - %ZLIB% @call dependencies/build_zlib.bat @@ -129,6 +140,10 @@ call cl 2>&1 | findstr /C:"x64" @echo Failed to setup %SSDEEP%... @goto failed +:build_failed_cmake +@echo Failed to setup %CMAKE%... +@goto failed + :failed @cd %CURRENT_DIR% @exit /B 1 diff --git a/iis/build_modsecurity.bat b/iis/build_modsecurity.bat index 680c05e8fb..4ee8348f56 100644 --- a/iis/build_modsecurity.bat +++ b/iis/build_modsecurity.bat @@ -15,21 +15,21 @@ set CURRENT_DIR=%cd% cd ..\apache2 del *.obj *.dll *.lib del libinjection\*.obj libinjection\*.dll libinjection\*.lib -NMAKE -f Makefile.win APACHE=..\iis\%DEPENDENCIES_DIR%\Apache24 PCRE=..\iis\%DEPENDENCIES_DIR%\pcre LIBXML2=..\iis\%DEPENDENCIES_DIR%\libxml2 LUA=..\iis\%DEPENDENCIES_DIR%\lua\src VERSION=VERSION_IIS YAJL=..\iis\%DEPENDENCIES_DIR%\yajl\build\yajl-2.0.1 SSDEEP=..\iis\%DEPENDENCIES_DIR%\ssdeep CURL=..\iis\%DEPENDENCIES_DIR%\curl IIS_BUILD=yes +NMAKE -f Makefile.win APACHE=..\iis\%DEPENDENCIES_DIR%\Apache24 PCRE=..\iis\%DEPENDENCIES_DIR%\pcre LIBXML2=..\iis\%DEPENDENCIES_DIR%\libxml2 LUA=..\iis\%DEPENDENCIES_DIR%\lua\src VERSION=VERSION_IIS YAJL=..\iis\%DEPENDENCIES_DIR%\yajl\build\yajl-2.1.0 SSDEEP=..\iis\%DEPENDENCIES_DIR%\ssdeep CURL=..\iis\%DEPENDENCIES_DIR%\curl IIS_BUILD=yes @if NOT (%ERRORLEVEL%) == (0) goto build_failed @echo mlogc... cd ..\mlogc del *.obj *.dll *.lib nmake -f Makefile.win clean -nmake -f Makefile.win APACHE=..\iis\%DEPENDENCIES_DIR%\Apache24 PCRE=..\iis\%DEPENDENCIES_DIR%\pcre CURL=..\iis\%DEPENDENCIES_DIR%\curl YAJL=..\iis\%DEPENDENCIES_DIR%\yajl SSDEEP=..\iis\%DEPENDENCIES_DIR%\ssdeep VERSION=VERSION_IIS +nmake -f Makefile.win APACHE=..\iis\%DEPENDENCIES_DIR%\Apache24 PCRE=..\iis\%DEPENDENCIES_DIR%\pcre CURL=..\iis\%DEPENDENCIES_DIR%\curl YAJL=..\iis\%DEPENDENCIES_DIR%\yajl\build\yajl-2.1.0 SSDEEP=..\iis\%DEPENDENCIES_DIR%\ssdeep VERSION=VERSION_IIS @if NOT (%ERRORLEVEL%) == (0) goto build_failed @echo iis... cd ..\iis del *.obj *.dll *.lib nmake -f Makefile.win clean -NMAKE -f Makefile.win APACHE=..\iis\%DEPENDENCIES_DIR%\Apache24 PCRE=..\iis\%DEPENDENCIES_DIR%\pcre LIBXML2=..\iis\%DEPENDENCIES_DIR%\libxml2 LUA=..\iis\%DEPENDENCIES_DIR%\lua\src VERSION=VERSION_IIS YAJL=..\iis\%DEPENDENCIES_DIR%\yajl\build\yajl-2.0.1 SSDEEP=..\iis\%DEPENDENCIES_DIR%\ssdeep CURL=..\iis\%DEPENDENCIES_DIR%\curl +NMAKE -f Makefile.win APACHE=..\iis\%DEPENDENCIES_DIR%\Apache24 PCRE=..\iis\%DEPENDENCIES_DIR%\pcre LIBXML2=..\iis\%DEPENDENCIES_DIR%\libxml2 LUA=..\iis\%DEPENDENCIES_DIR%\lua\src VERSION=VERSION_IIS YAJL=..\iis\%DEPENDENCIES_DIR%\yajl\build\yajl-2.1.0 SSDEEP=..\iis\%DEPENDENCIES_DIR%\ssdeep CURL=..\iis\%DEPENDENCIES_DIR%\curl @if NOT (%ERRORLEVEL%) == (0) goto build_failed cd %CURRENT_DIR% diff --git a/iis/build_msi.bat b/iis/build_msi.bat index 0ea7d369fe..d3c020fdd7 100644 --- a/iis/build_msi.bat +++ b/iis/build_msi.bat @@ -1,20 +1,20 @@ -set PATH="%PATH%;C:\Program Files (x86)\WiX Toolset v3.8\bin;C:\Program Files (x86)\WiX Toolset v3.7\bin;" +set PATH="%PATH%;C:\Program Files (x86)\WiX Toolset v3.11\bin;C:\Program Files (x86)\WiX Toolset v3.8\bin;C:\Program Files (x86)\WiX Toolset v3.7\bin;" set CURRENT_DIR=%cd% del installer.wix* -"candle.exe" -ext WixUtilExtension -ext WixUIExtension "%CURRENT_DIR%\installer.wxs" -out "%CURRENT_DIR%\installer.wixobj" -arch x64 +"C:\Program Files (x86)\WiX Toolset v3.11\bin\candle.exe" -ext WixUtilExtension -ext WixUIExtension "%CURRENT_DIR%\installer.wxs" -out "%CURRENT_DIR%\installer.wixobj" -arch x64 @if NOT (%ERRORLEVEL%) == (0) goto build_failed -"light.exe" -ext WixUtilExtension -ext WixUIExtension "%CURRENT_DIR%\installer.wixobj" -out "%CURRENT_DIR%\installer-64.msi" +"C:\Program Files (x86)\WiX Toolset v3.11\bin\light.exe" -ext WixUtilExtension -ext WixUIExtension "%CURRENT_DIR%\installer.wixobj" -out "%CURRENT_DIR%\installer-64.msi" @if NOT (%ERRORLEVEL%) == (0) goto build_failed -"candle.exe" -ext WixUtilExtension -ext WixUIExtension "%CURRENT_DIR%\installer.wxs" -out "%CURRENT_DIR%\installer.wixobj" -arch x86 +"C:\Program Files (x86)\WiX Toolset v3.11\bin\candle.exe" -ext WixUtilExtension -ext WixUIExtension "%CURRENT_DIR%\installer.wxs" -out "%CURRENT_DIR%\installer.wixobj" -arch x86 @if NOT (%ERRORLEVEL%) == (0) goto build_failed -"light.exe" -ext WixUtilExtension -ext WixUIExtension "%CURRENT_DIR%\installer.wixobj" -out "%CURRENT_DIR%\installer-32.msi" +"C:\Program Files (x86)\WiX Toolset v3.11\bin\light.exe" -ext WixUtilExtension -ext WixUIExtension "%CURRENT_DIR%\installer.wixobj" -out "%CURRENT_DIR%\installer-32.msi" @if NOT (%ERRORLEVEL%) == (0) goto build_failed exit /B 0 diff --git a/iis/build_release.bat b/iis/build_release.bat old mode 100644 new mode 100755 index 68bc587130..c1d8e85eeb --- a/iis/build_release.bat +++ b/iis/build_release.bat @@ -14,8 +14,9 @@ mkdir "%AMD64%" mkdir "%X86%" -set VCARGS32="C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\vcvars32.bat" -set VCARGS64="C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\x86_amd64\vcvarsx86_amd64.bat" +set VCARGS32="C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat" +set VCARGS64="C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsx86_amd64.bat" + set SSDEEP_ARCH="x64" call build_dependencies.bat %VCARGS64% diff --git a/iis/dependencies/build_cmake.bat b/iis/dependencies/build_cmake.bat new file mode 100644 index 0000000000..f76847153a --- /dev/null +++ b/iis/dependencies/build_cmake.bat @@ -0,0 +1,20 @@ +@cd "%WORK_DIR%" + +@if NOT EXIST "%SOURCE_DIR%\%CMAKE%" goto file_not_found + + +@7z.exe x "%SOURCE_DIR%\%CMAKE%" +@if NOT (%ERRORLEVEL%) == (0) goto something_went_wrong + +@exit /B 0 + +:file_not_found +@echo File not found: %SOURCE_DIR%\%CMAKE% +@goto failed + +:something_went_wrong +@echo Something went wrong while unzip CMake files. +@goto failed + +:failed +@exit /B 1 \ No newline at end of file diff --git a/iis/dependencies/build_curl.bat b/iis/dependencies/build_curl.bat index 6d66a1d11c..be26ca9a43 100644 --- a/iis/dependencies/build_curl.bat +++ b/iis/dependencies/build_curl.bat @@ -20,9 +20,10 @@ nmake /f Makefile.vc mode=dll ENABLE_WINSSL=yes MACHINE=%ARCH% WITH_ZLIB=dll cd "%WORK_DIR%" -copy /y "%WORK_DIR%\curl\builds\libcurl-vc-%ARCH%-release-dll-zlib-dll-ipv6-sspi-winssl-obj-lib\libcurl.dll" "%OUTPUT_DIR%" -copy /y "%WORK_DIR%\curl\builds\libcurl-vc-%ARCH%-release-dll-zlib-dll-ipv6-sspi-winssl-obj-lib\libcurl.lib" "%OUTPUT_DIR%" -copy /y "%WORK_DIR%\curl\builds\libcurl-vc-%ARCH%-release-dll-zlib-dll-ipv6-sspi-winssl-obj-lib\libcurl.lib" "%WORK_DIR%\curl\libcurl.lib" +copy /y "%WORK_DIR%\curl\builds\libcurl-vc-%ARCH%-release-dll-zlib-dll-ipv6-sspi-schannel-obj-lib\libcurl.dll" "%OUTPUT_DIR%" +copy /y "%WORK_DIR%\curl\builds\libcurl-vc-%ARCH%-release-dll-zlib-dll-ipv6-sspi-schannel-obj-lib\libcurl.lib" "%OUTPUT_DIR%" +copy /y "%WORK_DIR%\curl\builds\libcurl-vc-%ARCH%-release-dll-zlib-dll-ipv6-sspi-schannel-obj-lib\libcurl.lib" "%WORK_DIR%\curl\libcurl.lib" + exit /B 0 diff --git a/iis/dependencies/build_pcre.bat b/iis/dependencies/build_pcre.bat index 9d728a4042..d2cbcce0f9 100644 --- a/iis/dependencies/build_pcre.bat +++ b/iis/dependencies/build_pcre.bat @@ -1,32 +1,45 @@ -cd "%WORK_DIR%" - -@if NOT EXIST "%SOURCE_DIR%\%APACHE_BIN%" goto file_not_found_bin - -7z.exe x "%SOURCE_DIR%\%PCRE%" -set PCRE_DIR=%PCRE:~0,-4% - +::@if NOT (%ERRORLEVEL%) == (1) Echo "Patch successfull... For more info on patch see: https://vcs.pcre.org/pcre/code/trunk/CMakeLists.txt?r1=1659&r2=1677&view=patch" + +cd "%WORK_DIR%" + +@if NOT EXIST "%SOURCE_DIR%\%APACHE_BIN%" goto file_not_found_bin + +7z.exe x "%SOURCE_DIR%\%PCRE%" +set PCRE_DIR=%PCRE:~0,-4% + move "%PCRE_DIR%" "pcre" - + +@if "%PCRE_DIR%" == "pcre-8.40" ( + Echo. && Echo "PCRE 8.40 found... trying to patch it to compile cleanly" + ::cscript /B /Nologo ../patch-pcre-8.40.vbs + cd "pcre" + cat CMakeLists.txt | sed "s/PCRE_STATIC_RUNTIME OFF CACHE BOOL/PCRE_STATIC_RUNTIME/g" > CMakeLists.txt.ops + move CMakeLists.txt CMakeLists.txt.old + move CMakeLists.txt.ops CMakeLists.txt + cd .. +) + cd "pcre" -CMAKE -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=True -@if NOT (%ERRORLEVEL%) == (0) goto build_failed -NMAKE -@if NOT (%ERRORLEVEL%) == (0) goto build_failed -cd "%WORK%" - +CMAKE -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=RelWithDebInfo -DBUILD_SHARED_LIBS=True +@if NOT (%ERRORLEVEL%) == (0) goto build_failed +NMAKE +@if NOT (%ERRORLEVEL%) == (0) goto build_failed +cd "%WORK%" + copy /y "%WORK_DIR%\pcre\pcre.dll" "%OUTPUT_DIR%" copy /y "%WORK_DIR%\pcre\pcre.pdb" "%OUTPUT_DIR%" copy /y "%WORK_DIR%\pcre\pcre.lib" "%OUTPUT_DIR%" -echo "a" -@exit /B 0 - -:file_not_found_bin -@echo File not found: "%SOURCE_DIR%\%PCRE%" -@goto failed - -:build_failed -@echo Problems during the building phase -@goto failed - -:failed -@exit /B 1 +copy /y "%WORK_DIR%\pcre\pcre.h.generic" "%WORK_DIR%\pcre\pcre.h" +echo "a" +@exit /B 0 + +:file_not_found_bin +@echo File not found: "%SOURCE_DIR%\%PCRE%" +@goto failed + +:build_failed +@echo Problems during the building phase +@goto failed + +:failed +@exit /B 1 diff --git a/iis/dependencies/build_ssdeep.bat b/iis/dependencies/build_ssdeep.bat index 36b8b1595c..3b3a46bcd4 100644 --- a/iis/dependencies/build_ssdeep.bat +++ b/iis/dependencies/build_ssdeep.bat @@ -1,5 +1,8 @@ cd "%WORK_DIR%" +echo "%SOURCE_DIR%\%SSDEEP%" +echo "%SOURCE_DIR%\%SSDEEP_BIN%" + @if NOT EXIST "%SOURCE_DIR%\%SSDEEP%" goto build_failed @7z.exe x "%SOURCE_DIR%\%SSDEEP_BIN%" diff --git a/iis/dependencies/build_yajl.bat b/iis/dependencies/build_yajl.bat index 33ebc234ac..f5c6eff078 100644 --- a/iis/dependencies/build_yajl.bat +++ b/iis/dependencies/build_yajl.bat @@ -4,11 +4,13 @@ cd "%WORK_DIR%" 7z.exe x "%SOURCE_DIR%\%YAJL%" set YAJL_DIR=%YAJL:~0,-4% - -move "%YAJL_DIR%" "yajl" - +echo "%SOURCE_DIR%\%YAJL%" +echo "%YAJL_DIR%" +pwd +move "%YAJL_DIR%" "%WORK_DIR%\yajl" +pwd cd "yajl" - +pwd mkdir build @if NOT (%ERRORLEVEL%) == (0) goto build_failed cd build @@ -20,15 +22,20 @@ nmake cd "%WORK%" -copy /y "%WORK_DIR%\yajl\build\yajl-2.0.1\lib\yajl.dll" "%OUTPUT_DIR%" -copy /y "%WORK_DIR%\yajl\build\yajl-2.0.1\lib\yajl.pdb" "%OUTPUT_DIR%" -copy /y "%WORK_DIR%\yajl\build\yajl-2.0.1\lib\yajl.lib" "%OUTPUT_DIR%" -copy /y "%WORK_DIR%\yajl\build\yajl-2.0.1\lib\yajl_s.lib" "%OUTPUT_DIR%" +copy /y "%WORK_DIR%\yajl\build\%YAJL_DIR%\lib\yajl.dll" "%OUTPUT_DIR%" +:: copy /y "%WORK_DIR%\yajl\build\%YAJL_DIR%\lib\yajl.pdb" "%OUTPUT_DIR%" +copy /y "%WORK_DIR%\yajl\build\%YAJL_DIR%\lib\yajl.lib" "%OUTPUT_DIR%" +copy /y "%WORK_DIR%\yajl\build\%YAJL_DIR%\lib\yajl_s.lib" "%OUTPUT_DIR%" + +copy /y "%WORK_DIR%\yajl\build\%YAJL_DIR%\lib\yajl.dll" "%WORK_DIR%\yajl\build\%YAJL_DIR%\include\yajl.dll" +copy /y "%WORK_DIR%\yajl\build\%YAJL_DIR%\lib\yajl.lib" "%WORK_DIR%\yajl\build\%YAJL_DIR%\include\yajl.lib" +copy /y "%WORK_DIR%\yajl\build\%YAJL_DIR%\lib\yajl_s.lib" "%WORK_DIR%\yajl\build\%YAJL_DIR%\include\yajl_s.lib" + @exit /B 0 :file_not_found_bin -@echo File not found: "%SOURCE_DIR%\%PCRE%" +@echo File not found: "%SOURCE_DIR%\%YAJL%" @goto failed :build_failed diff --git a/iis/dependencies/howto.txt b/iis/dependencies/howto.txt index 1505a5f95d..830851f95a 100644 --- a/iis/dependencies/howto.txt +++ b/iis/dependencies/howto.txt @@ -1,44 +1,52 @@ -WARNING! +The build process for ModSecurityIIS for Windows was a relatively complicated process. Understanding it required advanced knowledge of Windows and Unix environments. +So the build process was refactored to make it easier for users to create their own builds with the automated batch scripts. -Building ModSecurityIIS on Windows is a relatively complicated process. Understanding it requires advanced knowledge of Windows and Unix environments. -Using the same versions of libraries as listed below is strongly recommended. +* build_release.bat -> The main build script that calls all the others to have a working release +* build_msi.bat -> Creates the MSI self-installer for easy deploy / removal / distribution +* build_dependencies.bat -> Sets (and downloads if needed) all required dependencies +* build_modsecurity.bat -> Builds ModSecurity (requires all depenedencies being set) + +* download_files.bat -> Downloads all required dependencies to the default Downloads folder +** This script is disabled by default. If you want to enable it, uncomment the "@call download_files.bat" line on build_dependencies.bat + +The dependencies folder also includes a set o batch scripts which sets each dependency +on its own. These scripts are called by the build_dependencies.bat script. + +Using the same versions of libraries as listed below is recommended. -------------------------------------- -Tested on: - -Windows 7 x64 -Vistual Studio 2010 Ultimate SP1 -IIS enabled/installed - -cmake 2.8.7 -curl 7.24.0 -apache 2.2.22 or apache 2.4.3 -libxml2 2.7.7 -lua 5.1.5 -pcre 8.30 -zlib 1.2.7 -7-Zip +Compilation Prerequisites: + +* Windows 7 x86_x64 (Should work on newer versions of Windows too) +* Vistual Studio 2013 Express (Other versions should work, but you need to set the correct path for vcvars.bat scripts) +* IIS enabled/installed +* 7-Zip + -------------------------------------- -1. Create working directory c:\work and drop directory c:\drop -2. Sync SVN ModSecurity branch to c:\work\mod_security -3. Copy files from c:\work\mod_security\iis\winbuild to c:\work -4. Download and install cmake (unpack to c:\work\cmake-2.8.7-win32-x86) -5. Download and install 7-Zip -6. Adjust paths in c:\work\init.bat accordingly if needed -7. Download curl, apache, libxml2, lua, pcre and zlib, place them in zip files in c:\work - -curl-7.24.0.zip -httpd-2.2.22-win32-src.zip or (httpd-2.4.3.zip (source) and httpd-2.4.3-win32.zip + httpd-2.4.3-win64.zip (binaries)) -libxml2-2.7.7.zip -lua-5.1.5.zip -pcre-8.30.zip -zlib-1.2.7.zip - -Modify c:\work\build.bat accordingly (if other versions were used) - -8. Open cmd.exe window, go to c:\work and run buildall.bat -9. When done, the binaries, lib and pdb files should appear under c:\drop\x86 (32-bit) and c:\drop\amd64 (64-bit) -10. Open the VS ModSecurity IIS installer project -11. Copy new binaries to the installer's x86 and amd64 directories -12. Build installer from within VS +The latest versions of ModSecurity dependencies known to work well are the following: + +cmake-3.8.2-win32-x86 +pcre-8.40 (patch required and included on file "patch-pcre-8.40.vbs") +zlib-1.2.11 +libxml2-2.9.4 +lua-5.3.4 +curl-7.54.1 +httpd-2.4.27 (bin-VC11) +yajl-2.1.0 +ssdeep-2.13 + +-------------------------------------- + +1. Create working directory (e.g. c:\work) and drop the latest clone from ModSecurity's 2.x Github (https://github.com/owasp-modsecurity/ModSecurity/archive/v2/master.zip) +2. Make sure the prerequisites mentioned above are all set +3. If you haven't download the dependency files before, uncomment the "@call download_files.bat" line on build_dependencies.bat to have them downloaded prior +4. Open a command prompt (cmd.exe) and head to the "iis" folder inside ModSecurity tree working directory (e.g. cd c:\work\ModSecurity\iis) +5. If you need to modify anything (e.g. paths, versions etc), carefully edit the batch files. +6. Run build_release.bat +7. When done, the binaries, lib and pdb files should appear under c:\work\ModSecurity\iis\release\x86 (32-bit) and c:\work\ModSecurity\iis\release\amd64 (64-bit) +* At this point, if you had a previous installation of ModSecurity and would like to test you can place the x86 files to "C:\Windows\SysWOW64\inetsrv" and x64 files to "C:\Windows\System32\inetsrv" + +8. If all went well, you can build the MSI installer by running the build_msi.bat script. + +* The built installable package places the files to the correct folders, automatically configures the ModSecurity IIS native module and configures web.config to enable ModSecurity for all IIS sites. diff --git a/iis/download_files.bat b/iis/download_files.bat new file mode 100644 index 0000000000..dd0773aad2 --- /dev/null +++ b/iis/download_files.bat @@ -0,0 +1,39 @@ +@set CMAKE=cmake-3.12.4-win32-x86.zip +@set PCRE=pcre-8.41.zip +@set ZLIB=zlib-1.2.11.tar.gz +@set LIBXML2=libxml2-2.9.8.tar.gz +@set LUA=lua-5.3.5.tar.gz +@set CURL=curl-7.62.0.zip +@set APACHE_SRC=httpd-2.4.37.tar.gz +@set APACHE_BIN32=httpd-2.4.37-win32-VC11.zip +@set APACHE_BIN64=httpd-2.4.37-win64-VC11.zip +@set YAJL=yajl-2.1.0.zip +@set SSDEEP=ssdeep-2.13.tar.gz +@set SSDEEP_BIN=ssdeep-2.13.zip + +:: BITSAdmin refuses to download YAJL from GitHub URL +:: @set YAJL_URL=https://github.com/lloyd/yajl/archive/%YAJL:~-9% +@set YAJL_URL=http://http.debian.net/debian/pool/main/y/yajl/yajl_2.1.0.orig.tar.gz + +@set CMAKE_URL=https://cmake.org/files/v3.12/%CMAKE% +@set PCRE_URL=https://ftp.pcre.org/pub/pcre/%PCRE% +@set ZLIB_URL=https://zlib.net/%ZLIB% +@set LIBXML2_URL=http://xmlsoft.org/sources/%LIBXML2% +@set LUA_URL=https://www.lua.org/ftp/%LUA% +@set CURL_URL=http://curl.askapache.com/download/%CURL% +@set APACHE_SRC_URL=https://www.apache.org/dist/httpd/%APACHE_SRC% +@set APACHE_BIN_URL=https://home.apache.org/~steffenal/VC11/binaries +@set SSDEEP_URL=https://downloads.sourceforge.net/project/ssdeep/ssdeep-2.13 + +bitsadmin.exe /transfer "Downloading dependencies..." %CMAKE_URL% %SOURCE_DIR%\%CMAKE% %PCRE_URL% %SOURCE_DIR%\%PCRE% %ZLIB_URL% %SOURCE_DIR%\%ZLIB% %LIBXML2_URL% %SOURCE_DIR%\%LIBXML2% %LUA_URL% %SOURCE_DIR%\%LUA% %CURL_URL% %SOURCE_DIR%\%CURL% %APACHE_SRC_URL% %SOURCE_DIR%\%APACHE_SRC% %APACHE_BIN_URL%/%APACHE_BIN32% %SOURCE_DIR%\%APACHE_BIN32% %APACHE_BIN_URL%/%APACHE_BIN64% %SOURCE_DIR%\%APACHE_BIN64% %YAJL_URL% %SOURCE_DIR%\%YAJL% %SSDEEP_URL%/%SSDEEP% %SOURCE_DIR%\%SSDEEP% %SSDEEP_URL%/%SSDEEP_BIN% %SOURCE_DIR%\%SSDEEP_BIN% + +@if NOT (%ERRORLEVEL%) == (0) goto :failed_to_download +@exit /B 0 + +:failed_to_download +@echo. && echo Failed to download dependency files... Try again or manually download the files to %SOURCE_DIR% and comment "@call download_files.bat" from build_dependencies.bat +@goto failed + +:failed +@exit /B 1 + diff --git a/iis/installer.wxs b/iis/installer.wxs index 015c9351f8..3f17443327 100644 --- a/iis/installer.wxs +++ b/iis/installer.wxs @@ -7,9 +7,9 @@ lightArgs: --> - + - + @@ -17,17 +17,19 @@ - + - + + + @@ -87,24 +89,28 @@ + + - + + - - VersionNT64 - - NOT VersionNT64 - - - - - + + + VersionNT64 + + NOT VersionNT64 + + + + + @@ -121,7 +127,7 @@ - + @@ -129,224 +135,37 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - + - + - + @@ -355,37 +174,14 @@ - - - - - - - - - - - - - + - - - - - - - - - - - - - + + @@ -398,56 +194,16 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + - + - + - - - - - - - - - - - - - - - - - - - - @@ -455,33 +211,36 @@ - + - + + + - + + - + (NOT &ModSec64=3) AND (NOT &ModSec32=3) &ModSec64=3 OR &ModSec32=3 - + (NOT &ModSec32=3) &ModSec32=3 - + 1 @@ -545,7 +304,7 @@ - + @@ -571,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 @@ -608,12 +367,12 @@ - + - + @@ -623,45 +382,45 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + \ No newline at end of file diff --git a/iis/moduleconfig.cpp b/iis/moduleconfig.cpp index a92d51ffb1..d3bcefc9b6 100644 --- a/iis/moduleconfig.cpp +++ b/iis/moduleconfig.cpp @@ -466,11 +466,8 @@ MODSECURITY_STORED_CONTEXT::~MODSECURITY_STORED_CONTEXT() MODSECURITY_STORED_CONTEXT::MODSECURITY_STORED_CONTEXT(): m_bIsEnabled ( FALSE ), m_pszPath( NULL ), - m_Config( NULL ), - m_dwLastCheck( 0 ) + m_Config( NULL ) { - m_LastChange.dwLowDateTime = 0; - m_LastChange.dwHighDateTime = 0; } DWORD diff --git a/iis/moduleconfig.h b/iis/moduleconfig.h index 83b7517d34..75ee71dca2 100644 --- a/iis/moduleconfig.h +++ b/iis/moduleconfig.h @@ -68,8 +68,6 @@ class MODSECURITY_STORED_CONTEXT : public IHttpStoredContext USHORT* pdwLengthDestination ); void* m_Config; - DWORD m_dwLastCheck; - FILETIME m_LastChange; private: HRESULT diff --git a/iis/mymodule.cpp b/iis/mymodule.cpp index 607fdf0a6d..dfaee4b2cb 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,7 +32,6 @@ #include "api.h" #include "moduleconfig.h" -#include "winsock2.h" class REQUEST_STORED_CONTEXT : public IHttpStoredContext { @@ -84,67 +85,71 @@ class REQUEST_STORED_CONTEXT : public IHttpStoredContext ULONGLONG m_pResponsePosition; }; + //---------------------------------------------------------------------------- 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; } @@ -286,6 +291,7 @@ REQUEST_STORED_CONTEXT *RetrieveIISContext(request_rec *r) return NULL; } + HRESULT CMyHttpModule::ReadFileChunk(HTTP_DATA_CHUNK *chunk, char *buf) { OVERLAPPED ovl; @@ -752,11 +758,7 @@ CMyHttpModule::OnBeginRequest( goto Finished; } - // every 3 seconds we check for changes in config file - // - DWORD ctime = GetTickCount(); - - if(pConfig->m_Config == NULL || (ctime - pConfig->m_dwLastCheck) > 3000) + if(pConfig->m_Config == NULL) { char *path; USHORT pathlen; @@ -769,55 +771,42 @@ CMyHttpModule::OnBeginRequest( goto Finished; } - WIN32_FILE_ATTRIBUTE_DATA fdata; - BOOL ret; + pConfig->m_Config = modsecGetDefaultConfig(); - ret = GetFileAttributesEx(path, GetFileExInfoStandard, &fdata); + PCWSTR servpath = pHttpContext->GetApplication()->GetApplicationPhysicalPath(); + char *apppath; + USHORT apppathlen; - pConfig->m_dwLastCheck = ctime; + hr = pConfig->GlobalWideCharToMultiByte((WCHAR *)servpath, wcslen(servpath), &apppath, &apppathlen); - if(pConfig->m_Config == NULL || (ret != 0 && (pConfig->m_LastChange.dwLowDateTime != fdata.ftLastWriteTime.dwLowDateTime || - pConfig->m_LastChange.dwHighDateTime != fdata.ftLastWriteTime.dwHighDateTime))) + if ( FAILED( hr ) ) { - pConfig->m_LastChange.dwLowDateTime = fdata.ftLastWriteTime.dwLowDateTime; - pConfig->m_LastChange.dwHighDateTime = fdata.ftLastWriteTime.dwHighDateTime; - - pConfig->m_Config = modsecGetDefaultConfig(); - - PCWSTR servpath = pHttpContext->GetApplication()->GetApplicationPhysicalPath(); - char *apppath; - USHORT apppathlen; + delete path; + hr = E_UNEXPECTED; + goto Finished; + } - hr = pConfig->GlobalWideCharToMultiByte((WCHAR *)servpath, wcslen(servpath), &apppath, &apppathlen); + if(path[0] != 0) + { + const char * err = modsecProcessConfig((directory_config *)pConfig->m_Config, path, apppath); - if ( FAILED( hr ) ) + if(err != NULL) { + WriteEventViewerLog(err, EVENTLOG_ERROR_TYPE); + delete apppath; delete path; - hr = E_UNEXPECTED; goto Finished; } - if(path[0] != 0) + modsecReportRemoteLoadedRules(); + if (this->status_call_already_sent == false) { - const char * err = modsecProcessConfig((directory_config *)pConfig->m_Config, path, apppath); - - if(err != NULL) - { - WriteEventViewerLog(err, EVENTLOG_ERROR_TYPE); - delete apppath; - delete path; - goto Finished; - } - - modsecReportRemoteLoadedRules(); - if (this->status_call_already_sent == false) - { - this->status_call_already_sent = true; - modsecStatusEngineCall(); - } + this->status_call_already_sent = true; + modsecStatusEngineCall(); } - delete apppath; } + + delete apppath; delete path; } @@ -1072,7 +1061,9 @@ CMyHttpModule::OnBeginRequest( #endif c->remote_host = NULL; + LeaveCriticalSection(&m_csLock); int status = modsecProcessRequest(r); + EnterCriticalSection(&m_csLock); if(status != DECLINED) { @@ -1093,42 +1084,43 @@ CMyHttpModule::OnBeginRequest( return RQ_NOTIFICATION_CONTINUE; } + apr_status_t ReadBodyCallback(request_rec *r, char *buf, unsigned int length, unsigned int *readcnt, int *is_eos) { - REQUEST_STORED_CONTEXT *rsc = RetrieveIISContext(r); + REQUEST_STORED_CONTEXT *rsc = RetrieveIISContext(r); - *readcnt = 0; + *readcnt = 0; - if(rsc == NULL) - { - *is_eos = 1; - return APR_SUCCESS; - } + if (rsc == NULL) + { + *is_eos = 1; + return APR_SUCCESS; + } - IHttpContext *pHttpContext = rsc->m_pHttpContext; - IHttpRequest *pRequest = pHttpContext->GetRequest(); + IHttpContext *pHttpContext = rsc->m_pHttpContext; + IHttpRequest *pRequest = pHttpContext->GetRequest(); - if(pRequest->GetRemainingEntityBytes() == 0) - { - *is_eos = 1; - return APR_SUCCESS; - } + if (pRequest->GetRemainingEntityBytes() == 0) + { + *is_eos = 1; + return APR_SUCCESS; + } - HRESULT hr = pRequest->ReadEntityBody(buf, length, false, (DWORD *)readcnt, NULL); + HRESULT hr = pRequest->ReadEntityBody(buf, length, false, (DWORD *)readcnt, NULL); - if (FAILED(hr)) + if (FAILED(hr)) { // End of data is okay. - if (ERROR_HANDLE_EOF != (hr & 0x0000FFFF)) + if (ERROR_HANDLE_EOF != (hr & 0x0000FFFF)) { // Set the error status. - rsc->m_pProvider->SetErrorStatus( hr ); + rsc->m_pProvider->SetErrorStatus(hr); } - *is_eos = 1; + *is_eos = 1; } - return APR_SUCCESS; + return APR_SUCCESS; } apr_status_t WriteBodyCallback(request_rec *r, char *buf, unsigned int length) diff --git a/iis/vcpkg.json b/iis/vcpkg.json new file mode 100644 index 0000000000..3abb499b35 --- /dev/null +++ b/iis/vcpkg.json @@ -0,0 +1,10 @@ +{ + "dependencies": [ + "curl", + "libxml2", + "lua", + "pcre2", + "yajl", + "apr" + ] +} \ No newline at end of file diff --git a/iis/wix/modsecurity.conf b/iis/wix/modsecurity.conf index 60f09129e1..fcce635963 100644 --- a/iis/wix/modsecurity.conf +++ b/iis/wix/modsecurity.conf @@ -16,11 +16,14 @@ SecRuleEngine DetectionOnly # SecRequestBodyAccess On +# SecStreamInBodyInspection is required by IIS for proper body inspection +# See issue #1299 for more information +SecStreamInBodyInspection On # Enable XML request body parser. # Initiate XML Processor in case of xml content-type # -SecRule REQUEST_HEADERS:Content-Type "text/xml" \ +SecRule REQUEST_HEADERS:Content-Type "(?:application(?:/soap\+|/)|text/)xml" \ "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML" # Enable JSON request body parser. @@ -40,7 +43,7 @@ SecRequestBodyLimit 13107200 SecRequestBodyNoFilesLimit 131072 # Store up to 128 KB of request body data in memory. When the multipart -# parser reachers this limit, it will start using your hard disk for +# parser reaches this limit, it will start using your hard disk for # storage. That is slow, but unavoidable. # SecRequestBodyInMemoryLimit 131072 @@ -110,7 +113,7 @@ SecRule TX:/^MSC_/ "!@streq 0" \ # Do keep in mind that enabling this directive does increases both # memory consumption and response latency. # -#SecResponseBodyAccess On +SecResponseBodyAccess On # Which response MIME types do you want to inspect? You should adjust the # configuration below to catch documents but avoid static files @@ -151,7 +154,7 @@ SecDataDir c:\inetpub\temp\ # location must be private to ModSecurity. You don't want other users on # the server to access the files, do you? # -#SecUploadDir /opt/modsecurity/var/upload/ +#SecUploadDir c:\inetpub\temp\ # By default, only keep the files that were determined to be unusual # in some way (by an external inspection script). For this to work you @@ -171,7 +174,7 @@ SecDataDir c:\inetpub\temp\ # The default debug log configuration is to duplicate the error, warning # and notice messages from the error log. # -#SecDebugLog /opt/modsecurity/var/log/debug.log +#SecDebugLog c:\inetpub\temp\debug.log #SecDebugLogLevel 3 @@ -181,20 +184,20 @@ SecDataDir c:\inetpub\temp\ # trigger a server error (determined by a 5xx or 4xx, excluding 404, # level response status codes). # -#SecAuditEngine RelevantOnly -#SecAuditLogRelevantStatus "^(?:5|4(?!04))" +SecAuditEngine RelevantOnly +SecAuditLogRelevantStatus "^(?:5|4(?!04))" # Log everything we know about a transaction. -#SecAuditLogParts ABIJDEFHZ +SecAuditLogParts ABIJDEFHZ # Use a single file for logging. This is much easier to look at, but # assumes that you will use the audit log only ocassionally. # -#SecAuditLogType Serial -#SecAuditLog c:\inetpub\log\modsec_audit.log +SecAuditLogType Serial +#SecAuditLog c:\inetpub\logs\modsec_audit.log # Specify the path for concurrent audit logging. -#SecAuditLogStorageDir c:\inetpub\log\ +#SecAuditLogStorageDir c:\inetpub\logs\ # -- Miscellaneous ----------------------------------------------------------- @@ -216,8 +219,7 @@ SecCookieFormat 0 # to properly map encoded data to your language. Properly setting # these directives helps to reduce false positives and negatives. # -#SecUnicodeCodePage 20127 -#SecUnicodeMapFile unicode.mappinga +SecUnicodeMapFile unicode.mapping 20127 # Improve the quality of ModSecurity by sharing information about your # current ModSecurity version and dependencies versions. diff --git a/iis/wix/modsecurity_iis.conf b/iis/wix/modsecurity_iis.conf index 85d20ba74f..a18a4a6bca 100644 --- a/iis/wix/modsecurity_iis.conf +++ b/iis/wix/modsecurity_iis.conf @@ -1,3 +1 @@ Include modsecurity.conf -Include modsecurity_crs_10_setup.conf -Include owasp_crs\base_rules\*.conf diff --git a/iis/wix/unicode.mapping b/iis/wix/unicode.mapping new file mode 100644 index 0000000000..2654c4a619 --- /dev/null +++ b/iis/wix/unicode.mapping @@ -0,0 +1,96 @@ +(MAC - Roman) + + +(MAC - Icelandic) + + +1250 (ANSI - Central Europe) +00a1:21 00a2:63 00a3:4c 00a5:59 00aa:61 00b2:32 00b3:33 00b9:31 00ba:6f 00bc:31 00bd:31 00be:33 00c0:41 00c3:41 00c5:41 00c6:41 00c8:45 00ca:45 00cc:49 00cf:49 00d1:4e 00d2:4f 00d5:4f 00d8:4f 00d9:55 00db:55 00e0:61 00e3:61 00e5:61 00e6:61 00e8:65 00ea:65 00ec:69 00ef:69 00f1:6e 00f2:6f 00f5:6f 00f8:6f 00f9:75 00fb:75 00ff:79 0100:41 0101:61 0108:43 0109:63 010a:43 010b:63 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 013b:4c 013c:6c 0145:4e 0146:6e 014c:4f 014d:6f 014e:4f 014f:6f 0152:4f 0153:6f 0156:52 0157:72 015c:53 015d:73 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0180:62 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c0:7c 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2032:27 2035:60 203c:21 2044:2f 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2082:32 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:50 2119:50 211a:51 211b:52 211c:52 211d:52 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2191:5e 2194:2d 2195:7c 21a8:7c 2212:2d 2215:2f 2216:5c 2217:2a 221f:4c 2223:7c 2236:3a 223c:7e 2303:5e 2329:3c 232a:3e 2502:2d 250c:2d 2514:4c 2518:2d 251c:2b 2524:2b 252c:54 2534:2b 253c:2b 2550:3d 2554:2d 255a:4c 255d:2d 2566:54 256c:2b 2580:2d 2584:2d 2588:2d 2591:2d 2592:2d 2593:2d 25ac:2d 25b2:5e 25ba:3e 25c4:3c 25cb:30 25d9:30 263c:30 2640:2b 2642:3e 266a:64 266b:64 2758:7c 3000:20 3008:3c 3009:3e 301a:5b 301b:5d ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +1251 (ANSI - Cyrillic) +00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 203c:21 2190:3c 2191:5e 2192:3e 2193:76 2194:2d 221a:76 221f:4c 2500:2d 250c:2d 2514:4c 2518:2d 251c:2b 2524:2b 252c:54 2534:2b 253c:2b 2550:3d 2552:2d 2558:4c 2559:4c 255a:4c 255b:2d 255c:2d 255d:2d 2564:54 2565:54 2566:54 256a:2b 256b:2b 256c:2b 2580:2d 2584:2d 2588:2d 2591:2d 2592:2d 2593:2d 25ac:2d 25b2:5e 25ba:3e 25c4:3c 25cb:30 25d9:30 263a:4f 263b:4f 263c:30 2640:2b 2642:3e 266a:64 266b:64 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +1252 (ANSI - Latin I) +0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0179:5a 017b:5a 017c:7a 0180:62 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c0:7c 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c8:27 02cb:60 02cd:5f 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 037e:3b 0393:47 0398:54 03a3:53 03a6:46 03a9:4f 03b1:61 03b4:64 03b5:65 03c0:70 03c3:73 03c4:74 03c6:66 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2017:3d 2032:27 2035:60 2044:2f 2074:34 2075:35 2076:36 2077:37 2078:38 207f:6e 2080:30 2081:31 2082:32 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20a7:50 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:50 2119:50 211a:51 211b:52 211c:52 211d:52 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2212:2d 2215:2f 2216:5c 2217:2a 221a:76 221e:38 2223:7c 2229:6e 2236:3a 223c:7e 2261:3d 2264:3d 2265:3d 2303:5e 2320:28 2321:29 2329:3c 232a:3e 2500:2d 250c:2b 2510:2b 2514:2b 2518:2b 251c:2b 252c:2d 2534:2d 253c:2b 2550:2d 2552:2b 2553:2b 2554:2b 2555:2b 2556:2b 2557:2b 2558:2b 2559:2b 255a:2b 255b:2b 255c:2b 255d:2b 2564:2d 2565:2d 2566:2d 2567:2d 2568:2d 2569:2d 256a:2b 256b:2b 256c:2b 2584:5f 2758:7c 3000:20 3008:3c 3009:3e 301a:5b 301b:5d ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +1253 (ANSI - Greek) +00b4:2f 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 037e:3b 203c:21 2190:3c 2191:5e 2192:3e 2193:76 2194:2d 221f:4c 2500:2d 250c:2d 2514:4c 2518:2d 251c:2b 2524:2b 252c:54 2534:2b 253c:2b 2550:3d 2554:2d 255a:4c 255d:2d 2566:54 256c:2b 2580:2d 2584:2d 2588:2d 2591:2d 2592:2d 2593:2d 25ac:2d 25b2:5e 25ba:3e 25c4:3c 25cb:30 25d9:30 263a:4f 263b:4f 263c:30 2640:2b 2642:3e 266a:64 266b:64 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +1254 (ANSI - Turkish) +00dd:59 00fd:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c0:7c 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c7:5e 02c8:27 02cb:60 02cd:5f 02d8:5e 02d9:27 0300:60 0302:5e 0331:5f 0332:5f 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2032:27 2035:60 203c:21 2044:2f 2074:34 2075:35 2076:36 2077:37 2078:38 2081:30 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:50 2119:50 211a:51 211b:52 211c:52 211d:52 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2191:5e 2193:76 2194:2d 2195:7c 21a8:7c 2212:2d 2215:2f 2216:5c 2217:2a 221f:4c 2223:7c 2236:3a 223c:7e 2303:5e 2329:3c 232a:3e 2502:2d 250c:2d 2514:4c 2518:2d 251c:2b 2524:2b 252c:54 2534:2b 253c:2b 2550:3d 2554:2d 255a:4c 255d:2d 2566:54 256c:2b 2580:2d 2584:2d 2588:2d 2591:2d 2592:2d 2593:2d 25ac:2d 25b2:5e 25ba:3e 25c4:3c 25cb:30 25d9:30 263a:4f 263b:4f 263c:30 2640:2b 2642:3e 266a:64 266b:64 2758:7c 3000:20 3008:3c 3009:3e 301a:5b 301b:3d 301d:22 301e:22 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +1255 (ANSI - Hebrew) +0191:46 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +1256 (ANSI - Arabic) +0620:41 0621:41 0622:43 0623:45 0624:45 0625:45 0626:45 0627:49 0628:49 0629:4f 062a:55 062b:55 062c:55 062d:46 062e:43 062f:44 0630:45 0631:46 0632:47 0633:48 0634:49 0635:4a 0636:4b 0637:4c 0638:4d 0639:4e 063a:4f 0641:41 0642:42 0643:43 0644:44 0645:45 0646:46 0647:47 0648:48 0649:49 064a:4a 064b:4b 064c:4c 064d:4d 064e:4e 064f:4f 0650:50 0651:51 0652:52 + +1257 (ANSI - Baltic) +ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +1258 (ANSI/OEM - Viet Nam) +ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +20127 (US-ASCII) +00a0:20 00a1:21 00a2:63 00a4:24 00a5:59 00a6:7c 00a9:43 00aa:61 00ab:3c 00ad:2d 00ae:52 00b2:32 00b3:33 00b7:2e 00b8:2c 00b9:31 00ba:6f 00bb:3e 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c6:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d0:44 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e6:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:2c 201c:22 201d:22 201e:22 2022:2e 2026:2e 2032:27 2035:60 2039:3c 203a:3e 2122:54 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +20261 (T.61) +f8dd:5c f8de:5e f8df:60 f8e0:7b f8fc:7d f8fd:7e f8fe:7f + +20866 (Russian - KOI8) +00a7:15 00ab:3c 00ad:2d 00ae:52 00b1:2b 00b6:14 00bb:3e 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 2013:2d 2014:2d 2018:27 2019:27 201a:27 201c:22 201d:22 201e:22 2022:07 2026:3a 2030:25 2039:3c 203a:3e 203c:13 2122:54 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 221f:1c 2302:7f 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e + +28591 (ISO 8859-1 Latin I) +0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:2c 201c:22 201d:22 201e:22 2022:2e 2026:2e 2032:27 2035:60 2039:3c 203a:3e 2122:54 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +28592 (ISO 8859-2 Central Europe) +00a1:21 00a2:63 00a5:59 00a6:7c 00a9:43 00aa:61 00ab:3c 00ae:52 00b2:32 00b3:33 00b7:2e 00b9:31 00ba:6f 00bb:3e 00c0:41 00c3:41 00c5:41 00c6:41 00c8:45 00ca:45 00cc:49 00cf:49 00d0:44 00d1:4e 00d2:4f 00d5:4f 00d8:4f 00d9:55 00db:55 00e0:61 00e3:61 00e5:61 00e6:61 00e8:65 00ea:65 00ec:69 00ef:69 00f1:6e 00f2:6f 00f5:6f 00f8:6f 00f9:75 00fb:75 00ff:79 0100:41 0101:61 0108:43 0109:63 010a:43 010b:63 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 013b:4c 013c:6c 0145:4e 0146:6e 014c:4f 014d:6f 014e:4f 014f:6f 0152:4f 0153:6f 0156:52 0157:72 015c:53 015d:73 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0180:62 0189:44 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:2c 201c:22 201d:22 201e:22 2022:2e 2026:2e 2032:27 2035:60 2039:3c 203a:3e 2122:54 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +28605 (ISO 8859-15 Latin 9) +00a6:7c 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0138:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014a:4e 014b:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:54 0169:74 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0179:5a 017b:5a 017c:7a 0180:62 0189:44 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:2c 201c:22 201d:22 201e:22 2022:2e 2026:2e 2032:27 2035:60 2039:3c 203a:3e 2122:54 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +37 (IBM EBCDIC - U.S./Canada) +0004:37 0005:2d 0006:2e 0007:2f 0008:16 0009:05 000a:25 0014:3c 0015:3d 0016:32 0017:26 001a:3f 001b:27 0020:40 0021:5a 0022:7f 0023:7b 0024:5b 0025:6c 0026:50 0027:7d 0028:4d 0029:5d 002a:5c 002b:4e 002c:6b 002d:60 002e:4b 002f:61 003a:7a 003b:5e 003c:4c 003d:7e 003e:6e 003f:6f 0040:7c 005f:6d 0060:79 007c:4f 007f:07 0080:20 0081:21 0082:22 0083:23 0084:24 0085:15 0086:06 0087:17 0088:28 0089:29 008a:2a 008b:2b 008c:2c 008d:09 008e:0a 008f:1b 0090:30 0091:31 0092:1a 0093:33 0094:34 0095:35 0096:36 0097:08 0098:38 0099:39 009a:3a 009b:3b 009c:04 009d:14 009e:3e 00a0:41 00a2:4a 00a6:6a 00ac:5f 00c0:64 00c1:65 00c2:62 00c3:66 00c4:63 00c5:67 00c7:68 00c8:74 00c9:71 00ca:72 00cb:73 00cc:78 00cd:75 00ce:76 00cf:77 00d1:69 00df:59 00e0:44 00e1:45 00e2:42 00e3:46 00e4:43 00e5:47 00e7:48 00e8:54 00e9:51 00ea:52 00eb:53 00ec:58 00ed:55 00ee:56 00ef:57 00f1:49 00f8:70 ff01:5a ff02:7f ff03:7b ff04:5b ff05:6c ff06:50 ff07:7d ff08:4d ff09:5d ff0a:5c ff0b:4e ff0c:6b ff0d:60 ff0e:4b ff0f:61 ff1a:7a ff1b:5e ff1c:4c ff1d:7e ff1e:6e ff20:7c ff3f:6d ff40:79 ff5c:4f + +437 (OEM - United States) +00a4:0f 00a7:15 00a8:22 00a9:63 00ad:2d 00ae:72 00af:5f 00b3:33 00b4:27 00b6:14 00b8:2c 00b9:31 00be:5f 00c0:41 00c1:41 00c2:41 00c3:41 00c8:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d0:44 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d7:78 00d8:4f 00d9:55 00da:55 00db:55 00dd:59 00de:5f 00e3:61 00f0:64 00f5:6f 00f8:6f 00fd:79 00fe:5f 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c0:7c 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02ca:27 02cb:60 02cd:5f 02dc:7e 0300:60 0301:27 0302:5e 0303:7e 0308:22 030e:22 0327:2c 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2017:5f 2018:60 2019:27 201a:2c 201c:22 201d:22 201e:2c 2020:2b 2022:07 2026:2e 2030:25 2032:27 2035:60 2039:3c 203a:3e 203c:13 2044:2f 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2082:32 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20dd:09 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:50 2119:50 211a:51 211b:52 211c:52 211d:52 2122:54 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2212:2d 2215:2f 2216:5c 2217:2a 221f:1c 2223:7c 2236:3a 223c:7e 2302:7f 2303:5e 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 2758:7c 3000:20 3007:09 3008:3c 3009:3e 301a:5b 301b:5d ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +500 (IBM EBCDIC - International) +0004:37 0005:2d 0006:2e 0007:2f 0008:16 0009:05 000a:25 0014:3c 0015:3d 0016:32 0017:26 001a:3f 001b:27 0020:40 0021:4f 0022:7f 0023:7b 0024:5b 0025:6c 0026:50 0027:7d 0028:4d 0029:5d 002a:5c 002b:4e 002c:6b 002d:60 002e:4b 002f:61 003a:7a 003b:5e 003c:4c 003d:7e 003e:6e 003f:6f 0040:7c 005b:4a 005d:5a 005e:5f 005f:6d 0060:79 007f:07 0080:20 0081:21 0082:22 0083:23 0084:24 0085:15 0086:06 0087:17 0088:28 0089:29 008a:2a 008b:2b 008c:2c 008d:09 008e:0a 008f:1b 0090:30 0091:31 0092:1a 0093:33 0094:34 0095:35 0096:36 0097:08 0098:38 0099:39 009a:3a 009b:3b 009c:04 009d:14 009e:3e 00a0:41 00a6:6a 00c0:64 00c1:65 00c2:62 00c3:66 00c4:63 00c5:67 00c7:68 00c8:74 00c9:71 00ca:72 00cb:73 00cc:78 00cd:75 00ce:76 00cf:77 00d1:69 00df:59 00e0:44 00e1:45 00e2:42 00e3:46 00e4:43 00e5:47 00e7:48 00e8:54 00e9:51 00ea:52 00eb:53 00ec:58 00ed:55 00ee:56 00ef:57 00f1:49 00f8:70 ff01:4f ff02:7f ff03:7b ff04:5b ff05:6c ff06:50 ff07:7d ff08:4d ff09:5d ff0a:5c ff0b:4e ff0c:6b ff0d:60 ff0e:4b ff0f:61 ff1a:7a ff1b:5e ff1c:4c ff1d:7e ff1e:6e ff20:7c ff3b:4a ff3d:5a ff3e:5f ff3f:6d ff40:79 + +850 (OEM - Multilingual Latin I) +0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01a9:53 01ab:74 01ae:54 01af:55 01b0:75 01b6:5a 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:27 02cd:5f 02dc:7e 0300:27 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 037e:3b 0393:47 03a3:53 03a6:46 03a9:4f 03b1:61 03b4:64 03b5:65 03c0:70 03c3:73 03c4:74 03c6:66 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:27 201c:22 201d:22 201e:22 2022:07 2024:07 2026:2e 2030:25 2039:3c 203a:3e 203c:13 2044:2f 2070:30 2074:34 2075:35 2076:36 2077:37 2078:39 207f:6e 2080:30 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20a7:50 20dd:4f 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:50 2119:50 211a:51 211b:52 211c:52 211d:52 2122:54 2124:5a 2126:4f 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2211:53 2212:2d 2215:2f 2216:2f 2217:2a 2219:07 221a:56 221e:38 221f:1c 2229:6e 2236:3a 223c:7e 2248:7e 2261:3d 2264:3d 2265:3d 2302:7f 2303:5e 2320:28 2321:29 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 2713:56 3000:20 3007:4f 3008:3c 3009:3e 301a:5b 301b:5d ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +860 (OEM - Portuguese) +00a4:0f 00a5:59 00a7:15 00a8:22 00a9:63 00ad:5f 00ae:72 00af:16 00b3:33 00b4:2f 00b6:14 00b8:2c 00b9:31 00be:33 00c4:41 00c5:41 00c6:41 00cb:45 00ce:49 00cf:49 00d0:44 00d6:4f 00d7:58 00d8:4f 00db:55 00dd:59 00de:54 00e4:61 00e5:61 00e6:61 00eb:65 00ee:69 00ef:69 00f0:64 00f6:6f 00f8:6f 00fb:75 00fd:79 00fe:74 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:5c 0161:7c 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c0:7c 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 0278:66 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02c9:16 02ca:2f 02cb:60 02cd:5f 02dc:7e 0300:60 0301:2f 0302:5e 0303:7e 0304:16 0305:16 0308:22 030e:22 0327:2c 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:5f 2011:5f 2013:5f 2014:5f 2017:5f 2018:27 2019:27 201a:2c 201c:22 201d:22 201e:22 2022:07 2024:07 2026:2e 2030:25 2032:27 2035:60 2039:3c 203a:3e 203c:13 2044:2f 2070:30 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20dd:4f 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:70 2119:50 211a:51 211b:52 211c:52 211d:52 2122:74 2124:5a 2128:5a 212a:4b 212b:41 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2205:4f 2212:5f 2215:2f 2216:5c 2217:2a 221f:1c 2223:7c 2236:3a 223c:7e 22c5:07 2302:7f 2303:5e 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 3000:20 3007:4f 3008:3c 3009:3e 301a:5b 301b:5d 30fb:07 + +861 (OEM - Icelandic) +00a2:63 00a4:0f 00a5:59 00a7:15 00a8:22 00a9:63 00aa:61 00ad:5f 00ae:72 00af:16 00b3:33 00b4:2f 00b6:14 00b8:2c 00b9:31 00ba:6f 00be:33 00c0:41 00c2:41 00c3:41 00c8:45 00ca:45 00cb:45 00cc:49 00ce:49 00cf:49 00d1:4e 00d2:4f 00d4:4f 00d5:4f 00d7:58 00d9:55 00db:55 00e3:61 00ec:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f5:6f 00f9:75 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 0278:66 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02c9:16 02ca:2f 02cb:60 02cd:5f 02dc:7e 0300:60 0301:2f 0302:5e 0303:7e 0304:16 0305:16 0308:22 030e:22 0327:2c 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2017:5f 2018:27 2019:27 201a:27 201c:22 201d:22 201e:22 2022:07 2024:07 2026:07 2030:25 2032:27 2035:27 2039:3c 203a:3e 203c:13 2044:2f 2070:30 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20dd:4f 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:70 2119:50 211a:51 211b:52 211c:52 211d:52 2122:74 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2205:4f 2212:5f 2215:2f 2216:5c 2217:2a 221f:1c 2223:7c 2236:3a 223c:7e 22c5:07 2302:7f 2303:5e 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 3000:20 3007:4f 3008:3c 3009:3e 301a:5b 301b:5d 30fb:07 + +863 (OEM - Canadian French) +00a1:21 00a5:59 00a9:63 00aa:61 00ad:16 00ae:72 00b9:33 00ba:6f 00c1:41 00c3:41 00c4:41 00c5:41 00c6:41 00cc:49 00cd:49 00d0:44 00d1:4e 00d2:4f 00d3:4f 00d5:4f 00d6:4f 00d7:58 00d8:4f 00da:55 00dd:59 00de:54 00e1:61 00e3:61 00e4:61 00e5:61 00e6:61 00ec:69 00ed:69 00f0:64 00f1:6e 00f2:6f 00f5:6f 00f6:6f 00f8:6f 00fd:79 00fe:74 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:22 02ba:27 02bc:27 02c4:5e 02c6:5e 02c8:27 02c9:16 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 0304:16 0305:16 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:27 201c:22 201d:22 201e:22 2022:07 2024:07 2026:07 2030:25 2032:27 2035:27 2039:3c 203a:3e 203c:13 2044:2f 2070:30 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20a7:50 20dd:4f 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:70 2119:50 211a:51 211b:52 211c:52 211d:52 2122:74 2124:5a 2128:5a 212a:4b 212b:41 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2205:4f 2212:5f 2215:2f 2216:5c 2217:2a 221f:1c 2223:7c 2236:3a 223c:7e 22c5:07 2302:7f 2303:5e 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 3000:20 3007:4f 3008:3c 3009:3e 301a:5b 301b:5d 30fb:07 + +865 (OEM - Nordic) +00a2:63 00a5:59 00a7:15 00a8:22 00a9:63 00ad:5f 00ae:72 00af:16 00b3:33 00b4:2f 00b6:14 00b8:2c 00b9:31 00bb:3e 00be:33 00c0:41 00c1:41 00c2:41 00c3:41 00c8:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d0:44 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d7:58 00d9:55 00da:55 00db:55 00dd:59 00de:54 00e3:61 00f0:64 00f5:6f 00fd:79 00fe:74 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02c9:16 02ca:2f 02cb:60 02cd:5f 02dc:7e 0300:60 0301:2f 0302:5e 0303:7e 0304:16 0305:16 0308:22 030e:22 0327:2c 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2017:5f 2018:27 2019:27 201a:27 201c:22 201d:22 201e:22 2022:07 2024:07 2026:07 2030:25 2032:27 2035:27 2039:3c 203a:3e 203c:13 2044:2f 2070:30 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20dd:4f 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:70 2119:50 211a:51 211b:52 211c:52 211d:52 2122:74 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2205:4f 2212:5f 2215:2f 2216:5c 2217:2a 221f:1c 2223:7c 2236:3a 223c:7e 226b:3c 22c5:07 2302:7f 2303:5e 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 3000:20 3007:4f 3008:3c 3009:3e 300b:3e 301a:5b 301b:5d 30fb:07 + +874 (ANSI/OEM - Thai) +00a7:15 00b6:14 203c:13 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 221f:1c 2302:7f 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +932 (ANSI/OEM - Japanese Shift-JIS) +00a1:21 00a5:5c 00a6:7c 00a9:63 00aa:61 00ad:2d 00ae:52 00b2:32 00b3:33 00b9:31 00ba:6f 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c6:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d0:44 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00de:54 00df:73 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e6:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f0:64 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00fe:74 00ff:79 + +936 (ANSI/OEM - Simplified Chinese GBK) +00a6:7c 00aa:61 00ad:2d 00b2:32 00b3:33 00b9:31 00ba:6f 00d0:44 00dd:59 00de:54 00e2:61 00f0:65 00fd:79 00fe:74 + +949 (ANSI/OEM - Korean) +00a6:7c 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00ff:79 20a9:5c + +950 (ANSI/OEM - Traditional Chinese Big5) +00a1:21 00a6:7c 00a9:63 00aa:61 00ad:2d 00ae:52 00b2:32 00b3:33 00b9:31 00ba:6f 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c6:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d0:44 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00de:54 00df:73 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e6:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f0:65 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00fe:74 00ff:79 + +(UTF-7) + + +(UTF-8) + + diff --git a/mlogc/Makefile.am b/mlogc/Makefile.am index e84e3de504..6946ca492e 100644 --- a/mlogc/Makefile.am +++ b/mlogc/Makefile.am @@ -7,17 +7,21 @@ mlogc_SOURCES = mlogc.c mlogc_CPPFLAGS = @APR_CPPFLAGS@ \ @PCRE_CPPFLAGS@ \ + @PCRE2_CPPFLAGS@ \ @CURL_CPPFLAGS@ \ -I$(top_srcdir)/apache2 mlogc_CFLAGS = @APR_CFLAGS@ \ @CURL_CFLAGS@ \ - @PCRE_CFLAGS@ + @PCRE_CFLAGS@ \ + @PCRE2_CFLAGS@ mlogc_LDFLAGS = @APR_LDFLAGS@ \ @CURL_LDFLAGS@ \ - @PCRE_LDFLAGS@ + @PCRE_LDFLAGS@ \ + @PCRE2_LDFLAGS@ mlogc_LDADD = @APR_LDADD@ \ @CURL_LDADD@ \ - @PCRE_LDADD@ + @PCRE_LDADD@ \ + @PCRE2_LDADD@ diff --git a/mlogc/mlogc.c b/mlogc/mlogc.c index e650452dc4..7a9edaa12b 100644 --- a/mlogc/mlogc.c +++ b/mlogc/mlogc.c @@ -1,6 +1,6 @@ /* * ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/) +* Copyright (c) 2004-2022 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 @@ -28,7 +28,12 @@ #if APR_HAVE_UNISTD_H #include /* for getpid() */ #endif +#ifndef WITH_PCRE +#define PCRE2_CODE_UNIT_WIDTH 8 +#include +#else #include +#endif #include #include #include @@ -96,7 +101,7 @@ do { \ static const char logline_pattern[] = "^(\\S+)" "\\ (\\S+)\\ (\\S+)\\ (\\S+)" - "\\ \\[([^:]+):(\\d+:\\d+:\\d+)\\ ([^\\]]+)\\]" + "\\ \\[([^:]+):(\\d+:\\d+:\\d+(?:[.]\\d+)?)\\ ([^\\]]+)\\]" "\\ \"(.*)\"" "\\ (\\d+)\\ (\\S+)" "\\ \"(.*)\"\\ \"(.*)\"" @@ -147,7 +152,13 @@ static int keep_alive = 150; /* Not used yet. */ static int keep_alive_timeout = 300; /* Not used yet. */ static int keep_entries = 0; static const char *log_repository = NULL; +#ifndef WITH_PCRE +static pcre2_code *logline_regex = NULL; +static pcre2_code *requestline_regex = NULL; +#else static void *logline_regex = NULL; +static void *requestline_regex = NULL; +#endif static int max_connections = 10; static int max_worker_requests = 1000; static apr_global_mutex_t *gmutex = NULL; @@ -161,7 +172,6 @@ static int ssl_validation = 0; static int tlsprotocol = 1; static curl_version_info_data* curlversion = NULL; /* static apr_time_t queue_time = 0; */ -static void *requestline_regex = NULL; static int running = 0; static const char *sensor_password = NULL; static const char *sensor_username = NULL; @@ -1208,6 +1218,10 @@ static void logc_init(void) int i, erroffset; /* cURL major, minor and patch version */ short cmaj, cmin, cpat = 0; +#ifndef WITH_PCRE + int pcre2_errorcode = 0; + PCRE2_SIZE pcre2_erroffset = 0; +#endif queue = apr_array_make(pool, 64, sizeof(entry_t *)); if (queue == NULL) { @@ -1311,16 +1325,26 @@ static void logc_init(void) error_log(LOG_DEBUG2, NULL, "TLSv1.2 is unsupported in cURL %d.%d.%d", cmaj, cmin, cpat); } +#ifndef WITH_PCRE + logline_regex = pcre2_compile(logline_pattern, PCRE2_ZERO_TERMINATED, PCRE2_CASELESS, + &pcre2_errorcode, &pcre2_erroffset, NULL); +#else logline_regex = pcre_compile(logline_pattern, PCRE_CASELESS, &errptr, &erroffset, NULL); +#endif if (logline_regex == NULL) { error_log(LOG_ERROR, NULL, "Failed to compile pattern: %s\n", logline_pattern); logc_shutdown(1); } - requestline_regex = pcre_compile(requestline_pattern, - PCRE_CASELESS, &errptr, &erroffset, NULL); +#ifndef WITH_PCRE + requestline_regex = pcre2_compile(requestline_pattern, PCRE2_ZERO_TERMINATED, PCRE2_CASELESS, + &pcre2_errorcode, &pcre2_erroffset, NULL); +#else + requestline_regex = pcre_compile(requestline_pattern, PCRE_CASELESS, + &errptr, &erroffset, NULL); +#endif if (requestline_regex == NULL) { error_log(LOG_ERROR, NULL, "Failed to compile pattern: %s\n", requestline_pattern); @@ -1431,6 +1455,9 @@ static void * APR_THREAD_FUNC thread_worker(apr_thread_t *thread, void *data) apr_status_t rc; apr_finfo_t finfo; int capturevector[CAPTUREVECTORSIZE]; +#ifndef WITH_PCRE + pcre2_match_data *pcre2_match_data = NULL; +#endif int take_new = 1; apr_pool_t *tpool; struct curl_slist *headerlist = NULL; @@ -1536,9 +1563,24 @@ static void * APR_THREAD_FUNC thread_worker(apr_thread_t *thread, void *data) num_requests++; } +#ifndef WITH_PCRE + pcre2_match_data = pcre2_match_data_create_from_pattern(logline_regex, NULL); + rc = pcre2_match(logline_regex, entry->line, entry->line_size, 0, 0, + pcre2_match_data, NULL); + if (rc > 0) { + PCRE2_SIZE *pcre2_ovector = pcre2_get_ovector_pointer(pcre2_match_data); + for (int i = 0; i < rc; i++) { + capturevector[2*i] = pcre2_ovector[2*i]; + capturevector[2*i+1] = pcre2_ovector[2*i+1]; + } + } + pcre2_match_data_free(pcre2_match_data); + if (rc == PCRE2_ERROR_NOMATCH) { +#else rc = pcre_exec(logline_regex, NULL, entry->line, entry->line_size, 0, 0, capturevector, CAPTUREVECTORSIZE); - if (rc == PCRE_ERROR_NOMATCH) { /* No match. */ + if (rc == PCRE_ERROR_NOMATCH) { +#endif error_log(LOG_WARNING, thread, "Invalid entry (failed to match regex): %s", _log_escape(tpool, entry->line, entry->line_size)); @@ -2292,6 +2334,11 @@ static void usage(void) { * Version text. */ static void version(void) { +#ifndef WITH_PCRE + char pcre2_loaded_version_buffer[80] ={0}; + char *pcre_loaded_version = pcre2_loaded_version_buffer; + pcre2_config(PCRE2_CONFIG_VERSION, pcre_loaded_version); +#endif fprintf(stderr, "ModSecurity Log Collector (mlogc) v%s\n", VERSION); fprintf(stderr, @@ -2299,7 +2346,11 @@ static void version(void) { "loaded=\"%s\"\n", APR_VERSION_STRING, apr_version_string()); fprintf(stderr, " PCRE: compiled=\"%d.%d\"; " +#ifndef WITH_PCRE + "loaded=\"%s\"\n", PCRE2_MAJOR, PCRE2_MINOR, pcre_loaded_version); +#else "loaded=\"%s\"\n", PCRE_MAJOR, PCRE_MINOR, pcre_version()); +#endif fprintf(stderr, " cURL: compiled=\"%s\"; " "loaded=\"%s\"\n", LIBCURL_VERSION, curl_version()); diff --git a/modsecurity.conf-recommended b/modsecurity.conf-recommended index 728afc1afd..e120daef88 100644 --- a/modsecurity.conf-recommended +++ b/modsecurity.conf-recommended @@ -19,16 +19,23 @@ SecRequestBodyAccess On # Enable XML request body parser. # Initiate XML Processor in case of xml content-type # -SecRule REQUEST_HEADERS:Content-Type "(?:application(?:/soap\+|/)|text/)xml" \ +SecRule REQUEST_HEADERS:Content-Type "^(?:application(?:/soap\+|/)|text/)xml" \ "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML" # Enable JSON request body parser. # Initiate JSON Processor in case of JSON content-type; change accordingly # if your application does not use 'application/json' # -SecRule REQUEST_HEADERS:Content-Type "application/json" \ +SecRule REQUEST_HEADERS:Content-Type "^application/json" \ "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" +# Sample rule to enable JSON request body parser for more subtypes. +# Uncomment or adapt this rule if you want to engage the JSON +# Processor for "+json" subtypes +# +#SecRule REQUEST_HEADERS:Content-Type "^application/[a-z0-9.-]+[+]json" \ +# "id:'200006',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" + # Maximum request body size we will accept for buffering. If you support # file uploads then the value given on the first line has to be as large # as the largest file you are willing to accept. The second value refers @@ -39,7 +46,7 @@ SecRequestBodyLimit 13107200 SecRequestBodyNoFilesLimit 131072 # Store up to 128 KB of request body data in memory. When the multipart -# parser reachers this limit, it will start using your hard disk for +# parser reaches this limit, it will start using your hard disk for # storage. That is slow, but unavoidable. # SecRequestBodyInMemoryLimit 131072 @@ -51,6 +58,11 @@ SecRequestBodyInMemoryLimit 131072 # SecRequestBodyLimitAction Reject +# Maximum parsing depth allowed for JSON objects. You want to keep this +# value as low as practical. +# +SecRequestBodyJsonDepthLimit 512 + # Verify that we've correctly processed the request body. # As a rule of thumb, when failing to process a request body # you should reject the request (when deployed in blocking mode) @@ -97,7 +109,7 @@ SecPcreMatchLimitRecursion 1000 # MSC_PCRE_LIMITS_EXCEEDED: PCRE match limits were exceeded. # SecRule TX:/^MSC_/ "!@streq 0" \ - "id:'200005',phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'" + "id:'200005',phase:2,t:none,log,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'" # -- Response body handling -------------------------------------------------- @@ -222,5 +234,7 @@ SecUnicodeMapFile unicode.mapping 20127 # The following information will be shared: ModSecurity version, # Web Server version, APR version, PCRE version, Lua version, Libxml2 # version, Anonymous unique id for host. -SecStatusEngine On +# NB: As of April 2022, there is no longer any advantage to turning this +# setting On, as there is no active receiver for the information. +SecStatusEngine Off diff --git a/standalone/config.c b/standalone/config.c index 800d5b4344..6133e7d7e8 100644 --- a/standalone/config.c +++ b/standalone/config.c @@ -742,12 +742,12 @@ AP_DECLARE(char *) ap_make_full_path(apr_pool_t *a, const char *src1, return path; } -static int fname_alphasort(const void *fn1, const void *fn2) +static int fname_reversealphasort(const void *fn1, const void *fn2) { const fnames *f1 = fn1; const fnames *f2 = fn2; - return strcmp(f1->fname,f2->fname); + return strcmp(f2->fname,f1->fname); } int fnmatch_test(const char *pattern) @@ -840,7 +840,7 @@ static const char *process_resource_config_nofnmatch(const char *fname, apr_dir_close(dirp); if (candidates->nelts != 0) { qsort((void *) candidates->elts, candidates->nelts, - sizeof(fnames), fname_alphasort); + sizeof(fnames), fname_reversealphasort); /* * Now recurse these... we handle errors and subdirectories @@ -941,7 +941,7 @@ static const char *process_resource_config_fnmatch(const char *path, const char *error; qsort((void *) candidates->elts, candidates->nelts, - sizeof(fnames), fname_alphasort); + sizeof(fnames), fname_reversealphasort); /* * Now recurse these... we handle errors and subdirectories @@ -1028,7 +1028,8 @@ const char *process_command_config(server_rec *s, ap_directive_t *newdir; int optional; char *err = NULL; - char *rootpath, *incpath; + const char *rootpath, *incpath; + char *configfilepath; int li; errmsg = populate_include_files(p, ptemp, ari, filename, 0); @@ -1108,13 +1109,13 @@ const char *process_command_config(server_rec *s, /* we allow APR_SUCCESS and APR_EINCOMPLETE */ if (APR_ERELATIVE == status) { - rootpath = apr_pstrdup(ptemp, parms->config_file->name); - li = strlen(rootpath) - 1; + configfilepath = apr_pstrdup(ptemp, parms->config_file->name); + li = strlen(configfilepath) - 1; - while(li >= 0 && rootpath[li] != '/' && rootpath[li] != '\\') - rootpath[li--] = 0; + while(li >= 0 && configfilepath[li] != '/' && configfilepath[li] != '\\') + configfilepath[li--] = 0; - w = apr_pstrcat(p, rootpath, w, NULL); + w = apr_pstrcat(p, configfilepath, w, NULL); } else if (APR_EBADPATH == status) { ap_cfg_closefile(parms->config_file); @@ -1201,3 +1202,4 @@ const char *process_command_config(server_rec *s, return errmsg; } + diff --git a/standalone/regex.c b/standalone/regex.c index 495a18eb97..cdc79059fe 100644 --- a/standalone/regex.c +++ b/standalone/regex.c @@ -1,162 +1,242 @@ -/* -* ModSecurity for Apache 2.x, http://www.modsecurity.org/ -* Copyright (c) 2004-2013 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 -* -*     http://www.apache.org/licenses/LICENSE-2.0 -* -* If any of the files related to licensing are missing or if you have any -* other questions related to licensing please contact Trustwave Holdings, Inc. -* directly using the email address security@modsecurity.org. -*/ - -#include - -#include "http_core.h" -#include "http_request.h" - -#include "modsecurity.h" -#include "apache2.h" -#include "http_main.h" -#include "http_connection.h" - -#include "apr_optional.h" -#include "mod_log_config.h" - -#include "msc_logging.h" -#include "msc_util.h" - -#include "ap_mpm.h" -#include "scoreboard.h" - -#include "apr_version.h" - -#include "apr_lib.h" -#include "ap_config.h" -#include "http_config.h" - - -static apr_status_t regex_cleanup(void *preg) -{ - ap_regfree((ap_regex_t *) preg); - return APR_SUCCESS; -} - -AP_DECLARE(ap_regex_t *) ap_pregcomp(apr_pool_t *p, const char *pattern, - int cflags) -{ - ap_regex_t *preg = apr_palloc(p, sizeof *preg); - - if (ap_regcomp(preg, pattern, cflags)) { - return NULL; - } - - apr_pool_cleanup_register(p, (void *) preg, regex_cleanup, - apr_pool_cleanup_null); - - return preg; -} - -AP_DECLARE(void) ap_regfree(ap_regex_t *preg) -{ -(pcre_free)(preg->re_pcre); -} - -AP_DECLARE(int) ap_regcomp(ap_regex_t *preg, const char *pattern, int cflags) -{ -const char *errorptr; -int erroffset; -int options = 0; -int nsub = 0; - -if ((cflags & AP_REG_ICASE) != 0) options |= PCRE_CASELESS; -if ((cflags & AP_REG_NEWLINE) != 0) options |= PCRE_MULTILINE; - -preg->re_pcre = pcre_compile(pattern, options, &errorptr, &erroffset, NULL); -preg->re_erroffset = erroffset; - -if (preg->re_pcre == NULL) return AP_REG_INVARG; - -pcre_fullinfo((const pcre *)preg->re_pcre, NULL, PCRE_INFO_CAPTURECOUNT, &nsub); -preg->re_nsub = nsub; -return 0; -} - -#ifndef POSIX_MALLOC_THRESHOLD -#define POSIX_MALLOC_THRESHOLD (10) -#endif - -AP_DECLARE(int) ap_regexec(const ap_regex_t *preg, const char *string, - apr_size_t nmatch, ap_regmatch_t pmatch[], - int eflags) -{ -int rc; -int options = 0; -int *ovector = NULL; -int small_ovector[POSIX_MALLOC_THRESHOLD * 3]; -int allocated_ovector = 0; - -if ((eflags & AP_REG_NOTBOL) != 0) options |= PCRE_NOTBOL; -if ((eflags & AP_REG_NOTEOL) != 0) options |= PCRE_NOTEOL; - -((ap_regex_t *)preg)->re_erroffset = (apr_size_t)(-1); /* Only has meaning after compile */ - -if (nmatch > 0) - { - if (nmatch <= POSIX_MALLOC_THRESHOLD) - { - ovector = &(small_ovector[0]); - } - else - { - ovector = (int *)malloc(sizeof(int) * nmatch * 3); - if (ovector == NULL) return AP_REG_ESPACE; - allocated_ovector = 1; - } - } - -rc = pcre_exec((const pcre *)preg->re_pcre, NULL, string, (int)strlen(string), - 0, options, ovector, nmatch * 3); - -if (rc == 0) rc = nmatch; /* All captured slots were filled in */ - -if (rc >= 0) - { - apr_size_t i; - for (i = 0; i < (apr_size_t)rc; i++) - { - pmatch[i].rm_so = ovector[i*2]; - pmatch[i].rm_eo = ovector[i*2+1]; - } - if (allocated_ovector) free(ovector); - for (; i < nmatch; i++) pmatch[i].rm_so = pmatch[i].rm_eo = -1; - return 0; - } - -else - { - if (allocated_ovector) free(ovector); - switch(rc) - { - case PCRE_ERROR_NOMATCH: return AP_REG_NOMATCH; - case PCRE_ERROR_NULL: return AP_REG_INVARG; - case PCRE_ERROR_BADOPTION: return AP_REG_INVARG; - case PCRE_ERROR_BADMAGIC: return AP_REG_INVARG; - case PCRE_ERROR_UNKNOWN_NODE: return AP_REG_ASSERT; - case PCRE_ERROR_NOMEMORY: return AP_REG_ESPACE; -#ifdef PCRE_ERROR_MATCHLIMIT - case PCRE_ERROR_MATCHLIMIT: return AP_REG_ESPACE; -#endif -#ifdef PCRE_ERROR_BADUTF8 - case PCRE_ERROR_BADUTF8: return AP_REG_INVARG; -#endif -#ifdef PCRE_ERROR_BADUTF8_OFFSET - case PCRE_ERROR_BADUTF8_OFFSET: return AP_REG_INVARG; -#endif - default: return AP_REG_ASSERT; - } - } -} - +/* +* ModSecurity for Apache 2.x, http://www.modsecurity.org/ +* Copyright (c) 2004-2013 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 +* +*     http://www.apache.org/licenses/LICENSE-2.0 +* +* If any of the files related to licensing are missing or if you have any +* other questions related to licensing please contact Trustwave Holdings, Inc. +* directly using the email address security@modsecurity.org. +*/ + +#include + +#include "http_core.h" +#include "http_request.h" + +#include "modsecurity.h" +#include "apache2.h" +#include "http_main.h" +#include "http_connection.h" + +#include "apr_optional.h" +#include "mod_log_config.h" + +#include "msc_logging.h" +#include "msc_util.h" + +#include "ap_mpm.h" +#include "scoreboard.h" + +#include "apr_version.h" + +#include "apr_lib.h" +#include "ap_config.h" +#include "http_config.h" + + +static apr_status_t regex_cleanup(void *preg) +{ + ap_regfree((ap_regex_t *) preg); + return APR_SUCCESS; +} + +AP_DECLARE(ap_regex_t *) ap_pregcomp(apr_pool_t *p, const char *pattern, + int cflags) +{ + ap_regex_t *preg = apr_palloc(p, sizeof *preg); + + if (ap_regcomp(preg, pattern, cflags)) { + return NULL; + } + + apr_pool_cleanup_register(p, (void *) preg, regex_cleanup, + apr_pool_cleanup_null); + + return preg; +} + +AP_DECLARE(void) ap_regfree(ap_regex_t *preg) +{ +#ifndef WITH_PCRE +(pcre2_code_free)(preg->re_pcre); +#else +(pcre_free)(preg->re_pcre); +#endif +} + +AP_DECLARE(int) ap_regcomp(ap_regex_t *preg, const char *pattern, int cflags) +{ +const char *errorptr; +int erroffset; +int options = 0; +int nsub = 0; + +#ifndef WITH_PCRE +if ((cflags & AP_REG_ICASE) != 0) options |= PCRE2_CASELESS; +if ((cflags & AP_REG_NEWLINE) != 0) options |= PCRE2_MULTILINE; +int error_number = 0; +PCRE2_SIZE error_offset = 0; +PCRE2_SPTR pcre2_pattern = (PCRE2_SPTR)pattern; + +preg->re_pcre = pcre2_compile(pcre2_pattern, PCRE2_ZERO_TERMINATED, + options, &error_number, &error_offset, NULL); +preg->re_erroffset = error_offset; + +if (preg->re_pcre == NULL) return AP_REG_INVARG; + +pcre2_pattern_info((const pcre2_code *)preg->re_pcre, PCRE2_INFO_CAPTURECOUNT, &nsub); +preg->re_nsub = nsub; + +#else // otherwise use PCRE +if ((cflags & AP_REG_ICASE) != 0) options |= PCRE_CASELESS; +if ((cflags & AP_REG_NEWLINE) != 0) options |= PCRE_MULTILINE; + +preg->re_pcre = pcre_compile(pattern, options, &errorptr, &erroffset, NULL); +preg->re_erroffset = erroffset; + +if (preg->re_pcre == NULL) return AP_REG_INVARG; + +pcre_fullinfo((const pcre *)preg->re_pcre, NULL, PCRE_INFO_CAPTURECOUNT, &nsub); +preg->re_nsub = nsub; +#endif // end of WITH_PCRE +return 0; +} + +#ifndef POSIX_MALLOC_THRESHOLD +#define POSIX_MALLOC_THRESHOLD (10) +#endif + +AP_DECLARE(int) ap_regexec(const ap_regex_t *preg, const char *string, + apr_size_t nmatch, ap_regmatch_t pmatch[], + int eflags) +{ +int rc; +int options = 0; +int *ovector = NULL; +int small_ovector[POSIX_MALLOC_THRESHOLD * 3]; +int allocated_ovector = 0; + +#ifndef WITH_PCRE +if ((eflags & AP_REG_NOTBOL) != 0) options |= PCRE2_NOTBOL; +if ((eflags & AP_REG_NOTEOL) != 0) options |= PCRE2_NOTEOL; +#else +if ((eflags & AP_REG_NOTBOL) != 0) options |= PCRE_NOTBOL; +if ((eflags & AP_REG_NOTEOL) != 0) options |= PCRE_NOTEOL; +#endif + +((ap_regex_t *)preg)->re_erroffset = (apr_size_t)(-1); /* Only has meaning after compile */ + +if (nmatch > 0) + { + if (nmatch <= POSIX_MALLOC_THRESHOLD) + { + ovector = &(small_ovector[0]); + } + else + { + ovector = (int *)malloc(sizeof(int) * nmatch * 3); + if (ovector == NULL) return AP_REG_ESPACE; + allocated_ovector = 1; + } + } + +#ifndef WITH_PCRE +{ + PCRE2_SPTR pcre2_s; + int pcre2_ret; + pcre2_match_data *match_data; + PCRE2_SIZE *pcre2_ovector = NULL; + + pcre2_s = (PCRE2_SPTR)string; + match_data = pcre2_match_data_create_from_pattern(preg->re_pcre, NULL); + pcre2_match_context *match_context = pcre2_match_context_create(NULL); + + pcre2_ret = pcre2_match((const pcre2_code *)preg->re_pcre, pcre2_s, (int)strlen(string), + 0, (uint32_t)options, match_data, match_context); + + if (match_data != NULL) { + pcre2_ovector = pcre2_get_ovector_pointer(match_data); + if (pcre2_ovector != NULL) { + for (int i = 0; ((i < pcre2_ret) && ((i*2) <= nmatch * 3)); i++) { + if ((i*2) < nmatch * 3) { + ovector[2*i] = pcre2_ovector[2*i]; + ovector[2*i+1] = pcre2_ovector[2*i+1]; + } + } + } + pcre2_match_data_free(match_data); + pcre2_match_context_free(match_context); + } + /* + pcre2_match() returns one more than the highest numbered capturing pair + that has been set (for example, 1 if there are no captures) - see pcre2_match's manual + */ + rc = pcre2_ret - 1; +} +#else +rc = pcre_exec((const pcre *)preg->re_pcre, NULL, string, (int)strlen(string), + 0, options, ovector, nmatch * 3); +#endif + +if (rc == 0) rc = nmatch; /* All captured slots were filled in */ + +if (rc >= 0) + { + apr_size_t i; + for (i = 0; i < (apr_size_t)rc; i++) + { + pmatch[i].rm_so = ovector[i*2]; + pmatch[i].rm_eo = ovector[i*2+1]; + } + if (allocated_ovector) free(ovector); + for (; i < nmatch; i++) pmatch[i].rm_so = pmatch[i].rm_eo = -1; + return 0; + } + +else + { + if (allocated_ovector) free(ovector); + switch(rc) + { +#ifndef WITH_PCRE + case PCRE2_ERROR_NOMATCH: return AP_REG_NOMATCH; + case PCRE2_ERROR_NULL: return AP_REG_INVARG; + case PCRE2_ERROR_BADOPTION: return AP_REG_INVARG; + case PCRE2_ERROR_BADMAGIC: return AP_REG_INVARG; + // case PCRE2_ERROR_UNKNOWN_NODE: return AP_REG_ASSERT; not defined in PCRE2 + case PCRE2_ERROR_NOMEMORY: return AP_REG_ESPACE; +#ifdef PCRE2_ERROR_MATCHLIMIT + case PCRE2_ERROR_MATCHLIMIT: return AP_REG_ESPACE; +#endif +#ifdef PCRE2_ERROR_BADUTF8 + case PCRE2_ERROR_BADUTF8: return AP_REG_INVARG; +#endif +#ifdef PCRE2_ERROR_BADUTF8_OFFSET + case PCRE2_ERROR_BADUTF8_OFFSET: return AP_REG_INVARG; +#endif +#else // with old PCRE + case PCRE_ERROR_NOMATCH: return AP_REG_NOMATCH; + case PCRE_ERROR_NULL: return AP_REG_INVARG; + case PCRE_ERROR_BADOPTION: return AP_REG_INVARG; + case PCRE_ERROR_BADMAGIC: return AP_REG_INVARG; + case PCRE_ERROR_UNKNOWN_NODE: return AP_REG_ASSERT; + case PCRE_ERROR_NOMEMORY: return AP_REG_ESPACE; +#ifdef PCRE_ERROR_MATCHLIMIT + case PCRE_ERROR_MATCHLIMIT: return AP_REG_ESPACE; +#endif +#ifdef PCRE_ERROR_BADUTF8 + case PCRE_ERROR_BADUTF8: return AP_REG_INVARG; +#endif +#ifdef PCRE_ERROR_BADUTF8_OFFSET + case PCRE_ERROR_BADUTF8_OFFSET: return AP_REG_INVARG; +#endif +#endif // end of WITH_PCRE + default: return AP_REG_ASSERT; + } + } +} + diff --git a/tests/Makefile.am b/tests/Makefile.am index 8ffc3bb3db..181a5aa7ed 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -40,16 +40,18 @@ msc_test_CFLAGS = @APR_CFLAGS@ \ @LUA_CFLAGS@ \ @MODSEC_EXTRA_CFLAGS@ \ @PCRE_CFLAGS@ \ + @PCRE2_CFLAGS@ \ @YAJL_CFLAGS@ \ @SSDEEP_CFLAGS@ - + msc_test_CPPFLAGS = -I$(top_srcdir)/apache2 \ @APR_CPPFLAGS@ \ @CURL_CPPFLAGS@ \ @LIBXML2_CFLAGS@ \ @LIBXML2_CPPFLAGS@ \ - @PCRE_CPPFLAGS@ - + @PCRE_CPPFLAGS@ \ + @PCRE2_CPPFLAGS@ + msc_test_LDADD = @APR_LDADD@ \ @APU_LDADD@ \ @CURL_LDADD@ \ @@ -57,6 +59,7 @@ msc_test_LDADD = @APR_LDADD@ \ @LIBXML2_LDADD@ \ @LUA_LDADD@ \ @PCRE_LDADD@ \ + @PCRE2_LDADD@ \ @YAJL_LDADD@ \ @SSDEEP_CFLAGS@ @@ -67,6 +70,7 @@ msc_test_LDFLAGS = @APR_LDFLAGS@ \ @LIBXML2_LDFLAGS@ \ @LUA_LDFLAGS@ \ @PCRE_LDFLAGS@ \ + @PCRE2_LDFLAGS@ \ @YAJL_LDFLAGS@ \ @SSDEEP_LDFLAGS@ @@ -76,7 +80,7 @@ TESTS = $(check_SCRIPTS) test: check test-regression: run-regression-tests.pl - $(PERL) run-regression-tests.pl + $(PERL) run-regression-tests.pl -S . test-regression-nginx: run-regression-tests-nginx.pl $(PERL) run-regression-tests-nginx.pl diff --git a/tests/modsecurity.conf-minimal b/tests/modsecurity.conf-minimal new file mode 100644 index 0000000000..cddb0b762e --- /dev/null +++ b/tests/modsecurity.conf-minimal @@ -0,0 +1,240 @@ +# -- Rule engine initialization ---------------------------------------------- + +# Enable ModSecurity, attaching it to every transaction. Use detection +# only to start with, because that minimises the chances of post-installation +# disruption. +# +SecRuleEngine DetectionOnly + + +# -- Request body handling --------------------------------------------------- + +# Allow ModSecurity to access request bodies. If you don't, ModSecurity +# won't be able to see any POST parameters, which opens a large security +# hole for attackers to exploit. +# +SecRequestBodyAccess On + + +# Enable XML request body parser. +# Initiate XML Processor in case of xml content-type +# +SecRule REQUEST_HEADERS:Content-Type "^(?:application(?:/soap\+|/)|text/)xml" \ + "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML" + +# Enable JSON request body parser. +# Initiate JSON Processor in case of JSON content-type; change accordingly +# if your application does not use 'application/json' +# +SecRule REQUEST_HEADERS:Content-Type "^application/json" \ + "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" + +# Sample rule to enable JSON request body parser for more subtypes. +# Uncomment or adapt this rule if you want to engage the JSON +# Processor for "+json" subtypes +# +#SecRule REQUEST_HEADERS:Content-Type "^application/[a-z0-9.-]+[+]json" \ +# "id:'200006',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" + +# Maximum request body size we will accept for buffering. If you support +# file uploads then the value given on the first line has to be as large +# as the largest file you are willing to accept. The second value refers +# to the size of data, with files excluded. You want to keep that value as +# low as practical. +# +SecRequestBodyLimit 13107200 +SecRequestBodyNoFilesLimit 131072 + +# Store up to 128 KB of request body data in memory. When the multipart +# parser reaches this limit, it will start using your hard disk for +# storage. That is slow, but unavoidable. +# +SecRequestBodyInMemoryLimit 131072 + +# What do do if the request body size is above our configured limit. +# Keep in mind that this setting will automatically be set to ProcessPartial +# when SecRuleEngine is set to DetectionOnly mode in order to minimize +# disruptions when initially deploying ModSecurity. +# +SecRequestBodyLimitAction Reject + +# Maximum parsing depth allowed for JSON objects. You want to keep this +# value as low as practical. +# +SecRequestBodyJsonDepthLimit 512 + +# Verify that we've correctly processed the request body. +# As a rule of thumb, when failing to process a request body +# you should reject the request (when deployed in blocking mode) +# or log a high-severity alert (when deployed in detection-only mode). +# +SecRule REQBODY_ERROR "!@eq 0" \ +"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2" + +# By default be strict with what we accept in the multipart/form-data +# request body. If the rule below proves to be too strict for your +# environment consider changing it to detection-only. You are encouraged +# _not_ to remove it altogether. +# +SecRule MULTIPART_STRICT_ERROR "!@eq 0" \ +"id:'200003',phase:2,t:none,log,deny,status:400, \ +msg:'Multipart request body failed strict validation: \ +PE %{REQBODY_PROCESSOR_ERROR}, \ +BQ %{MULTIPART_BOUNDARY_QUOTED}, \ +BW %{MULTIPART_BOUNDARY_WHITESPACE}, \ +DB %{MULTIPART_DATA_BEFORE}, \ +DA %{MULTIPART_DATA_AFTER}, \ +HF %{MULTIPART_HEADER_FOLDING}, \ +LF %{MULTIPART_LF_LINE}, \ +SM %{MULTIPART_MISSING_SEMICOLON}, \ +IQ %{MULTIPART_INVALID_QUOTING}, \ +IP %{MULTIPART_INVALID_PART}, \ +IH %{MULTIPART_INVALID_HEADER_FOLDING}, \ +FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'" + +# Did we see anything that might be a boundary? +# +SecRule MULTIPART_UNMATCHED_BOUNDARY "!@eq 0" \ +"id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'" + +# PCRE Tuning +# We want to avoid a potential RegEx DoS condition +# +SecPcreMatchLimit 1000 +SecPcreMatchLimitRecursion 1000 + +# Some internal errors will set flags in TX and we will need to look for these. +# All of these are prefixed with "MSC_". The following flags currently exist: +# +# MSC_PCRE_LIMITS_EXCEEDED: PCRE match limits were exceeded. +# +SecRule TX:/^MSC_/ "!@streq 0" \ + "id:'200005',phase:2,t:none,log,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'" + + +# -- Response body handling -------------------------------------------------- + +# Allow ModSecurity to access response bodies. +# You should have this directive enabled in order to identify errors +# and data leakage issues. +# +# Do keep in mind that enabling this directive does increases both +# memory consumption and response latency. +# +SecResponseBodyAccess On + +# Which response MIME types do you want to inspect? You should adjust the +# configuration below to catch documents but avoid static files +# (e.g., images and archives). +# +SecResponseBodyMimeType text/plain text/html text/xml + +# Buffer response bodies of up to 512 KB in length. +SecResponseBodyLimit 524288 + +# What happens when we encounter a response body larger than the configured +# limit? By default, we process what we have and let the rest through. +# That's somewhat less secure, but does not break any legitimate pages. +# +SecResponseBodyLimitAction ProcessPartial + + +# -- Filesystem configuration ------------------------------------------------ + +# The location where ModSecurity stores temporary files (for example, when +# it needs to handle a file upload that is larger than the configured limit). +# +# This default setting is chosen due to all systems have /tmp available however, +# this is less than ideal. It is recommended that you specify a location that's private. +# +SecTmpDir /tmp/ + +# The location where ModSecurity will keep its persistent data. This default setting +# is chosen due to all systems have /tmp available however, it +# too should be updated to a place that other users can't access. +# +SecDataDir /tmp/ + + +# -- File uploads handling configuration ------------------------------------- + +# The location where ModSecurity stores intercepted uploaded files. This +# location must be private to ModSecurity. You don't want other users on +# the server to access the files, do you? +# +#SecUploadDir /opt/modsecurity/var/upload/ + +# By default, only keep the files that were determined to be unusual +# in some way (by an external inspection script). For this to work you +# will also need at least one file inspection rule. +# +#SecUploadKeepFiles RelevantOnly + +# Uploaded files are by default created with permissions that do not allow +# any other user to access them. You may need to relax that if you want to +# interface ModSecurity to an external program (e.g., an anti-virus). +# +#SecUploadFileMode 0600 + + +# -- Debug log configuration ------------------------------------------------- + +# The default debug log configuration is to duplicate the error, warning +# and notice messages from the error log. +# +#SecDebugLog /opt/modsecurity/var/log/debug.log +#SecDebugLogLevel 3 + + +# -- Audit log configuration ------------------------------------------------- + +# Log the transactions that are marked by a rule, as well as those that +# trigger a server error (determined by a 5xx or 4xx, excluding 404, +# level response status codes). +# +SecAuditEngine RelevantOnly +SecAuditLogRelevantStatus "^(?:5|4(?!04))" + +# Log everything we know about a transaction. +SecAuditLogParts ABIJDEFHZ + +# Use a single file for logging. This is much easier to look at, but +# assumes that you will use the audit log only ocassionally. +# +#SecAuditLogType Serial +#SecAuditLog /var/log/modsec_audit.log + +# Specify the path for concurrent audit logging. +#SecAuditLogStorageDir /opt/modsecurity/var/audit/ + + +# -- Miscellaneous ----------------------------------------------------------- + +# Use the most commonly used application/x-www-form-urlencoded parameter +# separator. There's probably only one application somewhere that uses +# something else so don't expect to change this value. +# +SecArgumentSeparator & + +# Settle on version 0 (zero) cookies, as that is what most applications +# use. Using an incorrect cookie version may open your installation to +# evasion attacks (against the rules that examine named cookies). +# +SecCookieFormat 0 + +# Specify your Unicode Code Point. +# This mapping is used by the t:urlDecodeUni transformation function +# to properly map encoded data to your language. Properly setting +# these directives helps to reduce false positives and negatives. +# +SecUnicodeMapFile unicode.mapping 20127 + +# Improve the quality of ModSecurity by sharing information about your +# current ModSecurity version and dependencies versions. +# The following information will be shared: ModSecurity version, +# Web Server version, APR version, PCRE version, Lua version, Libxml2 +# version, Anonymous unique id for host. +# NB: As of April 2022, there is no longer any advantage to turning this +# setting On, as there is no active receiver for the information. +SecStatusEngine Off + diff --git a/tests/msc_test.c b/tests/msc_test.c index 7c794a5d98..bf278d0add 100644 --- a/tests/msc_test.c +++ b/tests/msc_test.c @@ -81,7 +81,7 @@ char DSOLOCAL *real_server_signature = NULL; int DSOLOCAL remote_rules_fail_action = REMOTE_RULES_ABORT_ON_FAIL; char DSOLOCAL *remote_rules_fail_message = NULL; module AP_MODULE_DECLARE_DATA security2_module = { - NULL, + STANDARD20_MODULE_STUFF, NULL, NULL, NULL, diff --git a/tests/regression/config/00-load-modsec.t b/tests/regression/config/00-load-modsec.t index 222120da67..a5e3c6d89a 100644 --- a/tests/regression/config/00-load-modsec.t +++ b/tests/regression/config/00-load-modsec.t @@ -14,7 +14,7 @@ conf => sub { # Open the minimal conf file, substituting the # relative log paths with full paths. - open(C, "<$ENV{DIST_ROOT}/modsecurity.conf-minimal") or die "$!\n"; + open(C, "<$ENV{DIST_ROOT}/tests/modsecurity.conf-minimal") or die "$!\n"; (my $conf = join('', )) =~ s#Log logs/#Log $ENV{TEST_SERVER_ROOT}/logs/#g; close C; diff --git a/tests/regression/config/10-misc-directives.t b/tests/regression/config/10-misc-directives.t index 62a5e8e021..1c08d87961 100644 --- a/tests/regression/config/10-misc-directives.t +++ b/tests/regression/config/10-misc-directives.t @@ -55,7 +55,7 @@ error => [ qr/ModSecurity: Warning. Unconditional match in SecAction\./, 1 ], }, match_file => { - "$ENV{DATA_DIR}/ip.pag" => qr/\x00\x06dummy\x00\x00\x021\x00/, + "$ENV{DATA_DIR}/$ENV{RUNASUSER}-ip.pag" => qr/\x00\x06dummy\x00\x00\x021\x00/, }, match_response => { status => qr/^200$/, diff --git a/tests/regression/config/10-request-directives.t b/tests/regression/config/10-request-directives.t index 42094e11b1..d5c6f143b4 100644 --- a/tests/regression/config/10-request-directives.t +++ b/tests/regression/config/10-request-directives.t @@ -578,10 +578,10 @@ SecRequestBodyLimit 131072 ), match_log => { - -debug => [ qr/Request body is larger than the configured limit/, 1], + error => [ qr/Multipart parsing error: Multipart: Final boundary missing./, 1], }, match_response => { - status => qr/^200$/, + status => qr/^500$/, }, request => normalize_raw_request_data( qq( @@ -703,3 +703,50 @@ ), }, +# SecArgumentsLimit +{ + type => "config", + comment => "SecArgumentsLimit (pos)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecArgumentsLimit 5 + SecRule REQBODY_ERROR "!\@eq 0" "id:'500232',phase:2,log,deny,status:403,msg:'Failed to parse request body'" + ), + match_log => { + error => [ qr/Access denied with code 403 /, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2&c=3&d=4&e=5&f=6", + ), +}, +{ + type => "config", + comment => "SecArgumentsLimit (neg)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecArgumentsLimit 5 + SecRule REQBODY_ERROR "!\@eq 0" "id:'500233',phase:2,log,deny,status:403,msg:'Failed to parse request body'" + ), + match_log => { + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/x-www-form-urlencoded", + ], + "a=1&b=2&c=3&d=4&e=5", + ), +}, + diff --git a/tests/regression/misc/00-multipart-parser.t b/tests/regression/misc/00-multipart-parser.t index de39bf0864..35ba5ba3d7 100644 --- a/tests/regression/misc/00-multipart-parser.t +++ b/tests/regression/misc/00-multipart-parser.t @@ -270,7 +270,7 @@ debug => [ qr/Final boundary missing/, 1 ], }, match_response => { - status => qr/^200$/, + status => qr/^500$/, }, request => new HTTP::Request( POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", @@ -736,6 +736,45 @@ ), }, +# Single quote within double quotes is ok +{ + type => "misc", + comment => "multipart parser (C-D uses single quote within double quotes)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_INVALID_QUOTING "!\@eq 0" "phase:2,deny,id:500169" + ), + match_log => { + debug => [ qr/Adding request argument \(BODY\): name "a'b/s, 1 ], + -debug => [ qr/Adding request argument \(BODY\): name "b/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "multipart/form-data; boundary=---------------------------69343412719991675451336310646", + ], + normalize_raw_request_data( + q( + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="a'b" + + 1 + -----------------------------69343412719991675451336310646 + Content-Disposition: form-data; name="aaa"; filename="d'ummy" + + 2 + -----------------------------69343412719991675451336310646-- + ), + ), + ), +}, + # Invalid boundary separators { type => "misc", @@ -1081,10 +1120,10 @@ SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,id:500121" ), match_log => { - debug => [ qr/boundary whitespace in C-T header/, 1 ], + debug => [ qr/Multipart: Warning: boundary whitespace in C-T header./, 1 ], }, match_response => { - status => qr/^403$/, + status => qr/^500$/, }, request => new HTTP::Request( POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", @@ -1177,10 +1216,10 @@ SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,id:500127" ), match_log => { - debug => [ qr/No boundaries found in payload/, 1 ], + debug => [ qr/Multipart parsing error: Multipart: No boundaries found in payload./, 1 ], }, match_response => { - status => qr/^403$/, + status => qr/^500$/, }, request => new HTTP::Request( POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", @@ -1223,11 +1262,11 @@ SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,id:500130" ), match_log => { - debug => [ qr/Invalid boundary in C-T \(characters\)/, 1 ], + debug => [ qr/Multipart parsing error: Multipart: No boundaries found in payload./, 1 ], }, match_response => { - status => qr/^403$/, + status => qr/^500$/, }, request => new HTTP::Request( POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", @@ -1314,15 +1353,14 @@ SecDebugLog $ENV{DEBUG_LOG} SecDebugLogLevel 9 SecRequestBodyAccess On - SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny,id:500134" - SecRule MULTIPART_UNMATCHED_BOUNDARY "\@eq 1" "phase:2,deny,id:500135" - SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,id:500136" + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny,status:400,id:500134" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,status:400,id:500136" ), match_log => { - debug => [ qr/boundary was quoted.*No boundaries found in payload/s, 1 ], + debug => [ qr/Multipart: Warning: boundary was quoted./s, 1 ], }, match_response => { - status => qr/^403$/, + status => qr/^400$/, }, request => new HTTP::Request( POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", @@ -1331,20 +1369,20 @@ ], normalize_raw_request_data( q( - --0000 + -- 0000 Content-Disposition: form-data; name="name" Brian Rectanus - --0000 + -- 0000 Content-Disposition: form-data; name="email" brian.rectanus@breach.com - --0000 + -- 0000 Content-Disposition: form-data; name="image"; filename="image.jpg" Content-Type: image/jpeg BINARYDATA - --0000-- + -- 0000-- ), ), ), @@ -1366,7 +1404,7 @@ SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,id:500139" ), match_log => { - debug => [ qr/boundary was quoted.*No boundaries found in payload/s, 1 ], + error => [ qr/Multipart parsing error \(init\): Multipart: Invalid boundary in C-T \(characters\)./s, 1 ], }, match_response => { status => qr/^403$/, @@ -1811,3 +1849,48 @@ ), }, +# part headers +{ + type => "misc", + comment => "multipart parser (part headers)", + conf => qq( + SecRuleEngine On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyAccess On + SecRule MULTIPART_STRICT_ERROR "\@eq 1" "phase:2,deny,status:400,id:500168" + SecRule REQBODY_PROCESSOR_ERROR "\@eq 1" "phase:2,deny,status:400,id:500169" + SecRule MULTIPART_PART_HEADERS:image "\@rx content-type:.*jpeg" "phase:2,deny,status:403,id:500170,t:lowercase" + ), + match_log => { + debug => [ qr/500170.*against MULTIPART_PART_HEADERS:image.*Rule returned 1./s, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => q(multipart/form-data; boundary=0000), + ], + normalize_raw_request_data( + q( + --0000 + Content-Disposition: form-data; name="username" + + Bill + --0000 + Content-Disposition: form-data; name="email" + + bill@fakesite.com + --0000 + Content-Disposition: form-data; name="image"; filename="image.jpg" + Content-Type: image/jpeg + + BINARYDATA + --0000-- + ), + ), + ), +}, + diff --git a/tests/regression/misc/10-tfn-cache.t b/tests/regression/misc/10-tfn-cache.t index 672b610d22..f0a663e495 100644 --- a/tests/regression/misc/10-tfn-cache.t +++ b/tests/regression/misc/10-tfn-cache.t @@ -182,6 +182,6 @@ "Content-Type" => "application/x-www-form-urlencoded", ], # 1000 Args - join("&", map { sprintf "arg%08d=0123456789abcdef+0123456789ABCDEF+0123456789abcdef", $_ } (1 .. 1000))."&test=Foo+Bar", + join("&", map { sprintf "arg%08d=0123456789abcdef+0123456789ABCDEF+0123456789abcdef", $_ } (1 .. 999))."&test=Foo+Bar", ), }, diff --git a/tests/regression/misc/20-status-engine.t b/tests/regression/misc/20-status-engine.t deleted file mode 100644 index a8ec6f3e03..0000000000 --- a/tests/regression/misc/20-status-engine.t +++ /dev/null @@ -1,123 +0,0 @@ -### Test the SecStatusEngine - -# On -{ - type => "misc", - comment => "Setting SecStatusEngine to On", - conf => qq( - SecRuleEngine On - SecStatusEngine On - ), - match_log => { - error => [ qr/ModSecurity: StatusEngine call successfully sent/, 1], - -error => [ qr/Status engine is currently disabled, enable it by set SecStatusEngine to On/, 1], - }, - match_response => { - status => qr/^200$/, - }, - request => new HTTP::Request( - POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", - [ - "Content-Type" => "application/x-www-form-urlencoded", - ], - "arg1=val1&arg2=val2", - ), -}, -# Off -{ - type => "misc", - comment => "Setting SecStatusEngine to Off", - conf => qq( - SecRuleEngine On - SecStatusEngine Off - ), - match_log => { - -error => [ qr/ModSecurity: StatusEngine call successfully sent/, 1], - error => [ qr/Status engine is currently disabled, enable it by set SecStatusEngine to On/, 1], - }, - match_response => { - status => qr/^200$/, - }, - request => new HTTP::Request( - POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", - [ - "Content-Type" => "application/x-www-form-urlencoded", - ], - "arg1=val1&arg2=val2", - ), -}, -# On and SecServerSignature -{ - type => "misc", - comment => "SecStatusEngine On using SecServerSignature", - conf => qq( - SecRuleEngine On - SecServerSignature "SpiderServer v0.1a" - SecStatusEngine On - ), - match_log => { - error => [ qr/ModSecurity: StatusEngine call successfully sent/, 1], - -error => [ qr/StatusEngine call: .*SpiderServer v0.1a.*/, 1], - }, - match_response => { - status => qr/^200$/, - }, - request => new HTTP::Request( - POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", - [ - "Content-Type" => "application/x-www-form-urlencoded", - ], - "arg1=val1&arg2=val2", - ), -}, -# On and SecServerSignature -{ - type => "misc", - comment => "SecStatusEngine On/SecServerSignature - checking signature", - conf => qq( - SecRuleEngine On - SecServerSignature "SpiderServer v0.1a" - SecStatusEngine On - ), - match_log => { - error => { - apache => [ qr/StatusEngine call: \"[0-9]+.[0-9]+.[0-9]+[-RC]*[0-9]*\,Apache/, 1], - nginx => [ qr/StatusEngine call: \"[0-9]+.[0-9]+.[0-9]+[-RC]*[0-9]*\,nginx/, 1], - } - }, - match_response => { - status => qr/^200$/, - }, - request => new HTTP::Request( - POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", - [ - "Content-Type" => "application/x-www-form-urlencoded", - ], - "arg1=val1&arg2=val2", - ), -}, -# On and SecServerSignature -{ - type => "misc", - comment => "SecStatusEngine On - checking signature", - conf => qq( - SecStatusEngine On - ), - match_log => { - error => { - apache => [ qr/StatusEngine call: \"[0-9]+.[0-9]+.[0-9]+[-RC]*[0-9]*\,Apache/, 1], - nginx => [ qr/StatusEngine call: \"[0-9]+.[0-9]+.[0-9]+[-RC]*[0-9]*\,nginx/, 1], - } - }, - match_response => { - status => qr/^200$/, - }, - request => new HTTP::Request( - POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", - [ - "Content-Type" => "application/x-www-form-urlencoded", - ], - "arg1=val1&arg2=val2", - ), -}, - diff --git a/tests/regression/misc/40-secRemoteRules.t.in b/tests/regression/misc/40-secRemoteRules.t.in deleted file mode 100644 index 93acd7316d..0000000000 --- a/tests/regression/misc/40-secRemoteRules.t.in +++ /dev/null @@ -1,43 +0,0 @@ -### SecRemoteRules - -{ - type => "misc", - comment => "SecRemoteRules load", - conf => qq( - SecRuleEngine On - SecDebugLog $ENV{DEBUG_LOG} - SecDebugLogLevel 9 - SecRequestBodyAccess On - SecRemoteRules 123 "https://www.modsecurity.org/modsecurity-regression-test-secremoterules.txt" - ), - match_log => { - error => [ qr/ModSecurity: Loaded 1 rule/, 1], - }, -}, -{ - type => "misc", - comment => "SecRemoteRules apply some remote rules", - conf => qq( - SecRuleEngine On - SecDebugLog $ENV{DEBUG_LOG} - SecDebugLogLevel 9 - SecRequestBodyAccess On - SecRemoteRules 123 "https://www.modsecurity.org/modsecurity-regression-test-secremoterules.txt" - ), - match_log => { - error => [ qr/ModSecurity: Warning. Matched phrase \"127.0.0.1\" at REQUEST_FILENAME./, 1], - debug => [ qr/Matched phrase \"127.0.0.1\" at REQUEST_FILENAME/, 1 ], - }, - match_response => { - status => qr/^404$/, - }, - request => new HTTP::Request( - POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/127.0.0.1.html", - [ - "Content-Type" => "application/x-www-form-urlencoded", - ], - # Args - "some_variable=-1' and 1=1 union/* foo */select load_file('/etc/passwd')--" - ), -}, - diff --git a/tests/regression/misc/50-ipmatchfromfile-external.t.in b/tests/regression/misc/50-ipmatchfromfile-external.t.in deleted file mode 100644 index effe97bf32..0000000000 --- a/tests/regression/misc/50-ipmatchfromfile-external.t.in +++ /dev/null @@ -1,71 +0,0 @@ -### ipMatchFromFile external resource - -{ - type => "misc", - comment => "ipMatchFromFile", - conf => qq( - SecRuleEngine On - SecDebugLog $ENV{DEBUG_LOG} - SecDebugLogLevel 9 - SecRequestBodyAccess On - SecRule REMOTE_ADDR "\@ipMatchFromFile https://www.modsecurity.org/modsecurity-regression-test.txt" "id:10500,pass" - ), - match_log => { - error => [ qr/ModSecurity: Warning. IPmatchFromFile: \"127.0.0.1\" matched at REMOTE_ADDR./, 1], - debug => [ qr/IPmatchFromFile: \"127.0.0.1\" matched at REMOTE_ADDR./, 1 ], - -error => [ qr/ModSecurity: Problems loading external resources:/, 1], - }, - match_response => { - status => qr/^404$/, - }, - request => new HTTP::Request( - POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/127.0.0.1.html", - [ - "Content-Type" => "application/x-www-form-urlencoded", - ], - # Args - "some_variable=-1' and 1=1 union/* foo */select load_file('/etc/passwd')--" - ), -}, -{ - type => "misc", - comment => "ipMatchFromFile - 404 download", - conf => qq( - SecRuleEngine On - SecDebugLog $ENV{DEBUG_LOG} - SecDebugLogLevel 9 - SecRequestBodyAccess On - SecRemoteRulesFailAction Warn - SecRule REMOTE_ADDR "\@ipMatchFromFile https://www.modsecurity.org/modsecurity-regression-test-404.txt" "id:10500,pass" - ), - match_log => { - error => [ qr/ModSecurity: Problems loading external resources: Failed to download: \"https:\/\/www.modsecurity.org\/modsecurity-regression-test-404.txt\" error: HTTP response code said error./, 1], - }, - match_response => { - status => qr/^404$/, - }, - request => new HTTP::Request( - POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/127.0.0.1.html", - [ - "Content-Type" => "application/x-www-form-urlencoded", - ], - # Args - "some_variable=-1' and 1=1 union/* foo */select load_file('/etc/passwd')--" - ), -}, -{ - type => "misc", - comment => "ipMatchFromFile - bad certificate name", - conf => qq( - SecRuleEngine On - SecDebugLog $ENV{DEBUG_LOG} - SecDebugLogLevel 9 - SecRequestBodyAccess On - SecRemoteRulesFailAction Warn - SecRule REMOTE_ADDR "\@ipMatchFromFile https://status.modsecurity.org/modsecurity-regression-test-huge-ip-list.txt" "id:10500,pass" - ), - match_log => { - error => [ qr/ModSecurity: Problems loading external resources: Failed to download: \"https:\/\/status.modsecurity.org\/modsecurity-regression-test-huge-ip-list.txt\" error: SSL peer certificate or SSH remote key was not OK./, 1], - }, -}, - diff --git a/tests/regression/misc/60-pmfromfile-external.t.in b/tests/regression/misc/60-pmfromfile-external.t.in deleted file mode 100644 index bfa4038a4a..0000000000 --- a/tests/regression/misc/60-pmfromfile-external.t.in +++ /dev/null @@ -1,84 +0,0 @@ -### pmfromfile external resource - -{ - type => "misc", - comment => "pmfromfile", - conf => qq( - SecRuleEngine On - SecDebugLog $ENV{DEBUG_LOG} - SecDebugLogLevel 9 - SecRequestBodyAccess On - SecRule REQUEST_FILENAME "\@pmFromFile https://www.modsecurity.org/modsecurity-regression-test.txt" "id:'123',phase:2,log,pass,t:none" - ), - match_log => { - error => [ qr/ModSecurity: Warning. Matched phrase \"127.0.0.1\" at REQUEST_FILENAME./, 1], - debug => [ qr/Matched phrase \"127.0.0.1\" at REQUEST_FILENAME/, 1 ], - -error => [ qr/ModSecurity: Problems loading external resources:/, 1], - }, - match_response => { - status => qr/^404$/, - }, - request => new HTTP::Request( - POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/127.0.0.1.html", - [ - "Content-Type" => "application/x-www-form-urlencoded", - ], - # Args - "some_variable=-1' and 1=1 union/* foo */select load_file('/etc/passwd')--" - ), -}, -{ - type => "misc", - comment => "pmfromfile - 404 download", - conf => qq( - SecRuleEngine On - SecDebugLog $ENV{DEBUG_LOG} - SecDebugLogLevel 9 - SecRequestBodyAccess On - SecRemoteRulesFailAction Warn - SecRule REQUEST_FILENAME "\@pmFromFile https://www.modsecurity.org/modsecurity-regression-test-404.txt" "id:'123',phase:2,log,pass,t:none" - - ), - match_log => { - error => [ qr/ModSecurity: Problems loading external resources: Failed to download: \"https:\/\/www.modsecurity.org\/modsecurity-regression-test-404.txt\" error: HTTP response code said error./, 1], - }, - match_response => { - status => qr/^404$/, - }, - request => new HTTP::Request( - POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/127.0.0.1.html", - [ - "Content-Type" => "application/x-www-form-urlencoded", - ], - # Args - "some_variable=-1' and 1=1 union/* foo */select load_file('/etc/passwd')--" - ), -}, -{ - type => "misc", - comment => "pmfromfile - bad certificate name", - conf => qq( - SecRuleEngine On - SecDebugLog $ENV{DEBUG_LOG} - SecDebugLogLevel 9 - SecRequestBodyAccess On - SecRemoteRulesFailAction Warn - SecRule REQUEST_FILENAME "\@pmFromFile https://status.modsecurity.org/modsecurity-regression-test.txt" "id:'123',phase:2,log,pass,t:none" - - ), - match_log => { - error => [ qr/ModSecurity: Problems loading external resources: Failed to download: \"https:\/\/status.modsecurity.org\/modsecurity-regression-test.txt\" error: SSL peer certificate or SSH remote key was not OK./, 1], - }, - match_response => { - status => qr/^404$/, - }, - request => new HTTP::Request( - POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/127.0.0.1.html", - [ - "Content-Type" => "application/x-www-form-urlencoded", - ], - # Args - "some_variable=-1' and 1=1 union/* foo */select load_file('/etc/passwd')--" - ), -}, - diff --git a/tests/regression/rule/10-xml.t b/tests/regression/rule/10-xml.t index ea9d6ad99a..ad1ed91941 100644 --- a/tests/regression/rule/10-xml.t +++ b/tests/regression/rule/10-xml.t @@ -169,13 +169,13 @@ phase:2,deny,id:12345" ), match_log => { - debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 0\).*XML parser error.*validation failed because content is not well formed/s, 1 ], - -debug => [ qr/Failed to load|Successfully validated/, 1 ], - -error => [ qr/Failed to load|Successfully validated/, 1 ], - audit => [ qr/^Message: .*Failed parsing document.*\nMessage:/m, 1 ], + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 0\).*XML parser error: XML: Failed to parse document./s, 1 ], + debug => [ qr/XML parser error: XML: Failed to parse document./, 1 ], + error => [ qr/XML parser error: XML: Failed to parse document./, 1 ], + audit => [ qr/XML parser error: XML: Failed to parse document./m, 1 ], }, match_response => { - status => qr/^403$/, + status => qr/^500$/, }, request => new HTTP::Request( POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", @@ -350,6 +350,8 @@ SecXmlExternalEntity On SecDebugLog $ENV{DEBUG_LOG} SecDebugLogLevel 9 + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" "id:500026, \\ phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345,id:500027 @@ -357,12 +359,12 @@ phase:2,deny,id:12345" ), match_log => { - debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 0\).*XML parser error.*validation failed because content is not well formed/s, 1 ], - -debug => [ qr/Failed to load|Successfully validated/, 1 ], - -error => [ qr/Failed to load|Successfully validated/, 1 ], + debug => [ qr/XML: Initialising parser.*XML: Parsing complete \(well_formed 0\).*XML parser error: XML: Failed to parse document./s, 1 ], + debug => [ qr/XML parser error: XML: Failed to parse document./, 1 ], + audit => [ qr/^Message: .*Failed to parse document.*\nMessage:/m, 1 ], }, match_response => { - status => qr/^403$/, + status => qr/^500$/, }, request => new HTTP::Request( POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", @@ -394,7 +396,7 @@ SecXmlExternalEntity On SecDebugLog $ENV{DEBUG_LOG} SecDebugLogLevel 9 - SecRule REQUEST_HEADERS:Content-Type "^text/xml\$" "id:500029, \\ + SecRule REQUEST_HEADERS:Content-Type "^(?:application(?:/soap\+|/)|text/)xml" "id:500029, \\ phase:1,t:none,t:lowercase,nolog,pass,ctl:requestBodyProcessor=XML" SecRule REQBODY_PROCESSOR "!^XML\$" nolog,pass,skipAfter:12345,id:500030 SecRule XML "\@validateDTD $ENV{CONF_DIR}/SoapEnvelope-bad.dtd" "id:500031 \\ diff --git a/tests/regression/rule/15-json.t b/tests/regression/rule/15-json.t index e75f89c154..9c17817503 100644 --- a/tests/regression/rule/15-json.t +++ b/tests/regression/rule/15-json.t @@ -35,5 +35,229 @@ ), ), ), +}, +{ + type => "rule", + comment => "json parser - issue #1576 - 1", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_HEADERS:Content-Type "application/json" \\ + "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" + SecRule ARGS "bar" "id:'200441',phase:3,log" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Pattern match "bar" at ARGS:foo.|ModSecurity: JSON support was not enabled/s, 1 ], + debug => [ qr/ARGS:foo|ARGS:mod|ARGS:ops.ops.ops|ARGS:ops.ops.ops|ARGS:ops.ops|ARGS:ops.ops|ARGS:ops.ops.eins.eins|ARGS:ops.ops.eins.eins|ARGS:whee/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/json", + ], + normalize_raw_request_data( + q( + { + "foo":"bar", + "mod":"sec", + "ops":[ + [ + "um", + "um e meio" + ], + "dois", + "tres", + { + "eins":[ + "zwei", + "drei" + ] + } + ], + "whee":"lhebs" + } + ), + ), + ), +}, +{ + type => "rule", + comment => "json parser - issue #1576 - 2", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_HEADERS:Content-Type "application/json" \\ + "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" + SecRule ARGS "um" "id:'200441',phase:3,log" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Pattern match "um" at ARGS:array.array|ModSecurity: JSON support was not enabled/s, 1 ], + debug => [ qr/ARGS:array.array|ARGS:array.array/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/json", + ], + normalize_raw_request_data( + q( + [ + "um", + "um e meio" + ] + ), + ), + ), +}, +{ + type => "rule", + comment => "json parser - issue #1576 - 3", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_HEADERS:Content-Type "application/json" \\ + "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" + SecRule ARGS "seis" "id:'200441',phase:3,log" + ), + match_log => { + error => [ qr/ModSecurity: Warning. Pattern match "seis" at ARGS:array.array.cinco.|ModSecurity: JSON support was not enabled/s, 1 ], + debug => [ qr/ARGS:array.array|ARGS:array.array|ARGS:array.array.tres|ARGS:array.array.cinco/, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/json", + ], + normalize_raw_request_data( + q( + [ + "um", + "um e meio", + { + "tres": "quatro", + "cinco": "seis" + } + ] + ), + ), + ), +}, +{ + type => "rule", + comment => "json parser - parsing depth not exceeded", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRequestBodyJsonDepthLimit 5 + SecRule REQUEST_HEADERS:Content-Type "application/json" \\ + "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" + SecRule REQBODY_ERROR "!\@eq 0" "id:'200442',phase:2,log,deny,status:403,msg:'Failed to parse request body'" + ), + match_log => { + debug => [ qr/key/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/json", + ], + normalize_raw_request_data( + q( + { + "key1":{"key2":{"key3":{"key4":{"key5":"thevalue"}}}} + } + ), + ), + ), +}, +{ + type => "rule", + comment => "json parser - parsing depth exceeded", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecDebugLogLevel 9 + SecRequestBodyJsonDepthLimit 3 + SecRule REQUEST_HEADERS:Content-Type "application/json" \\ + "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" + SecRule REQBODY_ERROR "!\@eq 0" "id:'200443',phase:2,log,deny,status:403,msg:'Failed to parse request body'" + ), + match_log => { + audit => [ qr/JSON parsing error: JSON depth limit exceeded/s, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/json", + ], + normalize_raw_request_data( + q( + { + "key1":{"key2":{"key3":{"key4":{"key5":"thevalue"}}}} + } + ), + ), + ), +}, +{ + type => "rule", + comment => "json parser - no-key single value", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecDebugLog $ENV{DEBUG_LOG} + SecAuditEngine RelevantOnly + SecAuditLog "$ENV{AUDIT_LOG}" + SecDebugLogLevel 9 + SecRequestBodyJsonDepthLimit 3 + SecRule REQUEST_HEADERS:Content-Type "^application/json" \\ + "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON" + SecRule REQBODY_ERROR "!\@eq 0" "id:'200444',phase:2,log,deny,status:403,msg:'Failed to parse request body'" + SecRule ARGS "\@streq 25" "id:'200445',phase:2,log,deny,status:403" + ), + match_log => { + audit => [ qr/200445/s, 1 ], + }, + match_response => { + status => qr/^403$/, + }, + request => new HTTP::Request( + POST => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/test.txt", + [ + "Content-Type" => "application/json", + ], + normalize_raw_request_data( + q( + 25 + ), + ), + ), } + diff --git a/tests/regression/server_root/conf/unicode.mapping b/tests/regression/server_root/conf/unicode.mapping new file mode 100644 index 0000000000..2654c4a619 --- /dev/null +++ b/tests/regression/server_root/conf/unicode.mapping @@ -0,0 +1,96 @@ +(MAC - Roman) + + +(MAC - Icelandic) + + +1250 (ANSI - Central Europe) +00a1:21 00a2:63 00a3:4c 00a5:59 00aa:61 00b2:32 00b3:33 00b9:31 00ba:6f 00bc:31 00bd:31 00be:33 00c0:41 00c3:41 00c5:41 00c6:41 00c8:45 00ca:45 00cc:49 00cf:49 00d1:4e 00d2:4f 00d5:4f 00d8:4f 00d9:55 00db:55 00e0:61 00e3:61 00e5:61 00e6:61 00e8:65 00ea:65 00ec:69 00ef:69 00f1:6e 00f2:6f 00f5:6f 00f8:6f 00f9:75 00fb:75 00ff:79 0100:41 0101:61 0108:43 0109:63 010a:43 010b:63 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 013b:4c 013c:6c 0145:4e 0146:6e 014c:4f 014d:6f 014e:4f 014f:6f 0152:4f 0153:6f 0156:52 0157:72 015c:53 015d:73 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0180:62 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c0:7c 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2032:27 2035:60 203c:21 2044:2f 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2082:32 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:50 2119:50 211a:51 211b:52 211c:52 211d:52 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2191:5e 2194:2d 2195:7c 21a8:7c 2212:2d 2215:2f 2216:5c 2217:2a 221f:4c 2223:7c 2236:3a 223c:7e 2303:5e 2329:3c 232a:3e 2502:2d 250c:2d 2514:4c 2518:2d 251c:2b 2524:2b 252c:54 2534:2b 253c:2b 2550:3d 2554:2d 255a:4c 255d:2d 2566:54 256c:2b 2580:2d 2584:2d 2588:2d 2591:2d 2592:2d 2593:2d 25ac:2d 25b2:5e 25ba:3e 25c4:3c 25cb:30 25d9:30 263c:30 2640:2b 2642:3e 266a:64 266b:64 2758:7c 3000:20 3008:3c 3009:3e 301a:5b 301b:5d ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +1251 (ANSI - Cyrillic) +00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 203c:21 2190:3c 2191:5e 2192:3e 2193:76 2194:2d 221a:76 221f:4c 2500:2d 250c:2d 2514:4c 2518:2d 251c:2b 2524:2b 252c:54 2534:2b 253c:2b 2550:3d 2552:2d 2558:4c 2559:4c 255a:4c 255b:2d 255c:2d 255d:2d 2564:54 2565:54 2566:54 256a:2b 256b:2b 256c:2b 2580:2d 2584:2d 2588:2d 2591:2d 2592:2d 2593:2d 25ac:2d 25b2:5e 25ba:3e 25c4:3c 25cb:30 25d9:30 263a:4f 263b:4f 263c:30 2640:2b 2642:3e 266a:64 266b:64 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +1252 (ANSI - Latin I) +0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0179:5a 017b:5a 017c:7a 0180:62 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c0:7c 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c8:27 02cb:60 02cd:5f 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 037e:3b 0393:47 0398:54 03a3:53 03a6:46 03a9:4f 03b1:61 03b4:64 03b5:65 03c0:70 03c3:73 03c4:74 03c6:66 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2017:3d 2032:27 2035:60 2044:2f 2074:34 2075:35 2076:36 2077:37 2078:38 207f:6e 2080:30 2081:31 2082:32 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20a7:50 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:50 2119:50 211a:51 211b:52 211c:52 211d:52 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2212:2d 2215:2f 2216:5c 2217:2a 221a:76 221e:38 2223:7c 2229:6e 2236:3a 223c:7e 2261:3d 2264:3d 2265:3d 2303:5e 2320:28 2321:29 2329:3c 232a:3e 2500:2d 250c:2b 2510:2b 2514:2b 2518:2b 251c:2b 252c:2d 2534:2d 253c:2b 2550:2d 2552:2b 2553:2b 2554:2b 2555:2b 2556:2b 2557:2b 2558:2b 2559:2b 255a:2b 255b:2b 255c:2b 255d:2b 2564:2d 2565:2d 2566:2d 2567:2d 2568:2d 2569:2d 256a:2b 256b:2b 256c:2b 2584:5f 2758:7c 3000:20 3008:3c 3009:3e 301a:5b 301b:5d ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +1253 (ANSI - Greek) +00b4:2f 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 037e:3b 203c:21 2190:3c 2191:5e 2192:3e 2193:76 2194:2d 221f:4c 2500:2d 250c:2d 2514:4c 2518:2d 251c:2b 2524:2b 252c:54 2534:2b 253c:2b 2550:3d 2554:2d 255a:4c 255d:2d 2566:54 256c:2b 2580:2d 2584:2d 2588:2d 2591:2d 2592:2d 2593:2d 25ac:2d 25b2:5e 25ba:3e 25c4:3c 25cb:30 25d9:30 263a:4f 263b:4f 263c:30 2640:2b 2642:3e 266a:64 266b:64 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +1254 (ANSI - Turkish) +00dd:59 00fd:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c0:7c 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c7:5e 02c8:27 02cb:60 02cd:5f 02d8:5e 02d9:27 0300:60 0302:5e 0331:5f 0332:5f 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2032:27 2035:60 203c:21 2044:2f 2074:34 2075:35 2076:36 2077:37 2078:38 2081:30 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:50 2119:50 211a:51 211b:52 211c:52 211d:52 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2191:5e 2193:76 2194:2d 2195:7c 21a8:7c 2212:2d 2215:2f 2216:5c 2217:2a 221f:4c 2223:7c 2236:3a 223c:7e 2303:5e 2329:3c 232a:3e 2502:2d 250c:2d 2514:4c 2518:2d 251c:2b 2524:2b 252c:54 2534:2b 253c:2b 2550:3d 2554:2d 255a:4c 255d:2d 2566:54 256c:2b 2580:2d 2584:2d 2588:2d 2591:2d 2592:2d 2593:2d 25ac:2d 25b2:5e 25ba:3e 25c4:3c 25cb:30 25d9:30 263a:4f 263b:4f 263c:30 2640:2b 2642:3e 266a:64 266b:64 2758:7c 3000:20 3008:3c 3009:3e 301a:5b 301b:3d 301d:22 301e:22 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +1255 (ANSI - Hebrew) +0191:46 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +1256 (ANSI - Arabic) +0620:41 0621:41 0622:43 0623:45 0624:45 0625:45 0626:45 0627:49 0628:49 0629:4f 062a:55 062b:55 062c:55 062d:46 062e:43 062f:44 0630:45 0631:46 0632:47 0633:48 0634:49 0635:4a 0636:4b 0637:4c 0638:4d 0639:4e 063a:4f 0641:41 0642:42 0643:43 0644:44 0645:45 0646:46 0647:47 0648:48 0649:49 064a:4a 064b:4b 064c:4c 064d:4d 064e:4e 064f:4f 0650:50 0651:51 0652:52 + +1257 (ANSI - Baltic) +ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +1258 (ANSI/OEM - Viet Nam) +ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +20127 (US-ASCII) +00a0:20 00a1:21 00a2:63 00a4:24 00a5:59 00a6:7c 00a9:43 00aa:61 00ab:3c 00ad:2d 00ae:52 00b2:32 00b3:33 00b7:2e 00b8:2c 00b9:31 00ba:6f 00bb:3e 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c6:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d0:44 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e6:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:2c 201c:22 201d:22 201e:22 2022:2e 2026:2e 2032:27 2035:60 2039:3c 203a:3e 2122:54 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +20261 (T.61) +f8dd:5c f8de:5e f8df:60 f8e0:7b f8fc:7d f8fd:7e f8fe:7f + +20866 (Russian - KOI8) +00a7:15 00ab:3c 00ad:2d 00ae:52 00b1:2b 00b6:14 00bb:3e 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 2013:2d 2014:2d 2018:27 2019:27 201a:27 201c:22 201d:22 201e:22 2022:07 2026:3a 2030:25 2039:3c 203a:3e 203c:13 2122:54 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 221f:1c 2302:7f 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e + +28591 (ISO 8859-1 Latin I) +0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:2c 201c:22 201d:22 201e:22 2022:2e 2026:2e 2032:27 2035:60 2039:3c 203a:3e 2122:54 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +28592 (ISO 8859-2 Central Europe) +00a1:21 00a2:63 00a5:59 00a6:7c 00a9:43 00aa:61 00ab:3c 00ae:52 00b2:32 00b3:33 00b7:2e 00b9:31 00ba:6f 00bb:3e 00c0:41 00c3:41 00c5:41 00c6:41 00c8:45 00ca:45 00cc:49 00cf:49 00d0:44 00d1:4e 00d2:4f 00d5:4f 00d8:4f 00d9:55 00db:55 00e0:61 00e3:61 00e5:61 00e6:61 00e8:65 00ea:65 00ec:69 00ef:69 00f1:6e 00f2:6f 00f5:6f 00f8:6f 00f9:75 00fb:75 00ff:79 0100:41 0101:61 0108:43 0109:63 010a:43 010b:63 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 013b:4c 013c:6c 0145:4e 0146:6e 014c:4f 014d:6f 014e:4f 014f:6f 0152:4f 0153:6f 0156:52 0157:72 015c:53 015d:73 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0180:62 0189:44 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:2c 201c:22 201d:22 201e:22 2022:2e 2026:2e 2032:27 2035:60 2039:3c 203a:3e 2122:54 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +28605 (ISO 8859-15 Latin 9) +00a6:7c 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0138:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014a:4e 014b:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:54 0169:74 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0179:5a 017b:5a 017c:7a 0180:62 0189:44 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:2c 201c:22 201d:22 201e:22 2022:2e 2026:2e 2032:27 2035:60 2039:3c 203a:3e 2122:54 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +37 (IBM EBCDIC - U.S./Canada) +0004:37 0005:2d 0006:2e 0007:2f 0008:16 0009:05 000a:25 0014:3c 0015:3d 0016:32 0017:26 001a:3f 001b:27 0020:40 0021:5a 0022:7f 0023:7b 0024:5b 0025:6c 0026:50 0027:7d 0028:4d 0029:5d 002a:5c 002b:4e 002c:6b 002d:60 002e:4b 002f:61 003a:7a 003b:5e 003c:4c 003d:7e 003e:6e 003f:6f 0040:7c 005f:6d 0060:79 007c:4f 007f:07 0080:20 0081:21 0082:22 0083:23 0084:24 0085:15 0086:06 0087:17 0088:28 0089:29 008a:2a 008b:2b 008c:2c 008d:09 008e:0a 008f:1b 0090:30 0091:31 0092:1a 0093:33 0094:34 0095:35 0096:36 0097:08 0098:38 0099:39 009a:3a 009b:3b 009c:04 009d:14 009e:3e 00a0:41 00a2:4a 00a6:6a 00ac:5f 00c0:64 00c1:65 00c2:62 00c3:66 00c4:63 00c5:67 00c7:68 00c8:74 00c9:71 00ca:72 00cb:73 00cc:78 00cd:75 00ce:76 00cf:77 00d1:69 00df:59 00e0:44 00e1:45 00e2:42 00e3:46 00e4:43 00e5:47 00e7:48 00e8:54 00e9:51 00ea:52 00eb:53 00ec:58 00ed:55 00ee:56 00ef:57 00f1:49 00f8:70 ff01:5a ff02:7f ff03:7b ff04:5b ff05:6c ff06:50 ff07:7d ff08:4d ff09:5d ff0a:5c ff0b:4e ff0c:6b ff0d:60 ff0e:4b ff0f:61 ff1a:7a ff1b:5e ff1c:4c ff1d:7e ff1e:6e ff20:7c ff3f:6d ff40:79 ff5c:4f + +437 (OEM - United States) +00a4:0f 00a7:15 00a8:22 00a9:63 00ad:2d 00ae:72 00af:5f 00b3:33 00b4:27 00b6:14 00b8:2c 00b9:31 00be:5f 00c0:41 00c1:41 00c2:41 00c3:41 00c8:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d0:44 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d7:78 00d8:4f 00d9:55 00da:55 00db:55 00dd:59 00de:5f 00e3:61 00f0:64 00f5:6f 00f8:6f 00fd:79 00fe:5f 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c0:7c 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02ca:27 02cb:60 02cd:5f 02dc:7e 0300:60 0301:27 0302:5e 0303:7e 0308:22 030e:22 0327:2c 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2017:5f 2018:60 2019:27 201a:2c 201c:22 201d:22 201e:2c 2020:2b 2022:07 2026:2e 2030:25 2032:27 2035:60 2039:3c 203a:3e 203c:13 2044:2f 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2082:32 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20dd:09 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:50 2119:50 211a:51 211b:52 211c:52 211d:52 2122:54 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2212:2d 2215:2f 2216:5c 2217:2a 221f:1c 2223:7c 2236:3a 223c:7e 2302:7f 2303:5e 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 2758:7c 3000:20 3007:09 3008:3c 3009:3e 301a:5b 301b:5d ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +500 (IBM EBCDIC - International) +0004:37 0005:2d 0006:2e 0007:2f 0008:16 0009:05 000a:25 0014:3c 0015:3d 0016:32 0017:26 001a:3f 001b:27 0020:40 0021:4f 0022:7f 0023:7b 0024:5b 0025:6c 0026:50 0027:7d 0028:4d 0029:5d 002a:5c 002b:4e 002c:6b 002d:60 002e:4b 002f:61 003a:7a 003b:5e 003c:4c 003d:7e 003e:6e 003f:6f 0040:7c 005b:4a 005d:5a 005e:5f 005f:6d 0060:79 007f:07 0080:20 0081:21 0082:22 0083:23 0084:24 0085:15 0086:06 0087:17 0088:28 0089:29 008a:2a 008b:2b 008c:2c 008d:09 008e:0a 008f:1b 0090:30 0091:31 0092:1a 0093:33 0094:34 0095:35 0096:36 0097:08 0098:38 0099:39 009a:3a 009b:3b 009c:04 009d:14 009e:3e 00a0:41 00a6:6a 00c0:64 00c1:65 00c2:62 00c3:66 00c4:63 00c5:67 00c7:68 00c8:74 00c9:71 00ca:72 00cb:73 00cc:78 00cd:75 00ce:76 00cf:77 00d1:69 00df:59 00e0:44 00e1:45 00e2:42 00e3:46 00e4:43 00e5:47 00e7:48 00e8:54 00e9:51 00ea:52 00eb:53 00ec:58 00ed:55 00ee:56 00ef:57 00f1:49 00f8:70 ff01:4f ff02:7f ff03:7b ff04:5b ff05:6c ff06:50 ff07:7d ff08:4d ff09:5d ff0a:5c ff0b:4e ff0c:6b ff0d:60 ff0e:4b ff0f:61 ff1a:7a ff1b:5e ff1c:4c ff1d:7e ff1e:6e ff20:7c ff3b:4a ff3d:5a ff3e:5f ff3f:6d ff40:79 + +850 (OEM - Multilingual Latin I) +0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01a9:53 01ab:74 01ae:54 01af:55 01b0:75 01b6:5a 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02cb:27 02cd:5f 02dc:7e 0300:27 0302:5e 0303:7e 030e:22 0331:5f 0332:5f 037e:3b 0393:47 03a3:53 03a6:46 03a9:4f 03b1:61 03b4:64 03b5:65 03c0:70 03c3:73 03c4:74 03c6:66 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:27 201c:22 201d:22 201e:22 2022:07 2024:07 2026:2e 2030:25 2039:3c 203a:3e 203c:13 2044:2f 2070:30 2074:34 2075:35 2076:36 2077:37 2078:39 207f:6e 2080:30 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20a7:50 20dd:4f 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:50 2119:50 211a:51 211b:52 211c:52 211d:52 2122:54 2124:5a 2126:4f 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2211:53 2212:2d 2215:2f 2216:2f 2217:2a 2219:07 221a:56 221e:38 221f:1c 2229:6e 2236:3a 223c:7e 2248:7e 2261:3d 2264:3d 2265:3d 2302:7f 2303:5e 2320:28 2321:29 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 2713:56 3000:20 3007:4f 3008:3c 3009:3e 301a:5b 301b:5d ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +860 (OEM - Portuguese) +00a4:0f 00a5:59 00a7:15 00a8:22 00a9:63 00ad:5f 00ae:72 00af:16 00b3:33 00b4:2f 00b6:14 00b8:2c 00b9:31 00be:33 00c4:41 00c5:41 00c6:41 00cb:45 00ce:49 00cf:49 00d0:44 00d6:4f 00d7:58 00d8:4f 00db:55 00dd:59 00de:54 00e4:61 00e5:61 00e6:61 00eb:65 00ee:69 00ef:69 00f0:64 00f6:6f 00f8:6f 00fb:75 00fd:79 00fe:74 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:5c 0161:7c 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0191:46 0192:66 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c0:7c 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 0278:66 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02c9:16 02ca:2f 02cb:60 02cd:5f 02dc:7e 0300:60 0301:2f 0302:5e 0303:7e 0304:16 0305:16 0308:22 030e:22 0327:2c 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:5f 2011:5f 2013:5f 2014:5f 2017:5f 2018:27 2019:27 201a:2c 201c:22 201d:22 201e:22 2022:07 2024:07 2026:2e 2030:25 2032:27 2035:60 2039:3c 203a:3e 203c:13 2044:2f 2070:30 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20dd:4f 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:70 2119:50 211a:51 211b:52 211c:52 211d:52 2122:74 2124:5a 2128:5a 212a:4b 212b:41 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2205:4f 2212:5f 2215:2f 2216:5c 2217:2a 221f:1c 2223:7c 2236:3a 223c:7e 22c5:07 2302:7f 2303:5e 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 3000:20 3007:4f 3008:3c 3009:3e 301a:5b 301b:5d 30fb:07 + +861 (OEM - Icelandic) +00a2:63 00a4:0f 00a5:59 00a7:15 00a8:22 00a9:63 00aa:61 00ad:5f 00ae:72 00af:16 00b3:33 00b4:2f 00b6:14 00b8:2c 00b9:31 00ba:6f 00be:33 00c0:41 00c2:41 00c3:41 00c8:45 00ca:45 00cb:45 00cc:49 00ce:49 00cf:49 00d1:4e 00d2:4f 00d4:4f 00d5:4f 00d7:58 00d9:55 00db:55 00e3:61 00ec:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f5:6f 00f9:75 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 0278:66 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02c9:16 02ca:2f 02cb:60 02cd:5f 02dc:7e 0300:60 0301:2f 0302:5e 0303:7e 0304:16 0305:16 0308:22 030e:22 0327:2c 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2017:5f 2018:27 2019:27 201a:27 201c:22 201d:22 201e:22 2022:07 2024:07 2026:07 2030:25 2032:27 2035:27 2039:3c 203a:3e 203c:13 2044:2f 2070:30 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20dd:4f 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:70 2119:50 211a:51 211b:52 211c:52 211d:52 2122:74 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2205:4f 2212:5f 2215:2f 2216:5c 2217:2a 221f:1c 2223:7c 2236:3a 223c:7e 22c5:07 2302:7f 2303:5e 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 3000:20 3007:4f 3008:3c 3009:3e 301a:5b 301b:5d 30fb:07 + +863 (OEM - Canadian French) +00a1:21 00a5:59 00a9:63 00aa:61 00ad:16 00ae:72 00b9:33 00ba:6f 00c1:41 00c3:41 00c4:41 00c5:41 00c6:41 00cc:49 00cd:49 00d0:44 00d1:4e 00d2:4f 00d3:4f 00d5:4f 00d6:4f 00d7:58 00d8:4f 00da:55 00dd:59 00de:54 00e1:61 00e3:61 00e4:61 00e5:61 00e6:61 00ec:69 00ed:69 00f0:64 00f1:6e 00f2:6f 00f5:6f 00f6:6f 00f8:6f 00fd:79 00fe:74 00ff:79 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:22 02ba:27 02bc:27 02c4:5e 02c6:5e 02c8:27 02c9:16 02cb:60 02cd:5f 02dc:7e 0300:60 0302:5e 0303:7e 0304:16 0305:16 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2018:27 2019:27 201a:27 201c:22 201d:22 201e:22 2022:07 2024:07 2026:07 2030:25 2032:27 2035:27 2039:3c 203a:3e 203c:13 2044:2f 2070:30 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20a7:50 20dd:4f 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:70 2119:50 211a:51 211b:52 211c:52 211d:52 2122:74 2124:5a 2128:5a 212a:4b 212b:41 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2205:4f 2212:5f 2215:2f 2216:5c 2217:2a 221f:1c 2223:7c 2236:3a 223c:7e 22c5:07 2302:7f 2303:5e 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 3000:20 3007:4f 3008:3c 3009:3e 301a:5b 301b:5d 30fb:07 + +865 (OEM - Nordic) +00a2:63 00a5:59 00a7:15 00a8:22 00a9:63 00ad:5f 00ae:72 00af:16 00b3:33 00b4:2f 00b6:14 00b8:2c 00b9:31 00bb:3e 00be:33 00c0:41 00c1:41 00c2:41 00c3:41 00c8:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d0:44 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d7:58 00d9:55 00da:55 00db:55 00dd:59 00de:54 00e3:61 00f0:64 00f5:6f 00fd:79 00fe:74 0100:41 0101:61 0102:41 0103:61 0104:41 0105:61 0106:43 0107:63 0108:43 0109:63 010a:43 010b:63 010c:43 010d:63 010e:44 010f:64 0110:44 0111:64 0112:45 0113:65 0114:45 0115:65 0116:45 0117:65 0118:45 0119:65 011a:45 011b:65 011c:47 011d:67 011e:47 011f:67 0120:47 0121:67 0122:47 0123:67 0124:48 0125:68 0126:48 0127:68 0128:49 0129:69 012a:49 012b:69 012c:49 012d:69 012e:49 012f:69 0130:49 0131:69 0134:4a 0135:6a 0136:4b 0137:6b 0139:4c 013a:6c 013b:4c 013c:6c 013d:4c 013e:6c 0141:4c 0142:6c 0143:4e 0144:6e 0145:4e 0146:6e 0147:4e 0148:6e 014c:4f 014d:6f 014e:4f 014f:6f 0150:4f 0151:6f 0152:4f 0153:6f 0154:52 0155:72 0156:52 0157:72 0158:52 0159:72 015a:53 015b:73 015c:53 015d:73 015e:53 015f:73 0160:53 0161:73 0162:54 0163:74 0164:54 0165:74 0166:54 0167:74 0168:55 0169:75 016a:55 016b:75 016c:55 016d:75 016e:55 016f:75 0170:55 0171:75 0172:55 0173:75 0174:57 0175:77 0176:59 0177:79 0178:59 0179:5a 017b:5a 017c:7a 017d:5a 017e:7a 0180:62 0189:44 0197:49 019a:6c 019f:4f 01a0:4f 01a1:6f 01ab:74 01ae:54 01af:55 01b0:75 01b6:7a 01c3:21 01cd:41 01ce:61 01cf:49 01d0:69 01d1:4f 01d2:6f 01d3:55 01d4:75 01d5:55 01d6:75 01d7:55 01d8:75 01d9:55 01da:75 01db:55 01dc:75 01de:41 01df:61 01e4:47 01e5:67 01e6:47 01e7:67 01e8:4b 01e9:6b 01ea:4f 01eb:6f 01ec:4f 01ed:6f 01f0:6a 0261:67 02b9:27 02ba:22 02bc:27 02c4:5e 02c6:5e 02c8:27 02c9:16 02ca:2f 02cb:60 02cd:5f 02dc:7e 0300:60 0301:2f 0302:5e 0303:7e 0304:16 0305:16 0308:22 030e:22 0327:2c 0331:5f 0332:5f 037e:3b 04bb:68 0589:3a 066a:25 2000:20 2001:20 2002:20 2003:20 2004:20 2005:20 2006:20 2010:2d 2011:2d 2013:2d 2014:2d 2017:5f 2018:27 2019:27 201a:27 201c:22 201d:22 201e:22 2022:07 2024:07 2026:07 2030:25 2032:27 2035:27 2039:3c 203a:3e 203c:13 2044:2f 2070:30 2074:34 2075:35 2076:36 2077:37 2078:38 2080:30 2081:31 2083:33 2084:34 2085:35 2086:36 2087:37 2088:38 2089:39 20dd:4f 2102:43 2107:45 210a:67 210b:48 210c:48 210d:48 210e:68 2110:49 2111:49 2112:4c 2113:6c 2115:4e 2118:70 2119:50 211a:51 211b:52 211c:52 211d:52 2122:74 2124:5a 2128:5a 212a:4b 212c:42 212d:43 212e:65 212f:65 2130:45 2131:46 2133:4d 2134:6f 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 2205:4f 2212:5f 2215:2f 2216:5c 2217:2a 221f:1c 2223:7c 2236:3a 223c:7e 226b:3c 22c5:07 2302:7f 2303:5e 2329:3c 232a:3e 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e 3000:20 3007:4f 3008:3c 3009:3e 300b:3e 301a:5b 301b:5d 30fb:07 + +874 (ANSI/OEM - Thai) +00a7:15 00b6:14 203c:13 2190:1b 2191:18 2192:1a 2193:19 2194:1d 2195:12 21a8:17 221f:1c 2302:7f 25ac:16 25b2:1e 25ba:10 25bc:1f 25c4:11 25cb:09 25d8:08 25d9:0a 263a:01 263b:02 263c:0f 2640:0c 2642:0b 2660:06 2663:05 2665:03 2666:04 266a:0d 266b:0e ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e + +932 (ANSI/OEM - Japanese Shift-JIS) +00a1:21 00a5:5c 00a6:7c 00a9:63 00aa:61 00ad:2d 00ae:52 00b2:32 00b3:33 00b9:31 00ba:6f 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c6:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d0:44 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00de:54 00df:73 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e6:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f0:64 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00fe:74 00ff:79 + +936 (ANSI/OEM - Simplified Chinese GBK) +00a6:7c 00aa:61 00ad:2d 00b2:32 00b3:33 00b9:31 00ba:6f 00d0:44 00dd:59 00de:54 00e2:61 00f0:65 00fd:79 00fe:74 + +949 (ANSI/OEM - Korean) +00a6:7c 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00ff:79 20a9:5c + +950 (ANSI/OEM - Traditional Chinese Big5) +00a1:21 00a6:7c 00a9:63 00aa:61 00ad:2d 00ae:52 00b2:32 00b3:33 00b9:31 00ba:6f 00c0:41 00c1:41 00c2:41 00c3:41 00c4:41 00c5:41 00c6:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00cc:49 00cd:49 00ce:49 00cf:49 00d0:44 00d1:4e 00d2:4f 00d3:4f 00d4:4f 00d5:4f 00d6:4f 00d8:4f 00d9:55 00da:55 00db:55 00dc:55 00dd:59 00de:54 00df:73 00e0:61 00e1:61 00e2:61 00e3:61 00e4:61 00e5:61 00e6:61 00e7:63 00e8:65 00e9:65 00ea:65 00eb:65 00ec:69 00ed:69 00ee:69 00ef:69 00f0:65 00f1:6e 00f2:6f 00f3:6f 00f4:6f 00f5:6f 00f6:6f 00f8:6f 00f9:75 00fa:75 00fb:75 00fc:75 00fd:79 00fe:74 00ff:79 + +(UTF-7) + + +(UTF-8) + + diff --git a/tests/regression/target/00-targets.t b/tests/regression/target/00-targets.t index d00b5a50c8..62846c0bef 100644 --- a/tests/regression/target/00-targets.t +++ b/tests/regression/target/00-targets.t @@ -475,8 +475,29 @@ "arg1=val1&arg2=val2", ), }, - - +{ + type => "target", + comment => "REQUEST_BASENAME (get)", + conf => qq( + SecRuleEngine On + SecRequestBodyAccess On + SecResponseBodyAccess On + SecResponseBodyMimeType null + SecDebugLog $ENV{DEBUG_LOG} + SecDebugLogLevel 9 + SecRule REQUEST_BASENAME "index.html" "phase:2,log,pass,id:500189" + ), + match_log => { + error => [ qr/Pattern match "index.html" at REQUEST_BASENAME.*/s, 1 ], + debug => [ qr/Pattern match "index.html" at REQUEST_BASENAME.*/s, 1 ], + }, + match_response => { + status => qr/^200$/, + }, + request => new HTTP::Request( + GET => "http://$ENV{SERVER_NAME}:$ENV{SERVER_PORT}/index.html?apath=/my/cool/path.com", + ), +}, # AUTH_TYPE #{ diff --git a/tests/run-regression-tests.pl.in b/tests/run-regression-tests.pl.in index 40e885f0eb..d8db033040 100755 --- a/tests/run-regression-tests.pl.in +++ b/tests/run-regression-tests.pl.in @@ -11,7 +11,7 @@ use strict; use Time::HiRes qw(gettimeofday sleep); use POSIX qw(WIFEXITED WEXITSTATUS WIFSIGNALED WTERMSIG); -use File::Spec qw(rel2abs); +use File::Spec::Functions qw(rel2abs); use File::Basename qw(basename dirname); use FileHandle; use IPC::Open2 qw(open2); @@ -54,7 +54,7 @@ $SIG{TERM} = $SIG{INT} = \&handle_interrupt; my $platform = "apache"; my %opt; -getopts('A:E:D:C:T:H:a:p:dvh', \%opt); +getopts('A:E:D:C:T:H:S:a:p:dvh', \%opt); if ($opt{d}) { $Data::Dumper::Indent = 1; @@ -125,13 +125,14 @@ unless (defined $opt{S}) { LOGS_DIR => $FILES_DIR, SCRIPT_DIR => $SCRIPT_DIR, REGRESSION_DIR => $REG_DIR, - DIST_ROOT => File::Spec->rel2abs(dirname("$SCRIPT_DIR/../../..")), + DIST_ROOT => File::Spec->rel2abs(dirname("$SCRIPT_DIR/../..")), AUDIT_LOG => $opt{A}, DEBUG_LOG => $opt{D}, ERROR_LOG => $opt{E}, HTTPD_CONF => $opt{C}, HTDOCS => $opt{H}, USER_AGENT => $UA_NAME, + RUNASUSER => $ENV{USER} || $ENV{LOGNAME} || $ENV{USERNAME} || 'unknown', ); #dbg("OPTIONS: ", \%opt); diff --git a/unicode.mapping b/unicode.mapping index 6af8117f38..2654c4a619 100644 --- a/unicode.mapping +++ b/unicode.mapping @@ -23,7 +23,7 @@ 0191:46 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e 1256 (ANSI - Arabic) -00c0:41 00c2:41 00c7:43 00c8:45 00c9:45 00ca:45 00cb:45 00ce:49 00cf:49 00d4:4f 00d9:55 00db:55 00dc:55 0191:46 ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e +0620:41 0621:41 0622:43 0623:45 0624:45 0625:45 0626:45 0627:49 0628:49 0629:4f 062a:55 062b:55 062c:55 062d:46 062e:43 062f:44 0630:45 0631:46 0632:47 0633:48 0634:49 0635:4a 0636:4b 0637:4c 0638:4d 0639:4e 063a:4f 0641:41 0642:42 0643:43 0644:44 0645:45 0646:46 0647:47 0648:48 0649:49 064a:4a 064b:4b 064c:4c 064d:4d 064e:4e 064f:4f 0650:50 0651:51 0652:52 1257 (ANSI - Baltic) ff01:21 ff02:22 ff03:23 ff04:24 ff05:25 ff06:26 ff07:27 ff08:28 ff09:29 ff0a:2a ff0b:2b ff0c:2c ff0d:2d ff0e:2e ff0f:2f ff10:30 ff11:31 ff12:32 ff13:33 ff14:34 ff15:35 ff16:36 ff17:37 ff18:38 ff19:39 ff1a:3a ff1b:3b ff1c:3c ff1d:3d ff1e:3e ff20:40 ff21:41 ff22:42 ff23:43 ff24:44 ff25:45 ff26:46 ff27:47 ff28:48 ff29:49 ff2a:4a ff2b:4b ff2c:4c ff2d:4d ff2e:4e ff2f:4f ff30:50 ff31:51 ff32:52 ff33:53 ff34:54 ff35:55 ff36:56 ff37:57 ff38:58 ff39:59 ff3a:5a ff3b:5b ff3c:5c ff3d:5d ff3e:5e ff3f:5f ff40:60 ff41:61 ff42:62 ff43:63 ff44:64 ff45:65 ff46:66 ff47:67 ff48:68 ff49:69 ff4a:6a ff4b:6b ff4c:6c ff4d:6d ff4e:6e ff4f:6f ff50:70 ff51:71 ff52:72 ff53:73 ff54:74 ff55:75 ff56:76 ff57:77 ff58:78 ff59:79 ff5a:7a ff5b:7b ff5c:7c ff5d:7d ff5e:7e