pax_global_header 0000666 0000000 0000000 00000000064 15174655764 0014536 g ustar 00root root 0000000 0000000 52 comment=fe93a8dbe1e82ffc5b7a696169d1e8cb4450510b
modsecurity-apache-2.9.13/ 0000775 0000000 0000000 00000000000 15174655764 0015420 5 ustar 00root root 0000000 0000000 modsecurity-apache-2.9.13/.github/ 0000775 0000000 0000000 00000000000 15174655764 0016760 5 ustar 00root root 0000000 0000000 modsecurity-apache-2.9.13/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 15174655764 0021143 5 ustar 00root root 0000000 0000000 modsecurity-apache-2.9.13/.github/ISSUE_TEMPLATE/bug-report-for-version-2-x.md 0000664 0000000 0000000 00000002415 15174655764 0026430 0 ustar 00root root 0000000 0000000 ---
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.
modsecurity-apache-2.9.13/.github/ISSUE_TEMPLATE/bug-report-for-version-3-x.md 0000664 0000000 0000000 00000002611 15174655764 0026427 0 ustar 00root root 0000000 0000000 ---
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.
modsecurity-apache-2.9.13/.github/PULL_REQUEST_TEMPLATE.md 0000664 0000000 0000000 00000001416 15174655764 0022563 0 ustar 00root root 0000000 0000000
## what
## why
## references
modsecurity-apache-2.9.13/.github/security2.conf 0000664 0000000 0000000 00000000356 15174655764 0021564 0 ustar 00root root 0000000 0000000 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
modsecurity-apache-2.9.13/.github/workflows/ 0000775 0000000 0000000 00000000000 15174655764 0021015 5 ustar 00root root 0000000 0000000 modsecurity-apache-2.9.13/.github/workflows/ci.yml 0000664 0000000 0000000 00000017313 15174655764 0022140 0 ustar 00root root 0000000 0000000 name: Quality Assurance
on:
push:
pull_request:
jobs:
build-linux:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-24.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 libpcre3-dev libpcre3 pkg-config libyajl-dev apache2 apache2-bin apache2-data
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- 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-24.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 libpcre3-dev libpcre3 pkg-config libyajl-dev apache2 apache2-bin apache2-data
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- 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
test-regression-linux:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-24.04]
platform: [x64]
compiler: [gcc]
configure:
- {label: "with pcre2, with study, with jit", opt: "--enable-pcre-study=yes --enable-pcre-jit" }
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 libpcre3-dev libpcre3 pkg-config libyajl-dev apache2 apache2-bin apache2-data perl libwww-perl ssdeep libfuzzy-dev libfuzzy2
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- 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@0f9c86f9e693db67dacf53986e1674de5f2e5f28 #v0.3.0
- name: make
run: make -j `nproc`
- name: install module
run: sudo make install
- name: run regression tests
run: make test-regression
cppcheck:
runs-on: [ubuntu-24.04]
container:
image: debian:sid
steps:
- name: Setup Dependencies
run: |
apt-get update -y -qq
apt-get install -y --no-install-recommends build-essential \
autoconf \
automake \
libtool \
pkg-config \
cppcheck \
apache2-dev \
libpcre2-dev \
libapr1-dev \
libaprutil1-dev \
libxml2-dev \
liblua5.3-dev \
libyajl-dev \
libfuzzy-dev \
ssdeep \
curl \
ca-certificates \
git
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: configure
run: |
./autogen.sh
./configure --with-apxs=/usr/bin/apxs
- name: cppcheck
run: |
make check-static
modsecurity-apache-2.9.13/.github/workflows/test-ci-windows.yml 0000664 0000000 0000000 00000027556 15174655764 0024617 0 ustar 00root root 0000000 0000000 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
with:
submodules: recursive
fetch-depth: 0
- 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
with:
submodules: recursive
fetch-depth: 0
- 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
with:
submodules: recursive
fetch-depth: 0
- 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
modsecurity-apache-2.9.13/.gitignore 0000664 0000000 0000000 00000002424 15174655764 0017412 0 ustar 00root root 0000000 0000000 # 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
modsecurity-apache-2.9.13/.gitmodules 0000664 0000000 0000000 00000000206 15174655764 0017573 0 ustar 00root root 0000000 0000000 [submodule "apache2/others/libinjection"]
path = apache2/others/libinjection
url = https://github.com/libinjection/libinjection.git
modsecurity-apache-2.9.13/CHANGES 0000664 0000000 0000000 00000261211 15174655764 0016416 0 ustar 00root root 0000000 0000000 28 Apr 2026 - 2.9.13
--------------------
* fix: heap buffer overflow in acmp pm
[Issue #3545 - @fumfel,@airween]
* fix: probably UB (left shift of neg. val) in ip_tree code
[Issue #3542 - @fumfel,@airween]
* feat(libinjection): add libinjection 4 final to v2
[Issue #3535 - @airween]
* fix: cppcheck warnings
[Issue #3530 - @airween]
* feat: add Lua 5.5 support when available
[Issue #3527 - @fzipi,@airween]
* refactor: remove NGINX support from v2 (#3498)
[Issue #3502 - @sanjib2006]
* fix: cppcheck warnings in nginx/
[Issue #3493 - @umprayz]
* fix: cppcheck warnings in standalone/
[Issue #3488 - @umprayz]
* v2: Accept requests whose body length is exactly equal to SecRequestBodyNoFilesLimit
[Issue #3480 - @hnakamur]
* fix: yajl detection for source installations (#3457)
[Issue #3458 - @weida]
* feat: fix regression test, add a new CI workflow
[Issue #3456 - @airween]
* Fix libxml2 related deprecated issues
[Issue #3454 - @airween]
* Add CMake and CI Pipeline for ModSecurityIIS in ModSecurity V2
[Issue #3452 - @A13501350]
* fix(iis): IPv6 Handling in ModSecurity IIS Module
[Issue #3443 - @A13501350]
* fix: add event message resources to ModSecurityIIS.dll and resolve "Invalid function" errors (#3408)
[Issue #3438 - @A13501350]
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
[Issue #1412 - @client9, @zimmerle and @bjdijk]
* Avoid log flood while using SecConnEngine
[Issue #1436 - @victorhora]
* Make url path absolute for SecHashEngine only when it is relative
in the first place.
[Issue #752, #1071 - @hideaki]
* Fix the hex digit size for SHA1 on msc_crypt implementation.
[Issue #1354 - @zimmerle and @parthasarathi204]
* Avoid to flush xml buffer while assembling the injected html.
[Issue #742 - @zimmerle]
* Avoid additional operator invokation if last transform of a multimatch
doesn't modify the input
[Issue #1086, #1087 - Daniel Stelter-Gliese]
* Adds a sanity check before use ctl:ruleRemoveTargetByTag.
[Issue #1353 - @LukeP21 and @zimmerle]
* Uses an optional global lock while manipulating collections.
[Issues #1224 - @mturk and @zimmerle]
* Fix collection naming problem while merging collections.
[Issue #1274 - Coty Sutherland and @zimmerle]
* Fix --enable-docs adding missing Makefile, modifying autoconf and filenames
[Issue #1322 - @victorhora]
* Change from using rand() to thread-safe ap_random_pick.
[Issue #1289 - Robert Bost]
* Cosmetics: added comments on odd looking code to prevent future
scrutiny
[Issue #1279 - Coty Sutherland]
* {dis|en}able-server-context-logging: Option to disable logging of
server info (log producer, sanitized objects, ...) in audit log.
[Issue #1069 - Marc Stern]
* Allow drop to work with mod_http2
[Issue #1308, #992 - @bazzadp]
* Fix SecConn(Read|Write)StateLimit on Apache 2.4
[Issue #1340, #1337, #786 - Sander Hoentjen]
* {dis|en}able-stopwatch-logging: Option to disable logging of stopwatches
in audit log.
[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
in audit log
[Issue #1070, #1381 - Marc Stern]
* {dis|en}able-collection-delete-problem-logging: Option to disable logging of
collection delete problem in audit log when log level < 9.
[Issue #1380 - Marc Stern]
* Adds rule id in logs whenever a rule fail.
[Issue #1379, #391 - Marc Stern]
* {dis|en}able-server-logging: Option to disable logging of
"Server" in audit log when log level < 9.
[Issue #1070 - Marc Stern]
* {dis|en}able-filename-logging: Option to disable logging of filename
in audit log.
[Issue #1065 - Marc Stern]
* Reads fuzzy hash databases on init
[Issue #1339 - Robert Paprocki and @Rendername]
* Changes the configuration to recognize soap+xml as XML
[Issue #1374 - @emphazer and Chaim Sanders]
* Fix building with nginx >= 1.11.11
[Issue #1373, #1359 - Andrei Belov and Thomas Deutschmann]
* Using Czechia instea of Czech Republic
[Issue #1258 - Michael Kjeldsen]
* {dis|en}able-rule-id-validation: Option to disable rule id validation
[Issue #1150 - Marc Stern and ModSecurity team]
* JSON Log: Append a newline to concurrent JSON audit logs
[Issue #1233 - Robert Paprocki]
* JSON Log: Don't unnecessarily rename request body parts in cleanup
[Issue #1223 - Robert Paprocki]
* Fix error message inside audit logs
[Issue #1216 and #1073 - Armin Abfalterer]
* Remove port from IPV4 address when running under IIS.
[Issue #1220, #1109 and #734 - Robert Culyer]
* Remove logdata and msg fields from JSON audit log rule.
[Issue #1190 and #1174 - Robert Paprocki]
* Better handle the json parser cleanup
[Issue #1204 - Ephraim Vider]
* Fix status failing to report in Nginx auditlogs
[Issue #977, #1171 - @charlymps and Chaim Sanders]
* Fix file upload JSON audit log entry
[Issue #1181 and #1173 - Robert Paprocki and Christian Folini]
* configure: Fix detection whether libcurl is linked against gnutls and,
move verbose_output declaration up to the beginning.
[Issue #1158 - Thomas Deutschmann (@Whissi)]
* 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
-------------------
* No changes.
03 Feb 2016 - 2.9.1-RC1
-----------------------
* Added support to generate audit logs in JSON format.
[Issue #914, #897, #656 - Robert Paprocki]
* Creating AuditLog serial file (or parallel index) respecting the
permission configured with SecAuditLogFileMode. Previously, it was
used only to save the transactions while in parallel mode.
[Issue #852 - @littlecho and ModSecurity team]
* Checking for hashing injection response, to report in case of failure.
[Issue #1041 - ModSecurity team]
* Stop buffering when the request is larger than SecRequestBodyLimit
in ProcessPartial mode
[Issue #709, #705, #728 - Justin Gerace and ModSecurity team]
* Extended Lua support to include version 5.3
[Issue #837, #762, #814 - Athmane Madjoudj and ModSecurity team]
* mlogc: Allows user to choose between TLS versions (TLSProtocol option
introduced).
[Issue #881 - Ishwor Gurung]
* Allows mod_proxy's "nocanon" behavior to be specified in proxy actions
[Issue #1031, #961, #763 - Mario D. Santana and ModSecurity team]
* Refactoring conditional #if/#defs directives.
[Issue #996 - Wesley M and ModSecurity team]
* mlogc-batch-load.pl.in: fix searching SecAuditLogStorageDir
files with Apache 2.4
[Issue #775 - Elia Pinto]
* Understands IIS 10 as compatible on Windows installer.
[Issue #931 - Anton Serbulov, Pavel Vasilevich and ModSecurity team]
* Fix apache logging limitation by using correct Apache call.
[Issue #840 - Christian Folini]
* Fix apr_crypto.h check on 32-bit Linux platform
[Issue #882, #883 - Kurt Newman]
* Fix variable resolution duration (Content of the DURATION variable).
[Issue #662 - Andrew Elble]
* Fix crash while adding empty keys to persistent collections.
[Issue #927 - Eugene Alekseev, Marc Stern and ModSecurity team]
* Remove misguided call to srand()
[Issues #778, #781 and #836 - Michael Bunk, @gilperon]
* Fix compilation problem while ssdeep is installed in non-standard
location.
[Issue #872 - Kurt Newman]
* Fix invalid storage reference by apr_psprintf at msc_crypt.c
[Issue #609 - Jeff Trawick]
12 Feb 2015 - 2.9.0
-------------------
* Fix apr_crypto.h include, now checking if apr_crypto.h is available by
checking the definition WITH_APU_CRYPTO.
[martinjina and ModSecurity team]
15 Dez 2014 - 2.9.0-RC2
-----------------------
* OpenSSL dependency was removed on MS Windows builds. ModSecurity is now using
the Windows certificate storage.
[Gregg Smith, Steffen and ModSecurity team]
* Informs about external resources loaded/failed while reloading Apache.
[ModSecurity team]
* Adds missing 'ModSecurity:' prefix in some warnings messages.
[Walter Hop and ModSecurity team]
* Refactoring external resources download warn messages. Holding the message
to be displayed when Apache is ready to write on the error_log.
[ModSecurity team]
* Remote resources loading process is now failing in case of HTTP error.
[Walter Hop and ModSecurity team]
* Fixed start up crash on Apache with mod_ssl configured. Crash was happening
during the download of remote resources.
[Christian Folini, Walter Hop and ModSecurity team]
* Curl is not a mandatory dependency to ModSecurity core anymore.
[Rainer Jung and ModSecurity team]
18 Nov 2014 - 2.9.0-RC1
-----------------------
* `pmFromFile' and `ipMatchFromFile' operators are now accepting HTTPS served
files as parameter.
* `SecRemoteRules' directive - allows you to specify a HTTPS served file that
may contain rules in the SecRule format to be loaded into your ModSecurity
instance.
* `SecRemoteRulesFailAction' directive - allows you to control whenever the
user wants to Abort or just Warn when there is a problem while downloading
rules specified with the directive: `SecRemoteRules'.
* `fuzzyHash' operator - allows to match contents using fuzzy hashes.
* `FILES_TMP_CONTENT' collection - make available the content of uploaded
files.
* InsecureNoCheckCert - option to validate or not a chain of SSL certificates
on mlogc connections.
* ModSecurityIIS: ModSecurity event ID was changed from 0 to 0x1.
[Issue #676 - Kris Kater and ModSecurity team]
* Fixed signature on "status call": ModSecurity is now using the original
server signature.
[Issues #702 - Linas and ModSecurity team]
* YAJL version is printed while ModSecurity initialization.
[Issue #703 - Steffen (Apache Lounge) and Mauro Faccenda]
* Fixed subnet representation using slash notation on the @ipMatch operator.
[Issue #706 - Walter Hop and ModSecurity team]
* Limited the length of a status call.
[Issue #714 - 'cpanelkurt' and ModSecurity team]
* Added the missing -P option to nginx regression tests.
[Issue #720 - Paul Yang]
* Fixed automake scripts to do not use features which will be deprecated in
the upcoming releases of automake.
[Issue #760 - ModSecurity team]
* apr-utils's LDFALGS is now considered while building ModSecurity.
[Issue #782 - Daniel J. Luke]
* IIS installer is not considering IIS 6 as compatible anymore.
[Issue #790 - ModSecurity team]
* Fixed yajl build script: now looking for the correct header file.
[Issue #804 - 'rpfilomeno' and ModSecurity team]
* mlgoc is now forced to use TLS 1.x.
[Issue #806 - Josh Amishav-Zlatin and ModSecurity team]
14 Apr 2014 - 2.8.0
-------------------
Bug fix
* Build issue: Now using autotools to idenfiy if sys/utsname.h is present.
* Change configure.ac version to 2.8
31 Mar 2014 - 2.8.0-RC1
-----------------------
New features
* JSON Parser is no longer under tests. Now it is part of our mainline;
* Connection limits (SecConnReadStateLimit/SecConnWriteStateLimit) now support white and suspicious list;
* New variables: FULL_REQUEST and FULL_REQUEST_LENGTH were added, allowing the rules to access the full content of a request;
* ModSecurity status is now part of our mainline;
* New operator: @detectXSS was added. It makes usage of the newest libinjection XSS detection functionality;
* Append and prepend are now supported on nginx (Ref: #635);
* SecServerSignature is now available on nginx (Ref: #637).
Improvements
* Regression tests are not able to expect different values according to the platform;
* Visual C++ 12/10 runtime dependencies are now part of the IIS installer, no need to have it installed prior ModSecurity installation (Ref: #627);
* New script was added to the IIS versions to identify whenever there is a missing dependency (available through the Application Menu);
* Memory usage improvement: using correct memory pools according to the context (Ref: #618, #620, #619);
* Independent API call to free the connection allocations, independently from the request objects, improvements on Nginx performance, vide issue for more information (Ref: #620, #648);
* IIS installer is now using the correct 32/64bits folders to install;
* IIS Installer 32bits now refuses to install on 64bits environments;
* IIS: Using new WiX options to build the package in the correct architecture;
* While installing IIS version the installer will remove old ModSecurityIIS configuration or files before proceed with the installation, avoiding further errors;
* CRS from IIS version was upgraded to 2.2.9;
* IIS installer does not support repair anymore, in fact it was not working already and it is now disabled;
* ModSecurity now warns the user who tries to use "proxy" in IIS or Nginx. Proxy is Apache only;
* Remove warnings from the build process (Ref: #617);
* Apache configuration in regression tests was changed making it more platform independent;
* Reduced the amount of warnings during the compilation (Ref: #385a2828e87897bd611bd2a519727ef88dc6d632, #1e63e49db4a592d28e08a33fc60750c37a3886fe);
* Regression tests were refactored to be more Nginx friendly;
* Fixed some regression tests that were not being flexible to handle multiple platforms: (Ref #636)
- Fixed config/00-load-modsec.t test case. Now it expects for Nginx loaded message as it does for Apache. (Ref: #643);
- Fixed mixed/10-misc-directives.t. Now it does not expect for SecServerSignature on the logs, just in the headers as the Nginx does in silence.
- Fixed tnf/10-tfn-cache.t, action/10-logging.t, config/10-misc-directives.t, config/10-request-directives.t, misc/00-multipart-parser.t , misc/10-tfn-cache.t, rule/20-exceptions.t, rule/00-basics.t, rule/10-xml.t;
- Increased the timeout while reading the auditlog;
- SecAuditLogType Concurrent was removed from the regression test case, not compatible with all ports yet;
- Regression tests were speeded up, as the number of tests are growing it is impossible to have it slow;
- Fixed regression tests scripts paths, to make it MacOS friendly;
- Avoiding dead locks on Nginx regression tests by enforcing a timeout whenever a request appears to fail;
* Updates to fix errors found by Parfait static code analysis (Ref: #612);
* Cleaning up on the repository, by removing unused files;
* IIS installer now supports to perform the installation without register the DLL on the system. It means that the user can download our MSI installer as it was a tarball archive (Ref #629, #624);
* IIS now support 32bits and 64bits pools, both are registered on IIS (Ref #628).
Bug fix
* Correctly handling inet_pton in IIS version;
* Nginx was missing a terminator while the charset string was mounted (Ref: #148);
* Added mod_extract_forwarded.c to run before mod_security2.c (Ref: #594);
* Added missing environment variables to regression tests;
* Build system is now more flexible by looking at liblua at: /usr/local/lib;
* Fixed typo in README file.
* Removed the non standard compliant HTTP response status code 44 from modsecurity recommended file (Ref: #665);
* Fixed segmentation fault if it fails to write on the audit log (Ref: #668);
* Not rejecting a larger request with ProcessPartial. Regression tests were also added (Ref: #597);
* Fixed UF8 to unicode conversion. Regression tests were also added(Ref: #672);
* Avoiding segmentation fault by checking if a structure is null before access its members;
* Removed double charset-header that used happen due a hardcoded charset in Nginx implementation (Ref: #650);
* Now alerting the users that there is no memory to proceed loading the configuration instead of just die;
* If SecRuleEngine is set to Off and SecRequestBodyAccess On Nginx returns error 500. Standalone is now capable to identify whenever ModSecurity is enabled or disabled, independently of ModSecurity core (Ref: #645);
* Fixed missing headers on Nginx whenever SecResponseBodyAccess was set to On and happens to be a filter on phase equals or over 3. (Ref #634);
* IIS is now picking the correct version of AppCmd while uninstalling or installing ModSecurityISS. (Ref #632).
17 Dec 2013 - 2.7.7
-------------------
Fixes:
- Changed release version to 2.7.7
- Got the configure scripts inside the release tarball
16 Dec 2013 - 2.7.6
-------------------
Improvements:
- Organizes all Makefile.am - 1cde4d2dd9d96747536c1c25d06ba0677069477f
Now using one file per line (sorted). This is the better way to handle it, since it reduces the possibility of merge conflicts.
- nginx: generates config file using configure input. - 351b9cc357d439e30ebd61d89a9e38ecf55c6827
The nginx config file was looking for depedencies by its own, by doing that it was ignoring the options that were passed to configure script. This commit deletes this config file and adds a meta-config which is populated by configure whenever the standalone-module is enabled.
- nginx: adds lua support - da16d9e5d51d4ef8734687514a4e1368e7fb4284
- iis: Cosmetics fixies on sqli. - 5046c8327ea21c69b4c0d0c0057c692b05b09fef
This is needed to get it compiled with VS2011 on Windows8
- Regression tests: makes configuration compatible with 2.2 and 2.4 (try 2) - ae252ee8767069363906e5a611dff487b799b839
- nginx: Trying apxs and apxs2 while compiling nginx module - 65d9272fdc353e1263567b60604542d377d19672
- nginx: Trying apxs and apxs2 while compiling nginx module - 35fd75d859e4a8873b8843da1db13e04a1b08140
- macos: Using glibtoolize instead of libtoolize - 751a9f4e45213cd69f00c62c71edc9d7ad99b82d
- regression-tests: makes configuration compatible with 2.2 and 2.4 - 6fc4cac37ab1be8d1232140042b58fe4bd93ee17
- Regression test: get it working with apache 2.4 - e9813cd0d9bfc5b0c9aa5832634ec1b39b805108
Changes in httpd.conf.in to get it working with apache 2.4
- Code cosmetics. - 7366f35c1d80772d739b35da8faa972f92a72b97
Changed to reduce the number of possible fails during Build Bot compilation.
- iis: Waiting for 5 seconds before move curl directory - 9bf2959c919587ebc63f5a1b8c0785da8927bff5
Testing buildbot.
- Redefines unixd_set_global_mutex_perms on tests - f70f6f4281b806627e0cf0dbb9c84ae5864bdb16
Avoding conflicts with the standalone implementation
- Adds verbose quality check - 388943440cc9b8c6fdea09f5e365a2e5a3e792e2
Vera++ and ccpcheck are not outputing to the stderr instead stdout allowing the buildbot to extract some numbers about it.
- Adds support for coding style and quality check - b77e90152d119609ac78a7028383c3b79898b2cf
Initial effort to get the code on shape. This will be executed by the buildbots as soon as they get ready for it.
- iis: New improvements on the Wix installer - 2ea5a74a7bfb00f21312e51e48aa6dac03d84600
* Now the installation is divided in modules: ModSecurity and CRS.
* Added default configuration
* Configuration was moved to "Program Files" folder
* Build_msi script now using candle available in %PATH%
- iis: Removes the installer helper dependency - 1a12648c9f6028f251af0f03c889397c7954b74c
Now using appcmd directly with WiX instead of calling the installer helper.
- iis: Remove readme.html - 550d5aae21cba696cac1ce75ab8113e5255d5a59
This HTML is about "Creating a Native Module for IIS7" not straight related to ModSecurity itself.
- iis: Adds batch script to compile Wix - a2c5fc831baf0b324ebb66b0f878dacf1ec2f808
This batch script can be used to generate our msi installer.
- iis: Adds Wix installer resources - 3604763e15a665eb7a6ecae1f7e7c65cebbb1d17
This is all about cosmetic changes.
- iss: Removes Post-Build event. - 28bbde1bb218b004654cb865fc8563d69b848dc2
There was a copy on Post-Build event using a hard coded path. This patch removes this Post-Build event.
- iis: Relative paths on the VS project file - 368617ddb2443f9b6036f80a648d467d07c9a054
There are a ModSecurityIIS solution and project files, those were using hard coded paths to meet the dependencies. As consequence of the last update in our build scripts, now we are able to built the dependencies and load it to our Visual Studio project using relative paths.
- iis: Adds release script - 9477118903861ce80c4c27cb581bf3462315e98e
- iis: fixies the Installer.cpp coding style - 79875b1af8e8571098345b91557bab9c06eb7c88
- iis: Removes AppWizard remade file - 91738f93bcc82b6ab756c550a66b6cf6af2fa9f8
Apparently the AppWizard was used to generate part of this Installer, the ReadMe.txt created by the AppWizard was removed by this commit
- iss: Removes pre-compiled headers - adfbeb85dcfa9466b72eebb8d1bd8eb7728bab79
No need to use the pre-compiled headers in InstallerHelper, removing it, in order to keep the project lean.
- iis: Moves installer to InstallerHelper - 6adf25667dd4bfa33010bd6d8ae3d35046a69967
To organize the folder the Installer application was renamed to installer helper. It is not the real installer, it is just an helper which is executed during the installation phase.
- iss: Removes fart dependencies - 8c3b8d81b613aaa38f28472af1eb26c90c7fc9da
This commit removes the dependency of the fart.exe utility. The utility was responsible to rename contents inside some dependencies build files. Those modifications are not longer needed.
- iss: Better err handling in build scripts. - 192599bf63b6ae5aa08e4536a90d5d0a17f969f7
Now checking for errors in every step of the build phase
- iis: Moves build_module.bat to build_modsecurity.bat - e25c6b2e85ced7beba4d41867dbdf30e9c1286d3
The build_modsecurity.bat is now on the iis sub-directory, not in the dependencies anymore. Its content was also changed fixing all the paths.
- iis: Identifies arch before unzip apache - cf5de78dfb9fffd21edf17af9e1db8f2fd83c804
Currently we need the Apache binary which could be used in 32 or 64 bits. This patch makes usage of 'cl' to identify which architecture is set.
- iis: Renamves winbuild to dependencies - 1447766e816a896e88c9c8f053fcc3f62797bac1
Since the directory becomes all about dependencies there is no need to call it winbuild anymore.
- iis: Removes unnecessary files from winbuild dir - 9f8cbf6ed8034ba42aa4967699308df09864fd18
Those .mak files seems to be part of an old build system. Since the script are now working fine, this commit removes all those .mac files and also a CMakeList.txt and the Makefile.win.
- iis: Improves the iis build system - b277e538f28c87c81c1b50925dd8b82996b88294
Now checking for common errors while building. Refactoring on the build scripts, now there is this build_dependencies.bat script on the iis sub-folder. By calling this script all the dependencies should be build under the winbuild/. This commit also removes build scripts that were not needed anymore.
- iis: Fixes the vcxproj file - a946a163f0ad822c760af80ca32dda61f0e6b2a9
Versions of the dependencies were changed, as long as the version of the Visual Studio, now 12.
- iis: Removes unecessary files from the build system - 26738d2e34bcc7620047bd23180e0e26a64c71ee
The following files were removed:
* VCVarsQueryRegistry.bat
* vcvars64.bat
* vsvars32.bat
The visual studio files can be called direcltly, not necessary to distribute those files, at least in VS12.
- iss: Changes httpd version 2.4.6 - 0a772cb0748aa51a01800e0473309b9de792b456
Apache version was changed to 2.4.6 to sync with the current apache lounge version.
- iis: Changes the version of the dependencies - 3e6fb41d36b7a5e98a55d8f52b88b29d1bd50b64
* pcre from 8.30 to 8.33
* zlib from 1.2.7 to 1.2.8
* libxml2 from 2.7.7 to 2.9.1
* curl from 7.24 to 7.33.0
- Removes standalone/Makefile.in - e3c19d53d23c48fea337aae76a87b2a85c36a1f1
Makefile.in is recommended to be in the repository whenever it is edit manually, in our case the automatically generated Makefile.in is ok.
Bug Fixes:
- test: Avoids conflict of fuctions definition - cef72855e4106ce29e1d39103ebf9eb9ab28f17e
- test: Makes the unit tests to work again - cc982ae42ec86c79a67be1a01c6ee35fb06c272c
The unit tests was not working due to lack update. This patch adds the necessary stuff to have it work again.
- iis: Avoids directory link while building - ad330a44bfa39430cf6340cb52971568cccdf1d6
Build scripts was creating links allowing the project to be loaded into Visual Studio without care about the dependencies versions. Sometimes windows refuse to delete those links leading the script to fail. This patch moves the sources directories instead of create links to it.
- QA: Avoids the utilization of 3rd filedescriptor - 69c5ccac662f4e11a6eefd54a3e912583c067b9d
No need to use a 3rd description on the quality check scripts. Stderr is now redirected to stdout and filtered as needed.
- Supports WarningCountingShellCommand in cppcheck and vera - baaf502363e68c3240b60adb7f7c91f5b4f0ba03
WarningCountingShellCommand allow us to have some measurements on the buildbot waterfall.
- iis: Using base_rules instead of activated_rules - 7b1537058fa451e0df7098cd907ef19f04102f9d
- iis: Fix inet_pton build problem - a4202146b8d26b6615bbab986383fe0afae60d77
There is a function named inet_pton on windows API, with different signature. This patch just override the windows function and point the inet_pton to our implementation.
- iis: Adds Wix installer xml file.c - b32cb7d9ab397160f0154aa4bd4e9638658b41e6
This commit adds the Wix template to our git repository.
- iis: build_modsecurity.bat fixies - 7e03e3f840375ed682c35a5bb67932461cc77013
This commit enable a cleanup on the mod_security build directory avoiding symbols with different architectures.
- iis: Fix mlogc build on windows - 9b7663fa79377a0685130a019916d810f31e7478
The libcurl path was not pointing to the correct directory
- Fix #154, Uses addn instead of apr_table_setn - 1734221d9d3a78f9aafd68e35717da9ee1a4fe51
The headers are represented in the format of an apr_table, which is able to handle elements with the same key, however the function apr_table_setn checks if the key exists before add the element, if so it replaces the old value with the new one. This was making our implementation to just keep the last added Cookie. The apr_table_addn function, which is now used, just add a new item without check for olders one.
- Merge pull request #579 from zimmerle/revert_139 - 61e54f2067ae760808359926ff91d57275df1aac
Revert merge request #139
- Revert "Merge pull request #139 from chaizhenhua/remotes/trunk" - 7f7d00fa2c364716691df1b45779304b24a0debb
This reverts commit 10fd40fb0d06f6c577d870b6f15d2f6e2a3a5b1b, reversing changes made to 414033aafa94cd50c9b310afd3f164740caccc94.
- Merge pull request #578 from client9/remotes/trunk - b0c3977845f60747b15ae10531b7d20355a22627
libinjection sync to v3.8.0
- libinjection sync - a5f175d79fac1e69124da4e1e227b622e7e233d7
- Merge pull request #152 from client9/remotes/trunk - 88ebf8a0bdbc4db1be76f3a2e70df77cc52a5925
Sync to libinjection v3.7.1
- libinjection sync - fcb6dc13ed6efb066fb9b70405eecab8b83a2d96
- libinjection sync - f52242a013f301ca5c17e59b662124833cb7cc6d
- Merge pull request #148 from zimmerle/bugfix_charset_missing_string_terminator - b76e26d81ddafc2b99bffad53d1426f8fd33080a
Bugfix: missing string terminator while mounting the charset (nginx)
- Bugfix: missing string terminator while mounting the charset (nginx) - ff19dcd5c53d4af61d0a9397d4616f47f80ee207
The charset in headers is mounted using ngx_snprintf which does not place the string terminator. This patch adds the terminator at the end of the string. The size was correctly allocated, just missing the terminator.
- Merge pull request #141 from client9/remotes/trunk - 9a630eea23a7ead4e77617c86dc937fd7a421a57
libinjection sync to v3.6.0
- libinjection sync - 11217207e8f2e0cf15742273836399866971071a
- Merge pull request #139 from chaizhenhua/remotes/trunk - 10fd40fb0d06f6c577d870b6f15d2f6e2a3a5b1b
Fixed fd leackage after reload
- Merge pull request #138 from client9/remotes/trunk - 414033aafa94cd50c9b310afd3f164740caccc94
libinjection sync
- Fixed fd leackage after reload - e0993fcd7a166ce9e1a279a47d050af1311d9001
- libinjection sync - 2268626c20260e88cab9b7830f8a06101fa7172a
- Fix logical disjunction and conjunction issues - 7e0a9ecf7d492e85650671a0cfcfd53e5f15df2c
Security Issues:
- Fix Chunked string case sensitive issue - CVE-2013-5705 - f8d441cd25172fdfe5b613442fedfc0da3cc333d
(Thanks Martin Holst Swende - @mhswende)
- Revert "Fix Chuncked string case sensitive issue" - 3901128f17e0763ac1a260106b79859d2aad6d90
This reverts commit 16a815a3c2735f62238ef99af26090a2b8430d3d.
- Fix Chuncked string case sensitive issue - 16a815a3c2735f62238ef99af26090a2b8430d3d
23 Jul 2013 - 2.7.5
-------------------
Improvements:
* SecUnicodeCodePage is deprecated. SecUnicodeMapFile now accepts the code page as a second parameter.
* Updated Libinjection to version 3.4.1. Many improvements were made.
* Severity action now supports strings (emergency, alert, critical, error, warning, notice, info, debug).
Bug Fixes:
* Fixed utf8toUnicode tfn null byte conversion.
* Fixed NGINX crash when issue reload command.
* Fixed flush output buffer before inject modified hashed response body.
* Fixed url normalization for Hash Engine.
* Fixed NGINX ap_unixd_set_global_perms_mutex compilation error with apache 2.4 devel files.
Security Issues:
10 May 2013 - 2.7.4
-------------------
Improvements:
* Added Libinjection project http://www.client9.com/projects/libinjection/ as a new operator @detectSQLi. (Thanks Nick Galbreath).
* Added new variable SDBM_DELETE_ERROR that will be set to 1 when sdbm engine fails to delete entries.
* NGINX is now set to STABLE. Thanks chaizhenhua and all the people in community who help the project testing, sending feedback and patches.
Bug Fixes:
* Fixed SecRulePerfTime storing unnecessary rules performance times.
* Fixed Possible SDBM deadlock condition.
* Fixed Possible @rsub memory leak.
* Fixed REMOTE_ADDR content will receive the client ip address when mod_remoteip.c is present.
* Fixed NGINX Audit engine in Concurrent mode was overwriting existing alert files because a issue with UNIQUE_ID.
* Fixed CPU 100% issue in NGINX port. This is also related to an memory leak when loading response body.
Security Issues:
* Fixed Remote Null Pointer DeReference (CVE-2013-2765). When forceRequestBodyVariable action is triggered and a unknown Content-Type is used,
mod_security will crash trying to manipulate msr->msc_reqbody_chunks->elts however msr->msc_reqbody_chunks is NULL. (Thanks Younes JAAIDI).
28 Mar 2013 - 2.7.3
-------------------
* Fixed IIS version race condition when module is initialized.
* Fixed IIS version failing config commands in libapr.
* Nginx version is now RC quality. The rule engine should works for all phases.
We fixed many issues and missing features (for more information please check jira).
Code is running well with latest Nginx 1.2.7 stable.
Thanks chaizhenhua for your help.
* Added MULTIPART_NAME and MULTIPART_FILENAME. Should be used soon by CRS
and will help prevent attacks using multipart data.
* Added --enable-htaccess-config configure option. It will allow the follow directives
to be used into .htaccess files when AllowOverride Options is set:
- SecAction
- SecRule
- SecRuleRemoveByMsg
- SecRuleRemoveByTag
- SecRuleRemoveById
- SecRuleUpdateActionById
- SecRuleUpdateTargetById
- SecRuleUpdateTargetByTag
- SecRuleUpdateTargetByMsg
* Improvements in the ID duplicate code checking. Should be faster now.
* SECURITY: Added SecXmlExternalEntity (On|Off - default it Off) that will disable
by default the external entity load task executed by LibXml2. This is a security issue
[CVE-2013-1915] reported by Timur Yunusov, Alexey Osipov (Positive Technologies).
21 Jan 2013 - 2.7.2
-------------------
* IIS version is now stable.
* Fixed IIS version does not pass through POST data to ASP.NET when SecRequestBodyAccess
is set to On (MODSEC-372).
* Fixed IIS version HTTP Request Smuggling protection does not work (MODSEC-344).
* Fixed IIS version PHP Injection Attack (958976) protection does not work (MODSEC-346).
* Fixed IIS version Request limit protections are not working (MODSEC-349).
* Fixed IIS version Outbound protections are not working (MODSEC-350).
* Added IIS version better installer.
* NGINX version removed ModSecurityPassCommand (Thanks chaizhenhua).
* Fixed NGINX version ngx_http_read_client_request_body returned unexpected buffer type (Thanks chaizhenhua).
* Fixed NGINX version INCS config directories on fedora (Thanks chaizhenhua).
* Added NGINX version Added drop action for nginx (Thanks chaizhenhua).
* Fixed bug in cpf_verify operator (Thanks Hideaki Hayashi).
* Fixed build modsecurity under Arch Linux.
* Fixed make test crashing when JIT pcre is enabled.
* Fixed better cookie separator detection code.
* Fixed mod_security displaying wrong ip address in error.log using apache 2.4 and mod_remoteip.
* Fixed mod_security was not compiling when use apr without ipv6 support.
* Fixed mod_security was not compiling when use lua 5.2.
* Fixed issue when execute make install under Solaris.
* Fixed ipmatchf operator was not working as expected.
01 Nov 2012 - 2.7.1
-------------------
* Changed "Encryption" name of directives and options related to hmac feature to "Hash".
SecEncryptionEngine to SecHashEngine
SecEncryptionKey to SecHashKey
SecEncryptionParam to SecHashParam
SecEncryptionMethodRx to SecHashMethodRx
SecEncryptionMethodPm to SecHashMethodPm
@validateEncryption to @validateHash
ctl:EncryptionEnforcement to ctl:HashEnforcement
ctl:EncryptionEngine to ctl:HashEngine
* Added a better random bytes generator using apr_generate_random_bytes() to create
the HMAC key.
* Fixed byte conversion issue during logging under Linux s390x platform.
* Fixed compilation bug with LibXML2 2.9.0 (Thanks Athmane Madjoudj).
* Fixed parsing error with modsecurity-recommended.conf and Apache 2.4.
* Fixed DROP action was disabled for Apache 2 module by mistake.
* Fixed bug when use ctl:ruleRemoveTargetByTag.
* Fixed IIS and NGINX modules bugs.
* Fixed bug when @strmatch patterns use invalid escape sequence (Thanks Hideaki Hayashi).
* Fixed bugs in @verifySSN (Thanks Hideaki Hayashi).
* The doc/ directory now contains the instructions to access online documentation.
15 Oct 2012 - 2.7.0
-------------------
* Fixed Pause action should work as a disruptive action (MODSEC-297).
* Fixed Problem loading mod_env variables in phase 2 (MODSEC-226).
* Fixed Detect cookie v0 separator and use it for parsing (MODSEC-261).
* Fixed Variable REMOTE_ADDR with wrong IP address in NGINX version (MODSEC-337).
* Fixed Errors compiling NGINX version.
* Added Include directive into standalone module. IIS and NGINX module should
support Include directive like Apache2.
* Added MULTIPART_INVALID_PART flag. Also used in rule id 200002 for multipart strict
validation. https://www.sec-consult.com/fxdata/seccons/prod/temedia/advisories_txt/20121017-0_mod_security_ruleset_bypass.txt).
* Updated Reference Manual.
25 Sep 2012 - 2.6.8
-------------------
* Fixed ctl:ruleRemoveTargetByID order issue (MODSEC-333). Thanks to Armadillo Dasypodidae.
* Fixed variable HIGHEST_SEVERITY incorrectly gets reset in a chain rule (MODSEC-315). Thanks to Valery Reznic.
10 Sep 2012 - 2.7.0-rc3
-------------------
* Fixed requests bigger than SecRequestBodyNoFilesLimit were truncated even engine mode was detection only.
* Fixed double close() for multipart temporary files (Thanks Seema Deepak).
* Fixed many small issues reported by Coverity Scanner (Thanks Peter Vrabek).
* Fixed format string issue in ngnix experimental code. (Thanks Eldar Zaitov).
* Added ctl:ruleRemoveTargetByTag/Msg and removed ctl:ruleUpdateTargetByTag/Msg.
* Added IIS and Ngnix platform code.
* Added new transformation utf8toUnicode.
23 Jul 2012 - 2.6.7
-------------------
* Fixed explicit target replacement using SecUpdateTargetById was broken.
* The ctl:ruleUpdateTargetById is deprecated and will be removed for future versions since
there is no safe way to use it per-request.
* Added ctl:ruleRemoveTargetById that can be used to exclude targets to be processed per-request.
22 Jun 2012 - 2.7.0-rc2
-------------------
* Fixed compilation errors and warnings under Windows platform.
* Fixed SecEncryptionKey was not working as expected.
08 Jun 2012 - 2.7.0-rc1
-------------------
* Added SecEncryptionEngine. Initial crypt engine support, at the momment it will sign some Html
and Response Header options.
* Added SecEncryptionKey to define the a rand or static key for crypt engine.
* Added SecEncryptionParam to define the new parameter name.
* Added SecEncryptionMethodRx used with a regular expression to inspect the html in response
body/header and decide what to protect.
* Added SecEncryptionMethodPm used with multiple or single strings to inspect the html in response
body/header and decide what to protect.
* Added ctl encryptionEngine as a per transaction version of SecEncryptionEgine diretive.
* Added ctl encryptionEnforcement that will allow the engine to sign the data but the enforcement is
disabled.
* Added validateEncryption operator to enforce the signed elements.
* Added rsub operator supports the syntax |hex| allowing users to use special chars like \n \r.
* Added SecRuleUpdateTargetById now supports id range.
* Added SecRuleUpdateTargetByMsg and its ctl version (Thanks Scott Gifford).
* Added SecRuleUpdateTargetByTag and its ctl version (Thanks Scott Gifford).
* Added SecRulePerfTime when greater than zero it will fill rule id's execution time into PERF_RULE
and log id=usec information in the new Perf-rule-info: line in part H.
* Added PERF_RULES variable that contains rule execution time.
* Added Engine-mode: section in part H.
* Added ruleRemoveByMsg ctl version.
* Added removeCommentsChar and removeComments now can work with style.
* Added SecArgumentSeparator and SecCookieFormat can be used in different scope locations.
* Added Rules must have ID action and must be numeric.
* Added The use of tfns are deprecated in SecDefaultAction. Should be forbid in the future.
* Added Macro expansion support to the action pause.
* Added IpmatchFromFile/IpmatchF operator.
* Added New setrsc action, the RESOURCE collection used SecWebAppId Name Space
* Added Configure option --enable-cache-lua that allows reuse of Lua VM per transaction.
It will only take any effect when ModSecurity has multiple scripts to run per transaction.
* Added Configure option --enable-pcre-jit that allows ModSecurity regex engine to use PCRE Jit support.
* Added Configure option --enable-request-early that allows ModSecurity run phase 1 in post_read_request hook.
* Added RBL operator now support the httpBl api (http://www.projecthoneypot.org/httpbl_api.php).
* Added SecHttpBlKey to be used with httpBl api.
* Added SecSensorId will specify the modsecurity sensor name into audit log part H.
* Added aliases to phase:2 (phase:request), phase:4 (phase:response) and phase:5 (phase:logging).
* Added USERAGENT_IP variable. Created when Apache24 is used with mod_remoteip to know the real
client ip address.
^ Added new rule metadata actions ver, maturity and accuracy. Also included into RULE collection.
* Updated Reference manual into doc/ directory.
* Fixed Variable DURATION contains the elapsed time in microseconds for compatible reasons with apache and
other variables.
* Fixed Preserve names/identity of the variables going into MATCHED_VARS.
* Fixed Redirect macro expansion does not work in SecDefaultAction when SecRule uses block action.
* Fixed rsub operator does not work as expect if regex contains parentheses (Thanks Jerome Freilinger).
* Current Google Safe Browsing implementation is deprecated. Google changed the API and does not allow
anymore the malware database for download.
08 Jun 2012 - 2.6.6
-------------------
* Added build system support for KfreeBSD and HURD.
* Fixed a multipart bypass issue related to quote parsing
Credits to Qualys Vulnerability & Malware Research Labs (VMRL).
20 Mar 2012 - 2.6.5
-------------------
* Fixed increased a specific message debug level in SBDM code (MODSEC-293).
* Cleanup build system.
09 Mar 2012 - 2.6.4
-------------------
* Fixed Mlogc 100% CPU consume (Thanks Klaubert Herr and Ebrahim Khalilzadeh).
* Fixed ModSecurity cannot load session and user sdbm data.
* Fixed updateTargetById was creating rule unparsed content making apache memory grow.
* Code cleanup.
23 Feb 2012 - 2.6.4-rc1
-------------------
* Fixed @rsub adding garbage data into stream variables.
* Fixed regex for section A into mlogc-batch-load.pl (Thanks Ebrahim Khalilzadeh).
* Fixed logdata cuts message without closing it with final chars.
* Added sanitizeMatchedBytes support to verifyCPF, verifyCC and verifySSN.
06 Dec 2011 - 2.6.3-rc1
-------------------
* Fixed MATCHED_VARS does not correctly handle multiple VARS with the same name.
* Fixed SDBM garbage collection was not working as expected, increasing the size of files.
* Fixed wrong timestamp calculation for some time zones in log files.
* Fixed SecUpdateTargetById failed to load multiple VARS (MODSEC-270).
* Fixed Reverted hexDecode for hexEncode compatibility reason.
* Added SecCollectionTimeout to set collection timeout, default is 3600.
* Added sqlHexDecode transformation to decode sql hex data. Thanks Marc Stern.
30 Sep 2011 - 2.6.2
-------------------
* Fixed hexDecode test during make.
* Updated the reference manual into doc/ directory.
5 Sep 2011 - 2.6.2-rc1
-------------------
* Added support to macro expansion for rx operator.
* Added new transformations removeComments and removeCommentsChars
* Fixed colletion names are not case-sensitive anymore.
* Fixed compilation errors with apache 2.0.
* Fixed build system was not using some libraries CFLAGS.
* Fixed check for valid hex values into hexDecode transformation.
* Fixed ctl:ruleUpdateTargetById appending multiple targets.
18 Jun 2011 - 2.6.1
-------------------
* Updated the reference manual into doc/ directory.
11 Jul 2011 - trunk
-------------------
* Add HttpBl support to rbl operator.
30 Jun 2011 - 2.6.1-rc1
-------------------
* Fixed SecUploadFileMode doesn't work with the new build system.
* Fixed building with Lua library (Thanks Diego Elio).
* Fixed some ./configure --enable* features not being enabled in compilation time.
* Improvements on GSB database add/search operations.
* Log part K was removed from modsecurity.conf-recommended.
* Added SecUnicodeMapFile directive. Must be use to load the unicode.mapping file.
* Added SecUnicodeCodePage directive. Used to define the unicode code page. There are a few already available:
1250 (ANSI - Central Europe)
1251 (ANSI - Cyrillic)
1252 (ANSI - Latin I)
1253 (ANSI - Greek)
1254 (ANSI - Turkish)
1255 (ANSI - Hebrew)
1256 (ANSI - Arabic)
1257 (ANSI - Baltic)
1258 (ANSI/OEM - Viet Nam)
20127 (US-ASCII)
20261 (T.61)
20866 (Russian - KOI8)
28591 (ISO 8859-1 Latin I)
28592 (ISO 8859-2 Central Europe)
28605 (ISO 8859-15 Latin 9)
37 (IBM EBCDIC - U.S./Canada)
437 (OEM - United States)
500 (IBM EBCDIC - International)
850 (OEM - Multilingual Latin I)
860 (OEM - Portuguese)
861 (OEM - Icelandic)
863 (OEM - Canadian French)
865 (OEM - Nordic)
874 (ANSI/OEM - Thai)
932 (ANSI/OEM - Japanese Shift-JIS)
936 (ANSI/OEM - Simplified Chinese GBK)
949 (ANSI/OEM - Korean)
950 (ANSI/OEM - Traditional Chinese Big5)
Also mapping some extra unicode chars defined at http://tools.ietf.org/html/rfc3490#section-3.1
* Fixed SecRequestBodyLimit was truncating the real request body.
18 May 2011 - 2.6.0
-------------------
* Added SecWriteStateLimit for Slow Post DoS mitigation.
* Fix problem when buffering in input filter.
* Fix memory leak when use MATCHED_VAR_NAMES.
2 May 2011 - 2.6.0-rc2
-------------------
* Added code optimizations - thanks Diego Elio.
* Added support to AIX and HPUX in the build system (untested).
* Renamed decodeBase64Ext to base64DecodeExt.
* Build system improvements - thanks Diego Elio.
* Improvements on gsblookup parser.
* Fixed input filter bug when upload files and SecStreamInBodyInspect is enabled.
* Logging improvements and bug fix.
* Remove extra useless files when make clean and maintainer-clean
18 Apr 2011 - 2.6.0-rc1
-------------------
* Replaced previous GPLv2 License to Apachev2.
* Added Google Safe Browsing lookups operator and directive. It should be
used to extract and lookup urls from http packets.
* Added Data Modification operator. It must be used with STREAM_* variables
to replace/add/edit any data from http bodies.
* Added STREAM_OUPUT_BODY and STREAM_INPUT_BODY variables to work with data
modification operators.
* Added fast ip address operator. It supports partial ip address, cidr for
IPv4 and IPv6. Thanks Tom Donovan.
* Added new sensitive data tracking verifyCPF and verifySSN.
* Added MATCHED_VARS and MATCHED_VARS_NAMES. It is similiar to MATCHED_VAR,
but now we should see all matched variables.
* Added UNIQUE_ID variable. It holds the data created my mod_unique_id.
* Added new tranformation cmdline. Thanks Marc Stern.
* Added new exception handling operators and directives. It should help users
reduce FN and FPs. The directives SecRuleUpdateTargetById, SecRuleRemoveByTag
and its ctl actions were included.
* Added SecStreamOutBodyInspection and SecStreamInBodyInspection to enable STREAM_*
variables.
* Added SecGsbLookupDB used to load Google Safe Browsing malware databse into
memory.
* Added the directive SecInterceptOnError to control what to do if a rule returns
values less than zero.
* Improvements in DetectionOnly engine mode. Also added SecRequestBodyLimitAction
to control what to do if the engine receive a http request over a hard limit.
Note that there is now many combinations with SecRuleEngine and the limit action
directives for response and request data. Please see the reference manual.
* Improvements under RBL operator. It now will parse return code values for some
RBL lists.
* Added new Log Part J. It should log some informations about uploaded files.
* Added new sanitizeMatchedBytes action. It will give more flexibilty for user to sanitize
logged data, also improving peformance when sanitize big amount of data.
* Improvements on Logging phase. It is possible now see full chains, distinguish between
simple rules, chain starters and chain nodes.
* Improvements on AutoTools usage.
* Improvements on pattern matching operators, pmf, pm and strmatch now supports more flexible
input data allowing any kind of special char.
* Improvements on SecRuleUpdateActionById to update chain nodes.
* Many bugs were fixed. Please see the ModSecurity Jira for more details
19 Mar 2010 - trunk
-------------------
* Added SecDisableBackendCompression, which disabled backend compression
while keeping the frontend compression enabled (assuming mod_deflate
in installed and configured in the proxy). [Ivan Ristic]
* Added REQUEST_BODY_LENGTH, which contains the number of request body
bytes read. [Ivan Ristic]
* Integrate with mod_log_config using the %{VARNAME}M format string.
(MODSEC-108) [Ivan Ristic]
* Replaced the previous time-measuring mechanism with a new one, which
provides the following information: request time, request duration,
phase duration (for all 5 phases), time spent dealing with persistent
storage, and time spent on audit logging. The new information is now
available in the Stopwatch2 audit log header. The Stopwatch header
remains for backward compatiblity, although it now only includes
the request time and request duration values. Added the following
variables: PERF_COMBINED, PERF_PHASE1, PERF_PHASE2, PERF_PHASE3,
PERF_PHASE4, PERF_PHASE5, PERF_SREAD, PERF_SWRITE, PERF_LOGGING,
PERF_GC. [Ivan Ristic]
* Added DURATION, which contains the time ellapsed since the beginning
of the current transaction, in milliseconds. [Ivan Ristic]
* Adjusted phase 5 to execute just prior to mod_log_config. This should
allow phase 5 rules to to implement conditional logging, as well as
pave support for allowing access to all ModSecurity variables from
mog_log_config. [Ivan Ristic]
* Added the URLENCODED_ERROR flag, which is raised whenever invalid URL
encoding is encountered in the query string or in the request body
(but only if URLENCODED request body processor is used). (MODSEC-111)
[Ivan Ristic]
* Removed the obsolete PDF UXSS functionality. (MODSEC-96) [Ivan Ristic]
* Renamed normalisePath to normalizePath and normalisePathWin to
normalizePathWin. Kept the previous names for backward compatibility.
(MODSEC-103) [Ivan Ristic]
* Moved phase 1 to be run in the same Apache hook as phase 2. This means
that you can now have phase 1 rules in tags and, more
importantly, override server configuration in and others.
(MODSEC-98) [Ivan Ristic]
* Renamed the sanitise family of actions to sanitize. Kept the old variants
for backward compatibility. (MODSEC-95) [Ivan Ristic]
* Improve the logging of the ctl action. (MODSEC-99) [Ivan Ristic]
* Cleanup build files that were from the Apache source.
14 Feb 2010 - 2.5.13-dev1
-------------------------
* Cleaned up some mlogc code and debugging output.
* Remove the ability to use a relative path to a piped audit logger
(i.e. mlogc) as Apache does not support it in their piped loggers
and it was breaking Windows and probably other platforms that
use spaces in filesystem paths. Discovered by Tom Donovan.
* Fix memory leak freeing regex. Discovered by Tom Donovan.
* Fix some portability issues on Windows.
04 Feb 2010 - 2.5.12
--------------------
* Fixed SecUploadFileMode to set the correct mode.
* Fixed nolog,auditlog/noauditlog/nolog controls for disruptive actions.
* Added additional file info definitions introduced in APR 0.9.5 so that
build will work with older APRs (IBM HTTP Server v6).
* Added SecUploadFileLimit to limit the number of uploaded file parts that
will be processed in a multipart POST. The default is 100.
* Fixed path normalization to better handle backreferences that extend
above root directories. Reported by Sogeti/ESEC R&D.
* Trim whitespace around phrases used with @pmFromFile and allow
for both LF and CRLF terminated lines.
* Allow for more robust parsing for multipart header folding. Reported
by Sogeti/ESEC R&D.
* Fixed failure to match internally set TX variables with regex
(TX:/.../) syntax.
* Fixed failure to log full internal TX variable names and populate
MATCHED_VAR* vars.
* Enabled PCRE "studying" by default. This is now a configure-time option.
* Added PCRE match limits (SecPcreMatchLimit/SecPcreMatchLimitRecursion) to
aide in REDoS type attacks. A rule that goes over the limits will set
TX:MSC_PCRE_LIMITS_EXCEEDED. It is intended that the next major release
of ModSecurity (2.6.x) will move these flags to a dedicated collection.
* Reduced default PCRE match limits reducing impact of REDoS on poorly
written regex rules. Reported by Sogeti/ESEC R&D.
* Fixed memory leak in v1 cookie parser. Reported by Sogeti/ESEC R&D.
* Now support macro expansion in numeric operators (@eq, @ge, @lt, etc.)
* Update copyright to 2010.
* Reserved 700,000-799,999 IDs for Ivan Ristic.
* Fixed SecAction not working when CONNECT request method is used
(MODSEC-110). [Ivan Ristic]
* Do not escape quotes in macro resolution and only escape NUL in setenv
values.
04 Nov 2009 - 2.5.11
--------------------
* Added a new multipart flag, MULTIPART_INVALID_QUOTING, which will be
set true if any invalid quoting is found during multipart parsing.
* Fixed parsing quoted strings in multipart Content-Disposition headers.
Discovered by Stefan Esser.
* Cleanup persistence database locking code.
* Added warning during configure if libcurl is found linked against
gnutls for SSL. The openssl lib is recommended as gnutls has
proven to cause issues with mutexes and may crash.
* Cleanup some mlogc (over)logging.
* Do not log output filter errors in the error log.
* Moved output filter to run before other stock filters (mod_deflate,
mod_cache, mod_expires, mod_filter) to avoid analyzing modified data
in the response. Patch originally submitted by Ivan Ristic.
18 Sep 2009 - 2.5.10
--------------------
* Cleanup mlogc so that it builds on Windows.
* Added more detailed messages to replace "Unknown error" in filters.
* Added SecAuditLogDirMode and SecAuditLogFileMode to allow fine tuning
auditlog permissions (especially with mpm-itk).
* Cleanup SecUploadFileMode implementation.
* Cleanup build scripts.
* Fixed crash on configuration if SecMarker is used before any rules.
* Fixed SecRuleUpdateActionById so that it will work on chain starters.
* Cleanup build system for mlogc.
* Allow mlogc to periodically flush memory pools.
* Using nolog,auditlog will now log the "Message:" line to the auditlog, but
nothing to the error log. Prior versions dropped the "Message:" line from
both logs. To do this now, just use "nolog" or "nolog,noauditlog".
* Forced mlogc to use SSLv3 to avoid some potential auto negotiation
issues with some libcurl versions.
* Fixed mlogc issue seen on big endian machines where content type
could be listed as zero.
* Removed extra newline from audit log message line when logging XML errors.
This was causing problems parsing audit logs.
* Fixed @pm/@pmFromFile case insensitivity.
* Truncate long parameters in log message for "Match of ... against ...
required" messages.
* Correctly resolve chained rule actions in logs.
* Cleanup some code for portability.
* AIX does not support hidden visibility with xlc compiler.
* Allow specifying EXTRA_CFLAGS during configure to override gcc specific
values for non-gcc compilers.
* Populate GEO:COUNTRY_NAME and GEO:COUNTRY_CONTINENT as documented.
* Handle a newer geo database more gracefully, avoiding a potential crash for
new countries that ModSecurity is not yet aware.
* Allow checking &GEO "@eq 0" for a failed @geoLookup.
* Fixed mlogc global mutex locking issue and added more debugging output.
* Cleaned up build dependencies and configure options.
05 Mar 2009 - 2.5.9
-------------------
* Fixed parsing multipart content with a missing part header name which
would crash Apache. Discovered by "Internet Security Auditors"
(isecauditors.com).
* Added ability to specify the config script directly using --with-apr
and --with-apu.
* Updated copyright year to 2009.
* Added macro expansion for append/prepend action.
* Fixed race condition in concurrent updates of persistent counters. Updates
are now atomic.
* Cleaned up build, adding an option for verbose configure output and making
the mlogc build more portable.
21 Nov 2008 - 2.5.8
-------------------
* Fixed PDF XSS issue where a non-GET request for a PDF file would crash the
Apache httpd process. Discovered by Steve Grubb at Red Hat.
* Removed an invalid "Internal error: Issuing "%s" for unspecified error."
message that was logged when denying with nolog/noauditlog set and
causing the request to be audited.
24 Sep 2008 - 2.5.7
-------------------
* Fixed XML DTD/Schema validation which will now fail after request body
processing errors, even if the XML parser returns a document tree.
* Added ctl:forceRequestBodyVariable=on|off which, when enabled, will force
the REQUEST_BODY variable to be set when a request body processor is not set.
Previously the REQUEST_BODY target was only populated by the URLENCODED
request body processor.
* Integrated mlogc source.
* Fixed logging the hostname in the error_log which was logging the
request hostname instead of the Apache resolved hostname.
* Allow for disabling request body limit checks in phase:1.
* Added transformations for processing parity for legacy protocols ported
to HTTP(S): t:parityEven7bit, t:parityOdd7bit, t:parityZero7bit
* Added t:cssDecode transformation to decode CSS escapes.
* Now log XML parsing/validation warnings and errors to be in the debug log
at levels 3 and 4, respectivly.
31 Jul 2008 - 2.5.6
-------------------
* Transformation caching has been deprecated, and is now off by default. We
now advise against using transformation caching in production.
* Fixed two separate transformation caching issues that could cause incorrect
content inspection in some circumstances.
* Fixed an issue with the transformation cache using too much RAM, potentially
crashing Apache with a large number of cache entries. Two new configuration
options have been added to allow for a finer control of caching:
maxitems: Max number of items to cache (default 1024)
incremental: Whether to cache incrementally (default off)
* Added an experimental regression testing suite. The regression suite may
be executed via "make test-regression", however it is strongly advised
to only be executed on a non-production machine as it will startup the
Apache web server that ModSecurity is compiled against with various
configurations in which it will run tests.
* Added a licensing exception so that ModSecurity can be used in a derivative
work when that derivative is also under an approved open source license.
* Updated mlogc to version 1.4.5 which adds a LockFile directive and fixes an
issue in which the configuration file may be deleted.
05 Jun 2008 - 2.5.5
-------------------
* Fixed an issue where an alert was not logged in the error log
unless "auditlog" was used.
* Enable the "auditlog" action by default to help prevent a misconfiguration.
The new default is now: "phase:2,log,auditlog,pass"
* Improve request body processing error messages.
* Handle lack of a new line after the final boundary in a multipart request.
This fixes the reported WordPress Flash file uploader problem.
* Fixed issue with multithreaded servers where concurrent XML processing
could crash the web server (at least under Windows).
* Fixed blocking in phase 3.
* Force modules "mod_rpaf-2.0.c" and "mod_custom_header.c" to run before
ModSecurity so that the correct IP is used.
07 May 2008 - 2.5.4
-------------------
* Fixed issue where transformation cache was using the SecDefaultAction
value even when t:none was used within a rule.
24 Apr 2008 - 2.5.3
-------------------
* Fixed issue where the exec action may not be able to execute shell scripts.
* Macros are now expanded in expirevar and deprecatevar.
* Fixed crash if a persistent variable name was more than 126 characters.
* Updated included Core Ruleset to version 1.6.1 which fixes some
false negative issues in the migration to using some 2.5 features.
02 Apr 2008 - 2.5.2
-------------------
* Allow HTTP_* targets as an alias for REQUEST_HEADERS:*.
* Make sure temporary filehandles are closed after a transaction.
* Make sure the apache include directory is included during build.
02 Apr 2008 - 2.1.7
-------------------
* Make sure temporary filehandles are closed after a transaction.
14 Mar 2008 - 2.5.1
-------------------
* Fixed an issue where a match would not occur if transformation caching
was enabled.
* Using "severity" in a default action is now just a warning.
* Cleaned up the "make test" target to better locate headers/libraries.
* Now search /usr/lib64 and /usr/lib32 for lua libs.
* No longer treat warnings as errors by default (use --enable-strict-compile).
19 Feb 2008 - 2.5.0
-------------------
* Updated included Core Ruleset to version 1.6.0 which uses 2.5 features.
* Cleaned up and clarified some documentation.
* Updated code to be more portable so it builds with MS VC++.
* Added unit tests for most operators and transformations.
* Fixed crash on startup when ENV is improperly used without a parameter.
* Allow macro resolution in setenv action.
* The default action is now a minimal "phase:2,log,pass" with no default
transformations performed.
* Implemented SecUploadFileMode to allow setting the mode for uploaded files.
* Implemented "block" action.
* Implemented SecRuleUpdateActionById.
* Fixed removal of phase 5 rules via SecRuleRemoveBy* directives.
* No longer log the query portion of the URI in the error log as
it may contain sensitive data.
* Build is now 'configure' based: ./configure && make && make install
* Added support for Lua scripting in the following ways: SecRuleScript
can be used to specify a script to execute as a rule, the exec
action processes Lua scripts internally, as does the @inspectFile
operator. Refer to the documentation for more details.
* Changed how allow works. Used on its own it now allows phases 1-4. Used
with parameter "phase" (e.g. SecAction allow:phase) it only affects
the current phase. Used with parameter "request" it allows phases
1-2.
* Fixed issue where only the first phase 5 rule would run when the
request was intercepted in an earlier phase.
* Stricter configuration parsing. Disruptive actions, meta actions and
phases are no longer allowed in a chained rule. Disruptive actions,
are no longer allowed in a logging phase (phase 5) rule, including
inheriting from SecDefaultAction.
* More efficient collection persistance.
* Fixed t:escapeSeqDecode to better follow ANSI C escapes.
* Added t:jsDecode to decode JavScript escape sequences.
* Added IS_NEW built-in collection variables.
* New audit log part 'K' logs all matching rules.
* Implemented SecRequestBodyNoFilesLimit.
* Enhance handling of the case where we run out of disk space while
writing to audit log entry.
* Added SecComponentSignature to allow other components the ability
to append to the logged signature.
* Added skipAfter: action to allow skipping all rules until a rule
with a specified ID is reached. Rule execution then continues after
the specified rule.
* Added SecMarker directive to allow a fixed target for skipAfter.
* Added ctl:ruleRemoveById action to allow rule removal on a match.
* Added a @containsWord operator that will match a given string anywhere in
the target value, but only on word boundaries.
* Added a MATCHED_VAR_NAME variable to store the last matched variable name
so that it can be more easily used by rules.
* Added a MATCHED_VAR variable to store the last matched variable value
so that it can be more easily used by rules.
* Fixed expansion of macros when using relative changes with setvar. In
addition, added support for expanding macros in the variable name.
* Situations where ModSecurity will intercept, generate an error or log
a level 1-3 message to the debug log are now marked as 'relevant' and may
generate an audit log entry.
* Fixed deprecatevar:var=N/S action so that it decrements N every S seconds
as documented instead of decrementing by a rate.
* Enable ModSecurity to look at partial response bodies. In previous
versions, ModSecurity would respond with status code 500 when the
response body was too long. Now, if SecResponseBodyLimitAction is
set to "ProcessPartial", it will process the part of the response
body received up until that point but send the rest without buffering.
* ModSecurity will now process phases 3 and 4 even when request processing
is interrupted (either by Apache - e.g. by responding with 400, 401
or 403, or by ModSecurity itself).
* Fixed the base64decode transformation function to not return extra
characters at the end.
* Return from the output filter with an error in addition to setting
up the HTTP error status in the output data.
* Used new Apache API calls to get the server version/banner when available.
* Added "logdata" meta action to allow logging of raw transaction data.
* Added TX_SEVERITY that keeps track of the highest severity
for any matched rules so far.
* Added ARGS_GET, ARGS_POST, ARGS_GET_NAMES, ARGS_POST_NAMES variables to
allow seperation of GET and POST arguments.
* Added an Apache define (MODSEC_2.5) so that you can conditionally include
directives based on the ModSecurity major/minor versions with IfDefine.
* Added MODSEC_BUILD variable that contains the numeric build value based
on the ModSecurity version.
* Enhanced debug logging by displaying more data on rule execution. All
invoked rules are now logged in the debug log at level 5.
* Stricter validation for @validateUtf8Encoding.
* No longer process Apache internal subrequests.
* Fixed warnings on Solaris and/or 64bit builds.
* Added @within string comparison operator with support for macro expansion.
* Do not trigger "pause" action for internal requests.
* Added matching rule filename and line number to audit log.
* Added new phrase matching operators, @pm and @pmFromFile. These use
an alternate set based matching engine (Aho-Corasick) to perform faster
phrase type matches such as black/white lists, spam keywords, etc.
* Allow caching transformations per-request/phase so they are not repeated.
* Added Solaris and Cygwin to the list of platforms not supporting the hidden
visibility attribute.
* Fixed decoding full-width unicode in t:urlDecodeUni.
* Add SecGeoLookupDB, @geoLookups and GEO collection to support
geographical lookups by IP/host.
* Do not try to intercept a request after a failed rule. This fixes the
issue associated with an "Internal Error: Asked to intercept request
but was_intercepted is zero" error message.
* Removed extraneous exported symbols.
* Merged the PDF XSS protection functionality into ModSecurity.
* Exported API for registering custom variables. Example in api directory.
* Added experimental support for content injection. Directive
SecContentInjection (On|Off) controls whether injection is taking place.
Actions "prepend" and "append" inject content when executed. Do note that
it is your responsibility to make sure the response is of the appropriate
content type (e.g. HTML, plain text, etc).
* Added string comparison operators with support for macro expansion:
@contains, @streq, @beginsWith and @endsWith.
* Enhanced debug log output to log macro expansion, quote values and
correctly display values that contained NULs.
* Removed support for %0 - %9 capture macros as they were incorrectly
expanding url encoded values. Use %{TX.0} - %{TX.9} instead.
* Added t:length to transform a value to its character length.
* Added t:trimLeft, t:trimRight, t:trim to remove whitespace
from a value on the left, right or both.
* Added SecAuditLog2 directive to allow redundent concurrent audit log
index files. This will allow sending audit data to two consoles, etc.
* Removed CGI style HTTP_* variables in favor of REQUEST_HEADERS:Header-Name.
* Store filename/line for each rule and display it and the ID (if available)
in the debug log when invoking a rule. Thanks to Christian Bockermann
for the idea.
* Do not log 'allow' action as intercepted in the debug log.
* Fixed some collection variable names not printing with the parameter
and/or counting operator in the debug log.
19 Feb 2008 - 2.1.6
-------------------
* Fixed crash on startup when ENV is improperly used without a parameter.
* Allow macro resolution in setenv action.
* Implemented SecUploadFileMode to allow setting the mode for uploaded files.
* No longer log the query portion of the URI in the error log as
it may contain sensitive data.
10 Jan 2008 - 2.1.5
-------------------
* Updated included Core Ruleset to version 1.5.1.
* Phase 5 rules can now be removed via SecRuleRemoveBy* directives.
* Fixed issue where only the first phase 5 rule would run when the
request was intercepted in an earlier phase.
* Fixed configuration parsing so that disruptive actions, meta actions
and phases are not allowed in a chained rule (as originally intended).
* Fixed t:escapeSeqDecode to better follow ANSI C escapes.
27 Nov 2007 - 2.1.4
-------------------
* Updated included Core Ruleset to version 1.5 and noted in the docs that
XML support is required to use the rules without modification.
* Fixed an evasion FP, mistaking a multipart non-boundary for a boundary.
* Fixed multiple warnings on Solaris and/or 64bit builds.
* Do not process subrequests in phase 2-4, but do hand off the request data.
* Fixed a blocking FP in the multipart parser, which affected Safari.
11 Sep 2007 - 2.1.3
-------------------
* Updated multipart parsing code adding variables to allow checking
for various parsing issues (request body abnormalities).
* Allow mod_rpaf and mod_extract_forwarded2 to work before ModSecurity.
* Quiet some compiler warnings.
* Do not block internal ErrorDocument requests after blocking request.
* Added ability to compile without an external API (use -DNO_MODSEC_API).
27 Jul 2007 - 2.1.2
-------------------
* Cleaned up and clarified some documentation.
* Update included core rules to latest version (1.4.3).
* Enhanced ability to alert/audit failed requests.
* Do not trigger "pause" action for internal requests.
* Fixed issue with requests that use internal requests. These had the
potential to be intercepted incorrectly when other Apache httpd modules
that used internal requests were used with mod_security.
* Added Solaris and Cygwin to the list of platforms not supporting the hidden
visibility attribute.
* Fixed decoding full-width unicode in t:urlDecodeUni.
* Lessen some overhead of debugging messages and calculations.
* Do not try to intercept a request after a failed rule. This fixes the
issue associated with an "Internal Error: Asked to intercept request
but was_intercepted is zero" error message.
* Added SecAuditLog2 directive to allow redundent concurrent audit log
index files. This will allow sending audit data to two consoles, etc.
* Small performance improvement in memory management for rule execution.
11 Apr 2007 - 2.1.1
-------------------
* Add the PCRE_DOLLAR_ENDONLY option when compiling regular expression
for the @rx operator and variables.
* Really set PCRE_DOTALL option when compiling the regular expression
for the @rx operator as the docs state.
* Fixed potential memory corruption when expanding macros.
* Fixed error when a collection was retrieved from storage in the same second
as creation by setting the rate to zero.
* Fixed ASCIIZ (NUL) parsing for application/x-www-form-urlencoded forms.
* Fixed the faulty REQUEST_FILENAME variable, which used to change
the internal Apache structures by mistake.
* Updates to quiet some compiler warnings.
* Fixed some casting issues for compiling on NetWare (patch from Guenter Knauf).
23 Feb 2007 - 2.1.0
-------------------
* Removed the "Connection reset by peer" message, which has nothing
to do with us. Actually the message was downgraded from ERROR to
NOTICE so it will still appear in the debug log.
* Removed the (harmless) message mentioning LAST_UPDATE_TIME missing.
* It was not possible to remove a rule placed in phase 4 using
SecRuleRemoveById or SecRuleRemoveByMsg. Fixed.
* Fixed a problem with incorrectly setting requestBodyProcessor using
the ctl action.
* Bundled Core Rules 2.1-1.3.2b4.
* Updates to the reference manual.
* Reversed the return values of @validateDTD and @validateSchema, to
make them consistent with other operators.
* Added a few helpful debug messages in the XML validation area.
* Updates to the reference manual.
* Fixed the validateByteRange operator.
* Default value for the status action is now 403 (as it was supposed to
be but it was effectively 500).
* Rule exceptions (removing using an ID range or an regular expression)
is now applied to the current context too. (Previously it only worked
on rules that are inherited from the parent context.)
* Fix of a bug with expired variables.
* Fixed regular expression variable selectors for many collections.
* Performance improvements - up to two times for real-life work loads!
* Memory consumption improvements (not measured but significant).
* The allow action did not work in phases 3 and 4. Fixed.
* Unlocked collections GLOBAL and RESOURCE.
* Added support for variable expansion in the msg action.
* New feature: It is now possible to make relative changes to the
audit log parts with the ctl action. For example: "ctl:auditLogParts=+E".
* New feature: "tag" action. To be used for event categorisation.
* XML parser was not reporting errors that occured at the end
of XML payload.
* Files were not extracted from request if SecUploadKeepFiles was
Off. Fixed.
* Regular expressions that are too long are truncated to 256
characters before used in error messages. (In order to keep
the error messages in the log at a reasonable size.)
* Fixed the sha1 transformation function.
* Fixed the skip action.
* Fixed REQUEST_PROTOCOL, REMOTE_USER, and AUTH_TYPE.
* SecRuleEngine did not work in child configuration contexts
(e.g. ).
* Fixed base64Decode and base64Encode.
15 Nov 2006 - 2.0.4
-------------------
* Fixed the "deprecatevar" action.
* Decreasing variable values did not work.
* Made "nolog" do what it is supposed to do - cause a rule match to
not be logged. Also "nolog" now implies "noauditlog" but it's
possible to follow "nolog" with "auditlog" and have the match
not logged to the error log but logged to the auditlog. (Not
something that strikes me as useful but it's possible.)
* Relative paths given to SecDataDir will now be treated as relative
to the Apache server root.
* Added checks to make sure only correct actions are specified in
SecDefaultAction (some actions are required, some don't make any
sense) and in rules that are not chain starters (same). This should
make the unhelpful "Internal Error: Failed to add rule to the ruleset"
message go away.
* Fixed the problem when "SecRuleInheritance Off" is used in a context
with no rules defined.
* Fixed a problem of lost input (request body) data on some redirections,
for example when mod_rewrite is used.
26 Oct 2006 - 2.0.3
-------------------
* Fixed a memory leak (all platforms) and a concurrency control
problem that could cause a crash (multithreaded platforms only).
* Fixed a SecAuditLogRelevantStatus problem, which would not work
properly unless the regular expression contained a subexpression.
19 Oct 2006 - 2.0.2
-------------------
* Fixed incorrect permissions on the global mutex, which prevented
the mutex from working properly.
* Fixed incorrect actionset merging where the status was copied from
the child actionset even though it was not defined.
* Fixed missing metadata information (in the logs) for warnings.
16 Oct 2006 - 2.0.1
-------------------
* Rules that used operator negation did not work. Fixed.
* Fixed bug that prevented invalid regular expressions from being reported.
16 Oct 2006 - 2.0.0
-------------------
* First stable 2.x release.
modsecurity-apache-2.9.13/LICENSE 0000664 0000000 0000000 00000025004 15174655764 0016426 0 ustar 00root root 0000000 0000000 Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2016 ModSecurity
Licensed under the Apache License, Version 2.0 (the "License");
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
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
modsecurity-apache-2.9.13/Makefile.am 0000775 0000000 0000000 00000003245 15174655764 0017463 0 ustar 00root root 0000000 0000000 ACLOCAL_AMFLAGS = -I build
SUBDIRS = @TOPLEVEL_SUBDIRS@ tests
CLEANFILES =
MAINTAINERCLEANFILES =
CLEANFILES += tests/regression/server_root/conf/*.t_*.conf \
tests/regression/server_root/logs/*.log
MAINTAINERCLEANFILES += $(CLEANFILES) \
aclocal.m4 \
alp2/Makefile.in \
apache2/Makefile.in \
build/config.guess \
build/config.sub \
build/depcomp \
build/libtool.m4 \
build/ltmain.sh \
build/lt~obsolete.m4 \
build/ltoptions.m4 \
build/ltsugar.m4 \
build/ltversion.m4 \
build/missing \
config.log \
config.status \
configure \
ext/Makefile.in \
Makefile \
Makefile.in \
mlogc/Makefile.in \
modsecurity_config_auto.h.in~
test: check
test-regression:
(cd tests && $(MAKE) test-regression)
cppcheck:
@cppcheck \
-j `getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu || echo 1` \
--enable=all \
--force \
--verbose \
--library=gnu \
--library=posix \
--std=c++17 \
-I ./apache2 \
-I /usr/include/libxml2 \
-I @APXS_INCLUDEDIR@ \
-I @APR_INCLUDEDIR@ \
-I @APU_INCLUDEDIR@ \
--suppressions-list=./tests/cppcheck_suppressions.txt \
--suppress=syntaxError:@APR_INCLUDEDIR@/* \
--inline-suppr \
--inconclusive \
--template="warning: {file},{line},{severity},{id},{message}" \
--error-exitcode=1 \
standalone/
check-static: cppcheck
check-coding-style:
for i in `(find . -iname "*.c" ; find . -iname "*.h")`; \
do echo $$i...; \
vera++ -rule L004 -param max-line-length=80 $$i 2>&1 | sed 's/^/warning: /g' 1>&2; \
vera++ -rule L001 $$i 2>&1 | sed 's/^/warning: /g' 1>&2; \
done;
.PHONY: test
modsecurity-apache-2.9.13/Makefile.in 0000664 0000000 0000000 00000074560 15174655764 0017501 0 ustar 00root root 0000000 0000000 # Makefile.in generated by automake 1.18.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2025 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__is_gnu_make = { \
if test -z '$(MAKELEVEL)'; then \
false; \
elif test -n '$(MAKE_HOST)'; then \
true; \
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
true; \
else \
false; \
fi; \
}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
am__rm_f = rm -f $(am__rm_f_notfound)
am__rm_rf = rm -rf $(am__rm_f_notfound)
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
subdir = .
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/build/find_apr.m4 \
$(top_srcdir)/build/find_apu.m4 \
$(top_srcdir)/build/find_curl.m4 \
$(top_srcdir)/build/find_lua.m4 \
$(top_srcdir)/build/find_pcre.m4 \
$(top_srcdir)/build/find_pcre2.m4 \
$(top_srcdir)/build/find_ssdeep.m4 \
$(top_srcdir)/build/find_xml.m4 \
$(top_srcdir)/build/find_yajl.m4 \
$(top_srcdir)/build/libtool.m4 \
$(top_srcdir)/build/ltoptions.m4 \
$(top_srcdir)/build/ltsugar.m4 \
$(top_srcdir)/build/ltversion.m4 \
$(top_srcdir)/build/lt~obsolete.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
$(am__configure_deps) $(am__DIST_COMMON)
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
configure.lineno config.status.lineno
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/apache2/modsecurity_config_auto.h
CONFIG_CLEAN_FILES = build/apxs-wrapper \
tests/regression/server_root/conf/httpd.conf
CONFIG_CLEAN_VPATH_FILES =
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
SOURCES =
DIST_SOURCES =
RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
ctags-recursive dvi-recursive html-recursive info-recursive \
install-data-recursive install-dvi-recursive \
install-exec-recursive install-html-recursive \
install-info-recursive install-pdf-recursive \
install-ps-recursive install-recursive installcheck-recursive \
installdirs-recursive pdf-recursive ps-recursive \
tags-recursive uninstall-recursive
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
distclean-recursive maintainer-clean-recursive
am__recursive_targets = \
$(RECURSIVE_TARGETS) \
$(RECURSIVE_CLEAN_TARGETS) \
$(am__extra_recursive_targets)
AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
cscope distdir distdir-am dist dist-all distcheck
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
DIST_SUBDIRS = $(SUBDIRS)
am__DIST_COMMON = $(srcdir)/Makefile.in \
$(top_srcdir)/build/apxs-wrapper.in $(top_srcdir)/build/ar-lib \
$(top_srcdir)/build/compile $(top_srcdir)/build/config.guess \
$(top_srcdir)/build/config.sub $(top_srcdir)/build/install-sh \
$(top_srcdir)/build/ltmain.sh $(top_srcdir)/build/missing \
$(top_srcdir)/tests/regression/server_root/conf/httpd.conf.in \
README.md build/ar-lib build/compile build/config.guess \
build/config.sub build/depcomp build/install-sh \
build/ltmain.sh build/missing
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
distdir = $(PACKAGE)-$(VERSION)
top_distdir = $(distdir)
am__remove_distdir = \
if test -d "$(distdir)"; then \
find "$(distdir)" -type d ! -perm -700 -exec chmod u+rwx {} ';' \
; rm -rf "$(distdir)" \
|| { sleep 5 && rm -rf "$(distdir)"; }; \
else :; fi
am__post_remove_distdir = $(am__remove_distdir)
am__relativize = \
dir0=`pwd`; \
sed_first='s,^\([^/]*\)/.*$$,\1,'; \
sed_rest='s,^[^/]*/*,,'; \
sed_last='s,^.*/\([^/]*\)$$,\1,'; \
sed_butlast='s,/*[^/]*$$,,'; \
while test -n "$$dir1"; do \
first=`echo "$$dir1" | sed -e "$$sed_first"`; \
if test "$$first" != "."; then \
if test "$$first" = ".."; then \
dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
else \
first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
if test "$$first2" = "$$first"; then \
dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
else \
dir2="../$$dir2"; \
fi; \
dir0="$$dir0"/"$$first"; \
fi; \
fi; \
dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
done; \
reldir="$$dir2"
DIST_ARCHIVES = $(distdir).tar.gz
GZIP_ENV = -9
DIST_TARGETS = dist-gzip
# Exists only to be overridden by the user if desired.
AM_DISTCHECK_DVI_TARGET = dvi
distuninstallcheck_listfiles = find . -type f -print
am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
| sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
distcleancheck_listfiles = \
find . \( -type f -a \! \
\( -name .nfs* -o -name .smb* -o -name .__afs* \) \) -print
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
APR_CFLAGS = @APR_CFLAGS@
APR_CONFIG = @APR_CONFIG@
APR_CPPFLAGS = @APR_CPPFLAGS@
APR_INCLUDEDIR = @APR_INCLUDEDIR@
APR_LDADD = @APR_LDADD@
APR_LDFLAGS = @APR_LDFLAGS@
APR_LINKLD = @APR_LINKLD@
APR_VERSION = @APR_VERSION@
APU_CFLAGS = @APU_CFLAGS@
APU_CONFIG = @APU_CONFIG@
APU_INCLUDEDIR = @APU_INCLUDEDIR@
APU_LDADD = @APU_LDADD@
APU_LDFLAGS = @APU_LDFLAGS@
APU_LINKLD = @APU_LINKLD@
APU_VERSION = @APU_VERSION@
APXS = @APXS@
APXS_BINDIR = @APXS_BINDIR@
APXS_CC = @APXS_CC@
APXS_CFLAGS = @APXS_CFLAGS@
APXS_EXTRA_CFLAGS = @APXS_EXTRA_CFLAGS@
APXS_HTTPD = @APXS_HTTPD@
APXS_INCLUDEDIR = @APXS_INCLUDEDIR@
APXS_INCLUDES = @APXS_INCLUDES@
APXS_LDFLAGS = @APXS_LDFLAGS@
APXS_LIBDIR = @APXS_LIBDIR@
APXS_LIBEXECDIR = @APXS_LIBEXECDIR@
APXS_LIBS = @APXS_LIBS@
APXS_LIBTOOL = @APXS_LIBTOOL@
APXS_MODULES = @APXS_MODULES@
APXS_PROGNAME = @APXS_PROGNAME@
APXS_SBINDIR = @APXS_SBINDIR@
APXS_WRAPPER = @APXS_WRAPPER@
AR = @AR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CSCOPE = @CSCOPE@
CTAGS = @CTAGS@
CURL_CFLAGS = @CURL_CFLAGS@
CURL_CONFIG = @CURL_CONFIG@
CURL_CPPFLAGS = @CURL_CPPFLAGS@
CURL_LDADD = @CURL_LDADD@
CURL_LDFLAGS = @CURL_LDFLAGS@
CURL_USES_GNUTLS = @CURL_USES_GNUTLS@
CURL_VERSION = @CURL_VERSION@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DOXYGEN = @DOXYGEN@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
ENV_CMD = @ENV_CMD@
ETAGS = @ETAGS@
EXEEXT = @EXEEXT@
EXTRA_CFLAGS = @EXTRA_CFLAGS@
FGREP = @FGREP@
FILECMD = @FILECMD@
GREP = @GREP@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
LIBXML2_CONFIG = @LIBXML2_CONFIG@
LIBXML2_CPPFLAGS = @LIBXML2_CPPFLAGS@
LIBXML2_LDADD = @LIBXML2_LDADD@
LIBXML2_LDFLAGS = @LIBXML2_LDFLAGS@
LIBXML2_VERSION = @LIBXML2_VERSION@
LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
LUA_CFLAGS = @LUA_CFLAGS@
LUA_LDADD = @LUA_LDADD@
LUA_LDFLAGS = @LUA_LDFLAGS@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
MODSEC_APXS_EXTRA_CFLAGS = @MODSEC_APXS_EXTRA_CFLAGS@
MODSEC_EXTRA_CFLAGS = @MODSEC_EXTRA_CFLAGS@
MSC_BASE_DIR = @MSC_BASE_DIR@
MSC_PKGBASE_DIR = @MSC_PKGBASE_DIR@
MSC_REGRESSION_CONF_DIR = @MSC_REGRESSION_CONF_DIR@
MSC_REGRESSION_DIR = @MSC_REGRESSION_DIR@
MSC_REGRESSION_DOCROOT_DIR = @MSC_REGRESSION_DOCROOT_DIR@
MSC_REGRESSION_LOGS_DIR = @MSC_REGRESSION_LOGS_DIR@
MSC_REGRESSION_SERVERROOT_DIR = @MSC_REGRESSION_SERVERROOT_DIR@
MSC_TEST_DIR = @MSC_TEST_DIR@
NM = @NM@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PCRE2_CFLAGS = @PCRE2_CFLAGS@
PCRE2_CONFIG = @PCRE2_CONFIG@
PCRE2_CPPFLAGS = @PCRE2_CPPFLAGS@
PCRE2_LDADD = @PCRE2_LDADD@
PCRE2_LDFLAGS = @PCRE2_LDFLAGS@
PCRE2_VERSION = @PCRE2_VERSION@
PCRE_CFLAGS = @PCRE_CFLAGS@
PCRE_CONFIG = @PCRE_CONFIG@
PCRE_CPPFLAGS = @PCRE_CPPFLAGS@
PCRE_LDADD = @PCRE_LDADD@
PCRE_LDFLAGS = @PCRE_LDFLAGS@
PCRE_LD_PATH = @PCRE_LD_PATH@
PCRE_VERSION = @PCRE_VERSION@
PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
RANLIB = @RANLIB@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
SSDEEP_CFLAGS = @SSDEEP_CFLAGS@
SSDEEP_LDADD = @SSDEEP_LDADD@
SSDEEP_LDFLAGS = @SSDEEP_LDFLAGS@
STRIP = @STRIP@
TOPLEVEL_SUBDIRS = @TOPLEVEL_SUBDIRS@
VERSION = @VERSION@
YAJL_CFLAGS = @YAJL_CFLAGS@
YAJL_LDADD = @YAJL_LDADD@
YAJL_LDFLAGS = @YAJL_LDFLAGS@
YAJL_LIBS = @YAJL_LIBS@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__rm_f_notfound = @am__rm_f_notfound@
am__tar = @am__tar@
am__untar = @am__untar@
am__xargs_n = @am__xargs_n@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
runstatedir = @runstatedir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
ACLOCAL_AMFLAGS = -I build
SUBDIRS = @TOPLEVEL_SUBDIRS@ tests
CLEANFILES = tests/regression/server_root/conf/*.t_*.conf \
tests/regression/server_root/logs/*.log
MAINTAINERCLEANFILES = $(CLEANFILES) aclocal.m4 alp2/Makefile.in \
apache2/Makefile.in build/config.guess build/config.sub \
build/depcomp build/libtool.m4 build/ltmain.sh \
build/lt~obsolete.m4 build/ltoptions.m4 build/ltsugar.m4 \
build/ltversion.m4 build/missing config.log config.status \
configure ext/Makefile.in Makefile Makefile.in \
mlogc/Makefile.in modsecurity_config_auto.h.in~
all: all-recursive
.SUFFIXES:
am--refresh: Makefile
@:
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \
$(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \
&& exit 0; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
echo ' $(SHELL) ./config.status'; \
$(SHELL) ./config.status;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \
cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
$(SHELL) ./config.status --recheck
$(top_srcdir)/configure: $(am__configure_deps)
$(am__cd) $(srcdir) && $(AUTOCONF)
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
$(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
$(am__aclocal_m4_deps):
build/apxs-wrapper: $(top_builddir)/config.status $(top_srcdir)/build/apxs-wrapper.in
cd $(top_builddir) && $(SHELL) ./config.status $@
tests/regression/server_root/conf/httpd.conf: $(top_builddir)/config.status $(top_srcdir)/tests/regression/server_root/conf/httpd.conf.in
cd $(top_builddir) && $(SHELL) ./config.status $@
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
distclean-libtool:
-rm -f libtool config.lt
# This directory's subdirectories are mostly independent; you can cd
# into them and run 'make' without going through this Makefile.
# To change the values of 'make' variables: instead of editing Makefiles,
# (1) if the variable is set in 'config.status', edit 'config.status'
# (which will cause the Makefiles to be regenerated when you run 'make');
# (2) otherwise, pass the desired values on the 'make' command line.
$(am__recursive_targets):
@fail=; \
if $(am__make_keepgoing); then \
failcom='fail=yes'; \
else \
failcom='exit 1'; \
fi; \
dot_seen=no; \
target=`echo $@ | sed s/-recursive//`; \
case "$@" in \
distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
*) list='$(SUBDIRS)' ;; \
esac; \
for subdir in $$list; do \
echo "Making $$target in $$subdir"; \
if test "$$subdir" = "."; then \
dot_seen=yes; \
local_target="$$target-am"; \
else \
local_target="$$target"; \
fi; \
($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
|| eval $$failcom; \
done; \
if test "$$dot_seen" = "no"; then \
$(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
fi; test -z "$$fail"
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-recursive
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
include_option=--etags-include; \
empty_fix=.; \
else \
include_option=--include; \
empty_fix=; \
fi; \
list='$(SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
test ! -f $$subdir/TAGS || \
set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
fi; \
done; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-recursive
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscope: cscope.files
test ! -s cscope.files \
|| $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS)
clean-cscope:
-rm -f cscope.files
cscope.files: clean-cscope cscopelist
cscopelist: cscopelist-recursive
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-rm -f cscope.out cscope.in.out cscope.po.out cscope.files
distdir: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) distdir-am
distdir-am: $(DISTFILES)
$(am__remove_distdir)
$(AM_V_at)$(MKDIR_P) "$(distdir)"
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
$(am__make_dryrun) \
|| test -d "$(distdir)/$$subdir" \
|| $(MKDIR_P) "$(distdir)/$$subdir" \
|| exit 1; \
dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
$(am__relativize); \
new_distdir=$$reldir; \
dir1=$$subdir; dir2="$(top_distdir)"; \
$(am__relativize); \
new_top_distdir=$$reldir; \
echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
($(am__cd) $$subdir && \
$(MAKE) $(AM_MAKEFLAGS) \
top_distdir="$$new_top_distdir" \
distdir="$$new_distdir" \
am__remove_distdir=: \
am__skip_length_check=: \
am__skip_mode_fix=: \
distdir) \
|| exit 1; \
fi; \
done
-test -n "$(am__skip_mode_fix)" \
|| find "$(distdir)" -type d ! -perm -755 \
-exec chmod u+rwx,go+rx {} \; -o \
! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
! -type d ! -perm -400 -exec chmod a+r {} \; -o \
! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
|| chmod -R a+r "$(distdir)"
dist-gzip: distdir
tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz
$(am__post_remove_distdir)
dist-bzip2: distdir
tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2
$(am__post_remove_distdir)
dist-bzip3: distdir
tardir=$(distdir) && $(am__tar) | bzip3 -c >$(distdir).tar.bz3
$(am__post_remove_distdir)
dist-lzip: distdir
tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz
$(am__post_remove_distdir)
dist-xz: distdir
tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz
$(am__post_remove_distdir)
dist-zstd: distdir
tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst
$(am__post_remove_distdir)
dist-tarZ: distdir
@echo WARNING: "Support for distribution archives compressed with" \
"legacy program 'compress' is deprecated." >&2
@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
$(am__post_remove_distdir)
dist-shar: distdir
@echo WARNING: "Support for shar distribution archives is" \
"deprecated." >&2
@echo WARNING: "It will be removed altogether in Automake 2.0" >&2
shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz
$(am__post_remove_distdir)
dist-zip: distdir
-rm -f $(distdir).zip
zip -rq $(distdir).zip $(distdir)
$(am__post_remove_distdir)
dist dist-all:
$(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:'
$(am__post_remove_distdir)
# This target untars the dist file and tries a VPATH configuration. Then
# it guarantees that the distribution is self-contained by making another
# tarfile.
distcheck: dist
case '$(DIST_ARCHIVES)' in \
*.tar.gz*) \
eval GZIP= gzip -dc $(distdir).tar.gz | $(am__untar) ;;\
*.tar.bz2*) \
bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
*.tar.bz3*) \
bzip3 -dc $(distdir).tar.bz3 | $(am__untar) ;;\
*.tar.lz*) \
lzip -dc $(distdir).tar.lz | $(am__untar) ;;\
*.tar.xz*) \
xz -dc $(distdir).tar.xz | $(am__untar) ;;\
*.tar.Z*) \
uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
*.shar.gz*) \
eval GZIP= gzip -dc $(distdir).shar.gz | unshar ;;\
*.zip*) \
unzip $(distdir).zip ;;\
*.tar.zst*) \
zstd -dc $(distdir).tar.zst | $(am__untar) ;;\
esac
chmod -R a-w $(distdir)
chmod u+w $(distdir)
mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst
chmod a-w $(distdir)
test -d $(distdir)/_build || exit 0; \
dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
&& dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
&& am__cwd=`pwd` \
&& $(am__cd) $(distdir)/_build/sub \
&& ../../configure \
$(AM_DISTCHECK_CONFIGURE_FLAGS) \
$(DISTCHECK_CONFIGURE_FLAGS) \
--srcdir=../.. --prefix="$$dc_install_base" \
&& $(MAKE) $(AM_MAKEFLAGS) \
&& $(MAKE) $(AM_MAKEFLAGS) $(AM_DISTCHECK_DVI_TARGET) \
&& $(MAKE) $(AM_MAKEFLAGS) check \
&& $(MAKE) $(AM_MAKEFLAGS) install \
&& $(MAKE) $(AM_MAKEFLAGS) installcheck \
&& $(MAKE) $(AM_MAKEFLAGS) uninstall \
&& $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
distuninstallcheck \
&& chmod -R a-w "$$dc_install_base" \
&& ({ \
(cd ../.. && umask 077 && mkdir "$$dc_destdir") \
&& $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
&& $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
&& $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
} || { rm -rf "$$dc_destdir"; exit 1; }) \
&& rm -rf "$$dc_destdir" \
&& $(MAKE) $(AM_MAKEFLAGS) dist \
&& rm -rf $(DIST_ARCHIVES) \
&& $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
&& cd "$$am__cwd" \
|| exit 1
$(am__post_remove_distdir)
@(echo "$(distdir) archives ready for distribution: "; \
list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
distuninstallcheck:
@test -n '$(distuninstallcheck_dir)' || { \
echo 'ERROR: trying to run $@ with an empty' \
'$$(distuninstallcheck_dir)' >&2; \
exit 1; \
}; \
$(am__cd) '$(distuninstallcheck_dir)' || { \
echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \
exit 1; \
}; \
test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \
|| { echo "ERROR: files left after uninstall:" ; \
if test -n "$(DESTDIR)"; then \
echo " (check DESTDIR support)"; \
fi ; \
$(distuninstallcheck_listfiles) ; \
exit 1; } >&2
distcleancheck: distclean
@if test '$(srcdir)' = . ; then \
echo "ERROR: distcleancheck can only run from a VPATH build" ; \
exit 1 ; \
fi
@test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
|| { echo "ERROR: files left in build directory after distclean:" ; \
$(distcleancheck_listfiles) ; \
exit 1; } >&2
check-am: all-am
check: check-recursive
all-am: Makefile
installdirs: installdirs-recursive
installdirs-am:
install: install-recursive
install-exec: install-exec-recursive
install-data: install-data-recursive
uninstall: uninstall-recursive
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-recursive
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
-$(am__rm_f) $(CLEANFILES)
distclean-generic:
-$(am__rm_f) $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
-$(am__rm_f) $(MAINTAINERCLEANFILES)
clean: clean-recursive
clean-am: clean-generic clean-libtool mostlyclean-am
distclean: distclean-recursive
-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-rm -f Makefile
distclean-am: clean-am distclean-generic distclean-libtool \
distclean-tags
dvi: dvi-recursive
dvi-am:
html: html-recursive
html-am:
info: info-recursive
info-am:
install-data-am:
install-dvi: install-dvi-recursive
install-dvi-am:
install-exec-am:
install-html: install-html-recursive
install-html-am:
install-info: install-info-recursive
install-info-am:
install-man:
install-pdf: install-pdf-recursive
install-pdf-am:
install-ps: install-ps-recursive
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-recursive
-rm -f $(am__CONFIG_DISTCLEAN_FILES)
-rm -rf $(top_srcdir)/autom4te.cache
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-recursive
mostlyclean-am: mostlyclean-generic mostlyclean-libtool
pdf: pdf-recursive
pdf-am:
ps: ps-recursive
ps-am:
uninstall-am:
.MAKE: $(am__recursive_targets) install-am install-strip
.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
am--refresh check check-am clean clean-cscope clean-generic \
clean-libtool cscope cscopelist-am ctags ctags-am dist \
dist-all dist-bzip2 dist-bzip3 dist-gzip dist-lzip dist-shar \
dist-tarZ dist-xz dist-zip dist-zstd distcheck distclean \
distclean-generic distclean-libtool distclean-tags \
distcleancheck distdir distuninstallcheck dvi dvi-am html \
html-am info info-am install install-am install-data \
install-data-am install-dvi install-dvi-am install-exec \
install-exec-am install-html install-html-am install-info \
install-info-am install-man install-pdf install-pdf-am \
install-ps install-ps-am install-strip installcheck \
installcheck-am installdirs installdirs-am maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-generic \
mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
uninstall-am
.PRECIOUS: Makefile
test: check
test-regression:
(cd tests && $(MAKE) test-regression)
cppcheck:
@cppcheck \
-j `getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu || echo 1` \
--enable=all \
--force \
--verbose \
--library=gnu \
--library=posix \
--std=c++17 \
-I ./apache2 \
-I /usr/include/libxml2 \
-I @APXS_INCLUDEDIR@ \
-I @APR_INCLUDEDIR@ \
-I @APU_INCLUDEDIR@ \
--suppressions-list=./tests/cppcheck_suppressions.txt \
--suppress=syntaxError:@APR_INCLUDEDIR@/* \
--inline-suppr \
--inconclusive \
--template="warning: {file},{line},{severity},{id},{message}" \
--error-exitcode=1 \
standalone/
check-static: cppcheck
check-coding-style:
for i in `(find . -iname "*.c" ; find . -iname "*.h")`; \
do echo $$i...; \
vera++ -rule L004 -param max-line-length=80 $$i 2>&1 | sed 's/^/warning: /g' 1>&2; \
vera++ -rule L001 $$i 2>&1 | sed 's/^/warning: /g' 1>&2; \
done;
.PHONY: test
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
# Tell GNU make to disable its built-in pattern rules.
%:: %,v
%:: RCS/%,v
%:: RCS/%
%:: s.%
%:: SCCS/s.%
modsecurity-apache-2.9.13/NOTICE 0000664 0000000 0000000 00000000310 15174655764 0016316 0 ustar 00root root 0000000 0000000 ModSecurity (www.modsecurity.org)
Copyright [2004-2013] Trustwave Holdings, Inc
This product includes software developed at
Trustwave Holdings, Inc (http://www.trustwave.com/).
modsecurity-apache-2.9.13/README.md 0000664 0000000 0000000 00000002163 15174655764 0016701 0 ustar 00root root 0000000 0000000 # 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).
modsecurity-apache-2.9.13/README_WINDOWS.md 0000664 0000000 0000000 00000016067 15174655764 0020123 0 ustar 00root root 0000000 0000000
## 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
modsecurity-apache-2.9.13/aclocal.m4 0000664 0000000 0000000 00000176520 15174655764 0017273 0 ustar 00root root 0000000 0000000 # generated automatically by aclocal 1.18.1 -*- Autoconf -*-
# Copyright (C) 1996-2025 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.72],,
[m4_warning([this file was generated for autoconf 2.72.
You have another version of autoconf. It may work, but is not guaranteed to.
If you have problems, you may need to regenerate the build system entirely.
To do so, use the procedure documented by the package, typically 'autoreconf'.])])
# pkg.m4 - Macros to locate and use pkg-config. -*- Autoconf -*-
# serial 13 (pkgconf)
dnl Copyright © 2004 Scott James Remnant .
dnl Copyright © 2012-2015 Dan Nicholson
dnl
dnl This program is free software; you can redistribute it and/or modify
dnl it under the terms of the GNU General Public License as published by
dnl the Free Software Foundation; either version 2 of the License, or
dnl (at your option) any later version.
dnl
dnl This program is distributed in the hope that it will be useful, but
dnl WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
dnl General Public License for more details.
dnl
dnl You should have received a copy of the GNU General Public License
dnl along with this program; if not, see .
dnl
dnl As a special exception to the GNU General Public License, if you
dnl distribute this file as part of a program that contains a
dnl configuration script generated by Autoconf, you may include it under
dnl the same distribution terms that you use for the rest of that
dnl program.
dnl PKG_PREREQ(MIN-VERSION)
dnl -----------------------
dnl Since: 0.29
dnl
dnl Verify that the version of the pkg-config macros are at least
dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's
dnl installed version of pkg-config, this checks the developer's version
dnl of pkg.m4 when generating configure.
dnl
dnl To ensure that this macro is defined, also add:
dnl m4_ifndef([PKG_PREREQ],
dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])])
dnl
dnl See the "Since" comment for each macro you use to see what version
dnl of the macros you require.
m4_defun([PKG_PREREQ],
[m4_define([PKG_MACROS_VERSION], [0.29.2])
m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1,
[m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])])
])dnl PKG_PREREQ
dnl PKG_PROG_PKG_CONFIG([MIN-VERSION], [ACTION-IF-NOT-FOUND])
dnl ---------------------------------------------------------
dnl Since: 0.16
dnl
dnl Search for the pkg-config tool and set the PKG_CONFIG variable to
dnl first found in the path. Checks that the version of pkg-config found
dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is
dnl used since that's the first version where most current features of
dnl pkg-config existed.
dnl
dnl If pkg-config is not found or older than specified, it will result
dnl in an empty PKG_CONFIG variable. To avoid widespread issues with
dnl scripts not checking it, ACTION-IF-NOT-FOUND defaults to aborting.
dnl You can specify [PKG_CONFIG=false] as an action instead, which would
dnl result in pkg-config tests failing, but no bogus error messages.
AC_DEFUN([PKG_PROG_PKG_CONFIG],
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$])
m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$])
AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])
AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path])
AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path])
if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
fi
if test -n "$PKG_CONFIG"; then
_pkg_min_version=m4_default([$1], [0.9.0])
AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
PKG_CONFIG=""
fi
fi
if test -z "$PKG_CONFIG"; then
m4_default([$2], [AC_MSG_ERROR([pkg-config not found])])
fi[]dnl
])dnl PKG_PROG_PKG_CONFIG
dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl -------------------------------------------------------------------
dnl Since: 0.18
dnl
dnl Check to see whether a particular set of modules exists. Similar to
dnl PKG_CHECK_MODULES(), but does not set variables or print errors.
dnl
dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG])
dnl only at the first occurrence in configure.ac, so if the first place
dnl it's called might be skipped (such as if it is within an "if", you
dnl have to call PKG_CHECK_EXISTS manually
AC_DEFUN([PKG_CHECK_EXISTS],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
if test -n "$PKG_CONFIG" && \
AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
m4_default([$2], [:])
m4_ifvaln([$3], [else
$3])dnl
fi])
dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
dnl ---------------------------------------------
dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting
dnl pkg_failed based on the result.
m4_define([_PKG_CONFIG],
[if test -n "$$1"; then
pkg_cv_[]$1="$$1"
elif test -n "$PKG_CONFIG"; then
PKG_CHECK_EXISTS([$3],
[pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`
test "x$?" != "x0" && pkg_failed=yes ],
[pkg_failed=yes])
else
pkg_failed=untried
fi[]dnl
])dnl _PKG_CONFIG
dnl _PKG_SHORT_ERRORS_SUPPORTED
dnl ---------------------------
dnl Internal check to see if pkg-config supports short errors.
AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi[]dnl
])dnl _PKG_SHORT_ERRORS_SUPPORTED
dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
dnl [ACTION-IF-NOT-FOUND])
dnl --------------------------------------------------------------
dnl Since: 0.4.0
dnl
dnl Note that if there is a possibility the first call to
dnl PKG_CHECK_MODULES might not happen, you should be sure to include an
dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
AC_DEFUN([PKG_CHECK_MODULES],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
pkg_failed=no
AC_MSG_CHECKING([for $2])
_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
_PKG_CONFIG([$1][_LIBS], [libs], [$2])
m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
and $1[]_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.])
if test $pkg_failed = yes; then
AC_MSG_RESULT([no])
_PKG_SHORT_ERRORS_SUPPORTED
if test $_pkg_short_errors_supported = yes; then
$1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1`
else
$1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1`
fi
# Put the nasty error message in config.log where it belongs
echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
m4_default([$4], [AC_MSG_ERROR(
[Package requirements ($2) were not met:
$$1_PKG_ERRORS
Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.
_PKG_TEXT])[]dnl
])
elif test $pkg_failed = untried; then
AC_MSG_RESULT([no])
m4_default([$4], [AC_MSG_FAILURE(
[The pkg-config script could not be found or is too old. Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
path to pkg-config.
_PKG_TEXT
To get pkg-config, see .])[]dnl
])
else
$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
AC_MSG_RESULT([yes])
$3
fi[]dnl
])dnl PKG_CHECK_MODULES
dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
dnl [ACTION-IF-NOT-FOUND])
dnl ---------------------------------------------------------------------
dnl Since: 0.29
dnl
dnl Checks for existence of MODULES and gathers its build flags with
dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags
dnl and VARIABLE-PREFIX_LIBS from --libs.
dnl
dnl Note that if there is a possibility the first call to
dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to
dnl include an explicit call to PKG_PROG_PKG_CONFIG in your
dnl configure.ac.
AC_DEFUN([PKG_CHECK_MODULES_STATIC],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
_save_PKG_CONFIG=$PKG_CONFIG
PKG_CONFIG="$PKG_CONFIG --static"
PKG_CHECK_MODULES($@)
PKG_CONFIG=$_save_PKG_CONFIG[]dnl
])dnl PKG_CHECK_MODULES_STATIC
dnl PKG_INSTALLDIR([DIRECTORY])
dnl -------------------------
dnl Since: 0.27
dnl
dnl Substitutes the variable pkgconfigdir as the location where a module
dnl should install pkg-config .pc files. By default the directory is
dnl $libdir/pkgconfig, but the default can be changed by passing
dnl DIRECTORY. The user can override through the --with-pkgconfigdir
dnl parameter.
AC_DEFUN([PKG_INSTALLDIR],
[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])])
m4_pushdef([pkg_description],
[pkg-config installation directory @<:@]pkg_default[@:>@])
AC_ARG_WITH([pkgconfigdir],
[AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],,
[with_pkgconfigdir=]pkg_default)
AC_SUBST([pkgconfigdir], [$with_pkgconfigdir])
m4_popdef([pkg_default])
m4_popdef([pkg_description])
])dnl PKG_INSTALLDIR
dnl PKG_NOARCH_INSTALLDIR([DIRECTORY])
dnl --------------------------------
dnl Since: 0.27
dnl
dnl Substitutes the variable noarch_pkgconfigdir as the location where a
dnl module should install arch-independent pkg-config .pc files. By
dnl default the directory is $datadir/pkgconfig, but the default can be
dnl changed by passing DIRECTORY. The user can override through the
dnl --with-noarch-pkgconfigdir parameter.
AC_DEFUN([PKG_NOARCH_INSTALLDIR],
[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])])
m4_pushdef([pkg_description],
[pkg-config arch-independent installation directory @<:@]pkg_default[@:>@])
AC_ARG_WITH([noarch-pkgconfigdir],
[AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],,
[with_noarch_pkgconfigdir=]pkg_default)
AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir])
m4_popdef([pkg_default])
m4_popdef([pkg_description])
])dnl PKG_NOARCH_INSTALLDIR
dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE,
dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl -------------------------------------------
dnl Since: 0.28
dnl
dnl Retrieves the value of the pkg-config variable for the given module.
AC_DEFUN([PKG_CHECK_VAR],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl
_PKG_CONFIG([$1], [variable="][$3]["], [$2])
AS_VAR_COPY([$1], [pkg_cv_][$1])
AS_VAR_IF([$1], [""], [$5], [$4])dnl
])dnl PKG_CHECK_VAR
dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES,
dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND],
dnl [DESCRIPTION], [DEFAULT])
dnl ------------------------------------------
dnl
dnl Prepare a "--with-" configure option using the lowercase
dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and
dnl PKG_CHECK_MODULES in a single macro.
AC_DEFUN([PKG_WITH_MODULES],
[
m4_pushdef([with_arg], m4_tolower([$1]))
m4_pushdef([description],
[m4_default([$5], [build with ]with_arg[ support])])
m4_pushdef([def_arg], [m4_default([$6], [auto])])
m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes])
m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no])
m4_case(def_arg,
[yes],[m4_pushdef([with_without], [--without-]with_arg)],
[m4_pushdef([with_without],[--with-]with_arg)])
AC_ARG_WITH(with_arg,
AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),,
[AS_TR_SH([with_]with_arg)=def_arg])
AS_CASE([$AS_TR_SH([with_]with_arg)],
[yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)],
[auto],[PKG_CHECK_MODULES([$1],[$2],
[m4_n([def_action_if_found]) $3],
[m4_n([def_action_if_not_found]) $4])])
m4_popdef([with_arg])
m4_popdef([description])
m4_popdef([def_arg])
])dnl PKG_WITH_MODULES
dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES,
dnl [DESCRIPTION], [DEFAULT])
dnl -----------------------------------------------
dnl
dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES
dnl check._[VARIABLE-PREFIX] is exported as make variable.
AC_DEFUN([PKG_HAVE_WITH_MODULES],
[
PKG_WITH_MODULES([$1],[$2],,,[$3],[$4])
AM_CONDITIONAL([HAVE_][$1],
[test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"])
])dnl PKG_HAVE_WITH_MODULES
dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES,
dnl [DESCRIPTION], [DEFAULT])
dnl ------------------------------------------------------
dnl
dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after
dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make
dnl and preprocessor variable.
AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES],
[
PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4])
AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"],
[AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])])
])dnl PKG_HAVE_DEFINE_WITH_MODULES
# Copyright (C) 2002-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_AUTOMAKE_VERSION(VERSION)
# ----------------------------
# Automake X.Y traces this macro to ensure aclocal.m4 has been
# generated from the m4 files accompanying Automake X.Y.
# (This private macro should not be called outside this file.)
AC_DEFUN([AM_AUTOMAKE_VERSION],
[am__api_version='1.18'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
m4_if([$1], [1.18.1], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])
# _AM_AUTOCONF_VERSION(VERSION)
# -----------------------------
# aclocal traces this macro to find the Autoconf version.
# This is a private macro too. Using m4_define simplifies
# the logic in aclocal, which can simply ignore this definition.
m4_define([_AM_AUTOCONF_VERSION], [])
# AM_SET_CURRENT_AUTOMAKE_VERSION
# -------------------------------
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
[AM_AUTOMAKE_VERSION([1.18.1])dnl
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
# Copyright (C) 2011-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_AR([ACT-IF-FAIL])
# -------------------------
# Try to determine the archiver interface, and trigger the ar-lib wrapper
# if it is needed. If the detection of archiver interface fails, run
# ACT-IF-FAIL (default is to abort configure with a proper error message).
AC_DEFUN([AM_PROG_AR],
[AC_BEFORE([$0], [LT_INIT])dnl
AC_BEFORE([$0], [AC_PROG_LIBTOOL])dnl
AC_BEFORE([$0], [AC_PROG_AR])dnl
AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
AC_REQUIRE_AUX_FILE([ar-lib])dnl
AC_CHECK_TOOLS([AR], [ar lib "link -lib"], [false])
: ${AR=ar}
: ${ARFLAGS=cr}
AC_CACHE_CHECK([the archiver ($AR) interface], [am_cv_ar_interface],
[AC_LANG_PUSH([C])
am_cv_ar_interface=ar
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int some_variable = 0;]])],
[am_ar_try='$AR $ARFLAGS libconftest.a conftest.$ac_objext >&AS_MESSAGE_LOG_FD'
AC_TRY_EVAL([am_ar_try])
if test "$ac_status" -eq 0; then
am_cv_ar_interface=ar
else
am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&AS_MESSAGE_LOG_FD'
AC_TRY_EVAL([am_ar_try])
if test "$ac_status" -eq 0; then
am_cv_ar_interface=lib
else
am_cv_ar_interface=unknown
fi
fi
rm -f conftest.lib libconftest.a
])
AC_LANG_POP([C])])
case $am_cv_ar_interface in
ar)
;;
lib)
# Microsoft lib, so override with the ar-lib wrapper script.
# FIXME: It is wrong to rewrite AR.
# But if we don't then we get into trouble of one sort or another.
# A longer-term fix would be to have automake use am__AR in this case,
# and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something
# similar.
AR="$am_aux_dir/ar-lib $AR"
;;
unknown)
m4_default([$1],
[AC_MSG_ERROR([could not determine $AR interface])])
;;
esac
AC_SUBST([AR])dnl
])
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
# Copyright (C) 2001-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to
# '$srcdir', '$srcdir/..', or '$srcdir/../..'.
#
# Of course, Automake must honor this variable whenever it calls a
# tool from the auxiliary directory. The problem is that $srcdir (and
# therefore $ac_aux_dir as well) can be either absolute or relative,
# depending on how configure is run. This is pretty annoying, since
# it makes $ac_aux_dir quite unusable in subdirectories: in the top
# source directory, any form will work fine, but in subdirectories a
# relative path needs to be adjusted first.
#
# $ac_aux_dir/missing
# fails when called from a subdirectory if $ac_aux_dir is relative
# $top_srcdir/$ac_aux_dir/missing
# fails if $ac_aux_dir is absolute,
# fails when called from a subdirectory in a VPATH build with
# a relative $ac_aux_dir
#
# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
# are both prefixed by $srcdir. In an in-source build this is usually
# harmless because $srcdir is '.', but things will broke when you
# start a VPATH build or use an absolute $srcdir.
#
# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
# iff we strip the leading $srcdir from $ac_aux_dir. That would be:
# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
# and then we would define $MISSING as
# MISSING="\${SHELL} $am_aux_dir/missing"
# This will work as long as MISSING is not called from configure, because
# unfortunately $(top_srcdir) has no meaning in configure.
# However there are other variables, like CC, which are often used in
# configure, and could therefore not use this "fixed" $ac_aux_dir.
#
# Another solution, used here, is to always expand $ac_aux_dir to an
# absolute PATH. The drawback is that using absolute paths prevent a
# configured tree to be moved without reconfiguration.
AC_DEFUN([AM_AUX_DIR_EXPAND],
[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
# Expand $ac_aux_dir to an absolute path.
am_aux_dir=`cd "$ac_aux_dir" && pwd`
])
# AM_CONDITIONAL -*- Autoconf -*-
# Copyright (C) 1997-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_CONDITIONAL(NAME, SHELL-CONDITION)
# -------------------------------------
# Define a conditional.
AC_DEFUN([AM_CONDITIONAL],
[AC_PREREQ([2.52])dnl
m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
[$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
AC_SUBST([$1_TRUE])dnl
AC_SUBST([$1_FALSE])dnl
_AM_SUBST_NOTMAKE([$1_TRUE])dnl
_AM_SUBST_NOTMAKE([$1_FALSE])dnl
m4_define([_AM_COND_VALUE_$1], [$2])dnl
if $2; then
$1_TRUE=
$1_FALSE='#'
else
$1_TRUE='#'
$1_FALSE=
fi
AC_CONFIG_COMMANDS_PRE(
[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
AC_MSG_ERROR([[conditional "$1" was never defined.
Usually this means the macro was only invoked conditionally.]])
fi])])
# Copyright (C) 1999-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be
# written in clear, in which case automake, when reading aclocal.m4,
# will think it sees a *use*, and therefore will trigger all it's
# C support machinery. Also note that it means that autoscan, seeing
# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
# _AM_DEPENDENCIES(NAME)
# ----------------------
# See how the compiler implements dependency checking.
# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC".
# We try a few techniques and use that to set a single cache variable.
#
# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
# dependency, and given that the user is not expected to run this macro,
# just rely on AC_PROG_CC.
AC_DEFUN([_AM_DEPENDENCIES],
[AC_REQUIRE([AM_SET_DEPDIR])dnl
AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
AC_REQUIRE([AM_MAKE_INCLUDE])dnl
AC_REQUIRE([AM_DEP_TRACK])dnl
m4_if([$1], [CC], [depcc="$CC" am_compiler_list=],
[$1], [CXX], [depcc="$CXX" am_compiler_list=],
[$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
[$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'],
[$1], [UPC], [depcc="$UPC" am_compiler_list=],
[$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
[depcc="$$1" am_compiler_list=])
AC_CACHE_CHECK([dependency style of $depcc],
[am_cv_$1_dependencies_compiler_type],
[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
# We make a subdir and do the tests there. Otherwise we can end up
# making bogus files that we don't know about and never remove. For
# instance it was reported that on HP-UX the gcc test will end up
# making a dummy file named 'D' -- because '-MD' means "put the output
# in D".
rm -rf conftest.dir
mkdir conftest.dir
# Copy depcomp to subdir because otherwise we won't find it if we're
# using a relative directory.
cp "$am_depcomp" conftest.dir
cd conftest.dir
# We will build objects and dependencies in a subdirectory because
# it helps to detect inapplicable dependency modes. For instance
# both Tru64's cc and ICC support -MD to output dependencies as a
# side effect of compilation, but ICC will put the dependencies in
# the current directory while Tru64 will put them in the object
# directory.
mkdir sub
am_cv_$1_dependencies_compiler_type=none
if test "$am_compiler_list" = ""; then
am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
fi
am__universal=false
m4_case([$1], [CC],
[case " $depcc " in #(
*\ -arch\ *\ -arch\ *) am__universal=true ;;
esac],
[CXX],
[case " $depcc " in #(
*\ -arch\ *\ -arch\ *) am__universal=true ;;
esac])
for depmode in $am_compiler_list; do
# Setup a source with many dependencies, because some compilers
# like to wrap large dependency lists on column 80 (with \), and
# we should not choose a depcomp mode which is confused by this.
#
# We need to recreate these files for each test, as the compiler may
# overwrite some of them when testing with obscure command lines.
# This happens at least with the AIX C compiler.
: > sub/conftest.c
for i in 1 2 3 4 5 6; do
echo '#include "conftst'$i'.h"' >> sub/conftest.c
# Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
# Solaris 10 /bin/sh.
echo '/* dummy */' > sub/conftst$i.h
done
echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
# We check with '-c' and '-o' for the sake of the "dashmstdout"
# mode. It turns out that the SunPro C++ compiler does not properly
# handle '-M -o', and we need to detect this. Also, some Intel
# versions had trouble with output in subdirs.
am__obj=sub/conftest.${OBJEXT-o}
am__minus_obj="-o $am__obj"
case $depmode in
gcc)
# This depmode causes a compiler race in universal mode.
test "$am__universal" = false || continue
;;
nosideeffect)
# After this tag, mechanisms are not by side-effect, so they'll
# only be used when explicitly requested.
if test "x$enable_dependency_tracking" = xyes; then
continue
else
break
fi
;;
msvc7 | msvc7msys | msvisualcpp | msvcmsys)
# This compiler won't grok '-c -o', but also, the minuso test has
# not run yet. These depmodes are late enough in the game, and
# so weak that their functioning should not be impacted.
am__obj=conftest.${OBJEXT-o}
am__minus_obj=
;;
none) break ;;
esac
if depmode=$depmode \
source=sub/conftest.c object=$am__obj \
depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
$SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \
>/dev/null 2>conftest.err &&
grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
grep $am__obj sub/conftest.Po > /dev/null 2>&1 &&
${MAKE-make} -s -f confmf > /dev/null 2>&1; then
# icc doesn't choke on unknown options, it will just issue warnings
# or remarks (even with -Werror). So we grep stderr for any message
# that says an option was ignored or not supported.
# When given -MP, icc 7.0 and 7.1 complain thus:
# icc: Command line warning: ignoring option '-M'; no argument required
# The diagnosis changed in icc 8.0:
# icc: Command line remark: option '-MP' not supported
if (grep 'ignoring option' conftest.err ||
grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
am_cv_$1_dependencies_compiler_type=$depmode
break
fi
fi
done
cd ..
rm -rf conftest.dir
else
am_cv_$1_dependencies_compiler_type=none
fi
])
AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
AM_CONDITIONAL([am__fastdep$1], [
test "x$enable_dependency_tracking" != xno \
&& test "$am_cv_$1_dependencies_compiler_type" = gcc3])
])
# AM_SET_DEPDIR
# -------------
# Choose a directory name for dependency files.
# This macro is AC_REQUIREd in _AM_DEPENDENCIES.
AC_DEFUN([AM_SET_DEPDIR],
[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
])
# AM_DEP_TRACK
# ------------
AC_DEFUN([AM_DEP_TRACK],
[AC_ARG_ENABLE([dependency-tracking], [dnl
AS_HELP_STRING(
[--enable-dependency-tracking],
[do not reject slow dependency extractors])
AS_HELP_STRING(
[--disable-dependency-tracking],
[speeds up one-time build])])
if test "x$enable_dependency_tracking" != xno; then
am_depcomp="$ac_aux_dir/depcomp"
AMDEPBACKSLASH='\'
am__nodep='_no'
fi
AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
AC_SUBST([AMDEPBACKSLASH])dnl
_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
AC_SUBST([am__nodep])dnl
_AM_SUBST_NOTMAKE([am__nodep])dnl
])
# Generate code to set up dependency tracking. -*- Autoconf -*-
# Copyright (C) 1999-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_OUTPUT_DEPENDENCY_COMMANDS
# ------------------------------
AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
[{
# Older Autoconf quotes --file arguments for eval, but not when files
# are listed without --file. Let's play safe and only enable the eval
# if we detect the quoting.
# TODO: see whether this extra hack can be removed once we start
# requiring Autoconf 2.70 or later.
AS_CASE([$CONFIG_FILES],
[*\'*], [eval set x "$CONFIG_FILES"],
[*], [set x $CONFIG_FILES])
shift
# Used to flag and report bootstrapping failures.
am_rc=0
for am_mf
do
# Strip MF so we end up with the name of the file.
am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'`
# Check whether this is an Automake generated Makefile which includes
# dependency-tracking related rules and includes.
# Grep'ing the whole file directly is not great: AIX grep has a line
# limit of 2048, but all sed's we know have understand at least 4000.
sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
|| continue
am_dirpart=`AS_DIRNAME(["$am_mf"])`
am_filepart=`AS_BASENAME(["$am_mf"])`
AM_RUN_LOG([cd "$am_dirpart" \
&& sed -e '/# am--include-marker/d' "$am_filepart" \
| $MAKE -f - am--depfiles]) || am_rc=$?
done
if test $am_rc -ne 0; then
AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments
for automatic dependency tracking. If GNU make was not used, consider
re-running the configure script with MAKE="gmake" (or whatever is
necessary). You can also try re-running configure with the
'--disable-dependency-tracking' option to at least be able to build
the package (albeit without support for automatic dependency tracking).])
fi
AS_UNSET([am_dirpart])
AS_UNSET([am_filepart])
AS_UNSET([am_mf])
AS_UNSET([am_rc])
rm -f conftest-deps.mk
}
])# _AM_OUTPUT_DEPENDENCY_COMMANDS
# AM_OUTPUT_DEPENDENCY_COMMANDS
# -----------------------------
# This macro should only be invoked once -- use via AC_REQUIRE.
#
# This code is only required when automatic dependency tracking is enabled.
# This creates each '.Po' and '.Plo' makefile fragment that we'll need in
# order to bootstrap the dependency handling code.
AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
[AC_CONFIG_COMMANDS([depfiles],
[test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
[AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])])
# Do all the work for Automake. -*- Autoconf -*-
# Copyright (C) 1996-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This macro actually does too much. Some checks are only needed if
# your package does certain things. But this isn't really a big deal.
dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O.
m4_define([AC_PROG_CC],
m4_defn([AC_PROG_CC])
[_AM_PROG_CC_C_O
])
# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
# AM_INIT_AUTOMAKE([OPTIONS])
# -----------------------------------------------
# The call with PACKAGE and VERSION arguments is the old style
# call (pre autoconf-2.50), which is being phased out. PACKAGE
# and VERSION should now be passed to AC_INIT and removed from
# the call to AM_INIT_AUTOMAKE.
# We support both call styles for the transition. After
# the next Automake release, Autoconf can make the AC_INIT
# arguments mandatory, and then we can depend on a new Autoconf
# release and drop the old call support.
AC_DEFUN([AM_INIT_AUTOMAKE],
[AC_PREREQ([2.65])dnl
m4_ifdef([_$0_ALREADY_INIT],
[m4_fatal([$0 expanded multiple times
]m4_defn([_$0_ALREADY_INIT]))],
[m4_define([_$0_ALREADY_INIT], m4_expansion_stack)])dnl
dnl Autoconf wants to disallow AM_ names. We explicitly allow
dnl the ones we care about.
m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
AC_REQUIRE([AC_PROG_INSTALL])dnl
if test "`cd $srcdir && pwd`" != "`pwd`"; then
# Use -I$(srcdir) only when $(srcdir) != ., so that make's output
# is not polluted with repeated "-I."
AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
# test to see if srcdir already configured
if test -f $srcdir/config.status; then
AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
fi
fi
# test whether we have cygpath
if test -z "$CYGPATH_W"; then
if (cygpath --version) >/dev/null 2>/dev/null; then
CYGPATH_W='cygpath -w'
else
CYGPATH_W=echo
fi
fi
AC_SUBST([CYGPATH_W])
# Define the identity of the package.
dnl Distinguish between old-style and new-style calls.
m4_ifval([$2],
[AC_DIAGNOSE([obsolete],
[$0: two- and three-arguments forms are deprecated.])
m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
AC_SUBST([PACKAGE], [$1])dnl
AC_SUBST([VERSION], [$2])],
[_AM_SET_OPTIONS([$1])dnl
dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
m4_if(
m4_ifset([AC_PACKAGE_NAME], [ok]):m4_ifset([AC_PACKAGE_VERSION], [ok]),
[ok:ok],,
[m4_fatal([AC_INIT should be called with package and version arguments])])dnl
AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
_AM_IF_OPTION([no-define],,
[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package])
AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl
# Some tools Automake needs.
AC_REQUIRE([AM_SANITY_CHECK])dnl
AC_REQUIRE([AC_ARG_PROGRAM])dnl
AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}])
AM_MISSING_PROG([AUTOCONF], [autoconf])
AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}])
AM_MISSING_PROG([AUTOHEADER], [autoheader])
AM_MISSING_PROG([MAKEINFO], [makeinfo])
AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
AC_REQUIRE([AC_PROG_MKDIR_P])dnl
# For better backward compatibility. To be removed once Automake 1.9.x
# dies out for good. For more background, see:
#
#
AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
# We need awk for the "check" target (and possibly the TAP driver). The
# system "awk" is bad on some platforms.
AC_REQUIRE([AC_PROG_AWK])dnl
AC_REQUIRE([AC_PROG_MAKE_SET])dnl
AC_REQUIRE([AM_SET_LEADING_DOT])dnl
_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
[_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
[_AM_IF_OPTION([tar-v7], [_AM_PROG_TAR([v7])],
[_AM_PROG_TAR([ustar])])])])
_AM_IF_OPTION([no-dependencies],,
[AC_PROVIDE_IFELSE([AC_PROG_CC],
[_AM_DEPENDENCIES([CC])],
[m4_define([AC_PROG_CC],
m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl
AC_PROVIDE_IFELSE([AC_PROG_CXX],
[_AM_DEPENDENCIES([CXX])],
[m4_define([AC_PROG_CXX],
m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl
AC_PROVIDE_IFELSE([AC_PROG_OBJC],
[_AM_DEPENDENCIES([OBJC])],
[m4_define([AC_PROG_OBJC],
m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl
AC_PROVIDE_IFELSE([AC_PROG_OBJCXX],
[_AM_DEPENDENCIES([OBJCXX])],
[m4_define([AC_PROG_OBJCXX],
m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl
])
# Variables for tags utilities; see am/tags.am
if test -z "$CTAGS"; then
CTAGS=ctags
fi
AC_SUBST([CTAGS])
if test -z "$ETAGS"; then
ETAGS=etags
fi
AC_SUBST([ETAGS])
if test -z "$CSCOPE"; then
CSCOPE=cscope
fi
AC_SUBST([CSCOPE])
AC_REQUIRE([_AM_SILENT_RULES])dnl
dnl The testsuite driver may need to know about EXEEXT, so add the
dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This
dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below.
AC_CONFIG_COMMANDS_PRE(dnl
[m4_provide_if([_AM_COMPILER_EXEEXT],
[AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
AC_REQUIRE([_AM_PROG_RM_F])
AC_REQUIRE([_AM_PROG_XARGS_N])
dnl The trailing newline in this macro's definition is deliberate, for
dnl backward compatibility and to allow trailing 'dnl'-style comments
dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841.
])
dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
dnl mangled by Autoconf and run in a shell conditional statement.
m4_define([_AC_COMPILER_EXEEXT],
m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
# When config.status generates a header, we must update the stamp-h file.
# This file resides in the same directory as the config header
# that is generated. The stamp files are numbered to have different names.
# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
# loop where config.status creates the headers, so we can generate
# our stamp files there.
AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
[# Compute $1's index in $config_headers.
_am_arg=$1
_am_stamp_count=1
for _am_header in $config_headers :; do
case $_am_header in
$_am_arg | $_am_arg:* )
break ;;
* )
_am_stamp_count=`expr $_am_stamp_count + 1` ;;
esac
done
echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
# Copyright (C) 2001-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_INSTALL_SH
# ------------------
# Define $install_sh.
AC_DEFUN([AM_PROG_INSTALL_SH],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
if test x"${install_sh+set}" != xset; then
case $am_aux_dir in
*\ * | *\ *)
install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
*)
install_sh="\${SHELL} $am_aux_dir/install-sh"
esac
fi
AC_SUBST([install_sh])])
# Copyright (C) 2003-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# Check whether the underlying file-system supports filenames
# with a leading dot. For instance MS-DOS doesn't.
AC_DEFUN([AM_SET_LEADING_DOT],
[rm -rf .tst 2>/dev/null
mkdir .tst 2>/dev/null
if test -d .tst; then
am__leading_dot=.
else
am__leading_dot=_
fi
rmdir .tst 2>/dev/null
AC_SUBST([am__leading_dot])])
# Check to see how 'make' treats includes. -*- Autoconf -*-
# Copyright (C) 2001-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_MAKE_INCLUDE()
# -----------------
# Check whether make has an 'include' directive that can support all
# the idioms we need for our automatic dependency tracking code.
AC_DEFUN([AM_MAKE_INCLUDE],
[AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive])
cat > confinc.mk << 'END'
am__doit:
@echo this is the am__doit target >confinc.out
.PHONY: am__doit
END
am__include="#"
am__quote=
# BSD make does it like this.
echo '.include "confinc.mk" # ignored' > confmf.BSD
# Other make implementations (GNU, Solaris 10, AIX) do it like this.
echo 'include confinc.mk # ignored' > confmf.GNU
_am_result=no
for s in GNU BSD; do
AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out])
AS_CASE([$?:`cat confinc.out 2>/dev/null`],
['0:this is the am__doit target'],
[AS_CASE([$s],
[BSD], [am__include='.include' am__quote='"'],
[am__include='include' am__quote=''])])
if test "$am__include" != "#"; then
_am_result="yes ($s style)"
break
fi
done
rm -f confinc.* confmf.*
AC_MSG_RESULT([${_am_result}])
AC_SUBST([am__include])])
AC_SUBST([am__quote])])
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
# Copyright (C) 1997-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_MISSING_PROG(NAME, PROGRAM)
# ------------------------------
AC_DEFUN([AM_MISSING_PROG],
[AC_REQUIRE([AM_MISSING_HAS_RUN])
$1=${$1-"${am_missing_run}$2"}
AC_SUBST($1)])
# AM_MISSING_HAS_RUN
# ------------------
# Define MISSING if not defined so far and test if it is modern enough.
# If it is, set am_missing_run to use it, otherwise, to nothing.
AC_DEFUN([AM_MISSING_HAS_RUN],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
AC_REQUIRE_AUX_FILE([missing])dnl
if test x"${MISSING+set}" != xset; then
MISSING="\${SHELL} '$am_aux_dir/missing'"
fi
# Use eval to expand $SHELL
if eval "$MISSING --is-lightweight"; then
am_missing_run="$MISSING "
else
am_missing_run=
AC_MSG_WARN(['missing' script is too old or missing])
fi
])
# Helper functions for option handling. -*- Autoconf -*-
# Copyright (C) 2001-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_MANGLE_OPTION(NAME)
# -----------------------
AC_DEFUN([_AM_MANGLE_OPTION],
[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
# _AM_SET_OPTION(NAME)
# --------------------
# Set option NAME. Presently that only means defining a flag for this option.
AC_DEFUN([_AM_SET_OPTION],
[m4_define(_AM_MANGLE_OPTION([$1]), [1])])
# _AM_SET_OPTIONS(OPTIONS)
# ------------------------
# OPTIONS is a space-separated list of Automake options.
AC_DEFUN([_AM_SET_OPTIONS],
[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
# -------------------------------------------
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
AC_DEFUN([_AM_IF_OPTION],
[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
# Copyright (C) 1999-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_PROG_CC_C_O
# ---------------
# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC
# to automatically call this.
AC_DEFUN([_AM_PROG_CC_C_O],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
AC_REQUIRE_AUX_FILE([compile])dnl
AC_LANG_PUSH([C])dnl
AC_CACHE_CHECK(
[whether $CC understands -c and -o together],
[am_cv_prog_cc_c_o],
[AC_LANG_CONFTEST([AC_LANG_PROGRAM([])])
# Make sure it works both with $CC and with simple cc.
# Following AC_PROG_CC_C_O, we do the test twice because some
# compilers refuse to overwrite an existing .o file with -o,
# though they will create one.
am_cv_prog_cc_c_o=yes
for am_i in 1 2; do
if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \
&& test -f conftest2.$ac_objext; then
: OK
else
am_cv_prog_cc_c_o=no
break
fi
done
# aligned with autoconf, so not including core; see bug#72225.
rm -f -r a.out a.exe b.out conftest.$ac_ext conftest.$ac_objext \
conftest.dSYM conftest1.$ac_ext conftest1.$ac_objext conftest1.dSYM \
conftest2.$ac_ext conftest2.$ac_objext conftest2.dSYM
unset am_i])
if test "$am_cv_prog_cc_c_o" != yes; then
# Losing compiler, so override with the script.
# FIXME: It is wrong to rewrite CC.
# But if we don't then we get into trouble of one sort or another.
# A longer-term fix would be to have automake use am__CC in this case,
# and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
CC="$am_aux_dir/compile $CC"
fi
AC_LANG_POP([C])])
# For backward compatibility.
AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
# Copyright (C) 2022-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_PROG_RM_F
# ---------------
# Check whether 'rm -f' without any arguments works.
# https://bugs.gnu.org/10828
AC_DEFUN([_AM_PROG_RM_F],
[am__rm_f_notfound=
AS_IF([(rm -f && rm -fr && rm -rf) 2>/dev/null], [], [am__rm_f_notfound='""'])
AC_SUBST(am__rm_f_notfound)
])
# Copyright (C) 2001-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_RUN_LOG(COMMAND)
# -------------------
# Run COMMAND, save the exit status in ac_status, and log it.
# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
AC_DEFUN([AM_RUN_LOG],
[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
(exit $ac_status); }])
# Check to make sure that the build environment is sane. -*- Autoconf -*-
# Copyright (C) 1996-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_SLEEP_FRACTIONAL_SECONDS
# ----------------------------
AC_DEFUN([_AM_SLEEP_FRACTIONAL_SECONDS], [dnl
AC_CACHE_CHECK([whether sleep supports fractional seconds],
am_cv_sleep_fractional_seconds, [dnl
AS_IF([sleep 0.001 2>/dev/null], [am_cv_sleep_fractional_seconds=yes],
[am_cv_sleep_fractional_seconds=no])
])])
# _AM_FILESYSTEM_TIMESTAMP_RESOLUTION
# -----------------------------------
# Determine the filesystem's resolution for file modification
# timestamps. The coarsest we know of is FAT, with a resolution
# of only two seconds, even with the most recent "exFAT" extensions.
# The finest (e.g. ext4 with large inodes, XFS, ZFS) is one
# nanosecond, matching clock_gettime. However, it is probably not
# possible to delay execution of a shell script for less than one
# millisecond, due to process creation overhead and scheduling
# granularity, so we don't check for anything finer than that. (See below.)
AC_DEFUN([_AM_FILESYSTEM_TIMESTAMP_RESOLUTION], [dnl
AC_REQUIRE([_AM_SLEEP_FRACTIONAL_SECONDS])
AC_CACHE_CHECK([filesystem timestamp resolution],
am_cv_filesystem_timestamp_resolution, [dnl
# Default to the worst case.
am_cv_filesystem_timestamp_resolution=2
# Only try to go finer than 1 sec if sleep can do it.
# Don't try 1 sec, because if 0.01 sec and 0.1 sec don't work,
# - 1 sec is not much of a win compared to 2 sec, and
# - it takes 2 seconds to perform the test whether 1 sec works.
#
# Instead, just use the default 2s on platforms that have 1s resolution,
# accept the extra 1s delay when using $sleep in the Automake tests, in
# exchange for not incurring the 2s delay for running the test for all
# packages.
#
am_try_resolutions=
if test "$am_cv_sleep_fractional_seconds" = yes; then
# Even a millisecond often causes a bunch of false positives,
# so just try a hundredth of a second. The time saved between .001 and
# .01 is not terribly consequential.
am_try_resolutions="0.01 0.1 $am_try_resolutions"
fi
# In order to catch current-generation FAT out, we must *modify* files
# that already exist; the *creation* timestamp is finer. Use names
# that make ls -t sort them differently when they have equal
# timestamps than when they have distinct timestamps, keeping
# in mind that ls -t prints the *newest* file first.
rm -f conftest.ts?
: > conftest.ts1
: > conftest.ts2
: > conftest.ts3
# Make sure ls -t actually works. Do 'set' in a subshell so we don't
# clobber the current shell's arguments. (Outer-level square brackets
# are removed by m4; they're present so that m4 does not expand
# ; be careful, easy to get confused.)
if (
set X `[ls -t conftest.ts[12]]` &&
{
test "$[]*" != "X conftest.ts1 conftest.ts2" ||
test "$[]*" != "X conftest.ts2 conftest.ts1";
}
); then :; else
# If neither matched, then we have a broken ls. This can happen
# if, for instance, CONFIG_SHELL is bash and it inherits a
# broken ls alias from the environment. This has actually
# happened. Such a system could not be considered "sane".
_AS_ECHO_UNQUOTED(
["Bad output from ls -t: \"`[ls -t conftest.ts[12]]`\""],
[AS_MESSAGE_LOG_FD])
AC_MSG_FAILURE([ls -t produces unexpected output.
Make sure there is not a broken ls alias in your environment.])
fi
for am_try_res in $am_try_resolutions; do
# Any one fine-grained sleep might happen to cross the boundary
# between two values of a coarser actual resolution, but if we do
# two fine-grained sleeps in a row, at least one of them will fall
# entirely within a coarse interval.
echo alpha > conftest.ts1
sleep $am_try_res
echo beta > conftest.ts2
sleep $am_try_res
echo gamma > conftest.ts3
# We assume that 'ls -t' will make use of high-resolution
# timestamps if the operating system supports them at all.
if (set X `ls -t conftest.ts?` &&
test "$[]2" = conftest.ts3 &&
test "$[]3" = conftest.ts2 &&
test "$[]4" = conftest.ts1); then
#
# Ok, ls -t worked. If we're at a resolution of 1 second, we're done,
# because we don't need to test make.
make_ok=true
if test $am_try_res != 1; then
# But if we've succeeded so far with a subsecond resolution, we
# have one more thing to check: make. It can happen that
# everything else supports the subsecond mtimes, but make doesn't;
# notably on macOS, which ships make 3.81 from 2006 (the last one
# released under GPLv2). https://bugs.gnu.org/68808
#
# We test $MAKE if it is defined in the environment, else "make".
# It might get overridden later, but our hope is that in practice
# it does not matter: it is the system "make" which is (by far)
# the most likely to be broken, whereas if the user overrides it,
# probably they did so with a better, or at least not worse, make.
# https://lists.gnu.org/archive/html/automake/2024-06/msg00051.html
#
# Create a Makefile (real tab character here):
rm -f conftest.mk
echo 'conftest.ts1: conftest.ts2' >conftest.mk
echo ' touch conftest.ts2' >>conftest.mk
#
# Now, running
# touch conftest.ts1; touch conftest.ts2; make
# should touch ts1 because ts2 is newer. This could happen by luck,
# but most often, it will fail if make's support is insufficient. So
# test for several consecutive successes.
#
# (We reuse conftest.ts[12] because we still want to modify existing
# files, not create new ones, per above.)
n=0
make=${MAKE-make}
until test $n -eq 3; do
echo one > conftest.ts1
sleep $am_try_res
echo two > conftest.ts2 # ts2 should now be newer than ts1
if $make -f conftest.mk | grep 'up to date' >/dev/null; then
make_ok=false
break # out of $n loop
fi
n=`expr $n + 1`
done
fi
#
if $make_ok; then
# Everything we know to check worked out, so call this resolution good.
am_cv_filesystem_timestamp_resolution=$am_try_res
break # out of $am_try_res loop
fi
# Otherwise, we'll go on to check the next resolution.
fi
done
rm -f conftest.ts?
# (end _am_filesystem_timestamp_resolution)
])])
# AM_SANITY_CHECK
# ---------------
AC_DEFUN([AM_SANITY_CHECK],
[AC_REQUIRE([_AM_FILESYSTEM_TIMESTAMP_RESOLUTION])
# This check should not be cached, as it may vary across builds of
# different projects.
AC_MSG_CHECKING([whether build environment is sane])
# Reject unsafe characters in $srcdir or the absolute working directory
# name. Accept space and tab only in the latter.
am_lf='
'
case `pwd` in
*[[\\\"\#\$\&\'\`$am_lf]]*)
AC_MSG_RESULT([no])
AC_MSG_ERROR([unsafe absolute working directory name]);;
esac
case $srcdir in
*[[\\\"\#\$\&\'\`$am_lf\ \ ]]*)
AC_MSG_RESULT([no])
AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);;
esac
# Do 'set' in a subshell so we don't clobber the current shell's
# arguments. Must try -L first in case configure is actually a
# symlink; some systems play weird games with the mod time of symlinks
# (eg FreeBSD returns the mod time of the symlink's containing
# directory).
am_build_env_is_sane=no
am_has_slept=no
rm -f conftest.file
for am_try in 1 2; do
echo "timestamp, slept: $am_has_slept" > conftest.file
if (
set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
if test "$[]*" = "X"; then
# -L didn't work.
set X `ls -t "$srcdir/configure" conftest.file`
fi
test "$[]2" = conftest.file
); then
am_build_env_is_sane=yes
break
fi
# Just in case.
sleep "$am_cv_filesystem_timestamp_resolution"
am_has_slept=yes
done
AC_MSG_RESULT([$am_build_env_is_sane])
if test "$am_build_env_is_sane" = no; then
AC_MSG_ERROR([newly created file is older than distributed files!
Check your system clock])
fi
# If we didn't sleep, we still need to ensure time stamps of config.status and
# generated files are strictly newer.
am_sleep_pid=
AS_IF([test -e conftest.file || grep 'slept: no' conftest.file >/dev/null 2>&1],, [dnl
( sleep "$am_cv_filesystem_timestamp_resolution" ) &
am_sleep_pid=$!
])
AC_CONFIG_COMMANDS_PRE(
[AC_MSG_CHECKING([that generated files are newer than configure])
if test -n "$am_sleep_pid"; then
# Hide warnings about reused PIDs.
wait $am_sleep_pid 2>/dev/null
fi
AC_MSG_RESULT([done])])
rm -f conftest.file
])
# Copyright (C) 2009-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_SILENT_RULES
# ----------------
# Enable less verbose build rules support.
AC_DEFUN([_AM_SILENT_RULES],
[AM_DEFAULT_VERBOSITY=1
AC_ARG_ENABLE([silent-rules], [dnl
AS_HELP_STRING(
[--enable-silent-rules],
[less verbose build output (undo: "make V=1")])
AS_HELP_STRING(
[--disable-silent-rules],
[verbose build output (undo: "make V=0")])dnl
])
dnl
dnl A few 'make' implementations (e.g., NonStop OS and NextStep)
dnl do not support nested variable expansions.
dnl See automake bug#9928 and bug#10237.
am_make=${MAKE-make}
AC_CACHE_CHECK([whether $am_make supports nested variables],
[am_cv_make_support_nested_variables],
[if AS_ECHO([['TRUE=$(BAR$(V))
BAR0=false
BAR1=true
V=1
am__doit:
@$(TRUE)
.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then
am_cv_make_support_nested_variables=yes
else
am_cv_make_support_nested_variables=no
fi])
AC_SUBST([AM_V])dnl
AM_SUBST_NOTMAKE([AM_V])dnl
AC_SUBST([AM_DEFAULT_V])dnl
AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl
AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
AM_BACKSLASH='\'
AC_SUBST([AM_BACKSLASH])dnl
_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
dnl Delay evaluation of AM_DEFAULT_VERBOSITY to the end to allow multiple calls
dnl to AM_SILENT_RULES to change the default value.
AC_CONFIG_COMMANDS_PRE([dnl
case $enable_silent_rules in @%:@ (((
yes) AM_DEFAULT_VERBOSITY=0;;
no) AM_DEFAULT_VERBOSITY=1;;
esac
if test $am_cv_make_support_nested_variables = yes; then
dnl Using '$V' instead of '$(V)' breaks IRIX make.
AM_V='$(V)'
AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
else
AM_V=$AM_DEFAULT_VERBOSITY
AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
fi
])dnl
])
# AM_SILENT_RULES([DEFAULT])
# --------------------------
# Set the default verbosity level to DEFAULT ("yes" being less verbose, "no" or
# empty being verbose).
AC_DEFUN([AM_SILENT_RULES],
[AC_REQUIRE([_AM_SILENT_RULES])
AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1])m4_newline
dnl We intentionally force a newline after the assignment, since a) nothing
dnl good can come of more text following, and b) that was the behavior
dnl before 1.17. See https://bugs.gnu.org/72267.
])
# Copyright (C) 2001-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# AM_PROG_INSTALL_STRIP
# ---------------------
# One issue with vendor 'install' (even GNU) is that you can't
# specify the program used to strip binaries. This is especially
# annoying in cross-compiling environments, where the build's strip
# is unlikely to handle the host's binaries.
# Fortunately install-sh will honor a STRIPPROG variable, so we
# always use install-sh in "make install-strip", and initialize
# STRIPPROG with the value of the STRIP variable (set by the user).
AC_DEFUN([AM_PROG_INSTALL_STRIP],
[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
# Installed binaries are usually stripped using 'strip' when the user
# run "make install-strip". However 'strip' might not be the right
# tool to use in cross-compilation environments, therefore Automake
# will honor the 'STRIP' environment variable to overrule this program.
dnl Don't test for $cross_compiling = yes, because it might be 'maybe'.
if test "$cross_compiling" != no; then
AC_CHECK_TOOL([STRIP], [strip], :)
fi
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
AC_SUBST([INSTALL_STRIP_PROGRAM])])
# Copyright (C) 2006-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_SUBST_NOTMAKE(VARIABLE)
# ---------------------------
# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
# This macro is traced by Automake.
AC_DEFUN([_AM_SUBST_NOTMAKE])
# AM_SUBST_NOTMAKE(VARIABLE)
# --------------------------
# Public sister of _AM_SUBST_NOTMAKE.
AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
# Check how to create a tarball. -*- Autoconf -*-
# Copyright (C) 2004-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_PROG_TAR(FORMAT)
# --------------------
# Check how to create a tarball in format FORMAT.
# FORMAT should be one of 'v7', 'ustar', or 'pax'.
#
# Substitute a variable $(am__tar) that is a command
# writing to stdout a FORMAT-tarball containing the directory
# $tardir.
# tardir=directory && $(am__tar) > result.tar
#
# Substitute a variable $(am__untar) that extract such
# a tarball read from stdin.
# $(am__untar) < result.tar
#
AC_DEFUN([_AM_PROG_TAR],
[# Always define AMTAR for backward compatibility. Yes, it's still used
# in the wild :-( We should find a proper way to deprecate it ...
AC_SUBST([AMTAR], ['$${TAR-tar}'])
# We'll loop over all known methods to create a tar archive until one works.
_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
m4_if([$1], [v7],
[am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
[m4_case([$1],
[ustar],
[# The POSIX 1988 'ustar' format is defined with fixed-size fields.
# There is notably a 21 bits limit for the UID and the GID. In fact,
# the 'pax' utility can hang on bigger UID/GID (see automake bug#8343
# and bug#13588).
am_max_uid=2097151 # 2^21 - 1
am_max_gid=$am_max_uid
# The $UID and $GID variables are not portable, so we need to resort
# to the POSIX-mandated id(1) utility. Errors in the 'id' calls
# below are definitely unexpected, so allow the users to see them
# (that is, avoid stderr redirection).
am_uid=`id -u || echo unknown`
am_gid=`id -g || echo unknown`
AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format])
if test x$am_uid = xunknown; then
AC_MSG_WARN([ancient id detected; assuming current UID is ok, but dist-ustar might not work])
elif test $am_uid -le $am_max_uid; then
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
_am_tools=none
fi
AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format])
if test x$gm_gid = xunknown; then
AC_MSG_WARN([ancient id detected; assuming current GID is ok, but dist-ustar might not work])
elif test $am_gid -le $am_max_gid; then
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
_am_tools=none
fi],
[pax],
[],
[m4_fatal([Unknown tar format])])
AC_MSG_CHECKING([how to create a $1 tar archive])
# Go ahead even if we have the value already cached. We do so because we
# need to set the values for the 'am__tar' and 'am__untar' variables.
_am_tools=${am_cv_prog_tar_$1-$_am_tools}
for _am_tool in $_am_tools; do
case $_am_tool in
gnutar)
for _am_tar in tar gnutar gtar; do
AM_RUN_LOG([$_am_tar --version]) && break
done
am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
am__untar="$_am_tar -xf -"
;;
plaintar)
# Must skip GNU tar: if it does not support --format= it doesn't create
# ustar tarball either.
(tar --version) >/dev/null 2>&1 && continue
am__tar='tar chf - "$$tardir"'
am__tar_='tar chf - "$tardir"'
am__untar='tar xf -'
;;
pax)
am__tar='pax -L -x $1 -w "$$tardir"'
am__tar_='pax -L -x $1 -w "$tardir"'
am__untar='pax -r'
;;
cpio)
am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
am__untar='cpio -i -H $1 -d'
;;
none)
am__tar=false
am__tar_=false
am__untar=false
;;
esac
# If the value was cached, stop now. We just wanted to have am__tar
# and am__untar set.
test -n "${am_cv_prog_tar_$1}" && break
# tar/untar a dummy directory, and stop if the command works.
rm -rf conftest.dir
mkdir conftest.dir
echo GrepMe > conftest.dir/file
AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
rm -rf conftest.dir
if test -s conftest.tar; then
AM_RUN_LOG([$am__untar /dev/null 2>&1 && break
fi
done
rm -rf conftest.dir
AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
AC_MSG_RESULT([$am_cv_prog_tar_$1])])
AC_SUBST([am__tar])
AC_SUBST([am__untar])
]) # _AM_PROG_TAR
# Copyright (C) 2022-2025 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_PROG_XARGS_N
# ----------------
# Check whether 'xargs -n' works. It should work everywhere, so the fallback
# is not optimized at all as we never expect to use it.
AC_DEFUN([_AM_PROG_XARGS_N],
[AC_CACHE_CHECK([xargs -n works], am_cv_xargs_n_works, [dnl
AS_IF([test "`echo 1 2 3 | xargs -n2 echo`" = "1 2
3"], [am_cv_xargs_n_works=yes], [am_cv_xargs_n_works=no])])
AS_IF([test "$am_cv_xargs_n_works" = yes], [am__xargs_n='xargs -n'], [dnl
am__xargs_n='am__xargs_n () { shift; sed "s/ /\\n/g" | while read am__xargs_n_arg; do "$@" "$am__xargs_n_arg"; done; }'
])dnl
AC_SUBST(am__xargs_n)
])
m4_include([build/find_apr.m4])
m4_include([build/find_apu.m4])
m4_include([build/find_curl.m4])
m4_include([build/find_lua.m4])
m4_include([build/find_pcre.m4])
m4_include([build/find_pcre2.m4])
m4_include([build/find_ssdeep.m4])
m4_include([build/find_xml.m4])
m4_include([build/find_yajl.m4])
m4_include([build/libtool.m4])
m4_include([build/ltoptions.m4])
m4_include([build/ltsugar.m4])
m4_include([build/ltversion.m4])
m4_include([build/lt~obsolete.m4])
modsecurity-apache-2.9.13/alp2/ 0000775 0000000 0000000 00000000000 15174655764 0016256 5 ustar 00root root 0000000 0000000 modsecurity-apache-2.9.13/alp2/Makefile.am 0000664 0000000 0000000 00000000345 15174655764 0020314 0 ustar 00root root 0000000 0000000 lib_LTLIBRARIES = libalp2.la
include_HEADERS = alp2.h \
alp2_pp.h
libalp2_la_SOURCES = alp2.c \
alp2_pp.c
libalp2_la_CFLAGS = @APR_CFLAGS@ \
@APU_CFLAGS@
libalp2_la_LDFLAGS = @APR_LDFLAGS@ \
@APU_LDFLAGS@
modsecurity-apache-2.9.13/alp2/Makefile.in 0000664 0000000 0000000 00000065130 15174655764 0020330 0 ustar 00root root 0000000 0000000 # Makefile.in generated by automake 1.18.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2025 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__is_gnu_make = { \
if test -z '$(MAKELEVEL)'; then \
false; \
elif test -n '$(MAKE_HOST)'; then \
true; \
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
true; \
else \
false; \
fi; \
}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
am__rm_f = rm -f $(am__rm_f_notfound)
am__rm_rf = rm -rf $(am__rm_f_notfound)
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
subdir = alp2
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/build/find_apr.m4 \
$(top_srcdir)/build/find_apu.m4 \
$(top_srcdir)/build/find_curl.m4 \
$(top_srcdir)/build/find_lua.m4 \
$(top_srcdir)/build/find_pcre.m4 \
$(top_srcdir)/build/find_pcre2.m4 \
$(top_srcdir)/build/find_ssdeep.m4 \
$(top_srcdir)/build/find_xml.m4 \
$(top_srcdir)/build/find_yajl.m4 \
$(top_srcdir)/build/libtool.m4 \
$(top_srcdir)/build/ltoptions.m4 \
$(top_srcdir)/build/ltsugar.m4 \
$(top_srcdir)/build/ltversion.m4 \
$(top_srcdir)/build/lt~obsolete.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \
$(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/apache2/modsecurity_config_auto.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
am__install_max = 40
am__nobase_strip_setup = \
srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
am__nobase_strip = \
for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
am__nobase_list = $(am__nobase_strip_setup); \
for p in $$list; do echo "$$p $$p"; done | \
sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
$(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
if (++n[$$2] == $(am__install_max)) \
{ print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
END { for (dir in files) print dir, files[dir] }'
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
am__uninstall_files_from_dir = { \
{ test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
|| { echo " ( cd '$$dir' && rm -f" $$files ")"; \
$(am__cd) "$$dir" && echo $$files | $(am__xargs_n) 40 $(am__rm_f); }; \
}
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
libalp2_la_LIBADD =
am_libalp2_la_OBJECTS = libalp2_la-alp2.lo libalp2_la-alp2_pp.lo
libalp2_la_OBJECTS = $(am_libalp2_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
am__v_lt_1 =
libalp2_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(libalp2_la_CFLAGS) \
$(CFLAGS) $(libalp2_la_LDFLAGS) $(LDFLAGS) -o $@
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/apache2
depcomp = $(SHELL) $(top_srcdir)/build/depcomp
am__maybe_remake_depfiles = depfiles
am__depfiles_remade = ./$(DEPDIR)/libalp2_la-alp2.Plo \
./$(DEPDIR)/libalp2_la-alp2_pp.Plo
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
$(AM_CFLAGS) $(CFLAGS)
AM_V_CC = $(am__v_CC_@AM_V@)
am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
am__v_CC_0 = @echo " CC " $@;
am__v_CC_1 =
CCLD = $(CC)
LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(libalp2_la_SOURCES)
DIST_SOURCES = $(libalp2_la_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
HEADERS = $(include_HEADERS)
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/build/depcomp
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
APR_CFLAGS = @APR_CFLAGS@
APR_CONFIG = @APR_CONFIG@
APR_CPPFLAGS = @APR_CPPFLAGS@
APR_INCLUDEDIR = @APR_INCLUDEDIR@
APR_LDADD = @APR_LDADD@
APR_LDFLAGS = @APR_LDFLAGS@
APR_LINKLD = @APR_LINKLD@
APR_VERSION = @APR_VERSION@
APU_CFLAGS = @APU_CFLAGS@
APU_CONFIG = @APU_CONFIG@
APU_INCLUDEDIR = @APU_INCLUDEDIR@
APU_LDADD = @APU_LDADD@
APU_LDFLAGS = @APU_LDFLAGS@
APU_LINKLD = @APU_LINKLD@
APU_VERSION = @APU_VERSION@
APXS = @APXS@
APXS_BINDIR = @APXS_BINDIR@
APXS_CC = @APXS_CC@
APXS_CFLAGS = @APXS_CFLAGS@
APXS_EXTRA_CFLAGS = @APXS_EXTRA_CFLAGS@
APXS_HTTPD = @APXS_HTTPD@
APXS_INCLUDEDIR = @APXS_INCLUDEDIR@
APXS_INCLUDES = @APXS_INCLUDES@
APXS_LDFLAGS = @APXS_LDFLAGS@
APXS_LIBDIR = @APXS_LIBDIR@
APXS_LIBEXECDIR = @APXS_LIBEXECDIR@
APXS_LIBS = @APXS_LIBS@
APXS_LIBTOOL = @APXS_LIBTOOL@
APXS_MODULES = @APXS_MODULES@
APXS_PROGNAME = @APXS_PROGNAME@
APXS_SBINDIR = @APXS_SBINDIR@
APXS_WRAPPER = @APXS_WRAPPER@
AR = @AR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CSCOPE = @CSCOPE@
CTAGS = @CTAGS@
CURL_CFLAGS = @CURL_CFLAGS@
CURL_CONFIG = @CURL_CONFIG@
CURL_CPPFLAGS = @CURL_CPPFLAGS@
CURL_LDADD = @CURL_LDADD@
CURL_LDFLAGS = @CURL_LDFLAGS@
CURL_USES_GNUTLS = @CURL_USES_GNUTLS@
CURL_VERSION = @CURL_VERSION@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DOXYGEN = @DOXYGEN@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
ENV_CMD = @ENV_CMD@
ETAGS = @ETAGS@
EXEEXT = @EXEEXT@
EXTRA_CFLAGS = @EXTRA_CFLAGS@
FGREP = @FGREP@
FILECMD = @FILECMD@
GREP = @GREP@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
LIBXML2_CONFIG = @LIBXML2_CONFIG@
LIBXML2_CPPFLAGS = @LIBXML2_CPPFLAGS@
LIBXML2_LDADD = @LIBXML2_LDADD@
LIBXML2_LDFLAGS = @LIBXML2_LDFLAGS@
LIBXML2_VERSION = @LIBXML2_VERSION@
LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
LUA_CFLAGS = @LUA_CFLAGS@
LUA_LDADD = @LUA_LDADD@
LUA_LDFLAGS = @LUA_LDFLAGS@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
MODSEC_APXS_EXTRA_CFLAGS = @MODSEC_APXS_EXTRA_CFLAGS@
MODSEC_EXTRA_CFLAGS = @MODSEC_EXTRA_CFLAGS@
MSC_BASE_DIR = @MSC_BASE_DIR@
MSC_PKGBASE_DIR = @MSC_PKGBASE_DIR@
MSC_REGRESSION_CONF_DIR = @MSC_REGRESSION_CONF_DIR@
MSC_REGRESSION_DIR = @MSC_REGRESSION_DIR@
MSC_REGRESSION_DOCROOT_DIR = @MSC_REGRESSION_DOCROOT_DIR@
MSC_REGRESSION_LOGS_DIR = @MSC_REGRESSION_LOGS_DIR@
MSC_REGRESSION_SERVERROOT_DIR = @MSC_REGRESSION_SERVERROOT_DIR@
MSC_TEST_DIR = @MSC_TEST_DIR@
NM = @NM@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PCRE2_CFLAGS = @PCRE2_CFLAGS@
PCRE2_CONFIG = @PCRE2_CONFIG@
PCRE2_CPPFLAGS = @PCRE2_CPPFLAGS@
PCRE2_LDADD = @PCRE2_LDADD@
PCRE2_LDFLAGS = @PCRE2_LDFLAGS@
PCRE2_VERSION = @PCRE2_VERSION@
PCRE_CFLAGS = @PCRE_CFLAGS@
PCRE_CONFIG = @PCRE_CONFIG@
PCRE_CPPFLAGS = @PCRE_CPPFLAGS@
PCRE_LDADD = @PCRE_LDADD@
PCRE_LDFLAGS = @PCRE_LDFLAGS@
PCRE_LD_PATH = @PCRE_LD_PATH@
PCRE_VERSION = @PCRE_VERSION@
PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
RANLIB = @RANLIB@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
SSDEEP_CFLAGS = @SSDEEP_CFLAGS@
SSDEEP_LDADD = @SSDEEP_LDADD@
SSDEEP_LDFLAGS = @SSDEEP_LDFLAGS@
STRIP = @STRIP@
TOPLEVEL_SUBDIRS = @TOPLEVEL_SUBDIRS@
VERSION = @VERSION@
YAJL_CFLAGS = @YAJL_CFLAGS@
YAJL_LDADD = @YAJL_LDADD@
YAJL_LDFLAGS = @YAJL_LDFLAGS@
YAJL_LIBS = @YAJL_LIBS@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__rm_f_notfound = @am__rm_f_notfound@
am__tar = @am__tar@
am__untar = @am__untar@
am__xargs_n = @am__xargs_n@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
runstatedir = @runstatedir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
lib_LTLIBRARIES = libalp2.la
include_HEADERS = alp2.h \
alp2_pp.h
libalp2_la_SOURCES = alp2.c \
alp2_pp.c
libalp2_la_CFLAGS = @APR_CFLAGS@ \
@APU_CFLAGS@
libalp2_la_LDFLAGS = @APR_LDFLAGS@ \
@APU_LDFLAGS@
all: all-am
.SUFFIXES:
.SUFFIXES: .c .lo .o .obj
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign alp2/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign alp2/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
list2="$$list2 $$p"; \
else :; fi; \
done; \
test -z "$$list2" || { \
echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
$(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
uninstall-libLTLIBRARIES:
@$(NORMAL_UNINSTALL)
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
for p in $$list; do \
$(am__strip_dir) \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
done
clean-libLTLIBRARIES:
-$(am__rm_f) $(lib_LTLIBRARIES)
@list='$(lib_LTLIBRARIES)'; \
locs=`for p in $$list; do echo $$p; done | \
sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
sort -u`; \
echo rm -f $${locs}; \
$(am__rm_f) $${locs}
libalp2.la: $(libalp2_la_OBJECTS) $(libalp2_la_DEPENDENCIES) $(EXTRA_libalp2_la_DEPENDENCIES)
$(AM_V_CCLD)$(libalp2_la_LINK) -rpath $(libdir) $(libalp2_la_OBJECTS) $(libalp2_la_LIBADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libalp2_la-alp2.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libalp2_la-alp2_pp.Plo@am__quote@ # am--include-marker
$(am__depfiles_remade):
@$(MKDIR_P) $(@D)
@: >>$@
am--depfiles: $(am__depfiles_remade)
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
libalp2_la-alp2.lo: alp2.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libalp2_la_CFLAGS) $(CFLAGS) -MT libalp2_la-alp2.lo -MD -MP -MF $(DEPDIR)/libalp2_la-alp2.Tpo -c -o libalp2_la-alp2.lo `test -f 'alp2.c' || echo '$(srcdir)/'`alp2.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libalp2_la-alp2.Tpo $(DEPDIR)/libalp2_la-alp2.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='alp2.c' object='libalp2_la-alp2.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libalp2_la_CFLAGS) $(CFLAGS) -c -o libalp2_la-alp2.lo `test -f 'alp2.c' || echo '$(srcdir)/'`alp2.c
libalp2_la-alp2_pp.lo: alp2_pp.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libalp2_la_CFLAGS) $(CFLAGS) -MT libalp2_la-alp2_pp.lo -MD -MP -MF $(DEPDIR)/libalp2_la-alp2_pp.Tpo -c -o libalp2_la-alp2_pp.lo `test -f 'alp2_pp.c' || echo '$(srcdir)/'`alp2_pp.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libalp2_la-alp2_pp.Tpo $(DEPDIR)/libalp2_la-alp2_pp.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='alp2_pp.c' object='libalp2_la-alp2_pp.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libalp2_la_CFLAGS) $(CFLAGS) -c -o libalp2_la-alp2_pp.lo `test -f 'alp2_pp.c' || echo '$(srcdir)/'`alp2_pp.c
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
install-includeHEADERS: $(include_HEADERS)
@$(NORMAL_INSTALL)
@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
$(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
done | $(am__base_list) | \
while read files; do \
echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
$(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
done
uninstall-includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-am
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-am
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscopelist: cscopelist-am
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) distdir-am
distdir-am: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-am
all-am: Makefile $(LTLIBRARIES) $(HEADERS)
installdirs:
for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
distclean-generic:
-$(am__rm_f) $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
mostlyclean-am
distclean: distclean-am
-rm -f ./$(DEPDIR)/libalp2_la-alp2.Plo
-rm -f ./$(DEPDIR)/libalp2_la-alp2_pp.Plo
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am: install-includeHEADERS
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am: install-libLTLIBRARIES
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man:
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libalp2_la-alp2.Plo
-rm -f ./$(DEPDIR)/libalp2_la-alp2_pp.Plo
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
mostlyclean-libtool
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES
.MAKE: install-am install-strip
.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
clean-generic clean-libLTLIBRARIES clean-libtool cscopelist-am \
ctags ctags-am distclean distclean-compile distclean-generic \
distclean-libtool distclean-tags distdir dvi dvi-am html \
html-am info info-am install install-am install-data \
install-data-am install-dvi install-dvi-am install-exec \
install-exec-am install-html install-html-am \
install-includeHEADERS install-info install-info-am \
install-libLTLIBRARIES install-man install-pdf install-pdf-am \
install-ps install-ps-am install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
tags tags-am uninstall uninstall-am uninstall-includeHEADERS \
uninstall-libLTLIBRARIES
.PRECIOUS: Makefile
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
# Tell GNU make to disable its built-in pattern rules.
%:: %,v
%:: RCS/%,v
%:: RCS/%
%:: s.%
%:: SCCS/s.%
modsecurity-apache-2.9.13/alp2/alp2.c 0000775 0000000 0000000 00000115213 15174655764 0017266 0 ustar 00root root 0000000 0000000 /*
* 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
#include
#include
#include
#include "alp2.h"
#ifdef DEBUG
#define alp_debug(...) fprintf(stderr, __VA_ARGS__)
#else
#define alp_debug(...)
#endif /* DEBUG */
/**
* Add one error to the audit log entry.
*/
static void add_error(alp2_t *alp, int is_fatal, const char *text, ...)
{
char *str = NULL;
va_list ap;
if (is_fatal) {
alp->parse_error = 1;
}
va_start(ap, text);
str = apr_pvsprintf(alp->auditlog->mp, text, ap);
va_end(ap);
*(char **)apr_array_push(alp->errors) = str;
}
/**
* Parse the Response-Body-Transformed trailer header.
*/
static int handle_part_H_parse_ResponseTFN(alp2_t *alp, const char *s)
{
char *capture = NULL;
int ovector[33];
int rc;
// TODO This header is optional, but is not allowed to appear more than once.
return 1;
}
/**
* Parse the Action trailer header.
*/
static int handle_part_H_parse_Action(alp2_t *alp, const char *s)
{
char *capture = NULL;
int ovector[33];
int rc;
// TODO This header is optional, but is not allowed to appear more than once.
alp->auditlog->was_intercepted = 1;
rc = pcre_exec(alp->trailer_action_pattern, NULL, s, strlen(s), 0, 0, ovector, 30);
if (rc < 0) {
add_error(alp, 1, "Part H: Failed to parse Action header");
return -1;
}
capture = apr_pstrmemdup(alp->auditlog->mp, s + ovector[2 * 1],
ovector[2 * 1 + 1] - ovector[2 * 1]);
alp->auditlog->intercept_phase = atoi(capture);
return 1;
}
/**
* Convert two hexadecimal characters into a character.
*/
static uint8_t x2c(uint8_t *what)
{
register uint8_t digit;
digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
digit *= 16;
digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
return digit;
}
/**
* Remove a layer of encoding from a string. This function needs to be used
* for every piece of data ModSecurity encoded for a message.
*/
static int remove_slashes(uint8_t *s)
{
uint8_t *d = s;
while(*s != '\0') {
if ((*s == '\\')&&(*(s + 1) != '\0')) {
s++;
switch(*s) {
case 'b' :
*d = '\b';
break;
case 'n' :
*d = '\n';
break;
case 'r' :
*d = '\r';
break;
case 't' :
*d = '\t';
break;
case 'v' :
*d = '\v';
break;
case '\\' :
*d = '\\';
break;
case '"' :
*d = '"';
break;
case 'x' :
if ( (*(s + 1) != '\0')
&&(*(s + 2) != '\0')
&&(isxdigit(*(s + 1)))
&&(isxdigit(*(s + 2)))
) {
*d = x2c(s + 1);
s += 2;
}
else {
/* Invalid encoding. */
return -1;
}
break;
default :
/* Invalid encoding. */
return -1;
break;
}
}
else {
*d = *s;
}
s++;
d++;
}
*d = '\0';
return 1;
}
/**
* Process one (ModSecurity message) meta-data fragment.
*/
static int handle_part_H_parse_Message_meta(alp2_t *alp, alp2_msg_t *message,
const char *l, const char *string_start, const char *string_end)
{
const char *value;
// XXX if ((*string_start != '"')||(*string_end != '"')||(string_end <= string_start)) {
if (string_end <= string_start) {
add_error(alp, 1, "Part H: Invalid handle_part_H_parse_Message_meta invocation");
return -1;
}
if ((*string_start != '"')||(*string_end != '"')) {
value = apr_pstrndup(alp->auditlog->mp, string_start, (string_end - string_start) + 1);
}
else {
value = apr_pstrndup(alp->auditlog->mp, string_start + 1, (string_end - string_start - 1));
}
if (value == NULL) {
return -1;
}
if (remove_slashes((uint8_t *)value) < 0) {
add_error(alp, 1, "Part H: Invalid encoding in meta-data fragment");
return -1;
}
/* Target ( at THE_TARGET) */
if (strncmp(l, "at ", 3) == 0) {
if (message->target != NULL) {
add_error(alp, 1, "Part H: Already seen target");
return -1;
}
message->target = value;
return 1;
}
/* id */
if (strncmp(l, "id ", 3) == 0) {
if (message->id != NULL) {
add_error(alp, 1, "Part H: Already seen meta-data: id");
return -1;
}
message->id = value;
return 1;
}
/* rev */
if (strncmp(l, "rev ", 4) == 0) {
if (message->rev != NULL) {
add_error(alp, 1, "Part H: Already seen meta-data: rev");
return -1;
}
message->rev = value;
return 1;
}
/* msg */
if (strncmp(l, "msg ", 4) == 0) {
if (message->msg != NULL) {
add_error(alp, 1, "Part H: Already seen meta-data: msg");
return -1;
}
message->msg = value;
return 1;
}
/* data */
if (strncmp(l, "data ", 4) == 0) {
if (message->data != NULL) {
add_error(alp, 1, "Part H: Already seen meta-data: data");
return -1;
}
message->data = value;
return 1;
}
/* file */
if (strncmp(l, "file ", 5) == 0) {
if (message->file != NULL) {
add_error(alp, 1, "Part H: Already seen meta-data: file");
return -1;
}
message->file = value;
return 1;
}
/* line */
if (strncmp(l, "line ", 5) == 0) {
if (message->file_line != (unsigned long)-1) {
add_error(alp, 1, "Part H: Already seen meta-data: line");
return -1;
}
// TODO Validate.
message->file_line = atoi(value);
return 1;
}
/* tag */
if (strncmp(l, "tag ", 4) == 0) {
*(char **)apr_array_push(message->tags) = (char *)value;
return 1;
}
/* severity */
if (strncmp(l, "severity ", 9) == 0) {
if ( (strcmp(value, "0") == 0)
||(strcasecmp(value, "EMERGENCY") == 0))
{
message->severity = 0;
return 1;
}
if ( (strcmp(value, "1") == 0)
||(strcasecmp(value, "ALERT") == 0))
{
message->severity = 1;
return 1;
}
if ( (strcmp(value, "2") == 0)
||(strcasecmp(value, "CRITICAL") == 0))
{
message->severity = 2;
return 1;
}
if ( (strcmp(value, "3") == 0)
||(strcasecmp(value, "ERROR") == 0))
{
message->severity = 3;
return 1;
}
if ( (strcmp(value, "4") == 0)
||(strcasecmp(value, "WARNING") == 0))
{
message->severity = 4;
return 1;
}
if ( (strcmp(value, "5") == 0)
||(strcasecmp(value, "NOTICE") == 0))
{
message->severity = 5;
return 1;
}
if ( (strcmp(value, "6") == 0)
||(strcasecmp(value, "INFO") == 0))
{
message->severity = 6;
return 1;
}
if ( (strcmp(value, "7") == 0)
||(strcasecmp(value, "DEBUG") == 0))
{
message->severity = 7;
return 1;
}
add_error(alp, 1, "Part H: Invalid severity value: %s", value);
return -1;
}
/* offset */
if (strncmp(l, "offset ", 7) == 0) {
if (message->offset != (size_t)-1) {
/* Already seen "offset". */
add_error(alp, 1, "Part H: Already seen fragment offset");
return -1;
}
// TODO Validate.
message->offset = atoi(value);
return 1;
}
/* Ignore unknown meta-data information. */
return 0;
}
/**
* Parse the Message trailer header. More than one such header
* can exist in an audit log, and each represents one ModSecurity
* message.
*/
static int handle_part_H_parse_Message(alp2_t *alp, const char *s)
{
alp2_msg_t *message = NULL;
char *l = (char *)(s + strlen(s) - 1);
char *engine_message_start = (char *)s;
char *engine_message_end = NULL;
char *string_start = NULL, *string_end = NULL;
char *fragment_end = NULL;
char *tptr;
int in_string;
int done;
/* Create one new message structure. */
message = apr_pcalloc(alp->auditlog->mp, sizeof(alp2_msg_t));
if (message == NULL) {
return -1;
}
message->file_line = (unsigned long)-1;
message->offset = (size_t)-1;
message->severity = -1;
message->warning = 0;
if (strncasecmp("warning. ", s, 9) == 0) {
message->warning = 1;
engine_message_start += 9;
}
message->tags = apr_array_make(alp->auditlog->mp, 4, sizeof(const char *));
/* Start at the end of the message and go back identifying
* the meta-data fragments as we go. Stop when we find the
* end of the engine message.
*/
done = in_string = 0;
while ((l >= s)&&(!done)) {
if (in_string == 0) {
/* Outside string. */
// TODO Make sure this is not an escaped char
switch(*l) {
case ' ' :
/* Do nothing. */
break;
case ']' :
fragment_end = l;
break;
case '[' :
if (fragment_end) {
/* Found one meta-data fragment. */
// TODO This parser implementation allows for invalid
// meta-data fragments to be accepted. It would be
// nice to check the format of the fragment (e.g.
// by matching it against a regular expression
// pattern) before we accept any data. At this point
// l points to the first byte of the fragment, and
// fragment_end to the last.
handle_part_H_parse_Message_meta(alp, message,
l + 1, string_start, string_end);
fragment_end = NULL;
string_start = NULL;
string_end = NULL;
}
break;
case '"' :
/* Found the end of a string. */
in_string = 1;
string_end = l;
break;
default :
if (!fragment_end) {
/* There are no more meta-data fragments. */
engine_message_end = l;
done = 1;
}
break;
}
}
else {
/* In string. We are only interested
* in where the string ends.
*/
if ((*l == '"')&&((l - 1) >= s)&&(*(l - 1) != '\\')) {
in_string = 0;
string_start = l;
}
}
l--;
}
/* Target is between " at " and "." */
tptr = engine_message_start;
while ((tptr = strstr(tptr, " at ")) && (tptr < engine_message_end)) {
char *tend = strchr(tptr, '.');
if ((tend <= engine_message_end) && (tend - tptr > 5)) {
int rc = handle_part_H_parse_Message_meta(alp, message, tptr + 1,
tptr + 4, tend - 1);
if (rc == 1) {
/* Remove the target data from the message */
engine_message_end = tptr;
}
}
break;
}
if (engine_message_end == NULL) {
add_error(alp, 1, "Part H: Failed parsing ModSecurity message: %s", s);
return -1;
}
message->engine_message = apr_pstrndup(alp->auditlog->mp, engine_message_start, (engine_message_end - engine_message_start + 1));
/* Add this message to the audit log. */
*(alp2_msg_t **)apr_array_push(alp->auditlog->messages) = message;
return 1;
}
/**
* Parse the Stopwatch trailer header.
*/
static int handle_part_H_parse_Stopwatch(alp2_t *alp, const char *s)
{
int ovector[33];
int i, rc;
// TODO This header is required (a check for its appearance is made when
// handling the end of an H part), and is not allowed to appear
// more than once.
rc = pcre_exec(alp->trailer_stopwatch_pattern, NULL, s, strlen(s), 0, 0, ovector, 30);
if (rc < 0) {
add_error(alp, 1, "Part H: Failed to parse Stopwatch header");
return -1;
}
/* Loop through the captures. */
for (i = 0; i < rc; i++) {
char *capture = apr_pstrmemdup(alp->auditlog->mp, s + ovector[2 * i],
ovector[2 * i + 1] - ovector[2 * i]);
switch (i) {
case 1 : /* timestamp */
// TODO Validate
alp->auditlog->timestamp = apr_atoi64(capture);
break;
case 2 : /* duration */
// TODO Validate
alp->auditlog->duration = apr_atoi64(capture);
break;
case 3 : /* ignore (group of three further time elements)*/
break;
case 4 : /* t1 */
break;
case 5 : /* t2 */
break;
case 6 : /* t3 */
break;
}
}
return 1;
}
/**
* Parse the WebApp-Info trailer header.
*/
static int handle_part_H_parse_WebAppInfo(alp2_t *alp, const char *s)
{
int ovector[33];
int i, rc;
// TODO This header is optional, but it is not allowed to appear more than once.
rc = pcre_exec(alp->trailer_webappinfo_pattern, NULL, s, strlen(s), 0, 0, ovector, 30);
if (rc < 0) {
add_error(alp, 1, "Part H: Failed to parse WebApp-Info header");
return -1;
}
/* Loop through the captures. */
for (i = 0; i < rc; i++) {
char *capture = apr_pstrmemdup(alp->auditlog->mp, s + ovector[2 * i],
ovector[2 * i + 1] - ovector[2 * i]);
switch (i) {
case 1 : /* application ID */
// TODO Validate
alp->auditlog->application_id = capture;
break;
case 2 : /* session ID */
// TODO Validate
if (strcmp(capture, "-") != 0) {
alp->auditlog->session_id = capture;
}
break;
case 3 : /* user ID */
// TODO Validate
if (strcmp(capture, "-") != 0) {
alp->auditlog->user_id = capture;
}
break;
}
}
return 1;
}
/**
* Handle part H events.
*/
static void handle_part_H(alp2_t *alp, int event_type)
{
/* Part data. */
if (event_type == ALP2_EVENT_PART_DATA) {
char *line = alp2_pp_line_chomp(alp->pp);
/* This part ends with an empty line. */
if (strlen(line) == 0) {
alp->part_data_done = 1;
return;
}
/* Extract the header information. */
{
char *name = NULL, *value = NULL;
int ovector[33];
int i, rc;
/* Header line. */
/* Extract the fields. */
rc = pcre_exec(alp->header_pattern, NULL, line, strlen(line), 0, 0, ovector, 30);
if (rc < 0) {
add_error(alp, 1, "Part H: Failed to parse header: %i", rc);
return;
}
/* Loop through the captures. */
for (i = 0; i < rc; i++) {
char *capture = apr_pstrmemdup(alp->auditlog->mp, line + ovector[2 * i],
ovector[2 * i + 1] - ovector[2 * i]);
switch(i) {
case 1 :
name = capture;
break;
case 2 :
value = capture;
break;
}
}
/* Add header to the table. */
apr_table_addn(alp->auditlog->trailer_headers, name, value);
}
return;
}
/* Part end. */
if (event_type == ALP2_EVENT_PART_END) {
const apr_array_header_t *tarr = apr_table_elts(alp->auditlog->trailer_headers);
apr_table_entry_t *te = NULL;
const char *s = NULL;
int stopwatch = 0;
int rc = 0;
int i;
if ((tarr == NULL) || (tarr->nelts == 0)) {
return;
}
/* Here we are going to extract certain headers and
* parse them to populate the corresponding fields in
* the auditlog structure.
*/
te = (apr_table_entry_t *)tarr->elts;
for (i = 0; i < tarr->nelts; i++) {
const char *key = te[i].key;
const char *val = te[i].val;
if ((key == NULL) || (val == NULL)) {
continue;
}
/* Action: optional */
else if (strcmp("Action", key) == 0) {
rc = handle_part_H_parse_Action(alp, val);
}
/* Message: optional */
else if (strcmp("Message", key) == 0) {
rc = handle_part_H_parse_Message(alp, val);
}
/* Apache-Handler: optional */
else if (strcmp("Apache-Handler", key) == 0) {
rc = 0;
// TODO Only one allowed
alp->auditlog->handler = apr_pstrdup(alp->auditlog->mp, val);
}
/* Producer: optional */
else if (strcmp("Producer", key) == 0) {
rc = 0;
// TODO Only one allowed
alp->auditlog->producer = apr_pstrdup(alp->auditlog->mp, val);
}
/* Server: optional */
else if (strcmp("Server", key) == 0) {
rc = 0;
// TODO Only one allowed
alp->auditlog->server = apr_pstrdup(alp->auditlog->mp, val);
}
/* Response-Body-Transformed: optional */
else if (strcmp("Response-Body-Transformed", key) == 0) {
rc = 0;
// TODO Only one allowed
alp->auditlog->response_tfn = apr_pstrdup(alp->auditlog->mp, val);
}
/* Stopwatch: required */
else if (strcmp("Stopwatch", key) == 0) {
stopwatch = 1;
rc = handle_part_H_parse_Stopwatch(alp, val);
}
/* WebApp-Info: optional */
else if (strcmp("WebApp-Info", key) == 0) {
rc = handle_part_H_parse_WebAppInfo(alp, val);
}
if (rc < 0) {
/* No need to report anything, it's already been reported. */
}
}
if (stopwatch == 0) {
add_error(alp, 1, "Part H: Stopwatch header missing");
}
return;
}
}
/**
* Handle part F events.
*/
static void handle_part_F(alp2_t *alp, int event_type)
{
/* Part data. */
if (event_type == ALP2_EVENT_PART_DATA) {
char *line = alp2_pp_line_chomp(alp->pp);
/* This part ends with an empty line. */
if (strlen(line) == 0) {
alp->part_data_done = 1;
return;
}
/* The first line should be the response line. */
if (alp->part_line_counter == 1) {
int ovector[33];
int i, rc;
/* Response line. */
/* Extract the fields. */
rc = pcre_exec(alp->response_line_pattern, NULL, line, strlen(line), 0, 0, ovector, 30);
if (rc < 0) {
add_error(alp, 1, "Part F: Failed to parse response line: %i", rc);
return;
}
/* Loop through the captures. */
for (i = 0; i < rc; i++) {
char *capture = apr_pstrmemdup(alp->auditlog->mp, line + ovector[2 * i],
ovector[2 * i + 1] - ovector[2 * i]);
switch(i) {
case 1 :
alp->auditlog->response_protocol = capture;
break;
case 2 :
alp->auditlog->response_status = atoi(capture);
break;
case 4 :
alp->auditlog->response_message = capture;
break;
break;
}
}
}
else {
char *name = NULL, *value = NULL;
int ovector[33];
int i, rc;
/* Response header line. */
/* Extract the fields. */
rc = pcre_exec(alp->header_pattern, NULL, line, strlen(line), 0, 0, ovector, 30);
if (rc < 0) {
add_error(alp, 1, "Part F: Failed to parse response header: %i", rc);
return;
}
/* Loop through the captures. */
for (i = 0; i < rc; i++) {
char *capture = apr_pstrmemdup(alp->auditlog->mp, line + ovector[2 * i],
ovector[2 * i + 1] - ovector[2 * i]);
switch(i) {
case 1 :
name = capture;
break;
case 2 :
value = capture;
break;
}
}
/* Add header to the table. */
apr_table_addn(alp->auditlog->response_headers, name, value);
}
return;
}
/* Part end. */
if (event_type == ALP2_EVENT_PART_END) {
/* If any of the response headers need
* special handling, place the code here.
*/
return;
}
}
/**
* Parse the URI. APR-Util does most of the work here.
*/
static int handle_part_B_parse_uri(alp2_t *alp)
{
char *u = (char *)alp->auditlog->request_uri;
apr_uri_t *uri = NULL;
if (( alp->auditlog->request_method == NULL)
||(alp->auditlog->request_uri == NULL))
{
return 0;
}
/* Since this is not a proper URI but a path, handle
* the leading double slash.
*/
while ((u[0] == '/') && (u[1] == '/')) {
u++;
}
uri = apr_pcalloc(alp->auditlog->mp, sizeof(apr_uri_t));
if (strcasecmp(alp->auditlog->request_method, "CONNECT") == 0) {
if (apr_uri_parse_hostinfo(alp->auditlog->mp, u, uri) != APR_SUCCESS) {
add_error(alp, 0, "Info: Failed to parse request URI (hostinfo)");
return -1;
}
}
else {
if (apr_uri_parse(alp->auditlog->mp, u, uri) != APR_SUCCESS) {
add_error(alp, 0, "Info: Failed to parse request URI");
return -1;
}
}
alp->auditlog->parsed_uri = uri;
return 1;
}
/**
* Handle part B events.
*/
static void handle_part_B(alp2_t *alp, int event_type)
{
/* Part data. */
if (event_type == ALP2_EVENT_PART_DATA) {
char *line = alp2_pp_line_chomp(alp->pp);
/* This part ends with an empty line. */
if (strlen(line) == 0) {
alp->part_data_done = 1;
return;
}
/* The first line should be the request line. */
if (alp->part_line_counter == 1) {
int ovector[33];
int i, rc;
/* Request line. */
/* Extract the fields. */
rc = pcre_exec(alp->request_line_pattern, NULL, line, strlen(line), 0, 0, ovector, 30);
if (rc < 0) {
add_error(alp, 1, "Part B: Failed to parse request line: %i", rc);
return;
}
alp->auditlog->request_line_valid = 1;
/* Loop through the captures. */
for (i = 0; i < rc; i++) {
char *capture = apr_pstrmemdup(alp->auditlog->mp, line + ovector[2 * i],
ovector[2 * i + 1] - ovector[2 * i]);
switch(i) {
case 0 :
alp->auditlog->request_line = capture;
break;
case 1 :
alp->auditlog->request_method = capture;
break;
case 2 :
alp->auditlog->request_uri = capture;
if (handle_part_B_parse_uri(alp) != 1) {
// TODO Do we want to do anything on error?
}
break;
case 3 :
alp->auditlog->request_protocol = capture;
break;
}
}
}
else {
char *name = NULL, *value = NULL;
int ovector[33];
int i, rc;
/* Header line. */
/* Extract the fields. */
rc = pcre_exec(alp->header_pattern, NULL, line, strlen(line), 0, 0, ovector, 30);
if (rc < 0) {
add_error(alp, 1, "Part B: Failed to parse request header: %i", rc);
return;
}
/* Loop through the captures. */
for (i = 0; i < rc; i++) {
char *capture = apr_pstrmemdup(alp->auditlog->mp, line + ovector[2 * i],
ovector[2 * i + 1] - ovector[2 * i]);
switch(i) {
case 1 :
name = capture;
break;
case 2 :
value = capture;
break;
}
}
/* ModSecurity 1.9.x adds some requests headers of
* its own, and we don't want them.
*/
if (strncmp(name, "mod_security-", 13) != 0) {
/* Add header to the table. */
apr_table_addn(alp->auditlog->request_headers, name, value);
}
}
return;
}
/* Part end. */
if (event_type == ALP2_EVENT_PART_END) {
/* Determine hostname. */
// TODO I think the right thing to do is use the port numbers
// only when the host itself is a numerical IP.
/* Try the URI first. */
if ( (alp->auditlog->parsed_uri != NULL)
&&(alp->auditlog->parsed_uri->hostname != NULL))
{
if ( (alp->auditlog->parsed_uri->port != 0)
&& (alp->auditlog->parsed_uri->port != 80)
&& (alp->auditlog->parsed_uri->port != 443) )
{
// TODO Do not use the port number if the hostname
// is not numeric.
alp->auditlog->hostname = apr_psprintf(alp->auditlog->mp, "%s:%i",
alp->auditlog->parsed_uri->hostname, alp->auditlog->parsed_uri->port);
}
else {
// TODO Always use the port number if the hostname
// is numeric.
alp->auditlog->hostname = alp->auditlog->parsed_uri->hostname;
}
}
else {
/* Try the Host header. */
char *s = (char *)apr_table_get(alp->auditlog->request_headers, "Host");
if (s != NULL) {
// TODO If the hostname is not numeric, remove the port
// numbers if present.
alp->auditlog->hostname = s;
}
else {
/* Use the destination IP and port. */
alp->auditlog->hostname = apr_psprintf(alp->auditlog->mp, "%s:%i",
alp->auditlog->dst_ip, alp->auditlog->dst_port);
}
}
return;
}
}
/**
* Handle part A events.
*/
static void handle_part_A(alp2_t *alp, int event_type)
{
/* Part data. */
if (event_type == ALP2_EVENT_PART_DATA) {
char *line = alp2_pp_line_chomp(alp->pp);
int ovector[33];
int i, rc;
/* This part can have only one line,
* so we don't expect to be here again.
*/
alp->part_data_done = 1;
/* Extract the fields. */
rc = pcre_exec(alp->part_a_pattern, NULL, line, strlen(line), 0, 0, ovector, 30);
if (rc < 0) {
add_error(alp, 1, "Part A: Parsing failed: %i", rc);
return;
}
/* Loop through the captures. */
for (i = 0; i < rc; i++) {
char *capture = apr_pstrmemdup(alp->auditlog->mp, line + ovector[2 * i],
ovector[2 * i + 1] - ovector[2 * i]);
switch(i) {
case 1 : /* timestamp in Apache format */
/* We don't need it as we use the one from the H part. */
break;
case 2 : /* transaction ID */
alp->auditlog->id = capture;
break;
case 3 : /* source address */
// TODO Validate
alp->auditlog->src_ip = capture;
break;
case 4 : /* source port */
// TODO Validate
alp->auditlog->src_port = atoi(capture);
break;
case 5 : /* destination address */
// TODO Validate
alp->auditlog->dst_ip = capture;
break;
case 6 : /* destinatio port */
// TODO Validate
alp->auditlog->dst_port = atoi(capture);
break;
}
}
return;
}
/* Part end. */
if (event_type == ALP2_EVENT_PART_END) {
/* Place part post-validation here. */
return;
}
}
/**
* Create a new audit log data structure, allocating
* memory from the provided memory pool.
*/
auditlog2_t *alp2_auditlog_create(apr_pool_t *mp)
{
auditlog2_t *al;
/* Create a new memory pool and the
* auditlog structure in it. We will use the
* parent pool of the parser pool, in order to
* ensure the auditlog memory structures survive
* the death of the parser.
*/
al = apr_pcalloc(mp, sizeof(auditlog2_t));
al->mp = mp;
al->request_headers = apr_table_make(al->mp, 20);
al->response_headers = apr_table_make(al->mp, 20);
al->trailer_headers = apr_table_make(al->mp, 20);
al->messages = apr_array_make(al->mp, 10, sizeof(const alp2_msg_t *));
al->intercept_phase = -1;
return al;
}
/**
* Destroy the provided audit log entry.
*/
void alp2_auditlog_destroy(auditlog2_t *al)
{
apr_pool_destroy(al->mp);
}
/**
* Handle ALP2_EVENT_ENTRY_START.
*/
static void handle_entry_start(alp2_t *alp)
{
/* Create a new data structure to hold the entry. */
alp->auditlog = alp2_auditlog_create(alp->pp->current_entry->mp);
alp->auditlog->pp_entry = alp->pp->current_entry;
/* Reset entry flags. */
alp->previous_part_id = 0;
alp->seen_part_h = 0;
alp->parse_error = 0;
alp->errors = apr_array_make(alp->auditlog->mp, 4, sizeof(const char *));
}
/**
* Handle ALP2_EVENT_ENTRY_END.
*/
static void handle_entry_end(alp2_t *alp)
{
if (alp->parse_error) {
/* No need to validate the entry since we've
* previously encountered a problem with it.
*/
}
else {
/* Final entry validation. */
/* Have we seen the H part? (We must have seen the A
* part, otherwise the entry would have begain in
* the first place.
*/
if (alp->seen_part_h == 0) {
add_error(alp, 1, "Entry does not have part H.");
}
}
/* Invoke the upstream callback to handle the entry. */
if (alp->user_callback(alp) == 0) {
alp->done = 1;
}
/* Upstream owns the audit log entry now. */
alp->auditlog = NULL;
}
/**
* Handle ALP2_EVENT_PART_START.
*/
static void handle_part_start(alp2_t *alp)
{
if (alp->parse_error) {
return;
}
/* Reset part flags. */
alp->part_line_counter = 0;
alp->part_data_done = 0;
/* Is this part allowed/expected? */
if (alp->previous_part_id == 0) {
if (alp->pp->current_part->id != 'A') {
add_error(alp, 1, "Expected part A but got %c.", alp->pp->current_part->id);
return;
}
}
/* Invoke the appropriate part handler. */
switch(alp->pp->current_part->id) {
case 'A' :
handle_part_A(alp, ALP2_EVENT_PART_START);
break;
case 'B' :
handle_part_B(alp, ALP2_EVENT_PART_START);
break;
case 'F' :
handle_part_F(alp, ALP2_EVENT_PART_START);
break;
case 'H' :
alp->seen_part_h = 1;
handle_part_H(alp, ALP2_EVENT_PART_START);
break;
default :
/* Ignore unknown part. */
break;
}
}
/*
* Handle ALP2_EVENT_PART_END.
*/
static void handle_part_end(alp2_t *alp)
{
if (alp->parse_error) {
return;
}
/* Invoke the appropriate part handler. */
switch(alp->pp->current_part->id) {
case 'A' :
handle_part_A(alp, ALP2_EVENT_PART_END);
break;
case 'B' :
handle_part_B(alp, ALP2_EVENT_PART_END);
case 'F' :
handle_part_F(alp, ALP2_EVENT_PART_END);
break;
case 'H' :
handle_part_H(alp, ALP2_EVENT_PART_END);
break;
default :
/* Ignore unknown part. */
break;
}
/* Remember the last part processed. */
alp->previous_part_id = alp->pp->current_part->id;
}
/*
* Handle ALP2_EVENT_PART_DATA.
*/
static void handle_part_data(alp2_t *alp)
{
if (alp->parse_error) {
return;
}
alp->part_line_counter++;
if (alp->part_data_done) {
add_error(alp, 1, "Unexpected data for part %c.", alp->pp->current_part->id);
return;
}
/* Invoke the appropriate part handler. */
switch(alp->pp->current_part->id) {
case 'A' :
handle_part_A(alp, ALP2_EVENT_PART_DATA);
break;
case 'B' :
handle_part_B(alp, ALP2_EVENT_PART_DATA);
break;
case 'F' :
handle_part_F(alp, ALP2_EVENT_PART_DATA);
break;
case 'H' :
handle_part_H(alp, ALP2_EVENT_PART_DATA);
break;
default :
/* Ignore unknown part. */
break;
}
}
/**
* This function handles callbacks from
* the lower-level (part) parser.
*/
static int alp2_callback(alp2_pp_t *pp, int event_type)
{
alp2_t *alp = (alp2_t *)pp->user_data;
/* Choose where to dispatch the event based
* on the event type.
*/
switch(event_type) {
case ALP2_EVENT_ENTRY_START :
handle_entry_start(alp);
break;
case ALP2_EVENT_ENTRY_END :
handle_entry_end(alp);
break;
case ALP2_EVENT_PART_START :
handle_part_start(alp);
break;
case ALP2_EVENT_PART_END :
handle_part_end(alp);
break;
case ALP2_EVENT_PART_DATA :
handle_part_data(alp);
break;
default :
/* Unexpected event type. */
break;
}
if (alp->done) {
/* Stop parsing. */
return 0;
}
else {
/* Go on. */
return 1;
}
}
/**
* Initialise parser.
*/
// XXX Make callback a typedef
int alp2_create(alp2_t **_alp, apr_pool_t *mp,
void *user_data, int (*user_callback)(alp2_t *alp))
{
alp2_t *alp;
apr_pool_t *new_pool;
const char *errptr = NULL;
int erroffset;
/* We require a callback. */
if (user_callback == NULL) {
return -1;
}
/* We will use our own memory pool. */
apr_pool_create(&new_pool, mp);
alp = apr_pcalloc(mp, sizeof(alp2_t));
*_alp = alp;
alp->mp = new_pool;
alp->user_data = user_data;
alp->user_callback = user_callback;
/* Initialise the part parser. */
alp->pp = apr_pcalloc(mp, sizeof(alp2_pp_t));
if (alp->pp == NULL) return -1;
if (alp2_pp_init(alp->pp, alp, alp2_callback, mp) < 0) {
return -2;
}
/* Compile the patterns we use for parsing. */
/* part A pattern */
if ((alp->part_a_pattern = pcre_compile(
"^\\[(.+)\\] (\\S+) ([.:0-9a-f]+) (\\d+) ([.:0-9a-f]+) (\\d+)$",
PCRE_DOTALL, &errptr, &erroffset, NULL)) == NULL)
{
return -3;
}
/* request line pattern */
if ((alp->request_line_pattern = pcre_compile(
// TODO Needs improving (e.g. to support simplified HTTP/0.9 requests
"^(\\S+) (.*?) (HTTP/\\d\\.\\d)$",
PCRE_DOTALL, &errptr, &erroffset, NULL)) == NULL)
{
return -4;
}
/* header pattern */
if ((alp->header_pattern = pcre_compile(
"^([^:]+):\\s*(.+)$",
PCRE_DOTALL, &errptr, &erroffset, NULL)) == NULL)
{
return -5;
}
/* response line pattern */
if ((alp->response_line_pattern = pcre_compile(
"^(HTTP/\\d\\.\\d) (\\d{3})( (.+))?$",
PCRE_DOTALL, &errptr, &erroffset, NULL)) == NULL)
{
return -6;
}
/* Action trailer header pattern */
if ((alp->trailer_action_pattern = pcre_compile(
"^Intercepted \\(phase (\\d)\\)$",
PCRE_DOTALL, &errptr, &erroffset, NULL)) == NULL)
{
return -7;
}
/* Stopwatch trailer header pattern */
if ((alp->trailer_stopwatch_pattern = pcre_compile(
"^(\\d+) (\\d+)( \\((-|\\d+)\\*? (-|\\d+) (-|\\d+)\\))?$",
PCRE_DOTALL, &errptr, &erroffset, NULL)) == NULL)
{
return -8;
}
/* WebApp-Info trailer header pattern */
if ((alp->trailer_webappinfo_pattern = pcre_compile(
"^\"(.*)\" \"(.*)\" \"(.*)\"$",
PCRE_DOTALL, &errptr, &erroffset, NULL)) == NULL)
{
return -9;
}
return 1;
}
/**
* Process a piece of a stream of audit log entries.
*/
int alp2_process(alp2_t *alp, const char *data, size_t len)
{
alp2_pp_process(alp->pp, data, len);
if (alp->done) {
return 0;
}
else {
return 1;
}
}
/**
* Destroy the parser.
*/
void alp2_destroy(alp2_t *alp)
{
apr_pool_destroy(alp->mp);
}
modsecurity-apache-2.9.13/alp2/alp2.h 0000664 0000000 0000000 00000007377 15174655764 0017303 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#ifndef _ALP2_H_
#define _ALP2_H_
#include
#include
#include "alp2_pp.h"
#include "pcre.h"
/* -- Data structures -- */
typedef struct alp2_msg_t alp2_msg_t;
struct alp2_msg_t {
const char *engine_message;
const char *target;
const char *id;
const char *rev;
const char *msg;
const char *data;
const char *file;
unsigned long file_line;
size_t offset;
int severity;
int warning;
apr_array_header_t *tags;
};
typedef struct auditlog2_t auditlog2_t;
struct auditlog2_t {
apr_pool_t *mp;
/* Transaction data */
const char *id;
apr_time_t timestamp;
unsigned int duration;
const char *src_ip;
unsigned int src_port;
const char *dst_ip;
unsigned int dst_port;
/* Request */
unsigned int request_line_valid;
const char *request_line;
const char *request_method;
const char *request_uri;
apr_uri_t *parsed_uri;
const char *request_protocol;
apr_table_t *request_headers;
/* Determine the hostname: The hostname from the URI is
* used where present, otherwise the value of the Host
* request header is used.
*
* If neither of these two is available we will use the
* combination of the destination IP and port as hostname.
*
* The resulting hostname may have the port attached.
*/
const char *hostname;
/* Response */
const char *response_protocol;
unsigned int response_status;
const char *response_message;
apr_table_t *response_headers;
const char *response_tfn;
/* Other */
apr_table_t *trailer_headers;
unsigned int was_intercepted;
unsigned int intercept_phase; /* -1 if interception did not happen */
const char *producer;
const char *server;
const char *handler;
const char *application_id;
const char *session_id;
const char *user_id;
apr_array_header_t *messages;
alp2_pp_entry_t *pp_entry;
};
typedef struct alp2_t alp2_t;
struct alp2_t {
apr_pool_t *mp;
void *user_data;
int (*user_callback)(alp2_t *alp);
alp2_pp_t *pp;
unsigned int previous_part_id;
unsigned int part_line_counter;
unsigned int part_data_done;
unsigned int seen_part_h;
unsigned int done;
unsigned int parse_error;
apr_array_header_t *errors;
/* Regular expression patterns. */
// TODO All these need reviewing
pcre *part_a_pattern;
pcre *request_line_pattern;
pcre *header_pattern;
pcre *response_line_pattern;
pcre *trailer_action_pattern;
pcre *trailer_stopwatch_pattern;
pcre *trailer_webappinfo_pattern;
auditlog2_t *auditlog;
};
/* Higher-level (user) parser. */
/* NOTE Parser will create a subpool for its own use, but each
* entry will be created in a separate subpool directly
* under the main pool. This allows the created audit log
* entries to survive the death of the parser.
*/
/* -- Functions -- */
int alp2_create(alp2_t **_alp, apr_pool_t *mp,
void *user_data, int (*user_callback)(alp2_t *alp));
int alp2_process(alp2_t *alp, const char *data, size_t len);
void alp2_destroy(alp2_t *alp);
auditlog2_t *alp2_auditlog_create(apr_pool_t *mp);
void alp2_auditlog_destroy(auditlog2_t *al);
#endif
modsecurity-apache-2.9.13/alp2/alp2_pp.c 0000775 0000000 0000000 00000027643 15174655764 0017776 0 ustar 00root root 0000000 0000000 /*
* 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
#include
#include "alp2_pp.h"
/**
* Take the line in the buffer and replace the new line
* at the end with a NUL byte.
*/
char *alp2_pp_line_chomp(alp2_pp_t *pp) {
if (pp->line_pos == 0) {
pp->line_buf[0] = '\0';
}
else {
pp->line_buf[pp->line_pos - 1] = '\0';
}
return &(pp->line_buf[0]);
}
/**
* Look into the line buffer to determine if it
* contains a boundary line.
*/
static int alp2_pp_is_boundary_line(alp2_pp_t *alp_pp) {
char *new_boundary = NULL;
unsigned int id;
size_t i;
/* A boundary line cannot be less than 14 characters long. */
if (alp_pp->line_pos < 15) {
return 0;
}
/* The first two characters must both be dashes. */
if ((alp_pp->line_buf[0] != '-')||(alp_pp->line_buf[1] != '-')) {
return 0;
}
/* Extract the boundary. */
i = 2; /* Start after the second dash. */
while((isxdigit(alp_pp->line_buf[i]))&&(i < alp_pp->line_pos)) {
i++;
}
/* The boundary cannot be shorter than 8 characters. */
if (i - 2 < 8) {
return 0;
}
// TODO Memory leak; use a single parser buffer to avoid per-entry
// allocation from the parser pool.
new_boundary = apr_pstrndup(alp_pp->mp, &(alp_pp->line_buf[2]), i - 2);
/* Check if the rest of the line is valid. */
if ( (i + 5 < alp_pp->line_pos) /* Need at lest 5 more bytes. */
||(alp_pp->line_buf[i + 0] != '-')
||(alp_pp->line_buf[i + 1] < 'A')
||(alp_pp->line_buf[i + 1] > 'Z')
||(alp_pp->line_buf[i + 2] != '-')
||(alp_pp->line_buf[i + 3] != '-')
||(alp_pp->line_buf[i + 4] != '\n') )
{
return 0;
}
id = alp_pp->line_buf[i + 1];
/* Are we in a middle of an entry right now? */
if (alp_pp->current_entry == NULL) {
/* We will accept a new boundary. */
alp_pp->boundary = new_boundary;
return id;
}
else {
/* The boundary must match the boundary of
* the entry we are currently working on.
*/
if (strcmp(alp_pp->current_entry->boundary, new_boundary) != 0) {
return 0;
}
else {
return id;
}
}
return 0;
}
/**
* Process data belonging to a single part.
*/
static void alp2_pp_process_part_data(alp2_pp_t *alp_pp) {
if (alp_pp->current_part == NULL) {
return;
}
/* Invoke part processor. */
if (alp_pp->callback != NULL) {
if (alp_pp->callback(alp_pp, ALP2_EVENT_PART_DATA) == 0) {
alp_pp->done = 1;
}
}
/* Keep track of part size. */
alp_pp->current_part->size += alp_pp->line_pos;
/* Update the MD5 hash calculation. */
if ((alp_pp->current_entry != NULL)&&(alp_pp->line_pos > 0)) {
apr_md5_update(alp_pp->current_entry->md5_context, &alp_pp->line_buf[0], alp_pp->line_pos - 1);
}
}
/**
* Initialise parser.
*/
int alp2_pp_init(alp2_pp_t *alp_pp, void *user_data,
int (*callback)(alp2_pp_t *alp_pp, int event_type), apr_pool_t *mp)
{
memset(alp_pp, 0, sizeof(alp2_pp_t));
alp_pp->user_data = user_data;
alp_pp->callback = callback;
alp_pp->mp = mp; /* Use the parent pool directly. */
/* Set-up the line buffer. */
alp_pp->line_buf = apr_pcalloc(mp, ALP2_MAX_LINE_SIZE);
alp_pp->line_size = ALP2_MAX_LINE_SIZE;
alp_pp->line_has_start = 1;
alp_pp->line_offset = 0;
return 1;
}
/**
* Process data the parser has stored in the input buffer.
*/
static apr_status_t alp2_pp_process_internal(alp2_pp_t *alp_pp) {
/* Do not proceed if we've previously
* encountered a fatal error.
*/
if (alp_pp->errored != 0) {
return ALP2_ERROR_FATAL;
}
if (alp_pp->done) {
return ALP2_DONE;
}
/* Go back straight away if we don't have anything to work with. */
if (alp_pp->input_len == 0) {
return ALP2_NEED_DATA;
}
while (alp_pp->input_pos < alp_pp->input_len) {
int c;
if (alp_pp->done) {
return ALP2_DONE;
}
if (alp_pp->line_pos >= alp_pp->line_size) {
/* Our line buffer is full with the
* line incomplete.
*/
alp2_pp_process_part_data(alp_pp);
/* Reset line buffer . */
alp_pp->line_pos = 0;
alp_pp->line_has_start = 0;
alp_pp->line_offset = alp_pp->current_offset;
}
/* Consume one byte. */
c = alp_pp->input_buf[alp_pp->input_pos];
alp_pp->input_pos++;
alp_pp->current_offset++;
/* Copy the byte to the line buffer. */
alp_pp->line_buf[alp_pp->line_pos] = c;
alp_pp->line_pos++;
/* Are we at the end of a line? */
if (c == '\n') {
if (alp_pp->line_has_start) {
/* We have one complete line. */
int id = alp2_pp_is_boundary_line(alp_pp);
if (id != 0) {
/* The line is a boundary. */
/* Finish with the previous part, if any. */
if (alp_pp->current_part != NULL) {
/* Update the MD5 context. */
apr_md5_update(alp_pp->current_entry->md5_context,
&alp_pp->line_buf[0], alp_pp->line_pos - 1);
/* Event PART_END. */
if (alp_pp->callback != NULL) {
if (alp_pp->callback(alp_pp, ALP2_EVENT_PART_END) == 0) {
alp_pp->done = 1;
}
}
/* Add part to the current entry. */
*(alp2_pp_part_t **)apr_array_push(alp_pp->current_entry->parts)
= alp_pp->current_part;
/* Delete part. */
alp_pp->current_part = NULL;
/* If the new part is part Z, then finish
* with the current entry. */
if (id == 'Z') {
alp_pp->current_entry->size = alp_pp->current_offset - alp_pp->current_entry->offset;
/* Create the MD5 digest. */
apr_md5_final(alp_pp->current_entry->md5_digest,
alp_pp->current_entry->md5_context);
/* Event ENTRY_END. */
if (alp_pp->callback != NULL) {
if (alp_pp->callback(alp_pp, ALP2_EVENT_ENTRY_END) == 0) {
alp_pp->done = 1;
}
}
/* We are about to destroy our only reference to the per-entry
* memory pool, but that is all right since we've passed all
* responsibility for the entry to the higher-level handler.
*/
alp_pp->current_entry = NULL;
}
}
if (id != 'Z') {
/* Create new entry if necessary. */
if (alp_pp->current_entry == NULL) {
apr_pool_t *new_pool = NULL;
/* Create a per-entry pool directly from the main memory pool. */
apr_pool_create(&new_pool, apr_pool_parent_get(alp_pp->mp));
alp_pp->current_entry = apr_pcalloc(new_pool, sizeof(alp2_pp_entry_t));
alp_pp->current_entry->mp = new_pool;
alp_pp->current_entry->offset = alp_pp->line_offset;
alp_pp->current_entry->boundary = apr_pstrdup(new_pool, alp_pp->boundary);
alp_pp->boundary = NULL;
alp_pp->current_entry->parts = apr_array_make(alp_pp->current_entry->mp,
16, sizeof(alp2_pp_part_t *));
/* Initialise the MD5 context. */
alp_pp->current_entry->md5_context = apr_pcalloc(alp_pp->current_entry->mp,
sizeof(apr_md5_ctx_t));
apr_md5_init(alp_pp->current_entry->md5_context);
/* Start calculating the has with the first line. */
apr_md5_update(alp_pp->current_entry->md5_context, &alp_pp->line_buf[0], alp_pp->line_pos - 1);
/* Event ENTRY_START. */
if (alp_pp->callback != NULL) {
if (alp_pp->callback(alp_pp, ALP2_EVENT_ENTRY_START) == 0) {
alp_pp->done = 1;
}
}
}
/* Create new part, but only if we are not
* dealing with an entry terminator.
*/
alp_pp->current_part = apr_pcalloc(alp_pp->current_entry->mp, sizeof(alp2_pp_part_t));
alp_pp->current_part->id = id;
alp_pp->current_part->offset = alp_pp->current_offset;
/* Event PART_START. */
if (alp_pp->callback != NULL) {
if (alp_pp->callback(alp_pp, ALP2_EVENT_PART_START) == 0) {
alp_pp->done = 1;
}
}
}
}
else {
/* The line does not contain a boundary,
* so process it as part data.
*/
alp2_pp_process_part_data(alp_pp);
}
}
else {
/* We have a chunk of data that is not a line, which
* probably means that our buffer was not big enough, either
* because the line (is a line and it) was too big, or because
* we are processing binary data. Ideally the latter.
*/
alp2_pp_process_part_data(alp_pp);
}
/* Reset the line buffer. */
alp_pp->line_pos = 0;
alp_pp->line_has_start = 1;
alp_pp->line_offset = alp_pp->current_offset;
}
}
if (alp_pp->done) {
return ALP2_DONE;
}
else {
return ALP2_NEED_DATA;
}
}
/**
* Process the provided data.
*/
int alp2_pp_process(alp2_pp_t *alp_pp, const char *data, size_t len) {
/* Do not proceed if we've previously
* encountered a fatal error.
*/
if (alp_pp->errored != 0) {
return ALP2_ERROR_FATAL;
}
/* Check that we've used up the existing buffer. */
if (alp_pp->input_pos < alp_pp->input_len) {
return ALP2_ERROR_INCORRECT_STATE;
}
alp_pp->input_buf = data;
alp_pp->input_len = len;
alp_pp->input_pos = 0;
return alp2_pp_process_internal(alp_pp);
}
/**
* Clean-up the parser structures.
*/
void alp2_pp_terminate(alp2_pp_t *alp_pp) {
/* Nothing to do, but we may need
* to do something in the future.
*/
}
modsecurity-apache-2.9.13/alp2/alp2_pp.h 0000664 0000000 0000000 00000005513 15174655764 0017770 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#ifndef _ALP2_LL_H_
#define _ALP2_LL_H_
#include
#include
#include
#include
#include
/* -- Constants -- */
#define ALP2_MAX_LINE_SIZE 16384
#define ALP2_ERROR_INCORRECT_STATE -1001
#define ALP2_ERROR_FATAL -1002
#define ALP2_DONE 0
#define ALP2_NEED_DATA 1
#define ALP2_EVENT_ENTRY_START 1
#define ALP2_EVENT_ENTRY_END 2
#define ALP2_EVENT_PART_START 3
#define ALP2_EVENT_PART_END 4
#define ALP2_EVENT_PART_DATA 5
/* -- Data structures -- */
typedef struct alp2_pp_part_t alp2_pp_part_t;
struct alp2_pp_part_t {
int id; // XXX int here but unsigned int other places???
/* Relative to the beginning of the entry, not
* including the boundary lines. Just content.
*/
size_t offset;
size_t size;
};
typedef struct alp2_pp_entry_t alp2_pp_entry_t;
struct alp2_pp_entry_t {
apr_pool_t *mp;
apr_array_header_t *parts;
/* Entry offset and size include
* the delimiting boundaries.
*/
size_t offset;
size_t size;
const char *boundary;
apr_md5_ctx_t *md5_context;
uint8_t md5_digest[APR_MD5_DIGESTSIZE];
};
typedef struct alp2_pp_t alp2_pp_t;
struct alp2_pp_t {
void *user_data;
int (*callback)(alp2_pp_t *alp, int event_type);
/* The memory pool used during the parsing of
* individual audit log entries. Cleared between
* entries.
*/
apr_pool_t *mp;
unsigned int errored;
unsigned int done;
const char *boundary;
char *last_processed_part;
char *current_line;
/* The number of bytes processed since
* the beginning or the last reset.
*/
size_t current_offset;
const char *input_buf;
size_t input_len;
size_t input_pos;
char *line_buf;
size_t line_pos;
size_t line_size;
unsigned int line_has_start;
size_t line_offset;
alp2_pp_part_t *current_part;
alp2_pp_entry_t *current_entry;
};
/* Functions. */
int alp2_pp_init(alp2_pp_t *alp_pp, void *user_data,
int (*callback)(alp2_pp_t *alp, int event_type), apr_pool_t *mp);
int alp2_pp_process(alp2_pp_t *alp_pp, const char *data, size_t len);
void alp2_pp_terminate(alp2_pp_t *alp_pp);
char *alp2_pp_line_chomp(alp2_pp_t *alp_pp);
#endif
modsecurity-apache-2.9.13/apache2/ 0000775 0000000 0000000 00000000000 15174655764 0016723 5 ustar 00root root 0000000 0000000 modsecurity-apache-2.9.13/apache2/Makefile.am 0000664 0000000 0000000 00000010314 15174655764 0020756 0 ustar 00root root 0000000 0000000 pkglibdir = $(prefix)/lib
pkglib_LTLIBRARIES = mod_security2.la
mod_security2_la_SOURCES = acmp.c \
apache2_config.c \
apache2_io.c \
apache2_util.c \
others/libinjection/src/libinjection_html5.c \
others/libinjection/src/libinjection_sqli.c \
others/libinjection/src/libinjection_xss.c \
mod_security2.c \
modsecurity.c \
msc_status_engine.c \
msc_crypt.c \
msc_geo.c \
msc_gsb.c \
msc_json.c \
msc_logging.c \
msc_lua.c \
msc_multipart.c \
msc_parsers.c \
msc_pcre.c \
msc_release.c \
msc_remote_rules.c \
msc_reqbody.c \
msc_tree.c \
msc_unicode.c \
msc_util.c \
msc_xml.c \
persist_dbm.c \
re_actions.c \
re.c \
re_operators.c \
re_tfns.c \
re_variables.c
mod_security2_la_CFLAGS = @APR_CFLAGS@ \
@APU_CFLAGS@ \
@APXS_CFLAGS@ \
@CURL_CFLAGS@ \
@LIBXML2_CFLAGS@ \
@LUA_CFLAGS@ \
@MODSEC_EXTRA_CFLAGS@ \
@PCRE_CFLAGS@ \
@PCRE2_CFLAGS@ \
@YAJL_CFLAGS@ \
@SSDEEP_CFLAGS@
mod_security2_la_CPPFLAGS = @APR_CPPFLAGS@ \
@CURL_CPPFLAGS@ \
@LIBXML2_CFLAGS@ \
@LIBXML2_CPPFLAGS@ \
@PCRE_CPPFLAGS@ \
@PCRE2_CPPFLAGS@
mod_security2_la_LIBADD = @APR_LDADD@ \
@APU_LDADD@ \
@CURL_LDADD@ \
@LIBXML2_LDADD@ \
@LUA_LDADD@ \
@PCRE_LDADD@ \
@PCRE2_LDADD@ \
@YAJL_LDADD@
if AIX
mod_security2_la_LDFLAGS = -module -avoid-version \
@APR_LDFLAGS@ \
@APU_LDFLAGS@ \
@APXS_LDFLAGS@ \
@CURL_LDFLAGS@ \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
@PCRE2_LDFLAGS@ \
@YAJL_LDFLAGS@ \
@SSDEEP_LDFLAGS@
endif
if HPUX
mod_security2_la_LDFLAGS = -module -avoid-version \
@APR_LDFLAGS@ \
@APU_LDFLAGS@ \
@APXS_LDFLAGS@ \
@CURL_LDFLAGS@ \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
@PCRE2_LDFLAGS@ \
@YAJL_LDFLAGS@ \
@SSDEEP_LDFLAGS@
endif
if MACOSX
mod_security2_la_LDFLAGS = -module -avoid-version \
@APR_LDFLAGS@ \
@APU_LDFLAGS@ \
@APXS_LDFLAGS@ \
@CURL_LDFLAGS@ \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
@PCRE2_LDFLAGS@ \
@YAJL_LDFLAGS@ \
@SSDEEP_LDFLAGS@
endif
if SOLARIS
mod_security2_la_LDFLAGS = -module -avoid-version \
@APR_LDFLAGS@ \
@APU_LDFLAGS@ \
@APXS_LDFLAGS@ \
@CURL_LDFLAGS@ \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
@PCRE2_LDFLAGS@ \
@YAJL_LDFLAGS@ \
@SSDEEP_LDFLAGS@
endif
if LINUX
mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version -R @PCRE_LD_PATH@ \
@APR_LDFLAGS@ \
@APU_LDFLAGS@ \
@APXS_LDFLAGS@ \
@CURL_LDFLAGS@ \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
@PCRE2_LDFLAGS@ \
@YAJL_LDFLAGS@ \
@SSDEEP_LDFLAGS@
endif
if FREEBSD
mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version \
@APR_LDFLAGS@ \
@APU_LDFLAGS@ \
@APXS_LDFLAGS@ \
@CURL_LDFLAGS@ \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
@PCRE2_LDFLAGS@ \
@YAJL_LDFLAGS@ \
@SSDEEP_LDFLAGS@
endif
if OPENBSD
mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version \
@APR_LDFLAGS@ \
@APU_LDFLAGS@ \
@APXS_LDFLAGS@ \
@CURL_LDFLAGS@ \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
@PCRE2_LDFLAGS@ \
@YAJL_LDFLAGS@ \
@SSDEEP_LDFLAGS@
endif
if NETBSD
mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version \
@APR_LDFLAGS@ \
@APU_LDFLAGS@ \
@APXS_LDFLAGS@ \
@CURL_LDFLAGS@ \
@LIBXML2_LDFLAGS@ \
@LUA_LDFLAGS@ \
@PCRE_LDFLAGS@ \
@PCRE2_LDFLAGS@ \
@YAJL_LDFLAGS@ \
@SSDEEP_LDFLAGS@
endif
if LINUX
install-exec-hook: $(pkglib_LTLIBRARIES)
@echo "Removing unused static libraries..."; \
for m in $(pkglib_LTLIBRARIES); do \
base=`echo $$m | sed 's/\..*//'`; \
rm -f $(DESTDIR)$(pkglibdir)/$$base.*a; \
install -D -m444 $(DESTDIR)$(pkglibdir)/$$base.so $(DESTDIR)$(APXS_MODULES)/$$base.so; \
done
else
install-exec-hook: $(pkglib_LTLIBRARIES)
@echo "Removing unused static libraries..."; \
for m in $(pkglib_LTLIBRARIES); do \
base=`echo $$m | sed 's/\..*//'`; \
rm -f $(DESTDIR)$(pkglibdir)/$$base.*a; \
cp -p $(DESTDIR)$(pkglibdir)/$$base.so $(DESTDIR)$(APXS_MODULES); \
done
endif
modsecurity-apache-2.9.13/apache2/Makefile.in 0000664 0000000 0000000 00000232656 15174655764 0021006 0 ustar 00root root 0000000 0000000 # Makefile.in generated by automake 1.18.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2025 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__is_gnu_make = { \
if test -z '$(MAKELEVEL)'; then \
false; \
elif test -n '$(MAKE_HOST)'; then \
true; \
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
true; \
else \
false; \
fi; \
}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
am__rm_f = rm -f $(am__rm_f_notfound)
am__rm_rf = rm -rf $(am__rm_f_notfound)
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
subdir = apache2
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/build/find_apr.m4 \
$(top_srcdir)/build/find_apu.m4 \
$(top_srcdir)/build/find_curl.m4 \
$(top_srcdir)/build/find_lua.m4 \
$(top_srcdir)/build/find_pcre.m4 \
$(top_srcdir)/build/find_pcre2.m4 \
$(top_srcdir)/build/find_ssdeep.m4 \
$(top_srcdir)/build/find_xml.m4 \
$(top_srcdir)/build/find_yajl.m4 \
$(top_srcdir)/build/libtool.m4 \
$(top_srcdir)/build/ltoptions.m4 \
$(top_srcdir)/build/ltsugar.m4 \
$(top_srcdir)/build/ltversion.m4 \
$(top_srcdir)/build/lt~obsolete.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = modsecurity_config_auto.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
am__install_max = 40
am__nobase_strip_setup = \
srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
am__nobase_strip = \
for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
am__nobase_list = $(am__nobase_strip_setup); \
for p in $$list; do echo "$$p $$p"; done | \
sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
$(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
if (++n[$$2] == $(am__install_max)) \
{ print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
END { for (dir in files) print dir, files[dir] }'
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
am__uninstall_files_from_dir = { \
{ test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
|| { echo " ( cd '$$dir' && rm -f" $$files ")"; \
$(am__cd) "$$dir" && echo $$files | $(am__xargs_n) 40 $(am__rm_f); }; \
}
am__installdirs = "$(DESTDIR)$(pkglibdir)"
LTLIBRARIES = $(pkglib_LTLIBRARIES)
mod_security2_la_DEPENDENCIES =
am__dirstamp = $(am__leading_dot)dirstamp
am_mod_security2_la_OBJECTS = mod_security2_la-acmp.lo \
mod_security2_la-apache2_config.lo \
mod_security2_la-apache2_io.lo \
mod_security2_la-apache2_util.lo \
others/libinjection/src/mod_security2_la-libinjection_html5.lo \
others/libinjection/src/mod_security2_la-libinjection_sqli.lo \
others/libinjection/src/mod_security2_la-libinjection_xss.lo \
mod_security2_la-mod_security2.lo \
mod_security2_la-modsecurity.lo \
mod_security2_la-msc_status_engine.lo \
mod_security2_la-msc_crypt.lo mod_security2_la-msc_geo.lo \
mod_security2_la-msc_gsb.lo mod_security2_la-msc_json.lo \
mod_security2_la-msc_logging.lo mod_security2_la-msc_lua.lo \
mod_security2_la-msc_multipart.lo \
mod_security2_la-msc_parsers.lo mod_security2_la-msc_pcre.lo \
mod_security2_la-msc_release.lo \
mod_security2_la-msc_remote_rules.lo \
mod_security2_la-msc_reqbody.lo mod_security2_la-msc_tree.lo \
mod_security2_la-msc_unicode.lo mod_security2_la-msc_util.lo \
mod_security2_la-msc_xml.lo mod_security2_la-persist_dbm.lo \
mod_security2_la-re_actions.lo mod_security2_la-re.lo \
mod_security2_la-re_operators.lo mod_security2_la-re_tfns.lo \
mod_security2_la-re_variables.lo
mod_security2_la_OBJECTS = $(am_mod_security2_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
am__v_lt_1 =
mod_security2_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
$(mod_security2_la_CFLAGS) $(CFLAGS) \
$(mod_security2_la_LDFLAGS) $(LDFLAGS) -o $@
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@
depcomp = $(SHELL) $(top_srcdir)/build/depcomp
am__maybe_remake_depfiles = depfiles
am__depfiles_remade = ./$(DEPDIR)/mod_security2_la-acmp.Plo \
./$(DEPDIR)/mod_security2_la-apache2_config.Plo \
./$(DEPDIR)/mod_security2_la-apache2_io.Plo \
./$(DEPDIR)/mod_security2_la-apache2_util.Plo \
./$(DEPDIR)/mod_security2_la-mod_security2.Plo \
./$(DEPDIR)/mod_security2_la-modsecurity.Plo \
./$(DEPDIR)/mod_security2_la-msc_crypt.Plo \
./$(DEPDIR)/mod_security2_la-msc_geo.Plo \
./$(DEPDIR)/mod_security2_la-msc_gsb.Plo \
./$(DEPDIR)/mod_security2_la-msc_json.Plo \
./$(DEPDIR)/mod_security2_la-msc_logging.Plo \
./$(DEPDIR)/mod_security2_la-msc_lua.Plo \
./$(DEPDIR)/mod_security2_la-msc_multipart.Plo \
./$(DEPDIR)/mod_security2_la-msc_parsers.Plo \
./$(DEPDIR)/mod_security2_la-msc_pcre.Plo \
./$(DEPDIR)/mod_security2_la-msc_release.Plo \
./$(DEPDIR)/mod_security2_la-msc_remote_rules.Plo \
./$(DEPDIR)/mod_security2_la-msc_reqbody.Plo \
./$(DEPDIR)/mod_security2_la-msc_status_engine.Plo \
./$(DEPDIR)/mod_security2_la-msc_tree.Plo \
./$(DEPDIR)/mod_security2_la-msc_unicode.Plo \
./$(DEPDIR)/mod_security2_la-msc_util.Plo \
./$(DEPDIR)/mod_security2_la-msc_xml.Plo \
./$(DEPDIR)/mod_security2_la-persist_dbm.Plo \
./$(DEPDIR)/mod_security2_la-re.Plo \
./$(DEPDIR)/mod_security2_la-re_actions.Plo \
./$(DEPDIR)/mod_security2_la-re_operators.Plo \
./$(DEPDIR)/mod_security2_la-re_tfns.Plo \
./$(DEPDIR)/mod_security2_la-re_variables.Plo \
others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_html5.Plo \
others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_sqli.Plo \
others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_xss.Plo
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
$(AM_CFLAGS) $(CFLAGS)
AM_V_CC = $(am__v_CC_@AM_V@)
am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
am__v_CC_0 = @echo " CC " $@;
am__v_CC_1 =
CCLD = $(CC)
LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
AM_V_CCLD = $(am__v_CCLD_@AM_V@)
am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
am__v_CCLD_0 = @echo " CCLD " $@;
am__v_CCLD_1 =
SOURCES = $(mod_security2_la_SOURCES)
DIST_SOURCES = $(mod_security2_la_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \
modsecurity_config_auto.h.in
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
am__DIST_COMMON = $(srcdir)/Makefile.in \
$(srcdir)/modsecurity_config_auto.h.in \
$(top_srcdir)/build/depcomp
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
pkglibdir = $(prefix)/lib
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
APR_CFLAGS = @APR_CFLAGS@
APR_CONFIG = @APR_CONFIG@
APR_CPPFLAGS = @APR_CPPFLAGS@
APR_INCLUDEDIR = @APR_INCLUDEDIR@
APR_LDADD = @APR_LDADD@
APR_LDFLAGS = @APR_LDFLAGS@
APR_LINKLD = @APR_LINKLD@
APR_VERSION = @APR_VERSION@
APU_CFLAGS = @APU_CFLAGS@
APU_CONFIG = @APU_CONFIG@
APU_INCLUDEDIR = @APU_INCLUDEDIR@
APU_LDADD = @APU_LDADD@
APU_LDFLAGS = @APU_LDFLAGS@
APU_LINKLD = @APU_LINKLD@
APU_VERSION = @APU_VERSION@
APXS = @APXS@
APXS_BINDIR = @APXS_BINDIR@
APXS_CC = @APXS_CC@
APXS_CFLAGS = @APXS_CFLAGS@
APXS_EXTRA_CFLAGS = @APXS_EXTRA_CFLAGS@
APXS_HTTPD = @APXS_HTTPD@
APXS_INCLUDEDIR = @APXS_INCLUDEDIR@
APXS_INCLUDES = @APXS_INCLUDES@
APXS_LDFLAGS = @APXS_LDFLAGS@
APXS_LIBDIR = @APXS_LIBDIR@
APXS_LIBEXECDIR = @APXS_LIBEXECDIR@
APXS_LIBS = @APXS_LIBS@
APXS_LIBTOOL = @APXS_LIBTOOL@
APXS_MODULES = @APXS_MODULES@
APXS_PROGNAME = @APXS_PROGNAME@
APXS_SBINDIR = @APXS_SBINDIR@
APXS_WRAPPER = @APXS_WRAPPER@
AR = @AR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CSCOPE = @CSCOPE@
CTAGS = @CTAGS@
CURL_CFLAGS = @CURL_CFLAGS@
CURL_CONFIG = @CURL_CONFIG@
CURL_CPPFLAGS = @CURL_CPPFLAGS@
CURL_LDADD = @CURL_LDADD@
CURL_LDFLAGS = @CURL_LDFLAGS@
CURL_USES_GNUTLS = @CURL_USES_GNUTLS@
CURL_VERSION = @CURL_VERSION@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DOXYGEN = @DOXYGEN@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
ENV_CMD = @ENV_CMD@
ETAGS = @ETAGS@
EXEEXT = @EXEEXT@
EXTRA_CFLAGS = @EXTRA_CFLAGS@
FGREP = @FGREP@
FILECMD = @FILECMD@
GREP = @GREP@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
LIBXML2_CONFIG = @LIBXML2_CONFIG@
LIBXML2_CPPFLAGS = @LIBXML2_CPPFLAGS@
LIBXML2_LDADD = @LIBXML2_LDADD@
LIBXML2_LDFLAGS = @LIBXML2_LDFLAGS@
LIBXML2_VERSION = @LIBXML2_VERSION@
LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
LUA_CFLAGS = @LUA_CFLAGS@
LUA_LDADD = @LUA_LDADD@
LUA_LDFLAGS = @LUA_LDFLAGS@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
MODSEC_APXS_EXTRA_CFLAGS = @MODSEC_APXS_EXTRA_CFLAGS@
MODSEC_EXTRA_CFLAGS = @MODSEC_EXTRA_CFLAGS@
MSC_BASE_DIR = @MSC_BASE_DIR@
MSC_PKGBASE_DIR = @MSC_PKGBASE_DIR@
MSC_REGRESSION_CONF_DIR = @MSC_REGRESSION_CONF_DIR@
MSC_REGRESSION_DIR = @MSC_REGRESSION_DIR@
MSC_REGRESSION_DOCROOT_DIR = @MSC_REGRESSION_DOCROOT_DIR@
MSC_REGRESSION_LOGS_DIR = @MSC_REGRESSION_LOGS_DIR@
MSC_REGRESSION_SERVERROOT_DIR = @MSC_REGRESSION_SERVERROOT_DIR@
MSC_TEST_DIR = @MSC_TEST_DIR@
NM = @NM@
NMEDIT = @NMEDIT@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PCRE2_CFLAGS = @PCRE2_CFLAGS@
PCRE2_CONFIG = @PCRE2_CONFIG@
PCRE2_CPPFLAGS = @PCRE2_CPPFLAGS@
PCRE2_LDADD = @PCRE2_LDADD@
PCRE2_LDFLAGS = @PCRE2_LDFLAGS@
PCRE2_VERSION = @PCRE2_VERSION@
PCRE_CFLAGS = @PCRE_CFLAGS@
PCRE_CONFIG = @PCRE_CONFIG@
PCRE_CPPFLAGS = @PCRE_CPPFLAGS@
PCRE_LDADD = @PCRE_LDADD@
PCRE_LDFLAGS = @PCRE_LDFLAGS@
PCRE_LD_PATH = @PCRE_LD_PATH@
PCRE_VERSION = @PCRE_VERSION@
PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
RANLIB = @RANLIB@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
SSDEEP_CFLAGS = @SSDEEP_CFLAGS@
SSDEEP_LDADD = @SSDEEP_LDADD@
SSDEEP_LDFLAGS = @SSDEEP_LDFLAGS@
STRIP = @STRIP@
TOPLEVEL_SUBDIRS = @TOPLEVEL_SUBDIRS@
VERSION = @VERSION@
YAJL_CFLAGS = @YAJL_CFLAGS@
YAJL_LDADD = @YAJL_LDADD@
YAJL_LDFLAGS = @YAJL_LDFLAGS@
YAJL_LIBS = @YAJL_LIBS@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__rm_f_notfound = @am__rm_f_notfound@
am__tar = @am__tar@
am__untar = @am__untar@
am__xargs_n = @am__xargs_n@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
runstatedir = @runstatedir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
pkglib_LTLIBRARIES = mod_security2.la
mod_security2_la_SOURCES = acmp.c \
apache2_config.c \
apache2_io.c \
apache2_util.c \
others/libinjection/src/libinjection_html5.c \
others/libinjection/src/libinjection_sqli.c \
others/libinjection/src/libinjection_xss.c \
mod_security2.c \
modsecurity.c \
msc_status_engine.c \
msc_crypt.c \
msc_geo.c \
msc_gsb.c \
msc_json.c \
msc_logging.c \
msc_lua.c \
msc_multipart.c \
msc_parsers.c \
msc_pcre.c \
msc_release.c \
msc_remote_rules.c \
msc_reqbody.c \
msc_tree.c \
msc_unicode.c \
msc_util.c \
msc_xml.c \
persist_dbm.c \
re_actions.c \
re.c \
re_operators.c \
re_tfns.c \
re_variables.c
mod_security2_la_CFLAGS = @APR_CFLAGS@ \
@APU_CFLAGS@ \
@APXS_CFLAGS@ \
@CURL_CFLAGS@ \
@LIBXML2_CFLAGS@ \
@LUA_CFLAGS@ \
@MODSEC_EXTRA_CFLAGS@ \
@PCRE_CFLAGS@ \
@PCRE2_CFLAGS@ \
@YAJL_CFLAGS@ \
@SSDEEP_CFLAGS@
mod_security2_la_CPPFLAGS = @APR_CPPFLAGS@ \
@CURL_CPPFLAGS@ \
@LIBXML2_CFLAGS@ \
@LIBXML2_CPPFLAGS@ \
@PCRE_CPPFLAGS@ \
@PCRE2_CPPFLAGS@
mod_security2_la_LIBADD = @APR_LDADD@ \
@APU_LDADD@ \
@CURL_LDADD@ \
@LIBXML2_LDADD@ \
@LUA_LDADD@ \
@PCRE_LDADD@ \
@PCRE2_LDADD@ \
@YAJL_LDADD@
@AIX_TRUE@mod_security2_la_LDFLAGS = -module -avoid-version \
@AIX_TRUE@ @APR_LDFLAGS@ \
@AIX_TRUE@ @APU_LDFLAGS@ \
@AIX_TRUE@ @APXS_LDFLAGS@ \
@AIX_TRUE@ @CURL_LDFLAGS@ \
@AIX_TRUE@ @LIBXML2_LDFLAGS@ \
@AIX_TRUE@ @LUA_LDFLAGS@ \
@AIX_TRUE@ @PCRE_LDFLAGS@ \
@AIX_TRUE@ @PCRE2_LDFLAGS@ \
@AIX_TRUE@ @YAJL_LDFLAGS@ \
@AIX_TRUE@ @SSDEEP_LDFLAGS@
@FREEBSD_TRUE@mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version \
@FREEBSD_TRUE@ @APR_LDFLAGS@ \
@FREEBSD_TRUE@ @APU_LDFLAGS@ \
@FREEBSD_TRUE@ @APXS_LDFLAGS@ \
@FREEBSD_TRUE@ @CURL_LDFLAGS@ \
@FREEBSD_TRUE@ @LIBXML2_LDFLAGS@ \
@FREEBSD_TRUE@ @LUA_LDFLAGS@ \
@FREEBSD_TRUE@ @PCRE_LDFLAGS@ \
@FREEBSD_TRUE@ @PCRE2_LDFLAGS@ \
@FREEBSD_TRUE@ @YAJL_LDFLAGS@ \
@FREEBSD_TRUE@ @SSDEEP_LDFLAGS@
@HPUX_TRUE@mod_security2_la_LDFLAGS = -module -avoid-version \
@HPUX_TRUE@ @APR_LDFLAGS@ \
@HPUX_TRUE@ @APU_LDFLAGS@ \
@HPUX_TRUE@ @APXS_LDFLAGS@ \
@HPUX_TRUE@ @CURL_LDFLAGS@ \
@HPUX_TRUE@ @LIBXML2_LDFLAGS@ \
@HPUX_TRUE@ @LUA_LDFLAGS@ \
@HPUX_TRUE@ @PCRE_LDFLAGS@ \
@HPUX_TRUE@ @PCRE2_LDFLAGS@ \
@HPUX_TRUE@ @YAJL_LDFLAGS@ \
@HPUX_TRUE@ @SSDEEP_LDFLAGS@
@LINUX_TRUE@mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version -R @PCRE_LD_PATH@ \
@LINUX_TRUE@ @APR_LDFLAGS@ \
@LINUX_TRUE@ @APU_LDFLAGS@ \
@LINUX_TRUE@ @APXS_LDFLAGS@ \
@LINUX_TRUE@ @CURL_LDFLAGS@ \
@LINUX_TRUE@ @LIBXML2_LDFLAGS@ \
@LINUX_TRUE@ @LUA_LDFLAGS@ \
@LINUX_TRUE@ @PCRE_LDFLAGS@ \
@LINUX_TRUE@ @PCRE2_LDFLAGS@ \
@LINUX_TRUE@ @YAJL_LDFLAGS@ \
@LINUX_TRUE@ @SSDEEP_LDFLAGS@
@MACOSX_TRUE@mod_security2_la_LDFLAGS = -module -avoid-version \
@MACOSX_TRUE@ @APR_LDFLAGS@ \
@MACOSX_TRUE@ @APU_LDFLAGS@ \
@MACOSX_TRUE@ @APXS_LDFLAGS@ \
@MACOSX_TRUE@ @CURL_LDFLAGS@ \
@MACOSX_TRUE@ @LIBXML2_LDFLAGS@ \
@MACOSX_TRUE@ @LUA_LDFLAGS@ \
@MACOSX_TRUE@ @PCRE_LDFLAGS@ \
@MACOSX_TRUE@ @PCRE2_LDFLAGS@ \
@MACOSX_TRUE@ @YAJL_LDFLAGS@ \
@MACOSX_TRUE@ @SSDEEP_LDFLAGS@
@NETBSD_TRUE@mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version \
@NETBSD_TRUE@ @APR_LDFLAGS@ \
@NETBSD_TRUE@ @APU_LDFLAGS@ \
@NETBSD_TRUE@ @APXS_LDFLAGS@ \
@NETBSD_TRUE@ @CURL_LDFLAGS@ \
@NETBSD_TRUE@ @LIBXML2_LDFLAGS@ \
@NETBSD_TRUE@ @LUA_LDFLAGS@ \
@NETBSD_TRUE@ @PCRE_LDFLAGS@ \
@NETBSD_TRUE@ @PCRE2_LDFLAGS@ \
@NETBSD_TRUE@ @YAJL_LDFLAGS@ \
@NETBSD_TRUE@ @SSDEEP_LDFLAGS@
@OPENBSD_TRUE@mod_security2_la_LDFLAGS = -no-undefined -module -avoid-version \
@OPENBSD_TRUE@ @APR_LDFLAGS@ \
@OPENBSD_TRUE@ @APU_LDFLAGS@ \
@OPENBSD_TRUE@ @APXS_LDFLAGS@ \
@OPENBSD_TRUE@ @CURL_LDFLAGS@ \
@OPENBSD_TRUE@ @LIBXML2_LDFLAGS@ \
@OPENBSD_TRUE@ @LUA_LDFLAGS@ \
@OPENBSD_TRUE@ @PCRE_LDFLAGS@ \
@OPENBSD_TRUE@ @PCRE2_LDFLAGS@ \
@OPENBSD_TRUE@ @YAJL_LDFLAGS@ \
@OPENBSD_TRUE@ @SSDEEP_LDFLAGS@
@SOLARIS_TRUE@mod_security2_la_LDFLAGS = -module -avoid-version \
@SOLARIS_TRUE@ @APR_LDFLAGS@ \
@SOLARIS_TRUE@ @APU_LDFLAGS@ \
@SOLARIS_TRUE@ @APXS_LDFLAGS@ \
@SOLARIS_TRUE@ @CURL_LDFLAGS@ \
@SOLARIS_TRUE@ @LIBXML2_LDFLAGS@ \
@SOLARIS_TRUE@ @LUA_LDFLAGS@ \
@SOLARIS_TRUE@ @PCRE_LDFLAGS@ \
@SOLARIS_TRUE@ @PCRE2_LDFLAGS@ \
@SOLARIS_TRUE@ @YAJL_LDFLAGS@ \
@SOLARIS_TRUE@ @SSDEEP_LDFLAGS@
all: modsecurity_config_auto.h
$(MAKE) $(AM_MAKEFLAGS) all-am
.SUFFIXES:
.SUFFIXES: .c .lo .o .obj
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign apache2/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign apache2/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
modsecurity_config_auto.h: stamp-h1
@test -f $@ || rm -f stamp-h1
@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1
stamp-h1: $(srcdir)/modsecurity_config_auto.h.in $(top_builddir)/config.status
$(AM_V_at)rm -f stamp-h1
$(AM_V_GEN)cd $(top_builddir) && $(SHELL) ./config.status apache2/modsecurity_config_auto.h
$(srcdir)/modsecurity_config_auto.h.in: $(am__configure_deps)
$(AM_V_GEN)($(am__cd) $(top_srcdir) && $(AUTOHEADER))
$(AM_V_at)rm -f stamp-h1
$(AM_V_at)touch $@
distclean-hdr:
-rm -f modsecurity_config_auto.h stamp-h1
install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES)
@$(NORMAL_INSTALL)
@list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
list2="$$list2 $$p"; \
else :; fi; \
done; \
test -z "$$list2" || { \
echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \
$(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \
}
uninstall-pkglibLTLIBRARIES:
@$(NORMAL_UNINSTALL)
@list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \
for p in $$list; do \
$(am__strip_dir) \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \
done
clean-pkglibLTLIBRARIES:
-$(am__rm_f) $(pkglib_LTLIBRARIES)
@list='$(pkglib_LTLIBRARIES)'; \
locs=`for p in $$list; do echo $$p; done | \
sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
sort -u`; \
echo rm -f $${locs}; \
$(am__rm_f) $${locs}
others/libinjection/src/$(am__dirstamp):
@$(MKDIR_P) others/libinjection/src
@: >>others/libinjection/src/$(am__dirstamp)
others/libinjection/src/$(DEPDIR)/$(am__dirstamp):
@$(MKDIR_P) others/libinjection/src/$(DEPDIR)
@: >>others/libinjection/src/$(DEPDIR)/$(am__dirstamp)
others/libinjection/src/mod_security2_la-libinjection_html5.lo: \
others/libinjection/src/$(am__dirstamp) \
others/libinjection/src/$(DEPDIR)/$(am__dirstamp)
others/libinjection/src/mod_security2_la-libinjection_sqli.lo: \
others/libinjection/src/$(am__dirstamp) \
others/libinjection/src/$(DEPDIR)/$(am__dirstamp)
others/libinjection/src/mod_security2_la-libinjection_xss.lo: \
others/libinjection/src/$(am__dirstamp) \
others/libinjection/src/$(DEPDIR)/$(am__dirstamp)
mod_security2.la: $(mod_security2_la_OBJECTS) $(mod_security2_la_DEPENDENCIES) $(EXTRA_mod_security2_la_DEPENDENCIES)
$(AM_V_CCLD)$(mod_security2_la_LINK) -rpath $(pkglibdir) $(mod_security2_la_OBJECTS) $(mod_security2_la_LIBADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
-rm -f others/libinjection/src/*.$(OBJEXT)
-rm -f others/libinjection/src/*.lo
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-acmp.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-apache2_config.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-apache2_io.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-apache2_util.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-mod_security2.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-modsecurity.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-msc_crypt.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-msc_geo.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-msc_gsb.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-msc_json.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-msc_logging.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-msc_lua.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-msc_multipart.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-msc_parsers.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-msc_pcre.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-msc_release.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-msc_remote_rules.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-msc_reqbody.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-msc_status_engine.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-msc_tree.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-msc_unicode.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-msc_util.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-msc_xml.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-persist_dbm.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-re.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-re_actions.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-re_operators.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-re_tfns.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mod_security2_la-re_variables.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_html5.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_sqli.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_xss.Plo@am__quote@ # am--include-marker
$(am__depfiles_remade):
@$(MKDIR_P) $(@D)
@: >>$@
am--depfiles: $(am__depfiles_remade)
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
mod_security2_la-acmp.lo: acmp.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-acmp.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-acmp.Tpo -c -o mod_security2_la-acmp.lo `test -f 'acmp.c' || echo '$(srcdir)/'`acmp.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-acmp.Tpo $(DEPDIR)/mod_security2_la-acmp.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='acmp.c' object='mod_security2_la-acmp.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-acmp.lo `test -f 'acmp.c' || echo '$(srcdir)/'`acmp.c
mod_security2_la-apache2_config.lo: apache2_config.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-apache2_config.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-apache2_config.Tpo -c -o mod_security2_la-apache2_config.lo `test -f 'apache2_config.c' || echo '$(srcdir)/'`apache2_config.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-apache2_config.Tpo $(DEPDIR)/mod_security2_la-apache2_config.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='apache2_config.c' object='mod_security2_la-apache2_config.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-apache2_config.lo `test -f 'apache2_config.c' || echo '$(srcdir)/'`apache2_config.c
mod_security2_la-apache2_io.lo: apache2_io.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-apache2_io.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-apache2_io.Tpo -c -o mod_security2_la-apache2_io.lo `test -f 'apache2_io.c' || echo '$(srcdir)/'`apache2_io.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-apache2_io.Tpo $(DEPDIR)/mod_security2_la-apache2_io.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='apache2_io.c' object='mod_security2_la-apache2_io.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-apache2_io.lo `test -f 'apache2_io.c' || echo '$(srcdir)/'`apache2_io.c
mod_security2_la-apache2_util.lo: apache2_util.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-apache2_util.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-apache2_util.Tpo -c -o mod_security2_la-apache2_util.lo `test -f 'apache2_util.c' || echo '$(srcdir)/'`apache2_util.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-apache2_util.Tpo $(DEPDIR)/mod_security2_la-apache2_util.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='apache2_util.c' object='mod_security2_la-apache2_util.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-apache2_util.lo `test -f 'apache2_util.c' || echo '$(srcdir)/'`apache2_util.c
others/libinjection/src/mod_security2_la-libinjection_html5.lo: others/libinjection/src/libinjection_html5.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT others/libinjection/src/mod_security2_la-libinjection_html5.lo -MD -MP -MF others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_html5.Tpo -c -o others/libinjection/src/mod_security2_la-libinjection_html5.lo `test -f 'others/libinjection/src/libinjection_html5.c' || echo '$(srcdir)/'`others/libinjection/src/libinjection_html5.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_html5.Tpo others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_html5.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='others/libinjection/src/libinjection_html5.c' object='others/libinjection/src/mod_security2_la-libinjection_html5.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o others/libinjection/src/mod_security2_la-libinjection_html5.lo `test -f 'others/libinjection/src/libinjection_html5.c' || echo '$(srcdir)/'`others/libinjection/src/libinjection_html5.c
others/libinjection/src/mod_security2_la-libinjection_sqli.lo: others/libinjection/src/libinjection_sqli.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT others/libinjection/src/mod_security2_la-libinjection_sqli.lo -MD -MP -MF others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_sqli.Tpo -c -o others/libinjection/src/mod_security2_la-libinjection_sqli.lo `test -f 'others/libinjection/src/libinjection_sqli.c' || echo '$(srcdir)/'`others/libinjection/src/libinjection_sqli.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_sqli.Tpo others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_sqli.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='others/libinjection/src/libinjection_sqli.c' object='others/libinjection/src/mod_security2_la-libinjection_sqli.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o others/libinjection/src/mod_security2_la-libinjection_sqli.lo `test -f 'others/libinjection/src/libinjection_sqli.c' || echo '$(srcdir)/'`others/libinjection/src/libinjection_sqli.c
others/libinjection/src/mod_security2_la-libinjection_xss.lo: others/libinjection/src/libinjection_xss.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT others/libinjection/src/mod_security2_la-libinjection_xss.lo -MD -MP -MF others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_xss.Tpo -c -o others/libinjection/src/mod_security2_la-libinjection_xss.lo `test -f 'others/libinjection/src/libinjection_xss.c' || echo '$(srcdir)/'`others/libinjection/src/libinjection_xss.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_xss.Tpo others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_xss.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='others/libinjection/src/libinjection_xss.c' object='others/libinjection/src/mod_security2_la-libinjection_xss.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o others/libinjection/src/mod_security2_la-libinjection_xss.lo `test -f 'others/libinjection/src/libinjection_xss.c' || echo '$(srcdir)/'`others/libinjection/src/libinjection_xss.c
mod_security2_la-mod_security2.lo: mod_security2.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-mod_security2.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-mod_security2.Tpo -c -o mod_security2_la-mod_security2.lo `test -f 'mod_security2.c' || echo '$(srcdir)/'`mod_security2.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-mod_security2.Tpo $(DEPDIR)/mod_security2_la-mod_security2.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mod_security2.c' object='mod_security2_la-mod_security2.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-mod_security2.lo `test -f 'mod_security2.c' || echo '$(srcdir)/'`mod_security2.c
mod_security2_la-modsecurity.lo: modsecurity.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-modsecurity.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-modsecurity.Tpo -c -o mod_security2_la-modsecurity.lo `test -f 'modsecurity.c' || echo '$(srcdir)/'`modsecurity.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-modsecurity.Tpo $(DEPDIR)/mod_security2_la-modsecurity.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='modsecurity.c' object='mod_security2_la-modsecurity.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-modsecurity.lo `test -f 'modsecurity.c' || echo '$(srcdir)/'`modsecurity.c
mod_security2_la-msc_status_engine.lo: msc_status_engine.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-msc_status_engine.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-msc_status_engine.Tpo -c -o mod_security2_la-msc_status_engine.lo `test -f 'msc_status_engine.c' || echo '$(srcdir)/'`msc_status_engine.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-msc_status_engine.Tpo $(DEPDIR)/mod_security2_la-msc_status_engine.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msc_status_engine.c' object='mod_security2_la-msc_status_engine.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-msc_status_engine.lo `test -f 'msc_status_engine.c' || echo '$(srcdir)/'`msc_status_engine.c
mod_security2_la-msc_crypt.lo: msc_crypt.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-msc_crypt.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-msc_crypt.Tpo -c -o mod_security2_la-msc_crypt.lo `test -f 'msc_crypt.c' || echo '$(srcdir)/'`msc_crypt.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-msc_crypt.Tpo $(DEPDIR)/mod_security2_la-msc_crypt.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msc_crypt.c' object='mod_security2_la-msc_crypt.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-msc_crypt.lo `test -f 'msc_crypt.c' || echo '$(srcdir)/'`msc_crypt.c
mod_security2_la-msc_geo.lo: msc_geo.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-msc_geo.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-msc_geo.Tpo -c -o mod_security2_la-msc_geo.lo `test -f 'msc_geo.c' || echo '$(srcdir)/'`msc_geo.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-msc_geo.Tpo $(DEPDIR)/mod_security2_la-msc_geo.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msc_geo.c' object='mod_security2_la-msc_geo.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-msc_geo.lo `test -f 'msc_geo.c' || echo '$(srcdir)/'`msc_geo.c
mod_security2_la-msc_gsb.lo: msc_gsb.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-msc_gsb.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-msc_gsb.Tpo -c -o mod_security2_la-msc_gsb.lo `test -f 'msc_gsb.c' || echo '$(srcdir)/'`msc_gsb.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-msc_gsb.Tpo $(DEPDIR)/mod_security2_la-msc_gsb.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msc_gsb.c' object='mod_security2_la-msc_gsb.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-msc_gsb.lo `test -f 'msc_gsb.c' || echo '$(srcdir)/'`msc_gsb.c
mod_security2_la-msc_json.lo: msc_json.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-msc_json.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-msc_json.Tpo -c -o mod_security2_la-msc_json.lo `test -f 'msc_json.c' || echo '$(srcdir)/'`msc_json.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-msc_json.Tpo $(DEPDIR)/mod_security2_la-msc_json.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msc_json.c' object='mod_security2_la-msc_json.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-msc_json.lo `test -f 'msc_json.c' || echo '$(srcdir)/'`msc_json.c
mod_security2_la-msc_logging.lo: msc_logging.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-msc_logging.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-msc_logging.Tpo -c -o mod_security2_la-msc_logging.lo `test -f 'msc_logging.c' || echo '$(srcdir)/'`msc_logging.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-msc_logging.Tpo $(DEPDIR)/mod_security2_la-msc_logging.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msc_logging.c' object='mod_security2_la-msc_logging.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-msc_logging.lo `test -f 'msc_logging.c' || echo '$(srcdir)/'`msc_logging.c
mod_security2_la-msc_lua.lo: msc_lua.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-msc_lua.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-msc_lua.Tpo -c -o mod_security2_la-msc_lua.lo `test -f 'msc_lua.c' || echo '$(srcdir)/'`msc_lua.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-msc_lua.Tpo $(DEPDIR)/mod_security2_la-msc_lua.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msc_lua.c' object='mod_security2_la-msc_lua.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-msc_lua.lo `test -f 'msc_lua.c' || echo '$(srcdir)/'`msc_lua.c
mod_security2_la-msc_multipart.lo: msc_multipart.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-msc_multipart.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-msc_multipart.Tpo -c -o mod_security2_la-msc_multipart.lo `test -f 'msc_multipart.c' || echo '$(srcdir)/'`msc_multipart.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-msc_multipart.Tpo $(DEPDIR)/mod_security2_la-msc_multipart.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msc_multipart.c' object='mod_security2_la-msc_multipart.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-msc_multipart.lo `test -f 'msc_multipart.c' || echo '$(srcdir)/'`msc_multipart.c
mod_security2_la-msc_parsers.lo: msc_parsers.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-msc_parsers.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-msc_parsers.Tpo -c -o mod_security2_la-msc_parsers.lo `test -f 'msc_parsers.c' || echo '$(srcdir)/'`msc_parsers.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-msc_parsers.Tpo $(DEPDIR)/mod_security2_la-msc_parsers.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msc_parsers.c' object='mod_security2_la-msc_parsers.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-msc_parsers.lo `test -f 'msc_parsers.c' || echo '$(srcdir)/'`msc_parsers.c
mod_security2_la-msc_pcre.lo: msc_pcre.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-msc_pcre.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-msc_pcre.Tpo -c -o mod_security2_la-msc_pcre.lo `test -f 'msc_pcre.c' || echo '$(srcdir)/'`msc_pcre.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-msc_pcre.Tpo $(DEPDIR)/mod_security2_la-msc_pcre.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msc_pcre.c' object='mod_security2_la-msc_pcre.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-msc_pcre.lo `test -f 'msc_pcre.c' || echo '$(srcdir)/'`msc_pcre.c
mod_security2_la-msc_release.lo: msc_release.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-msc_release.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-msc_release.Tpo -c -o mod_security2_la-msc_release.lo `test -f 'msc_release.c' || echo '$(srcdir)/'`msc_release.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-msc_release.Tpo $(DEPDIR)/mod_security2_la-msc_release.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msc_release.c' object='mod_security2_la-msc_release.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-msc_release.lo `test -f 'msc_release.c' || echo '$(srcdir)/'`msc_release.c
mod_security2_la-msc_remote_rules.lo: msc_remote_rules.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-msc_remote_rules.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-msc_remote_rules.Tpo -c -o mod_security2_la-msc_remote_rules.lo `test -f 'msc_remote_rules.c' || echo '$(srcdir)/'`msc_remote_rules.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-msc_remote_rules.Tpo $(DEPDIR)/mod_security2_la-msc_remote_rules.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msc_remote_rules.c' object='mod_security2_la-msc_remote_rules.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-msc_remote_rules.lo `test -f 'msc_remote_rules.c' || echo '$(srcdir)/'`msc_remote_rules.c
mod_security2_la-msc_reqbody.lo: msc_reqbody.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-msc_reqbody.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-msc_reqbody.Tpo -c -o mod_security2_la-msc_reqbody.lo `test -f 'msc_reqbody.c' || echo '$(srcdir)/'`msc_reqbody.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-msc_reqbody.Tpo $(DEPDIR)/mod_security2_la-msc_reqbody.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msc_reqbody.c' object='mod_security2_la-msc_reqbody.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-msc_reqbody.lo `test -f 'msc_reqbody.c' || echo '$(srcdir)/'`msc_reqbody.c
mod_security2_la-msc_tree.lo: msc_tree.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-msc_tree.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-msc_tree.Tpo -c -o mod_security2_la-msc_tree.lo `test -f 'msc_tree.c' || echo '$(srcdir)/'`msc_tree.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-msc_tree.Tpo $(DEPDIR)/mod_security2_la-msc_tree.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msc_tree.c' object='mod_security2_la-msc_tree.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-msc_tree.lo `test -f 'msc_tree.c' || echo '$(srcdir)/'`msc_tree.c
mod_security2_la-msc_unicode.lo: msc_unicode.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-msc_unicode.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-msc_unicode.Tpo -c -o mod_security2_la-msc_unicode.lo `test -f 'msc_unicode.c' || echo '$(srcdir)/'`msc_unicode.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-msc_unicode.Tpo $(DEPDIR)/mod_security2_la-msc_unicode.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msc_unicode.c' object='mod_security2_la-msc_unicode.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-msc_unicode.lo `test -f 'msc_unicode.c' || echo '$(srcdir)/'`msc_unicode.c
mod_security2_la-msc_util.lo: msc_util.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-msc_util.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-msc_util.Tpo -c -o mod_security2_la-msc_util.lo `test -f 'msc_util.c' || echo '$(srcdir)/'`msc_util.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-msc_util.Tpo $(DEPDIR)/mod_security2_la-msc_util.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msc_util.c' object='mod_security2_la-msc_util.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-msc_util.lo `test -f 'msc_util.c' || echo '$(srcdir)/'`msc_util.c
mod_security2_la-msc_xml.lo: msc_xml.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-msc_xml.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-msc_xml.Tpo -c -o mod_security2_la-msc_xml.lo `test -f 'msc_xml.c' || echo '$(srcdir)/'`msc_xml.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-msc_xml.Tpo $(DEPDIR)/mod_security2_la-msc_xml.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='msc_xml.c' object='mod_security2_la-msc_xml.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-msc_xml.lo `test -f 'msc_xml.c' || echo '$(srcdir)/'`msc_xml.c
mod_security2_la-persist_dbm.lo: persist_dbm.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-persist_dbm.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-persist_dbm.Tpo -c -o mod_security2_la-persist_dbm.lo `test -f 'persist_dbm.c' || echo '$(srcdir)/'`persist_dbm.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-persist_dbm.Tpo $(DEPDIR)/mod_security2_la-persist_dbm.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='persist_dbm.c' object='mod_security2_la-persist_dbm.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-persist_dbm.lo `test -f 'persist_dbm.c' || echo '$(srcdir)/'`persist_dbm.c
mod_security2_la-re_actions.lo: re_actions.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-re_actions.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-re_actions.Tpo -c -o mod_security2_la-re_actions.lo `test -f 're_actions.c' || echo '$(srcdir)/'`re_actions.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-re_actions.Tpo $(DEPDIR)/mod_security2_la-re_actions.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='re_actions.c' object='mod_security2_la-re_actions.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-re_actions.lo `test -f 're_actions.c' || echo '$(srcdir)/'`re_actions.c
mod_security2_la-re.lo: re.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-re.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-re.Tpo -c -o mod_security2_la-re.lo `test -f 're.c' || echo '$(srcdir)/'`re.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-re.Tpo $(DEPDIR)/mod_security2_la-re.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='re.c' object='mod_security2_la-re.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-re.lo `test -f 're.c' || echo '$(srcdir)/'`re.c
mod_security2_la-re_operators.lo: re_operators.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-re_operators.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-re_operators.Tpo -c -o mod_security2_la-re_operators.lo `test -f 're_operators.c' || echo '$(srcdir)/'`re_operators.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-re_operators.Tpo $(DEPDIR)/mod_security2_la-re_operators.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='re_operators.c' object='mod_security2_la-re_operators.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-re_operators.lo `test -f 're_operators.c' || echo '$(srcdir)/'`re_operators.c
mod_security2_la-re_tfns.lo: re_tfns.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-re_tfns.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-re_tfns.Tpo -c -o mod_security2_la-re_tfns.lo `test -f 're_tfns.c' || echo '$(srcdir)/'`re_tfns.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-re_tfns.Tpo $(DEPDIR)/mod_security2_la-re_tfns.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='re_tfns.c' object='mod_security2_la-re_tfns.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-re_tfns.lo `test -f 're_tfns.c' || echo '$(srcdir)/'`re_tfns.c
mod_security2_la-re_variables.lo: re_variables.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -MT mod_security2_la-re_variables.lo -MD -MP -MF $(DEPDIR)/mod_security2_la-re_variables.Tpo -c -o mod_security2_la-re_variables.lo `test -f 're_variables.c' || echo '$(srcdir)/'`re_variables.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mod_security2_la-re_variables.Tpo $(DEPDIR)/mod_security2_la-re_variables.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='re_variables.c' object='mod_security2_la-re_variables.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mod_security2_la_CPPFLAGS) $(CPPFLAGS) $(mod_security2_la_CFLAGS) $(CFLAGS) -c -o mod_security2_la-re_variables.lo `test -f 're_variables.c' || echo '$(srcdir)/'`re_variables.c
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
-rm -rf others/libinjection/src/.libs others/libinjection/src/_libs
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-am
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-am
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscopelist: cscopelist-am
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) distdir-am
distdir-am: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-am
all-am: Makefile $(LTLIBRARIES) modsecurity_config_auto.h
installdirs:
for dir in "$(DESTDIR)$(pkglibdir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
distclean-generic:
-$(am__rm_f) $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES)
-$(am__rm_f) others/libinjection/src/$(DEPDIR)/$(am__dirstamp)
-$(am__rm_f) others/libinjection/src/$(am__dirstamp)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-generic clean-libtool clean-pkglibLTLIBRARIES \
mostlyclean-am
distclean: distclean-am
-rm -f ./$(DEPDIR)/mod_security2_la-acmp.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-apache2_config.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-apache2_io.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-apache2_util.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-mod_security2.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-modsecurity.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_crypt.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_geo.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_gsb.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_json.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_logging.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_lua.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_multipart.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_parsers.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_pcre.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_release.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_remote_rules.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_reqbody.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_status_engine.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_tree.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_unicode.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_util.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_xml.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-persist_dbm.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-re.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-re_actions.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-re_operators.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-re_tfns.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-re_variables.Plo
-rm -f others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_html5.Plo
-rm -f others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_sqli.Plo
-rm -f others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_xss.Plo
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-hdr distclean-tags
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am:
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am: install-pkglibLTLIBRARIES
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) install-exec-hook
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man:
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/mod_security2_la-acmp.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-apache2_config.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-apache2_io.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-apache2_util.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-mod_security2.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-modsecurity.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_crypt.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_geo.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_gsb.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_json.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_logging.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_lua.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_multipart.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_parsers.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_pcre.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_release.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_remote_rules.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_reqbody.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_status_engine.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_tree.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_unicode.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_util.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-msc_xml.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-persist_dbm.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-re.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-re_actions.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-re_operators.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-re_tfns.Plo
-rm -f ./$(DEPDIR)/mod_security2_la-re_variables.Plo
-rm -f others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_html5.Plo
-rm -f others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_sqli.Plo
-rm -f others/libinjection/src/$(DEPDIR)/mod_security2_la-libinjection_xss.Plo
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
mostlyclean-libtool
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am: uninstall-pkglibLTLIBRARIES
.MAKE: all install-am install-exec-am install-strip
.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
clean-generic clean-libtool clean-pkglibLTLIBRARIES \
cscopelist-am ctags ctags-am distclean distclean-compile \
distclean-generic distclean-hdr distclean-libtool \
distclean-tags distdir dvi dvi-am html html-am info info-am \
install install-am install-data install-data-am install-dvi \
install-dvi-am install-exec install-exec-am install-exec-hook \
install-html install-html-am install-info install-info-am \
install-man install-pdf install-pdf-am \
install-pkglibLTLIBRARIES install-ps install-ps-am \
install-strip installcheck installcheck-am installdirs \
maintainer-clean maintainer-clean-generic mostlyclean \
mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \
uninstall-pkglibLTLIBRARIES
.PRECIOUS: Makefile
@LINUX_TRUE@install-exec-hook: $(pkglib_LTLIBRARIES)
@LINUX_TRUE@ @echo "Removing unused static libraries..."; \
@LINUX_TRUE@ for m in $(pkglib_LTLIBRARIES); do \
@LINUX_TRUE@ base=`echo $$m | sed 's/\..*//'`; \
@LINUX_TRUE@ rm -f $(DESTDIR)$(pkglibdir)/$$base.*a; \
@LINUX_TRUE@ install -D -m444 $(DESTDIR)$(pkglibdir)/$$base.so $(DESTDIR)$(APXS_MODULES)/$$base.so; \
@LINUX_TRUE@ done
@LINUX_FALSE@install-exec-hook: $(pkglib_LTLIBRARIES)
@LINUX_FALSE@ @echo "Removing unused static libraries..."; \
@LINUX_FALSE@ for m in $(pkglib_LTLIBRARIES); do \
@LINUX_FALSE@ base=`echo $$m | sed 's/\..*//'`; \
@LINUX_FALSE@ rm -f $(DESTDIR)$(pkglibdir)/$$base.*a; \
@LINUX_FALSE@ cp -p $(DESTDIR)$(pkglibdir)/$$base.so $(DESTDIR)$(APXS_MODULES); \
@LINUX_FALSE@ done
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
# Tell GNU make to disable its built-in pattern rules.
%:: %,v
%:: RCS/%,v
%:: RCS/%
%:: s.%
%:: SCCS/s.%
modsecurity-apache-2.9.13/apache2/Makefile.win 0000664 0000000 0000000 00000005506 15174655764 0021165 0 ustar 00root root 0000000 0000000 ###########################################################################
#
# Usage: NMAKE -f Makefile.win APACHE={httpd installion dir} PCRE={pcre dir} LIBXML2={LibXML2 dir} [ LUA={Lua dir} ]
#
!IF "$(APACHE)" == "" || "$(PCRE)" == "" || "$(LIBXML2)" == "" || "$(CURL)" == ""
!ERROR NMAKE arguments: APACHE=dir PCRE=dir LIBXML2=dir CURL=dir are required to build mod_security2 for Windows
!ENDIF
# Linking libraries
LIBS = $(APACHE)\lib\libhttpd.lib \
$(APACHE)\lib\libapr-1.lib \
$(APACHE)\lib\libaprutil-1.lib \
$(PCRE)\pcre.lib \
$(CURL)\libcurl.lib \
$(LIBXML2)\win32\bin.msvc\libxml2.lib \
Ws2_32.lib \
"iphlpapi.lib"
###########################################################################
###########################################################################
!IF "$(IIS_BUILD)" == "yes"
DEFS=$(DEFS) -DVERSION_IIS
!ENDIF
CC = CL
MT = mt
DEFS = /nologo /O2 /LD /W3 /wd4244 /wd4018 -DWIN32 -DWINNT -Dinline=APR_INLINE -D$(VERSION)
DLL = mod_security2.so
INCLUDES = -I. -I.. \
-I$(CURL)\include -I$(CURL) \
-I$(PCRE)\include -I$(PCRE) \
-I$(LIBXML2)\include \
-I$(APACHE)\include
# Enables support for SecRemoteRules and external resources.
DEFS=$(DEFS) -DWITH_CURL -DWITH_REMOTE_RULES
# Lua is optional
!IF "$(LUA)" != ""
LIBS = $(LIBS) $(LUA)\lua5.1.lib
DEFS=$(DEFS) -DWITH_LUA
INCLUDES = $(INCLUDES) -I$(LUA)\include -I$(LUA) \
!ENDIF
# Yajl/Json is optional
!IF "$(YAJL)" != ""
LIBS = $(LIBS) $(YAJL)\lib\yajl.lib
DEFS=$(DEFS) -DWITH_YAJL
INCLUDES = $(INCLUDES) -I$(YAJL)\include -I$(YAJL) \
!ENDIF
CFLAGS= -MD $(INCLUDES) $(DEFS)
LDFLAGS =
OBJS = mod_security2.obj apache2_config.obj apache2_io.obj apache2_util.obj \
re.obj re_operators.obj re_actions.obj re_tfns.obj re_variables.obj \
msc_logging.obj msc_xml.obj msc_multipart.obj modsecurity.obj \
msc_parsers.obj msc_util.obj msc_pcre.obj persist_dbm.obj \
msc_reqbody.obj msc_geo.obj msc_gsb.obj msc_crypt.obj msc_tree.obj msc_unicode.obj acmp.obj msc_lua.obj \
msc_release.obj \
msc_status_engine.obj \
msc_remote_rules.obj \
msc_json.obj \
others/libinjection/src/libinjection_html5.obj \
others/libinjection/src/libinjection_sqli.obj \
others/libinjection/src/libinjection_xss.obj
all: $(DLL)
dll: $(DLL)
.c.obj:
$(CC) $(CFLAGS) -c $< -Fo$@
.cpp.obj:
$(CC) $(CFLAGS) -c $< -Fo$@
$(DLL): $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -LD $(OBJS) -Fe$(DLL) $(LIBS) /link
IF EXIST $(DLL).manifest $(MT) -manifest $(DLL).manifest -outputresource:$(DLL);2
install: $(DLL)
copy /Y $(DLL) $(APACHE)\modules
clean:
del $(OBJS) $(DLL) *.dll *.lib *.pdb *.idb *.ilk *.exp *.res *.rc *.bin *.manifest
modsecurity-apache-2.9.13/apache2/acmp.c 0000664 0000000 0000000 00000041353 15174655764 0020015 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
/* Aho-Corasick Matching */
#include "acmp.h"
#ifdef ACMP_USE_UTF8
/* UTF support */
#include "utf8tables.h"
#else
/* No UTF support */
#define acmp_utf8_char_t long
#include
#define utf8_lcase(a) apr_tolower(a)
#endif
#include
#include
#include
/*
*******************************************************************************
*******************************************************************************
* Data structures for acmp parser
*/
/**
* One node in trie
*/
typedef struct acmp_node_t acmp_node_t;
typedef struct acmp_btree_node_t acmp_btree_node_t;
struct acmp_node_t {
acmp_utf8_char_t letter;
int is_last;
acmp_callback_t callback;
void *callback_data;
int depth;
acmp_node_t *child;
acmp_node_t *sibling;
acmp_node_t *fail;
acmp_node_t *parent;
acmp_node_t *o_match;
acmp_btree_node_t *btree;
apr_size_t hit_count;
char *text;
char *pattern;
};
struct acmp_btree_node_t {
acmp_utf8_char_t letter;
acmp_btree_node_t *left;
acmp_btree_node_t *right;
acmp_node_t *node;
};
/**
* Data related to parser, not to individual nodes
*/
struct ACMP {
#ifdef ACMP_USE_UTF8
int is_utf8;
#endif
int is_case_sensitive;
apr_pool_t *parent_pool;
apr_pool_t *pool;
int dict_count;
apr_size_t longest_entry;
acmp_node_t *root_node;
const char *data_start;
const char *data_end;
const char *data_pos;
apr_size_t data_len;
apr_size_t *bp_buffer;
apr_size_t bp_buff_len;
acmp_node_t *active_node;
char u8_buff[6];
apr_size_t u8buff_len;
apr_size_t hit_count;
int is_failtree_done;
int is_active;
apr_size_t byte_pos;
apr_size_t char_pos;
};
/*
*******************************************************************************
*******************************************************************************
* Functions for UTF-8 support
*/
#ifdef ACMP_USE_UTF8
/**
* Returns length of utf-8 sequence based on its first byte
*/
static int utf8_seq_len(const char *first_byte) {
return utf8_seq_lengths[(unsigned int)(unsigned char)first_byte[0]];
}
/**
* Returns length of utf8-encoded text
*/
static size_t utf8_strlen(const char *str) {
int len = 0;
const char *c = str;
while (*c != 0) {
c += utf8_seq_len(c);
len++;
}
return len;
}
/**
* Returns ucs code for given utf-8 sequence
*/
static acmp_utf8_char_t utf8_decodechar(const char *str) {
int len = utf8_seq_len(str);
acmp_utf8_char_t ch = 0;
switch (len) {
case 6: ch += (unsigned char)*str++; ch <<= 6;
case 5: ch += (unsigned char)*str++; ch <<= 6;
case 4: ch += (unsigned char)*str++; ch <<= 6;
case 3: ch += (unsigned char)*str++; ch <<= 6;
case 2: ch += (unsigned char)*str++; ch <<= 6;
case 1: ch += (unsigned char)*str++;
}
ch -= utf8_offsets[len - 1];
return ch;
}
/**
* Returns lowercase for given unicode character. Searches through
* utf8_lcase_map table, if it doesn't find the code assumes
* it doesn't have a lowercase variant and returns code itself.
*/
static long utf8_lcase(acmp_utf8_char_t ucs_code) {
long mid, left, right;
left = 1;
right = UTF8_LCASEMAP_LEN * 2 + 1;
while (left <= right) {
mid = (left + right) >> 1;
mid -= (mid % 2); mid++;
if (ucs_code > utf8_lcase_map[mid])
left = mid + 2;
else if (ucs_code < utf8_lcase_map[mid])
right = mid - 2;
else if (ucs_code == utf8_lcase_map[mid])
return utf8_lcase_map[mid - 1];
}
return ucs_code;
}
#endif
/*
*******************************************************************************
*******************************************************************************
* Code for local / static utility functions
*/
/**
* Returns length of given string for parser's encoding
*/
static size_t acmp_strlen(ACMP *parser, const char *str) {
#ifdef ACMP_USE_UTF8
return (parser->is_utf8 == 0) ? strlen(str) : utf8_strlen(str);
#else
return strlen(str);
#endif
}
/**
* Turns string to array of ucs values, depending on parser's encoding
* str - string to convert, doesn't have to be NULL-terminated
* ucs_chars - where to write ucs values
* len - length of input string
*/
static void acmp_strtoucs(ACMP *parser, const char *str, acmp_utf8_char_t *ucs_chars, int len) {
int i;
const char *c = str;
#ifdef ACMP_USE_UTF8
if (parser->is_utf8) {
for (i = 0; i < len; i++) {
*(ucs_chars++) = utf8_decodechar(c);
c += utf8_seq_len(c);
}
} else
#endif
{
for (i = 0; i < len; i++) {
*(ucs_chars++) = *(c++);
}
}
}
/**
* Returns node with given letter, or null if not found
*/
static acmp_node_t *acmp_child_for_code(acmp_node_t *parent_node, acmp_utf8_char_t ucs_code) {
acmp_node_t *node = parent_node->child;
if (node == NULL) return NULL;
for (;;) {
if (node->letter == ucs_code) return node;
node = node->sibling;
if (node == NULL) return NULL;
}
}
/**
* Adds node to parent node, if it is not already there
*/
static void acmp_add_node_to_parent(acmp_node_t *parent, acmp_node_t *child) {
acmp_node_t *node = NULL;
child->parent = parent;
if (parent->child == NULL) {
parent->child = child;
return;
}
node = parent->child;
for (;;) {
if (node == child) return;
if (node->sibling == NULL) {
node->sibling = child;
return;
}
node = node->sibling;
}
}
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 (;;) {
if (bnode == NULL) return NULL;
if (bnode->letter == letter) return bnode->node;
if (bnode->letter > letter) {
bnode = bnode->left;
} else {
bnode = bnode->right;
}
}
}
/**
*
*/
static inline acmp_node_t *acmp_goto(acmp_node_t *node, acmp_utf8_char_t letter) {
return acmp_btree_find(node, letter);
}
/**
* Connects each node with its first fail node that is end of a phrase.
*/
static void acmp_connect_other_matches(ACMP *parser, acmp_node_t *node) {
acmp_node_t *child, *om;
for (child = node->child; child != NULL; child = child->sibling) {
if (child->fail == NULL) continue;
for (om = child->fail; om != parser->root_node; om = om->fail) {
if (om->is_last) {
child->o_match = om;
break;
}
}
}
/* Go recursively through children of this node that have a child node */
for(child = node->child; child != NULL; child = child->sibling) {
if (child->child != NULL) acmp_connect_other_matches(parser, child);
}
}
/**
* Adds leaves to binary tree, working from sorted array of keyword tree nodes
*/
static void acmp_add_btree_leaves(acmp_btree_node_t *node, acmp_node_t *nodes[],
int pos, int lb, int rb, apr_pool_t *pool) {
int left = 0, right = 0;
if ((pos - lb) > 1) {
left = lb + (pos - lb) / 2;
node->left = apr_pcalloc(pool, sizeof(acmp_btree_node_t));
/* ENH: Check alloc succeded */
node->left->node = nodes[left];
node->left->letter = nodes[left]->letter;
#ifdef DEBUG_ACMP
fprintf(stderr, "%lc ->left %lc\n", (wint_t)node->node->letter, (wint_t)node->left->node->letter);
#endif
}
if ((rb - pos) > 1) {
right = pos + (rb - pos) / 2;
node->right = apr_pcalloc(pool, sizeof(acmp_btree_node_t));
/* ENH: Check alloc succeded */
node->right->node = nodes[right];
node->right->letter = nodes[right]->letter;
#ifdef DEBUG_ACMP
fprintf(stderr, "%lc ->right %lc\n", (wint_t)node->node->letter, (wint_t)node->right->node->letter);
#endif
}
if (node->right != NULL) {
acmp_add_btree_leaves(node->right, nodes, right, pos, rb, pool);
}
if (node->left != NULL) {
acmp_add_btree_leaves(node->left, nodes, left, lb, pos, pool);
}
}
/**
* Builds balanced binary tree from children nodes of given node.
*/
static void acmp_build_binary_tree(ACMP *parser, acmp_node_t *node) {
apr_size_t count, i, j;
acmp_node_t *child = node->child;
acmp_node_t **nodes;
apr_size_t pos;
/* Build an array big enough */
for (count = 0; child != NULL; child = child->sibling) count++;
nodes = apr_pcalloc(parser->pool, count * sizeof(acmp_node_t *));
/* ENH: Check alloc succeded */
/* ENH: Combine this in the loop below - we do not need two loops */
child = node->child;
for (i = 0; i < count; i++) {
nodes[i] = child;
child = child->sibling;
};
/* We have array with all children of the node and number of those children
*/
for (i = 0; i < count - 1; i++)
for (j = i + 1; j < count; j++) {
acmp_node_t *tmp;
if (nodes[i]->letter < nodes[j]->letter) continue;
tmp = nodes[i];
nodes[i] = nodes[j];
nodes[j] = tmp;
}
node->btree = apr_pcalloc(parser->pool, sizeof(acmp_btree_node_t));
/* ENH: Check alloc succeded */
pos = count / 2;
node->btree->node = nodes[pos];
node->btree->letter = nodes[pos]->letter;
acmp_add_btree_leaves(node->btree, nodes, pos, -1, count, parser->pool);
for (i = 0; i < count; i++) {
if (nodes[i]->child != NULL) acmp_build_binary_tree(parser, nodes[i]);
}
}
/**
* Constructs fail paths on keyword trie
*/
static apr_status_t acmp_connect_fail_branches(ACMP *parser) {
/* Already connected ? */
acmp_node_t *child, *node, *goto_node;
apr_array_header_t *arr, *arr2, *tmp;
if (parser->is_failtree_done != 0) return APR_SUCCESS;
parser->root_node->text = "";
arr = apr_array_make(parser->pool, 32, sizeof(acmp_node_t *));
arr2 = apr_array_make(parser->pool, 32, sizeof(acmp_node_t *));
parser->root_node->fail = parser->root_node;
/* All first-level children will fail back to root node */
for (child = parser->root_node->child; child != NULL; child = child->sibling) {
child->fail = parser->root_node;
*(acmp_node_t **)apr_array_push(arr) = child;
#ifdef DEBUG_ACMP
fprintf(stderr, "fail direction: *%s* => *%s*\n", child->text, child->fail->text);
#endif
}
for (;;) {
while (apr_is_empty_array(arr) == 0) {
node = *(acmp_node_t **)apr_array_pop(arr);
node->fail = parser->root_node;
if (node->parent != parser->root_node) {
goto_node = acmp_child_for_code(node->parent->fail, node->letter);
node->fail = (goto_node != NULL) ? goto_node : parser->root_node;
}
#ifdef DEBUG_ACMP
fprintf(stderr, "fail direction: *%s* => *%s*\n", node->text, node->fail->text);
#endif
child = node->child;
while (child != NULL) {
*(acmp_node_t **)apr_array_push(arr2) = child;
child = child->sibling;
}
}
if (apr_is_empty_array(arr2) != 0) break;
tmp = arr;
arr = arr2;
arr2 = tmp;
}
acmp_connect_other_matches(parser, parser->root_node);
if (parser->root_node->child != NULL) acmp_build_binary_tree(parser, parser->root_node);
parser->is_failtree_done = 1;
return APR_SUCCESS;
}
/*
*******************************************************************************
*******************************************************************************
* Code for functions from header file
*/
/**
* flags - OR-ed values of ACMP_FLAG constants
* pool - apr_pool to use as parent pool, can be set to NULL
*/
ACMP *acmp_create(int flags, apr_pool_t *pool) {
apr_status_t rc;
apr_pool_t *p;
ACMP *parser;
rc = apr_pool_create(&p, pool);
if (rc != APR_SUCCESS) return NULL;
parser = apr_pcalloc(p, sizeof(ACMP));
/* ENH: Check alloc succeded */
parser->pool = p;
parser->parent_pool = pool;
#ifdef ACMP_USE_UTF8
parser->is_utf8 = (flags & ACMP_FLAG_UTF8) == 0 ? 0 : 1;
#endif
parser->is_case_sensitive = (flags & ACMP_FLAG_CASE_SENSITIVE) == 0 ? 0 : 1;
parser->root_node = apr_pcalloc(p, sizeof(acmp_node_t));
/* ENH: Check alloc succeded */
return parser;
}
/**
* Creates fail tree and initializes buffer
*/
apr_status_t acmp_prepare(ACMP *parser) {
apr_status_t st;
if (parser->bp_buff_len < parser->longest_entry) {
parser->bp_buff_len = parser->longest_entry * 2;
parser->bp_buffer = apr_pcalloc(parser->pool, sizeof(apr_size_t) * parser->bp_buff_len);
/* ENH: Check alloc succeded */
}
st = acmp_connect_fail_branches(parser);
parser->active_node = parser->root_node;
if (st != APR_SUCCESS) return st;
parser->is_active = 1;
return APR_SUCCESS;
}
/**
* Adds pattern to parser
* parser - ACMP parser
* pattern - string with pattern to match
* callback - Optional, pointer to an acmp_callback_t function
* data - pointer to data that will be passed to callback function, only used if callback
* is supplied
* len - Length of pattern in characters, if zero string length is used.
*/
apr_status_t acmp_add_pattern(ACMP *parser, const char *pattern,
acmp_callback_t callback, void *data, apr_size_t len)
{
size_t length, i, j;
acmp_utf8_char_t *ucs_chars;
acmp_node_t *parent, *child;
if (parser->is_active != 0) return APR_EGENERAL;
length = (len == 0) ? acmp_strlen(parser, pattern) : len;
ucs_chars = apr_pcalloc(parser->pool, length * sizeof(acmp_utf8_char_t));
/* ENH: Check alloc succeded */
parent = parser->root_node;
acmp_strtoucs(parser, pattern, ucs_chars, length);
for (i = 0; i < length; i++) {
acmp_utf8_char_t letter = ucs_chars[i];
if (parser->is_case_sensitive == 0) {
letter = utf8_lcase(letter);
}
child = acmp_child_for_code(parent, letter);
if (child == NULL) {
child = apr_pcalloc(parser->pool, sizeof(acmp_node_t));
/* ENH: Check alloc succeded */
child->pattern = "";
child->letter = letter;
child->depth = i;
child->text = apr_pcalloc(parser->pool, i + 2);
/* ENH: Check alloc succeded */
for (j = 0; j <= i; j++) {
child->text[j] = pattern[j];
}
}
if (i == length - 1) {
if (child->is_last == 0) {
parser->dict_count++;
child->is_last = 1;
child->pattern = apr_pcalloc(parser->pool, length + 1);
/* ENH: Check alloc succeded */
memcpy(child->pattern, pattern, length);
child->pattern[length] = '\0';
}
child->callback = callback;
child->callback_data = data;
}
acmp_add_node_to_parent(parent, child);
parent = child;
}
if (length > parser->longest_entry) parser->longest_entry = length;
parser->is_failtree_done = 0;
return APR_SUCCESS;
}
/**
* Process the data using ACMPT to keep state, and ACMPT's parser to keep the tree
*/
apr_status_t acmp_process_quick(ACMPT *acmpt, const char **match, const char *data, apr_size_t len) {
ACMP *parser;
acmp_node_t *node, *go_to;
const char *end;
if (acmpt->parser->is_failtree_done == 0) {
acmp_prepare(acmpt->parser);
};
parser = acmpt->parser;
if (acmpt->ptr == NULL) acmpt->ptr = parser->root_node;
node = acmpt->ptr;
end = data + len;
while (data < end) {
acmp_utf8_char_t letter = (unsigned char)*data++;
if (parser->is_case_sensitive == 0) letter = utf8_lcase(letter);
go_to = NULL;
while (go_to == NULL) {
go_to = acmp_goto(node, letter);
if (go_to != NULL) {
if (go_to->is_last) {
*match = go_to->text;
return 1;
}
}
if (node == parser->root_node) break;
if (go_to == NULL) node = node->fail;
}
if (go_to != NULL) node = go_to;
/* If node has o_match, then we found a pattern */
if (node->o_match != NULL) {
*match = node->text;
return 1;
}
}
acmpt->ptr = node;
return 0;
}
modsecurity-apache-2.9.13/apache2/acmp.h 0000664 0000000 0000000 00000006553 15174655764 0020025 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#ifndef ACMP_H_
#define ACMP_H_
#include
#include
#define ACMP_FLAG_BYTE 0
#define ACMP_FLAG_CASE_SENSITIVE 1
#define ACMP_FLAG_CASE_INSENSITIVE 0
#ifdef ACMP_USE_UTF8
#define ACMP_FLAG_UTF8 0x100
#endif
/**
* Opaque struct with parser data
*/
typedef struct ACMP ACMP;
/**
* Used to separate state from the trie for acmp_process_quick function
*/
typedef struct {
ACMP *parser;
void *ptr;
} ACMPT;
/**
* Callback function. Arguments are:
* ACMP * - acmp parser that initiated callback
* void * - custom data you supplied when adding callback
* apr_size_t - position in bytes where pattern was found
* apr_size_t - position in chars where pattern was found, for multibyte strings
*/
typedef void (*acmp_callback_t)(ACMP *, void *, apr_size_t, apr_size_t);
/**
* flags - OR-ed values of ACMP_FLAG constants
* pool - apr_pool to use as parent pool, can be set to NULL
*/
ACMP *acmp_create(int flags, apr_pool_t *pool);
/**
* Destroys previously created parser
*/
void acmp_destroy(ACMP *parser);
/**
* Creates parser with same options and same patterns
* parser - ACMP parser to duplicate
* pool - parent pool to use, if left as NULL original parser's parent pool is used
*/
ACMP *acmp_duplicate(ACMP *parser, apr_pool_t *pool);
/**
* Adds pattern to parser. Cannot be done after starting the search.
* parser - ACMP parser
* pattern - string with pattern to match
* callback - Optional, pointer to an acmp_callback_t function
* data - pointer to data that will be passed to callback function, only used if callback
* is supplied
* len - Length of pattern in characters, if zero string length is used.
*/
apr_status_t acmp_add_pattern(ACMP *parser, const char *pattern,
acmp_callback_t callback, void *data, apr_size_t len);
/**
* Called to process incoming data stream. You must call acmp_done after sending
* last data packet
*
* data - ptr to incoming data
* len - size of data in bytes
*/
apr_status_t acmp_process(ACMP *parser, const char *data, apr_size_t len);
/**
* Returns number of matches on all patterns combined
*/
apr_size_t acmp_match_count_total(ACMP *parser);
/**
* Returns number of matches for given pattern
*/
apr_size_t acmp_match_count(ACMP *parser, const char *pattern);
/**
* Resets the state of parser so you can start using it with new set of data,
* or add new patterns.
*/
void acmp_reset(ACMP *parser);
/**
* Creates an ACMPT struct that will use parser's tree, without duplicating its data
*/
ACMPT *acmp_duplicate_quick(ACMP *parser, apr_pool_t *pool);
/**
* Process the data using ACMPT to keep state, and ACMPT's parser to keep the tree
*/
apr_status_t acmp_process_quick(ACMPT *acmpt, const char **match, const char *data, apr_size_t len);
/**
* Prepares parser for searching
*/
apr_status_t acmp_prepare(ACMP *parser);
#endif /*ACMP_H_*/
modsecurity-apache-2.9.13/apache2/apache2.h 0000664 0000000 0000000 00000006456 15174655764 0020412 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#ifndef _APACHE2_H_
#define _APACHE2_H_
#include "http_core.h"
#include "http_request.h"
#include "httpd.h"
#include "ap_release.h"
#include
#include
#if (!defined(NO_MODSEC_API))
/* Optional functions. */
APR_DECLARE_OPTIONAL_FN(void, modsec_register_tfn, (const char *name, void *fn));
APR_DECLARE_OPTIONAL_FN(void, modsec_register_operator, (const char *name, void *fn_init, void *fn_exec));
APR_DECLARE_OPTIONAL_FN(void, modsec_register_variable,
(const char *name, unsigned int type,
unsigned int argc_min, unsigned int argc_max,
void *fn_validate, void *fn_generate,
unsigned int is_cacheable, unsigned int availability));
APR_DECLARE_OPTIONAL_FN(void, modsec_register_reqbody_processor, (const char *name, void *fn_init, void *fn_process, void *fn_complete));
#endif
/* ap_get_server_version() is gone in 2.3.0.
* It was replaced by two calls in 2.2.4 and higher:
* ap_get_server_banner()
* ap_get_server_description()
*/
#if (AP_SERVER_MAJORVERSION_NUMBER > 2) \
|| ((AP_SERVER_MAJORVERSION_NUMBER == 2)&& (AP_SERVER_MINORVERSION_NUMBER > 2)) \
|| ((AP_SERVER_MAJORVERSION_NUMBER == 2) && (AP_SERVER_MINORVERSION_NUMBER == 2) && (AP_SERVER_PATCHLEVEL_NUMBER >= 4))
#define apache_get_server_version() ap_get_server_banner()
#else
#define apache_get_server_version() ap_get_server_version()
#endif
/* Configuration functions. */
void DSOLOCAL *create_directory_config(apr_pool_t *mp, char *path);
void DSOLOCAL *merge_directory_configs(apr_pool_t *mp, void *_parent, void *_child);
void DSOLOCAL init_directory_config(directory_config *dcfg);
/* IO functions. */
apr_status_t DSOLOCAL input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out,
ap_input_mode_t mode, apr_read_type_e block, apr_off_t nbytes);
apr_status_t DSOLOCAL output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in);
apr_status_t DSOLOCAL read_request_body(modsec_rec *msr, char **error_msg);
/* Utility functions */
int DSOLOCAL perform_interception(modsec_rec *msr);
apr_status_t DSOLOCAL send_error_bucket(modsec_rec *msr, ap_filter_t *f, int status);
int DSOLOCAL apache2_exec(modsec_rec *msr, const char *command, const char **argv, char **output);
void DSOLOCAL record_time_checkpoint(modsec_rec *msr, int checkpoint_no);
char DSOLOCAL *get_apr_error(apr_pool_t *p, apr_status_t rc);
char DSOLOCAL *get_env_var(request_rec *r, char *name);
void DSOLOCAL msr_log(modsec_rec *msr, int level, const char *text, ...) PRINTF_ATTRIBUTE(3,4);
void DSOLOCAL msr_log_error(modsec_rec *msr, const char *text, ...) PRINTF_ATTRIBUTE(2,3);
void DSOLOCAL msr_log_warn(modsec_rec *msr, const char *text, ...) PRINTF_ATTRIBUTE(2,3);
char DSOLOCAL *format_error_log_message(apr_pool_t *mp, error_message_t *em);
const DSOLOCAL char *get_response_protocol(request_rec *r);
#endif
modsecurity-apache-2.9.13/apache2/apache2_config.c 0000664 0000000 0000000 00000443027 15174655764 0021731 0 ustar 00root root 0000000 0000000 /*
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
* 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
*
* 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 "modsecurity.h"
#include "msc_logging.h"
#include "msc_util.h"
#include "http_log.h"
#include "apr_lib.h"
#include "acmp.h"
#include "msc_crypt.h"
#if defined(WITH_LUA)
#include "msc_lua.h"
#endif
#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(security2);
#endif
/* -- Directory context creation and initialisation -- */
/**
* Creates a fresh directory configuration.
*/
void *create_directory_config(apr_pool_t *mp, char *path)
{
directory_config *dcfg = (directory_config *)apr_pcalloc(mp, sizeof(directory_config));
if (dcfg == NULL) return NULL;
#ifdef DEBUG_CONF
ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Created directory config %pp path %s", dcfg, path);
#endif
dcfg->mp = mp;
dcfg->is_enabled = NOT_SET;
dcfg->reqbody_access = NOT_SET;
dcfg->reqintercept_oe = NOT_SET;
dcfg->reqbody_buffering = NOT_SET;
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;
dcfg->debuglog_level = NOT_SET;
dcfg->debuglog_fd = NOT_SET_P;
dcfg->of_limit = NOT_SET;
dcfg->if_limit_action = NOT_SET;
dcfg->of_limit_action = NOT_SET;
dcfg->of_mime_types = NOT_SET_P;
dcfg->of_mime_types_cleared = NOT_SET;
dcfg->cookie_format = NOT_SET;
dcfg->argument_separator = NOT_SET;
dcfg->cookiev0_separator = NOT_SET_P;
dcfg->rule_inheritance = NOT_SET;
dcfg->rule_exceptions = apr_array_make(mp, 16, sizeof(rule_exception *));
dcfg->hash_method = apr_array_make(mp, 16, sizeof(hash_method *));
/* audit log variables */
dcfg->auditlog_flag = NOT_SET;
dcfg->auditlog_type = NOT_SET;
#ifdef WITH_YAJL
dcfg->auditlog_format = NOT_SET;
#endif
dcfg->max_rule_time = NOT_SET;
dcfg->auditlog_dirperms = NOT_SET;
dcfg->auditlog_fileperms = NOT_SET;
dcfg->auditlog_name = NOT_SET_P;
dcfg->auditlog2_name = NOT_SET_P;
dcfg->auditlog_fd = NOT_SET_P;
dcfg->auditlog2_fd = NOT_SET_P;
dcfg->auditlog_storage_dir = NOT_SET_P;
dcfg->auditlog_parts = NOT_SET_P;
dcfg->auditlog_relevant_regex = NOT_SET_P;
dcfg->ruleset = NULL;
/* Upload */
dcfg->tmp_dir = NOT_SET_P;
dcfg->upload_dir = NOT_SET_P;
dcfg->upload_keep_files = NOT_SET;
dcfg->upload_validates_files = NOT_SET;
dcfg->upload_filemode = NOT_SET;
dcfg->upload_file_limit = NOT_SET;
/* These are only used during the configuration process. */
dcfg->tmp_chain_starter = NULL;
dcfg->tmp_default_actionset = NULL;
dcfg->tmp_rule_placeholders = NULL;
/* Misc */
dcfg->data_dir = NOT_SET_P;
dcfg->webappid = NOT_SET_P;
dcfg->sensor_id = NOT_SET_P;
dcfg->httpBlkey = NOT_SET_P;
/* Content injection. */
dcfg->content_injection_enabled = NOT_SET;
/* Stream inspection */
dcfg->stream_inbody_inspection = NOT_SET;
dcfg->stream_outbody_inspection = NOT_SET;
/* Geo Lookups */
dcfg->geo = NOT_SET_P;
/* Gsb Lookups */
dcfg->gsb = NOT_SET_P;
/* Unicode Map */
dcfg->u_map = NOT_SET_P;
/* Cache */
dcfg->cache_trans = NOT_SET;
dcfg->cache_trans_incremental = NOT_SET;
dcfg->cache_trans_min = NOT_SET;
dcfg->cache_trans_max = NOT_SET;
dcfg->cache_trans_maxitems = NOT_SET;
/* Rule ids */
dcfg->rule_id_htab = apr_hash_make(mp);
dcfg->component_signatures = apr_array_make(mp, 16, sizeof(char *));
dcfg->request_encoding = NOT_SET_P;
dcfg->disable_backend_compression = NOT_SET;
/* Collection timeout */
dcfg->col_timeout = NOT_SET;
dcfg->crypto_key = NOT_SET_P;
dcfg->crypto_key_len = NOT_SET;
dcfg->crypto_key_add = NOT_SET;
dcfg->crypto_param_name = NOT_SET_P;
dcfg->hash_is_enabled = NOT_SET;
dcfg->hash_enforcement = NOT_SET;
dcfg->crypto_hash_href_rx = NOT_SET;
dcfg->crypto_hash_faction_rx = NOT_SET;
dcfg->crypto_hash_location_rx = NOT_SET;
dcfg->crypto_hash_iframesrc_rx = NOT_SET;
dcfg->crypto_hash_framesrc_rx = NOT_SET;
dcfg->crypto_hash_href_pm = NOT_SET;
dcfg->crypto_hash_faction_pm = NOT_SET;
dcfg->crypto_hash_location_pm = NOT_SET;
dcfg->crypto_hash_iframesrc_pm = NOT_SET;
dcfg->crypto_hash_framesrc_pm = NOT_SET;
/* xml external entity */
dcfg->xml_external_entity = NOT_SET;
dcfg->parse_xml_into_args = NOT_SET;
return dcfg;
}
/**
* Copies rules between one phase of two configuration contexts,
* taking exceptions into account.
*/
static void copy_rules_phase(apr_pool_t *mp,
apr_array_header_t *parent_phase_arr,
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;
int mode = 0;
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. */
switch(exceptions[j]->type) {
case RULE_EXCEPTION_REMOVE_ID :
if ((rule->actionset != NULL)&&(rule->actionset->id != NULL)) {
int ruleid = atoi(rule->actionset->id);
if (rule_id_in_range(ruleid, exceptions[j]->param)) copy--;
}
break;
case RULE_EXCEPTION_REMOVE_MSG :
if ((rule->actionset != NULL)&&(rule->actionset->msg != NULL)) {
char *my_error_msg = NULL;
int rc = msc_regexec(exceptions[j]->param_data,
rule->actionset->msg, strlen(rule->actionset->msg),
&my_error_msg);
if (rc >= 0) copy--;
}
break;
case RULE_EXCEPTION_REMOVE_TAG :
if ((rule->actionset != NULL)&&(apr_is_empty_table(rule->actionset->actions) == 0)) {
char *my_error_msg = NULL;
const apr_array_header_t *tarr = NULL;
const apr_table_entry_t *telts = NULL;
int c;
tarr = apr_table_elts(rule->actionset->actions);
telts = (const apr_table_entry_t*)tarr->elts;
for (c = 0; c < tarr->nelts; c++) {
msre_action *action = (msre_action *)telts[c].val;
if(strcmp("tag", action->metadata->name) == 0) {
int rc = msc_regexec(exceptions[j]->param_data,
action->param, strlen(action->param),
&my_error_msg);
if (rc >= 0) copy--;
}
}
}
break;
}
}
if (copy > 0) {
#ifdef DEBUG_CONF
ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Copy rule %pp [id \"%s\"]", rule, id_log(rule));
#endif
/* Copy the rule. */
*(msre_rule **)apr_array_push(child_phase_arr) = rule;
if (rule->actionset->is_chained) mode = 2;
} else {
if (rule->actionset->is_chained) mode = 1;
}
} 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, id_log(rule->chain_starter));
#endif
/* Copy the rule (it belongs to the chain we want to include. */
*(msre_rule **)apr_array_push(child_phase_arr) = rule;
}
if ((rule->actionset == NULL)||(rule->actionset->is_chained == 0)) mode = 0;
}
}
}
/**
* @brief Copies rules between one phase of two configuration contexts.
*
* Copies rules between one phase of two configuration contexts,
* taking exceptions into account.
*
* @param mp apr pool structure
* @param parent_ruleset Parent's msre_ruleset
* @param child_ruleset Child's msre_ruleset
* @param exceptions_arr Exceptions' apr_array_header_t
* @retval 0 Everything went well.
* @retval -1 Something went wrong.
*
*/
static void copy_rules(apr_pool_t *mp, msre_ruleset *parent_ruleset,
msre_ruleset *child_ruleset,
apr_array_header_t *exceptions_arr)
{
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,
child_ruleset->phase_request_body, exceptions_arr);
copy_rules_phase(mp, parent_ruleset->phase_response_headers,
child_ruleset->phase_response_headers, exceptions_arr);
copy_rules_phase(mp, parent_ruleset->phase_response_body,
child_ruleset->phase_response_body, exceptions_arr);
copy_rules_phase(mp, parent_ruleset->phase_logging,
child_ruleset->phase_logging, exceptions_arr);
}
/**
* Merges two directory configurations.
*/
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);
#ifdef DEBUG_CONF
ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Merge parent %pp child %pp RESULT %pp", _parent, _child, merged);
#endif
if (merged == NULL) return NULL;
/* Use values from the child configuration where possible,
* otherwise use the parent's.
*/
merged->is_enabled = (child->is_enabled == NOT_SET
? parent->is_enabled : child->is_enabled);
/* IO parameters */
merged->reqbody_access = (child->reqbody_access == NOT_SET
? parent->reqbody_access : child->reqbody_access);
merged->reqbody_buffering = (child->reqbody_buffering == NOT_SET
? parent->reqbody_buffering : child->reqbody_buffering);
merged->reqbody_inmemory_limit = (child->reqbody_inmemory_limit == NOT_SET
? parent->reqbody_inmemory_limit : child->reqbody_inmemory_limit);
merged->reqbody_limit = (child->reqbody_limit == NOT_SET
? 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);
merged->of_limit = (child->of_limit == NOT_SET
? parent->of_limit : child->of_limit);
merged->if_limit_action = (child->if_limit_action == NOT_SET
? parent->if_limit_action : child->if_limit_action);
merged->of_limit_action = (child->of_limit_action == NOT_SET
? parent->of_limit_action : child->of_limit_action);
merged->reqintercept_oe = (child->reqintercept_oe == NOT_SET
? parent->reqintercept_oe : child->reqintercept_oe);
if (child->of_mime_types != NOT_SET_P) {
/* Child added to the table */
if (child->of_mime_types_cleared == 1) {
/* The list of MIME types was cleared in the child,
* which means the parent's MIME types went away and
* we should not take them into consideration here.
*/
merged->of_mime_types = child->of_mime_types;
merged->of_mime_types_cleared = 1;
} else {
/* Add MIME types defined in the child to those
* defined in the parent context.
*/
if (parent->of_mime_types == NOT_SET_P) {
merged->of_mime_types = child->of_mime_types;
merged->of_mime_types_cleared = NOT_SET;
} else {
merged->of_mime_types = apr_table_overlay(mp, parent->of_mime_types,
child->of_mime_types);
if (merged->of_mime_types == NULL) return NULL;
}
}
} else {
/* Child did not add to the table */
if (child->of_mime_types_cleared == 1) {
merged->of_mime_types_cleared = 1;
} else {
merged->of_mime_types = parent->of_mime_types;
merged->of_mime_types_cleared = parent->of_mime_types_cleared;
}
}
/* debug log */
if (child->debuglog_fd == NOT_SET_P) {
merged->debuglog_name = parent->debuglog_name;
merged->debuglog_fd = parent->debuglog_fd;
} else {
merged->debuglog_name = child->debuglog_name;
merged->debuglog_fd = child->debuglog_fd;
}
merged->debuglog_level = (child->debuglog_level == NOT_SET
? parent->debuglog_level : child->debuglog_level);
merged->cookie_format = (child->cookie_format == NOT_SET
? parent->cookie_format : child->cookie_format);
merged->argument_separator = (child->argument_separator == NOT_SET
? parent->argument_separator : child->argument_separator);
merged->cookiev0_separator = (child->cookiev0_separator == NOT_SET_P
? parent->cookiev0_separator : child->cookiev0_separator);
/* rule inheritance */
if ((child->rule_inheritance == NOT_SET)||(child->rule_inheritance == 1)) {
merged->rule_inheritance = parent->rule_inheritance;
if ((child->ruleset == NULL)&&(parent->ruleset == NULL)) {
#ifdef DEBUG_CONF
ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "No rules in this context.");
#endif
/* Do nothing, there are no rules in either context. */
} else
if (child->ruleset == NULL) {
#ifdef DEBUG_CONF
ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Using parent rules in this context.");
#endif
/* Copy the rules from the parent context. */
merged->ruleset = msre_ruleset_create(parent->ruleset->engine, mp);
copy_rules(mp, parent->ruleset, merged->ruleset, child->rule_exceptions);
} else
if (parent->ruleset == NULL) {
#ifdef DEBUG_CONF
ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Using child rules in this context.");
#endif
/* Copy child rules. */
merged->ruleset = msre_ruleset_create(child->ruleset->engine, mp);
merged->ruleset->phase_request_headers = apr_array_copy(mp,
child->ruleset->phase_request_headers);
merged->ruleset->phase_request_body = apr_array_copy(mp,
child->ruleset->phase_request_body);
merged->ruleset->phase_response_headers = apr_array_copy(mp,
child->ruleset->phase_response_headers);
merged->ruleset->phase_response_body = apr_array_copy(mp,
child->ruleset->phase_response_body);
merged->ruleset->phase_logging = apr_array_copy(mp,
child->ruleset->phase_logging);
} else {
#ifdef DEBUG_CONF
ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, mp, "Using parent then child rules in this context.");
#endif
/* Copy parent rules, then add child rules to it. */
merged->ruleset = msre_ruleset_create(parent->ruleset->engine, mp);
copy_rules(mp, parent->ruleset, merged->ruleset, child->rule_exceptions);
apr_array_cat(merged->ruleset->phase_request_headers,
child->ruleset->phase_request_headers);
apr_array_cat(merged->ruleset->phase_request_body,
child->ruleset->phase_request_body);
apr_array_cat(merged->ruleset->phase_response_headers,
child->ruleset->phase_response_headers);
apr_array_cat(merged->ruleset->phase_response_body,
child->ruleset->phase_response_body);
apr_array_cat(merged->ruleset->phase_logging,
child->ruleset->phase_logging);
}
} else {
merged->rule_inheritance = 0;
if (child->ruleset != NULL) {
/* Copy child rules. */
merged->ruleset = msre_ruleset_create(child->ruleset->engine, mp);
merged->ruleset->phase_request_headers = apr_array_copy(mp,
child->ruleset->phase_request_headers);
merged->ruleset->phase_request_body = apr_array_copy(mp,
child->ruleset->phase_request_body);
merged->ruleset->phase_response_headers = apr_array_copy(mp,
child->ruleset->phase_response_headers);
merged->ruleset->phase_response_body = apr_array_copy(mp,
child->ruleset->phase_response_body);
merged->ruleset->phase_logging = apr_array_copy(mp,
child->ruleset->phase_logging);
}
}
/* Merge rule exceptions. */
merged->rule_exceptions = apr_array_append(mp, parent->rule_exceptions,
child->rule_exceptions);
merged->hash_method = apr_array_append(mp, parent->hash_method,
child->hash_method);
/* audit log variables */
merged->auditlog_flag = (child->auditlog_flag == NOT_SET
? parent->auditlog_flag : child->auditlog_flag);
merged->auditlog_type = (child->auditlog_type == NOT_SET
? parent->auditlog_type : child->auditlog_type);
merged->max_rule_time = (child->max_rule_time == NOT_SET
? parent->max_rule_time : child->max_rule_time);
merged->auditlog_dirperms = (child->auditlog_dirperms == NOT_SET
? parent->auditlog_dirperms : child->auditlog_dirperms);
merged->auditlog_fileperms = (child->auditlog_fileperms == NOT_SET
? parent->auditlog_fileperms : child->auditlog_fileperms);
if (child->auditlog_fd != NOT_SET_P) {
merged->auditlog_fd = child->auditlog_fd;
merged->auditlog_name = child->auditlog_name;
} else {
merged->auditlog_fd = parent->auditlog_fd;
merged->auditlog_name = parent->auditlog_name;
}
if (child->auditlog2_fd != NOT_SET_P) {
merged->auditlog2_fd = child->auditlog2_fd;
merged->auditlog2_name = child->auditlog2_name;
} else {
merged->auditlog2_fd = parent->auditlog2_fd;
merged->auditlog2_name = parent->auditlog2_name;
}
#ifdef WITH_YAJL
merged->auditlog_format = (child->auditlog_format == NOT_SET
? parent->auditlog_format : child->auditlog_format);
#endif
merged->auditlog_storage_dir = (child->auditlog_storage_dir == NOT_SET_P
? parent->auditlog_storage_dir : child->auditlog_storage_dir);
merged->auditlog_parts = (child->auditlog_parts == NOT_SET_P
? parent->auditlog_parts : child->auditlog_parts);
merged->auditlog_relevant_regex = (child->auditlog_relevant_regex == NOT_SET_P
? parent->auditlog_relevant_regex : child->auditlog_relevant_regex);
/* Upload */
merged->tmp_dir = (child->tmp_dir == NOT_SET_P
? parent->tmp_dir : child->tmp_dir);
merged->upload_dir = (child->upload_dir == NOT_SET_P
? parent->upload_dir : child->upload_dir);
merged->upload_keep_files = (child->upload_keep_files == NOT_SET
? parent->upload_keep_files : child->upload_keep_files);
merged->upload_validates_files = (child->upload_validates_files == NOT_SET
? parent->upload_validates_files : child->upload_validates_files);
merged->upload_filemode = (child->upload_filemode == NOT_SET
? parent->upload_filemode : child->upload_filemode);
merged->upload_file_limit = (child->upload_file_limit == NOT_SET
? parent->upload_file_limit : child->upload_file_limit);
/* Misc */
merged->data_dir = (child->data_dir == NOT_SET_P
? parent->data_dir : child->data_dir);
merged->webappid = (child->webappid == NOT_SET_P
? parent->webappid : child->webappid);
merged->sensor_id = (child->sensor_id == NOT_SET_P
? parent->sensor_id : child->sensor_id);
merged->httpBlkey = (child->httpBlkey == NOT_SET_P
? parent->httpBlkey : child->httpBlkey);
/* Content injection. */
merged->content_injection_enabled = (child->content_injection_enabled == NOT_SET
? parent->content_injection_enabled : child->content_injection_enabled);
/* Stream inspection */
merged->stream_inbody_inspection = (child->stream_inbody_inspection == NOT_SET
? parent->stream_inbody_inspection : child->stream_inbody_inspection);
merged->stream_outbody_inspection = (child->stream_outbody_inspection == NOT_SET
? parent->stream_outbody_inspection : child->stream_outbody_inspection);
/* Geo Lookup */
merged->geo = (child->geo == NOT_SET_P
? parent->geo : child->geo);
/* Gsb Lookup */
merged->gsb = (child->gsb == NOT_SET_P
? parent->gsb : child->gsb);
/* Unicode Map */
merged->u_map = (child->u_map == NOT_SET_P
? parent->u_map : child->u_map);
/* Cache */
merged->cache_trans = (child->cache_trans == NOT_SET
? parent->cache_trans : child->cache_trans);
merged->cache_trans_incremental = (child->cache_trans_incremental == NOT_SET
? parent->cache_trans_incremental : child->cache_trans_incremental);
merged->cache_trans_min = (child->cache_trans_min == (apr_size_t)NOT_SET
? parent->cache_trans_min : child->cache_trans_min);
merged->cache_trans_max = (child->cache_trans_max == (apr_size_t)NOT_SET
? parent->cache_trans_max : child->cache_trans_max);
merged->cache_trans_maxitems = (child->cache_trans_maxitems == (apr_size_t)NOT_SET
? parent->cache_trans_maxitems : child->cache_trans_maxitems);
/* Merge component signatures. */
merged->component_signatures = apr_array_append(mp, parent->component_signatures,
child->component_signatures);
merged->request_encoding = (child->request_encoding == NOT_SET_P
? parent->request_encoding : child->request_encoding);
merged->disable_backend_compression = (child->disable_backend_compression == NOT_SET
? parent->disable_backend_compression : child->disable_backend_compression);
merged->col_timeout = (child->col_timeout == NOT_SET
? parent->col_timeout : child->col_timeout);
/* Hash */
merged->crypto_key = (child->crypto_key == NOT_SET_P
? parent->crypto_key : child->crypto_key);
merged->crypto_key_len = (child->crypto_key_len == NOT_SET
? parent->crypto_key_len : child->crypto_key_len);
merged->crypto_key_add = (child->crypto_key_add == NOT_SET
? parent->crypto_key_add : child->crypto_key_add);
merged->crypto_param_name = (child->crypto_param_name == NOT_SET_P
? parent->crypto_param_name : child->crypto_param_name);
merged->hash_is_enabled = (child->hash_is_enabled == NOT_SET
? parent->hash_is_enabled : child->hash_is_enabled);
merged->hash_enforcement = (child->hash_enforcement == NOT_SET
? parent->hash_enforcement : child->hash_enforcement);
merged->crypto_hash_href_rx = (child->crypto_hash_href_rx == NOT_SET
? parent->crypto_hash_href_rx : child->crypto_hash_href_rx);
merged->crypto_hash_faction_rx = (child->crypto_hash_faction_rx == NOT_SET
? parent->crypto_hash_faction_rx : child->crypto_hash_faction_rx);
merged->crypto_hash_location_rx = (child->crypto_hash_location_rx == NOT_SET
? parent->crypto_hash_location_rx : child->crypto_hash_location_rx);
merged->crypto_hash_iframesrc_rx = (child->crypto_hash_iframesrc_rx == NOT_SET
? parent->crypto_hash_iframesrc_rx : child->crypto_hash_iframesrc_rx);
merged->crypto_hash_framesrc_rx = (child->crypto_hash_framesrc_rx == NOT_SET
? parent->crypto_hash_framesrc_rx : child->crypto_hash_framesrc_rx);
merged->crypto_hash_href_pm = (child->crypto_hash_href_pm == NOT_SET
? parent->crypto_hash_href_pm : child->crypto_hash_href_pm);
merged->crypto_hash_faction_pm = (child->crypto_hash_faction_pm == NOT_SET
? parent->crypto_hash_faction_pm : child->crypto_hash_faction_pm);
merged->crypto_hash_location_pm = (child->crypto_hash_location_pm == NOT_SET
? parent->crypto_hash_location_pm : child->crypto_hash_location_pm);
merged->crypto_hash_iframesrc_pm = (child->crypto_hash_iframesrc_pm == NOT_SET
? parent->crypto_hash_iframesrc_pm : child->crypto_hash_iframesrc_pm);
merged->crypto_hash_framesrc_pm = (child->crypto_hash_framesrc_pm == NOT_SET
? parent->crypto_hash_framesrc_pm : child->crypto_hash_framesrc_pm);
/* 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;
}
/**
* Initialise directory configuration. This function is *not* meant
* to be called for directory configuration instances created during
* the configuration phase. It can only be called on copies of those
* (created fresh for every transaction).
*/
void init_directory_config(directory_config *dcfg)
{
if (dcfg == NULL) return;
if (dcfg->is_enabled == NOT_SET) dcfg->is_enabled = 0;
if (dcfg->reqbody_access == NOT_SET) dcfg->reqbody_access = 0;
if (dcfg->reqintercept_oe == NOT_SET) dcfg->reqintercept_oe = 0;
if (dcfg->reqbody_buffering == NOT_SET) dcfg->reqbody_buffering = REQUEST_BODY_FORCEBUF_OFF;
if (dcfg->reqbody_inmemory_limit == NOT_SET)
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;
if (dcfg->of_limit_action == NOT_SET) dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_REJECT;
if (dcfg->of_mime_types == NOT_SET_P) {
dcfg->of_mime_types = apr_table_make(dcfg->mp, 3);
if (dcfg->of_mime_types_cleared != 1) {
apr_table_setn(dcfg->of_mime_types, "text/plain", "1");
apr_table_setn(dcfg->of_mime_types, "text/html", "1");
}
}
if (dcfg->debuglog_fd == NOT_SET_P) dcfg->debuglog_fd = NULL;
if (dcfg->debuglog_name == NOT_SET_P) dcfg->debuglog_name = NULL;
if (dcfg->debuglog_level == NOT_SET) dcfg->debuglog_level = 0;
if (dcfg->cookie_format == NOT_SET) dcfg->cookie_format = 0;
if (dcfg->argument_separator == NOT_SET) dcfg->argument_separator = '&';
if (dcfg->cookiev0_separator == NOT_SET_P) dcfg->cookiev0_separator = NULL;
if (dcfg->rule_inheritance == NOT_SET) dcfg->rule_inheritance = 1;
/* audit log variables */
if (dcfg->auditlog_flag == NOT_SET) dcfg->auditlog_flag = 0;
if (dcfg->auditlog_type == NOT_SET) dcfg->auditlog_type = AUDITLOG_SERIAL;
#ifdef WITH_YAJL
if (dcfg->auditlog_format == NOT_SET) dcfg->auditlog_format = AUDITLOGFORMAT_NATIVE;
#endif
if (dcfg->max_rule_time == NOT_SET) dcfg->max_rule_time = 0;
if (dcfg->auditlog_dirperms == NOT_SET) dcfg->auditlog_dirperms = CREATEMODE_DIR;
if (dcfg->auditlog_fileperms == NOT_SET) dcfg->auditlog_fileperms = CREATEMODE;
if (dcfg->auditlog_fd == NOT_SET_P) dcfg->auditlog_fd = NULL;
if (dcfg->auditlog2_fd == NOT_SET_P) dcfg->auditlog2_fd = NULL;
if (dcfg->auditlog_name == NOT_SET_P) dcfg->auditlog_name = NULL;
if (dcfg->auditlog2_name == NOT_SET_P) dcfg->auditlog2_name = NULL;
if (dcfg->auditlog_storage_dir == NOT_SET_P) dcfg->auditlog_storage_dir = NULL;
if (dcfg->auditlog_parts == NOT_SET_P) dcfg->auditlog_parts = "ABCFHZ";
if (dcfg->auditlog_relevant_regex == NOT_SET_P) dcfg->auditlog_relevant_regex = NULL;
/* Upload */
if (dcfg->tmp_dir == NOT_SET_P) dcfg->tmp_dir = guess_tmp_dir(dcfg->mp);
if (dcfg->upload_dir == NOT_SET_P) dcfg->upload_dir = NULL;
if (dcfg->upload_keep_files == NOT_SET) dcfg->upload_keep_files = KEEP_FILES_OFF;
if (dcfg->upload_validates_files == NOT_SET) dcfg->upload_validates_files = 0;
if (dcfg->upload_filemode == NOT_SET) dcfg->upload_filemode = 0600;
if (dcfg->upload_file_limit == NOT_SET) dcfg->upload_file_limit = 100;
/* Misc */
if (dcfg->data_dir == NOT_SET_P) dcfg->data_dir = NULL;
if (dcfg->webappid == NOT_SET_P) dcfg->webappid = "default";
if (dcfg->sensor_id == NOT_SET_P) dcfg->sensor_id = "default";
if (dcfg->httpBlkey == NOT_SET_P) dcfg->httpBlkey = NULL;
/* Content injection. */
if (dcfg->content_injection_enabled == NOT_SET) dcfg->content_injection_enabled = 0;
/* Stream inspection */
if (dcfg->stream_inbody_inspection == NOT_SET) dcfg->stream_inbody_inspection = 0;
if (dcfg->stream_outbody_inspection == NOT_SET) dcfg->stream_outbody_inspection = 0;
/* Geo Lookup */
if (dcfg->geo == NOT_SET_P) dcfg->geo = NULL;
/* Gsb Lookup */
if (dcfg->gsb == NOT_SET_P) dcfg->gsb = NULL;
/* Unicode Map */
if (dcfg->u_map == NOT_SET_P) dcfg->u_map = NULL;
/* Cache */
if (dcfg->cache_trans == NOT_SET) dcfg->cache_trans = MODSEC_CACHE_DISABLED;
if (dcfg->cache_trans_incremental == NOT_SET) dcfg->cache_trans_incremental = 0;
if (dcfg->cache_trans_min == (apr_size_t)NOT_SET) dcfg->cache_trans_min = 32;
if (dcfg->cache_trans_max == (apr_size_t)NOT_SET) dcfg->cache_trans_max = 1024;
if (dcfg->cache_trans_maxitems == (apr_size_t)NOT_SET) dcfg->cache_trans_maxitems = 512;
if (dcfg->request_encoding == NOT_SET_P) dcfg->request_encoding = NULL;
if (dcfg->disable_backend_compression == NOT_SET) dcfg->disable_backend_compression = 0;
if (dcfg->col_timeout == NOT_SET) dcfg->col_timeout = 3600;
/* Hash */
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;
if (dcfg->hash_enforcement == NOT_SET) dcfg->hash_enforcement = HASH_DISABLED;
if (dcfg->crypto_hash_href_rx == NOT_SET) dcfg->crypto_hash_href_rx = 0;
if (dcfg->crypto_hash_faction_rx == NOT_SET) dcfg->crypto_hash_faction_rx = 0;
if (dcfg->crypto_hash_location_rx == NOT_SET) dcfg->crypto_hash_location_rx = 0;
if (dcfg->crypto_hash_iframesrc_rx == NOT_SET) dcfg->crypto_hash_iframesrc_rx = 0;
if (dcfg->crypto_hash_framesrc_rx == NOT_SET) dcfg->crypto_hash_framesrc_rx = 0;
if (dcfg->crypto_hash_href_pm == NOT_SET) dcfg->crypto_hash_href_pm = 0;
if (dcfg->crypto_hash_faction_pm == NOT_SET) dcfg->crypto_hash_faction_pm = 0;
if (dcfg->crypto_hash_location_pm == NOT_SET) dcfg->crypto_hash_location_pm = 0;
if (dcfg->crypto_hash_iframesrc_pm == NOT_SET) dcfg->crypto_hash_iframesrc_pm = 0;
if (dcfg->crypto_hash_framesrc_pm == NOT_SET) dcfg->crypto_hash_framesrc_pm = 0;
/* 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;
}
/**
*
*/
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;
#ifdef DEBUG_CONF
ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
"Rule: type=%d p1='%s' p2='%s' p3='%s'", type, p1, p2, p3);
#endif
/* Create a ruleset if one does not exist. */
if ((dcfg->ruleset == NULL)||(dcfg->ruleset == NOT_SET_P)) {
dcfg->ruleset = msre_ruleset_create(modsecurity->msre, cmd->pool);
if (dcfg->ruleset == NULL) return FATAL_ERROR;
}
/* Create the rule now. */
switch(type) {
#if defined(WITH_LUA)
case RULE_TYPE_LUA :
rule = msre_rule_lua_create(dcfg->ruleset, cmd->directive->filename,
cmd->directive->line_num, p1, p2, &my_error_msg);
break;
#endif
default :
rule = msre_rule_create(dcfg->ruleset, type, cmd->directive->filename,
cmd->directive->line_num, p1, p2, p3, &my_error_msg);
break;
}
if (rule == NULL) {
return my_error_msg;
}
#ifndef ALLOW_ID_NOT_UNIQUE
/* Rules must have uniq ID */
type_rule = (dcfg->tmp_chain_starter == NULL);
#if defined(WITH_LUA)
type_rule = (type != RULE_TYPE_LUA && type_rule);
#endif
if (type_rule)
if(rule->actionset == NULL)
return "ModSecurity: Rules must have at least id action";
if(rule->actionset != NULL && (dcfg->tmp_chain_starter == NULL)) {
rule_actionset = (rule->actionset->id == NOT_SET_P);
#if defined(WITH_LUA)
rule_actionset = (rule_actionset && (type != RULE_TYPE_LUA));
#endif
if (rule_actionset)
return "ModSecurity: No action id present within the rule";
#if defined(WITH_LUA)
type_with_lua = (type != RULE_TYPE_LUA);
#endif
if (type_with_lua){
rid = apr_hash_get(dcfg->rule_id_htab, rule->actionset->id, APR_HASH_KEY_STRING);
if(rid != NULL) {
return "ModSecurity: Found another rule with the same id";
} else {
apr_hash_set(dcfg->rule_id_htab, apr_pstrdup(dcfg->mp, rule->actionset->id), APR_HASH_KEY_STRING, apr_pstrdup(dcfg->mp, "1"));
}
//tmp_rule = msre_ruleset_fetch_rule(dcfg->ruleset, rule->actionset->id, offset);
//if(tmp_rule != NULL)
// return "ModSecurity: Found another rule with the same id";
}
}
#endif
/* Create default actionset if one does not already exist. */
if (dcfg->tmp_default_actionset == NULL) {
dcfg->tmp_default_actionset = msre_actionset_create_default(modsecurity->msre);
if (dcfg->tmp_default_actionset == NULL) return FATAL_ERROR;
}
/* Check some cases prior to merging so we know where it came from */
/* Check syntax for chained rules */
if ((rule->actionset != NULL) && (dcfg->tmp_chain_starter != NULL)) {
/* Must NOT specify a disruptive action. */
if (rule->actionset->intercept_action != NOT_SET) {
return apr_psprintf(cmd->pool, "ModSecurity: Disruptive actions can only "
"be specified by chain starter rules.");
}
/* Must NOT specify a skipafter action. */
if (rule->actionset->skip_after != NOT_SET_P) {
return apr_psprintf(cmd->pool, "ModSecurity: SkipAfter actions can only "
"be specified by chain starter rules.");
}
/* Must NOT specify a phase. */
if (rule->actionset->phase != NOT_SET) {
return apr_psprintf(cmd->pool, "ModSecurity: Execution phases can only be "
"specified by chain starter rules.");
}
/* Must NOT use metadata actions. */
/* ENH: loop through to check for tags */
if ((rule->actionset->id != NOT_SET_P)
||(rule->actionset->rev != NOT_SET_P)
||(rule->actionset->msg != NOT_SET_P)
||(rule->actionset->severity != NOT_SET)
||(rule->actionset->version != NOT_SET_P)
||(rule->actionset->accuracy != NOT_SET)
||(rule->actionset->maturity != NOT_SET)
||(rule->actionset->logdata != NOT_SET_P))
{
return apr_psprintf(cmd->pool, "ModSecurity: Metadata actions (id, rev, msg, tag, severity, ver, accuracy, maturity, logdata) "
" can only be specified by chain starter rules.");
}
/* Must NOT use skip. */
if (rule->actionset->skip_count != NOT_SET) {
return apr_psprintf(cmd->pool, "ModSecurity: The skip action can only be used "
" by chain starter rules. ");
}
}
/* Merge actions with the parent.
*
* ENH Probably do not want this done fully for chained rules.
*/
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->phase == PHASE_LOGGING)
&& (rule->actionset->intercept_action != ACTION_ALLOW)
&& (rule->actionset->intercept_action != ACTION_ALLOW_REQUEST)
&& (rule->actionset->intercept_action != ACTION_NONE)
) {
return apr_psprintf(cmd->pool, "ModSecurity: Disruptive actions "
"cannot be specified in the logging phase.");
}
if (dcfg->tmp_chain_starter != NULL) {
rule->chain_starter = dcfg->tmp_chain_starter;
rule->actionset->phase = rule->chain_starter->actionset->phase;
}
if (rule->actionset->is_chained != 1) {
/* If this rule is part of the chain but does
* not want more rules to follow in the chain
* then cut it (the chain).
*/
dcfg->tmp_chain_starter = NULL;
} else {
/* On the other hand, if this rule wants other
* rules to follow it, then start a new chain
* if there isn't one already.
*/
if (dcfg->tmp_chain_starter == NULL) {
dcfg->tmp_chain_starter = rule;
}
}
/* Create skip table if one does not already exist. */
if (dcfg->tmp_rule_placeholders == NULL) {
dcfg->tmp_rule_placeholders = apr_table_make(cmd->pool, 10);
if (dcfg->tmp_rule_placeholders == NULL) return FATAL_ERROR;
}
/* Keep track of any rule IDs we need to skip after */
if (rule->actionset->skip_after != NOT_SET_P) {
char *tmp_id = apr_pstrdup(cmd->pool, rule->actionset->skip_after);
apr_table_setn(dcfg->tmp_rule_placeholders, tmp_id, tmp_id);
#ifdef DEBUG_CONF
ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
"Watching for skipafter target rule id=\"%s\".", tmp_id);
#endif
}
#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, id_log(rule));
#endif
/* Add rule to the recipe. */
if (msre_ruleset_rule_add(dcfg->ruleset, rule, rule->actionset->phase) < 0) {
return "Internal Error: Failed to add rule to the ruleset.";
}
/* Add an additional placeholder if this rule ID is on the list */
if ((rule->actionset->id != NULL) && apr_table_get(dcfg->tmp_rule_placeholders, rule->actionset->id)) {
msre_rule *phrule = apr_palloc(rule->ruleset->mp, sizeof(msre_rule));
if (phrule == NULL) {
return FATAL_ERROR;
}
#ifdef DEBUG_CONF
ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
"Adding placeholder %pp for rule %pp id=\"%s\".", phrule, rule, rule->actionset->id);
#endif
/* shallow copy of original rule with placeholder marked as target */
memcpy(phrule, rule, sizeof(msre_rule));
phrule->placeholder = RULE_PH_SKIPAFTER;
/* Add placeholder. */
if (msre_ruleset_rule_add(dcfg->ruleset, phrule, phrule->actionset->phase) < 0) {
return "Internal Error: Failed to add placeholder to the ruleset.";
}
/* No longer need to search for the ID */
apr_table_unset(dcfg->tmp_rule_placeholders, rule->actionset->id);
}
/* Update the unparsed rule */
rule->unparsed = msre_rule_generate_unparsed(dcfg->ruleset->mp, rule, NULL, NULL, NULL);
return NULL;
}
/**
*
*/
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
ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
"Rule: type=%d p1='%s' p2='%s' p3='%s'", RULE_TYPE_MARKER, p1, p2, p3);
#endif
/* Create a ruleset if one does not exist. */
if ((dcfg->ruleset == NULL)||(dcfg->ruleset == NOT_SET_P)) {
dcfg->ruleset = msre_ruleset_create(modsecurity->msre, cmd->pool);
if (dcfg->ruleset == NULL) return FATAL_ERROR;
}
/* Create the rule now. */
rule = msre_rule_create(dcfg->ruleset, RULE_TYPE_MARKER, cmd->directive->filename, cmd->directive->line_num, p1, p2, p3, &my_error_msg);
if (rule == NULL) {
return my_error_msg;
}
/* This is a marker */
rule->placeholder = RULE_PH_MARKER;
/* Add placeholder to each phase */
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, id_log(rule));
#endif
if (msre_ruleset_rule_add(dcfg->ruleset, rule, p) < 0) {
return "Internal Error: Failed to add marker to the ruleset.";
}
}
/* No longer need to search for the ID */
if (dcfg->tmp_rule_placeholders != NULL) {
apr_table_unset(dcfg->tmp_rule_placeholders, rule->actionset->id);
}
return NULL;
}
/**
*
*/
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)) {
return NULL;
}
#ifdef DEBUG_CONF
ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
"Update rule id=\"%s\" with action \"%s\".", p1, p2);
#endif
/* Fetch the rule */
rule = msre_ruleset_fetch_rule(ruleset, p1, offset);
if (rule == NULL) {
#ifdef DEBUG_CONF
ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool,
"Update rule id=\"%s\" with action \"%s\" failed: Rule not found.", p1, p2);
#endif
return NULL;
}
assert(rule->actionset != NULL);
/* Create a new actionset */
new_actionset = msre_actionset_create(modsecurity->msre, cmd->pool, p2, &my_error_msg);
if (new_actionset == NULL) return FATAL_ERROR;
if (my_error_msg != NULL) return my_error_msg;
/* Must NOT change an id */
if ((new_actionset->id != NOT_SET_P) && (rule->actionset->id != NULL) && (strcmp(rule->actionset->id, new_actionset->id) != 0)) {
return apr_psprintf(cmd->pool, "ModSecurity: Rule IDs cannot be updated via SecRuleUpdateActionById.");
}
/* Must NOT alter the phase */
if ((new_actionset->phase != NOT_SET) && (rule->actionset->phase != new_actionset->phase)) {
return apr_psprintf(cmd->pool, "ModSecurity: Rule phases cannot be updated via SecRuleUpdateActionById.");
}
#ifdef DEBUG_CONF
{
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, id_log(rule), actions);
}
#endif
/* Merge new actions with the rule */
/* 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 */
rule->unparsed = msre_rule_generate_unparsed(ruleset->mp, rule, NULL, NULL, NULL);
#ifdef DEBUG_CONF
{
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, id_log(rule), actions);
}
#endif
return NULL;
}
/* -- Configuration directives -- */
static const char *cmd_action(cmd_parms *cmd, void *_dcfg, const char *p1)
{
return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_ACTION, SECACTION_TARGETS, SECACTION_ARGS, 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);
}
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) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid cookie v0 separator: %s", p1);
}
dcfg->cookiev0_separator = p1;
return NULL;
}
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) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid argument separator: %s", p1);
}
dcfg->argument_separator = p1[0];
return NULL;
}
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;
else
if (strcasecmp(p1, "Off") == 0) dcfg->auditlog_flag = AUDITLOG_OFF;
else
if (strcasecmp(p1, "RelevantOnly") == 0) dcfg->auditlog_flag = AUDITLOG_RELEVANT;
else
return (const char *)apr_psprintf(cmd->pool,
"ModSecurity: Unrecognised parameter value for SecAuditEngine: %s", p1);
return NULL;
}
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;
if (dcfg->auditlog_name[0] == '|') {
const char *pipe_name = dcfg->auditlog_name + 1;
piped_log *pipe_log;
pipe_log = ap_open_piped_log(cmd->pool, pipe_name);
if (pipe_log == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the audit log pipe: %s",
pipe_name);
}
dcfg->auditlog_fd = ap_piped_log_write_fd(pipe_log);
}
else {
const char *file_name = ap_server_root_relative(cmd->pool, dcfg->auditlog_name);
apr_status_t rc;
if (dcfg->auditlog_fileperms == NOT_SET) {
dcfg->auditlog_fileperms = CREATEMODE;
}
rc = apr_file_open(&dcfg->auditlog_fd, file_name,
APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY,
dcfg->auditlog_fileperms, cmd->pool);
if (rc != APR_SUCCESS) {
return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the audit log file: %s",
file_name);
}
}
return NULL;
}
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) {
return apr_psprintf(cmd->pool, "ModSecurity: Cannot configure a secondary audit log without a primary defined: %s", p1);
}
dcfg->auditlog2_name = (char *)p1;
if (dcfg->auditlog2_name[0] == '|') {
const char *pipe_name = ap_server_root_relative(cmd->pool, dcfg->auditlog2_name + 1);
piped_log *pipe_log;
pipe_log = ap_open_piped_log(cmd->pool, pipe_name);
if (pipe_log == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the secondary audit log pipe: %s",
pipe_name);
}
dcfg->auditlog2_fd = ap_piped_log_write_fd(pipe_log);
}
else {
const char *file_name = ap_server_root_relative(cmd->pool, dcfg->auditlog2_name);
apr_status_t rc;
if (dcfg->auditlog_fileperms == NOT_SET) {
dcfg->auditlog_fileperms = CREATEMODE;
}
rc = apr_file_open(&dcfg->auditlog2_fd, file_name,
APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY,
dcfg->auditlog_fileperms, cmd->pool);
if (rc != APR_SUCCESS) {
return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the secondary audit log file: %s",
file_name);
}
}
return NULL;
}
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) {
return apr_psprintf(cmd->pool, "Invalid parts specification for SecAuditLogParts: %s", p1);
}
dcfg->auditlog_parts = (char *)p1;
return NULL;
}
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);
}
return NULL;
}
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;
else
if (strcasecmp(p1, "Concurrent") == 0) dcfg->auditlog_type = AUDITLOG_CONCURRENT;
else
return (const char *)apr_psprintf(cmd->pool,
"ModSecurity: Unrecognised parameter value for SecAuditLogType: %s", p1);
return NULL;
}
#ifdef WITH_YAJL
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;
else
if (strcasecmp(p1, "Native") == 0) dcfg->auditlog_format = AUDITLOGFORMAT_NATIVE;
else
return (const char *)apr_psprintf(cmd->pool,
"ModSecurity: Unrecognised parameter value for SecAuditLogFormat: %s", p1);
return NULL;
}
#endif
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 (strcasecmp(p1, "default") == 0) {
dcfg->auditlog_dirperms = NOT_SET;
}
else {
long int mode = strtol(p1, NULL, 8); /* expects octal mode */
if ((mode == LONG_MAX)||(mode == LONG_MIN)||(mode <= 0)||(mode > 07777)) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecAuditLogDirMode: %s", p1);
}
dcfg->auditlog_dirperms = mode2fileperms(mode);
}
return NULL;
}
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 (strcasecmp(p1, "default") == 0) {
dcfg->auditlog_fileperms = NOT_SET;
}
else {
long int mode = strtol(p1, NULL, 8); /* expects octal mode */
if ((mode == LONG_MAX)||(mode == LONG_MIN)||(mode <= 0)||(mode > 07777)) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecAuditLogFileMode: %s", p1);
}
dcfg->auditlog_fileperms = mode2fileperms(mode);
}
return NULL;
}
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);
return NULL;
}
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;
else
if (strcmp(p1, "1") == 0) dcfg->cookie_format = COOKIES_V1;
else {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid cookie format: %s", p1);
}
return NULL;
}
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) {
return "ModSecurity: SecChrootDir not allowed in VirtualHost";
}
chroot_dir = (char *)p1;
if (getcwd(cwd, 1024) == NULL) {
return "ModSecurity: Failed to get the current working directory";
}
if (chdir(chroot_dir) < 0) {
return apr_psprintf(cmd->pool, "ModSecurity: Failed to chdir to %s, errno=%d (%s)",
chroot_dir, errno, strerror(errno));
}
if (chdir(cwd) < 0) {
return apr_psprintf(cmd->pool, "ModSecurity: Failed to chdir to %s, errno=%d (%s)",
cwd, errno, strerror(errno));
}
return NULL;
}
/**
* Adds component signature to the list of signatures kept in configuration.
*/
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. */
*(char **)apr_array_push(dcfg->component_signatures) = (char *)p1;
return NULL;
}
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;
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) {
return "ModSecurity: SecDataDir not allowed in VirtualHost.";
}
dcfg->data_dir = ap_server_root_relative(cmd->pool, p1);
return NULL;
}
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;
dcfg->debuglog_name = ap_server_root_relative(cmd->pool, p1);
rc = apr_file_open(&dcfg->debuglog_fd, dcfg->debuglog_name,
APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY,
CREATEMODE, cmd->pool);
if (rc != APR_SUCCESS) {
return apr_psprintf(cmd->pool, "ModSecurity: Failed to open debug log file: %s",
dcfg->debuglog_name);
}
return NULL;
}
/**
* \brief Add SecCollectionTimeout configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
*
* \retval NULL On failure
* \retval apr_psprintf On Success
*/
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);
/* max 30 days */
if ((dcfg->col_timeout >= 0)&&(dcfg->col_timeout <= 2592000)) return NULL;
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecCollectionTimeout: %s", p1);
}
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);
if ((dcfg->debuglog_level >= 0)&&(dcfg->debuglog_level <= 9)) return NULL;
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecDebugLogLevel: %s", p1);
}
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;
dcfg->tmp_default_actionset = msre_actionset_create(modsecurity->msre, cmd->pool, p1, &my_error_msg);
if (dcfg->tmp_default_actionset == NULL) {
if (my_error_msg != NULL) return my_error_msg;
else return FATAL_ERROR;
}
/* Must specify a disruptive action. */
/* ENH: Remove this requirement? */
if (dcfg->tmp_default_actionset->intercept_action == NOT_SET) {
return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must specify a disruptive action.");
}
/* Must specify a phase. */
/* ENH: Remove this requirement? */
if (dcfg->tmp_default_actionset->phase == NOT_SET) {
return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must specify a phase.");
}
/* Must not use metadata actions. */
/* ENH: loop through to check for tags */
if ((dcfg->tmp_default_actionset->id != NOT_SET_P)
||(dcfg->tmp_default_actionset->rev != NOT_SET_P)
||(dcfg->tmp_default_actionset->version != NOT_SET_P)
||(dcfg->tmp_default_actionset->maturity != NOT_SET)
||(dcfg->tmp_default_actionset->accuracy != NOT_SET)
||(dcfg->tmp_default_actionset->msg != NOT_SET_P))
{
return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not "
"contain any metadata actions (id, rev, msg, tag, severity, ver, accuracy, maturity, logdata).");
}
/* These are just a warning for now. */
if ((dcfg->tmp_default_actionset->severity != NOT_SET)
||(dcfg->tmp_default_actionset->logdata != NOT_SET_P))
{
ap_log_perror(APLOG_MARK,
APLOG_STARTUP|APLOG_WARNING|APLOG_NOERRNO, 0, cmd->pool,
"ModSecurity: WARNING Using \"severity\" or \"logdata\" in "
"SecDefaultAction is deprecated (%s:%d).",
cmd->directive->filename, cmd->directive->line_num);
}
if (apr_table_get(dcfg->tmp_default_actionset->actions, "t")) {
ap_log_perror(APLOG_MARK,
APLOG_STARTUP|APLOG_WARNING|APLOG_NOERRNO, 0, cmd->pool,
"ModSecurity: WARNING Using transformations in "
"SecDefaultAction is deprecated (%s:%d).",
cmd->directive->filename, cmd->directive->line_num);
}
/* Must not use chain. */
if (dcfg->tmp_default_actionset->is_chained != NOT_SET) {
return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not "
"contain a chain action.");
}
/* Must not use skip. */
if (dcfg->tmp_default_actionset->skip_count != NOT_SET) {
return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not "
"contain a skip action.");
}
/* Must not use skipAfter. */
if (dcfg->tmp_default_actionset->skip_after != NOT_SET_P) {
return apr_psprintf(cmd->pool, "ModSecurity: SecDefaultAction must not "
"contain a skipAfter action.");
}
return NULL;
}
static const char *cmd_disable_backend_compression(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_disable_backend_compression: _dcfg is NULL");
return NULL;
}
directory_config* dcfg = (directory_config*)_dcfg;
dcfg->disable_backend_compression = flag;
return NULL;
}
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;
if (cmd->server->is_virtual) {
return "ModSecurity: SecGuardianLog not allowed in VirtualHost";
}
if (p2 != NULL) {
if (strncmp(p2, "env=", 4) != 0) {
return "ModSecurity: Error in condition clause";
}
if ( (p2[4] == '\0') || ((p2[4] == '!')&&(p2[5] == '\0')) ) {
return "ModSecurity: Missing variable name";
}
guardianlog_condition = apr_pstrdup(cmd->pool, p2 + 4);
}
guardianlog_name = (char *)p1;
if (guardianlog_name[0] == '|') {
const char *pipe_name = ap_server_root_relative(cmd->pool, guardianlog_name + 1);
piped_log *pipe_log;
pipe_log = ap_open_piped_log(cmd->pool, pipe_name);
if (pipe_log == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the guardian log pipe: %s",
pipe_name);
}
guardianlog_fd = ap_piped_log_write_fd(pipe_log);
}
else {
const char *file_name = ap_server_root_relative(cmd->pool, guardianlog_name);
apr_status_t rc;
rc = apr_file_open(&guardianlog_fd, file_name,
APR_WRITE | APR_APPEND | APR_CREATE | APR_BINARY,
CREATEMODE, cmd->pool);
if (rc != APR_SUCCESS) {
return apr_psprintf(cmd->pool, "ModSecurity: Failed to open the guardian log file: %s",
file_name);
}
}
return NULL;
}
/**
* \brief Add SecStreamInBodyInspection configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
*
* \retval NULL On failure
* \retval apr_psprintf On Success
*/
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;
dcfg->stream_inbody_inspection = flag;
return NULL;
}
/**
* \brief Add SecStreamOutBodyInspection configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
*
* \retval NULL On failure
* \retval apr_psprintf On Success
*/
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;
dcfg->stream_outbody_inspection = flag;
return NULL;
}
/**
* \brief Add SecRulePerfTime configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
*
* \retval NULL On failure
* \retval apr_psprintf On Success
*/
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;
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);
}
dcfg->max_rule_time = limit;
return NULL;
}
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);
if ((strncasecmp(p2, "!@ipMatchFromFile", strlen("!@ipMatchFromFile")) == 0) ||
(strncasecmp(p2, "!@ipMatchF", strlen("!@ipMatchF")) == 0)) {
res = ip_tree_from_file(whitelist, file, mp, &error_msg);
}
else if (strncasecmp(p2, "!@ipMatch", strlen("!@ipMatch")) == 0) {
res = ip_tree_from_param(mp, param, whitelist, &error_msg);
}
else if ((strncasecmp(p2, "@ipMatchFromFile", strlen("@ipMatchFromFile")) == 0) ||
(strncasecmp(p2, "@ipMatchF", strlen("@ipMatchF")) == 0)) {
res = ip_tree_from_file(suspicious_list, file, mp, &error_msg);
}
else if (strncasecmp(p2, "@ipMatch", strlen("@ipMatch")) == 0) {
res = ip_tree_from_param(mp, param, suspicious_list, &error_msg);
}
else {
return apr_psprintf(mp, "ModSecurity: Invalid operator for " \
"SecConnReadStateLimit: %s, expected operators: @ipMatch, @ipMatchF " \
"or @ipMatchFromFile with or without !", p2);
}
if (res) {
char *error;
error = apr_psprintf(mp, "ModSecurity: failed to load IPs " \
"from: %s", param);
if (*error_msg) {
error = apr_psprintf(mp, "%s %s", error, error_msg);
}
return error;
}
return NULL;
}
/**
* \brief Add SecConnReadStateLimit configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
* \param p2 Pointer to configuration option
*
* \retval NULL On failure
* \retval apr_psprintf On Success
*/
static const char *cmd_conn_read_state_limit(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_conn_read_state_limit: _dcfg is NULL");
return NULL;
}
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 " \
"SecConnReadStateLimit: %s", p1);
}
if (p2 != NULL) {
char *param = parser_conn_limits_operator(cmd->pool, p2,
&conn_read_state_whitelist, &conn_read_state_suspicious_list,
cmd->directive->filename);
if (param)
return param;
}
conn_read_state_limit = limit;
return NULL;
}
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.");
return cmd_conn_read_state_limit(cmd, _dcfg, p1, p2);
}
/**
* \brief Add SecConnWriteStateLimit configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
* \param p2 Pointer to configuration option
*
* \retval NULL On failure
* \retval apr_psprintf On Success
*/
static const char *cmd_conn_write_state_limit(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_conn_write_state_limit: _dcfg is NULL");
return NULL;
}
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 " \
"SecConnWriteStateLimit: %s", p1);
}
if (p2 != NULL) {
char *param = parser_conn_limits_operator(cmd->pool, p2,
&conn_write_state_whitelist, &conn_write_state_suspicious_list,
cmd->directive->filename);
if (param)
return param;
}
conn_write_state_limit = limit;
return NULL;
}
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.");
return cmd_conn_write_state_limit(cmd, _dcfg, p1, p2);
}
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;
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);
}
dcfg->reqbody_inmemory_limit = limit;
return NULL;
}
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;
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);
}
dcfg->reqbody_limit = limit;
return NULL;
}
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;
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);
}
dcfg->reqbody_no_files_limit = limit;
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 (strcasecmp(p1, "on") == 0) dcfg->reqbody_access = 1;
else
if (strcasecmp(p1, "off") == 0) dcfg->reqbody_access = 0;
else
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyAccess: %s", p1);
return NULL;
}
/**
* \brief Add SecInterceptOnError configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
*
* \retval NULL On failure
* \retval apr_psprintf On success
*/
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 (strcasecmp(p1, "on") == 0) dcfg->reqintercept_oe = 1;
else
if (strcasecmp(p1, "off") == 0) dcfg->reqintercept_oe = 0;
else
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecInterceptOnError: %s", p1);
return NULL;
}
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;
/* ENH Validate encoding */
dcfg->request_encoding = p1;
return NULL;
}
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 (strcasecmp(p1, "on") == 0) dcfg->resbody_access = 1;
else
if (strcasecmp(p1, "off") == 0) dcfg->resbody_access = 0;
else
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecResponseBodyAccess: %s", p1);
return NULL;
}
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;
limit = strtol(p1, NULL, 10);
if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecResponseBodyLimit: %s", p1);
}
if (limit > RESPONSE_BODY_HARD_LIMIT) {
return apr_psprintf(cmd->pool, "ModSecurity: Response size limit can not exceed the hard limit: %li", RESPONSE_BODY_HARD_LIMIT);
}
dcfg->of_limit = limit;
return NULL;
}
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->is_enabled == MODSEC_DETECTION_ONLY) {
dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_PARTIAL;
return NULL;
}
if (strcasecmp(p1, "ProcessPartial") == 0) dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_PARTIAL;
else
if (strcasecmp(p1, "Reject") == 0) dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_REJECT;
else
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecResponseBodyLimitAction: %s", p1);
return NULL;
}
/**
* \brief Add SecRequestBodyLimitAction configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
*
* \retval NULL On failure
* \retval apr_psprintf On success
*/
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->is_enabled == MODSEC_DETECTION_ONLY) {
dcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_PARTIAL;
return NULL;
}
if (strcasecmp(p1, "ProcessPartial") == 0) dcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_PARTIAL;
else
if (strcasecmp(p1, "Reject") == 0) dcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_REJECT;
else
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecRequestBodyLimitAction: %s", p1);
return NULL;
}
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);
/* TODO check whether the parameter is a valid MIME type of "???" */
if ((dcfg->of_mime_types == NULL)||(dcfg->of_mime_types == NOT_SET_P)) {
dcfg->of_mime_types = apr_table_make(cmd->pool, 10);
}
strtolower_inplace((unsigned char *)p1);
apr_table_setn(dcfg->of_mime_types, p1, "1");
return NULL;
}
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;
dcfg->of_mime_types_cleared = 1;
if ((dcfg->of_mime_types != NULL)&&(dcfg->of_mime_types != NOT_SET_P)) {
apr_table_clear(dcfg->of_mime_types);
}
return NULL;
}
/**
* \brief Add SecRuleUpdateTargetById
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
* \param p2 Pointer to configuration option
* \param p3 Pointer to configuration option
*
* \retval NULL On failure|Success
*/
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(p1 == NULL) {
return apr_psprintf(cmd->pool, "Updating target by ID with no ID");
}
re->type = RULE_EXCEPTION_REMOVE_ID;
/* TODO: Validate the range here, while we can still tell the user if it's invalid */
re->param = p1;
if(dcfg->ruleset == NULL) {
return apr_psprintf(cmd->pool, "Updating target by ID with no ruleset in this context");
}
return msre_ruleset_rule_update_target_matching_exception(NULL, dcfg->ruleset, re, p2, p3);
}
/**
* \brief Add SecRuleUpdateTargetByTag configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option RULETAG
* \param p2 Pointer to configuration option TARGET
* \param p3 Pointer to configuration option REPLACED_TARGET
* \todo Finish documenting
*
* \retval NULL On success
* \retval apr_psprintf On failure
*
* \todo Figure out error checking
*/
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(p1 == NULL) {
return apr_psprintf(cmd->pool, "Updating target by tag with no tag");
}
re->type = RULE_EXCEPTION_REMOVE_TAG;
re->param = p1;
re->param_data = msc_pregcomp(cmd->pool, p1, 0, NULL, NULL);
if (re->param_data == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p1);
}
return msre_ruleset_rule_update_target_matching_exception(NULL, dcfg->ruleset, re, p2, p3);
}
/**
* \brief Add SecRuleUpdateTargetByMsg configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option RULEMSG
* \param p2 Pointer to configuration option TARGET
* \param p3 Pointer to configuration option REPLACED_TARGET
* \todo Finish documenting
*
* \retval NULL On success
* \retval apr_psprintf On failure
*
* \todo Figure out error checking
*/
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(p1 == NULL) {
return apr_psprintf(cmd->pool, "Updating target by message with no message");
}
re->type = RULE_EXCEPTION_REMOVE_MSG;
re->param = p1;
re->param_data = msc_pregcomp(cmd->pool, p1, 0, NULL, NULL);
if (re->param_data == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p1);
}
return msre_ruleset_rule_update_target_matching_exception(NULL, dcfg->ruleset, re, p2, p3);
}
static const char *cmd_rule(cmd_parms *cmd, void *_dcfg,
const char *p1, const char *p2, const char *p3)
{
return add_rule(cmd, (directory_config *)_dcfg, RULE_TYPE_NORMAL, p1, p2, p3);
}
static const char *cmd_sever_conn_filters_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_sever_conn_filters_engine: _dcfg is NULL");
return NULL;
}
if (strcasecmp(p1, "on") == 0)
{
conn_limits_filter_state = MODSEC_ENABLED;
}
else if (strcasecmp(p1, "off") == 0)
{
conn_limits_filter_state = MODSEC_DISABLED;
}
else if (strcasecmp(p1, "detectiononly") == 0)
{
conn_limits_filter_state = MODSEC_DETECTION_ONLY;
}
else
{
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for " \
"SecConnEngine: %s", p1);
}
return NULL;
}
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 (strcasecmp(p1, "on") == 0)
{
dcfg->is_enabled = MODSEC_ENABLED;
}
else if (strcasecmp(p1, "off") == 0)
{
dcfg->is_enabled = MODSEC_DISABLED;
}
else if (strcasecmp(p1, "detectiononly") == 0)
{
dcfg->is_enabled = MODSEC_DETECTION_ONLY;
dcfg->of_limit_action = RESPONSE_BODY_LIMIT_ACTION_PARTIAL;
dcfg->if_limit_action = REQUEST_BODY_LIMIT_ACTION_PARTIAL;
}
else
{
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for " \
"SecRuleEngine: %s", p1);
}
return NULL;
}
static const char *cmd_remote_rules_fail(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_remote_rules_fail: _dcfg is NULL");
return NULL;
}
if (strncasecmp(p1, "warn", 4) == 0)
{
remote_rules_fail_action = REMOTE_RULES_WARN_ON_FAIL;
}
else if (strncasecmp(p1, "abort", 5) == 0)
{
remote_rules_fail_action = REMOTE_RULES_ABORT_ON_FAIL;
}
else
{
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for " \
"SecRemoteRulesFailAction, expected: Abort or Warn.");
}
return NULL;
}
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;
const char *uri = p2;
const char *key = p1;
#endif
#ifdef WITH_REMOTE_RULES
if (strncasecmp(p1, "crypto", 6) == 0)
{
#ifdef WITH_APU_CRYPTO
uri = p3;
key = p2;
crypto = 1;
#else
return apr_psprintf(cmd->pool, "ModSecurity: SecRemoteRule using " \
"`crypto' but ModSecurity was not compiled with crypto " \
"support.");
#endif
}
if (uri == NULL || key == NULL)
{
return apr_psprintf(cmd->pool, "ModSecurity: Use SecRemoteRule with " \
"Key and URI");
}
if (strncasecmp(uri, "https", 5) != 0) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid URI:" \
" '%s'. Expected HTTPS.", uri);
}
// FIXME: Should we handle more then one server at once?
if (remote_rules_server != NULL)
{
return apr_psprintf(cmd->pool, "ModSecurity: " \
"SecRemoteRules cannot be used more than once.");
}
remote_rules_server = apr_pcalloc(cmd->pool, sizeof(msc_remote_rules_server));
if (remote_rules_server == NULL)
{
return apr_psprintf(cmd->pool, "ModSecurity: " \
"SecRemoteRules: Internal failure. Not enougth memory.");
}
remote_rules_server->context = dcfg;
remote_rules_server->context_label = apr_pstrdup(cmd->pool, "Unkwon context");
remote_rules_server->key = key;
remote_rules_server->uri = uri;
remote_rules_server->amount_of_rules = 0;
remote_rules_server->crypto = crypto;
msc_remote_add_rules_from_uri(cmd, remote_rules_server, &error_msg);
if (error_msg != NULL)
{
return error_msg;
}
#else
return apr_psprintf(cmd->pool, "ModSecurity: SecRemoteRules: " \
"ModSecurity was not compiled with SecRemoteRules support.");
#endif
return NULL;
}
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;
}
else if (strcasecmp(p1, "off") == 0) {
status_engine_state = STATUS_ENGINE_DISABLED;
}
else {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for " \
"SecStatusEngine: %s", p1);
}
return NULL;
}
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;
dcfg->rule_inheritance = flag;
return NULL;
}
static const char *cmd_rule_script(cmd_parms *cmd, void *_dcfg,
const char *p1, const char *p2)
{
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
ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Ignoring SecRuleScript \"%s\" directive (%s:%d): No Lua scripting support.", p1, cmd->directive->filename, cmd->directive->line_num);
return NULL;
#endif
}
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 (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;
*(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re;
/* Remove the corresponding rules from the context straight away. */
msre_ruleset_rule_remove_with_exception(dcfg->ruleset, re);
return NULL;
}
/**
* \brief Add SecRuleRemoveByTag configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
*
* \retval NULL On failure
* \retval apr_psprintf On success
*/
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));
re->type = RULE_EXCEPTION_REMOVE_TAG;
re->param = p1;
re->param_data = msc_pregcomp(cmd->pool, p1, 0, NULL, NULL);
if (re->param_data == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p1);
}
*(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re;
/* Remove the corresponding rules from the context straight away. */
msre_ruleset_rule_remove_with_exception(dcfg->ruleset, re);
#ifdef DEBUG_CONF
ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Added exception %pp (%d %s) to dcfg %pp.", re, re->type, re->param, dcfg);
#endif
return NULL;
}
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));
re->type = RULE_EXCEPTION_REMOVE_MSG;
re->param = p1;
re->param_data = msc_pregcomp(cmd->pool, p1, 0, NULL, NULL);
if (re->param_data == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p1);
}
*(rule_exception **)apr_array_push(dcfg->rule_exceptions) = re;
/* Remove the corresponding rules from the context straight away. */
msre_ruleset_rule_remove_with_exception(dcfg->ruleset, re);
#ifdef DEBUG_CONF
ap_log_perror(APLOG_MARK, APLOG_STARTUP|APLOG_NOERRNO, 0, cmd->pool, "Added exception %pp (%d %s) to dcfg %pp.", re, re->type, re->param, dcfg);
#endif
return NULL;
}
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;
char *param = apr_pstrdup(cmd->pool, p1);
if ((rule_id == LONG_MAX)||(rule_id == LONG_MIN)||(rule_id <= 0)) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for ID for update action: %s", p1);
}
if(opt != NULL) {
opt++;
offset = atoi(opt);
opt = apr_strtok(param,":", &savedptr);
return update_rule_action(cmd, (directory_config *)_dcfg, (const char *)opt, p2, offset);
}
return update_rule_action(cmd, (directory_config *)_dcfg, p1, p2, offset);
}
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";
}
new_server_signature = (char *)p1;
return NULL;
}
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 (strcasecmp(p1, "none") == 0) dcfg->tmp_dir = NULL;
else dcfg->tmp_dir = ap_server_root_relative(cmd->pool, p1);
return NULL;
}
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 (strcasecmp(p1, "none") == 0) dcfg->upload_dir = NULL;
else dcfg->upload_dir = ap_server_root_relative(cmd->pool, p1);
return NULL;
}
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 (strcasecmp(p1, "default") == 0) {
dcfg->upload_file_limit = NOT_SET;
}
else {
dcfg->upload_file_limit = atoi(p1);
}
return NULL;
}
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 (strcasecmp(p1, "default") == 0) {
dcfg->upload_filemode = NOT_SET;
}
else {
long int mode = strtol(p1, NULL, 8); /* expects octal mode */
if ((mode == LONG_MAX)||(mode == LONG_MIN)||(mode <= 0)||(mode > 07777)) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecUploadFileMode: %s", p1);
}
dcfg->upload_filemode = (int)mode;
}
return NULL;
}
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 (strcasecmp(p1, "on") == 0) {
dcfg->upload_keep_files = KEEP_FILES_ON;
} else
if (strcasecmp(p1, "off") == 0) {
dcfg->upload_keep_files = KEEP_FILES_OFF;
} else
if (strcasecmp(p1, "relevantonly") == 0) {
dcfg->upload_keep_files = KEEP_FILES_RELEVANT_ONLY;
} else {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for SecUploadKeepFiles: %s",
p1);
}
return NULL;
}
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 (strcasecmp(p1, "on") == 0)
{
dcfg->upload_validates_files = 1;
}
else if (strcasecmp(p1, "off") == 0)
{
dcfg->upload_validates_files = 0;
}
else
{
return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for SecTmpSaveUploadedFiles: %s",
p1);
}
return NULL;
}
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, ., _, -) */
dcfg->webappid = p1;
return NULL;
}
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, ., _, -) */
dcfg->sensor_id = p1;
return NULL;
}
/**
* \brief Add SecXmlExternalEntity configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
*
* \retval NULL On failure
* \retval apr_psprintf On Success
*/
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 (strcasecmp(p1, "on") == 0) {
dcfg->xml_external_entity = 1;
}
else if (strcasecmp(p1, "off") == 0) {
dcfg->xml_external_entity = 0;
}
else return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecXmlExternalEntity: %s", p1);
return NULL;
}
/**
* \brief Add SecHashEngine configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
*
* \retval NULL On failure
* \retval apr_psprintf On Success
*/
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 (strcasecmp(p1, "on") == 0) {
dcfg->hash_is_enabled = HASH_ENABLED;
dcfg->hash_enforcement = HASH_ENABLED;
}
else if (strcasecmp(p1, "off") == 0) {
dcfg->hash_is_enabled = HASH_DISABLED;
dcfg->hash_enforcement = HASH_DISABLED;
}
else return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecHashEngine: %s", p1);
return NULL;
}
/**
* \brief Add SecHashParam configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
*
* \retval NULL On success
*/
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;
dcfg->crypto_param_name = p1;
return NULL;
}
/**
* \brief Add SecHashKey configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param _p1 Pointer to configuration option
* \param _p2 Pointer to configuration option
*
* \retval NULL On success
*/
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 (strcasecmp(_p1, "Rand") == 0) {
p1 = apr_pstrdup(cmd->pool, getkey(cmd->pool));
dcfg->crypto_key = p1;
dcfg->crypto_key_len = strlen(dcfg->crypto_key);
} else {
p1 = apr_pstrdup(cmd->pool, _p1);
dcfg->crypto_key = p1;
dcfg->crypto_key_len = strlen(p1);
}
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;
}
/**
* \brief Add SecHashMethodPm configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
* \param p2 Pointer to configuration option
*
* \retval NULL On failure
* \retval apr_psprintf On Success
*/
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);
ACMP *p = NULL;
const char *phrase = NULL;
const char *next = NULL;
p = acmp_create(0, cmd->pool);
if (p == NULL) return NULL;
if(phrase == NULL)
phrase = apr_pstrdup(cmd->pool, _p2);
for (;;) {
while((apr_isspace(*phrase) != 0) && (*phrase != '\0')) phrase++;
if (*phrase == '\0') break;
next = phrase;
while((apr_isspace(*next) == 0) && (*next != 0)) next++;
acmp_add_pattern(p, phrase, NULL, NULL, next - phrase);
phrase = next;
}
acmp_prepare(p);
if (strcasecmp(p1, "HashHref") == 0) {
re->type = HASH_URL_HREF_HASH_PM;
re->param = _p2;
re->param_data = (void *)p;
if (re->param_data == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid pattern: %s", p2);
}
dcfg->crypto_hash_href_pm = 1;
}
else if (strcasecmp(p1, "HashFormAction") == 0) {
re->type = HASH_URL_FACTION_HASH_PM;
re->param = _p2;
re->param_data = (void *)p;
if (re->param_data == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid pattern: %s", p2);
}
dcfg->crypto_hash_faction_pm = 1;
}
else if (strcasecmp(p1, "HashLocation") == 0) {
re->type = HASH_URL_LOCATION_HASH_PM;
re->param = _p2;
re->param_data = (void *)p;
if (re->param_data == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid pattern: %s", p2);
}
dcfg->crypto_hash_location_pm = 1;
}
else if (strcasecmp(p1, "HashIframeSrc") == 0) {
re->type = HASH_URL_IFRAMESRC_HASH_PM;
re->param = _p2;
re->param_data = (void *)p;
if (re->param_data == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid pattern: %s", p2);
}
dcfg->crypto_hash_iframesrc_pm = 1;
}
else if (strcasecmp(p1, "HashFrameSrc") == 0) {
re->type = HASH_URL_FRAMESRC_HASH_PM;
re->param = _p2;
re->param_data = (void *)p;
if (re->param_data == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid pattern: %s", p2);
}
dcfg->crypto_hash_framesrc_pm = 1;
}
*(hash_method **)apr_array_push(dcfg->hash_method) = re;
return NULL;
}
/**
* \brief Add SecHashMethodRx configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
* \param p2 Pointer to configuration option
*
* \retval NULL On failure
* \retval apr_psprintf On Success
*/
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 (strcasecmp(p1, "HashHref") == 0) {
re->type = HASH_URL_HREF_HASH_RX;
re->param = _p2;
re->param_data = msc_pregcomp(cmd->pool, p2, 0, NULL, NULL);
if (re->param_data == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p2);
}
dcfg->crypto_hash_href_rx = 1;
}
else if (strcasecmp(p1, "HashFormAction") == 0) {
re->type = HASH_URL_FACTION_HASH_RX;
re->param = _p2;
re->param_data = msc_pregcomp(cmd->pool, p2, 0, NULL, NULL);
if (re->param_data == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p2);
}
dcfg->crypto_hash_faction_rx = 1;
}
else if (strcasecmp(p1, "HashLocation") == 0) {
re->type = HASH_URL_LOCATION_HASH_RX;
re->param = _p2;
re->param_data = msc_pregcomp(cmd->pool, p2, 0, NULL, NULL);
if (re->param_data == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p2);
}
dcfg->crypto_hash_location_rx = 1;
}
else if (strcasecmp(p1, "HashIframeSrc") == 0) {
re->type = HASH_URL_IFRAMESRC_HASH_RX;
re->param = _p2;
re->param_data = msc_pregcomp(cmd->pool, p2, 0, NULL, NULL);
if (re->param_data == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p2);
}
dcfg->crypto_hash_iframesrc_rx = 1;
}
else if (strcasecmp(p1, "HashFrameSrc") == 0) {
re->type = HASH_URL_FRAMESRC_HASH_RX;
re->param = _p2;
re->param_data = msc_pregcomp(cmd->pool, p2, 0, NULL, NULL);
if (re->param_data == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid regular expression: %s", p2);
}
dcfg->crypto_hash_framesrc_rx = 1;
}
*(hash_method **)apr_array_push(dcfg->hash_method) = re;
return NULL;
}
/**
* \brief Add SecHttpBlKey configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
*
* \retval NULL On success
*/
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;
dcfg->httpBlkey = p1;
return NULL;
}
/* PCRE Limits */
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) {
return "ModSecurity: SecPcreMatchLimit not allowed in VirtualHost";
}
val = atol(p1);
if (val <= 0) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for "
"SecPcreMatchLimit: %s", p1);
}
msc_pcre_match_limit = (unsigned long int)val;
return NULL;
}
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) {
return "ModSecurity: SecPcreMatchLimitRecursion not allowed in VirtualHost";
}
val = atol(p1);
if (val <= 0) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for "
"SecPcreMatchLimitRecursion: %s", p1);
}
msc_pcre_match_limit_recursion = (unsigned long int)val;
return NULL;
}
/* -- Geo Lookup configuration -- */
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 (geo_init(dcfg, filename, &error_msg) <= 0) {
return error_msg;
}
return NULL;
}
/**
* \brief Add SecUnicodeCodePage configuration option
*
* Depcrecated
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
*
* \retval NULL On success
*/
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);
if (val <= 0) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for "
"SecUnicodeCodePage: %s", p1);
}
unicode_codepage = (unsigned long int)val;
return NULL;
}
/**
* \brief Add SecUnicodeMapFile configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
*
* \retval NULL On success
*/
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(p2 != NULL) {
val = atol(p2);
if (val <= 0) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid setting for "
"SecUnicodeMapFile: %s", p2);
}
unicode_codepage = (unsigned long int)val;
}
if (unicode_map_init(dcfg, filename, &error_msg) <= 0) {
return error_msg;
}
return NULL;
}
/**
* \brief Add SecGsbLookupDb configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
*
* \retval NULL On success
*/
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 (gsb_db_init(dcfg, filename, &error_msg) <= 0) {
return error_msg;
}
return NULL;
}
/* -- Cache -- */
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 (strcasecmp(p1, "on") == 0)
dcfg->cache_trans = MODSEC_CACHE_ENABLED;
else if (strcasecmp(p1, "off") == 0)
dcfg->cache_trans = MODSEC_CACHE_DISABLED;
else
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecCacheTransformations: %s", p1);
/* Process options */
if (p2 != NULL) {
apr_table_t *vartable = apr_table_make(cmd->pool, 4);
apr_status_t rc;
char *error_msg = NULL;
const char *charval = NULL;
apr_int64_t intval = 0;
if (vartable == NULL) {
return apr_psprintf(cmd->pool, "ModSecurity: Unable to process options for SecCacheTransformations");
}
rc = msre_parse_generic(cmd->pool, p2, vartable, &error_msg);
if (rc < 0) {
return apr_psprintf(cmd->pool, "ModSecurity: Unable to parse options for SecCacheTransformations: %s", error_msg);
}
/* incremental */
charval = apr_table_get(vartable, "incremental");
if (charval != NULL) {
if (strcasecmp(charval, "on") == 0)
dcfg->cache_trans_incremental = 1;
else if (strcasecmp(charval, "off") == 0)
dcfg->cache_trans_incremental = 0;
else
return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations invalid incremental value: %s", charval);
}
/* minlen */
charval = apr_table_get(vartable, "minlen");
if (charval != NULL) {
intval = apr_atoi64(charval);
if (errno == ERANGE) {
return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen out of range: %s", charval);
}
if (intval < 0) {
return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen must be positive: %s", charval);
}
/* The NOT_SET indicator is -1, a signed long, and therfore
* we cannot be >= the unsigned value of NOT_SET.
*/
if ((unsigned long)intval >= (unsigned long)NOT_SET) {
return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations minlen must be less than: %lu", (unsigned long)NOT_SET);
}
dcfg->cache_trans_min = (apr_size_t)intval;
}
/* maxlen */
charval = apr_table_get(vartable, "maxlen");
if (charval != NULL) {
intval = apr_atoi64(charval);
if (errno == ERANGE) {
return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen out of range: %s", charval);
}
if (intval < 0) {
return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must be positive: %s", charval);
}
/* The NOT_SET indicator is -1, a signed long, and therfore
* we cannot be >= the unsigned value of NOT_SET.
*/
if ((unsigned long)intval >= (unsigned long)NOT_SET) {
return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must be less than: %lu", (unsigned long)NOT_SET);
}
if ((intval != 0) && ((apr_size_t)intval < dcfg->cache_trans_min)) {
return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxlen must not be less than minlen: %lu < %" APR_SIZE_T_FMT, (unsigned long)intval, dcfg->cache_trans_min);
}
dcfg->cache_trans_max = (apr_size_t)intval;
}
/* maxitems */
charval = apr_table_get(vartable, "maxitems");
if (charval != NULL) {
intval = apr_atoi64(charval);
if (errno == ERANGE) {
return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxitems out of range: %s", charval);
}
if (intval < 0) {
return apr_psprintf(cmd->pool, "ModSecurity: SecCacheTransformations maxitems must be positive: %s", charval);
}
dcfg->cache_trans_maxitems = (apr_size_t)intval;
}
}
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 -- */
#define CMD_SCOPE_MAIN (RSRC_CONF)
#define CMD_SCOPE_ANY (RSRC_CONF | ACCESS_CONF)
#if defined(HTACCESS_CONFIG)
#define CMD_SCOPE_HTACCESS (OR_OPTIONS)
#endif
const command_rec module_directives[] = {
#ifdef HTACCESS_CONFIG
AP_INIT_TAKE1 (
"SecAction",
cmd_action,
NULL,
CMD_SCOPE_HTACCESS,
"an action list"
),
#else
AP_INIT_TAKE1 (
"SecAction",
cmd_action,
NULL,
CMD_SCOPE_ANY,
"an action list"
),
#endif
AP_INIT_TAKE1 (
"SecArgumentSeparator",
cmd_argument_separator,
NULL,
CMD_SCOPE_ANY,
"character that will be used as separator when parsing application/x-www-form-urlencoded content."
),
AP_INIT_TAKE1 (
"SecCookiev0Separator",
cmd_cookiev0_separator,
NULL,
CMD_SCOPE_ANY,
"character that will be used as separator when parsing cookie v0 content."
),
AP_INIT_TAKE1 (
"SecAuditEngine",
cmd_audit_engine,
NULL,
CMD_SCOPE_ANY,
"On, Off or RelevantOnly to determine the level of audit logging"
),
AP_INIT_TAKE1 (
"SecAuditLog",
cmd_audit_log,
NULL,
CMD_SCOPE_ANY,
"filename of the primary audit log file"
),
AP_INIT_TAKE1 (
"SecAuditLog2",
cmd_audit_log2,
NULL,
CMD_SCOPE_ANY,
"filename of the secondary audit log file"
),
AP_INIT_TAKE1 (
"SecAuditLogParts",
cmd_audit_log_parts,
NULL,
CMD_SCOPE_ANY,
"list of audit log parts that go into the log."
),
AP_INIT_TAKE1 (
"SecAuditLogRelevantStatus",
cmd_audit_log_relevant_status,
NULL,
CMD_SCOPE_ANY,
"regular expression that will be used to determine if the response status is relevant for audit logging"
),
AP_INIT_TAKE1 (
"SecAuditLogType",
cmd_audit_log_type,
NULL,
CMD_SCOPE_ANY,
"whether to use the old audit log format (Serial) or new (Concurrent)"
),
#ifdef WITH_YAJL
AP_INIT_TAKE1 (
"SecAuditLogFormat",
cmd_audit_log_mode,
NULL,
CMD_SCOPE_ANY,
"whether to emit audit log data in native format or JSON"
),
#endif
AP_INIT_TAKE1 (
"SecAuditLogStorageDir",
cmd_audit_log_storage_dir,
NULL,
CMD_SCOPE_ANY,
"path to the audit log storage area; absolute, or relative to the root of the server"
),
AP_INIT_TAKE1 (
"SecAuditLogDirMode",
cmd_audit_log_dirmode,
NULL,
CMD_SCOPE_ANY,
"octal permissions mode for concurrent audit log directories"
),
AP_INIT_TAKE1 (
"SecAuditLogFileMode",
cmd_audit_log_filemode,
NULL,
CMD_SCOPE_ANY,
"octal permissions mode for concurrent audit log files"
),
AP_INIT_TAKE12 (
"SecCacheTransformations",
cmd_cache_transformations,
NULL,
CMD_SCOPE_ANY,
"whether or not to cache transformations. Defaults to true."
),
AP_INIT_TAKE1 (
"SecChrootDir",
cmd_chroot_dir,
NULL,
CMD_SCOPE_MAIN,
"path of the directory to which server will be chrooted"
),
AP_INIT_TAKE1 (
"SecComponentSignature",
cmd_component_signature,
NULL,
CMD_SCOPE_MAIN,
"component signature to add to ModSecurity signature."
),
AP_INIT_FLAG (
"SecContentInjection",
cmd_content_injection,
NULL,
CMD_SCOPE_ANY,
"On or Off"
),
AP_INIT_FLAG (
"SecStreamOutBodyInspection",
cmd_stream_outbody_inspection,
NULL,
CMD_SCOPE_ANY,
"On or Off"
),
AP_INIT_FLAG (
"SecStreamInBodyInspection",
cmd_stream_inbody_inspection,
NULL,
CMD_SCOPE_ANY,
"On or Off"
),
AP_INIT_TAKE1 (
"SecCookieFormat",
cmd_cookie_format,
NULL,
CMD_SCOPE_ANY,
"version of the Cookie specification to use for parsing. Possible values are 0 and 1."
),
AP_INIT_TAKE1 (
"SecDataDir",
cmd_data_dir,
NULL,
CMD_SCOPE_MAIN,
"path to the persistent data storage area" // TODO
),
AP_INIT_TAKE1 (
"SecDebugLog",
cmd_debug_log,
NULL,
CMD_SCOPE_ANY,
"path to the debug log file"
),
AP_INIT_TAKE1 (
"SecDebugLogLevel",
cmd_debug_log_level,
NULL,
CMD_SCOPE_ANY,
"debug log level, which controls the verbosity of logging."
" Use values from 0 (no logging) to 9 (a *lot* of logging)."
),
AP_INIT_TAKE1 (
"SecCollectionTimeout",
cmd_collection_timeout,
NULL,
CMD_SCOPE_ANY,
"set default collections timeout. default it 3600"
),
AP_INIT_TAKE1 (
"SecDefaultAction",
cmd_default_action,
NULL,
CMD_SCOPE_ANY,
"default action list"
),
AP_INIT_FLAG (
"SecDisableBackendCompression",
cmd_disable_backend_compression,
NULL,
CMD_SCOPE_ANY,
"When set to On, removes the compression headers from the backend requests."
),
AP_INIT_TAKE1 (
"SecGsbLookupDB",
cmd_gsb_lookup_db,
NULL,
RSRC_CONF,
"database google safe browsing"
),
AP_INIT_TAKE1 (
"SecUnicodeCodePage",
cmd_unicode_codepage,
NULL,
CMD_SCOPE_MAIN,
"Unicode CodePage"
),
AP_INIT_TAKE12 (
"SecUnicodeMapFile",
cmd_unicode_map,
NULL,
CMD_SCOPE_MAIN,
"Unicode Map file"
),
AP_INIT_TAKE1 (
"SecGeoLookupDB",
cmd_geo_lookup_db,
NULL,
RSRC_CONF,
"database for geographical lookups module."
),
AP_INIT_TAKE12 (
"SecGuardianLog",
cmd_guardian_log,
NULL,
CMD_SCOPE_MAIN,
"The filename of the filter debugging log file"
),
AP_INIT_TAKE1 (
"SecMarker",
cmd_marker,
NULL,
CMD_SCOPE_ANY,
"marker for a skipAfter target"
),
AP_INIT_TAKE1 (
"SecPcreMatchLimit",
cmd_pcre_match_limit,
NULL,
CMD_SCOPE_MAIN,
"PCRE match limit"
),
AP_INIT_TAKE1 (
"SecPcreMatchLimitRecursion",
cmd_pcre_match_limit_recursion,
NULL,
CMD_SCOPE_MAIN,
"PCRE match limit recursion"
),
AP_INIT_TAKE1 (
"SecRequestBodyAccess",
cmd_request_body_access,
NULL,
CMD_SCOPE_ANY,
"On or Off"
),
AP_INIT_TAKE1 (
"SecInterceptOnError",
cmd_request_intercept_on_error,
NULL,
CMD_SCOPE_ANY,
"On or Off"
),
AP_INIT_TAKE1 (
"SecRulePerfTime",
cmd_rule_perf_time,
NULL,
CMD_SCOPE_ANY,
"Threshold to log slow rules in usecs."
),
AP_INIT_TAKE12 (
"SecConnReadStateLimit",
cmd_conn_read_state_limit,
NULL,
CMD_SCOPE_ANY,
"maximum number of threads in READ_BUSY state per ip address"
),
AP_INIT_TAKE12 (
"SecReadStateLimit",
cmd_read_state_limit,
NULL,
CMD_SCOPE_ANY,
"maximum number of threads in READ_BUSY state per ip address"
),
AP_INIT_TAKE12 (
"SecConnWriteStateLimit",
cmd_conn_write_state_limit,
NULL,
CMD_SCOPE_ANY,
"maximum number of threads in WRITE_BUSY state per ip address"
),
AP_INIT_TAKE12 (
"SecWriteStateLimit",
cmd_write_state_limit,
NULL,
CMD_SCOPE_ANY,
"maximum number of threads in WRITE_BUSY state per ip address"
),
AP_INIT_TAKE1 (
"SecRequestBodyInMemoryLimit",
cmd_request_body_inmemory_limit,
NULL,
CMD_SCOPE_ANY,
"maximum request body size that will be placed in memory (except for POST urlencoded requests)."
),
AP_INIT_TAKE1 (
"SecRequestBodyLimit",
cmd_request_body_limit,
NULL,
CMD_SCOPE_ANY,
"maximum request body size ModSecurity will accept."
),
AP_INIT_TAKE1 (
"SecRequestBodyNoFilesLimit",
cmd_request_body_no_files_limit,
NULL,
CMD_SCOPE_ANY,
"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,
NULL,
CMD_SCOPE_ANY,
"character encoding used in request."
),
AP_INIT_TAKE1 (
"SecResponseBodyAccess",
cmd_response_body_access,
NULL,
CMD_SCOPE_ANY,
"On or Off"
),
AP_INIT_TAKE1 (
"SecResponseBodyLimit",
cmd_response_body_limit,
NULL,
CMD_SCOPE_ANY,
"byte limit for response body"
),
AP_INIT_TAKE1 (
"SecResponseBodyLimitAction",
cmd_response_body_limit_action,
NULL,
CMD_SCOPE_ANY,
"what happens when the response body limit is reached"
),
AP_INIT_TAKE1 (
"SecRequestBodyLimitAction",
cmd_resquest_body_limit_action,
NULL,
CMD_SCOPE_ANY,
"what happens when the request body limit is reached"
),
AP_INIT_ITERATE (
"SecResponseBodyMimeType",
cmd_response_body_mime_type,
NULL,
CMD_SCOPE_ANY,
"adds given MIME types to the list of types that will be buffered on output"
),
AP_INIT_NO_ARGS (
"SecResponseBodyMimeTypesClear",
cmd_response_body_mime_types_clear,
NULL,
CMD_SCOPE_ANY,
"clears the list of MIME types that will be buffered on output"
),
#ifdef HTACCESS_CONFIG
AP_INIT_TAKE23 (
"SecRule",
cmd_rule,
NULL,
CMD_SCOPE_HTACCESS,
"rule target, operator and optional action list"
),
#else
AP_INIT_TAKE23 (
"SecRule",
cmd_rule,
NULL,
CMD_SCOPE_ANY,
"rule target, operator and optional action list"
),
#endif
AP_INIT_TAKE1 (
"SecRuleEngine",
cmd_rule_engine,
NULL,
CMD_SCOPE_ANY,
"On or Off"
),
AP_INIT_TAKE1 (
"SecStatusEngine",
cmd_status_engine,
NULL,
CMD_SCOPE_ANY,
"On or Off"
),
AP_INIT_TAKE1 (
"SecConnEngine",
cmd_sever_conn_filters_engine,
NULL,
CMD_SCOPE_ANY,
"On or Off"
),
AP_INIT_TAKE23 (
"SecRemoteRules",
cmd_remote_rules,
NULL,
CMD_SCOPE_ANY,
"key and URI to the remote rules"
),
AP_INIT_TAKE1 (
"SecRemoteRulesFailAction",
cmd_remote_rules_fail,
NULL,
CMD_SCOPE_ANY,
"Abort or Warn"
),
AP_INIT_TAKE1 (
"SecXmlExternalEntity",
cmd_xml_external_entity,
NULL,
CMD_SCOPE_ANY,
"On or Off"
),
AP_INIT_FLAG (
"SecRuleInheritance",
cmd_rule_inheritance,
NULL,
CMD_SCOPE_ANY,
"On or Off"
),
AP_INIT_TAKE12 (
"SecRuleScript",
cmd_rule_script,
NULL,
CMD_SCOPE_ANY,
"rule script and optional actionlist"
),
#ifdef HTACCESS_CONFIG
AP_INIT_ITERATE (
"SecRuleRemoveById",
cmd_rule_remove_by_id,
NULL,
CMD_SCOPE_HTACCESS,
"rule ID for removal"
),
AP_INIT_ITERATE (
"SecRuleRemoveByTag",
cmd_rule_remove_by_tag,
NULL,
CMD_SCOPE_HTACCESS,
"rule tag for removal"
),
AP_INIT_ITERATE (
"SecRuleRemoveByMsg",
cmd_rule_remove_by_msg,
NULL,
CMD_SCOPE_HTACCESS,
"rule message for removal"
),
#else
AP_INIT_ITERATE (
"SecRuleRemoveById",
cmd_rule_remove_by_id,
NULL,
CMD_SCOPE_ANY,
"rule ID for removal"
),
AP_INIT_ITERATE (
"SecRuleRemoveByTag",
cmd_rule_remove_by_tag,
NULL,
CMD_SCOPE_ANY,
"rule tag for removal"
),
AP_INIT_ITERATE (
"SecRuleRemoveByMsg",
cmd_rule_remove_by_msg,
NULL,
CMD_SCOPE_ANY,
"rule message for removal"
),
#endif
AP_INIT_TAKE2 (
"SecHashMethodPm",
cmd_hash_method_pm,
NULL,
CMD_SCOPE_ANY,
"Hash method and pattern"
),
AP_INIT_TAKE2 (
"SecHashMethodRx",
cmd_hash_method_rx,
NULL,
CMD_SCOPE_ANY,
"Hash method and regex"
),
#ifdef HTACCESS_CONFIG
AP_INIT_TAKE2 (
"SecRuleUpdateActionById",
cmd_rule_update_action_by_id,
NULL,
CMD_SCOPE_HTACCESS,
"updated action list"
),
AP_INIT_TAKE23 (
"SecRuleUpdateTargetById",
cmd_rule_update_target_by_id,
NULL,
CMD_SCOPE_HTACCESS,
"updated target list"
),
AP_INIT_TAKE23 (
"SecRuleUpdateTargetByTag",
cmd_rule_update_target_by_tag,
NULL,
CMD_SCOPE_HTACCESS,
"rule tag pattern and updated target list"
),
AP_INIT_TAKE23 (
"SecRuleUpdateTargetByMsg",
cmd_rule_update_target_by_msg,
NULL,
CMD_SCOPE_HTACCESS,
"rule message pattern and updated target list"
),
#else
AP_INIT_TAKE2 (
"SecRuleUpdateActionById",
cmd_rule_update_action_by_id,
NULL,
CMD_SCOPE_ANY,
"updated action list"
),
AP_INIT_TAKE23 (
"SecRuleUpdateTargetById",
cmd_rule_update_target_by_id,
NULL,
CMD_SCOPE_ANY,
"updated target list"
),
AP_INIT_TAKE23 (
"SecRuleUpdateTargetByTag",
cmd_rule_update_target_by_tag,
NULL,
CMD_SCOPE_ANY,
"rule tag pattern and updated target list"
),
AP_INIT_TAKE23 (
"SecRuleUpdateTargetByMsg",
cmd_rule_update_target_by_msg,
NULL,
CMD_SCOPE_ANY,
"rule message pattern and updated target list"
),
#endif
AP_INIT_TAKE1 (
"SecServerSignature",
cmd_server_signature,
NULL,
CMD_SCOPE_MAIN,
"the new signature of the server"
),
AP_INIT_TAKE1 (
"SecTmpDir",
cmd_tmp_dir,
NULL,
CMD_SCOPE_ANY,
"path to the temporary storage area"
),
AP_INIT_TAKE1 (
"SecUploadDir",
cmd_upload_dir,
NULL,
CMD_SCOPE_ANY,
"path to the file upload area"
),
AP_INIT_TAKE1 (
"SecUploadFileLimit",
cmd_upload_file_limit,
NULL,
CMD_SCOPE_ANY,
"limit the number of uploaded files processed"
),
AP_INIT_TAKE1 (
"SecUploadFileMode",
cmd_upload_filemode,
NULL,
CMD_SCOPE_ANY,
"octal permissions mode for uploaded files"
),
AP_INIT_TAKE1 (
"SecUploadKeepFiles",
cmd_upload_keep_files,
NULL,
CMD_SCOPE_ANY,
"On or Off"
),
AP_INIT_TAKE1 (
"SecTmpSaveUploadedFiles",
cmd_upload_save_tmp_files,
NULL,
CMD_SCOPE_ANY,
"On or Off"
),
AP_INIT_TAKE1 (
"SecWebAppId",
cmd_web_app_id,
NULL,
CMD_SCOPE_ANY,
"id"
),
AP_INIT_TAKE1 (
"SecSensorId",
cmd_sensor_id,
NULL,
CMD_SCOPE_MAIN,
"sensor id"
),
AP_INIT_TAKE1 (
"SecHttpBlKey",
cmd_httpBl_key,
NULL,
CMD_SCOPE_ANY,
"httpBl access key"
),
AP_INIT_TAKE1 (
"SecHashEngine",
cmd_hash_engine,
NULL,
CMD_SCOPE_ANY,
"On or Off"
),
AP_INIT_TAKE2 (
"SecHashKey",
cmd_hash_key,
NULL,
CMD_SCOPE_ANY,
"Set Hash key"
),
AP_INIT_TAKE1 (
"SecHashParam",
cmd_hash_param,
NULL,
CMD_SCOPE_ANY,
"Set Hash parameter"
),
AP_INIT_TAKE1 (
"SecParseXmlIntoArgs",
cmd_parse_xml_into_args,
NULL,
CMD_SCOPE_ANY,
"On, Off or OnlyArgs"
),
{ NULL }
};
modsecurity-apache-2.9.13/apache2/apache2_io.c 0000664 0000000 0000000 00000116374 15174655764 0021075 0 ustar 00root root 0000000 0000000 /*
* 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 "modsecurity.h"
#include "apache2.h"
#include "msc_crypt.h"
#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(security2);
#endif
/* -- Input filter -- */
#if 0
static void dummy_free_func(void *data) {}
#endif
/**
* This request filter will forward the previously stored
* request body further down the chain (most likely to the
* processing module).
*/
apr_status_t input_filter(ap_filter_t *f, apr_bucket_brigade *bb_out,
ap_input_mode_t mode, apr_read_type_e block, apr_off_t nbytes)
{
modsec_rec *msr = (modsec_rec *)f->ctx;
msc_data_chunk *chunk = NULL;
apr_bucket *bucket;
apr_status_t rc;
int no_data = 1;
char *my_error_msg = NULL;
if (msr == NULL) {
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, f->r->server,
"ModSecurity: Internal error in input filter: msr is null.");
ap_remove_input_filter(f);
return APR_EGENERAL;
}
/* Make sure we are using the current request */
msr->r = f->r;
if (msr->phase < PHASE_REQUEST_BODY) {
msr_log(msr, 1, "Internal error: REQUEST_BODY phase incomplete for input filter in phase %d", msr->phase);
return APR_EGENERAL;
}
if ((msr->if_status == IF_STATUS_COMPLETE)||(msr->if_status == IF_STATUS_NONE)) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Input filter: Input forwarding already complete, skipping (f %pp, r %pp).", f, f->r);
}
ap_remove_input_filter(f);
return ap_get_brigade(f->next, bb_out, mode, block, nbytes);
}
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Input filter: Forwarding input: mode=%d, block=%d, nbytes=%" APR_OFF_T_FMT
" (f %pp, r %pp).", mode, block, nbytes, f, f->r);
}
if (msr->if_started_forwarding == 0) {
msr->if_started_forwarding = 1;
rc = modsecurity_request_body_retrieve_start(msr, &my_error_msg);
if (rc == -1) {
if (my_error_msg != NULL) {
msr_log(msr, 1, "%s", my_error_msg);
}
return APR_EGENERAL;
}
}
rc = modsecurity_request_body_retrieve(msr, &chunk, (unsigned int)nbytes, &my_error_msg);
if (rc == -1) {
if (my_error_msg != NULL) {
msr_log(msr, 1, "%s", my_error_msg);
}
return APR_EGENERAL;
}
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
It would seem that we cannot prevent other filters in the chain
from modifying data in-place. Hence we copy.
if (chunk->is_permanent) {
/* Do not make a copy of the data we received in the chunk. */
bucket = apr_bucket_heap_create(chunk->data, chunk->length, dummy_free_func,
f->r->connection->bucket_alloc);
} else {
/* Copy the data we received in the chunk. */
bucket = apr_bucket_heap_create(chunk->data, chunk->length, NULL,
f->r->connection->bucket_alloc);
}
#endif
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) {
msr->if_stream_changed = 0;
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 (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 (rc == 0) {
modsecurity_request_body_retrieve_end(msr);
if (msr->if_seen_eos) {
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.");
}
}
/* We're done */
msr->if_status = IF_STATUS_COMPLETE;
ap_remove_input_filter(f);
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;
}
/**
* 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;
*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 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 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 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 HTTP_INTERNAL_SERVER_ERROR;
}
do {
apr_status_t rc;
rc = ap_get_brigade(r->input_filters, bb_in, AP_MODE_READBYTES, APR_BLOCK_READ, HUGE_STRING_LEN);
if (rc != APR_SUCCESS) {
/* NOTE Apache returns AP_FILTER_ERROR here when the request is
* too large and APR_EGENERAL when the client disconnects.
*/
switch(rc) {
case AP_FILTER_ERROR :
*error_msg = apr_psprintf(msr->mp, "Error reading request body: HTTP Error 413 - Request entity too large. (Most likely.)");
break;
case APR_EGENERAL :
*error_msg = apr_psprintf(msr->mp, "Error reading request body: Client went away.");
break;
default :
*error_msg = apr_psprintf(msr->mp, "Error reading request body: %s", get_apr_error(msr->mp, rc));
break;
}
return ap_map_http_request_error(rc, HTTP_BAD_REQUEST);
}
/* Loop through the buckets in the brigade in order
* to extract the size of the data available.
*/
for(bucket = APR_BRIGADE_FIRST(bb_in);
bucket != APR_BRIGADE_SENTINEL(bb_in);
bucket = APR_BUCKET_NEXT(bucket))
{
const char *buf;
apr_size_t buflen;
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 HTTP_INTERNAL_SERVER_ERROR;
}
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Input filter: Bucket type %s contains %" APR_SIZE_T_FMT " bytes.",
bucket->type->name, buflen);
}
/* Check request body limit (should only trigger on chunked requests). */
if (msr->reqbody_length + buflen > (apr_size_t)msr->txcfg->reqbody_limit) {
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 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 "
"configured limit (%ld).", msr->txcfg->reqbody_limit);
} else if ((msr->txcfg->is_enabled == MODSEC_DETECTION_ONLY) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)){
*error_msg = apr_psprintf(msr->mp, "Request body is larger than the "
"configured limit (%ld).", msr->txcfg->reqbody_limit);
} else if ((msr->txcfg->is_enabled == MODSEC_DETECTION_ONLY) && (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);
} else {
*error_msg = apr_psprintf(msr->mp, "Request body is larger than the "
"configured limit (%ld).", msr->txcfg->reqbody_limit);
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;
if (buflen != 0) {
int rcbs = modsecurity_request_body_store(msr, buf, buflen, error_msg);
if (msr->reqbody_length > (apr_size_t)msr->txcfg->reqbody_limit && msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL) {
finished_reading = 1;
}
if (rcbs < 0) {
if (rcbs == -5) {
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 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);
} else if ((msr->txcfg->is_enabled == MODSEC_DETECTION_ONLY) && (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);
} 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 HTTP_REQUEST_ENTITY_TOO_LARGE;
}
}
if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT))
return HTTP_INTERNAL_SERVER_ERROR;
}
}
if (APR_BUCKET_IS_EOS(bucket)) {
finished_reading = 1;
msr->if_seen_eos = 1;
}
}
apr_brigade_cleanup(bb_in);
} while(!finished_reading);
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 ").",
msr->reqbody_length);
}
msr->if_status = IF_STATUS_WANTS_TO_RUN;
if (rcbe == -5) {
return HTTP_REQUEST_ENTITY_TOO_LARGE;
}
if (rcbe < 0) {
return HTTP_INTERNAL_SERVER_ERROR;
}
return APR_SUCCESS;
}
/* -- Output filter -- */
/**
* Examines the configuration and the response MIME type
* in order to determine whether output buffering should
* 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. */
if (msr->txcfg->resbody_access != 1) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Output filter: Response body buffering is not enabled.");
}
return 0;
}
/* Check MIME type. */
if ((msr->txcfg->of_mime_types == NULL)||(msr->txcfg->of_mime_types == NOT_SET_P)) {
msr_log(msr, 1, "Output filter: MIME type structures corrupted (internal error).");
return -1;
}
if (r->content_type != NULL) {
char *p = NULL;
content_type = apr_pstrdup(msr->mp, r->content_type);
if (content_type == NULL) {
msr_log(msr, 1, "Output filter: Failed to allocate memory for content type.");
return -1;
}
/* Hide the character encoding information
* if present. Sometimes the content type header
* looks like this "text/html; charset=xyz" ...
*/
p = strstr(content_type, ";");
if (p != NULL) {
*p = '\0';
}
strtolower_inplace((unsigned char *)content_type);
if (strcmp(content_type, "text/html") == 0) {
/* Useful information to have should we later
* decide to do something with the HTML output.
*/
msr->resbody_contains_html = 1;
}
} else {
content_type = "null";
}
if (apr_table_get(msr->txcfg->of_mime_types, content_type) != NULL) return 1;
msr_log(msr, 4, "Output filter: Not buffering response body for unconfigured MIME type \"%s\".", content_type);
return 0;
}
/**
* Initialises the output filter.
*/
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.");
return -1;
}
msr->of_status = OF_STATUS_IN_PROGRESS;
rc = output_filter_should_run(msr, r);
if (rc < 0) return -1; /* output_filter_should_run() generates error msg */
if (rc == 0) return 0;
/* Do not check the output limit if we are willing to
* process partial response bodies.
*/
if (msr->txcfg->of_limit_action == RESPONSE_BODY_LIMIT_ACTION_PARTIAL) {
return 1;
}
/* Look up the Content-Length header to see if we know
* the amount of data coming our way. If we do and if
* it's too much we might want to stop processing right here.
*/
s_content_length = apr_table_get(r->headers_out, "Content-Length");
if (s_content_length == NULL) {
/* Try this too, mod_cgi seems to put headers there. */
s_content_length = apr_table_get(r->err_headers_out, "Content-Length");
}
if (s_content_length != NULL) {
long int len;
len = strtol(s_content_length, NULL, 10);
if ((len == LONG_MIN)||(len == LONG_MAX)||(len < 0)||(len >= 1073741824)) {
msr_log(msr, 1, "Output filter: Invalid Content-Length: %s", log_escape_nq(r->pool,
(char *)s_content_length));
return -1; /* Invalid. */
}
if (len == 0) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Output filter: Skipping response since Content-Length is zero.");
}
return 0;
}
if (len > msr->txcfg->of_limit) {
msr_log(msr, 1, "Output filter: Content-Length (%s) over the limit (%ld).",
log_escape_nq(r->pool, (char *)s_content_length), msr->txcfg->of_limit);
msr->outbound_error = 1;
return -2; /* Over the limit. */
}
}
return 1;
}
/**
* Send the accumulated content down the filter stream
* 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);
if (rc != APR_SUCCESS) {
/* TODO: These need to move to flags in 2.6. For now log them
* at level 4 so that they are not confusing users.
*/
int log_level = 4;
if (msr->txcfg->debuglog_level >= log_level) {
switch(rc) {
case AP_NOBODY_WROTE :
msr_log(msr, log_level, "Output filter: Error while forwarding response data (%d): No data", rc);
break;
case AP_FILTER_ERROR :
/* Look like this is caused by the error
* already being handled, so we should ignore it
*
msr_log(msr, log_level, "Output filter: Error while forwarding response data (%d): Filter error", rc);
*/
break;
default :
msr_log(msr, log_level, "Output filter: Error while forwarding response data (%d): %s",
rc, get_apr_error(msr->mp, rc));
break;
}
}
return rc;
}
return APR_SUCCESS;
}
/** \brief Inject data into brigade
*
* \param msr ModSecurity transation resource
* \param ap_filter_t Apache filter
*
*/
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) {
apr_bucket *bucket_ci = NULL;
bucket_ci = apr_bucket_heap_create(msr->stream_output_data,
msr->stream_output_length, NULL, f->r->connection->bucket_alloc);
for (b = APR_BRIGADE_FIRST(msr->of_brigade); b != APR_BRIGADE_SENTINEL(msr->of_brigade); b = APR_BUCKET_NEXT(b)) {
if(!APR_BUCKET_IS_METADATA(b))
apr_bucket_delete(b);
}
APR_BRIGADE_INSERT_HEAD(msr->of_brigade, bucket_ci);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Content Injection: Data reinjected bytes [%"APR_SIZE_T_FMT"]",msr->stream_output_length);
}
}
}
/**
*
*/
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;
bucket_ci = apr_bucket_heap_create(msr->content_prepend,
msr->content_prepend_len, NULL, f->r->connection->bucket_alloc);
APR_BRIGADE_INSERT_HEAD(msr->of_brigade, bucket_ci);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Content Injection (b): Added content to top: %s",
log_escape_nq_ex(msr->mp, msr->content_prepend, msr->content_prepend_len));
}
}
}
/**
*
*/
static int flatten_response_body(modsec_rec *msr) {
apr_status_t rc;
msr->resbody_status = RESBODY_STATUS_READ_BRIGADE;
if (msr->resbody_length + 1 <= 0) {
msr_log(msr, 1, "Output filter: Invalid response length: %" APR_SIZE_T_FMT, msr->resbody_length);
return -1;
}
msr->resbody_data = apr_palloc(msr->mp, msr->resbody_length + 1);
if (msr->resbody_data == NULL) {
msr_log(msr, 1, "Output filter: Response body data memory allocation failed. Asked for: %" APR_SIZE_T_FMT,
msr->resbody_length + 1);
return -1;
}
rc = apr_brigade_flatten(msr->of_brigade, msr->resbody_data, &msr->resbody_length);
if (rc != APR_SUCCESS) {
msr_log(msr, 1, "Output filter: Failed to flatten brigade (%d): %s", rc,
get_apr_error(msr->mp, rc));
return -1;
}
msr->resbody_data[msr->resbody_length] = '\0';
msr->resbody_status = RESBODY_STATUS_READ;
if (msr->txcfg->stream_outbody_inspection && msr->txcfg->hash_is_enabled == HASH_DISABLED) {
msr->stream_output_length = msr->resbody_length;
if (msr->stream_output_data == NULL) {
msr_log(msr, 1, "Output filter: Stream Response body data memory allocation failed. Asked for: %" APR_SIZE_T_FMT,
msr->stream_output_length + 1);
return -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) {
int retval = 0;
apr_time_t time1 = apr_time_now();
retval = init_response_body_html_parser(msr);
if(retval == 1) {
retval = hash_response_body_links(msr);
if(retval > 0) {
retval = inject_hashed_response_body(msr, retval);
if(retval < 0){
msr_log(msr, 1, "inject_hashed_response_body: Unable to inject hash into response body. Returning response without changes." );
}else{
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Hash completed in %" APR_TIME_T_FMT " usec.", (apr_time_now() - time1));
}
}
}
}
if(msr->of_stream_changed == 0) {
msr->stream_output_length = msr->resbody_length;
if (msr->stream_output_data == NULL) {
msr_log(msr, 1, "Output filter: Stream Response body data memory allocation failed. Asked for: %" APR_SIZE_T_FMT,
msr->stream_output_length + 1);
return -1;
}
memcpy(msr->stream_output_data, msr->resbody_data, msr->stream_output_length);
msr->stream_output_data[msr->stream_output_length] = '\0';
}
}
return 1;
}
/**
* Output filter.
*/
apr_status_t output_filter(ap_filter_t *f, apr_bucket_brigade *bb_in) {
request_rec *r = f->r;
modsec_rec *msr = (modsec_rec *)f->ctx;
apr_bucket *bucket = NULL, *eos_bucket = NULL;
apr_status_t rc;
int start_skipping = 0;
/* Do we have the context? */
if (msr == NULL) {
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, f->r->server,
"ModSecurity: Internal Error: msr is null in output filter.");
ap_remove_output_filter(f);
return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR);
}
msr->r = r;
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Output filter: Receiving output (f %pp, r %pp).", f, f->r);
}
/* Put back the Accept-Encoding and TE request headers
* if they were removed from the request.
*/
if (msr->txcfg->disable_backend_compression) {
char *ae = (char *)apr_table_get(msr->request_headers, "Accept-Encoding");
char *te = (char *)apr_table_get(msr->request_headers, "TE");
if ((ae != NULL)&&(apr_table_get(f->r->headers_in, "Accept-Encoding") == NULL)) {
apr_table_add(f->r->headers_in, "Accept-Encoding", ae);
}
if ((te != NULL)&&(apr_table_get(f->r->headers_in, "TE") == NULL)) {
apr_table_add(f->r->headers_in, "TE", te);
}
}
/* Initialise on first invocation */
if (msr->of_status == OF_STATUS_NOT_STARTED) {
/* Update our context from the request structure. */
msr->r = r;
msr->response_status = r->status;
msr->status_line = ((r->status_line != NULL)
? r->status_line : ap_get_status_line(r->status));
msr->response_protocol = get_response_protocol(r);
if(msr->txcfg->crypto_hash_location_rx == 1 || msr->txcfg->crypto_hash_location_pm == 1)
rc = modify_response_header(msr);
msr->response_headers = apr_table_overlay(msr->mp, r->err_headers_out, r->headers_out);
/* Process phase RESPONSE_HEADERS */
rc = modsecurity_process_phase(msr, PHASE_RESPONSE_HEADERS);
if (rc < 0) { /* error */
ap_remove_output_filter(f);
return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR);
}
if (rc > 0) { /* transaction needs to be interrupted */
int status = perform_interception(msr);
if (status != DECLINED) { /* DECLINED means we allow-ed the request. */
ap_remove_output_filter(f);
msr->of_status = OF_STATUS_COMPLETE;
msr->resbody_status = RESBODY_STATUS_ERROR;
return send_error_bucket(msr, f, status);
}
}
msr->outbound_error = 0;
/* Decide whether to observe the response body. */
rc = output_filter_init(msr, f, bb_in);
switch(rc) {
case -2 : /* response too large */
case -1 : /* error */
/* there's something wrong with this response */
ap_remove_output_filter(f);
msr->of_status = OF_STATUS_COMPLETE;
msr->resbody_status = RESBODY_STATUS_ERROR;
return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR);
case 0 :
/* We do not want to observe this response body
* but we need to remain attached to observe
* when it is completed so that we can run
* the RESPONSE_BODY phase.
*/
msr->of_skipping = 1;
msr->resbody_status = RESBODY_STATUS_NOT_READ;
break;
default :
/* Continue (observe the response body). */
break;
}
/* If injecting content unset headers now. */
if (msr->txcfg->content_injection_enabled == 0) {
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Content Injection: Not enabled.");
}
} else {
if ((msr->content_prepend) || (msr->content_append)) {
apr_table_unset(msr->r->headers_out, "Content-Length");
apr_table_unset(msr->r->headers_out, "Last-Modified");
apr_table_unset(msr->r->headers_out, "ETag");
apr_table_unset(msr->r->headers_out, "Expires");
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Content Injection: Removing headers (C-L, L-M, Etag, Expires).");
}
} else {
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Content Injection: Nothing to inject.");
}
}
}
/* Content injection (prepend & non-buffering). */
if ((msr->txcfg->content_injection_enabled) && (msr->content_prepend) && (msr->of_skipping)) {
apr_bucket *bucket_ci = apr_bucket_heap_create(msr->content_prepend,
msr->content_prepend_len, NULL, f->r->connection->bucket_alloc);
APR_BRIGADE_INSERT_HEAD(bb_in, bucket_ci);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Content Injection (nb): Added content to top: %s",
log_escape_nq_ex(msr->mp, msr->content_prepend, msr->content_prepend_len));
}
}
} else
if (msr->of_status == OF_STATUS_COMPLETE) {
msr_log(msr, 1, "Output filter: Internal error: output filtering complete yet filter was invoked.");
ap_remove_output_filter(f);
return APR_EGENERAL;
}
/* Loop through the buckets in the brigade in order
* to extract the size of the data available.
*/
for(bucket = APR_BRIGADE_FIRST(bb_in);
bucket != APR_BRIGADE_SENTINEL(bb_in);
bucket = APR_BUCKET_NEXT(bucket)) {
const char *buf;
apr_size_t buflen;
/* Look into response data if configured to do so,
* unless we've already processed a partial response.
*/
if ((msr->of_skipping == 0)&&(!msr->of_partial)) { /* Observe the response data. */
/* Retrieve data from the bucket. */
rc = apr_bucket_read(bucket, &buf, &buflen, APR_BLOCK_READ);
if (rc != APR_SUCCESS) {
msr->of_status = OF_STATUS_COMPLETE;
msr->resbody_status = RESBODY_STATUS_ERROR;
msr_log(msr, 1, "Output filter: Failed to read bucket (rc %d): %s",
rc, get_apr_error(r->pool, rc));
ap_remove_output_filter(f);
return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR);
}
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Output filter: Bucket type %s contains %" APR_SIZE_T_FMT " bytes.",
bucket->type->name, buflen);
}
/* Check the response size. */
if (msr->resbody_length > (apr_size_t)msr->txcfg->of_limit) {
/* The size of the response is larger than what we're
* ready to accept. We need to decide what we want to do
* about it.
*/
msr->outbound_error = 1;
if (msr->txcfg->of_limit_action == RESPONSE_BODY_LIMIT_ACTION_REJECT) {
/* Reject response. */
msr_log(msr, 1, "Output filter: Response body too large (over limit of %ld, "
"total not specified).", msr->txcfg->of_limit);
msr->of_status = OF_STATUS_COMPLETE;
msr->resbody_status = RESBODY_STATUS_PARTIAL;
ap_remove_output_filter(f);
return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR);
} else {
/* Process partial response. */
start_skipping = 1;
msr->resbody_length = msr->txcfg->of_limit;
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Output filter: Processing partial response body (limit %ld)",
msr->txcfg->of_limit);
}
}
} else {
msr->resbody_length += buflen;
}
}
/* Have we reached the end of the response? */
if (APR_BUCKET_IS_EOS(bucket)) {
eos_bucket = bucket;
/* Inject content (append & non-buffering). */
if ((msr->txcfg->content_injection_enabled) && (msr->content_append)
&& (msr->of_skipping || msr->of_partial || start_skipping))
{
apr_bucket *bucket_ci = NULL;
bucket_ci = apr_bucket_heap_create(msr->content_append,
msr->content_append_len, NULL, f->r->connection->bucket_alloc);
APR_BUCKET_INSERT_BEFORE(bucket, bucket_ci);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Content-Injection (nb): Added content to bottom: %s",
log_escape_nq_ex(msr->mp, msr->content_append, msr->content_append_len));
}
}
msr->of_done_reading = 1;
}
}
/* Add buckets in this brigade to the brigade
* we have in the context, but only if we actually
* want to keep the response body.
*/
if ((msr->of_skipping == 0)&&(msr->of_partial == 0)) {
ap_save_brigade(f, &msr->of_brigade, &bb_in, msr->mp);
/* Do we need to process a partial response? */
if (start_skipping) {
if (msr->txcfg->stream_outbody_inspection) {
if(msr->stream_output_data != NULL) {
free(msr->stream_output_data);
msr->stream_output_data = NULL;
}
msr->stream_output_data = (char *)malloc(msr->resbody_length+1);
}
if (flatten_response_body(msr) < 0) {
if (msr->txcfg->stream_outbody_inspection) {
if(msr->stream_output_data != NULL) {
free(msr->stream_output_data);
msr->stream_output_data = NULL;
}
}
ap_remove_output_filter(f);
return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR);
}
/* Process phase RESPONSE_BODY */
rc = modsecurity_process_phase(msr, PHASE_RESPONSE_BODY);
if (rc < 0) {
ap_remove_output_filter(f);
return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR);
}
if (rc > 0) {
int status = perform_interception(msr);
if (status != DECLINED) { /* DECLINED means we allow-ed the request. */
ap_remove_output_filter(f);
return send_error_bucket(msr, f, status);
}
}
/* Prepend content as necessary. */
prepend_content_to_of_brigade(msr, f);
if ((rc = send_of_brigade(msr, f)) != APR_SUCCESS) {
return rc;
}
msr->of_partial = 1;
}
if (msr->of_done_reading == 0) {
/* We are done for now. We will be called again with more data. */
return APR_SUCCESS;
}
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Output filter: Completed receiving response body (buffered %s - %" APR_SIZE_T_FMT " bytes).",
(msr->of_partial ? "partial" : "full"), msr->resbody_length);
}
} else { /* Not looking at response data. */
if (msr->of_done_reading == 0) {
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Output filter: Sending input brigade directly.");
}
return ap_pass_brigade(f->next, bb_in);
}
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Output filter: Completed receiving response body (non-buffering).");
}
}
/* We've done our thing; remove us from the filter list. */
msr->of_status = OF_STATUS_COMPLETE;
ap_remove_output_filter(f);
/* Process phase RESPONSE_BODY, but
* only if it hasn't been processed already.
*/
if (msr->phase < PHASE_RESPONSE_BODY) {
if (msr->txcfg->stream_outbody_inspection) {
if(msr->stream_output_data != NULL) {
free(msr->stream_output_data);
msr->stream_output_data = NULL;
}
msr->stream_output_data = (char *)malloc(msr->resbody_length+1);
}
if (flatten_response_body(msr) < 0) {
if (msr->txcfg->stream_outbody_inspection) {
if(msr->stream_output_data != NULL) {
free(msr->stream_output_data);
msr->stream_output_data = NULL;
}
}
ap_remove_output_filter(f);
return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR);
}
rc = modsecurity_process_phase(msr, PHASE_RESPONSE_BODY);
if (rc < 0) {
ap_remove_output_filter(f);
return send_error_bucket(msr, f, HTTP_INTERNAL_SERVER_ERROR);
}
if (rc > 0) {
int status = perform_interception(msr);
if (status != DECLINED) { /* DECLINED means we allow-ed the request. */
ap_remove_output_filter(f);
return send_error_bucket(msr, f, status);
}
}
}
/* 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);
msr->of_stream_changed = 0;
}
if (msr->txcfg->stream_outbody_inspection) {
if(msr->stream_output_data != NULL) {
free(msr->stream_output_data);
msr->stream_output_data = NULL;
}
}
prepend_content_to_of_brigade(msr, f);
/* Inject content into response (append & buffering). */
if ((msr->txcfg->content_injection_enabled) && (msr->content_append)) {
apr_bucket *bucket_ci = NULL;
bucket_ci = apr_bucket_heap_create(msr->content_append,
msr->content_append_len, NULL, f->r->connection->bucket_alloc);
APR_BUCKET_INSERT_BEFORE(eos_bucket, bucket_ci);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Content-Injection (b): Added content to bottom: %s",
log_escape_nq_ex(msr->mp, msr->content_append, msr->content_append_len));
}
}
/* Send data down the filter stream. */
if ((rc = send_of_brigade(msr, f)) != APR_SUCCESS) {
return rc;
}
}
/* Another job well done! */
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Output filter: Output forwarding complete.");
}
if ((msr->of_skipping == 0)&&(msr->of_partial == 0)) {
return APR_SUCCESS;
} else {
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Output filter: Sending input brigade directly.");
}
return ap_pass_brigade(f->next, bb_in);
}
}
modsecurity-apache-2.9.13/apache2/apache2_util.c 0000664 0000000 0000000 00000031625 15174655764 0021436 0 ustar 00root root 0000000 0000000 /*
* 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 "modsecurity.h"
#include "apache2.h"
#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;
/* Set the status line explicitly for the error document */
f->r->status_line = ap_get_status_line(status);
brigade = apr_brigade_create(f->r->pool, f->r->connection->bucket_alloc);
if (brigade == NULL) return APR_EGENERAL;
bucket = ap_bucket_error_create(status, NULL, f->r->pool, f->r->connection->bucket_alloc);
if (bucket == NULL) return APR_EGENERAL;
APR_BRIGADE_INSERT_TAIL(brigade, bucket);
bucket = apr_bucket_eos_create(f->r->connection->bucket_alloc);
if (bucket == NULL) return APR_EGENERAL;
APR_BRIGADE_INSERT_TAIL(brigade, bucket);
ap_pass_brigade(f->next, brigade);
/* NOTE:
* It may not matter what we do from the filter as it may be too
* late to even generate an error (already sent to client). Nick Kew
* recommends to return APR_EGENERAL in hopes that the handler in control
* will notice and do The Right Thing. So, that is what we do now.
*/
return APR_EGENERAL;
}
/**
* Execute system command. First line of the output will be returned in
* 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;
const char *const *env = NULL;
apr_file_t *script_out = NULL;
request_rec *r = msr->r;
if (argv == NULL) {
argv = apr_pcalloc(r->pool, 3 * sizeof(char *));
argv[0] = command;
argv[1] = NULL;
}
ap_add_cgi_vars(r);
ap_add_common_vars(r);
/* PHP hack, getting around its silly security checks. */
apr_table_add(r->subprocess_env, "PATH_TRANSLATED", command);
apr_table_add(r->subprocess_env, "REDIRECT_STATUS", "302");
env = (const char * const *)ap_create_environment(r->pool, r->subprocess_env);
if (env == NULL) {
msr_log(msr, 1, "Exec: Unable to create environment.");
return -1;
}
procnew = apr_pcalloc(r->pool, sizeof(*procnew));
if (procnew == NULL) {
msr_log(msr, 1, "Exec: Unable to allocate %lu bytes.", (unsigned long)sizeof(*procnew));
return -1;
}
apr_procattr_create(&procattr, r->pool);
if (procattr == NULL) {
msr_log(msr, 1, "Exec: Unable to create procattr.");
return -1;
}
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) {
msr_log(msr, 9, "Exec: %s", log_escape_nq(r->pool, command));
}
rc = apr_proc_create(procnew, command, argv, env, procattr, r->pool);
if (rc != APR_SUCCESS) {
msr_log(msr, 1, "Exec: Execution failed: %s (%s)", log_escape_nq(r->pool, command),
get_apr_error(r->pool, rc));
return -1;
}
apr_pool_note_subprocess(r->pool, procnew, APR_KILL_AFTER_TIMEOUT);
script_out = procnew->out;
if (!script_out) {
msr_log(msr, 1, "Exec: Failed to get script output pipe.");
return -1;
}
apr_file_pipe_timeout_set(script_out, r->server->timeout);
/* Now read from the pipe. */
{
char buf[260] = "";
char *p = buf;
apr_size_t nbytes = 255;
apr_status_t rc2;
rc2 = apr_file_read(script_out, buf, &nbytes);
if (rc2 == APR_SUCCESS) {
buf[nbytes] = 0;
/* if there is more than one line ignore them */
while(*p != 0) {
if (*p == 0x0a) *p = 0;
p++;
}
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Exec: First line from script output: \"%s\"",
log_escape(r->pool, buf));
}
if (output != NULL) *output = apr_pstrdup(r->pool, buf);
/* Soak up the remaining data. */
nbytes = 255;
while(apr_file_read(script_out, buf, &nbytes) == APR_SUCCESS) nbytes = 255;
} else {
msr_log(msr, 1, "Exec: Execution failed while reading output: %s (%s)",
log_escape_nq(r->pool, command),
get_apr_error(r->pool, rc2));
return -1;
}
}
apr_proc_wait(procnew, NULL, NULL, APR_WAIT);
return 1;
}
/**
* Returns a new string that contains the error
* message for the given return code.
*/
char *get_apr_error(apr_pool_t *p, apr_status_t rc) {
char *text = apr_pcalloc(p, 201);
if (text == NULL) return NULL;
apr_strerror(rc, text, 200);
return text;
}
/**
* Retrieve named environment variable.
*/
char *get_env_var(request_rec *r, char *name) {
char *result = (char *)apr_table_get(r->notes, name);
if (result == NULL) {
result = (char *)apr_table_get(r->subprocess_env, name);
}
if (result == NULL) {
result = getenv(name);
}
return result;
}
/**
* Extended internal log helper function. Use msr_log instead. If fixup is
* true, the message will be stripped of any trailing newline and any
* required bytes will be escaped.
*/
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 str1[1024] = "";
char str2[1256] = "";
/* Find the logging FD and determine the logging level from configuration. */
if (dcfg != NULL) {
if ((dcfg->debuglog_fd != NULL)&&(dcfg->debuglog_fd != NOT_SET_P)) {
debuglog_fd = dcfg->debuglog_fd;
}
if (dcfg->debuglog_level != NOT_SET) {
filter_debug_level = dcfg->debuglog_level;
}
}
/* Return immediately if we don't have where to write
* or if the log level of the message is higher than
* wanted in the log.
*/
if ((level > 3)&&( (debuglog_fd == NULL) || (level > filter_debug_level) )) return;
/* Construct the message. */
apr_vsnprintf(str1, sizeof(str1), text, ap);
if (fixup) {
int len = strlen(str1);
/* Strip line ending. */
if (len && str1[len - 1] == '\n') {
str1[len - 1] = '\0';
}
if (len > 1 && str1[len - 2] == '\r') {
str1[len - 2] = '\0';
}
}
/* Construct the log entry. */
apr_snprintf(str2, sizeof(str2),
"[%s] [%s/sid#%pp][rid#%pp][%s][%d] %s\n",
current_logtime(msr->mp), ap_get_server_name(r), (r->server),
r, ((r->uri == NULL) ? "" : log_escape_nq(msr->mp, r->uri)),
level, (fixup ? log_escape_nq(msr->mp, str1) : str1));
/* Write to the debug log. */
if ((debuglog_fd != NULL)&&(level <= filter_debug_level)) {
nbytes = strlen(str2);
apr_file_write_full(debuglog_fd, str2, nbytes, &nbytes_written);
}
/* Send message levels 1-3 to the Apache error log and
* add it to the message list in the audit log. */
if (level <= 3) {
char *unique_id = (char *)get_env_var(r, "UNIQUE_ID");
char *hostname = (char *)msr->hostname;
if (unique_id != NULL) {
unique_id = apr_psprintf(msr->mp, " [unique_id \"%s\"]",
log_escape(msr->mp, unique_id));
}
else unique_id = "";
if (hostname != NULL) {
hostname = apr_psprintf(msr->mp, " [hostname \"%s\"]",
log_escape(msr->mp, hostname));
}
else hostname = "";
/* 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
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);
#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);
}
}
return;
}
/**
* Logs one message at the given level to the debug log (and to the
* 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);
internal_log_ex(msr->r, msr->txcfg, msr, level, 0, text, ap);
va_end(ap);
}
/**
* Logs one message at level 3 to the debug log and to the
* 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);
internal_log_ex(msr->r, msr->txcfg, msr, 3, 1, text, ap);
va_end(ap);
}
/**
* Logs one message at level 4 to the debug log and to the
* Apache error log. This is intended for warning callbacks.
*
* 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);
internal_log_ex(msr->r, msr->txcfg, msr, 4, 1, text, ap);
va_end(ap);
}
/**
* Converts an Apache error log message into one line of text.
*/
char *format_error_log_message(apr_pool_t *mp, error_message_t *em) {
char *s_file = "", *s_line = "", *s_level = "";
char *s_status = "", *s_message = "";
char *msg = NULL;
if (em == NULL) return NULL;
#ifndef LOG_NO_FILENAME
if (em->file != NULL) {
s_file = apr_psprintf(mp, "[file \"%s\"] ",
log_escape(mp, (char *)em->file));
if (s_file == NULL) return NULL;
}
if (em->line > 0) {
s_line = apr_psprintf(mp, "[line %d] ", em->line);
if (s_line == NULL) return NULL;
}
#endif
s_level = apr_psprintf(mp, "[level %d] ", em->level);
if (s_level == NULL) return NULL;
if (em->status != 0) {
s_status = apr_psprintf(mp, "[status %d] ", em->status);
if (s_status == NULL) return NULL;
}
if (em->message != NULL) {
s_message = log_escape_nq(mp, em->message);
if (s_message == NULL) return NULL;
}
msg = apr_psprintf(mp, "%s%s%s%s%s", s_file, s_line, s_level, s_status, s_message);
if (msg == NULL) return NULL;
return msg;
}
/**
* Determines the reponse protocol Apache will use (or has used)
* to respond to the given request.
*/
const char *get_response_protocol(request_rec *r) {
int proto_num = r->proto_num;
if (r->assbackwards) {
return NULL;
}
if (proto_num > HTTP_VERSION(1,0)
&& apr_table_get(r->subprocess_env, "downgrade-1.0"))
{
proto_num = HTTP_VERSION(1,0);
}
if (proto_num == HTTP_VERSION(1,0)
&& apr_table_get(r->subprocess_env, "force-response-1.0"))
{
return "HTTP/1.0";
}
return AP_SERVER_PROTOCOL;
}
modsecurity-apache-2.9.13/apache2/mod_security2.c 0000664 0000000 0000000 00000166173 15174655764 0021675 0 ustar 00root root 0000000 0000000 /*
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
* 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
*
* 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 "msc_remote_rules.h"
#if defined(WITH_LUA)
#include "msc_lua.h"
#endif
#include "msc_status_engine.h"
#ifdef WITH_YAJL
#include
#endif /* WITH_YAJL */
#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(security2);
#endif
/* ModSecurity structure */
msc_engine DSOLOCAL *modsecurity = NULL;
/* Global module variables; these are used for the Apache-specific functionality */
char DSOLOCAL *chroot_dir = NULL;
char DSOLOCAL *new_server_signature = NULL;
char DSOLOCAL *real_server_signature = NULL;
char DSOLOCAL *guardianlog_name = NULL;
apr_file_t DSOLOCAL *guardianlog_fd = NULL;
char DSOLOCAL *guardianlog_condition = NULL;
unsigned long int DSOLOCAL msc_pcre_match_limit = 0;
unsigned long int DSOLOCAL msc_pcre_match_limit_recursion = 0;
#ifdef WITH_REMOTE_RULES
msc_remote_rules_server DSOLOCAL *remote_rules_server = NULL;
#endif
int DSOLOCAL remote_rules_fail_action = REMOTE_RULES_ABORT_ON_FAIL;
char DSOLOCAL *remote_rules_fail_message = NULL;
int DSOLOCAL status_engine_state = STATUS_ENGINE_DISABLED;
int DSOLOCAL conn_limits_filter_state = MODSEC_DISABLED;
unsigned long int DSOLOCAL conn_read_state_limit = 0;
TreeRoot DSOLOCAL *conn_read_state_whitelist = 0;
TreeRoot DSOLOCAL *conn_read_state_suspicious_list = 0;
unsigned long int DSOLOCAL conn_write_state_limit = 0;
TreeRoot DSOLOCAL *conn_write_state_whitelist = 0;
TreeRoot DSOLOCAL *conn_write_state_suspicious_list = 0;
#if defined(WIN32)
int (*modsecDropAction)(request_rec *r) = NULL;
#endif
static int server_limit, thread_limit;
/* -- Miscellaneous functions -- */
/**
* \brief Print informations from used libraries
*
* \param mp Pointer to memory pool
*/
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, 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, 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\"; "
#endif
"loaded version=\"%s\"", pcre_vrs, pcre_loaded_vrs);
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, s,
"ModSecurity: LUA compiled version=\"%s\"", LUA_VERSION);
#endif /* WITH_LUA */
#ifdef WITH_YAJL
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, s,
"ModSecurity: LIBXML compiled version=\"%s\"", LIBXML_DOTTED_VERSION);
}
/**
* Intercepts transaction, using the method specified
* in the structure itself. MUST return an HTTP status code,
* which will be used to terminate the transaction.
*/
int perform_interception(modsec_rec *msr) {
msre_actionset *actionset = NULL;
const char *message = NULL;
const char *phase_text = "";
unsigned int pause = 0;
int status = DECLINED;
int log_level = 1;
/* Sanity checks first. */
if (msr->was_intercepted == 0) {
msr_log(msr, 1, "Internal Error: Asked to intercept request but was_intercepted is zero");
return DECLINED;
}
if (msr->phase > 4) {
msr_log(msr, 1, "Internal Error: Asked to intercept request in phase %d.", msr->phase);
msr->was_intercepted = 0;
return DECLINED;
}
/* OK, we're good to go. */
actionset = msr->intercept_actionset;
phase_text = apr_psprintf(msr->mp, " (phase %d)", msr->phase);
/* By default we log at level 1 but we switch to 4
* if a nolog action was used or this is not the initial request
* to hide the message.
*/
log_level = (actionset->log != 1) ? 4 : 1;
/* Pause the request first (if configured and the initial request). */
if (actionset->intercept_pause != NULL) {
if(strstr(actionset->intercept_pause,"%{") != NULL) {
msc_string *var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
var->value = (char *)actionset->intercept_pause;
var->value_len = strlen(actionset->intercept_pause);
expand_macros(msr, var, NULL, msr->mp);
pause = atoi(var->value);
if ((pause == LONG_MAX)||(pause == LONG_MIN)||(pause <= 0))
pause = 0;
msr_log(msr, (log_level > 3 ? log_level : log_level + 1), "Pausing transaction for "
"%d msec.", pause);
/* apr_sleep accepts microseconds */
apr_sleep((apr_interval_time_t)(pause * 1000));
} else {
pause = atoi(actionset->intercept_pause);
if ((pause == LONG_MAX)||(pause == LONG_MIN)||(pause <= 0))
pause = 0;
msr_log(msr, (log_level > 3 ? log_level : log_level + 1), "Pausing transaction for "
"%d msec.", pause);
/* apr_sleep accepts microseconds */
apr_sleep((apr_interval_time_t)(pause * 1000));
}
}
/* Determine how to respond and prepare the log message. */
switch(actionset->intercept_action) {
case ACTION_DENY :
if (actionset->intercept_status != 0) {
status = actionset->intercept_status;
message = apr_psprintf(msr->mp, "Access denied with code %d%s.",
status, phase_text);
} else {
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Internal Error: Invalid status code requested %d).",
phase_text, actionset->intercept_status);
}
break;
case ACTION_PROXY :
#if !(defined(VERSION_IIS)) && !(defined(VERSION_STANDALONE))
if (msr->phase < 3) {
if (ap_find_linked_module("mod_proxy.c") == NULL) {
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Configuration Error: Proxy action to %s requested but mod_proxy not found).",
phase_text,
log_escape_nq(msr->mp, actionset->intercept_uri));
} else {
msr->r->filename = apr_psprintf(msr->mp, "proxy:%s", actionset->intercept_uri);
msr->r->proxyreq = PROXYREQ_REVERSE;
msr->r->handler = "proxy-server";
status = OK;
message = apr_psprintf(msr->mp, "Access denied using proxy to%s %s.",
phase_text,
log_escape_nq(msr->mp, actionset->intercept_uri));
}
} else {
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Configuration Error: Proxy action requested but it does not work in output phases).",
phase_text);
}
#else
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Configuration Error: Proxy action to %s requested but "
"proxy is only available in Apache version).",
phase_text,
log_escape_nq(msr->mp, actionset->intercept_uri));
#endif
break;
case ACTION_DROP :
/* ENH This does not seem to work on Windows. Is there a
* better way to drop a connection anyway?
*/
#if !defined(WIN32)
{
extern module core_module;
apr_socket_t *csd;
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 && AP_SERVER_PATCHLEVEL_NUMBER > 17
/* For mod_http2 used by HTTP/2 there is a virtual connection so must go through
* master to get the main connection or the drop request doesn't seem to do anything.
* For HTTP/1.1 master will not be defined so just go through normal connection.
* More details here: https://github.com/icing/mod_h2/issues/127
*/
if (msr->r->connection->master) {
csd = ap_get_module_config(msr->r->connection->master->conn_config, &core_module);
} else {
csd = ap_get_module_config(msr->r->connection->conn_config, &core_module);
}
#else
csd = ap_get_module_config(msr->r->connection->conn_config, &core_module);
#endif
if (csd) {
if (apr_socket_close(csd) == APR_SUCCESS) {
status = HTTP_FORBIDDEN;
message = apr_psprintf(msr->mp, "Access denied with connection close%s.",
phase_text);
} else {
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Error: Connection drop requested but failed to close the "
" socket).",
phase_text);
}
} else {
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Error: Connection drop requested but socket not found.",
phase_text);
}
}
#else
{
if (modsecDropAction == NULL) {
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Error: Connection drop not implemented on this platform.",
phase_text);
} else if (modsecDropAction(msr->r) == 0) {
status = HTTP_FORBIDDEN;
message = apr_psprintf(msr->mp, "Access denied with connection close%s.",
phase_text);
} else {
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Error: Connection drop request failed.",
phase_text);
}
}
#endif
break;
case ACTION_REDIRECT :
if(strstr(actionset->intercept_uri,"%{") != NULL) {
msc_string *var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
var->value = (char *)actionset->intercept_uri;
var->value_len = strlen(actionset->intercept_uri);
expand_macros(msr, var, NULL, msr->mp);
apr_table_setn(msr->r->headers_out, "Location", var->value);
if ((actionset->intercept_status == 301)||(actionset->intercept_status == 302)
||(actionset->intercept_status == 303)||(actionset->intercept_status == 307))
{
status = actionset->intercept_status;
} else {
status = HTTP_MOVED_TEMPORARILY;
}
message = apr_psprintf(msr->mp, "Access denied with redirection to %s using "
"status %d%s.",
log_escape_nq(msr->mp, var->value), status,
phase_text);
} else {
apr_table_setn(msr->r->headers_out, "Location", actionset->intercept_uri);
if ((actionset->intercept_status == 301)||(actionset->intercept_status == 302)
||(actionset->intercept_status == 303)||(actionset->intercept_status == 307))
{
status = actionset->intercept_status;
} else {
status = HTTP_MOVED_TEMPORARILY;
}
message = apr_psprintf(msr->mp, "Access denied with redirection to %s using "
"status %d%s.",
log_escape_nq(msr->mp, actionset->intercept_uri), status,
phase_text);
}
break;
case ACTION_ALLOW :
status = DECLINED;
message = apr_psprintf(msr->mp, "Access allowed%s.", phase_text);
msr->was_intercepted = 0;
msr->allow_scope = ACTION_ALLOW;
break;
case ACTION_PAUSE :
status = DECLINED;
message = apr_psprintf(msr->mp, "Paused Access%s.", phase_text);
msr->was_intercepted = 0;
msr->allow_scope = ACTION_ALLOW;
break;
case ACTION_ALLOW_PHASE :
status = DECLINED;
message = apr_psprintf(msr->mp, "Access to phase allowed%s.", phase_text);
msr->was_intercepted = 0;
msr->allow_scope = ACTION_ALLOW_PHASE;
break;
case ACTION_ALLOW_REQUEST :
status = DECLINED;
message = apr_psprintf(msr->mp, "Access to request allowed%s.", phase_text);
msr->was_intercepted = 0;
msr->allow_scope = ACTION_ALLOW_REQUEST;
break;
default :
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Internal Error: invalid interception action %d).",
phase_text, actionset->intercept_action);
break;
}
/* If the level is not high enough to add an alert message, but "auditlog"
* is enabled, then still add the message. */
if ((log_level > 3) && (actionset->auditlog != 0)) {
*(const char **)apr_array_push(msr->alerts) = msc_alert_message(msr, actionset, NULL, message);
}
/* Log the message now. */
msc_alert(msr, log_level, actionset, message, msr->intercept_message);
/* However, this will mark the txn relevant again if it is <= 3,
* which will mess up noauditlog. We need to compensate for this
* so that we do not increment twice when auditlog is enabled and
* prevent incrementing when auditlog is disabled.
*/
if ((actionset->auditlog == 0) && (log_level <= 3)) {
msr->is_relevant--;
}
return status;
}
/**
* Retrieves a previously stored transaction context by
* looking at the main request, and the previous requests.
*/
static modsec_rec *retrieve_tx_context(request_rec *r) {
modsec_rec *msr = NULL;
request_rec *rx = NULL;
/* Look in the current request first. */
msr = (modsec_rec *)apr_table_get(r->notes, NOTE_MSR);
if (msr != NULL) {
msr->r = r;
return msr;
}
/* If this is a subrequest then look in the main request. */
if (r->main != NULL) {
msr = (modsec_rec *)apr_table_get(r->main->notes, NOTE_MSR);
if (msr != NULL) {
msr->r = r;
return msr;
}
}
/* If the request was redirected then look in the previous requests. */
rx = r->prev;
while(rx != NULL) {
msr = (modsec_rec *)apr_table_get(rx->notes, NOTE_MSR);
if (msr != NULL) {
msr->r = r;
return msr;
}
rx = rx->prev;
}
return NULL;
}
/**
* Stores transaction context where it can be found in subsequent
* 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);
}
/**
* Creates a new transaction context.
*/
static modsec_rec *create_tx_context(request_rec *r) {
apr_allocator_t *allocator = NULL;
modsec_rec *msr = NULL;
msr = (modsec_rec *)apr_pcalloc(r->pool, sizeof(modsec_rec));
if (msr == NULL) return NULL;
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) {
apr_allocator_destroy(allocator);
return NULL;
}
apr_allocator_owner_set(allocator, msr->mp);
msr->modsecurity = modsecurity;
msr->r = r;
msr->r_early = r;
msr->request_time = r->request_time;
msr->dcfg1 = (directory_config *)ap_get_module_config(r->per_dir_config,
&security2_module);
#if defined(WITH_LUA)
#ifdef CACHE_LUA
#if LUA_VERSION_NUM > 501
msr->L = luaL_newstate();
#else
msr->L = lua_open();
#endif
luaL_openlibs(msr->L);
#endif
#endif
/**
* Create a special user configuration. This is where
* explicit instructions will be stored. This will be used
* to override the default settings (and to override the
* configuration in the second phase, dcfg2, with the user
* setting executed in the first phase.
*/
msr->usercfg = create_directory_config(msr->mp, NULL);
if (msr->usercfg == NULL) return NULL;
/* Create a transaction context and populate
* it using the directory config we just
* got from Apache.
*/
msr->txcfg = create_directory_config(msr->mp, NULL);
if (msr->txcfg == NULL) return NULL;
if (msr->dcfg1 != NULL) {
msr->txcfg = merge_directory_configs(msr->mp, msr->txcfg, msr->dcfg1);
if (msr->txcfg == NULL) return NULL;
}
init_directory_config(msr->txcfg);
msr->txid = get_env_var(r, "UNIQUE_ID");
if (msr->txid == NULL) {
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r->server,
"ModSecurity: ModSecurity requires mod_unique_id to be installed.");
return NULL;
}
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Initialising transaction (txid %s).", msr->txid);
}
/* Populate tx fields */
msr->error_messages = apr_array_make(msr->mp, 5, sizeof(error_message_t *));
msr->alerts = apr_array_make(msr->mp, 5, sizeof(char *));
msr->server_software = real_server_signature;
msr->local_addr = r->connection->local_ip;
msr->local_port = r->connection->local_addr->port;
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER < 3
msr->remote_addr = r->connection->remote_ip;
msr->remote_port = r->connection->remote_addr->port;
#else
msr->remote_addr = r->connection->client_ip;
msr->remote_port = r->connection->client_addr->port;
msr->useragent_ip = r->useragent_ip;
#endif
msr->request_line = r->the_request;
msr->request_uri = r->uri;
msr->request_method = r->method;
msr->query_string = r->args;
msr->request_protocol = r->protocol;
msr->request_headers = apr_table_copy(msr->mp, r->headers_in);
msr->hostname = ap_get_server_name(r);
msr->msc_full_request_buffer = NULL;
msr->msc_full_request_length = 0;
msr->msc_rule_mptmp = NULL;
/* Invoke the engine to continue with initialisation */
if (modsecurity_tx_init(msr) < 0) {
msr_log(msr, 1, "Failed to initialise transaction (txid %s).", msr->txid);
return NULL;
}
store_tx_context(msr, r);
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Transaction context created (dcfg %pp).", msr->dcfg1);
}
return msr;
}
/* -- Hooks -- */
/*
* Change the signature of the server if change was requested in
* configuration. We do this by locating the signature in server
* memory and writing over it.
*/
static apr_status_t change_server_signature(server_rec *s) {
char *server_version = NULL;
/* This is a very particular way to handle the server banner. It is Apache
* only. Standalone and descendants should address that in its specifics
* implementations, e.g. IIS module.
*/
#if !(defined(VERSION_IIS)) && !(defined(VERSION_STANDALONE))
if (new_server_signature == NULL) return 0;
server_version = (char *)apache_get_server_version();
if (server_version == NULL) {
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s,
"SecServerSignature: Apache returned null as signature.");
return -1;
}
if (strlen(server_version) >= strlen(new_server_signature)) {
strcpy(server_version, new_server_signature);
}
else {
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s,
"SecServerSignature: original signature too short. Please set "
"ServerTokens to Full.");
return -1;
}
/* Check that it really changed. This assumes that what we got was
* not a copy and this could change in future versions of Apache.
*/
server_version = (char *)apache_get_server_version();
if ((server_version == NULL) || (strcmp(server_version, new_server_signature) != 0)) {
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s, "SecServerSignature: Failed to change server signature to \"%s\".", new_server_signature);
return 0;
}
else {
ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, 0, s, "SecServerSignature: Changed server signature to \"%s\".", server_version);
}
#endif
return 1;
}
/**
* Executed at the end of server lifetime to cleanup the allocated resources.
*/
static apr_status_t module_cleanup(void *data) {
modsecurity_shutdown(modsecurity);
return APR_SUCCESS;
}
/**
* Generate a single variable for use with mod_log_config.
*/
static const char *modsec_var_log_handler(request_rec *r, char *name) {
modsec_rec *msr = NULL;
if (name == NULL) return NULL;
msr = retrieve_tx_context(r);
if (msr == NULL) return NULL;
return construct_single_var(msr, name);
}
/**
* Pre-configuration initialisation hook.
*/
static int hook_pre_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_temp) {
static APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
/* Initialise ModSecurity engine */
modsecurity = modsecurity_create(mp, MODSEC_ONLINE);
if (modsecurity == NULL) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"ModSecurity: Failed to initialise engine.");
return HTTP_INTERNAL_SERVER_ERROR;
}
log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
if (log_pfn_register) {
log_pfn_register(mp, "M", modsec_var_log_handler, 0);
}
return OK;
}
/**
* Main (post-configuration) module initialisation.
*/
static int hook_post_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_temp, server_rec *s) {
void *init_flag = NULL;
int first_time = 0;
/* ENH Figure out a way to validate config before we start
* - skipafter: need to make sure we found all of our targets
*/
/* Figure out if we are here for the first time */
apr_pool_userdata_get(&init_flag, "modsecurity-init-flag", s->process->pool);
if (init_flag == NULL) {
first_time = 1;
apr_pool_userdata_set((const void *)1, "modsecurity-init-flag",
apr_pool_cleanup_null, s->process->pool);
} else {
modsecurity_init(modsecurity, mp);
}
/* Store the original server signature */
real_server_signature = apr_pstrdup(mp, apache_get_server_version());
/* Make some space in the server signature for later */
if (new_server_signature != NULL) {
ap_add_version_component(mp, new_server_signature);
change_server_signature(s);
}
/* For connection level hook */
ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
#if (!(defined(WIN32) || defined(NETWARE)))
/* Internal chroot functionality */
if (chroot_dir != NULL) {
/* ENH Is it safe to simply return with an error, instead
* of using exit()?
*/
if (first_time == 0) {
ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s,
"ModSecurity: chroot checkpoint #2 (pid=%ld ppid=%ld)", (long)getpid(), (long)getppid());
if (chdir(chroot_dir) < 0) {
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s,
"ModSecurity: chroot failed, unable to chdir to %s, errno=%d (%s)",
chroot_dir, errno, strerror(errno));
exit(1);
}
if (chroot(chroot_dir) < 0) {
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s,
"ModSecurity: chroot failed, path=%s, errno=%d(%s)",
chroot_dir, errno, strerror(errno));
exit(1);
}
if (chdir("/") < 0) {
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, s,
"ModSecurity: chdoot failed, unable to chdir to /, errno=%d (%s)",
errno, strerror(errno));
exit(1);
}
ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s,
"ModSecurity: chroot successful, path=%s", chroot_dir);
} else {
ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s,
"ModSecurity: chroot checkpoint #1 (pid=%ld ppid=%ld)", (long)getpid(), (long)getppid());
}
}
#endif
/* Schedule main cleanup for later, when the main pool is destroyed. */
apr_pool_cleanup_register(mp, (void *)s, module_cleanup, apr_pool_cleanup_null);
/* Log our presence to the error log. */
if (first_time) {
ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s,
"%s configured.", MODSEC_MODULE_NAME_FULL);
version(mp, s);
/* If we've changed the server signature make note of the original. */
if (new_server_signature != NULL) {
ap_log_error(APLOG_MARK, APLOG_NOTICE | APLOG_NOERRNO, 0, s,
"ModSecurity: Original server signature: %s",
real_server_signature);
}
#ifndef VERSION_IIS
if (status_engine_state != STATUS_ENGINE_DISABLED) {
msc_status_engine_call();
}
else {
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
"ModSecurity: Status engine is currently disabled, enable " \
"it by set SecStatusEngine to On.");
}
#endif
}
/**
* Checking if it is not the first time that we are in this very function.
* We want to show the messages below during the start and the reload.
*/
#ifndef VERSION_IIS
if (first_time != 1)
{
#ifdef WITH_REMOTE_RULES
if (remote_rules_server != NULL)
{
if (remote_rules_server->amount_of_rules == 1)
{
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
"ModSecurity: Loaded %d rule from: '%s'.",
remote_rules_server->amount_of_rules,
remote_rules_server->uri);
}
else
{
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
"ModSecurity: Loaded %d rules from: '%s'.",
remote_rules_server->amount_of_rules,
remote_rules_server->uri);
}
}
#endif
if (remote_rules_fail_message != NULL)
{
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "ModSecurity: " \
"Problems loading external resources: %s",
remote_rules_fail_message);
}
}
#endif
srand((unsigned int)(time(NULL) * getpid()));
return OK;
}
/**
* Initialisation performed for every new child process.
*/
static void hook_child_init(apr_pool_t *mp, server_rec *s) {
modsecurity_child_init(modsecurity);
}
/**
* Initial request processing, executed immediatelly after
* Apache receives the request headers. This function wil create
* a transaction context.
*/
static int hook_request_early(request_rec *r) {
modsec_rec *msr = NULL;
int rc = DECLINED;
/* This function needs to run only once per transaction
* (i.e. subrequests and redirects are excluded).
*/
if ((r->main != NULL)||(r->prev != NULL)) {
return DECLINED;
}
/* Initialise transaction context and
* create the initial configuration.
*/
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
/* Are we allowed to continue? */
if (msr->txcfg->is_enabled == MODSEC_DISABLED) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Processing disabled, skipping (hook request_early).");
}
return DECLINED;
}
/* Process phase REQUEST_HEADERS */
if (modsecurity_process_phase(msr, PHASE_REQUEST_HEADERS) > 0) {
rc = perform_interception(msr);
}
if ( (msr->txcfg->is_enabled != MODSEC_DISABLED)
&& (msr->txcfg->reqbody_access == 1)
&& (rc == DECLINED))
{
/* Check request body limit (non-chunked requests only). */
if (msr->request_content_length > msr->txcfg->reqbody_limit) {
msr_log(msr, 1, "Request body (Content-Length) is larger than the "
"configured limit (%ld).", msr->txcfg->reqbody_limit);
if(msr->txcfg->is_enabled != MODSEC_DETECTION_ONLY && msr->txcfg->if_limit_action != REQUEST_BODY_LIMIT_ACTION_PARTIAL)
return HTTP_REQUEST_ENTITY_TOO_LARGE;
}
}
#endif
return rc;
}
/**
* Invoked as the first hook in the handler chain, this function
* executes the second phase of ModSecurity request processing.
*/
static int hook_request_late(request_rec *r) {
char *my_error_msg = NULL;
modsec_rec *msr = NULL;
int rc;
/* This function needs to run only once per transaction
* (i.e. subrequests and redirects are excluded).
*/
if ((r->main != NULL)||(r->prev != NULL)) {
return DECLINED;
}
/* Find the transaction context and make sure
* we are supposed to proceed.
*/
msr = retrieve_tx_context(r);
if (msr == NULL) {
/* If we can't find the context that probably means it's
* a subrequest that was not initiated from the outside.
*/
return DECLINED;
}
/* Has this phase been completed already? */
if (msr->phase_request_body_complete) {
msr_log(msr, 1, "Internal Error: Attempted to process the request body more than once.");
return DECLINED;
}
msr->phase_request_body_complete = 1;
msr->remote_user = r->user;
/* Get the second configuration context. */
msr->dcfg2 = (directory_config *)ap_get_module_config(r->per_dir_config,
&security2_module);
/* Create a transaction context. */
msr->txcfg = create_directory_config(msr->mp, NULL);
if (msr->txcfg == NULL) return DECLINED;
if (msr->dcfg2 != NULL) {
msr->txcfg = merge_directory_configs(msr->mp, msr->txcfg, msr->dcfg2);
if (msr->txcfg == NULL) return DECLINED;
}
/* Update with the explicit user settings. */
msr->txcfg = merge_directory_configs(msr->mp, msr->txcfg, msr->usercfg);
init_directory_config(msr->txcfg);
if (msr->txcfg->is_enabled == 0) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Processing disabled, skipping (hook request_late).");
}
return DECLINED;
}
#ifndef REQUEST_EARLY
/* Phase 1 */
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "First phase starting (dcfg %pp).", msr->dcfg2);
}
/* Process phase REQUEST_HEADERS */
if (modsecurity_process_phase(msr, PHASE_REQUEST_HEADERS) > 0) {
/* There was a match; see if we need to intercept. */
rc = perform_interception(msr);
if (rc != DECLINED) {
/* Intercepted */
return rc;
}
}
#endif
/* The rule engine could have been disabled in phase 1. */
if (msr->txcfg->is_enabled == MODSEC_DISABLED) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Skipping phase 2 as the rule engine was disabled by a rule in phase 1.");
}
return DECLINED;
}
/* Phase 2 */
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Second phase starting (dcfg %pp).", msr->dcfg2);
}
/* Check that the request body is not too long, but only
* if configuration allows for request body access.
*/
msr->inbound_error = 0;
if (msr->txcfg->reqbody_access == 1) {
/* Check request body limit (non-chunked requests only). */
if (msr->request_content_length > msr->txcfg->reqbody_limit) {
if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) {
msr->inbound_error = 1;
msr_log(msr, 1, "Request body (Content-Length) is larger than the "
"configured limit (%ld). Deny with status (%d)", msr->txcfg->reqbody_limit, HTTP_REQUEST_ENTITY_TOO_LARGE);
return HTTP_REQUEST_ENTITY_TOO_LARGE;
} else if ((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)){
msr->inbound_error = 1;
msr_log(msr, 1, "Request body (Content-Length) is larger than the "
"configured limit (%ld).", msr->txcfg->reqbody_limit);
} else if ((msr->txcfg->is_enabled == MODSEC_DETECTION_ONLY) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)){
msr_log(msr, 1, "Request body (Content-Length) is larger than the "
"configured limit (%ld).", msr->txcfg->reqbody_limit);
msr->inbound_error = 1;
} else {
msr_log(msr, 1, "Request body (Content-Length) is larger than the "
"configured limit (%ld).", msr->txcfg->reqbody_limit);
msr->inbound_error = 1;
}
}
}
/* Figure out whether to extract multipart files. */
if ((msr->txcfg->upload_keep_files != KEEP_FILES_OFF) /* user might want to keep them */
|| (msr->txcfg->upload_validates_files)) /* user might want to validate them */
{
msr->upload_extract_files = 1;
msr->upload_remove_files = 1;
}
rc = read_request_body(msr, &my_error_msg);
if (rc != OK) {
if (my_error_msg != NULL) {
msr_log(msr, 1, "%s", 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
* the body was read (trailers).
*
* TODO We might still want to hold onto the original headers
* so that we can log them. Keeping them is probably not
* going to increase our memory requirements (because all
* headers are allocated from the request memory pool
* anyway).
*/
msr->request_headers = apr_table_copy(msr->mp, r->headers_in);
/* Process phase REQUEST_BODY */
rc = DECLINED;
if (modsecurity_process_phase(msr, PHASE_REQUEST_BODY) > 0) {
rc = perform_interception(msr);
}
if(msr->txcfg->stream_inbody_inspection && msr->msc_reqbody_read) {
const char *clen = NULL;
clen = apr_psprintf(msr->mp,"%"APR_SIZE_T_FMT,msr->stream_input_length);
if(clen)
apr_table_setn(r->headers_in, "Content-Length",clen);
}
/* Remove the compression ability indications the client set,
* but only if we need to disable backend compression.
*/
if (msr->txcfg->disable_backend_compression) {
apr_table_unset(r->headers_in, "Accept-Encoding");
apr_table_unset(r->headers_in, "TE");
}
return rc;
}
/**
* Invoked every time Apache has something to write to the error log.
*/
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
static void hook_error_log(const ap_errorlog_info *info, const char *errstr)
#else
static void hook_error_log(const char *file, int line, int level, apr_status_t status,
const server_rec *s, const request_rec *r, apr_pool_t *mp, const char *fmt)
#endif
{
modsec_rec *msr = NULL;
error_message_t *em = NULL;
int msr_ap_server;
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
if (info == NULL) return;
if (info->r == NULL) return;
#else
if (r == NULL) return;
#endif
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
msr = retrieve_tx_context((request_rec *)info->r);
#else
msr = retrieve_tx_context((request_rec *)r);
#endif
/* Create a context for requests we never had the chance to process */
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
msr_ap_server = ((msr == NULL)
&& ((info->level & APLOG_LEVELMASK) < APLOG_DEBUG)
&& apr_table_get(info->r->subprocess_env, "UNIQUE_ID"));
#else
msr_ap_server = ((msr == NULL)
&& ((level & APLOG_LEVELMASK) < APLOG_DEBUG)
&& apr_table_get(r->subprocess_env, "UNIQUE_ID"));
#endif
if (msr_ap_server) {
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
msr = create_tx_context((request_rec*)info->r);
#else
msr = create_tx_context((request_rec*)r);
#endif
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Context created after request failure.");
}
}
if (msr == NULL) return;
/* Store the error message for later */
em = (error_message_t *)apr_pcalloc(msr->mp, sizeof(error_message_t));
if (em == NULL) return;
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
if (info->file != NULL) em->file = apr_pstrdup(msr->mp, info->file);
em->line = info->line;
em->level = info->level;
em->status = info->status;
em->message = apr_pstrdup(msr->mp, errstr);
#else
if (file != NULL) em->file = apr_pstrdup(msr->mp, file);
em->line = line;
em->level = level;
em->status = status;
if (fmt != NULL) em->message = apr_pstrdup(msr->mp, fmt);
#endif
/* Remove \n from the end of the message */
if (em->message != NULL) {
char *p = (char *)em->message;
while(*p != '\0') {
if ((*(p + 1) == '\0')&&(*p == '\n')) {
*p = '\0';
break;
}
p++;
}
}
*(const error_message_t **)apr_array_push(msr->error_messages) = em;
}
/**
* Guardian logger is used to interface to the external
* script for web server protection - httpd_guardian.
*/
static void sec_guardian_logger(request_rec *r, request_rec *origr, modsec_rec *msr) {
char *str1, *str2, *text;
char *modsec_message = "-";
int modsec_rating = 0; /* not used yet */
apr_size_t nbytes, nbytes_written;
apr_time_t duration = (apr_time_now() - origr->request_time);
int limit, was_limited;
/* bail out if we do not have where to write */
if ((guardianlog_name == NULL)||(guardianlog_fd == NULL)) return;
/* process the condition, if we have one */
if (guardianlog_condition != NULL) {
if (*guardianlog_condition == '!') {
if (apr_table_get(r->subprocess_env, guardianlog_condition + 1) != NULL) {
return;
}
}
else {
if (apr_table_get(r->subprocess_env, guardianlog_condition) == NULL) {
return;
}
}
}
/*
* Log format is as follows:
*
* %V %h %l %u %t "%r" %>s %b "%{Referer}i" "%{User-agent}i" %{UNIQUE_ID}e
* "SESSION_ID" %T %D "MODSEC_MESSAGE" MODSEC_RATING
*
* The fields SESSION_ID, MODSEC_MESSAGE, and MODSEC_RATING are not used at the moment.
*/
str2 = apr_psprintf(msr->mp, "%" APR_TIME_T_FMT " %" APR_TIME_T_FMT " \"%s\" %d",
duration, apr_time_sec(duration), log_escape(msr->mp, modsec_message), modsec_rating);
if (str2 == NULL) return;
/* We do not want the index line to be longer than 3980 bytes. */
limit = 3980;
was_limited = 0;
/* If we are logging to a pipe we need to observe and
* obey the pipe atomic write limit - PIPE_BUF. For
* more details see the discussion in sec_guardian_logger,
* above.
*/
if (msr->txcfg->auditlog_name[0] == '|') {
if (PIPE_BUF < limit) {
limit = PIPE_BUF;
}
}
limit = limit - strlen(str2) - 5;
if (limit <= 0) {
msr_log(msr, 1, "Audit Log: Atomic PIPE write buffer too small: %d", PIPE_BUF);
return;
}
str1 = construct_log_vcombinedus_limited(msr, limit, &was_limited);
if (str1 == NULL) return;
if (was_limited == 0) {
text = apr_psprintf(msr->mp, "%s %s \n", str1, str2);
} else {
text = apr_psprintf(msr->mp, "%s %s L\n", str1, str2);
}
if (text == NULL) return;
nbytes = strlen(text);
apr_file_write_full(guardianlog_fd, text, nbytes, &nbytes_written);
}
/**
* Invoked at the end of each transaction.
*/
static int hook_log_transaction(request_rec *r) {
const apr_array_header_t *arr = NULL;
request_rec *origr = NULL;
modsec_rec *msr = NULL;
msr = retrieve_tx_context(r);
if (msr == NULL) {
return DECLINED;
}
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Initialising logging.");
}
/* Find the first (origr) and the last (r) request */
origr = r;
while(origr->prev) {
origr = origr->prev;
}
while(r->next) {
r = r->next;
}
/* At this point r is the last request in the
* chain. However, we now need to detect a case when
* a bad ErrorDocument was used and back out of it. That's
* how Apache does it internally. Except where Apache knows
* exactly what is happening we will have to rely on the missing
* headers in the final request to detect this condition.
*/
arr = apr_table_elts(r->headers_out);
while ((arr->nelts == 0)&&(r->prev != NULL)) {
r = r->prev;
arr = apr_table_elts(r->headers_out);
}
msr->r = r;
msr->response_status = r->status;
msr->status_line = ((r->status_line != NULL)
? r->status_line : ap_get_status_line(r->status));
msr->response_protocol = get_response_protocol(origr);
msr->response_headers = apr_table_copy(msr->mp, r->headers_out);
if (!r->assbackwards) msr->response_headers_sent = 1;
msr->bytes_sent = r->bytes_sent;
msr->local_user = r->user;
msr->remote_user = r->connection->remote_logname;
/* -- Guardian -- */
sec_guardian_logger(r, origr, msr);
/* Invoke the engine to do the rest of the work now. */
modsecurity_process_phase(msr, PHASE_LOGGING);
return DECLINED;
}
/**
* Invoked right before request processing begins. This is
* when we need to decide if we want to hook into the output
* filter chain.
*/
static void hook_insert_filter(request_rec *r) {
modsec_rec *msr = NULL;
/* Find the transaction context first. */
msr = retrieve_tx_context(r);
if (msr == NULL) return;
/* Add the input filter, but only if we need it to run. */
if (msr->if_status == IF_STATUS_WANTS_TO_RUN) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Hook insert_filter: Adding input forwarding filter %s(r %pp).",
(((r->main != NULL)||(r->prev != NULL)) ? "for subrequest " : ""), r);
}
ap_add_input_filter("MODSECURITY_IN", msr, r, r->connection);
}
/* The output filters only need to be added only once per transaction
* (i.e. subrequests and redirects are excluded).
*/
if ((r->main != NULL)||(r->prev != NULL)) {
return;
}
/* Only proceed to add the second filter if the engine is enabled. */
if (msr->txcfg->is_enabled == 0) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Hook insert_filter: Processing disabled, skipping.");
}
return;
}
/* We always add the output filter because that's where we need to
* initiate our 3rd and 4th processing phases from. The filter is
* smart enough not to buffer the data if it is not supposed to.
*/
if (msr->of_status != OF_STATUS_COMPLETE) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Hook insert_filter: Adding output filter (r %pp).", r);
}
ap_add_output_filter("MODSECURITY_OUT", msr, r, r->connection);
}
}
// TODO: Holding off on this for now (needs more testing)
/**
* Invoked whenever Apache starts processing an error. A chance
* to insert ourselves into the output filter chain.
*/
static void hook_insert_error_filter(request_rec *r) {
modsec_rec *msr = NULL;
/* Find the transaction context and make sure we are
* supposed to proceed.
*/
msr = retrieve_tx_context(r);
if (msr == NULL) return;
/* Do not run if not enabled. */
if (msr->txcfg->is_enabled == 0) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Hook insert_error_filter: Processing disabled, skipping.");
}
return;
}
/* Do not run if the output filter already completed. This will
* happen if we intercept in phase 4.
*/
if (msr->of_status != OF_STATUS_COMPLETE) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Hook insert_error_filter: Adding output filter (r %pp).", r);
}
/* Make a note that the output we will be receiving is a
* result of error processing.
*/
msr->of_is_error = 1;
ap_add_output_filter("MODSECURITY_OUT", msr, r, r->connection);
} else {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Hook insert_error_filter: Output buffering already complete.");
}
}
}
#if (!defined(NO_MODSEC_API))
/**
* This function is exported for other Apache modules to
* register new transformation functions.
*/
static void modsec_register_tfn(const char *name, void *fn) {
if (modsecurity != NULL) {
msre_engine_tfn_register(modsecurity->msre, name, (fn_tfn_execute_t)fn);
}
}
/**
* This function is exported for other Apache modules to
* register new operators.
*/
static void modsec_register_operator(const char *name, void *fn_init, void *fn_exec) {
if (modsecurity != NULL) {
msre_engine_op_register(modsecurity->msre, name, (fn_op_param_init_t)fn_init, (fn_op_execute_t)fn_exec);
}
}
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
#else
typedef struct {
int child_num;
int thread_num;
} sb_handle;
#endif
/**
* \brief Connetion hook to limit the number of
* connections in BUSY state
*
* \param conn Pointer to connection struct
*
* \retval DECLINED On failure
* \retval OK On Success
*/
static int hook_connection_early(conn_rec *conn)
{
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
ap_sb_handle_t *sbh = conn->sbh;
char *client_ip = conn->client_ip;
#else
sb_handle *sbh = conn->sbh;
char *client_ip = conn->remote_ip;
#endif
int i, j;
unsigned long int ip_count_r = 0, ip_count_w = 0;
char *error_msg;
worker_score *ws_record = NULL;
if (sbh != NULL && (conn_read_state_limit > 0 || conn_write_state_limit > 0)) {
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
ws_record = ap_get_scoreboard_worker(sbh);
#else
ws_record = ap_get_scoreboard_worker(sbh->child_num, sbh->thread_num);
#endif
if (ws_record == NULL)
return DECLINED;
/* If ws_record does not have correct ip yet, we count it already */
if (strcmp(client_ip, ws_record->client) != 0) {
switch (ws_record->status) {
case SERVER_BUSY_READ:
ip_count_r++;
break;
case SERVER_BUSY_WRITE:
ip_count_w++;
break;
default:
break;
}
}
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, conn,
"ModSecurity: going to loop through %d servers with %d threads",
server_limit, thread_limit);
for (i = 0; i < server_limit; ++i) {
for (j = 0; j < thread_limit; ++j) {
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
ws_record = ap_get_scoreboard_worker_from_indexes(i, j);
#else
ws_record = ap_get_scoreboard_worker(i, j);
#endif
if (ws_record == NULL)
return DECLINED;
switch (ws_record->status) {
case SERVER_BUSY_READ:
if (strcmp(client_ip, ws_record->client) == 0)
ip_count_r++;
break;
case SERVER_BUSY_WRITE:
if (strcmp(client_ip, ws_record->client) == 0)
ip_count_w++;
break;
default:
break;
}
}
}
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, conn,
"ModSecurity: threads in READ: %ld of %ld, WRITE: %ld of %ld, IP: %s",
ip_count_r, conn_read_state_limit, ip_count_w, conn_write_state_limit, client_ip);
if (conn_read_state_limit > 0 && ip_count_r > conn_read_state_limit)
{
if (conn_read_state_suspicious_list &&
(tree_contains_ip(conn->pool,
conn_read_state_suspicious_list, client_ip, NULL, &error_msg) <= 0))
{
if (conn_limits_filter_state == MODSEC_DETECTION_ONLY)
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, conn,
"ModSecurity: Too many threads [%ld] of %ld allowed " \
"in READ state from %s - There is a suspission list " \
"but that IP is not part of it, access granted",
ip_count_r, conn_read_state_limit, client_ip);
}
else if (tree_contains_ip(conn->pool,
conn_read_state_whitelist, client_ip, NULL, &error_msg) > 0)
{
if (conn_limits_filter_state == MODSEC_DETECTION_ONLY)
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, conn,
"ModSecurity: Too many threads [%ld] of %ld allowed " \
"in READ state from %s - Ip is on whitelist, access " \
"granted", ip_count_r, conn_read_state_limit,
client_ip);
}
else
{
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, conn,
"ModSecurity: Access denied with code 400. Too many " \
"threads [%ld] of %ld allowed in READ state from %s - " \
"Possible DoS Consumption Attack [Rejected]", ip_count_r,
conn_read_state_limit, client_ip);
if (conn_limits_filter_state == MODSEC_ENABLED)
return OK;
}
}
if (conn_write_state_limit > 0 && ip_count_w > conn_write_state_limit)
{
if (conn_write_state_suspicious_list &&
(tree_contains_ip(conn->pool,
conn_write_state_suspicious_list, client_ip, NULL, &error_msg) <= 0))
{
if (conn_limits_filter_state == MODSEC_DETECTION_ONLY)
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, conn,
"ModSecurity: Too many threads [%ld] of %ld allowed " \
"in WRITE state from %s - There is a suspission list " \
"but that IP is not part of it, access granted",
ip_count_w, conn_read_state_limit, client_ip);
}
else if (tree_contains_ip(conn->pool,
conn_write_state_whitelist, client_ip, NULL, &error_msg) > 0)
{
if (conn_limits_filter_state == MODSEC_DETECTION_ONLY)
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, conn,
"ModSecurity: Too many threads [%ld] of %ld allowed " \
"in WRITE state from %s - Ip is on whitelist, " \
"access granted", ip_count_w, conn_read_state_limit,
client_ip);
}
else
{
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, conn,
"ModSecurity: Access denied with code 400. Too many " \
"threads [%ld] of %ld allowed in WRITE state from %s - " \
"Possible DoS Consumption Attack [Rejected]", ip_count_w,
conn_write_state_limit, client_ip);
if (conn_limits_filter_state == MODSEC_ENABLED)
return OK;
}
}
}
return DECLINED;
}
/**
* This function is exported for other Apache modules to
* register new variables.
*/
static void modsec_register_variable(const char *name, unsigned int type,
unsigned int argc_min, unsigned int argc_max,
void *fn_validate, void *fn_generate,
unsigned int is_cacheable, unsigned int availability) {
if (modsecurity != NULL) {
msre_engine_variable_register(modsecurity->msre, name, type, argc_min, argc_max, (fn_var_validate_t)fn_validate, (fn_var_generate_t)fn_generate, is_cacheable, availability);
}
else {
fprintf(stderr,"modsecurity is NULL\n");
}
}
/**
* This function is exported for other Apache modules to
* register new request body processors.
*/
static void modsec_register_reqbody_processor(const char *name,
void *fn_init,
void *fn_process,
void *fn_complete)
{
if (modsecurity != NULL) {
msre_engine_reqbody_processor_register(modsecurity->msre, name,
(fn_reqbody_processor_init_t)fn_init,
(fn_reqbody_processor_init_t)fn_process,
(fn_reqbody_processor_init_t)fn_complete);
}
}
#endif
/**
* Registers module hooks with Apache.
*/
static void register_hooks(apr_pool_t *mp) {
static const char *const postconfig_beforeme_list[] = {
"mod_unique_id.c",
"mod_ssl.c",
NULL
};
static const char *const postconfig_afterme_list[] = {
"mod_fcgid.c",
"mod_cgid.c",
NULL
};
static const char *const postread_beforeme_list[] = {
"mod_rpaf.c",
"mod_rpaf-2.0.c",
"mod_extract_forwarded.c",
"mod_extract_forwarded2.c",
"mod_remoteip.c",
"mod_custom_header.c",
"mod_breach_realip.c",
"mod_breach_trans.c",
"mod_unique_id.c",
NULL
};
static const char *const postread_afterme_list[] = {
"mod_log_forensic.c",
NULL
};
static const char *const transaction_afterme_list[] = {
"mod_log_config.c",
NULL
};
static const char *const fixups_beforeme_list[] = {
"mod_env.c",
NULL
};
/* Add the MODSEC_2.x compatibility defines */
*(char **)apr_array_push(ap_server_config_defines) = apr_pstrdup(mp, "MODSEC_2.5");
/* Add the MODSEC_a.b define */
*(char **)apr_array_push(ap_server_config_defines) = apr_psprintf(mp, "MODSEC_%s.%s", MODSEC_VERSION_MAJOR, MODSEC_VERSION_MINOR);
#if (!defined(NO_MODSEC_API))
/* Export optional functions. */
APR_REGISTER_OPTIONAL_FN(modsec_register_tfn);
APR_REGISTER_OPTIONAL_FN(modsec_register_operator);
APR_REGISTER_OPTIONAL_FN(modsec_register_variable);
APR_REGISTER_OPTIONAL_FN(modsec_register_reqbody_processor);
#endif
/* Main hooks */
ap_hook_pre_config(hook_pre_config, NULL, NULL, APR_HOOK_FIRST);
ap_hook_post_config(hook_post_config, postconfig_beforeme_list,
postconfig_afterme_list, APR_HOOK_REALLY_LAST);
ap_hook_child_init(hook_child_init, NULL, NULL, APR_HOOK_MIDDLE);
/* Our own hook to handle RPC transactions (not used at the moment).
* // ap_hook_handler(hook_handler, NULL, NULL, APR_HOOK_MIDDLE);
*/
/* Connection processing hooks */
ap_hook_process_connection(hook_connection_early, NULL, NULL, APR_HOOK_FIRST);
/* Transaction processing hooks */
ap_hook_post_read_request(hook_request_early,
postread_beforeme_list, postread_afterme_list, APR_HOOK_REALLY_FIRST);
ap_hook_fixups(hook_request_late, fixups_beforeme_list, NULL, APR_HOOK_REALLY_FIRST);
/* Logging */
ap_hook_error_log(hook_error_log, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_log_transaction(hook_log_transaction, NULL, transaction_afterme_list, APR_HOOK_MIDDLE);
/* Filter hooks */
ap_hook_insert_filter(hook_insert_filter, NULL, NULL, APR_HOOK_FIRST);
ap_hook_insert_error_filter(hook_insert_error_filter, NULL, NULL, APR_HOOK_FIRST);
ap_register_input_filter("MODSECURITY_IN", input_filter,
NULL, AP_FTYPE_CONTENT_SET);
/* Ensure that the output filter runs before other modules so that
* we get a request that has a better chance of not being modified:
*
* Currently:
* mod_expires = -2
* mod_cache = -1
* mod_deflate = -1
* mod_headers = 0
*/
ap_register_output_filter("MODSECURITY_OUT", output_filter,
NULL, AP_FTYPE_CONTENT_SET - 3);
}
/* Defined in apache2_config.c */
extern const command_rec module_directives[];
/* Module entry points */
module AP_MODULE_DECLARE_DATA security2_module = {
STANDARD20_MODULE_STUFF,
create_directory_config,
merge_directory_configs,
NULL, /* create_server_config */
NULL, /* merge_server_configs */
module_directives,
register_hooks
};
modsecurity-apache-2.9.13/apache2/mod_security2_config.hw 0000664 0000000 0000000 00000000070 15174655764 0023375 0 ustar 00root root 0000000 0000000 /* This file is left empty for building on Windows. */
modsecurity-apache-2.9.13/apache2/modsecurity.c 0000664 0000000 0000000 00000066230 15174655764 0021445 0 ustar 00root root 0000000 0000000 /*
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
* 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
*
* 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 "apr_global_mutex.h"
#include "modsecurity.h"
#include "msc_parsers.h"
#include "msc_util.h"
#include "msc_json.h"
#include "msc_xml.h"
#include "apr_version.h"
#ifdef WITH_CURL
#include
#endif
#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(security2);
#endif
unsigned long int DSOLOCAL unicode_codepage = 0;
int DSOLOCAL *unicode_map_table = NULL;
/**
* Format an alert message.
*/
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.";
if (action_message == NULL) {
message = apr_psprintf(msr->mp, "%s%s",
rule_message, msre_format_metadata(msr, actionset));
}
else {
message = apr_psprintf(msr->mp, "%s %s%s", action_message,
rule_message, msre_format_metadata(msr, actionset));
}
return message;
}
/**
* Log an alert message to the log, adding the rule metadata at the end.
*/
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);
}
#if 0
/**
* Return phase name associated with the given phase number.
*/
static const char *phase_name(int phase) {
switch(phase) {
case 1 :
return "REQUEST_HEADERS";
break;
case 2 :
return "REQUEST_BODY";
break;
case 3 :
return "RESPONSE_HEADERS";
break;
case 4 :
return "RESPONSE_BODY";
break;
case 5 :
return "LOGGING";
break;
}
return "INVALID";
}
#endif
/**
* Creates and initialises a ModSecurity engine instance.
*/
msc_engine *modsecurity_create(apr_pool_t *mp, int processing_mode) {
msc_engine *msce = NULL;
msce = apr_pcalloc(mp, sizeof(msc_engine));
if (msce == NULL) return NULL;
msce->mp = mp;
msce->processing_mode = processing_mode;
msce->msre = msre_engine_create(msce->mp);
if (msce->msre == NULL) return NULL;
msre_engine_register_default_variables(msce->msre);
msre_engine_register_default_operators(msce->msre);
msre_engine_register_default_tfns(msce->msre);
msre_engine_register_default_actions(msce->msre);
// TODO: msre_engine_register_default_reqbody_processors(msce->msre);
return msce;
}
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_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(*lock);
#else
rc = unixd_set_global_mutex_perms(*lock);
#endif
if (rc != APR_SUCCESS) {
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;
}
/**
* 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;
}
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;
}
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;
}
/**
* 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;
}
/**
* Performs per-child (new process) initialisation.
*/
void modsecurity_child_init(msc_engine *msce) {
/* Need to call this once per process before any other XML calls. */
xmlInitParser();
if (msce->auditlog_lock != NULL) {
apr_status_t rc = apr_global_mutex_child_init(&msce->auditlog_lock, NULL, msce->mp);
if (rc != APR_SUCCESS) {
// ap_log_error(APLOG_MARK, APLOG_ERR, rs, s, "Failed to child-init auditlog mutex");
}
}
if (msce->geo_lock != NULL) {
apr_status_t rc = apr_global_mutex_child_init(&msce->geo_lock, NULL, msce->mp);
if (rc != APR_SUCCESS) {
// ap_log_error(APLOG_MARK, APLOG_ERR, rs, s, "Failed to child-init geo mutex");
}
}
#ifdef GLOBAL_COLLECTION_LOCK
if (msce->dbm_lock != NULL) {
apr_status_t rc = apr_global_mutex_child_init(&msce->dbm_lock, NULL, msce->mp);
if (rc != APR_SUCCESS) {
// ap_log_error(APLOG_MARK, APLOG_ERR, rs, s, "Failed to child-init dbm mutex");
}
}
#endif
}
/**
* Releases resources held by engine instance.
*/
void modsecurity_shutdown(msc_engine *msce) {
if (msce == NULL) return;
}
/**
*
*/
static void modsecurity_persist_data(modsec_rec *msr) {
const apr_array_header_t *arr;
apr_table_entry_t *te;
apr_time_t time_before, time_after;
int i;
time_before = apr_time_now();
/* Collections, store & remove stale. */
arr = apr_table_elts(msr->collections);
te = (apr_table_entry_t *)arr->elts;
for (i = 0; i < arr->nelts; i++) {
apr_table_t *col = (apr_table_t *)te[i].val;
/* Only store those collections that changed. */
if (apr_table_get(msr->collections_dirty, te[i].key)) {
collection_store(msr, col);
}
}
time_after = apr_time_now();
msr->time_storage_write += time_after - time_before;
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Recording persistent data took %" APR_TIME_T_FMT
" microseconds.", msr->time_gc);
}
/* Remove stale collections. */
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 3
if (ap_random_pick(0, RAND_MAX) < RAND_MAX/100) {
#else
if (rand() < RAND_MAX/100) {
#endif
arr = apr_table_elts(msr->collections);
te = (apr_table_entry_t *)arr->elts;
for (i = 0; i < arr->nelts; i++) {
collections_remove_stale(msr, te[i].key);
}
msr->time_gc = apr_time_now() - time_after;
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Garbage collection took %" APR_TIME_T_FMT
" microseconds.", msr->time_gc);
}
}
}
/**
*
*/
static apr_status_t modsecurity_tx_cleanup(void *data) {
modsec_rec *msr = (modsec_rec *)data;
char *my_error_msg = NULL;
if (msr == NULL) return APR_SUCCESS;
/* Multipart processor cleanup. */
if (msr->mpd != NULL) multipart_cleanup(msr);
/* XML processor cleanup. */
if (msr->xml != NULL) xml_cleanup(msr);
#ifdef WITH_YAJL
/* JSON processor cleanup. */
if (msr->json != NULL) json_cleanup(msr);
#endif
// TODO: Why do we ignore return code here?
modsecurity_request_body_clear(msr, &my_error_msg);
if (my_error_msg != NULL) {
msr_log(msr, 1, "%s", my_error_msg);
}
if (msr->msc_full_request_length > 0 &&
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)
#ifdef CACHE_LUA
if(msr->L != NULL) lua_close(msr->L);
#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;
}
/**
*
*/
apr_status_t modsecurity_tx_init(modsec_rec *msr) {
const char *s = NULL;
const apr_array_header_t *arr;
char *semicolon = NULL;
char *comma = NULL;
apr_table_entry_t *te;
int i;
/* Register TX cleanup */
apr_pool_cleanup_register(msr->mp, msr, modsecurity_tx_cleanup, apr_pool_cleanup_null);
/* Initialise C-L */
msr->request_content_length = -1;
s = apr_table_get(msr->request_headers, "Content-Length");
if (s != NULL) {
msr->request_content_length = strtol(s, NULL, 10);
}
/* Figure out whether this request has a body */
msr->reqbody_chunked = 0;
msr->reqbody_should_exist = 0;
if (msr->request_content_length == -1) {
/* There's no C-L, but is chunked encoding used? */
char *transfer_encoding = (char *)apr_table_get(msr->request_headers, "Transfer-Encoding");
if ((transfer_encoding != NULL)&&(m_strcasestr(transfer_encoding, "chunked") != NULL)) {
msr->reqbody_should_exist = 1;
msr->reqbody_chunked = 1;
}
} else {
/* C-L found */
msr->reqbody_should_exist = 1;
}
/* Initialise C-T */
msr->request_content_type = NULL;
s = apr_table_get(msr->request_headers, "Content-Type");
if (s != NULL) msr->request_content_type = s;
/* Decide what to do with the request body. */
if ((msr->request_content_type != NULL)
&& (strncasecmp(msr->request_content_type, "application/x-www-form-urlencoded", 33) == 0))
{
/* Always place POST requests with
* "application/x-www-form-urlencoded" payloads in memory.
*/
msr->msc_reqbody_storage = MSC_REQBODY_MEMORY;
msr->msc_reqbody_spilltodisk = 0;
msr->msc_reqbody_processor = "URLENCODED";
} else {
/* If the C-L is known and there's more data than
* our limit go to disk straight away.
*/
if ((msr->request_content_length != -1)
&& (msr->request_content_length > msr->txcfg->reqbody_inmemory_limit))
{
msr->msc_reqbody_storage = MSC_REQBODY_DISK;
}
/* In all other cases, try using the memory first
* but switch over to disk for larger bodies.
*/
msr->msc_reqbody_storage = MSC_REQBODY_MEMORY;
msr->msc_reqbody_spilltodisk = 1;
if (msr->request_content_type != NULL) {
if (strncasecmp(msr->request_content_type, "multipart/form-data", 19) == 0) {
msr->msc_reqbody_processor = "MULTIPART";
}
}
}
/* Check if we are forcing buffering, then use memory only. */
if (msr->txcfg->reqbody_buffering != REQUEST_BODY_FORCEBUF_OFF) {
msr->msc_reqbody_storage = MSC_REQBODY_MEMORY;
msr->msc_reqbody_spilltodisk = 0;
}
/* Initialise arguments */
msr->arguments = apr_table_make(msr->mp, 32);
if (msr->arguments == NULL) return -1;
if (msr->query_string != NULL) {
int invalid_count = 0;
if (parse_arguments(msr, msr->query_string, strlen(msr->query_string),
msr->txcfg->argument_separator, "QUERY_STRING", msr->arguments,
&invalid_count) < 0)
{
msr_log(msr, 1, "Initialisation: Error occurred while parsing QUERY_STRING arguments.");
return -1;
}
if (invalid_count) {
msr->urlencoded_error = 1;
}
}
msr->arguments_to_sanitize = apr_table_make(msr->mp, 16);
if (msr->arguments_to_sanitize == NULL) return -1;
msr->request_headers_to_sanitize = apr_table_make(msr->mp, 16);
if (msr->request_headers_to_sanitize == NULL) return -1;
msr->response_headers_to_sanitize = apr_table_make(msr->mp, 16);
if (msr->response_headers_to_sanitize == NULL) return -1;
msr->pattern_to_sanitize = apr_table_make(msr->mp, 32);
if (msr->pattern_to_sanitize == NULL) return -1;
/* remove targets */
msr->removed_targets = apr_table_make(msr->mp, 16);
if (msr->removed_targets == NULL) return -1;
/* Initialise cookies */
msr->request_cookies = apr_table_make(msr->mp, 16);
if (msr->request_cookies == NULL) return -1;
/* Initialize matched vars */
msr->matched_vars = apr_table_make(msr->mp, 8);
if (msr->matched_vars == NULL) return -1;
apr_table_clear(msr->matched_vars);
msr->perf_rules = apr_table_make(msr->mp, 8);
if (msr->perf_rules == NULL) return -1;
apr_table_clear(msr->perf_rules);
/* Locate the cookie headers and parse them */
arr = apr_table_elts(msr->request_headers);
te = (apr_table_entry_t *)arr->elts;
for (i = 0; i < arr->nelts; i++) {
if (strcasecmp(te[i].key, "Cookie") == 0) {
if (msr->txcfg->cookie_format == COOKIES_V0) {
semicolon = apr_pstrdup(msr->mp, te[i].val);
while((*semicolon != 0)&&(*semicolon != ';')) semicolon++;
if(*semicolon == ';') {
parse_cookies_v0(msr, te[i].val, msr->request_cookies, ";");
} else {
comma = apr_pstrdup(msr->mp, te[i].val);
while((*comma != 0)&&(*comma != ',')) comma++;
if(*comma == ',') {
comma++;
if(*comma == 0x20) {// looks like comma is the separator
if (msr->txcfg->debuglog_level >= 5) {
msr_log(msr, 5, "Cookie v0 parser: Using comma as a separator. Semi-colon was not identified!");
}
parse_cookies_v0(msr, te[i].val, msr->request_cookies, ",");
} else {
parse_cookies_v0(msr, te[i].val, msr->request_cookies, ";");
}
} else {
parse_cookies_v0(msr, te[i].val, msr->request_cookies, ";");
}
}
} else {
parse_cookies_v1(msr, te[i].val, msr->request_cookies);
}
}
}
/* Collections. */
msr->tx_vars = apr_table_make(msr->mp, 32);
if (msr->tx_vars == NULL) return -1;
msr->geo_vars = apr_table_make(msr->mp, 8);
if (msr->geo_vars == NULL) return -1;
msr->collections_original = apr_table_make(msr->mp, 8);
if (msr->collections_original == NULL) return -1;
msr->collections = apr_table_make(msr->mp, 8);
if (msr->collections == NULL) return -1;
msr->collections_dirty = apr_table_make(msr->mp, 8);
if (msr->collections_dirty == NULL) return -1;
/* Other */
msr->tcache = NULL;
msr->tcache_items = 0;
msr->matched_rules = apr_array_make(msr->mp, 16, sizeof(void *));
if (msr->matched_rules == NULL) return -1;
msr->matched_var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
if (msr->matched_var == NULL) return -1;
msr->highest_severity = 255; /* high, invalid value */
msr->removed_rules = apr_array_make(msr->mp, 16, sizeof(char *));
if (msr->removed_rules == NULL) return -1;
msr->removed_rules_tag = apr_array_make(msr->mp, 16, sizeof(char *));
if (msr->removed_rules_tag == NULL) return -1;
msr->removed_rules_msg = apr_array_make(msr->mp, 16, sizeof(char *));
if (msr->removed_rules_msg == NULL) return -1;
return 1;
}
/**
*
*/
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];
/* ENH: Setting is_relevant here will cause an audit even if noauditlog
* was set for the last rule that matched. Is this what we want?
*/
if ((msr->txcfg->auditlog_relevant_regex == NULL)
||(msr->txcfg->auditlog_relevant_regex == NOT_SET_P))
{
return 0;
}
apr_snprintf(buf, sizeof(buf), "%d", 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);
return 0;
}
/**
*
*/
static apr_status_t modsecurity_process_phase_request_headers(modsec_rec *msr) {
apr_time_t time_before;
apr_status_t rc = 0;
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Starting phase REQUEST_HEADERS.");
}
time_before = apr_time_now();
if (msr->txcfg->ruleset != NULL) {
rc = msre_ruleset_process_phase(msr->txcfg->ruleset, msr);
}
msr->time_phase1 = apr_time_now() - time_before;
return rc;
}
/**
*
*/
static apr_status_t modsecurity_process_phase_request_body(modsec_rec *msr) {
apr_time_t time_before;
apr_status_t rc = 0;
if ((msr->allow_scope == ACTION_ALLOW_REQUEST)||(msr->allow_scope == ACTION_ALLOW)) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Skipping phase REQUEST_BODY (allow used).");
}
return 0;
} else {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Starting phase REQUEST_BODY.");
}
}
time_before = apr_time_now();
if (msr->txcfg->ruleset != NULL) {
rc = msre_ruleset_process_phase(msr->txcfg->ruleset, msr);
}
msr->time_phase2 = apr_time_now() - time_before;
return rc;
}
/**
*
*/
static apr_status_t modsecurity_process_phase_response_headers(modsec_rec *msr) {
apr_time_t time_before;
apr_status_t rc = 0;
if (msr->allow_scope == ACTION_ALLOW) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Skipping phase RESPONSE_HEADERS (allow used).");
}
return 0;
} else {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Starting phase RESPONSE_HEADERS.");
}
}
time_before = apr_time_now();
if (msr->txcfg->ruleset != NULL) {
rc = msre_ruleset_process_phase(msr->txcfg->ruleset, msr);
}
msr->time_phase3 = apr_time_now() - time_before;
return rc;
}
/**
*
*/
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;
if (msr->allow_scope == ACTION_ALLOW) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Skipping phase RESPONSE_BODY (allow used).");
}
return 0;
} else {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Starting phase RESPONSE_BODY.");
}
}
time_before = apr_time_now();
if (msr->txcfg->ruleset != NULL) {
rc = msre_ruleset_process_phase(msr->txcfg->ruleset, msr);
}
msr->time_phase4 = apr_time_now() - time_before;
return rc;
}
/**
*
*/
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) {
msr_log(msr, 4, "Starting phase LOGGING.");
}
time_before = apr_time_now();
if (msr->txcfg->ruleset != NULL) {
msre_ruleset_process_phase(msr->txcfg->ruleset, msr);
}
modsecurity_persist_data(msr);
time_after = apr_time_now();
msr->time_phase5 = time_after - time_before;
/* Is this request relevant for logging purposes? */
if (msr->is_relevant == 0) {
/* Check the status */
msr->is_relevant += is_response_status_relevant(msr, msr->r->status);
/* If we processed two requests and statuses are different then
* check the other status too.
*/
if (msr->r_early->status != msr->r->status) {
msr->is_relevant += is_response_status_relevant(msr, msr->r_early->status);
}
}
/* Figure out if we want to keep the files (if there are any, of course). */
if ((msr->txcfg->upload_keep_files == KEEP_FILES_ON)
|| ((msr->txcfg->upload_keep_files == KEEP_FILES_RELEVANT_ONLY)&&(msr->is_relevant)))
{
msr->upload_remove_files = 0;
} else {
msr->upload_remove_files = 1;
}
/* Are we configured for audit logging? */
switch(msr->txcfg->auditlog_flag) {
case AUDITLOG_OFF :
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Audit log: Not configured to run for this request.");
}
return DECLINED;
break;
case AUDITLOG_RELEVANT :
if (msr->is_relevant == 0) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Audit log: Ignoring a non-relevant request.");
}
return DECLINED;
}
break;
case AUDITLOG_ON :
/* All right, do nothing */
break;
default :
msr_log(msr, 1, "Internal error: Could not determine if auditing is needed, so forcing auditing.");
break;
}
/* Invoke the Audit logger */
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Audit log: Logging this transaction.");
}
sec_audit_logger(msr);
msr->time_logging = apr_time_now() - time_after;
return 0;
}
/**
* Processes one transaction phase. The phase number does not
* need to be explicitly provided since it's already available
* 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) {
msr_log(msr, 4, "Skipping phase %d as request was already intercepted.", phase);
}
return 0;
}
/* Do not process the same phase twice. */
if (msr->phase >= phase) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Skipping phase %d because it was previously run (at %d now).",
phase, msr->phase);
}
return 0;
}
msr->phase = phase;
/* Clear out the transformation cache at the start of each phase */
if (msr->txcfg->cache_trans == MODSEC_CACHE_ENABLED) {
if (msr->tcache) {
apr_hash_index_t *hi;
void *dummy;
apr_table_t *tab;
const void *key;
apr_ssize_t klen;
#ifdef CACHE_DEBUG
apr_pool_t *mp = msr->msc_rule_mptmp;
const apr_array_header_t *ctarr;
const apr_table_entry_t *ctelts;
msre_cache_rec *rec;
int cn = 0;
int ri;
#else
apr_pool_t *mp = msr->mp;
#endif
for (hi = apr_hash_first(mp, msr->tcache); hi; hi = apr_hash_next(hi)) {
apr_hash_this(hi, &key, &klen, &dummy);
tab = (apr_table_t *)dummy;
if (tab == NULL) continue;
#ifdef CACHE_DEBUG
/* Dump the cache out as we clear */
ctarr = apr_table_elts(tab);
ctelts = (const apr_table_entry_t*)ctarr->elts;
for (ri = 0; ri < ctarr->nelts; ri++) {
cn++;
rec = (msre_cache_rec *)ctelts[ri].val;
if (rec->changed) {
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CACHE: %5d) hits=%d key=%pp %x;%s=\"%s\" (%pp - %pp)",
cn, rec->hits, key, rec->num, rec->path, log_escape_nq_ex(mp, rec->val, rec->val_len),
rec->val, rec->val + rec->val_len);
}
}
else {
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CACHE: %5d) hits=%d key=%pp %x;%s=",
cn, rec->hits, key, rec->num, rec->path);
}
}
}
#endif
apr_table_clear(tab);
apr_hash_set(msr->tcache, key, klen, NULL);
}
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Cleared transformation cache for phase %d", msr->phase);
}
}
msr->tcache_items = 0;
msr->tcache = apr_hash_make(msr->mp);
if (msr->tcache == NULL) return -1;
}
switch(phase) {
case 1 :
return modsecurity_process_phase_request_headers(msr);
case 2 :
return modsecurity_process_phase_request_body(msr);
case 3 :
return modsecurity_process_phase_response_headers(msr);
case 4 :
return modsecurity_process_phase_response_body(msr);
case 5 :
return modsecurity_process_phase_logging(msr);
default :
msr_log(msr, 1, "Invalid processing phase: %d", msr->phase);
break;
}
return -1;
}
modsecurity-apache-2.9.13/apache2/modsecurity.h 0000664 0000000 0000000 00000057571 15174655764 0021462 0 ustar 00root root 0000000 0000000 /*
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
* 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
*
* 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.
*/
#ifndef _MODSECURITY_H_
#define _MODSECURITY_H_
#include
#include
#include
#include
#include
typedef struct rule_exception rule_exception;
typedef struct rule_exception hash_method;
typedef struct modsec_rec modsec_rec;
typedef struct directory_config directory_config;
typedef struct error_message_t error_message_t;
typedef struct msc_engine msc_engine;
typedef struct msc_data_chunk msc_data_chunk;
typedef struct msc_arg msc_arg;
typedef struct msc_string msc_string;
typedef struct msc_parm msc_parm;
#include "msc_release.h"
#include "msc_logging.h"
#include "msc_multipart.h"
#include "msc_pcre.h"
#include "msc_util.h"
#include "msc_json.h"
#include "msc_xml.h"
#include "msc_tree.h"
#include "msc_geo.h"
#include "msc_gsb.h"
#include "msc_unicode.h"
#include "re.h"
#include "msc_crypt.h"
#include "msc_remote_rules.h"
#include "ap_config.h"
#include "apr_md5.h"
#include "apr_strings.h"
#include "apr_hash.h"
#include "httpd.h"
#include "http_config.h"
#include "http_log.h"
#include "http_protocol.h"
#if defined(WITH_LUA)
#include "msc_lua.h"
#endif
#define PHASE_REQUEST_HEADERS 1
#define PHASE_REQUEST_BODY 2
#define PHASE_RESPONSE_HEADERS 3
#define PHASE_RESPONSE_BODY 4
#define PHASE_LOGGING 5
#define PHASE_FIRST PHASE_REQUEST_HEADERS
#define PHASE_LAST PHASE_LOGGING
#define NOT_SET -1l
#define NOT_SET_P ((void *)-1l)
#define CREATEMODE ( APR_UREAD | APR_UWRITE | APR_GREAD )
#define CREATEMODE_DIR ( APR_UREAD | APR_UWRITE | APR_UEXECUTE | APR_GREAD | APR_GEXECUTE )
#if defined(NETWARE)
#define CREATEMODE_UNISTD ( S_IREAD | S_IWRITE )
#elif defined(WIN32)
#define CREATEMODE_UNISTD ( _S_IREAD | _S_IWRITE )
#else
#define CREATEMODE_UNISTD ( S_IRUSR | S_IWUSR | S_IRGRP )
#endif
#if !defined(O_BINARY)
#define O_BINARY (0)
#endif
#ifndef PIPE_BUF
#define PIPE_BUF (512)
#endif
#define REQUEST_BODY_HARD_LIMIT 1073741824L
#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
#define RESPONSE_BODY_LIMIT_ACTION_REJECT 0
#define RESPONSE_BODY_LIMIT_ACTION_PARTIAL 1
#define REQUEST_BODY_FORCEBUF_OFF 0
#define REQUEST_BODY_FORCEBUF_ON 1
#define REQUEST_BODY_LIMIT_ACTION_REJECT 0
#define REQUEST_BODY_LIMIT_ACTION_PARTIAL 1
#define SECACTION_TARGETS "REMOTE_ADDR"
#define SECACTION_ARGS "@unconditionalMatch"
#define SECMARKER_TARGETS "REMOTE_ADDR"
#define SECMARKER_ARGS "@noMatch"
#define SECMARKER_BASE_ACTIONS "t:none,pass,marker:"
#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
#include "unixd.h"
#define __SET_MUTEX_PERMS
#endif
#define COOKIES_V0 0
#define COOKIES_V1 1
#ifdef WIN32
#include
#else
#include
#include
#endif
#define NOTE_MSR "modsecurity-tx-context"
#define FATAL_ERROR "ModSecurity: Fatal error (memory allocation or unexpected internal error)!"
extern DSOLOCAL char *new_server_signature;
extern DSOLOCAL char *real_server_signature;
extern DSOLOCAL char *chroot_dir;
extern module AP_MODULE_DECLARE_DATA security2_module;
extern DSOLOCAL const command_rec module_directives[];
extern DSOLOCAL unsigned long int msc_pcre_match_limit;
extern DSOLOCAL unsigned long int msc_pcre_match_limit_recursion;
#ifdef WITH_REMOTE_RULES
extern DSOLOCAL msc_remote_rules_server *remote_rules_server;
#endif
extern DSOLOCAL int remote_rules_fail_action;
extern DSOLOCAL char *remote_rules_fail_message;
extern DSOLOCAL int status_engine_state;
extern DSOLOCAL int conn_limits_filter_state;
extern DSOLOCAL unsigned long int conn_read_state_limit;
extern DSOLOCAL TreeRoot *conn_read_state_whitelist;
extern DSOLOCAL TreeRoot *conn_read_state_suspicious_list;
extern DSOLOCAL unsigned long int conn_write_state_limit;
extern DSOLOCAL TreeRoot *conn_write_state_whitelist;
extern DSOLOCAL TreeRoot *conn_write_state_suspicious_list;
extern DSOLOCAL unsigned long int unicode_codepage;
extern DSOLOCAL int *unicode_map_table;
#define RESBODY_STATUS_NOT_READ 0 /* we were not configured to read the body */
#define RESBODY_STATUS_ERROR 1 /* error occured while we were reading the body */
#define RESBODY_STATUS_PARTIAL 2 /* partial body content available in the brigade */
#define RESBODY_STATUS_READ_BRIGADE 3 /* body was read but not flattened */
#define RESBODY_STATUS_READ 4 /* body was read and flattened */
#define IF_STATUS_NONE 0
#define IF_STATUS_WANTS_TO_RUN 1
#define IF_STATUS_COMPLETE 2
#define OF_STATUS_NOT_STARTED 0
#define OF_STATUS_IN_PROGRESS 1
#define OF_STATUS_COMPLETE 2
#define MSC_REQBODY_NONE 0
#define MSC_REQBODY_MEMORY 1
#define MSC_REQBODY_DISK 2
#define ACTION_NONE 0
#define ACTION_DENY 1
#define ACTION_REDIRECT 2
#define ACTION_PROXY 3
#define ACTION_DROP 4
#define ACTION_ALLOW 5
#define ACTION_ALLOW_REQUEST 6
#define ACTION_ALLOW_PHASE 7
#define ACTION_PAUSE 8
#define MODSEC_DISABLED 0
#define MODSEC_DETECTION_ONLY 1
#define MODSEC_ENABLED 2
#define STATUS_ENGINE_ENABLED 1
#define STATUS_ENGINE_DISABLED 0
#define REMOTE_RULES_ABORT_ON_FAIL 0
#define REMOTE_RULES_WARN_ON_FAIL 1
#define HASH_DISABLED 0
#define HASH_ENABLED 1
#define HASH_URL_HREF_HASH_RX 0
#define HASH_URL_HREF_HASH_PM 1
#define HASH_URL_FACTION_HASH_RX 2
#define HASH_URL_FACTION_HASH_PM 3
#define HASH_URL_LOCATION_HASH_RX 4
#define HASH_URL_LOCATION_HASH_PM 5
#define HASH_URL_IFRAMESRC_HASH_RX 6
#define HASH_URL_IFRAMESRC_HASH_PM 7
#define HASH_URL_FRAMESRC_HASH_RX 8
#define HASH_URL_FRAMESRC_HASH_PM 9
#define HASH_KEYONLY 0
#define HASH_SESSIONID 1
#define HASH_REMOTEIP 2
#define MODSEC_CACHE_DISABLED 0
#define MODSEC_CACHE_ENABLED 1
#define MODSEC_OFFLINE 0
#define MODSEC_ONLINE 1
#define REGEX_CAPTURE_BUFLEN 1024
#define KEEP_FILES_OFF 0
#define KEEP_FILES_ON 1
#define KEEP_FILES_RELEVANT_ONLY 2
#define RULE_EXCEPTION_IMPORT_ID 1
#define RULE_EXCEPTION_IMPORT_MSG 2
#define RULE_EXCEPTION_REMOVE_ID 3
#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 {
int type;
const char *param;
void *param_data;
};
struct modsec_rec {
apr_pool_t *mp;
msc_engine *modsecurity;
request_rec *r_early;
request_rec *r;
directory_config *dcfg1;
directory_config *dcfg2;
directory_config *usercfg;
directory_config *txcfg;
unsigned int reqbody_should_exist;
unsigned int reqbody_chunked;
unsigned int phase;
unsigned int phase_request_headers_complete;
unsigned int phase_request_body_complete;
apr_bucket_brigade *if_brigade;
unsigned int if_seen_eos;
unsigned int if_status;
unsigned int if_started_forwarding;
apr_size_t reqbody_length;
apr_bucket_brigade *of_brigade;
unsigned int of_status;
unsigned int of_done_reading;
unsigned int of_skipping;
unsigned int of_partial;
unsigned int of_is_error;
unsigned int resbody_status;
apr_size_t resbody_length;
char *resbody_data;
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;
unsigned int of_stream_changed;
unsigned int if_stream_changed;
apr_array_header_t *error_messages;
apr_array_header_t *alerts;
const char *txid;
const char *sessionid;
const char *userid;
const char *server_software;
const char *local_addr;
unsigned int local_port;
const char *local_user;
/* client */
const char *remote_addr;
unsigned int remote_port;
const char *remote_user;
/* useragent */
const char *useragent_ip;
/* request */
const char *request_line;
const char *request_method;
const char *request_uri;
const char *query_string;
const char *request_protocol;
const char *hostname;
apr_table_t *request_headers;
apr_off_t request_content_length;
const char *request_content_type;
apr_table_t *arguments;
apr_table_t *arguments_to_sanitize;
apr_table_t *request_headers_to_sanitize;
apr_table_t *response_headers_to_sanitize;
apr_table_t *request_cookies;
apr_table_t *pattern_to_sanitize;
unsigned int urlencoded_error;
unsigned int inbound_error;
unsigned int outbound_error;
unsigned int is_relevant;
apr_table_t *tx_vars;
/* ENH: refactor to allow arbitrary var tables */
apr_table_t *geo_vars;
/* response */
unsigned int response_status;
const char *status_line;
const char *response_protocol;
apr_table_t *response_headers;
unsigned int response_headers_sent;
apr_off_t bytes_sent;
/* modsecurity request body processing stuff */
unsigned int msc_reqbody_storage; /* on disk or in memory */
unsigned int msc_reqbody_spilltodisk;
unsigned int msc_reqbody_read;
apr_pool_t *msc_reqbody_mp; /* this is where chunks are allocated from */
apr_array_header_t *msc_reqbody_chunks; /* data chunks when stored in memory */
unsigned int msc_reqbody_length; /* the amount of data received */
int msc_reqbody_chunk_position; /* used when retrieving the body */
unsigned int msc_reqbody_chunk_offset; /* offset of the chunk currently in use */
msc_data_chunk *msc_reqbody_chunk_current; /* current chunk */
char *msc_reqbody_buffer;
const char *msc_reqbody_filename; /* when stored on disk */
int msc_reqbody_fd;
msc_data_chunk *msc_reqbody_disk_chunk;
const char *msc_reqbody_processor;
int msc_reqbody_error;
const char *msc_reqbody_error_msg;
apr_size_t msc_reqbody_no_files_length;
char *msc_full_request_buffer;
int msc_full_request_length;
char *multipart_filename;
char *multipart_name;
multipart_data *mpd; /* MULTIPART processor data structure */
xml_data *xml; /* XML processor data structure */
#ifdef WITH_YAJL
json_data *json; /* JSON processor data structure */
#endif
/* audit logging */
char *new_auditlog_boundary;
char *new_auditlog_filename;
apr_file_t *new_auditlog_fd;
unsigned int new_auditlog_size;
apr_md5_ctx_t new_auditlog_md5ctx;
unsigned int was_intercepted;
unsigned int rule_was_intercepted;
unsigned int intercept_phase;
msre_actionset *intercept_actionset;
const char *intercept_message;
/* performance measurement */
apr_time_t request_time;
apr_time_t time_phase1;
apr_time_t time_phase2;
apr_time_t time_phase3;
apr_time_t time_phase4;
apr_time_t time_phase5;
apr_time_t time_storage_read;
apr_time_t time_storage_write;
apr_time_t time_logging;
apr_time_t time_gc;
apr_table_t *perf_rules;
apr_array_header_t *matched_rules;
msc_string *matched_var;
int highest_severity;
/* upload */
int upload_extract_files;
int upload_remove_files;
int upload_files_count;
/* other */
apr_table_t *collections_original;
apr_table_t *collections;
apr_table_t *collections_dirty;
/* rule processing temp pool */
apr_pool_t *msc_rule_mptmp;
/* content injection */
const char *content_prepend;
apr_off_t content_prepend_len;
const char *content_append;
apr_off_t content_append_len;
/* data cache */
apr_hash_t *tcache;
apr_size_t tcache_items;
/* removed rules */
apr_array_header_t *removed_rules;
apr_array_header_t *removed_rules_tag;
apr_array_header_t *removed_rules_msg;
/* removed targets */
apr_table_t *removed_targets;
/* When "allow" is executed the variable below is
* updated to contain the scope of the allow action. Set
* at 0 by default, it will have ACTION_ALLOW if we are
* to allow phases 1-4 and ACTION_ALLOW_REQUEST if we
* are to allow phases 1-2 only.
*/
unsigned int allow_scope;
/* matched vars */
apr_table_t *matched_vars;
/* Generic request body processor context to be used by custom parsers. */
void *reqbody_processor_ctx;
htmlDocPtr crypto_html_tree;
#if defined(WITH_LUA)
#ifdef CACHE_LUA
lua_State *L;
#endif
#endif
int msc_sdbm_delete_error;
};
struct directory_config {
apr_pool_t *mp;
msre_ruleset *ruleset;
int is_enabled;
int reqbody_access;
int reqintercept_oe;
int reqbody_buffering;
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;
apr_table_t *of_mime_types;
int of_mime_types_cleared;
int of_limit_action;
int if_limit_action;
const char *debuglog_name;
int debuglog_level;
apr_file_t *debuglog_fd;
int cookie_format;
int argument_separator;
const char *cookiev0_separator;
int rule_inheritance;
apr_array_header_t *rule_exceptions;
/* -- Audit log -- */
/* Max rule time */
int max_rule_time;
/* Whether audit log should be enabled in the context or not */
int auditlog_flag;
/* AUDITLOG_SERIAL (single file) or AUDITLOG_CONCURRENT (multiple files) */
int auditlog_type;
#ifdef WITH_YAJL
/* AUDITLOGFORMAT_NATIVE or AUDITLOGFORMAT_JSON */
int auditlog_format;
#endif
/* Mode for audit log directories and files */
apr_fileperms_t auditlog_dirperms;
apr_fileperms_t auditlog_fileperms;
/* The name of the audit log file (for the old type), or the
* name of the index file (for the new audit log type)
*/
char *auditlog_name;
/* The name of the secondary index file */
char *auditlog2_name;
/* The file descriptors for the files above */
apr_file_t *auditlog_fd;
apr_file_t *auditlog2_fd;
/* For the new-style audit log only, the path where
* audit log entries will be stored
*/
char *auditlog_storage_dir;
/* A list of parts to include in the new-style audit log
* entry. By default, it contains 'ABCFHZ'. Have a look at
* the AUDITLOG_PART_* constants above to decipher the
* meaning.
*/
char *auditlog_parts;
/* A regular expression that determines if a response
* status is treated as relevant.
*/
msc_regex_t *auditlog_relevant_regex;
/* Upload */
const char *tmp_dir;
const char *upload_dir;
int upload_keep_files;
int upload_validates_files;
int upload_filemode; /* int only so NOT_SET works */
int upload_file_limit;
/* Used only in the configuration phase. */
msre_rule *tmp_chain_starter;
msre_actionset *tmp_default_actionset;
apr_table_t *tmp_rule_placeholders;
/* Misc */
const char *data_dir;
const char *webappid;
const char *sensor_id;
const char *httpBlkey;
/* Content injection. */
int content_injection_enabled;
/* Stream Inspection */
int stream_inbody_inspection;
int stream_outbody_inspection;
/* Geo Lookup */
geo_db *geo;
/* Gsb Lookup */
gsb_db *gsb;
/* Unicode map */
unicode_map *u_map;
/* Cache */
int cache_trans;
int cache_trans_incremental;
apr_size_t cache_trans_min;
apr_size_t cache_trans_max;
apr_size_t cache_trans_maxitems;
/* Array to hold signatures of components, which will
* appear in the ModSecurity signature in the audit log.
*/
apr_array_header_t *component_signatures;
/* Request character encoding. */
const char *request_encoding;
int disable_backend_compression;
/* Collection timeout */
int col_timeout;
/* hash of ids */
apr_hash_t *rule_id_htab;
/* Hash */
apr_array_header_t *hash_method;
const char *crypto_key;
int crypto_key_len;
const char *crypto_param_name;
int hash_is_enabled;
int hash_enforcement;
int crypto_key_add;
int crypto_hash_href_rx;
int crypto_hash_faction_rx;
int crypto_hash_location_rx;
int crypto_hash_iframesrc_rx;
int crypto_hash_framesrc_rx;
int crypto_hash_href_pm;
int crypto_hash_faction_pm;
int crypto_hash_location_pm;
int crypto_hash_iframesrc_pm;
int crypto_hash_framesrc_pm;
/* 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.
*/
#if 0
msc_remote_rules_server *remote_rules;
int remote_timeout;
#endif
};
struct error_message_t {
const char *file;
int line;
int level;
apr_status_t status;
const char *message;
};
struct msc_engine {
apr_pool_t *mp;
apr_global_mutex_t *auditlog_lock;
apr_global_mutex_t *geo_lock;
#ifdef GLOBAL_COLLECTION_LOCK
apr_global_mutex_t *dbm_lock;
#endif
msre_engine *msre;
unsigned int processing_mode;
};
struct msc_data_chunk {
char *data;
apr_size_t length;
unsigned int is_permanent;
};
struct msc_arg {
const char *name;
unsigned int name_len;
unsigned int name_origin_offset;
unsigned int name_origin_len;
const char *value;
unsigned int value_len;
unsigned int value_origin_offset;
unsigned int value_origin_len;
const char *origin;
unsigned int marked_for_sanitization;
};
struct msc_string {
char *name;
unsigned int name_len;
char *value;
unsigned int value_len;
};
struct msc_parm {
char *value;
int pad_1;
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);
int DSOLOCAL modsecurity_init(msc_engine *msce, apr_pool_t *mp);
void DSOLOCAL modsecurity_child_init(msc_engine *msce);
void DSOLOCAL modsecurity_shutdown(msc_engine *msce);
apr_status_t DSOLOCAL modsecurity_tx_init(modsec_rec *msr);
apr_status_t DSOLOCAL modsecurity_process_phase(modsec_rec *msr, unsigned int phase);
/* Request body functions */
apr_status_t DSOLOCAL modsecurity_request_body_start(modsec_rec *msr, char **error_msg);
apr_status_t DSOLOCAL modsecurity_request_body_store(modsec_rec *msr,
const char *data, apr_size_t length, char **error_msg);
apr_status_t DSOLOCAL modsecurity_request_body_end(modsec_rec *msr, char **error_msg);
apr_status_t DSOLOCAL modsecurity_request_body_to_stream(modsec_rec *msr, const char *buffer, int buflen, char **error_msg);
apr_status_t DSOLOCAL modsecurity_request_body_retrieve_start(modsec_rec *msr, char **error_msg);
apr_status_t DSOLOCAL modsecurity_request_body_retrieve_end(modsec_rec *msr);
/* Retrieves up to nbytes bytes of the request body. Returns 1 on
* success, 0 when there is no more data, or -1 on error. On return
* nbytes will contain the number of bytes stored in the buffer.
*/
apr_status_t DSOLOCAL modsecurity_request_body_retrieve(modsec_rec *msr, msc_data_chunk **chunk,
long int nbytes, char **error_msg);
void DSOLOCAL msc_add(modsec_rec *msr, int level, msre_actionset *actionset,
const char *action_message, const char *rule_message);
const char DSOLOCAL *msc_alert_message(modsec_rec *msr, msre_actionset *actionset, const char *action_message,
const char *rule_message);
void DSOLOCAL msc_alert(modsec_rec *msr, int level, msre_actionset *actionset, const char *action_message,
const char *rule_message);
apr_status_t DSOLOCAL modsecurity_request_body_clear(modsec_rec *msr, char **error_msg);
#endif
modsecurity-apache-2.9.13/apache2/modsecurity_config.h 0000664 0000000 0000000 00000001217 15174655764 0022771 0 ustar 00root root 0000000 0000000 /* Some APR files define PACKAGE* constants, which may conflict
* so this is here to prevent that by removing them.
*/
#ifndef WIN32
/* Undefine all these so there are no conflicts */
#undef PACKAGE
#undef PACKAGE_BUGREPORT
#undef PACKAGE_NAME
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#undef PACKAGE_URL
#undef PACKAGE_VERSION
/* Include the real autoconf header */
#include "modsecurity_config_auto.h"
/* Undefine all these (again) so there are no conflicts */
#undef PACKAGE
#undef PACKAGE_BUGREPORT
#undef PACKAGE_NAME
#undef PACKAGE_STRING
#undef PACKAGE_TARNAME
#undef PACKAGE_URL
#undef PACKAGE_VERSION
#endif
modsecurity-apache-2.9.13/apache2/modsecurity_config_auto.h.in 0000664 0000000 0000000 00000012572 15174655764 0024434 0 ustar 00root root 0000000 0000000 /* apache2/modsecurity_config_auto.h.in. Generated from configure.ac by autoheader. */
/* Define if the operating system is AIX */
#undef AIX
/* Define if the operating system is FREEBSD */
#undef FREEBSD
/* Define to 1 if you have the 'atexit' function. */
#undef HAVE_ATEXIT
/* Define to 1 if you have the header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you have the 'fchmod' function. */
#undef HAVE_FCHMOD
/* Define to 1 if you have the header file. */
#undef HAVE_FCNTL_H
/* Define to 1 if you have the 'getcwd' function. */
#undef HAVE_GETCWD
/* Define to 1 if you have the header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the header file. */
#undef HAVE_LIMITS_H
/* Define to 1 if your system has a GNU libc compatible 'malloc' function, and
to 0 otherwise. */
#undef HAVE_MALLOC
/* Define to 1 if you have the 'memmove' function. */
#undef HAVE_MEMMOVE
/* Define to 1 if you have the 'memset' function. */
#undef HAVE_MEMSET
/* Define to 1 if you have the header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the header file. */
#undef HAVE_STDIO_H
/* Define to 1 if you have the header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the 'strcasecmp' function. */
#undef HAVE_STRCASECMP
/* Define to 1 if you have the 'strcasestr' function. */
#undef HAVE_STRCASESTR
/* Define to 1 if you have the 'strchr' function. */
#undef HAVE_STRCHR
/* Define to 1 if you have the 'strdup' function. */
#undef HAVE_STRDUP
/* Define to 1 if you have the 'strerror' function. */
#undef HAVE_STRERROR
/* Define to 1 if you have the header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the 'strncasecmp' function. */
#undef HAVE_STRNCASECMP
/* Define to 1 if you have the 'strrchr' function. */
#undef HAVE_STRRCHR
/* Define to 1 if you have the 'strstr' function. */
#undef HAVE_STRSTR
/* Define to 1 if you have the 'strtol' function. */
#undef HAVE_STRTOL
/* Define to 1 if you have the header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the header file. */
#undef HAVE_SYS_UTSNAME_H
/* Define to 1 if you have the header file. */
#undef HAVE_UNISTD_H
/* Define if the operating system is HPUX */
#undef HPUX
/* Define if the operating system is LINUX */
#undef LINUX
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#undef LT_OBJDIR
/* Define if the operating system is Macintosh OSX */
#undef MACOSX
/* Define if the operating system is NetBSD */
#undef NETBSD
/* Define if the operating system is OpenBSD */
#undef OPENBSD
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define if the operating system is SOLARIS */
#undef SOLARIS
/* Define to 1 if all of the C89 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
#undef STDC_HEADERS
/* Define to 1 if your declares 'struct tm'. */
#undef TM_IN_SYS_TIME
/* Version number of package */
#undef VERSION
/* APR util was compiled with crypto support */
#undef WITH_APU_CRYPTO
/* Enables SecRemoteRules support */
#undef WITH_REMOTE_RULES
/* Define for Solaris 2.5.1 so the uint8_t typedef from ,
, or is not used. If the typedef were allowed, the
#define below would cause a syntax error. */
#undef _UINT8_T
/* Define to empty if 'const' does not conform to ANSI C. */
#undef const
/* Define to '__inline__' or '__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */
#ifndef __cplusplus
#undef inline
#endif
/* Define to rpl_malloc if the replacement function should be used. */
#undef malloc
/* Define as a signed integer type capable of holding a process identifier. */
#undef pid_t
/* Define to the equivalent of the C99 'restrict' keyword, or to
nothing if this is not supported. Do not define if restrict is
supported only directly. */
#undef restrict
/* Work around a bug in older versions of Sun C++, which did not
#define __restrict__ or support _Restrict or __restrict__
even though the corresponding Sun C compiler ended up with
"#define restrict _Restrict" or "#define restrict __restrict__"
in the previous line. This workaround can be removed once
we assume Oracle Developer Studio 12.5 (2016) or later. */
#if defined __SUNPRO_CC && !defined __RESTRICT && !defined __restrict__
# define _Restrict
# define __restrict__
#endif
/* Define as 'unsigned int' if doesn't define. */
#undef size_t
/* Define to the type of an unsigned integer type of width exactly 8 bits if
such a type exists and the standard includes do not define it. */
#undef uint8_t
modsecurity-apache-2.9.13/apache2/modules.mk 0000664 0000000 0000000 00000001475 15174655764 0020733 0 ustar 00root root 0000000 0000000 MOD_SECURITY2 = mod_security2 apache2_config apache2_io apache2_util \
re re_operators re_actions re_tfns re_variables msc_json \
msc_logging msc_xml msc_multipart modsecurity msc_parsers msc_util msc_pcre \
persist_dbm msc_reqbody pdf_protect msc_geo msc_gsb msc_crypt msc_tree msc_unicode acmp msc_lua
H = re.h modsecurity.h msc_logging.h msc_multipart.h msc_parsers.h msc_json.h \
msc_pcre.h msc_util.h msc_xml.h persist_dbm.h apache2.h pdf_protect.h \
msc_geo.h msc_gsb.h msc_crypt.h msc_tree.h msc_unicode.h acmp.h utf8tables.h msc_lua.h
${MOD_SECURITY2:=.slo}: ${H}
${MOD_SECURITY2:=.lo}: ${H}
${MOD_SECURITY2:=.o}: ${H}
mod_security2.la: ${MOD_SECURITY2:=.slo}
$(SH_LINK) -rpath $(libexecdir) -module -avoid-version ${MOD_SECURITY2:=.lo}
DISTCLEAN_TARGETS = modules.mk
shared = mod_security2.la
modsecurity-apache-2.9.13/apache2/msc_crypt.c 0000664 0000000 0000000 00000201131 15174655764 0021070 0 ustar 00root root 0000000 0000000 /*
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
* 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
*
* 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 "msc_crypt.h"
#include "msc_util.h"
#include "apr_sha1.h"
#include "apr_uri.h"
#include "apr_base64.h"
#include "acmp.h"
#include "libxml/HTMLtree.h"
#include "libxml/uri.h"
#include
/**
* \brief Normalize path in URI
*
* \param msr ModSecurity transaction resource
* \param input The URI to be normalized
*
* \retval input normalized input
* \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;
uri = xmlParseURI(input);
if(uri != NULL && uri->path) {
if(uri->scheme) {
content = apr_psprintf(msr->mp, "%s://", uri->scheme);
parsed_content = apr_pstrcat(msr->mp, content, NULL);
}
if(uri->server) {
content = apr_psprintf(msr->mp, "%s", uri->server);
if(parsed_content)
parsed_content = apr_pstrcat(msr->mp, parsed_content, content, NULL);
else
parsed_content = apr_pstrcat(msr->mp, content, NULL);
}
if(uri->port) {
content = apr_psprintf(msr->mp, ":%d", uri->port);
if(parsed_content)
parsed_content = apr_pstrcat(msr->mp, parsed_content, content, NULL);
else
parsed_content = apr_pstrcat(msr->mp, content, NULL);
}
if(uri->path) {
char *Uri = NULL;
/*int i;*/
char *abs_link = NULL;
char *filename = NULL;
char *abs_path = NULL;
char *abs_uri = NULL;
if (uri->path[0] != '/') {
/* uri->path is relative. make it absolute */
filename = file_basename(msr->mp, msr->r->parsed_uri.path);
if(filename == NULL || (strlen(msr->r->parsed_uri.path) - strlen(filename) < 0))
return NULL;
abs_path = apr_pstrndup(msr->mp, msr->r->parsed_uri.path, strlen(msr->r->parsed_uri.path) - strlen(filename));
abs_uri = apr_pstrcat(msr->mp, abs_path, uri->path, NULL);
abs_link = apr_pstrdup(msr->mp, abs_uri);
}
else {
abs_link = apr_pstrdup(msr->mp, uri->path);
}
xmlNormalizeURIPath(abs_link);
Uri = apr_pstrdup(msr->mp, abs_link);
/*
for(i = 0; i < (int)strlen(Uri); i++) {
if(Uri[i] != '.' && Uri[i] != '/') {
if (i - 1 < 0)
i = 0;
else
i--;
if(Uri[i] == '/')
--bytes;
break;
} else {
bytes++;
}
}
if(bytes >= (int)strlen(uri->path))
return NULL;
*/
content = apr_psprintf(msr->mp, "%s", Uri);
if(parsed_content)
parsed_content = apr_pstrcat(msr->mp, parsed_content, content, NULL);
else
parsed_content = apr_pstrcat(msr->mp, content, NULL);
}
if(uri->query_raw) {
content = apr_psprintf(msr->mp, "?%s", uri->query_raw);
if(parsed_content)
parsed_content = apr_pstrcat(msr->mp, parsed_content, content, NULL);
else
parsed_content = apr_pstrcat(msr->mp, content, NULL);
}
if(uri->fragment) {
content = apr_psprintf(msr->mp, "#%s", uri->fragment);
if(parsed_content)
parsed_content = apr_pstrcat(msr->mp, parsed_content, content, NULL);
else
parsed_content = apr_pstrcat(msr->mp, content, NULL);
}
xmlFreeURI(uri);
return apr_pstrdup(msr->mp, parsed_content);
}
if(uri != NULL) xmlFreeURI(uri);
return apr_pstrdup(msr->mp, input);
}
/**
* \brief Create a random password
*
* \param mp ModSecurity transaction memory pool
*
* \retval key random key
*/
char *getkey(apr_pool_t *mp) {
unsigned char digest[APR_SHA1_DIGESTSIZE];
char *sig, *key, *value;
apr_sha1_ctx_t ctx;
char salt[64];
apr_generate_random_bytes(salt, sizeof(salt));
key = apr_psprintf(mp,"%.*s",(int)sizeof(salt),salt);
apr_sha1_init (&ctx);
apr_sha1_update (&ctx, (const char*)key, strlen(key));
apr_sha1_update (&ctx, "\0", 1);
apr_generate_random_bytes(salt, sizeof(salt));
value = apr_psprintf(mp,"%.*s",(int)sizeof(salt),salt);
apr_sha1_update (&ctx, value, strlen (value));
apr_sha1_final (digest, &ctx);
sig = apr_pcalloc (mp, apr_base64_encode_len (sizeof (digest)));
apr_base64_encode (sig, (const char*)digest, sizeof (digest));
return sig;
}
/**
* \brief Generate the MAC for a given message
*
* \param msr ModSecurity transaction resource
* \param key The key used within HMAC
* \param key_len Key length
* \param msg The message to generate the MAC
* \param msglen The message length
*
* \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];
unsigned char nkey[APR_SHA1_DIGESTSIZE];
unsigned char *hmac_key = (unsigned char *) key;
char hex_digest[APR_SHA1_DIGESTSIZE * 2 + 1], *hmac_digest;
const char hex[] = "0123456789abcdef";
int i;
if (key_len > HMAC_PAD_SIZE-1) {
hmac_key = nkey;
key_len = APR_SHA1_DIGESTSIZE;
}
memset ((void *) hmac_ipad, 0, sizeof (hmac_ipad));
memset ((void *) hmac_opad, 0, sizeof (hmac_opad));
memmove (hmac_ipad, hmac_key, key_len);
memmove (hmac_opad, hmac_key, key_len);
for (i = 0; i < HMAC_PAD_SIZE-1; i++) {
hmac_ipad[i] ^= 0x36;
hmac_opad[i] ^= 0x5c;
}
apr_sha1_init (&ctx);
apr_sha1_update_binary (&ctx, hmac_ipad, HMAC_PAD_SIZE-1);
apr_sha1_update_binary (&ctx, (const unsigned char *) msg, msglen);
apr_sha1_final (digest, &ctx);
apr_sha1_init (&ctx);
apr_sha1_update_binary (&ctx, hmac_opad, HMAC_PAD_SIZE-1);
apr_sha1_update_binary (&ctx, digest, sizeof (digest));
apr_sha1_final (digest, &ctx);
hmac_digest = hex_digest;
for (i = 0; i < sizeof (digest); i++) {
*hmac_digest++ = hex[digest[i] >> 4];
*hmac_digest++ = hex[digest[i] & 0xF];
}
*hmac_digest = '\0';
return apr_pstrdup (msr->mp, hex_digest);
}
/**
* \brief Init html response body parser
*
* \param msr ModSecurity transaction resource
*
* \retval 1 on success
* \retval -1 on fail
*/
int init_response_body_html_parser(modsec_rec *msr) {
char *charset = NULL;
char *final_charset = NULL;
char sep;
if(msr == NULL) return -1;
if(msr->r == NULL) return -1;
if(msr->r->content_type == NULL) return -1;
if(msr->crypto_html_tree != NULL){
xmlFreeDoc(msr->crypto_html_tree);
msr->crypto_html_tree = NULL;
}
if((strncmp("text/html",msr->r->content_type, 9) != 0)){
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4,
"init_response_body_html_parser: skipping html_tree generation for Content[%s].", msr->r->content_type);
if(msr->crypto_html_tree != NULL){
xmlFreeDoc(msr->crypto_html_tree);
msr->crypto_html_tree = NULL;
}
return -1;
}
if (msr->resbody_length == 0) {
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4, "init_response_body_html_parser: skipping html_tree generation for zero length respomse body.");
msr->crypto_html_tree = NULL;
return 0;
}
if((msr->r->content_encoding == NULL)||(apr_strnatcasecmp(msr->r->content_encoding,"(null)")==0)){
charset=m_strcasestr(msr->r->content_type,"charset=");
if(charset == NULL){
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4, "init_response_body_html_parser: assuming ISO-8859-1.");
msr->crypto_html_tree = htmlReadMemory(msr->resbody_data, msr->resbody_length, NULL,
"ISO-8859-1", HTML_PARSE_RECOVER | HTML_PARSE_NOBLANKS | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
htmlSetMetaEncoding ((htmlDocPtr) msr->crypto_html_tree, (const xmlChar *) "ISO-8859-1");
}
else{
charset+=8;
final_charset=strchr(charset,' ');
if(final_charset == NULL) final_charset = strchr(charset,';');
if(final_charset != NULL) {
sep = *final_charset;
*final_charset = '\0';
}
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4,
"init_response_body_html_parser: Charset[%s]",charset);
msr->crypto_html_tree = htmlReadMemory(msr->resbody_data, msr->resbody_length, NULL,
charset, HTML_PARSE_RECOVER| HTML_PARSE_NOBLANKS | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
htmlSetMetaEncoding ((htmlDocPtr) msr->crypto_html_tree, (const xmlChar *)charset);
if(final_charset != NULL) *final_charset=sep;
}
}
else{
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4,"init_response_body_html_parser: Enconding[%s].",msr->r->content_encoding);
msr->crypto_html_tree = htmlReadMemory(msr->resbody_data, msr->resbody_length, NULL,
msr->r->content_encoding, HTML_PARSE_RECOVER | HTML_PARSE_NOBLANKS | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
htmlSetMetaEncoding ((htmlDocPtr) msr->crypto_html_tree, (const xmlChar *)msr->r->content_encoding);
}
if(msr->crypto_html_tree == NULL){
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4,
"init_response_body_html_parser: Failed to parse response body.");
return -1;
}
else {
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4,
"init_response_body_html_parser: Successfully html parser generated.");
return 1;
}
return 1;
}
/**
* \brief Execute all hash methods
*
* \param msr ModSecurity transaction resource
* \param link The html attr value to be checked
* \param type The hash method type
*
* \retval 1 Match
* \retval 0 No Match
* \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;
char *my_error_msg = NULL;
int ovector[33];
int rc;
if(msr == NULL) return -1;
em = (hash_method **)msr->txcfg->hash_method->elts;
if(msr->txcfg->hash_method->nelts == 0)
return 1;
for (i = 0; i < msr->txcfg->hash_method->nelts; i++) {
if(em[i] != NULL && em[i]->param_data != NULL){
switch(type) {
case HASH_URL_HREF_HASH_PM:
if(em[i]->type == HASH_URL_HREF_HASH_PM) {
const char *match = NULL;
apr_status_t rc = 0;
ACMPT pt;
pt.parser = (ACMP *)em[i]->param_data;
pt.ptr = NULL;
rc = acmp_process_quick(&pt, &match, link, strlen(link));
if (rc) {
return 1;
} else {
return 0;
}
}
break;
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;
s->name = apr_pstrdup(msr->mp, "MSC_PCRE_LIMITS_EXCEEDED");
if (s->name == NULL) return -1;
s->name_len = strlen(s->name);
s->value = apr_pstrdup(msr->mp, "1");
if (s->value == NULL) return -1;
s->value_len = 1;
apr_table_setn(msr->tx_vars, s->name, (void *)s);
error_msg = apr_psprintf(msr->mp,
"Execution error - "
"PCRE limits exceeded for Hash regex [%s] (%d): %s",
em[i]->param,rc, my_error_msg);
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4, "%s.", error_msg);
return 0; /* No match. */
}
else if (rc < -1) {
error_msg = apr_psprintf(msr->mp, "Regex execution failed (%d): %s",
rc, my_error_msg);
if (msr->txcfg->debuglog_level >= 4)
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;
}
}
break;
case HASH_URL_FACTION_HASH_PM:
if(em[i]->type == HASH_URL_FACTION_HASH_PM) {
const char *match = NULL;
apr_status_t rc = 0;
ACMPT pt;
pt.parser = (ACMP *)em[i]->param_data;
pt.ptr = NULL;
rc = acmp_process_quick(&pt, &match, link, strlen(link));
if (rc) {
return 1;
} else {
return 0;
}
}
break;
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;
s->name = apr_pstrdup(msr->mp, "MSC_PCRE_LIMITS_EXCEEDED");
if (s->name == NULL) return -1;
s->name_len = strlen(s->name);
s->value = apr_pstrdup(msr->mp, "1");
if (s->value == NULL) return -1;
s->value_len = 1;
apr_table_setn(msr->tx_vars, s->name, (void *)s);
error_msg = apr_psprintf(msr->mp,
"Execution error - "
"PCRE limits exceeded for Hash regex [%s] (%d): %s",
em[i]->param,rc, my_error_msg);
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4, "%s.", error_msg);
return 0; /* No match. */
}
else if (rc < -1) {
error_msg = apr_psprintf(msr->mp, "Regex execution failed (%d): %s",
rc, my_error_msg);
if (msr->txcfg->debuglog_level >= 4)
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;
}
}
break;
case HASH_URL_LOCATION_HASH_PM:
if(em[i]->type == HASH_URL_LOCATION_HASH_PM) {
const char *match = NULL;
apr_status_t rc = 0;
ACMPT pt;
pt.parser = (ACMP *)em[i]->param_data;
pt.ptr = NULL;
rc = acmp_process_quick(&pt, &match, link, strlen(link));
if (rc) {
return 1;
} else {
return 0;
}
}
break;
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;
s->name = apr_pstrdup(msr->mp, "MSC_PCRE_LIMITS_EXCEEDED");
if (s->name == NULL) return -1;
s->name_len = strlen(s->name);
s->value = apr_pstrdup(msr->mp, "1");
if (s->value == NULL) return -1;
s->value_len = 1;
apr_table_setn(msr->tx_vars, s->name, (void *)s);
error_msg = apr_psprintf(msr->mp,
"Execution error - "
"PCRE limits exceeded for Hash regex [%s] (%d): %s",
em[i]->param,rc, my_error_msg);
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4, "%s.", error_msg);
return 0; /* No match. */
}
else if (rc < -1) {
error_msg = apr_psprintf(msr->mp, "Regex execution failed (%d): %s",
rc, my_error_msg);
if (msr->txcfg->debuglog_level >= 4)
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;
}
}
break;
case HASH_URL_IFRAMESRC_HASH_PM:
if(em[i]->type == HASH_URL_IFRAMESRC_HASH_PM) {
const char *match = NULL;
apr_status_t rc = 0;
ACMPT pt;
pt.parser = (ACMP *)em[i]->param_data;
pt.ptr = NULL;
rc = acmp_process_quick(&pt, &match, link, strlen(link));
if (rc) {
return 1;
} else {
return 0;
}
}
break;
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;
s->name = apr_pstrdup(msr->mp, "MSC_PCRE_LIMITS_EXCEEDED");
if (s->name == NULL) return -1;
s->name_len = strlen(s->name);
s->value = apr_pstrdup(msr->mp, "1");
if (s->value == NULL) return -1;
s->value_len = 1;
apr_table_setn(msr->tx_vars, s->name, (void *)s);
error_msg = apr_psprintf(msr->mp,
"Execution error - "
"PCRE limits exceeded for Hash regex [%s] (%d): %s",
em[i]->param,rc, my_error_msg);
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4, "%s.", error_msg);
return 0; /* No match. */
}
else if (rc < -1) {
error_msg = apr_psprintf(msr->mp, "Regex execution failed (%d): %s",
rc, my_error_msg);
if (msr->txcfg->debuglog_level >= 4)
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;
}
}
break;
case HASH_URL_FRAMESRC_HASH_PM:
if(em[i]->type == HASH_URL_FRAMESRC_HASH_PM) {
const char *match = NULL;
apr_status_t rc = 0;
ACMPT pt;
pt.parser = (ACMP *)em[i]->param_data;
pt.ptr = NULL;
rc = acmp_process_quick(&pt, &match, link, strlen(link));
if (rc) {
return 1;
} else {
return 0;
}
}
break;
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;
s->name = apr_pstrdup(msr->mp, "MSC_PCRE_LIMITS_EXCEEDED");
if (s->name == NULL) return -1;
s->name_len = strlen(s->name);
s->value = apr_pstrdup(msr->mp, "1");
if (s->value == NULL) return -1;
s->value_len = 1;
apr_table_setn(msr->tx_vars, s->name, (void *)s);
error_msg = apr_psprintf(msr->mp,
"Execution error - "
"PCRE limits exceeded for Hash regex [%s] (%d): %s",
em[i]->param,rc, my_error_msg);
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4, "%s.", error_msg);
return 0; /* No match. */
}
else if (rc < -1) {
error_msg = apr_psprintf(msr->mp, "Regex execution failed (%d): %s",
rc, my_error_msg);
if (msr->txcfg->debuglog_level >= 4)
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;
}
}
break;
}
}
}
return 0;
}
/**
* \brief Hash the html elements
*
* \param msr ModSecurity transaction resource
*
* \retval 1 On success
* \retval 0 No element was changed
* \retval -1 On fail
*/
int hash_response_body_links(modsec_rec *msr) {
int lsize = 0, fsize = 0, lcount = 0, fcount = 0, i;
int isize = 0, icount = 0, frsize = 0, frcount = 0;
int bytes = 0;
xmlXPathContextPtr xpathCtx = NULL;
xmlXPathObjectPtr xpathObj = NULL;
xmlChar *content_option = NULL;
char *mac_link = NULL;
int rc, elts = 0;
if(msr == NULL)
return -1;
if (msr->crypto_html_tree == NULL) {
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4, "hash_response_body_links: Cannot parse NULL html tree");
return -1;
}
if(msr->txcfg->crypto_hash_href_rx == 0 && msr->txcfg->crypto_hash_href_pm == 0
&& msr->txcfg->crypto_hash_faction_rx == 0 && msr->txcfg->crypto_hash_faction_pm == 0
&& msr->txcfg->crypto_hash_iframesrc_rx == 0 && msr->txcfg->crypto_hash_iframesrc_pm == 0
&& msr->txcfg->crypto_hash_framesrc_rx == 0 && msr->txcfg->crypto_hash_framesrc_pm == 0)
return -1;
xpathCtx = xmlXPathNewContext(msr->crypto_html_tree);
if(xpathCtx == NULL) {
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4, "hash_response_body_links: Unable to create Xpath context.");
goto ctx_error;
}
lcount=fcount=0;
if(msr->txcfg->crypto_hash_href_rx == 1 || msr->txcfg->crypto_hash_href_pm == 1) {
xpathObj = xmlXPathEvalExpression((xmlChar*)"//*[@href]", xpathCtx);
if(xpathObj == NULL) {
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4,
"hash_response_body_links: Unable to evaluate xpath expression.");
goto obj_error;
}
lsize = (xpathObj->nodesetval) ? xpathObj->nodesetval->nodeNr : 0;
for(i = lsize - 1; i >=0; i--) {
register xmlNodePtr cur;
cur = xpathObj->nodesetval->nodeTab[i];
if(cur != NULL){
xmlChar *href = xmlGetProp(cur, (const xmlChar *) "href");
char *content_href = normalize_path(msr, (char *)href);
if(content_href != NULL && strstr(content_href,msr->txcfg->crypto_param_name) == NULL) {
if(msr->txcfg->crypto_hash_href_rx == 1) {
rc = do_hash_method(msr, (char *)content_href, HASH_URL_HREF_HASH_RX);
if(rc > 0) {
mac_link = NULL;
mac_link = do_hash_link(msr, (char *)content_href, FULL_LINK);
if(mac_link != NULL) {
xmlSetProp(cur, (const xmlChar *) "href", (const xmlChar *) mac_link);
lcount++;
bytes += strlen(mac_link);
msr->of_stream_changed = 1;
}
mac_link = NULL;
if(href != NULL)
xmlFree(href);
continue;
}
}
if(msr->txcfg->crypto_hash_href_pm == 1) {
rc = do_hash_method(msr, (char *)content_href, HASH_URL_HREF_HASH_PM);
if(rc > 0) {
mac_link = NULL;
mac_link = do_hash_link(msr, (char *)content_href, FULL_LINK);
if(mac_link != NULL) {
xmlSetProp(cur, (const xmlChar *) "href", (const xmlChar *) mac_link);
lcount++;
bytes += strlen(mac_link);
msr->of_stream_changed = 1;
}
mac_link = NULL;
if(href != NULL)
xmlFree(href);
continue;
}
}
}
if(href != NULL) {
xmlFree(href);
href = NULL;
}
}
}
if(xpathObj != NULL)
xmlXPathFreeObject(xpathObj);
}
if(msr->txcfg->crypto_hash_faction_rx == 1 || msr->txcfg->crypto_hash_faction_pm == 1) {
xpathObj = xmlXPathEvalExpression((xmlChar*)"//form", xpathCtx);
if(xpathObj == NULL) {
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4,
"hash_response_body_links: Unable to evaluate xpath expression.");
goto obj_error;
}
fsize = (xpathObj->nodesetval) ? xpathObj->nodesetval->nodeNr : 0;
for(i = fsize - 1; i >=0; i--) {
register xmlNodePtr cur;
cur = xpathObj->nodesetval->nodeTab[i];
if((cur != NULL)){
xmlChar *action = NULL;
char *content_action = NULL;
if(content_option)
xmlFree(content_option);
action = xmlGetProp(cur, (const xmlChar *) "action");
content_action = normalize_path(msr, (char *)action);
content_option = xmlGetProp(cur, (const xmlChar *) "option");
if(content_action != NULL && content_option == NULL && strstr(content_action,msr->txcfg->crypto_param_name) == NULL) {
if(msr->txcfg->crypto_hash_faction_rx == 1) {
rc = do_hash_method(msr, (char *)content_action, HASH_URL_FACTION_HASH_RX);
if(rc > 0) {
mac_link = NULL;
mac_link = do_hash_link(msr, (char *)content_action, FULL_LINK);
if(mac_link != NULL) {
xmlSetProp(cur, (const xmlChar *) "action", (const xmlChar *) mac_link);
fcount++;
bytes += strlen(mac_link);
msr->of_stream_changed = 1;
}
mac_link = NULL;
if(action != NULL)
xmlFree(action);
continue;
}
}
if(msr->txcfg->crypto_hash_faction_pm == 1) {
rc = do_hash_method(msr, (char *)content_action, HASH_URL_FACTION_HASH_PM);
if(rc > 0) {
mac_link = NULL;
mac_link = do_hash_link(msr, (char *)content_action, FULL_LINK);
if(mac_link != NULL) {
xmlSetProp(cur, (const xmlChar *) "action", (const xmlChar *) mac_link);
fcount++;
bytes += strlen(mac_link);
msr->of_stream_changed = 1;
}
mac_link = NULL;
if(action != NULL)
xmlFree(action);
continue;
}
}
}
if(action != NULL) {
xmlFree(action);
action = NULL;
}
if(content_option) {
xmlFree(content_option);
content_option = NULL;
}
}
}
if(xpathObj != NULL)
xmlXPathFreeObject(xpathObj);
}
if(msr->txcfg->crypto_hash_iframesrc_rx == 1 || msr->txcfg->crypto_hash_iframesrc_pm == 1) {
xpathObj = xmlXPathEvalExpression((xmlChar*)"//iframe", xpathCtx);
if(xpathObj == NULL) {
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4,
"hash_response_body_links: Unable to evaluate xpath expression.");
goto obj_error;
}
isize = (xpathObj->nodesetval) ? xpathObj->nodesetval->nodeNr : 0;
for(i = isize - 1; i >=0; i--) {
register xmlNodePtr cur;
cur = xpathObj->nodesetval->nodeTab[i];
if((cur != NULL)){
xmlChar *src = xmlGetProp(cur, (const xmlChar *) "src");
char *content_src = normalize_path(msr, (char *)src);
if(content_src != NULL && strstr(content_src,msr->txcfg->crypto_param_name) == NULL) {
if(msr->txcfg->crypto_hash_iframesrc_rx == 1) {
rc = do_hash_method(msr, (char *)content_src, HASH_URL_IFRAMESRC_HASH_RX);
if(rc > 0) {
mac_link = NULL;
mac_link = do_hash_link(msr, (char *)content_src, FULL_LINK);
if(mac_link != NULL) {
xmlSetProp(cur, (const xmlChar *) "src", (const xmlChar *) mac_link);
icount++;
bytes += strlen(mac_link);
msr->of_stream_changed = 1;
}
mac_link = NULL;
if(src != NULL)
xmlFree(src);
continue;
}
}
if(msr->txcfg->crypto_hash_iframesrc_pm == 1) {
rc = do_hash_method(msr, (char *)content_src, HASH_URL_IFRAMESRC_HASH_PM);
if(rc > 0) {
mac_link = NULL;
mac_link = do_hash_link(msr, (char *)content_src, FULL_LINK);
if(mac_link != NULL) {
xmlSetProp(cur, (const xmlChar *) "src", (const xmlChar *) mac_link);
icount++;
bytes += strlen(mac_link);
msr->of_stream_changed = 1;
}
mac_link = NULL;
if(src != NULL)
xmlFree(src);
continue;
}
}
}
if(src != NULL) {
xmlFree(src);
src = NULL;
}
}
}
if(xpathObj != NULL)
xmlXPathFreeObject(xpathObj);
}
if(msr->txcfg->crypto_hash_framesrc_rx == 1 || msr->txcfg->crypto_hash_framesrc_pm == 1) {
xpathObj = xmlXPathEvalExpression((xmlChar*)"//frame", xpathCtx);
if(xpathObj == NULL) {
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4,
"hash_response_body_links: Unable to evaluate xpath expression.");
goto obj_error;
}
frsize = (xpathObj->nodesetval) ? xpathObj->nodesetval->nodeNr : 0;
for(i = frsize - 1; i >=0; i--) {
register xmlNodePtr cur;
cur = xpathObj->nodesetval->nodeTab[i];
if((cur != NULL)){
xmlChar *src = xmlGetProp(cur, (const xmlChar *) "src");
char *content_src = normalize_path(msr, (char *)src);
if(content_src != NULL && strstr(content_src,msr->txcfg->crypto_param_name) == NULL) {
if(msr->txcfg->crypto_hash_framesrc_rx == 1) {
rc = do_hash_method(msr, (char *)content_src, HASH_URL_FRAMESRC_HASH_RX);
if(rc > 0) {
mac_link = NULL;
mac_link = do_hash_link(msr, (char *)content_src, FULL_LINK);
if(mac_link != NULL) {
xmlSetProp(cur, (const xmlChar *) "src", (const xmlChar *) mac_link);
frcount++;
bytes += strlen(mac_link);
msr->of_stream_changed = 1;
}
mac_link = NULL;
if(src != NULL)
xmlFree(src);
continue;
}
}
if(msr->txcfg->crypto_hash_framesrc_pm == 1) {
rc = do_hash_method(msr, (char *)content_src, HASH_URL_FRAMESRC_HASH_PM);
if(rc > 0) {
mac_link = NULL;
mac_link = do_hash_link(msr, (char *)content_src, FULL_LINK);
if(mac_link != NULL) {
xmlSetProp(cur, (const xmlChar *) "src", (const xmlChar *) mac_link);
frcount++;
bytes += strlen(mac_link);
msr->of_stream_changed = 1;
}
mac_link = NULL;
if(src != NULL)
xmlFree(src);
continue;
}
}
}
if(src != NULL) {
xmlFree(src);
src = NULL;
}
}
}
if(xpathObj != NULL)
xmlXPathFreeObject(xpathObj);
}
if(xpathCtx != NULL)
xmlXPathFreeContext(xpathCtx);
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "hash_response_body_links: Processed [%d] iframe src, [%d] hashed.",isize, icount);
msr_log(msr, 4, "hash_response_body_links: Processed [%d] frame src, [%d] hashed.",frsize, frcount);
msr_log(msr, 4, "hash_response_body_links: Processed [%d] form actions, [%d] hashed.",fsize, fcount);
msr_log(msr, 4, "hash_response_body_links: Processed [%d] links, [%d] hashed.",lsize, lcount);
}
if(msr->of_stream_changed == 0) {
if(msr->crypto_html_tree != NULL) {
xmlFreeDoc(msr->crypto_html_tree);
msr->crypto_html_tree = NULL;
}
return 0;
}
elts = (icount+frcount+fcount+lcount);
if((elts >= INT32_MAX) || (elts < 0))
return 0;
return bytes;
obj_error:
if(xpathCtx != NULL)
xmlXPathFreeContext(xpathCtx);
ctx_error:
return -1;
}
/**
* \brief Inject the new response body
*
* \param msr ModSecurity transaction resource
* \param elts Number of hashed elements
*
* \retval 1 On success
* \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;
const char *ctype = NULL;
const char *encoding = NULL;
char *new_ct = NULL, *content_value = NULL;
if(msr == NULL) return -1;
if(msr->r == NULL) return -1;
if (msr->crypto_html_tree == NULL) {
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4, "inject_hashed_response_body: Cannot parse NULL html tree");
return -1;
}
if(msr->r->content_type != NULL)
ctype = msr->r->content_type;
encoding = (const char *) htmlGetMetaEncoding(msr->crypto_html_tree);
if (ctype && encoding == NULL) {
if (ctype && (p = m_strcasestr(ctype, "charset=") , p != NULL)) {
p += 8 ;
encoding = apr_pstrndup(msr->mp, p, strcspn(p, " ;"));
handler = xmlFindCharEncodingHandler(encoding);
}
} else {
if(encoding != NULL) {
handler = xmlFindCharEncodingHandler(encoding);
}
}
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4, "inject_hashed_response_body: Detected encoding type [%s].",
encoding ? encoding : "(none)");
if (handler == NULL) {
handler = xmlFindCharEncodingHandler("UTF-8");
encoding = apr_pstrdup(msr->mp, "UTF-8");
}
if (handler == NULL) {
handler = xmlFindCharEncodingHandler("ISO-8859-1");
encoding = apr_pstrdup(msr->mp, "ISO-8859-1");
}
if (handler == NULL) {
handler = xmlFindCharEncodingHandler("HTML");
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);
return -1;
}
apr_table_unset(msr->r->headers_out,"Content-Type");
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].", encoding);
output_buf = xmlAllocOutputBuffer(handler);
if (output_buf == NULL) {
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4, "inject_hashed_response_body: Unable to allocate memory buffer.");
xmlFreeDoc(msr->crypto_html_tree);
return -1;
}
htmlDocContentDumpFormatOutput(output_buf, msr->crypto_html_tree, NULL, 0);
// Not necessary in 2.9.4+
//xmlOutputBufferFlush(output_buf);
#ifdef LIBXML2_NEW_BUFFER
if (output_buf->conv == NULL || (output_buf->conv && xmlOutputBufferGetSize(output_buf) == 0)) {
if(output_buf->buffer == NULL || xmlOutputBufferGetSize(output_buf) == 0) {
xmlOutputBufferClose(output_buf);
xmlFreeDoc(msr->crypto_html_tree);
msr->of_stream_changed = 0;
msr_log(msr, 4, "inject_hashed_response_body: NEW_BUFFER Output buffer is null.");
return -1;
}
if(msr->stream_output_data != NULL) {
free(msr->stream_output_data);
msr->stream_output_data = NULL;
}
msr->stream_output_length = xmlOutputBufferGetSize(output_buf);
msr->stream_output_data = (char *)malloc(msr->stream_output_length+1);
if (msr->stream_output_data == NULL) {
xmlOutputBufferClose(output_buf);
xmlFreeDoc(msr->crypto_html_tree);
msr_log(msr, 4, "inject_hashed_response_body: NEW BUFFER Stream Output is null.");
return -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));
} else {
if(output_buf->conv == NULL || xmlOutputBufferGetSize(output_buf) == 0) {
xmlOutputBufferClose(output_buf);
xmlFreeDoc(msr->crypto_html_tree);
msr->of_stream_changed = 0;
msr_log(msr, 4, "inject_hashed_response_body: Conv is null.");
return -1;
}
if(msr->stream_output_data != NULL) {
free(msr->stream_output_data);
msr->stream_output_data = NULL;
}
msr->stream_output_length = xmlOutputBufferGetSize(output_buf);
msr->stream_output_data = (char *)malloc(msr->stream_output_length+1);
if (msr->stream_output_data == NULL) {
msr_log(msr, 4, "inject_hashed_response_body: Stream Output data is NULL.");
xmlOutputBufferClose(output_buf);
xmlFreeDoc(msr->crypto_html_tree);
return -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));
}
#else
if (output_buf->conv == NULL || (output_buf->conv && output_buf->conv->use == 0)) {
if(output_buf->buffer == NULL || output_buf->buffer->use == 0) {
xmlOutputBufferClose(output_buf);
xmlFreeDoc(msr->crypto_html_tree);
msr_log(msr, 4, "inject_hashed_response_body: Output buffer is null.");
msr->of_stream_changed = 0;
return -1;
}
if(msr->stream_output_data != NULL) {
free(msr->stream_output_data);
msr->stream_output_data = NULL;
}
msr->stream_output_length = output_buf->buffer->use;
msr->stream_output_data = (char *)malloc(msr->stream_output_length+1);
if (msr->stream_output_data == NULL) {
xmlOutputBufferClose(output_buf);
xmlFreeDoc(msr->crypto_html_tree);
msr_log(msr, 4, "inject_hashed_response_body: Stream Output is null.");
return -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);
} else {
if(output_buf->conv == NULL || output_buf->conv->use == 0) {
xmlOutputBufferClose(output_buf);
xmlFreeDoc(msr->crypto_html_tree);
msr->of_stream_changed = 0;
msr_log(msr, 4, "inject_hashed_response_body: Stream Output is null.");
return -1;
}
if(msr->stream_output_data != NULL) {
free(msr->stream_output_data);
msr->stream_output_data = NULL;
}
msr->stream_output_length = output_buf->conv->use;
msr->stream_output_data = (char *)malloc(msr->stream_output_length+1);
if (msr->stream_output_data == NULL) {
xmlOutputBufferClose(output_buf);
xmlFreeDoc(msr->crypto_html_tree);
msr_log(msr, 4, "inject_hashed_response_body: Stream Output Data is null.");
return -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);
}
#endif
xmlOutputBufferClose(output_buf);
content_value = (char*)apr_psprintf(msr->mp, "%"APR_SIZE_T_FMT, msr->stream_output_length);
apr_table_unset(msr->r->headers_out,"Content-Length");
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4, "inject_hashed_response_body: Setting new content value %s", content_value);
apr_table_set(msr->r->headers_out, "Content-Length", content_value);
xmlFreeDoc(msr->crypto_html_tree);
if (msr->txcfg->debuglog_level >= 4)
msr_log(msr, 4, "inject_hashed_response_body: Stream buffer [%"APR_SIZE_T_FMT"]. Done",msr->stream_output_length);
return 1;
}
/**
* \brief Parse and MAC html elements
*
* \param msr ModSecurity transaction resource
* \param link The html attr value to be checked
* \param type The hash method type
*
* \retval mac_link MACed link
* \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(strlen(link) > 7 && strncmp("http:",(char*)link,5)==0){
path_chunk = strchr(link+7,'/');
if(path_chunk != NULL) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Signing data [%s]", path_chunk+1);
}
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
const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->client_ip);
#else
const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->remote_ip);
#endif
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
}
}
if(msr->txcfg->crypto_key_add == HASH_REMOTEIP) {
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->client_ip);
#else
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;
}
} else
if(strlen(link) > 8 && strncmp("https",(char*)link,5)==0){
path_chunk = strchr(link+8,'/');
if(path_chunk != NULL) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Signing data [%s]", path_chunk+1);
}
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
const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->client_ip);
#else
const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->remote_ip);
#endif
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
}
}
if(msr->txcfg->crypto_key_add == HASH_REMOTEIP) {
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->client_ip);
#else
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;
}
}
else if(*link=='/'){
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Signing data [%s]", link+1);
}
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
const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->client_ip);
#else
const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->remote_ip);
#endif
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
}
}
if(msr->txcfg->crypto_key_add == HASH_REMOTEIP) {
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->client_ip);
#else
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
}
}
else {
char *relative_link = NULL;
char *filename = NULL;
char *relative_path = NULL;
char *relative_uri = NULL;
filename = file_basename(msr->mp, msr->r->parsed_uri.path);
if(filename == NULL || (strlen(msr->r->parsed_uri.path) - strlen(filename) < 0))
return NULL;
relative_path = apr_pstrndup(msr->mp, msr->r->parsed_uri.path, strlen(msr->r->parsed_uri.path) - strlen(filename));
relative_uri = apr_pstrcat(msr->mp, relative_path, link, NULL);
relative_link = relative_uri+1;
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Signing data [%s] size %zu", relative_link, strlen(relative_link));
}
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) {
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->client_ip);
#else
const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->remote_ip);
#endif
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
}
}
if(msr->txcfg->crypto_key_add == HASH_REMOTEIP) {
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
const char *new_pwd = apr_psprintf(msr->mp,"%s%s", msr->txcfg->crypto_key, msr->r->connection->client_ip);
#else
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;
}
if(hash_value == NULL) return NULL;
if(type == HASH_ONLY)
return hash_value;
qm = strchr((char*)link,'?');
if(qm == NULL){
mac_link= (char*)apr_psprintf(msr->mp, "%s?%s=%s", link, msr->txcfg->crypto_param_name, (char *)hash_value);
}
else{
mac_link= (char*)apr_psprintf(msr->mp, "%s&%s=%s", link, msr->txcfg->crypto_param_name, (char*)hash_value);
}
return mac_link;
}
/**
* \brief Modify Location in case of status 302 and 301
*
* \param msr ModSecurity transaction resource
*
* \retval 1 On Success
* \retval 0 on fail
*/
int modify_response_header(modsec_rec *msr) {
char *mac_link = NULL;
const char *location = NULL;
int rc = 0;
if(msr == NULL) return 0;
if (msr->txcfg->debuglog_level >= 9)
msr_log(msr, 4, "HTTP status (%d)", msr->response_status);
if(msr->response_status != HTTP_MOVED_TEMPORARILY &&
msr->response_status != HTTP_MOVED_PERMANENTLY) {
if (msr->txcfg->debuglog_level >= 9)
msr_log(msr, 4, "Skipping status other than 302 an 301");
return 0;
}
location = apr_table_get(msr->r->headers_out, "Location");
if(location == NULL || strlen(location) == 0)
return 0;
if (msr->txcfg->debuglog_level >= 9)
msr_log(msr, 4, "Processing reponse header location [%s]", location);
if(msr->txcfg->crypto_hash_location_rx == 1) {
rc = do_hash_method(msr, (char *)location, HASH_URL_LOCATION_HASH_RX);
if(rc > 0) {
mac_link = NULL;
mac_link = do_hash_link(msr, (char *)location, FULL_LINK);
} else {
return 0;
}
} else if(msr->txcfg->crypto_hash_location_pm == 1) {
rc = do_hash_method(msr, (char *)location, HASH_URL_LOCATION_HASH_PM);
if(rc > 0) {
mac_link = NULL;
mac_link = do_hash_link(msr, (char *)location, FULL_LINK);
} else {
return 0;
}
}
if(mac_link == NULL)
return 0;
if (msr->txcfg->debuglog_level >= 9)
msr_log(msr, 4, "Setting new reponse header location [%s]", mac_link);
if(rc > 0) {
apr_table_unset(msr->r->headers_out,"Location");
apr_table_set(msr->r->headers_out, "Location",(char*)apr_psprintf(msr->mp,"%s", mac_link));
}
return 1;
}
modsecurity-apache-2.9.13/apache2/msc_crypt.h 0000664 0000000 0000000 00000003035 15174655764 0021100 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#ifndef _MSC_CRYPT_H_
#define _MSC_CRYPT_H_
#include "modsecurity.h"
#include
#include
#define HMAC_PAD_SIZE 65
#define HASH_ONLY 0
#define FULL_LINK 1
#ifndef INT32_MAX
#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);
int DSOLOCAL init_response_body_html_parser(modsec_rec *msr);
int DSOLOCAL hash_response_body_links(modsec_rec *msr);
int DSOLOCAL inject_hashed_response_body(modsec_rec *msr, int elts);
int DSOLOCAL do_hash_method(modsec_rec *msr, char *link, int type);
int DSOLOCAL modify_response_header(modsec_rec *msr);
char DSOLOCAL *normalize_path(modsec_rec *msr, char *input);
#endif
modsecurity-apache-2.9.13/apache2/msc_geo.c 0000664 0000000 0000000 00000051265 15174655764 0020514 0 ustar 00root root 0000000 0000000 /*
* 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 "msc_geo.h"
/* -- Lookup Tables -- */
static const char geo_country_code[GEO_COUNTRY_LAST + 1][4] = {
"--",
"AP","EU","AD","AE","AF","AG","AI","AL","AM","AN",
"AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB",
"BD","BE","BF","BG","BH","BI","BJ","BM","BN","BO",
"BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD",
"CF","CG","CH","CI","CK","CL","CM","CN","CO","CR",
"CU","CV","CX","CY","CZ","DE","DJ","DK","DM","DO",
"DZ","EC","EE","EG","EH","ER","ES","ET","FI","FJ",
"FK","FM","FO","FR","FX","GA","GB","GD","GE","GF",
"GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT",
"GU","GW","GY","HK","HM","HN","HR","HT","HU","ID",
"IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO",
"JP","KE","KG","KH","KI","KM","KN","KP","KR","KW",
"KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT",
"LU","LV","LY","MA","MC","MD","MG","MH","MK","ML",
"MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV",
"MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI",
"NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF",
"PG","PH","PK","PL","PM","PN","PR","PS","PT","PW",
"PY","QA","RE","RO","RU","RW","SA","SB","SC","SD",
"SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO",
"SR","ST","SV","SY","SZ","TC","TD","TF","TG","TH",
"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",
"BL","BQ","CW","MF","SS","SX"
};
static const char geo_country_code3[GEO_COUNTRY_LAST + 1][4] = {
"--",
"AP","EU","AND","ARE","AFG","ATG","AIA","ALB","ARM","ANT",
"AGO","AQ","ARG","ASM","AUT","AUS","ABW","AZE","BIH","BRB",
"BGD","BEL","BFA","BGR","BHR","BDI","BEN","BMU","BRN","BOL",
"BRA","BHS","BTN","BV","BWA","BLR","BLZ","CAN","CC","COD",
"CAF","COG","CHE","CIV","COK","CHL","CMR","CHN","COL","CRI",
"CUB","CPV","CX","CYP","CZE","DEU","DJI","DNK","DMA","DOM",
"DZA","ECU","EST","EGY","ESH","ERI","ESP","ETH","FIN","FJI",
"FLK","FSM","FRO","FRA","FX","GAB","GBR","GRD","GEO","GUF",
"GHA","GIB","GRL","GMB","GIN","GLP","GNQ","GRC","GS","GTM",
"GUM","GNB","GUY","HKG","HM","HND","HRV","HTI","HUN","IDN",
"IRL","ISR","IND","IO","IRQ","IRN","ISL","ITA","JAM","JOR",
"JPN","KEN","KGZ","KHM","KIR","COM","KNA","PRK","KOR","KWT",
"CYM","KAZ","LAO","LBN","LCA","LIE","LKA","LBR","LSO","LTU",
"LUX","LVA","LBY","MAR","MCO","MDA","MDG","MHL","MKD","MLI",
"MMR","MNG","MAC","MNP","MTQ","MRT","MSR","MLT","MUS","MDV",
"MWI","MEX","MYS","MOZ","NAM","NCL","NER","NFK","NGA","NIC",
"NLD","NOR","NPL","NRU","NIU","NZL","OMN","PAN","PER","PYF",
"PNG","PHL","PAK","POL","SPM","PCN","PRI","PSE","PRT","PLW",
"PRY","QAT","REU","ROU","RUS","RWA","SAU","SLB","SYC","SDN",
"SWE","SGP","SHN","SVN","SJM","SVK","SLE","SMR","SEN","SOM",
"SUR","STP","SLV","SYR","SWZ","TCA","TCD","TF","TGO","THA",
"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",
"BLM","BES","CUW","MAF","SSD","SXM"
};
static const char *const geo_country_name[GEO_COUNTRY_LAST + 1] = {
"N/A",
"Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles",
"Angola","Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina","Barbados",
"Bangladesh","Belgium","Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia",
"Brazil","Bahamas","Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos (Keeling) Islands","Congo, The Democratic Republic of the",
"Central African Republic","Congo","Switzerland","Cote D'Ivoire","Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica",
"Cuba","Cape Verde","Christmas Island","Cyprus","Czechia","Germany","Djibouti","Denmark","Dominica","Dominican Republic",
"Algeria","Ecuador","Estonia","Egypt","Western Sahara","Eritrea","Spain","Ethiopia","Finland","Fiji",
"Falkland Islands (Malvinas)","Micronesia, Federated States of","Faroe Islands","France","France, Metropolitan","Gabon","United Kingdom","Grenada","Georgia","French Guiana",
"Ghana","Gibraltar","Greenland","Gambia","Guinea","Guadeloupe","Equatorial Guinea","Greece","South Georgia and the South Sandwich Islands","Guatemala",
"Guam","Guinea-Bissau","Guyana","Hong Kong","Heard Island and McDonald Islands","Honduras","Croatia","Haiti","Hungary","Indonesia",
"Ireland","Israel","India","British Indian Ocean Territory","Iraq","Iran, Islamic Republic of","Iceland","Italy","Jamaica","Jordan",
"Japan","Kenya","Kyrgyzstan","Cambodia","Kiribati","Comoros","Saint Kitts and Nevis","Korea, Democratic People's Republic of","Korea, Republic of","Kuwait",
"Cayman Islands","Kazakhstan","Lao People's Democratic Republic","Lebanon","Saint Lucia","Liechtenstein","Sri Lanka","Liberia","Lesotho","Lithuania",
"Luxembourg","Latvia","Libyan Arab Jamahiriya","Morocco","Monaco","Moldova, Republic of","Madagascar","Marshall Islands","Macedonia","Mali",
"Myanmar","Mongolia","Macau","Northern Mariana Islands","Martinique","Mauritania","Montserrat","Malta","Mauritius","Maldives",
"Malawi","Mexico","Malaysia","Mozambique","Namibia","New Caledonia","Niger","Norfolk Island","Nigeria","Nicaragua",
"Netherlands","Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama","Peru","French Polynesia",
"Papua New Guinea","Philippines","Pakistan","Poland","Saint Pierre and Miquelon","Pitcairn Islands","Puerto Rico","Palestinian Territory","Portugal","Palau",
"Paraguay","Qatar","Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia","Solomon Islands","Seychelles","Sudan",
"Sweden","Singapore","Saint Helena","Slovenia","Svalbard and Jan Mayen","Slovakia","Sierra Leone","San Marino","Senegal","Somalia","Suriname",
"Sao Tome and Principe","El Salvador","Syrian Arab Republic","Swaziland","Turks and Caicos Islands","Chad","French Southern Territories","Togo","Thailand",
"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",
"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] = {
"--",
"AS","EU","EU","AS","AS","SA","SA","EU","AS","SA",
"AF","AN","SA","OC","EU","OC","SA","AS","EU","SA",
"AS","EU","AF","EU","AS","AF","AF","SA","AS","SA",
"SA","SA","AS","AF","AF","EU","SA","NA","AS","AF",
"AF","AF","EU","AF","OC","SA","AF","AS","SA","SA",
"SA","AF","AS","AS","EU","EU","AF","EU","SA","SA",
"AF","SA","EU","AF","AF","AF","EU","AF","EU","OC",
"SA","OC","EU","EU","EU","AF","EU","SA","AS","SA",
"AF","EU","SA","AF","AF","SA","AF","EU","SA","SA",
"OC","AF","SA","AS","AF","SA","EU","SA","EU","AS",
"EU","AS","AS","AS","AS","AS","EU","EU","SA","AS",
"AS","AF","AS","AS","OC","AF","SA","AS","AS","AS",
"SA","AS","AS","AS","SA","EU","AS","AF","AF","EU",
"EU","EU","AF","AF","EU","EU","AF","OC","EU","AF",
"AS","AS","AS","OC","SA","AF","SA","EU","AF","AS",
"AF","NA","AS","AF","AF","OC","AF","OC","AF","SA",
"EU","EU","AS","OC","OC","OC","AS","SA","SA","OC",
"OC","AS","AS","EU","SA","OC","SA","AS","EU","OC",
"SA","AS","AF","EU","AS","AF","AS","OC","AF","AF",
"EU","AS","AF","EU","EU","EU","AF","EU","AF","AF",
"SA","AF","SA","AS","AF","SA","AF","AF","AF","AS",
"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","--"
};
typedef enum {
GEOIP_COUNTRY_EDITION = 1,
GEOIP_REGION_EDITION_REV0 = 7,
GEOIP_CITY_EDITION_REV0 = 6,
GEOIP_ORG_EDITION = 5,
GEOIP_ISP_EDITION = 4,
GEOIP_CITY_EDITION_REV1 = 2,
GEOIP_REGION_EDITION_REV1 = 3,
GEOIP_PROXY_EDITION = 8,
GEOIP_ASNUM_EDITION = 9,
GEOIP_NETSPEED_EDITION = 10,
GEOIP_DOMAIN_EDITION = 11
} GeoIPDBTypes;
static void create_segments(geo_db *geo) {
int i, j;
unsigned char delim[3];
unsigned char buf[GEO_SEGMENT_RECORD_LENGTH];
apr_size_t nbytes;
apr_off_t offset;
geo->ctry_offset = 0;
geo->dbtype = GEOIP_COUNTRY_EDITION;
offset = -3l;
apr_file_seek(geo->db, APR_END, &offset);
for (i = 0; i < GEO_STRUCT_INFO_MAX_SIZE; i++) {
apr_file_read_full(geo->db, &delim, 3, &nbytes);
if (delim[0] == 255 && delim[1] == 255 && delim[2] == 255) {
apr_file_read_full(geo->db, &geo->dbtype, 1, &nbytes);
if (geo->dbtype >= 106) {
geo->dbtype -= 105;
}
if (geo->dbtype == GEOIP_REGION_EDITION_REV0) {
geo->ctry_offset = GEO_STATE_BEGIN_REV0;
} else if (geo->dbtype == GEOIP_REGION_EDITION_REV1) {
geo->ctry_offset = GEO_STATE_BEGIN_REV1;
} else if (geo->dbtype == GEOIP_CITY_EDITION_REV0 ||
geo->dbtype == GEOIP_CITY_EDITION_REV1 ||
geo->dbtype == GEOIP_ORG_EDITION ||
geo->dbtype == GEOIP_ISP_EDITION ||
geo->dbtype == GEOIP_ASNUM_EDITION) {
geo->ctry_offset = 0;
apr_file_read_full(geo->db, &buf, GEO_SEGMENT_RECORD_LENGTH, &nbytes);
for (j = 0; j < GEO_SEGMENT_RECORD_LENGTH; j++) {
geo->ctry_offset += (buf[j] << (j * 8));
}
}
break;
} else {
offset = -4l;
apr_file_seek(geo->db, APR_CUR, &offset);
}
}
if (geo->dbtype == GEOIP_COUNTRY_EDITION ||
geo->dbtype == GEOIP_PROXY_EDITION ||
geo->dbtype == GEOIP_NETSPEED_EDITION) {
geo->ctry_offset = GEO_COUNTRY_BEGIN;
}
}
static int db_open(directory_config *dcfg, char **error_msg)
{
char errstr[1024];
apr_pool_t *mp = dcfg->mp;
geo_db *geo = dcfg->geo;
apr_status_t rc;
#ifdef DEBUG_CONF
fprintf(stderr, "GEO: Initializing geo DB \"%s\".\n", geo->dbfn);
#endif
if ((rc = apr_file_open(&geo->db, geo->dbfn, APR_READ, APR_OS_DEFAULT, mp)) != APR_SUCCESS) {
*error_msg = apr_psprintf(mp, "Could not open geo database \"%s\": %s", geo->dbfn, apr_strerror(rc, errstr, 1024));
return 0;
}
create_segments(geo);
return 1;
}
static int field_length(const char *field, int maxlen)
{
int i;
if (field == NULL) {
return 0;
}
for (i = 0; i < maxlen; i++) {
if (field[i] == '\0') {
break;
}
}
return i;
}
/**
* Initialise Geo data structure
*/
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)) {
dcfg->geo = apr_pcalloc(dcfg->mp, sizeof(geo_db));
}
dcfg->geo->db = NULL;
dcfg->geo->dbfn = apr_pstrdup(dcfg->mp, dbfn);
dcfg->geo->dbtype = 0;
dcfg->geo->ctry_offset = 0;
return db_open(dcfg, error_msg);
}
/**
* Perform geographical lookup on target.
*/
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;
geo_db *geo = msr->txcfg->geo;
char errstr[1024];
unsigned char buf[2* GEO_MAX_RECORD_LEN];
const int reclen = 3; /* Algorithm needs changed if this changes */
apr_size_t nbytes;
unsigned int rec_val = 0;
apr_off_t seekto = 0;
int rc;
int country = 0;
int level;
double dtmp;
int itmp;
*error_msg = NULL;
/* init */
georec->country_code = geo_country_code[0];
georec->country_code3 = geo_country_code3[0];
georec->country_name = geo_country_name[0];
georec->country_continent = geo_country_continent[0];
georec->region = "";
georec->city = "";
georec->postal_code = "";
georec->latitude = 0;
georec->longitude = 0;
georec->dma_code = 0;
georec->area_code = 0;
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: Looking up \"%s\".", log_escape(msr->mp, target));
}
/* NOTE: This only works with ipv4 */
if ((rc = apr_sockaddr_info_get(&addr, target, APR_INET, 0, 0, msr->mp)) != APR_SUCCESS) {
*error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" failed: %s", log_escape(msr->mp, target), apr_strerror(rc, errstr, 1024));
msr_log(msr, 4, "%s", *error_msg);
return 0;
}
if ((rc = apr_sockaddr_ip_get(&targetip, addr)) != APR_SUCCESS) {
*error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" failed: %s", log_escape(msr->mp, target), apr_strerror(rc, errstr, 1024));
msr_log(msr, 4, "%s", *error_msg);
return 0;
};
/* Why is this in host byte order? */
ipnum = ntohl(addr->sa.sin.sin_addr.s_addr);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: Using address \"%s\" (0x%08lx). %lu", targetip, ipnum, ipnum);
}
msr_global_mutex_lock(msr, msr->modsecurity->geo_lock, "Geo lookup");
for (level = 31; level >= 0; level--) {
/* Read the record */
seekto = 2 * reclen * rec_val;
apr_file_seek(geo->db, APR_SET, &seekto);
/* TODO: check rc */
rc = apr_file_read_full(geo->db, &buf, (2 * reclen), &nbytes);
/* NOTE: This is hard-coded for size 3 records */
/* Left */
if ((ipnum & (1 << level)) == 0) {
rec_val = (buf[3*0 + 0] << (0*8)) +
(buf[3*0 + 1] << (1*8)) +
(buf[3*0 + 2] << (2*8));
}
/* Right */
else {
rec_val = (buf[3*1 + 0] << (0*8)) +
(buf[3*1 + 1] << (1*8)) +
(buf[3*1 + 2] << (2*8));
}
/* If we are past the country offset, then we are done */
if (rec_val >= geo->ctry_offset) {
break;
}
}
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);
msr_global_mutex_unlock(msr, msr->modsecurity->geo_lock, "Geo Lookup");
return 0;
}
if (geo->dbtype == GEO_COUNTRY_DATABASE) {
country = rec_val;
country -= geo->ctry_offset;
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);
msr_global_mutex_unlock(msr, msr->modsecurity->geo_lock, "Geo Lookup");
return 0;
}
/* Country */
georec->country_code = geo_country_code[country];
georec->country_code3 = geo_country_code3[country];
georec->country_name = geo_country_name[country];
georec->country_continent = geo_country_continent[country];
}
else {
int field_len = 0;
int rec_offset = 0;
int remaining = GEO_CITY_RECORD_LEN;
unsigned char cbuf[GEO_CITY_RECORD_LEN];
seekto = rec_val + (2 * reclen - 1) * geo->ctry_offset;
apr_file_seek(geo->db, APR_SET, &seekto);
/* TODO: check rc */
rc = apr_file_read_full(geo->db, &cbuf, sizeof(cbuf), &nbytes);
country = cbuf[0];
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);
msr_global_mutex_unlock(msr, msr->modsecurity->geo_lock, "Geo Lookup");
return 0;
}
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: rec=\"%s\"", log_escape_raw(msr->mp, cbuf, sizeof(cbuf)));
}
/* Country */
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: country=\"%.*s\"", (1*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf)));
}
georec->country_code = geo_country_code[country];
georec->country_code3 = geo_country_code3[country];
georec->country_name = geo_country_name[country];
georec->country_continent = geo_country_continent[country];
rec_offset++;
remaining -= rec_offset;
/* Region */
field_len = field_length((const char *)cbuf+rec_offset, remaining);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: region=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
}
georec->region = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining));
rec_offset += field_len + 1;
remaining -= field_len + 1;
/* City */
field_len = field_length((const char *)cbuf+rec_offset, remaining);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: city=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
}
georec->city = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining));
rec_offset += field_len + 1;
remaining -= field_len + 1;
/* Postal Code */
field_len = field_length((const char *)cbuf+rec_offset, remaining);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: postal_code=\"%.*s\"", ((field_len+1)*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
}
georec->postal_code = apr_pstrmemdup(msr->mp, (const char *)cbuf+rec_offset, (remaining));
rec_offset += field_len + 1;
remaining -= field_len + 1;
/* Latitude */
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: latitude=\"%.*s\"", (3*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
}
dtmp = cbuf[rec_offset] +
(cbuf[rec_offset+1] << 8) +
(cbuf[rec_offset+2] << 16);
georec->latitude = dtmp/10000 - 180;
rec_offset += 3;
remaining -= 3;
/* Longitude */
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: longitude=\"%.*s\"", (3*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
}
dtmp = cbuf[rec_offset] +
(cbuf[rec_offset+1] << 8) +
(cbuf[rec_offset+2] << 16);
georec->longitude = dtmp/10000 - 180;
rec_offset += 3;
remaining -= 3;
/* dma/area codes are in city rev1 and US only */
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "GEO: dma/area=\"%.*s\"", (3*4), log_escape_raw(msr->mp, cbuf, sizeof(cbuf))+(rec_offset*4));
}
if (geo->dbtype == GEO_CITY_DATABASE_1
&& georec->country_code[0] == 'U'
&& georec->country_code[1] == 'S')
{
/* DMA Code */
itmp = cbuf[rec_offset] +
(cbuf[rec_offset+1] << 8) +
(cbuf[rec_offset+2] << 16);
georec->dma_code = itmp / 1000;
georec->area_code = itmp % 1000;
rec_offset += 6;
remaining -= 6;
}
}
*error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" succeeded.", log_escape(msr->mp, target));
msr_global_mutex_unlock(msr, msr->modsecurity->geo_lock, "Geo Lookup");
return 1;
}
modsecurity-apache-2.9.13/apache2/msc_geo.h 0000664 0000000 0000000 00000003612 15174655764 0020512 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#ifndef _MSC_GEO_H_
#define _MSC_GEO_H_
#define GEO_STRUCT_INFO_MAX_SIZE 20
#define GEO_DB_INFO_MAX_SIZE 100
#define GEO_COUNTRY_OFFSET 0xffff00
#define GEO_MAX_RECORD_LEN 4
#define GEO_COUNTRY_UNKNOWN "Unknown"
#define GEO_CITY_UNKNOWN "Unknown"
#define GEO_CITY_RECORD_LEN 50
#define GEO_COUNTRY_DATABASE 1
#define GEO_CITY_DATABASE_0 6
#define GEO_CITY_DATABASE_1 2
#define GEO_COUNTRY_LAST 256
#define GEO_SEGMENT_RECORD_LENGTH 3
#define GEO_STATE_BEGIN_REV0 16700000
#define GEO_STATE_BEGIN_REV1 16000000
#define GEO_COUNTRY_BEGIN 16776960
typedef struct geo_rec geo_rec;
typedef struct geo_db geo_db;
#include
#include "modsecurity.h"
/* Structures */
struct geo_rec {
const char *country_code;
const char *country_code3;
const char *country_name;
const char *country_continent;
const char *region;
const char *city;
const char *postal_code;
float latitude;
float longitude;
int dma_code;
int area_code;
};
struct geo_db {
apr_file_t *db;
const char *dbfn;
int dbtype;
unsigned int ctry_offset;
};
/* Functions */
int DSOLOCAL geo_init(directory_config *dcfg, const char *dbfn, char **error_msg);
int DSOLOCAL geo_lookup(modsec_rec *msr, geo_rec *rec, const char *target, char **error_msg);
#endif
modsecurity-apache-2.9.13/apache2/msc_gsb.c 0000664 0000000 0000000 00000006435 15174655764 0020514 0 ustar 00root root 0000000 0000000 /*
* 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 "msc_gsb.h"
/** \brief Load GSB database
*
* \param dcfg Pointer to directory configuration
* \param error_msg Error message
*
* \retval 1 On Success
* \retval 0 On Fail
*/
static int gsb_db_create(directory_config *dcfg, char **error_msg)
{
char errstr[1024];
apr_pool_t *mp = dcfg->mp;
gsb_db *gsb = dcfg->gsb;
apr_int32_t wanted = APR_FINFO_SIZE;
apr_finfo_t finfo;
apr_status_t rc;
apr_size_t nbytes;
char *buf = NULL, *p = NULL, *savedptr = NULL;
char *op = NULL;
if ((rc = apr_file_open(&gsb->db, gsb->dbfn, APR_READ, APR_OS_DEFAULT, mp)) != APR_SUCCESS) {
*error_msg = apr_psprintf(mp, "Could not open gsb database \"%s\": %s", gsb->dbfn, apr_strerror(rc, errstr, 1024));
return 0;
}
if ((rc = apr_file_info_get(&finfo, wanted, gsb->db)) != APR_SUCCESS) {
*error_msg = apr_psprintf(mp, "Could not cannot get gsb malware file information \"%s\": %s", gsb->dbfn, apr_strerror(rc, errstr, 1024));
apr_file_close(gsb->db);
return 0;
}
buf = (char *)malloc(finfo.size+1);
if (buf == NULL) {
*error_msg = apr_psprintf(mp, "Could not alloc memory for gsb data");
apr_file_close(gsb->db);
return 0;
}
rc = apr_file_read_full(gsb->db, buf, finfo.size, &nbytes);
gsb->gsb_table = apr_hash_make(dcfg->mp);
if (gsb->gsb_table == NULL) {
*error_msg = apr_psprintf(mp, "Could not alloc memory for gsb table");
free(buf);
buf = NULL;
apr_file_close(gsb->db);
return 0;
}
p = apr_strtok(buf,"\t",&savedptr);
while (p != NULL) {
op = strchr(p,'+');
if(op != NULL) {
char *hash = ++op;
if(strlen(hash) == 32)
apr_hash_set(gsb->gsb_table, hash, APR_HASH_KEY_STRING, "malware");
}
op = strchr(p,'-');
if(op != NULL) {
char *hash = ++op;
if(strlen(hash) == 32)
apr_hash_set(gsb->gsb_table, hash, APR_HASH_KEY_STRING, NULL);
}
p = apr_strtok(NULL,"\t",&savedptr);
}
apr_file_close(gsb->db);
free(buf);
buf = NULL;
return 1;
}
/** \brief Init GSB database
*
* \param dcfg Pointer to directory configuration
* \param dbfn Database filename
* \param error_msg Error message
*
* \retval gsb_db_create On Success
* \retval -1 On Fail
*/
int gsb_db_init(directory_config *dcfg, const char *dbfn, char **error_msg)
{
*error_msg = NULL;
if ((dcfg->gsb == NULL) || (dcfg->gsb == NOT_SET_P)) {
dcfg->gsb = apr_pcalloc(dcfg->mp, sizeof(gsb_db));
if (dcfg->gsb == NULL) {
return -1;
}
}
dcfg->gsb->db = NULL;
dcfg->gsb->dbfn = apr_pstrdup(dcfg->mp, dbfn);
return gsb_db_create(dcfg, error_msg);
}
modsecurity-apache-2.9.13/apache2/msc_gsb.h 0000664 0000000 0000000 00000001526 15174655764 0020515 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#ifndef _MSC_GSB_H_
#define _MSC_GSB_H_
typedef struct gsb_db gsb_db;
#include
#include "modsecurity.h"
#include "apr_hash.h"
struct gsb_db {
apr_file_t *db;
const char *dbfn;
apr_hash_t *gsb_table;
};
int DSOLOCAL gsb_db_init(directory_config *dcfg, const char *dbfn, char **error_msg);
#endif
modsecurity-apache-2.9.13/apache2/msc_json.c 0000664 0000000 0000000 00000030213 15174655764 0020701 0 ustar 00root root 0000000 0000000 /*
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
* 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
*
* 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 "msc_json.h"
#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;
/**
* If we do not have a prefix, we cannot create a variable name
* to reference this argument; for now we simply ignore these
*/
if (!msr->json->current_key) {
msr->json->current_key = "";
}
arg = (msc_arg *) apr_pcalloc(msr->mp, sizeof(msc_arg));
/**
* Argument name is 'prefix + current_key'
*/
if (msr->json->prefix) {
arg->name = apr_psprintf(msr->mp, "%s.%s", msr->json->prefix,
msr->json->current_key);
}
else {
arg->name = apr_psprintf(msr->mp, "%s", msr->json->current_key);
}
arg->name_len = strlen(arg->name);
/**
* Argument value is copied from the provided string
*/
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);
return 1;
}
/**
* yajl callback functions
* For more information on the function signatures and order, check
* http://lloyd.github.com/yajl/yajl-1.0.12/structyajl__callbacks.html
*/
/**
* Callback for hash key values; we use those to define the variable names
* under ARGS. Whenever we reach a new key, we update the current key value.
*/
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;
/**
* yajl does not provide us with null-terminated strings, but
* rather expects us to copy the data from the key up to the
* length informed; we create a standalone null-termined copy
* in safe_key
*/
safe_key = apr_pstrndup(msr->mp, key, length);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "New JSON hash key '%s'", safe_key);
}
/**
* TODO: How do we free the previously string value stored here?
*/
msr->json->current_key = safe_key;
return 1;
}
/**
* Callback for null values
*
* TODO: Is there a way to define true null parameter values instead of
* empty values?
*/
static int yajl_null(void *ctx)
{
modsec_rec *msr = (modsec_rec *) ctx;
assert(msr != NULL);
return json_add_argument(msr, "", 0);
}
/**
* Callback for boolean values
*/
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"));
}
else {
return json_add_argument(msr, "false", strlen("false"));
}
}
/**
* Callback for string values
*/
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);
}
/**
* Callback for numbers; YAJL can use separate callbacks for integers/longs and
* float/double values, but since we are not interested in using the numeric
* values here, we use a generic handler which uses numeric strings
*/
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
*/
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
* need to do anything
*/
if (!msr->json->current_key) return 1;
/**
* Check if we are already inside a hash context, and append or create the
* current key name accordingly
*/
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;
}
/**
* Callback for end hash, meaning the current subtree is being closed, and that
* we should go back to the parent variable label
*/
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;
/**
* 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);
msr->json->current_key = apr_psprintf(msr->mp, "%s", separator + 1);
}
else {
/**
* TODO: Check if it is safe to do this kind of pointer tricks
*/
msr->json->current_key = msr->json->prefix;
msr->json->prefix = (unsigned char *) NULL;
}
msr->json->current_depth--;
return 1;
}
/**
* Initialise JSON parser.
*/
int json_init(modsec_rec *msr, char **error_msg) {
assert(msr != NULL);
assert(error_msg != NULL);
/**
* yajl configuration and callbacks
*/
static yajl_callbacks callbacks = {
yajl_null,
yajl_boolean,
NULL /* yajl_integer */,
NULL /* yajl_double */,
yajl_number,
yajl_string,
yajl_start_map,
yajl_map_key,
yajl_end_map,
yajl_start_array,
yajl_end_array
};
*error_msg = NULL;
msr_log(msr, 4, "JSON parser initialization");
msr->json = apr_pcalloc(msr->mp, sizeof(json_data));
if (msr->json == NULL) return -1;
/**
* Prefix and current key are initially empty
*/
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
*
* yajl_parser_config definition:
* http://lloyd.github.io/yajl/yajl-2.0.1/yajl__parse_8h.html#aec816c5518264d2ac41c05469a0f986c
*
* TODO: make UTF8 validation optional, as it depends on Content-Encoding
*/
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "yajl JSON parsing callback initialization");
}
msr->json->handle = yajl_alloc(&callbacks, NULL, msr);
yajl_config(msr->json->handle, yajl_allow_partial_values, 0);
return 1;
}
/**
* 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) {
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) {
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;
}
return 1;
}
/**
* Finalise JSON parsing.
*/
int json_complete(modsec_rec *msr, char **error_msg) {
assert(msr != NULL);
assert(msr->json != NULL);
assert(error_msg != NULL);
*error_msg = NULL;
/* Wrap up the parsing process */
msr->json->status = yajl_complete_parse(msr->json->handle);
if (msr->json->status != yajl_status_ok) {
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;
}
return 1;
}
/**
* 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);
msr->json->handle = NULL;
}
return 1;
}
#endif
modsecurity-apache-2.9.13/apache2/msc_json.h 0000664 0000000 0000000 00000003040 15174655764 0020704 0 ustar 00root root 0000000 0000000 /*
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
* Copyright (c) 2004-2011 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.
*/
#ifndef _MSC_JSON_H_
#define _MSC_JSON_H_
#ifdef WITH_YAJL
typedef struct json_data json_data;
#include "modsecurity.h"
#include
//#ifdef WITH_YAJL
//#else
#include "modsecurity.h"
/* Structures */
struct json_data {
/* yajl configuration and parser state */
yajl_handle handle;
yajl_status status;
/* error reporting and JSON array flag */
unsigned char *yajl_error;
/* prefix is used to create data hierarchy (i.e., 'parent.child.value') */
unsigned char *prefix;
const unsigned char *current_key;
long int current_depth;
int depth_limit_exceeded;
};
/* Functions */
int DSOLOCAL json_init(modsec_rec *msr, char **error_msg);
int DSOLOCAL json_process(modsec_rec *msr, const char *buf,
unsigned int size, char **error_msg);
int DSOLOCAL json_complete(modsec_rec *msr, char **error_msg);
apr_status_t DSOLOCAL json_cleanup(modsec_rec *msr);
int DSOLOCAL json_process_chunk(modsec_rec *msr, const char *buf,
unsigned int size, char **error_msg);
#endif
#endif
modsecurity-apache-2.9.13/apache2/msc_logging.c 0000664 0000000 0000000 00000255572 15174655764 0021377 0 ustar 00root root 0000000 0000000 /*
* 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 "modsecurity.h"
#include
#include "re.h"
#include "msc_logging.h"
#include "httpd.h"
#include "apr_strings.h"
#include "apr_global_mutex.h"
#include "msc_util.h"
#include "apr_version.h"
#include
#ifdef WITH_YAJL
#include
#include "msc_logging_json.h"
#endif
/**
* Write the supplied data to the audit log (if the FD is ready), update
* 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;
/* Do nothing if there's no data. */
if (data == NULL) return -1;
/* Update size counters and the hash calculation. We always do this,
* even in cases where write fails. That will make it easier to detect
* problems with partial writes.
*/
msr->new_auditlog_size += len;
apr_md5_update(&msr->new_auditlog_md5ctx, data, len);
/* Do not write if we do not have a file descriptor. */
if (msr->new_auditlog_fd == NULL) return -1;
/* Write data to file. */
rc = apr_file_write_full(msr->new_auditlog_fd, data, nbytes, &nbytes_written);
if (rc != APR_SUCCESS) {
char errstr[1024];
msr_log(msr, 1, "Audit log: Failed writing (requested %" APR_SIZE_T_FMT
" bytes, written %" APR_SIZE_T_FMT "): %s", nbytes, nbytes_written,
apr_strerror(rc, errstr, sizeof(errstr)));
/* Concurrent log format: don't leak file handle. */
if (msr->txcfg->auditlog_type == AUDITLOG_CONCURRENT) {
apr_file_close(msr->new_auditlog_fd);
}
/* Set to NULL to prevent more than one error message on
* out-of-disk-space events and to prevent further attempts
* to write to the same file in this request.
*
* Serial log format: Note that, as we opened the file through the
* pool mechanism of the APR, we do not need to close the file
* here. It will be closed automatically at the end of the request.
*/
msr->new_auditlog_fd = NULL;
return -1;
}
return 1;
}
/**
* Constructs a log line in vcombined log format trying to truncate
* 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;
const char *sessionid;
char *the_request, *bytes_sent;
int limit = _limit;
/* hostname */
hostname = (msr->hostname == NULL ? "-" : log_escape_nq(msr->mp, msr->hostname));
/* remote log name */
if (msr->remote_user == NULL) remote_user = "-";
else remote_user = log_escape_nq(msr->mp, msr->remote_user);
/* authenticated user */
if (msr->local_user == NULL) local_user = "-";
else local_user = log_escape_nq(msr->mp, msr->local_user);
/* unique id */
if (msr->txid == NULL) uniqueid = "-";
else uniqueid = log_escape(msr->mp, msr->txid);
/* referer */
referer = "-";
/*
referer = (char *)apr_table_get(msr->request_headers, "Referer");
if (referer == NULL) referer = "-";
else referer = log_escape(msr->mp, referer);
*/
/* user agent */
user_agent = "-";
/*
user_agent = (char *)apr_table_get(msr->request_headers, "User-Agent");
if (user_agent == NULL) user_agent = "-";
else user_agent = log_escape(msr->mp, user_agent);
*/
/* sessionid */
sessionid = (msr->sessionid == NULL) ? "-" : log_escape(msr->mp, msr->sessionid);
the_request = (msr->request_line == NULL) ? "" : log_escape(msr->mp, msr->request_line);
bytes_sent = apr_psprintf(msr->mp, "%" APR_OFF_T_FMT, msr->bytes_sent);
/* first take away the size of the
* information we must log
*/
limit -= 22; /* spaces and double quotes */
limit -= strlen(hostname); /* server name or IP */
limit -= strlen(msr->remote_addr); /* remote IP */
limit -= 28; /* current_logtime */
limit -= 3; /* status */
limit -= strlen(bytes_sent); /* bytes sent */
limit -= strlen(uniqueid); /* unique id */
limit -= strlen(sessionid); /* session id */
if (limit <= 0) {
msr_log(msr, 1, "GuardianLog: Atomic pipe write size too small: %d", PIPE_BUF);
return NULL;
}
/* we hope to be able to squeeze everything in */
if (limit < (int)(strlen(remote_user) + strlen(local_user) + strlen(referer)
+ strlen(user_agent) + strlen(the_request)))
{
/* Boo hoo hoo, there's not enough space available. */
*was_limited = 1;
/* Let's see if we can reduce the size of something. This
* is a very crude approach but it seems to work for our
* needs.
*/
if (strlen(remote_user) > 32) {
msr_log(msr, 9, "GuardianLog: Reduced remote_user to 32.");
remote_user[32] = '\0';
}
limit -= strlen(remote_user);
if (strlen(local_user) > 32) {
msr_log(msr, 9, "GuardianLog: Reduced local_user to 32.");
local_user[32] = '\0';
}
limit -= strlen(local_user);
if (strlen(referer) > 64) {
msr_log(msr, 9, "GuardianLog: Reduced referer to 64.");
referer[64] = '\0';
}
limit -= strlen(referer);
if (strlen(user_agent) > 64) {
msr_log(msr, 9, "GuardianLog: Reduced user_agent to 64.");
user_agent[64] = '\0';
}
limit -= strlen(user_agent);
if (limit <= 0) {
msr_log(msr, 1, "GuardianLog: Atomic pipe write size too small: %d.", PIPE_BUF);
return NULL;
}
/* use what's left for the request line */
if ((int)strlen(the_request) > limit) {
the_request[limit] = '\0';
msr_log(msr, 9, "GuardianLog: Reduced the_request to %d bytes.", limit);
}
} else {
/* Yay! We have enough space! */
*was_limited = 0;
}
return apr_psprintf(msr->mp, "%s %s %s %s [%s] \"%s\" %u %s \"%s\" \"%s\" %s \"%s\"",
hostname, msr->remote_addr, remote_user,
local_user, current_logtime(msr->mp), the_request,
msr->response_status, bytes_sent, referer, user_agent,
uniqueid, sessionid
);
}
/**
* Checks if the provided string is a valid audit log parts specification.
*/
int is_valid_parts_specification(char *p) {
char c, *t = p;
while((c = *(t++)) != '\0') {
if ((c != AUDITLOG_PART_ENDMARKER)&&((c < AUDITLOG_PART_FIRST)||(c > AUDITLOG_PART_LAST))) {
return 0;
}
}
return 1;
}
/**
* Constructs a filename that will be used to store an
* audit log entry.
*/
static char *construct_auditlog_filename(apr_pool_t *mp, const char *uniqueid) {
apr_time_exp_t t;
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-%s", userinfo, tstr, uniqueid);
}
/**
* Creates a random 8-character string that
* consists of hexadecimal numbers, to be used
* as an audit log boundary.
*/
static char *create_auditlog_boundary(request_rec *r) {
#ifdef LINUX_S390
int data = swap_int32(rand());
#else
unsigned long data = rand();
#endif
/* Do note that I tried using apr_generate_random_bytes but it turned
* out to be terribly slow for some reason. Needs further investigation.
*/
return bytes2hex(r->pool, (void *)&data, 4);
}
/**
* Sanitises the request line by removing the parameters
* that have been marked as sensitive.
*/
static void sanitize_request_line(modsec_rec *msr) {
const apr_array_header_t *tarr = NULL;
const apr_table_entry_t *telts = NULL;
const apr_array_header_t *tarr_pattern = NULL;
const apr_table_entry_t *telts_pattern = NULL;
msc_parm *mparm = NULL;
int i, k;
char *qspos;
char *buf = NULL;
int sanitized_partial = 0;
int sanitize_matched = 0;
/* Locate the query string. */
qspos = strstr(msr->request_line, "?");
if (qspos == NULL) return;
qspos++;
/* Loop through the list of sensitive parameters. */
tarr = apr_table_elts(msr->arguments_to_sanitize);
telts = (const apr_table_entry_t*)tarr->elts;
for (i = 0; i < tarr->nelts; i++) {
msc_arg *arg = (msc_arg *)telts[i].val;
/* Only look at the parameters that appeared in the query string. */
if (strcmp(arg->origin, "QUERY_STRING") == 0) {
char *pat = NULL;
char *p;
int j, arg_min, arg_max;
/* Go to the beginning of the parameter. */
p = qspos;
j = arg->value_origin_offset;
while((*p != '\0')&&(j--)) p++;
if (*p == '\0') {
msr_log(msr, 1, "Unable to sanitize variable \"%s\" at offset %u of QUERY_STRING"
"because the request line is too short.",
log_escape_ex(msr->mp, arg->name, arg->name_len),
arg->value_origin_offset);
continue;
}
tarr_pattern = apr_table_elts(msr->pattern_to_sanitize);
telts_pattern = (const apr_table_entry_t*)tarr_pattern->elts;
sanitized_partial = 0;
sanitize_matched = 0;
buf = apr_psprintf(msr->mp, "%s",p);
for ( k = 0; k < tarr_pattern->nelts; k++) {
if(strncmp(telts_pattern[k].key,arg->name,strlen(arg->name)) ==0 ) {
mparm = (msc_parm *)telts_pattern[k].val;
pat = strstr(buf,mparm->value);
if(mparm->pad_1 == -1)
sanitize_matched = 1;
if (pat != NULL) {
j = strlen(mparm->value);
arg_min = j;
arg_max = 1;
while((*pat != '\0')&&(j--)) {
if(arg_max > mparm->pad_2) {
int off = (strlen(mparm->value) - arg_max);
int pos = (mparm->pad_1-1);
if(off > pos) {
*pat = '*';
}
}
arg_max++;
arg_min--;
pat++;
}
}
sanitized_partial = 1;
}
}
if(sanitized_partial == 1 && sanitize_matched == 0) {
while(*buf != '\0') {
*p++ = *buf++;
}
continue;
} else {
/* Write over the value. */
j = arg->value_origin_len;
while((*p != '\0')&&(j--)) {
*p++ = '*';
}
if (*p == '\0') {
msr_log(msr, 1, "Unable to sanitize variable \"%s\" at offset %u (size %d) "
"of QUERY_STRING because the request line is too short.",
log_escape_ex(msr->mp, arg->name, arg->name_len),
arg->value_origin_offset, arg->value_origin_len);
continue;
}
}
}
}
}
/**
* Output the Producer header.
*/
static void sec_auditlog_write_producer_header(modsec_rec *msr) {
char **signatures = NULL;
char *text = NULL;
int i;
/* Try to write everything in one go. */
if (msr->txcfg->component_signatures->nelts == 0) {
text = apr_psprintf(msr->mp, "Producer: %s.\n", MODSEC_MODULE_NAME_FULL);
sec_auditlog_write(msr, text, strlen(text));
return;
}
/* Start with the ModSecurity signature. */
text = apr_psprintf(msr->mp, "Producer: %s", MODSEC_MODULE_NAME_FULL);
sec_auditlog_write(msr, text, strlen(text));
/* Then loop through the components and output individual signatures. */
signatures = (char **)msr->txcfg->component_signatures->elts;
for(i = 0; i < msr->txcfg->component_signatures->nelts; i++) {
text = apr_psprintf(msr->mp, "; %s", (char *)signatures[i]);
sec_auditlog_write(msr, text, strlen(text));
}
sec_auditlog_write(msr, ".\n", 2);
}
#ifdef WITH_YAJL
/**
* 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;
// this is written no matter what
yajl_string(g, "producer");
/* Try to write verything in one go. */
if (msr->txcfg->component_signatures->nelts == 0) {
yajl_string(g, MODSEC_MODULE_NAME_FULL);
return;
}
// we'll need an array if there are component signatures
yajl_gen_array_open(g);
/* Start with the ModSecurity signature. */
yajl_string(g, MODSEC_MODULE_NAME_FULL);
/* Then loop through the components and output individual signatures. */
signatures = (char **)msr->txcfg->component_signatures->elts;
for(i = 0; i < msr->txcfg->component_signatures->nelts; i++) {
yajl_string(g, (char *)signatures[i]);
}
yajl_gen_array_close(g); // array for producers is finished
}
#endif
/*
* \brief This function will returns the next chain node
*
* \param current Pointer to current chined rule
* \param msr Pointer to modsec_rec
*
* \retval NULL On failure
* \retval next_rule On Success
*/
static msre_rule *return_chained_rule(const msre_rule *current, modsec_rec *msr) {
apr_array_header_t *arr = NULL;
msre_rule **rules = NULL;
msre_rule *rule = NULL, *next_rule = NULL;
int i;
int phase;
if (current == NULL || current->actionset == NULL || current->ruleset == NULL)
return NULL;
phase = current->actionset->phase;
switch (phase) {
case PHASE_REQUEST_HEADERS :
arr = current->ruleset->phase_request_headers;
break;
case PHASE_REQUEST_BODY :
arr = current->ruleset->phase_request_body;
break;
case PHASE_RESPONSE_HEADERS :
arr = current->ruleset->phase_response_headers;
break;
case PHASE_RESPONSE_BODY :
arr = current->ruleset->phase_response_body;
break;
case PHASE_LOGGING :
arr = current->ruleset->phase_logging;
break;
default :
msr_log(msr, 1, "Logging: Invalid phase %d",phase);
return NULL;
}
rules = (msre_rule **)arr->elts;
for (i = 0; i < arr->nelts; i++) {
rule = rules[i];
if (rule != NULL) {
if (strncmp(current->unparsed,rule->unparsed,strlen(current->unparsed)) == 0) {
if (i < arr->nelts -1) {
next_rule = rules[i+1];
} else {
next_rule = rules[i];
}
if (next_rule == NULL || next_rule->chain_starter == NULL)
return NULL;
if(current->chain_starter == NULL && next_rule->chain_starter != NULL) {
if (strncmp(current->unparsed, next_rule->chain_starter->unparsed, strlen(current->unparsed)) != 0)
return NULL;
}
if(current->chain_starter != NULL && next_rule->chain_starter != NULL) {
if (strncmp(current->chain_starter->unparsed, rule->chain_starter->unparsed, strlen(current->chain_starter->unparsed)) != 0)
return NULL;
}
return next_rule;
}
}
}
return NULL;
}
/*
* \brief This function will check if a chained rule
* appears into matched array.
*
* \param msr Pointer to modsec_rec
* \param next_rule Pointer to chained rule
*
* \retval 0 On failure
* \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;
for (i = 0; i < msr->matched_rules->nelts; i++) {
rule = ((msre_rule **)msr->matched_rules->elts)[i];
if (rule != NULL && (strncmp(rule->unparsed,next_rule->unparsed,strlen(rule->unparsed)) == 0)) {
return 1;
}
}
return 0;
}
#ifdef WITH_YAJL
/**
* 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);
yajl_kv_int(g, "p1", msr->time_phase1);
yajl_kv_int(g, "p2", msr->time_phase2);
yajl_kv_int(g, "p3", msr->time_phase3);
yajl_kv_int(g, "p4", msr->time_phase4);
yajl_kv_int(g, "p5", msr->time_phase5);
yajl_kv_int(g, "sr", msr->time_storage_read);
yajl_kv_int(g, "sw", msr->time_storage_write);
yajl_kv_int(g, "l", msr->time_logging);
yajl_kv_int(g, "gc", msr->time_gc);
yajl_gen_map_close(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;
int k;
yajl_gen_map_open(g);
yajl_string(g, "actionset");
yajl_gen_map_open(g);
if (rule->actionset->id) {
yajl_kv_string(g, "id", log_escape(msr->mp, rule->actionset->id));
}
if (rule->actionset->rev) {
yajl_kv_string(g, "rev", log_escape(msr->mp, rule->actionset->rev));
}
if (rule->actionset->version) {
yajl_kv_string(g, "version", log_escape(msr->mp, rule->actionset->version));
}
if (rule->actionset->severity != NOT_SET) {
yajl_kv_int(g, "severity", rule->actionset->severity);
}
if (rule->actionset->accuracy != NOT_SET) {
yajl_kv_int(g, "accuracy", rule->actionset->accuracy);
}
if (rule->actionset->maturity != NOT_SET) {
yajl_kv_int(g, "maturity", rule->actionset->maturity);
}
if (rule->actionset->phase != NOT_SET) {
yajl_kv_int(g, "phase", rule->actionset->phase);
}
yajl_kv_bool(g, "is_chained", rule->actionset->is_chained || (rule->chain_starter != NULL));
if (rule->actionset->is_chained && (rule->chain_starter == NULL)) {
yajl_kv_bool(g, "chain_starter", 1);
}
// tags, lazily opened
tarr = apr_table_elts(rule->actionset->actions);
telts = (const apr_table_entry_t*)tarr->elts;
for (k = 0; k < tarr->nelts; k++) {
msre_action *action = (msre_action *)telts[k].val;
if (strcmp(telts[k].key, "tag") == 0) {
msc_string *var = NULL;
if (been_opened == 0) {
yajl_string(g, "tags");
yajl_gen_array_open(g);
been_opened = 1;
}
// expand variables in the tag
var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
var->value = (char *)action->param;
var->value_len = strlen(action->param);
expand_macros(msr, var, NULL, msr->mp);
yajl_string(g, log_escape(msr->mp, var->value));
}
}
if (been_opened == 1) {
yajl_gen_array_close(g);
}
yajl_gen_map_close(g);
yajl_string(g, "operator");
yajl_gen_map_open(g);
yajl_kv_string(g, "operator", rule->op_name);
yajl_kv_string(g, "operator_param", rule->op_param);
yajl_kv_string(g, "target", rule->p1);
yajl_kv_bool(g, "negated", rule->op_negated);
yajl_gen_map_close(g);
yajl_string(g, "config");
yajl_gen_map_open(g);
yajl_kv_string(g, "filename", rule->filename);
yajl_kv_int(g, "line_num", rule->line_num);
yajl_gen_map_close(g);
yajl_kv_string(g, "unparsed", rule->unparsed);
yajl_kv_bool(g, "is_matched", chained_is_matched(msr, rule));
yajl_gen_map_close(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;
const apr_table_entry_t *telts_pattern = NULL;
char *str1 = NULL, *str2 = NULL, *text = NULL;
const msre_rule *rule = NULL, *next_rule = NULL;
apr_size_t nbytes, nbytes_written;
unsigned char md5hash[APR_MD5_DIGESTSIZE];
int was_limited = 0;
int present = 0;
int wrote_response_body = 0;
char *entry_filename, *entry_basename;
apr_status_t rc;
int i, limit, k, sanitized_partial, j;
char *buf = NULL, *pat = NULL;
msc_parm *mparm = NULL;
int arg_min, arg_max, sanitize_matched;
yajl_gen g;
int been_opened = 0; // helper flag for conditionally opening maps
const unsigned char *final_buf;
size_t len;
/* Return silently if we don't have a request line. This
* means we will not be logging request timeouts.
*/
if (msr->request_line == NULL) {
msr_log(msr, 4, "Audit log: Skipping request whose request_line is null.");
return;
}
/* Also return silently if we don't have a file descriptor. */
if (msr->txcfg->auditlog_fd == NULL) {
msr_log(msr, 4, "Audit log: Skipping request since there is nowhere to write to.");
return;
}
if (msr->txcfg->auditlog_type != AUDITLOG_CONCURRENT) {
/* Serial logging - we already have an open file
* descriptor to write to.
*/
msr->new_auditlog_fd = msr->txcfg->auditlog_fd;
} else {
/* Concurrent logging - we need to create a brand
* new file for this request.
*/
apr_md5_init(&msr->new_auditlog_md5ctx);
msr->new_auditlog_filename = construct_auditlog_filename(msr->mp, msr->txid);
if (msr->new_auditlog_filename == NULL) return;
/* The audit log storage directory should be explicitly
* defined. But if it isn't try to write to the same
* directory where the index file is placed. Of course,
* it is *very* bad practice to allow the Apache user
* to write to the same directory where a root user is
* writing to but it's not us that's causing the problem
* and there isn't anything we can do about that.
*
* ENH Actually there is something we can do! We will make
* SecAuditStorageDir mandatory, ask the user to explicitly
* define the storage location *and* refuse to work if the
* index and the storage location are in the same folder.
*/
if (msr->txcfg->auditlog_storage_dir == NULL) {
entry_filename = file_dirname(msr->mp, msr->txcfg->auditlog_name);
}
else {
entry_filename = msr->txcfg->auditlog_storage_dir;
}
if (entry_filename == NULL) return;
entry_filename = apr_psprintf(msr->mp, "%s%s", entry_filename, msr->new_auditlog_filename);
if (entry_filename == NULL) return;
entry_basename = file_dirname(msr->mp, entry_filename);
if (entry_basename == NULL) return;
/* IMP1 Surely it would be more efficient to check the folders for
* the audit log repository base path in the configuration phase, to reduce
* the work we do on every request. Also, since our path depends on time,
* we could cache the time we last checked and don't check if we know
* the folder is there.
*/
rc = apr_dir_make_recursive(entry_basename, msr->txcfg->auditlog_dirperms, msr->mp);
if ((rc != APR_SUCCESS) && (rc != APR_EEXIST)) {
msr_log(msr, 1, "Audit log: Failed to create subdirectories: %s (%s)",
entry_basename, get_apr_error(msr->mp, rc));
return;
}
rc = apr_file_open(&msr->new_auditlog_fd, entry_filename,
APR_WRITE | APR_TRUNCATE | APR_CREATE | APR_BINARY | APR_FILE_NOCLEANUP,
msr->txcfg->auditlog_fileperms, msr->mp);
if (rc != APR_SUCCESS) {
msr_log(msr, 1, "Audit log: Failed to create file: %s (%s)",
entry_filename, get_apr_error(msr->mp, rc));
return;
}
}
/* Lock the mutex, but only if we are using serial format. */
if (msr->txcfg->auditlog_type != AUDITLOG_CONCURRENT) {
msr_global_mutex_lock(msr, msr->modsecurity->auditlog_lock, "Audit log");
}
/**
* allocate the buffer for the JSON generator
* passing null will force yajl to use malloc/realloc/free
* need to perf test using APR routines
*/
g = yajl_gen_alloc(NULL);
/**
* don't pretty print JSON
* this is harder to eyeball but much easier to parse programmatically
*/
yajl_gen_config(g, yajl_gen_beautify, 0);
yajl_gen_map_open(g); // IT BEGINS
/* AUDITLOG_PART_HEADER */
yajl_string(g, "transaction");
yajl_gen_map_open(g); // transaction top-level key
yajl_kv_string(g, "time", current_logtime(msr->mp));
yajl_kv_string(g, "transaction_id", msr->txid);
yajl_kv_string(g, "remote_address", msr->remote_addr);
yajl_kv_int(g, "remote_port", (int)msr->remote_port); // msr->remote_port is unsigned, yajl wants signed
yajl_kv_string(g, "local_address", msr->local_addr);
yajl_kv_int(g, "local_port", (int)msr->local_port);
yajl_gen_map_close(g); // transaction top-level key is finished
yajl_string(g, "request");
yajl_gen_map_open(g); // request top-level key
/* AUDITLOG_PART_REQUEST_HEADERS */
if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_REQUEST_HEADERS) != NULL) {
sanitize_request_line(msr);
yajl_kv_string(g, "request_line", msr->request_line);
yajl_string(g, "headers");
yajl_gen_map_open(g); // separate map for request headers
arr = apr_table_elts(msr->request_headers);
te = (apr_table_entry_t *)arr->elts;
tarr_pattern = apr_table_elts(msr->pattern_to_sanitize);
telts_pattern = (const apr_table_entry_t*)tarr_pattern->elts;
for (i = 0; i < arr->nelts; i++) {
sanitized_partial = 0;
sanitize_matched = 0;
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, te[i].val);
// write the key no matter what
// since sanitization only occurs on the value
yajl_string(g, te[i].key);
if (apr_table_get(msr->request_headers_to_sanitize, te[i].key) != NULL) {
buf = apr_psprintf(msr->mp, "%s",text+strlen(te[i].key)+2);
for ( k = 0; k < tarr_pattern->nelts; k++) {
if(strncmp(telts_pattern[k].key,te[i].key,strlen(te[i].key)) ==0 ) {
mparm = (msc_parm *)telts_pattern[k].val;
if(mparm->pad_1 == -1)
sanitize_matched = 1;
pat = strstr(buf,mparm->value);
if (pat != NULL) {
j = strlen(mparm->value);
arg_min = j;
arg_max = 1;
while((*pat != '\0')&&(j--)) {
if(arg_max > mparm->pad_2) {
int off = strlen(mparm->value) - arg_max;
int pos = mparm->pad_1-1;
if(off > pos) {
*pat = '*';
}
}
arg_max++;
arg_min--;
pat++;
}
sanitized_partial = 1;
}
}
}
if(sanitized_partial == 1 && sanitize_matched == 0) {
yajl_string(g, buf);
} else {
memset(buf, '*', strlen(buf)); // strlen also includes the appended newline on the header
yajl_string(g, buf);
}
} else {
// we diverge from the original logic a bit because we always print the key
// at this no point sanitization had occured, so we just print the value
yajl_string(g, te[i].val);
}
}
yajl_gen_map_close(g); // request headers map is finished
}
/* AUDITLOG_PART_REQUEST_BODY */
/* Output this part of it was explicitly requested (C) or if it was the faked
* request body that was requested (I) but we have no reason to fake it (it's
* already in the correct format).
*/
if ( (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_REQUEST_BODY) != NULL)
|| ( (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_FAKE_REQUEST_BODY) != NULL)
&& (msr->mpd == NULL) ) )
{
if (msr->msc_reqbody_read) {
const apr_array_header_t *tarr;
const apr_table_entry_t *telts;
apr_array_header_t *sorted_args;
unsigned int offset = 0, last_offset = 0;
msc_arg *nextarg = NULL;
int sanitize = 0; /* IMP1 Use constants for "sanitize" values. */
char *my_error_msg = NULL;
sorted_args = apr_array_make(msr->mp, 25, sizeof(const msc_arg *));
/* First we need to sort the arguments that need to be
* sanitized in descending order (we are using a stack structure
* to store then so the order will be ascending when we start
* popping them out). This is because we will
* be reading the request body sequentially and must
* sanitize it as we go.
*/
for(;;) {
nextarg = NULL;
/* Find the next largest offset (excluding
* the ones we've used up already).
*/
tarr = apr_table_elts(msr->arguments_to_sanitize);
telts = (const apr_table_entry_t*)tarr->elts;
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, "JSON") !=0) )
continue;
if (last_offset == 0) { /* The first time we're here. */
if (arg->value_origin_offset > offset) {
offset = arg->value_origin_offset;
nextarg = arg;
}
} else { /* Not the first time. */
if ((arg->value_origin_offset > offset)
&&(arg->value_origin_offset < last_offset))
{
offset = arg->value_origin_offset;
nextarg = arg;
}
}
}
/* If we don't have the next argument that means
* we're done here.
*/
if (nextarg == NULL) break;
sanitize = 2; /* Means time to pop the next argument out. */
last_offset = offset;
offset = 0;
{ /* IMP1 Fix this ugly bit here. */
msc_arg **x = apr_array_push(sorted_args);
*x = nextarg;
}
}
/* Now start retrieving the body chunk by chunk and
* sanitize data in pieces.
*/
rc = modsecurity_request_body_retrieve_start(msr, &my_error_msg);
if (rc < 0) {
msr_log(msr, 1, "Audit log: %s", my_error_msg);
} else {
msc_data_chunk *chunk = NULL;
unsigned int chunk_offset = 0;
unsigned int sanitize_offset = 0;
unsigned int sanitize_length = 0;
yajl_string(g, "body");
yajl_gen_array_open(g); // use an array here because we're writing in chunks
for(;;) {
rc = modsecurity_request_body_retrieve(msr, &chunk, -1, &my_error_msg);
if (chunk != NULL) {
/* Anything greater than 1 means we have more data to sanitize. */
while (sanitize > 1) {
msc_arg **arg = NULL;
if (sanitize == 2) {
/* Get the next argument from the stack. */
arg = (msc_arg **)apr_array_pop(sorted_args);
if (arg == NULL) sanitize = 0; /* We're done sanitising. */
else {
/* Continue with sanitation to process the
* retrieved argument.
*/
sanitize = 1;
sanitize_offset = (*arg)->value_origin_offset;
sanitize_length = (*arg)->value_origin_len;
}
}
if (sanitize) {
/* Check if the data we want to sanitize is
* stored in the current chunk.
*/
if (chunk_offset + chunk->length > sanitize_offset) {
unsigned int soff; /* data offset within chunk */
unsigned int len; /* amount in this chunk to sanitize */
soff = sanitize_offset - chunk_offset;
if (soff + sanitize_length <= chunk->length) {
/* The entire argument resides in the current chunk. */
len = sanitize_length;
sanitize = 2; /* Get another parameter to sanitize. */
} else {
/* Some work to do here but we'll need to seek
* another chunk.
*/
len = chunk->length - soff;
sanitize_offset += len;
sanitize_length -= len;
sanitize = 1; /* It's OK to go to the next chunk. */
}
/* Yes, we actually write over the original data.
* We shouldn't be needing it any more.
*/
if (soff + len <= chunk->length) { /* double check */
memset((char *)chunk->data + soff, '*', len);
}
}
}
}
/* 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;
}
if (rc <= 0) {
break;
}
}
yajl_gen_array_close(g); // request body chunks array is finished
if (rc < 0) {
msr_log(msr, 1, "Audit log: %s", my_error_msg);
}
modsecurity_request_body_retrieve_end(msr);
}
}
}
/* AUDITLOG_PART_FAKE_REQUEST_BODY */
if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_FAKE_REQUEST_BODY) != NULL) {
if ((msr->msc_reqbody_read)&&(msr->mpd != NULL)) {
char *buffer = NULL;
buffer = multipart_reconstruct_urlencoded_body_sanitize(msr);
if (buffer == NULL) {
msr_log(msr, 1, "Audit log: Failed to reconstruct request body.");
} else {
yajl_kv_string(g, "fake_body", buffer);
}
}
}
yajl_gen_map_close(g); // request top-level key is finished
yajl_string(g, "response");
yajl_gen_map_open(g); // response top-level key
/* AUDITLOG_PART_A_RESPONSE_HEADERS */
if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_A_RESPONSE_HEADERS) != NULL) {
/* There are no response headers (or the status line) in HTTP 0.9 */
if (msr->response_headers_sent) {
yajl_kv_string(g, "protocol", msr->response_protocol);
// as an integer, response status is easier to parse than status_line
yajl_kv_int(g, "status", (int)msr->response_status);
yajl_string(g, "headers");
yajl_gen_map_open(g); // separate map for response headers
/* Output headers */
arr = apr_table_elts(msr->response_headers);
te = (apr_table_entry_t *)arr->elts;
tarr_pattern = apr_table_elts(msr->pattern_to_sanitize);
telts_pattern = (const apr_table_entry_t*)tarr_pattern->elts;
for (i = 0; i < arr->nelts; i++) {
sanitized_partial = 0;
sanitize_matched = 0;
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, te[i].val);
// write the key no matter what
// since sanitization only occurs on the value
yajl_string(g, te[i].key);
if (apr_table_get(msr->response_headers_to_sanitize, te[i].key) != NULL) {
buf = apr_psprintf(msr->mp, "%s",text+strlen(te[i].key)+2);
for ( k = 0; k < tarr_pattern->nelts; k++) {
if(strncmp(telts_pattern[k].key,te[i].key,strlen(te[i].key)) ==0 ) {
mparm = (msc_parm *)telts_pattern[k].val;
if(mparm->pad_1 == -1)
sanitize_matched = 1;
pat = strstr(buf,mparm->value);
if (pat != NULL) {
j = strlen(mparm->value);
arg_min = j;
arg_max = 1;
while((*pat != '\0')&&(j--)) {
if(arg_max > mparm->pad_2) {
int off = strlen(mparm->value) - arg_max;
int pos = mparm->pad_1-1;
if(off > pos) {
*pat = '*';
}
}
arg_max++;
arg_min--;
pat++;
}
sanitized_partial = 1;
}
}
}
if(sanitized_partial == 1 && sanitize_matched == 0) {
yajl_string(g, buf);
} else {
memset(buf, '*', strlen(buf));
yajl_string(g, buf);
}
} else {
// we diverge from the original logic a bit because we always print the key
// at this point no sanitization had occured, so we just print the value
yajl_string(g, te[i].val);
}
}
yajl_gen_map_close(g); // response headers map is finised
}
}
apr_table_clear(msr->pattern_to_sanitize);
/* AUDITLOG_PART_RESPONSE_BODY */
if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_RESPONSE_BODY) != NULL) {
if (msr->resbody_data != NULL) {
yajl_kv_string(g, "body", msr->resbody_data);
wrote_response_body = 1;
}
}
yajl_gen_map_close(g); // response top-level key is finished
yajl_string(g, "audit_data");
yajl_gen_map_open(g); // audit_data top-level key
/* AUDITLOG_PART_TRAILER */
if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_TRAILER) != NULL) {
/* Messages */
been_opened = 0;
if (msr->alerts->nelts > 0) {
yajl_string(g, "messages");
yajl_gen_array_open(g);
been_opened = 1;
}
for(i = 0; i < msr->alerts->nelts; i++) {
yajl_string(g, ((char **)msr->alerts->elts)[i]);
}
if (been_opened == 1) {
yajl_gen_array_close(g);
}
/* Apache error messages */
been_opened = 0;
if (msr->error_messages->nelts > 0) {
yajl_string(g, "error_messages");
yajl_gen_array_open(g);
been_opened = 1;
}
for(i = 0; i < msr->error_messages->nelts; i++) {
error_message_t *em = (((error_message_t **)msr->error_messages->elts)[i]);
yajl_string(g, format_error_log_message(msr->mp, em));
}
if (been_opened == 1) {
yajl_gen_array_close(g);
}
/* Action */
if (msr->was_intercepted) {
yajl_string(g, "action");
yajl_gen_map_open(g);
yajl_kv_bool(g, "intercepted", 1);
yajl_kv_int(g, "phase", msr->intercept_phase);
yajl_kv_string(g, "message", msr->intercept_message);
yajl_gen_map_close(g);
}
/* Apache-Handler */
#ifdef LOG_NO_HANDLER
if (msr->txcfg->debuglog_level >= 9)
#endif
if (msr->r->handler != NULL) {
yajl_kv_string(g, "handler", msr->r->handler);
}
/* Stopwatch2 */
#ifdef LOG_NO_STOPWATCH
if (msr->txcfg->debuglog_level >= 9)
#endif
format_performance_variables_json(msr, g);
/* Our response body does not contain chunks */
/* ENH Only write this when the output was chunked. */
/* ENH Add info when request body was decompressed, dechunked too. */
#ifdef LOG_NO_DECHUNK
if (msr->txcfg->debuglog_level >= 9)
#endif
if (wrote_response_body) {
yajl_kv_bool(g, "response_body_dechunked", 1);
}
#ifdef LOG_NO_SERVER_CONTEXT
if (msr->txcfg->debuglog_level >= 9) {
#endif
sec_auditlog_write_producer_header_json(msr, g);
/* Server */
if (msr->server_software != NULL) {
yajl_kv_string(g, "server", msr->server_software);
}
been_opened = 0;
/* Sanitised arguments */
{
const apr_array_header_t *tarr;
const apr_table_entry_t *telts;
tarr = apr_table_elts(msr->arguments_to_sanitize);
telts = (const apr_table_entry_t*)tarr->elts;
if (tarr->nelts > 0) {
if (been_opened == 0) {
yajl_string(g, "sanitized");
yajl_gen_map_open(g);
been_opened = 1;
}
yajl_string(g, "args");
yajl_gen_array_open(g);
}
for(i = 0; i < tarr->nelts; i++) {
msc_arg *arg = (msc_arg *)telts[i].val;
// yay arrays actually make it easier here
yajl_string(g, log_escape(msr->mp, arg->name));
}
if (tarr->nelts > 0) {
yajl_gen_array_close(g);
}
}
/* Sanitised request headers */
{
const apr_array_header_t *tarr;
const apr_table_entry_t *telts;
tarr = apr_table_elts(msr->request_headers_to_sanitize);
telts = (const apr_table_entry_t*)tarr->elts;
if (tarr->nelts > 0) {
if (been_opened == 0) {
yajl_string(g, "sanitized");
yajl_gen_map_open(g);
been_opened = 1;
}
yajl_string(g, "request_headers");
yajl_gen_array_open(g);
}
for(i = 0; i < tarr->nelts; i++) {
yajl_string(g, log_escape(msr->mp, telts[i].key));
}
if (tarr->nelts > 0) {
yajl_gen_array_close(g);
}
}
/* Sanitised response headers */
{
const apr_array_header_t *tarr;
const apr_table_entry_t *telts;
tarr = apr_table_elts(msr->response_headers_to_sanitize);
telts = (const apr_table_entry_t*)tarr->elts;
if (tarr->nelts > 0) {
if (been_opened == 0) {
yajl_string(g, "sanitized");
yajl_gen_map_open(g);
been_opened = 1;
}
yajl_string(g, "response_headers");
yajl_gen_array_open(g);
}
for(i = 0; i < tarr->nelts; i++) {
yajl_string(g, log_escape(msr->mp, telts[i].key));
}
if (tarr->nelts > 0) {
yajl_gen_array_close(g);
}
}
if (been_opened == 1) {
yajl_gen_map_close(g); // sanitized args map is finished
}
#ifdef LOG_NO_SERVER_CONTEXT
}
#endif
/* Web application info. */
if ( ((msr->txcfg->webappid != NULL)&&(strcmp(msr->txcfg->webappid, "default") != 0))
|| (msr->sessionid != NULL) || (msr->userid != NULL))
{
yajl_string(g, "webapp_info");
yajl_gen_map_open(g);
if (msr->txcfg->webappid != NULL) {
yajl_kv_string(g, "id", log_escape(msr->mp, msr->txcfg->webappid));
}
if (msr->sessionid != NULL) {
yajl_kv_string(g, "session", log_escape(msr->mp, msr->sessionid));
}
if (msr->userid != NULL) {
yajl_kv_string(g, "user_id", log_escape(msr->mp, msr->userid));
}
yajl_gen_map_close(g);
}
if ( ((msr->txcfg->sensor_id != NULL)&&(strcmp(msr->txcfg->sensor_id, "default") != 0)))
{
if(msr->txcfg->sensor_id != NULL) {
yajl_kv_string(g, "sensor_id", log_escape(msr->mp, msr->txcfg->sensor_id));
}
}
if (msr->txcfg->is_enabled > 0) {
yajl_kv_string(g, "engine_mode", (msr->txcfg->is_enabled == 1 ? "DETECTION_ONLY" : "ENABLED"));
}
/* Rule performance time */
if(msr->txcfg->max_rule_time > 0) {
const apr_array_header_t *tarr;
const apr_table_entry_t *telts;
tarr = apr_table_elts(msr->perf_rules);
telts = (const apr_table_entry_t*)tarr->elts;
if (tarr->nelts > 0) {
yajl_string(g, "rules_performance_info");
yajl_gen_map_open(g); // separate map for rule perf info
}
for(i = 0; i < tarr->nelts; i++) {
yajl_kv_string(g, log_escape(msr->mp, telts[i].key), log_escape(msr->mp, telts[i].val));
}
if (tarr->nelts > 0) {
yajl_gen_map_close(g); // map for rule perf info is finished
}
}
}
yajl_gen_map_close(g); // audit_data top-level key is finished
/* AUDITLOG_PART_UPLOADS */
if ((strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_UPLOADS) != NULL) && (msr->mpd != NULL)) {
multipart_part **parts = NULL;
unsigned int total_size = 0;
int cfiles = 0;
yajl_string(g, "uploads");
yajl_gen_map_open(g);
parts = (multipart_part **)msr->mpd->parts->elts;
yajl_string(g, "info");
yajl_gen_array_open(g); // separate array for upload info
for(cfiles = 0; cfiles < msr->mpd->parts->nelts; cfiles++) {
if (parts[cfiles]->type == MULTIPART_FILE) {
if(parts[cfiles]->filename != NULL) {
yajl_gen_map_open(g);
yajl_kv_int(g, "file_size", parts[cfiles]->tmp_file_size);
yajl_kv_string(g, "file_name", log_escape(msr->mp, parts[cfiles]->filename));
yajl_kv_string(g, "content_type", parts[cfiles]->content_type ? parts[cfiles]->content_type : "");
total_size += parts[cfiles]->tmp_file_size;
yajl_gen_map_close(g);
}
}
}
yajl_gen_array_close(g); // array for upload info is finished
yajl_kv_int(g, "total", total_size);
yajl_gen_map_close(g); // uploads top-level key is finished
}
/* AUDITLOG_PART_MATCHEDRULES */
if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_MATCHEDRULES) != NULL) {
yajl_string(g, "matched_rules");
yajl_gen_array_open(g); // matched_rules top-level key
/* Matched Rules */
for(i = 0; i < msr->matched_rules->nelts; i++) {
rule = ((msre_rule **)msr->matched_rules->elts)[i];
if ((rule != NULL) && (rule->actionset != NULL) && rule->actionset->is_chained && (rule->chain_starter == NULL)) {
/*
* create a separate map for each rule chain
* this makes it a lot easier to search for partial chains
*/
yajl_gen_map_open(g); // map for this chain
yajl_kv_bool(g, "chain", 1);
yajl_string(g, "rules");
yajl_gen_array_open(g); // array for the rules
write_rule_json(msr, rule, g);
do {
if (rule->ruleset != NULL) {
next_rule = return_chained_rule(rule,msr);
if (next_rule != NULL) {
present = chained_is_matched(msr,next_rule);
if (present == 1) {
i++;
}
write_rule_json(msr, next_rule, g);
}
}
rule = next_rule;
} while (rule != NULL && rule->actionset != NULL && rule->actionset->is_chained);
yajl_gen_array_close(g);
yajl_kv_bool(g, "full_chain_match", present); // if one of the rules didnt match, present is set to 0
yajl_gen_map_close(g); // close the map for this chain
} else {
yajl_gen_map_open(g);
yajl_kv_bool(g, "chain", 0);
yajl_string(g, "rules"); // this really should be 'rule', but we're keeping in line with other chain maps
yajl_gen_array_open(g);
if ((rule != NULL) && (rule->actionset != NULL) && !rule->actionset->is_chained && (rule->chain_starter == NULL)) {
write_rule_json(msr, rule, g);
}
yajl_gen_array_close(g);
yajl_gen_map_close(g);
}
}
yajl_gen_array_close(g); // matched_rules top-level key is finished
}
/* AUDITLOG_PART_ENDMARKER */
/* finished building JSON */
yajl_gen_map_close(g); // box it up!
yajl_gen_get_buf(g, &final_buf, &len);
sec_auditlog_write(msr, final_buf, len);
yajl_gen_clear(g);
yajl_gen_free(g);
sec_auditlog_write(msr, "\n", 1);
/* Return here if we were writing to a serial log
* 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. */
msr_global_mutex_unlock(msr, msr->modsecurity->auditlog_lock, "Audit log");
return;
}
/* From here on only concurrent-style processing. */
/* File handle might already be closed after write failure. */
if (msr->new_auditlog_fd) {
apr_file_close(msr->new_auditlog_fd);
}
/* Write an entry to the index file */
/* Calculate hash of the entry. */
apr_md5_final(md5hash, &msr->new_auditlog_md5ctx);
str2 = apr_psprintf(msr->mp, "%s %d %d md5:%s", msr->new_auditlog_filename, 0,
msr->new_auditlog_size, bytes2hex(msr->mp, md5hash, 16));
if (str2 == NULL) return;
/* We do not want the index line to be longer than 3980 bytes. */
limit = 3980;
was_limited = 0;
/* If we are logging to a pipe we need to observe and
* obey the pipe atomic write limit - PIPE_BUF. For
* more details see the discussion in sec_guardian_logger code.
*/
if (msr->txcfg->auditlog_name[0] == '|') {
if (PIPE_BUF < limit) {
limit = PIPE_BUF;
}
}
limit = limit - strlen(str2) - 5;
if (limit <= 0) {
msr_log(msr, 1, "Audit Log: Atomic PIPE write buffer too small: %d", PIPE_BUF);
return;
}
str1 = construct_log_vcombinedus_limited(msr, limit, &was_limited);
if (str1 == NULL) return;
if (was_limited == 0) {
text = apr_psprintf(msr->mp, "%s %s \n", str1, str2);
} else {
text = apr_psprintf(msr->mp, "%s %s L\n", str1, str2);
}
if (text == NULL) return;
nbytes = strlen(text);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Audit Log: Writing %" APR_SIZE_T_FMT " bytes to primary concurrent index", nbytes);
}
apr_file_write_full(msr->txcfg->auditlog_fd, text, nbytes, &nbytes_written);
/* Write to the secondary audit log if we have one */
if (msr->txcfg->auditlog2_fd != NULL) {
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Audit Log: Writing %" APR_SIZE_T_FMT " bytes to secondary concurrent index", nbytes);
}
apr_file_write_full(msr->txcfg->auditlog2_fd, text, nbytes, &nbytes_written);
}
}
#endif
/*
* 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;
const apr_table_entry_t *telts_pattern = NULL;
char *str1 = NULL, *str2 = NULL, *text = NULL;
const msre_rule *rule = NULL, *next_rule = NULL;
apr_size_t nbytes, nbytes_written;
unsigned char md5hash[APR_MD5_DIGESTSIZE];
int was_limited = 0;
int present = 0;
int wrote_response_body = 0;
char *entry_filename, *entry_basename;
apr_status_t rc;
int i, limit, k, sanitized_partial, j;
char *buf = NULL, *pat = NULL;
msc_parm *mparm = NULL;
int arg_min, arg_max, sanitize_matched;
/* the boundary is used by both audit log types */
msr->new_auditlog_boundary = create_auditlog_boundary(msr->r);
/* Return silently if we don't have a request line. This
* means we will not be logging request timeouts.
*/
if (msr->request_line == NULL) {
msr_log(msr, 4, "Audit log: Skipping request whose request_line is null.");
return;
}
/* Also return silently if we don't have a file descriptor. */
if (msr->txcfg->auditlog_fd == NULL) {
msr_log(msr, 4, "Audit log: Skipping request since there is nowhere to write to.");
return;
}
if (msr->txcfg->auditlog_type != AUDITLOG_CONCURRENT) {
/* Serial logging - we already have an open file
* descriptor to write to.
*/
msr->new_auditlog_fd = msr->txcfg->auditlog_fd;
} else {
/* Concurrent logging - we need to create a brand
* new file for this request.
*/
apr_md5_init(&msr->new_auditlog_md5ctx);
msr->new_auditlog_filename = construct_auditlog_filename(msr->mp, msr->txid);
if (msr->new_auditlog_filename == NULL) return;
/* The audit log storage directory should be explicitly
* defined. But if it isn't try to write to the same
* directory where the index file is placed. Of course,
* it is *very* bad practice to allow the Apache user
* to write to the same directory where a root user is
* writing to but it's not us that's causing the problem
* and there isn't anything we can do about that.
*
* ENH Actually there is something we can do! We will make
* SecAuditStorageDir mandatory, ask the user to explicitly
* define the storage location *and* refuse to work if the
* index and the storage location are in the same folder.
*/
if (msr->txcfg->auditlog_storage_dir == NULL) {
entry_filename = file_dirname(msr->mp, msr->txcfg->auditlog_name);
}
else {
entry_filename = msr->txcfg->auditlog_storage_dir;
}
if (entry_filename == NULL) return;
entry_filename = apr_psprintf(msr->mp, "%s%s", entry_filename, msr->new_auditlog_filename);
if (entry_filename == NULL) return;
entry_basename = file_dirname(msr->mp, entry_filename);
if (entry_basename == NULL) return;
/* IMP1 Surely it would be more efficient to check the folders for
* the audit log repository base path in the configuration phase, to reduce
* the work we do on every request. Also, since our path depends on time,
* we could cache the time we last checked and don't check if we know
* the folder is there.
*/
rc = apr_dir_make_recursive(entry_basename, msr->txcfg->auditlog_dirperms, msr->mp);
if ((rc != APR_SUCCESS) && (rc != APR_EEXIST)) {
msr_log(msr, 1, "Audit log: Failed to create subdirectories: %s (%s)",
entry_basename, get_apr_error(msr->mp, rc));
return;
}
rc = apr_file_open(&msr->new_auditlog_fd, entry_filename,
APR_WRITE | APR_TRUNCATE | APR_CREATE | APR_BINARY | APR_FILE_NOCLEANUP,
msr->txcfg->auditlog_fileperms, msr->mp);
if (rc != APR_SUCCESS) {
msr_log(msr, 1, "Audit log: Failed to create file: %s (%s)",
entry_filename, get_apr_error(msr->mp, rc));
return;
}
}
/* Lock the mutex, but only if we are using serial format. */
if (msr->txcfg->auditlog_type != AUDITLOG_CONCURRENT) {
msr_global_mutex_lock(msr, msr->modsecurity->auditlog_lock, "Audit log");
}
/* AUDITLOG_PART_HEADER */
text = apr_psprintf(msr->mp, "--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_HEADER);
sec_auditlog_write(msr, text, strlen(text));
/* Format: time transaction_id remote_addr remote_port local_addr local_port */
text = apr_psprintf(msr->mp, "[%s] %s %s %u %s %u",
current_logtime(msr->mp), msr->txid, msr->remote_addr, msr->remote_port,
msr->local_addr, msr->local_port);
sec_auditlog_write(msr, text, strlen(text));
/* AUDITLOG_PART_REQUEST_HEADERS */
if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_REQUEST_HEADERS) != NULL) {
text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_REQUEST_HEADERS);
sec_auditlog_write(msr, text, strlen(text));
sanitize_request_line(msr);
sec_auditlog_write(msr, msr->request_line, strlen(msr->request_line));
sec_auditlog_write(msr, "\n", 1);
arr = apr_table_elts(msr->request_headers);
te = (apr_table_entry_t *)arr->elts;
tarr_pattern = apr_table_elts(msr->pattern_to_sanitize);
telts_pattern = (const apr_table_entry_t*)tarr_pattern->elts;
for (i = 0; i < arr->nelts; i++) {
sanitized_partial = 0;
sanitize_matched = 0;
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, te[i].val);
if (apr_table_get(msr->request_headers_to_sanitize, te[i].key) != NULL) {
buf = apr_psprintf(msr->mp, "%s",text+strlen(te[i].key)+2);
for ( k = 0; k < tarr_pattern->nelts; k++) {
if(strncmp(telts_pattern[k].key,te[i].key,strlen(te[i].key)) ==0 ) {
mparm = (msc_parm *)telts_pattern[k].val;
if(mparm->pad_1 == -1)
sanitize_matched = 1;
pat = strstr(buf,mparm->value);
if (pat != NULL) {
j = strlen(mparm->value);
arg_min = j;
arg_max = 1;
while((*pat != '\0')&&(j--)) {
if(arg_max > mparm->pad_2) {
int off = strlen(mparm->value) - arg_max;
int pos = mparm->pad_1-1;
if(off > pos) {
*pat = '*';
}
}
arg_max++;
arg_min--;
pat++;
}
sanitized_partial = 1;
}
}
}
if(sanitized_partial == 1 && sanitize_matched == 0) {
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, buf);
} else {
memset(text + strlen(te[i].key) + 2, '*', strlen(te[i].val));
}
}
sec_auditlog_write(msr, text, strlen(text));
}
}
/* AUDITLOG_PART_REQUEST_BODY */
/* Output this part of it was explicitly requested (C) or if it was the faked
* request body that was requested (I) but we have no reason to fake it (it's
* already in the correct format).
*/
if ( (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_REQUEST_BODY) != NULL)
|| ( (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_FAKE_REQUEST_BODY) != NULL)
&& (msr->mpd == NULL) ) )
{
if (msr->msc_reqbody_read) {
const apr_array_header_t *tarr;
const apr_table_entry_t *telts;
apr_array_header_t *sorted_args;
unsigned int offset = 0, last_offset = 0;
msc_arg *nextarg = NULL;
int sanitize = 0; /* IMP1 Use constants for "sanitize" values. */
char *my_error_msg = NULL;
sorted_args = apr_array_make(msr->mp, 25, sizeof(const msc_arg *));
/* First we need to sort the arguments that need to be
* sanitized in descending order (we are using a stack structure
* to store then so the order will be ascending when we start
* popping them out). This is because we will
* be reading the request body sequentially and must
* sanitize it as we go.
*/
for(;;) {
nextarg = NULL;
/* Find the next largest offset (excluding
* the ones we've used up already).
*/
tarr = apr_table_elts(msr->arguments_to_sanitize);
telts = (const apr_table_entry_t*)tarr->elts;
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, "JSON") != 0) )
continue;
if (last_offset == 0) { /* The first time we're here. */
if (arg->value_origin_offset > offset) {
offset = arg->value_origin_offset;
nextarg = arg;
}
} else { /* Not the first time. */
if ((arg->value_origin_offset > offset)
&&(arg->value_origin_offset < last_offset))
{
offset = arg->value_origin_offset;
nextarg = arg;
}
}
}
/* If we don't have the next argument that means
* we're done here.
*/
if (nextarg == NULL) break;
sanitize = 2; /* Means time to pop the next argument out. */
last_offset = offset;
offset = 0;
{ /* IMP1 Fix this ugly bit here. */
msc_arg **x = apr_array_push(sorted_args);
*x = nextarg;
}
}
/* Now start retrieving the body chunk by chunk and
* sanitize data in pieces.
*/
rc = modsecurity_request_body_retrieve_start(msr, &my_error_msg);
if (rc < 0) {
msr_log(msr, 1, "Audit log: %s", my_error_msg);
} else {
msc_data_chunk *chunk = NULL;
unsigned int chunk_offset = 0;
unsigned int sanitize_offset = 0;
unsigned int sanitize_length = 0;
text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_REQUEST_BODY);
sec_auditlog_write(msr, text, strlen(text));
for(;;) {
rc = modsecurity_request_body_retrieve(msr, &chunk, -1, &my_error_msg);
if (chunk != NULL) {
/* Anything greater than 1 means we have more data to sanitize. */
while (sanitize > 1) {
msc_arg **arg = NULL;
if (sanitize == 2) {
/* Get the next argument from the stack. */
arg = (msc_arg **)apr_array_pop(sorted_args);
if (arg == NULL) sanitize = 0; /* We're done sanitising. */
else {
/* Continue with sanitation to process the
* retrieved argument.
*/
sanitize = 1;
sanitize_offset = (*arg)->value_origin_offset;
sanitize_length = (*arg)->value_origin_len;
}
}
if (sanitize) {
/* Check if the data we want to sanitize is
* stored in the current chunk.
*/
if (chunk_offset + chunk->length > sanitize_offset) {
unsigned int soff; /* data offset within chunk */
unsigned int len; /* amount in this chunk to sanitize */
soff = sanitize_offset - chunk_offset;
if (soff + sanitize_length <= chunk->length) {
/* The entire argument resides in the current chunk. */
len = sanitize_length;
sanitize = 2; /* Get another parameter to sanitize. */
} else {
/* Some work to do here but we'll need to seek
* another chunk.
*/
len = chunk->length - soff;
sanitize_offset += len;
sanitize_length -= len;
sanitize = 1; /* It's OK to go to the next chunk. */
}
/* Yes, we actually write over the original data.
* We shouldn't be needing it any more.
*/
if (soff + len <= chunk->length) { /* double check */
memset((char *)chunk->data + soff, '*', len);
}
}
}
}
/* Write the sanitized chunk to the log
* and advance to the next chunk. */
sec_auditlog_write(msr, chunk->data, chunk->length);
chunk_offset += chunk->length;
}
if (rc <= 0) {
break;
}
}
if (rc < 0) {
msr_log(msr, 1, "Audit log: %s", my_error_msg);
}
modsecurity_request_body_retrieve_end(msr);
}
}
}
/* AUDITLOG_PART_FAKE_REQUEST_BODY */
if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_FAKE_REQUEST_BODY) != NULL) {
if ((msr->msc_reqbody_read)&&(msr->mpd != NULL)) {
char *buffer = NULL;
buffer = multipart_reconstruct_urlencoded_body_sanitize(msr);
if (buffer == NULL) {
msr_log(msr, 1, "Audit log: Failed to reconstruct request body.");
} else {
text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_FAKE_REQUEST_BODY);
sec_auditlog_write(msr, text, strlen(text));
sec_auditlog_write(msr, buffer, strlen(buffer));
}
}
}
/* AUDITLOG_PART_A_RESPONSE_HEADERS */
if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_A_RESPONSE_HEADERS) != NULL) {
text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_A_RESPONSE_HEADERS);
sec_auditlog_write(msr, text, strlen(text));
/* There are no response headers (or the status line) in HTTP 0.9 */
if (msr->response_headers_sent) {
if (msr->status_line != NULL && msr->status_line[0] != '\0') {
text = apr_psprintf(msr->mp, "%s %s\n", msr->response_protocol,
msr->status_line);
} else {
text = apr_psprintf(msr->mp, "%s %u\n", msr->response_protocol,
msr->response_status);
}
sec_auditlog_write(msr, text, strlen(text));
/* Output headers */
arr = apr_table_elts(msr->response_headers);
te = (apr_table_entry_t *)arr->elts;
tarr_pattern = apr_table_elts(msr->pattern_to_sanitize);
telts_pattern = (const apr_table_entry_t*)tarr_pattern->elts;
for (i = 0; i < arr->nelts; i++) {
sanitized_partial = 0;
sanitize_matched = 0;
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, te[i].val);
if (apr_table_get(msr->response_headers_to_sanitize, te[i].key) != NULL) {
buf = apr_psprintf(msr->mp, "%s",text+strlen(te[i].key)+2);
for ( k = 0; k < tarr_pattern->nelts; k++) {
if(strncmp(telts_pattern[k].key,te[i].key,strlen(te[i].key)) ==0 ) {
mparm = (msc_parm *)telts_pattern[k].val;
if(mparm->pad_1 == -1)
sanitize_matched = 1;
pat = strstr(buf,mparm->value);
if (pat != NULL) {
j = strlen(mparm->value);
arg_min = j;
arg_max = 1;
while((*pat != '\0')&&(j--)) {
if(arg_max > mparm->pad_2) {
int off = strlen(mparm->value) - arg_max;
int pos = mparm->pad_1-1;
if(off > pos) {
*pat = '*';
}
}
arg_max++;
arg_min--;
pat++;
}
sanitized_partial = 1;
}
}
}
if(sanitized_partial == 1 && sanitize_matched == 0) {
text = apr_psprintf(msr->mp, "%s: %s\n", te[i].key, buf);
} else {
memset(text + strlen(te[i].key) + 2, '*', strlen(te[i].val));
}
}
sec_auditlog_write(msr, text, strlen(text));
}
}
}
apr_table_clear(msr->pattern_to_sanitize);
/* AUDITLOG_PART_RESPONSE_BODY */
if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_RESPONSE_BODY) != NULL) {
if (msr->resbody_data != NULL) {
text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_RESPONSE_BODY);
sec_auditlog_write(msr, text, strlen(text));
sec_auditlog_write(msr, msr->resbody_data, msr->resbody_length);
wrote_response_body = 1;
}
}
/* AUDITLOG_PART_TRAILER */
if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_TRAILER) != NULL) {
apr_time_t now = apr_time_now();
text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_TRAILER);
sec_auditlog_write(msr, text, strlen(text));
/* Messages */
for(i = 0; i < msr->alerts->nelts; i++) {
text = apr_psprintf(msr->mp, "Message: %s\n", ((char **)msr->alerts->elts)[i]);
sec_auditlog_write(msr, text, strlen(text));
}
/* Apache error messages */
for(i = 0; i < msr->error_messages->nelts; i++) {
error_message_t *em = (((error_message_t **)msr->error_messages->elts)[i]);
text = apr_psprintf(msr->mp, "Apache-Error: %s\n",
format_error_log_message(msr->mp, em));
sec_auditlog_write(msr, text, strlen(text));
}
/* Action */
if (msr->was_intercepted) {
text = apr_psprintf(msr->mp, "Action: Intercepted (phase %d)\n", msr->intercept_phase);
sec_auditlog_write(msr, text, strlen(text));
}
/* Apache-Handler */
#ifdef LOG_NO_HANDLER
if (msr->txcfg->debuglog_level >= 9)
#endif
if (msr->r->handler != NULL) {
text = apr_psprintf(msr->mp, "Apache-Handler: %s\n", msr->r->handler);
sec_auditlog_write(msr, text, strlen(text));
}
/* Stopwatch; left in for compatibility reasons */
#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",
msr->request_time, (now - msr->request_time));
sec_auditlog_write(msr, text, strlen(text));
/* Stopwatch2 */
{
char *perf_all = format_all_performance_variables(msr, msr->mp);
text = apr_psprintf(msr->mp, "Stopwatch2: %" APR_TIME_T_FMT " %" APR_TIME_T_FMT
"; %s\n", msr->request_time, (now - msr->request_time), perf_all);
sec_auditlog_write(msr, text, strlen(text));
}
#ifdef LOG_NO_STOPWATCH
}
#endif
/* Our response body does not contain chunks */
/* ENH Only write this when the output was chunked. */
/* ENH Add info when request body was decompressed, dechunked too. */
#ifdef LOG_NO_DECHUNK
if (msr->txcfg->debuglog_level >= 9)
#endif
if (wrote_response_body) {
text = apr_psprintf(msr->mp, "Response-Body-Transformed: Dechunked\n");
sec_auditlog_write(msr, text, strlen(text));
}
#ifdef LOG_NO_SERVER_CONTEXT
if (msr->txcfg->debuglog_level >= 9) {
#endif
sec_auditlog_write_producer_header(msr);
/* Server */
if (msr->server_software != NULL) {
text = apr_psprintf(msr->mp, "Server: %s\n", msr->server_software);
sec_auditlog_write(msr, text, strlen(text));
}
/* Sanitised arguments */
{
const apr_array_header_t *tarr;
const apr_table_entry_t *telts;
tarr = apr_table_elts(msr->arguments_to_sanitize);
telts = (const apr_table_entry_t*)tarr->elts;
if (tarr->nelts > 0) {
text = apr_psprintf(msr->mp, "Sanitised-Args: ");
sec_auditlog_write(msr, text, strlen(text));
}
for(i = 0; i < tarr->nelts; i++) {
msc_arg *arg = (msc_arg *)telts[i].val;
text = apr_psprintf(msr->mp, "%s\"%s\"%s", ((i == 0) ? "" : ", "),
log_escape(msr->mp, arg->name), ((i == (tarr->nelts - 1)) ? ".\n" : ""));
sec_auditlog_write(msr, text, strlen(text));
}
}
/* Sanitised request headers */
{
const apr_array_header_t *tarr;
const apr_table_entry_t *telts;
tarr = apr_table_elts(msr->request_headers_to_sanitize);
telts = (const apr_table_entry_t*)tarr->elts;
if (tarr->nelts > 0) {
text = apr_psprintf(msr->mp, "Sanitised-Request-Headers: ");
sec_auditlog_write(msr, text, strlen(text));
}
for(i = 0; i < tarr->nelts; i++) {
text = apr_psprintf(msr->mp, "%s\"%s\"%s", ((i == 0) ? "" : ", "),
log_escape(msr->mp, telts[i].key), ((i == (tarr->nelts - 1)) ? ".\n" : ""));
sec_auditlog_write(msr, text, strlen(text));
}
}
/* Sanitised response headers */
{
const apr_array_header_t *tarr;
const apr_table_entry_t *telts;
tarr = apr_table_elts(msr->response_headers_to_sanitize);
telts = (const apr_table_entry_t*)tarr->elts;
if (tarr->nelts > 0) {
text = apr_psprintf(msr->mp, "Sanitised-Response-Headers: ");
sec_auditlog_write(msr, text, strlen(text));
}
for(i = 0; i < tarr->nelts; i++) {
text = apr_psprintf(msr->mp, "%s\"%s\"%s", ((i == 0) ? "" : ", "),
log_escape(msr->mp, telts[i].key), ((i == (tarr->nelts - 1)) ? ".\n" : ""));
sec_auditlog_write(msr, text, strlen(text));
}
}
#ifdef LOG_NO_SERVER_CONTEXT
}
#endif
/* Web application info. */
if ( ((msr->txcfg->webappid != NULL)&&(strcmp(msr->txcfg->webappid, "default") != 0))
|| (msr->sessionid != NULL) || (msr->userid != NULL))
{
text = apr_psprintf(msr->mp, "WebApp-Info: \"%s\" \"%s\" \"%s\"\n",
msr->txcfg->webappid == NULL ? "-" : log_escape(msr->mp, msr->txcfg->webappid),
msr->sessionid == NULL ? "-" : log_escape(msr->mp, msr->sessionid),
msr->userid == NULL ? "-" : log_escape(msr->mp, msr->userid));
sec_auditlog_write(msr, text, strlen(text));
}
if ( ((msr->txcfg->sensor_id != NULL)&&(strcmp(msr->txcfg->sensor_id, "default") != 0)))
{
text = apr_psprintf(msr->mp, "Sensor-Id: \"%s\"\n",
msr->txcfg->sensor_id == NULL ? "-" : log_escape(msr->mp, msr->txcfg->sensor_id)),
sec_auditlog_write(msr, text, strlen(text));
}
if (msr->txcfg->is_enabled > 0) {
text = apr_psprintf(msr->mp, "Engine-Mode: \"%s\"\n",
msr->txcfg->is_enabled == 1 ? "DETECTION_ONLY" : "ENABLED"),
sec_auditlog_write(msr, text, strlen(text));
}
/* Rule performance time */
if(msr->txcfg->max_rule_time > 0) {
const apr_array_header_t *tarr;
const apr_table_entry_t *telts;
tarr = apr_table_elts(msr->perf_rules);
telts = (const apr_table_entry_t*)tarr->elts;
if (tarr->nelts > 0) {
text = apr_psprintf(msr->mp, "Rules-Performance-Info: ");
sec_auditlog_write(msr, text, strlen(text));
}
for(i = 0; i < tarr->nelts; i++) {
text = apr_psprintf(msr->mp, "%s\"%s=%s\"%s", ((i == 0) ? "" : ", "),
log_escape(msr->mp, telts[i].key), log_escape(msr->mp, telts[i].val), ((i == (tarr->nelts - 1)) ? ".\n" : ""));
sec_auditlog_write(msr, text, strlen(text));
}
}
}
/* AUDITLOG_PART_UPLOADS */
if ((strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_UPLOADS) != NULL) && (msr->mpd != NULL)) {
multipart_part **parts = NULL;
unsigned int total_size = 0;
int cfiles = 0;
text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_UPLOADS);
sec_auditlog_write(msr, text, strlen(text));
parts = (multipart_part **)msr->mpd->parts->elts;
for(cfiles = 0; cfiles < msr->mpd->parts->nelts; cfiles++) {
if (parts[cfiles]->type == MULTIPART_FILE) {
if(parts[cfiles]->filename != NULL) {
text = apr_psprintf(msr->mp, "%d,%u,\"%s\",\"%s\"\n", cfiles+1, parts[cfiles]->tmp_file_size, log_escape(msr->mp, parts[cfiles]->filename), log_escape(msr->mp, parts[cfiles]->content_type ? parts[cfiles]->content_type : ""));
sec_auditlog_write(msr, text, strlen(text));
total_size += parts[cfiles]->tmp_file_size;
}
}
}
text = apr_psprintf(msr->mp, "Total,%u\n", total_size);
sec_auditlog_write(msr, text, strlen(text));
}
/* AUDITLOG_PART_MATCHEDRULES */
if (strchr(msr->txcfg->auditlog_parts, AUDITLOG_PART_MATCHEDRULES) != NULL) {
text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_MATCHEDRULES);
sec_auditlog_write(msr, text, strlen(text));
/* Matched Rules */
for(i = 0; i < msr->matched_rules->nelts; i++) {
rule = ((msre_rule **)msr->matched_rules->elts)[i];
if ((rule != NULL) && (rule->actionset != NULL) && rule->actionset->is_chained && (rule->chain_starter == NULL)) {
text = apr_psprintf(msr->mp, "%s\n", rule->unparsed);
sec_auditlog_write(msr, text, strlen(text));
do {
if (rule->ruleset != NULL) {
next_rule = return_chained_rule(rule,msr);
if (next_rule != NULL) {
present = chained_is_matched(msr,next_rule);
if (present == 0) {
text = apr_psprintf(msr->mp, "#%s\n",next_rule->unparsed);
} else {
text = apr_psprintf(msr->mp, "%s\n",next_rule->unparsed);
i++;
}
sec_auditlog_write(msr, text, strlen(text));
}
}
rule = next_rule;
} while (rule != NULL && rule->actionset != NULL && rule->actionset->is_chained);
text = apr_psprintf(msr->mp, "\n");
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", rule->unparsed);
sec_auditlog_write(msr, text, strlen(text));
}
}
}
}
/* AUDITLOG_PART_ENDMARKER */
text = apr_psprintf(msr->mp, "\n--%s-%c--\n", msr->new_auditlog_boundary, AUDITLOG_PART_ENDMARKER);
sec_auditlog_write(msr, text, strlen(text));
/* Return here if we were writing to a serial log
* as it does not need an index file.
*/
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. */
msr_global_mutex_unlock(msr, msr->modsecurity->auditlog_lock, "Audit log");
return;
}
/* From here on only concurrent-style processing. */
/* File handle might already be closed after write failure. */
if (msr->new_auditlog_fd) {
apr_file_close(msr->new_auditlog_fd);
}
/* Write an entry to the index file */
/* Calculate hash of the entry. */
apr_md5_final(md5hash, &msr->new_auditlog_md5ctx);
str2 = apr_psprintf(msr->mp, "%s %d %d md5:%s", msr->new_auditlog_filename, 0,
msr->new_auditlog_size, bytes2hex(msr->mp, md5hash, 16));
if (str2 == NULL) return;
/* We do not want the index line to be longer than 3980 bytes. */
limit = 3980;
was_limited = 0;
/* If we are logging to a pipe we need to observe and
* obey the pipe atomic write limit - PIPE_BUF. For
* more details see the discussion in sec_guardian_logger code.
*/
if (msr->txcfg->auditlog_name[0] == '|') {
if (PIPE_BUF < limit) {
limit = PIPE_BUF;
}
}
limit = limit - strlen(str2) - 5;
if (limit <= 0) {
msr_log(msr, 1, "Audit Log: Atomic PIPE write buffer too small: %d", PIPE_BUF);
return;
}
str1 = construct_log_vcombinedus_limited(msr, limit, &was_limited);
if (str1 == NULL) return;
if (was_limited == 0) {
text = apr_psprintf(msr->mp, "%s %s \n", str1, str2);
} else {
text = apr_psprintf(msr->mp, "%s %s L\n", str1, str2);
}
if (text == NULL) return;
nbytes = strlen(text);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Audit Log: Writing %" APR_SIZE_T_FMT " bytes to primary concurrent index", nbytes);
}
apr_file_write_full(msr->txcfg->auditlog_fd, text, nbytes, &nbytes_written);
/* Write to the secondary audit log if we have one */
if (msr->txcfg->auditlog2_fd != NULL) {
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Audit Log: Writing %" APR_SIZE_T_FMT " bytes to secondary concurrent index", nbytes);
}
apr_file_write_full(msr->txcfg->auditlog2_fd, text, nbytes, &nbytes_written);
}
}
/*
* Handler for audit log writers.
*/
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 {
#endif
sec_audit_logger_native(msr);
#ifdef WITH_YAJL
}
#endif
}
modsecurity-apache-2.9.13/apache2/msc_logging.h 0000664 0000000 0000000 00000003505 15174655764 0021367 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#ifndef _MSC_LOGGING_H_
#define _MSC_LOGGING_H_
#define AUDITLOG_OFF 0
#define AUDITLOG_ON 1
#define AUDITLOG_RELEVANT 2
#define AUDITLOG_SERIAL 0
#define AUDITLOG_CONCURRENT 1
#ifdef WITH_YAJL
#define AUDITLOGFORMAT_JSON 0
#define AUDITLOGFORMAT_NATIVE 1
#endif
#define AUDITLOG_PART_FIRST 'A'
#define AUDITLOG_PART_HEADER 'A'
#define AUDITLOG_PART_REQUEST_HEADERS 'B'
#define AUDITLOG_PART_REQUEST_BODY 'C'
#define AUDITLOG_PART_RESPONSE_HEADERS 'D'
#define AUDITLOG_PART_RESPONSE_BODY 'E'
#define AUDITLOG_PART_A_RESPONSE_HEADERS 'F'
#define AUDITLOG_PART_A_RESPONSE_BODY 'G'
#define AUDITLOG_PART_TRAILER 'H'
#define AUDITLOG_PART_FAKE_REQUEST_BODY 'I'
#define AUDITLOG_PART_UPLOADS 'J'
#define AUDITLOG_PART_MATCHEDRULES 'K'
#define AUDITLOG_PART_LAST 'K'
#define AUDITLOG_PART_ENDMARKER 'Z'
#include "modsecurity.h"
#include "apr_pools.h"
int DSOLOCAL is_valid_parts_specification(char *p);
char DSOLOCAL *construct_log_vcombinedus_limited(modsec_rec *msr, int _limit, int *was_limited);
void DSOLOCAL sec_audit_logger(modsec_rec *msr);
#endif
modsecurity-apache-2.9.13/apache2/msc_logging_json.h 0000664 0000000 0000000 00000000627 15174655764 0022422 0 ustar 00root root 0000000 0000000 #include
#include
#define yajl_string(g, s) yajl_gen_string(g, (const unsigned char *)s, strlen(s))
#define yajl_kv_null(g, k) yajl_string(g, k); yajl_gen_null(g)
#define yajl_kv_int(g, k, v) yajl_string(g, k); yajl_gen_integer(g, v)
#define yajl_kv_string(g, k, v) yajl_string(g, k); yajl_string(g, v)
#define yajl_kv_bool(g, k, v) yajl_string(g, k); yajl_gen_bool(g, v)
modsecurity-apache-2.9.13/apache2/msc_lua.c 0000664 0000000 0000000 00000032333 15174655764 0020516 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#if defined(WITH_LUA)
#include "msc_lua.h"
#include "apr_strings.h"
typedef struct {
apr_array_header_t *parts;
apr_pool_t *pool;
} msc_lua_dumpw_t;
typedef struct {
msc_script *script;
int index;
} msc_lua_dumpr_t;
/**
*
*/
static const char* dump_reader(lua_State* L, void* user_data, size_t* size) {
msc_lua_dumpr_t *dumpr = (msc_lua_dumpr_t *)user_data;
msc_script_part *part;
/* Do we have more chunks to return? */
if (dumpr->index == dumpr->script->parts->nelts) {
return NULL;
}
/* Get one chunk. */
part = ((msc_script_part **)dumpr->script->parts->elts)[dumpr->index];
*size = part->len;
dumpr->index++;
return part->data;
}
/**
*
*/
static int dump_writer(lua_State *L, const void* data, size_t len, void* user_data) {
msc_lua_dumpw_t *dump = (msc_lua_dumpw_t *)user_data;
msc_script_part *part;
void *part_data;
/* Allocate new part, copy the data into it. */
part_data = apr_palloc(dump->pool, len);
memcpy(part_data, data, len);
part = apr_palloc(dump->pool, sizeof(msc_script_part));
part->data = part_data;
part->len = len;
/* Then add it to the list of parsts. */
*(const msc_script_part **)apr_array_push(dump->parts) = part;
return 0;
}
/**
*
*/
static int lua_restore(lua_State *L, msc_script *script) {
msc_lua_dumpr_t dumpr;
dumpr.script = script;
dumpr.index = 0;
#if LUA_VERSION_NUM > 501
return lua_load(L, dump_reader, &dumpr, script->name, NULL);
#else
return lua_load(L, dump_reader, &dumpr, script->name);
#endif
}
/**
*
*/
char *lua_compile(msc_script **script, const char *filename, apr_pool_t *pool) {
lua_State *L = NULL;
msc_lua_dumpw_t dump;
/* Initialise state. */
#if LUA_VERSION_NUM > 501
L = luaL_newstate();
#else
L = lua_open();
#endif
luaL_openlibs(L);
/* Find script. */
if (luaL_loadfile(L, filename)) {
return apr_psprintf(pool, "ModSecurity: Failed to compile script %s: %s",
filename, lua_tostring(L, -1));
}
/* Dump the script into binary form. */
dump.pool = pool;
dump.parts = apr_array_make(pool, 128, sizeof(msc_script_part *));
#if LUA_VERSION_NUM >= 503
lua_dump(L, dump_writer, &dump, 0);
#else
lua_dump(L, dump_writer, &dump);
#endif
(*script) = apr_pcalloc(pool, sizeof(msc_script));
(*script)->name = filename;
(*script)->parts = dump.parts;
/* Destroy state. */
lua_close(L);
return NULL;
}
/**
*
*/
static int l_log(lua_State *L) {
modsec_rec *msr = NULL;
const char *text;
int level;
/* Retrieve parameters. */
level = luaL_checknumber(L, 1);
text = luaL_checkstring(L, 2);
/* Retrieve msr. */
lua_getglobal(L, "__msr");
msr = (modsec_rec *)lua_topointer(L, -1);
/* Log message. */
if (msr != NULL) {
msr_log(msr, level, "%s", text);
}
return 0;
}
/**
*
*/
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;
tfn_arr = apr_array_make(mp, 25, sizeof(msre_tfn_metadata *));
if (tfn_arr == NULL) return NULL;
/* ENH: Why is this userdata and not none/nil when parameter not given? */
if (lua_isuserdata(L, idx) || lua_isnoneornil(L, idx)) { /* No second parameter */
return tfn_arr;
} else if (lua_istable(L, idx)) { /* Is the second parameter an array? */
#if LUA_VERSION_NUM > 501
int i, n = lua_rawlen(L, idx);
#else
int i, n = lua_objlen(L, idx);
#endif
for(i = 1; i <= n; i++) {
lua_rawgeti(L, idx, i);
name = (char *)luaL_checkstring(L, -1);
/* A "none" means start over */
if (strcmp("none", name) == 0) {
tfn_arr->nelts = 0;
continue;
}
tfn = msre_engine_tfn_resolve(msr->modsecurity->msre, name);
if (tfn == NULL) {
msr_log(msr, 1, "SecRuleScript: Invalid transformation function: %s", name);
} else {
*(msre_tfn_metadata **)apr_array_push(tfn_arr) = tfn;
}
}
} else if (lua_isstring(L, idx)) { /* The second parameter may be a simple string? */
name = (char *)luaL_checkstring(L, idx);
/* A "none" means start over */
if (strcmp("none", name) == 0) {
tfn_arr->nelts = 0;
}
else {
tfn = msre_engine_tfn_resolve(msr->modsecurity->msre, name);
if (tfn == NULL) {
msr_log(msr, 1, "SecRuleScript: Invalid transformation function: %s", name);
} else {
*(msre_tfn_metadata **)apr_array_push(tfn_arr) = tfn;
}
}
} else {
msr_log(msr, 1, "SecRuleScript: Transformation parameter must be a transformation name or array of transformation names, but found \"%s\" (type %d).", lua_typename(L, idx), lua_type(L, idx));
return NULL;
}
return tfn_arr;
}
/**
*
*/
static int l_getvar(lua_State *L) {
char *varname = NULL, *param = NULL;
modsec_rec *msr = NULL;
msre_rule *rule = NULL;
char *my_error_msg = NULL;
char *p1 = NULL;
apr_array_header_t *tfn_arr = NULL;
msre_var *vx = NULL;
msre_var *var;
/* Retrieve parameters. */
p1 = (char *)luaL_checkstring(L, 1);
/* Retrieve msr. */
lua_getglobal(L, "__msr");
msr = (modsec_rec *)lua_topointer(L, -1);
/* Retrieve rule. */
lua_getglobal(L, "__rule");
rule = (msre_rule *)lua_topointer(L, -1);
/* Extract the variable name and its parameter from the script. */
varname = apr_pstrdup(msr->msc_rule_mptmp, p1);
param = strchr(varname, '.');
if (param != NULL) {
*param = '\0';
param++;
}
/* Resolve variable. */
var = msre_create_var_ex(msr->msc_rule_mptmp, msr->modsecurity->msre,
varname, param, msr, &my_error_msg);
if (var == NULL) {
msr_log(msr, 1, "%s", my_error_msg);
lua_pushnil(L);
return 0;
}
/* Resolve transformation functions. */
tfn_arr = resolve_tfns(L, 2, msr, msr->msc_rule_mptmp);
/* Generate variable. */
vx = generate_single_var(msr, var, tfn_arr, rule, msr->msc_rule_mptmp);
if (vx == NULL) {
lua_pushnil(L);
return 0;
}
/* Return variable value. */
lua_pushlstring(L, vx->value, vx->value_len);
return 1;
}
/**
*
*/
static int l_getvars(lua_State *L) {
const apr_array_header_t *tarr;
const apr_table_entry_t *telts;
apr_table_t *vartable = NULL;
apr_array_header_t *tfn_arr = NULL;
char *varname = NULL, *param = NULL;
modsec_rec *msr = NULL;
msre_rule *rule = NULL;
msre_var *vartemplate = NULL;
char *my_error_msg = NULL;
char *p1 = NULL;
int i;
/* Retrieve parameters. */
p1 = (char *)luaL_checkstring(L, 1);
/* Retrieve msr. */
lua_getglobal(L, "__msr");
msr = (modsec_rec *)lua_topointer(L, -1);
/* Retrieve rule. */
lua_getglobal(L, "__rule");
rule = (msre_rule *)lua_topointer(L, -1);
/* Extract the variable name and its parameter from the script. */
varname = apr_pstrdup(msr->msc_rule_mptmp, p1);
param = strchr(varname, '.');
if (param != NULL) {
*param = '\0';
param++;
}
/* Resolve transformation functions. */
tfn_arr = resolve_tfns(L, 2, msr, msr->msc_rule_mptmp);
lua_newtable(L);
/* Resolve variable. */
vartemplate = msre_create_var_ex(msr->msc_rule_mptmp, msr->modsecurity->msre,
varname, param, msr, &my_error_msg);
if (vartemplate == NULL) {
msr_log(msr, 1, "%s", my_error_msg);
/* Returning empty table. */
return 1;
}
vartable = generate_multi_var(msr, vartemplate, tfn_arr, rule, msr->msc_rule_mptmp);
tarr = apr_table_elts(vartable);
telts = (const apr_table_entry_t*)tarr->elts;
for (i = 0; i < tarr->nelts; i++) {
msre_var *var = (msre_var *)telts[i].val;
lua_pushnumber(L, i + 1); /* Index is not zero-based. */
lua_newtable(L); /* Per-parameter table. */
lua_pushstring(L, "name");
lua_pushlstring(L, var->name, strlen(var->name));
lua_settable(L, -3);
lua_pushstring(L, "value");
lua_pushlstring(L, var->value, var->value_len);
lua_settable(L, -3);
lua_settable(L, -3); /* Push one parameter into the results table. */
}
return 1;
}
/*
* \brief New setvar function for Lua API. Users can put back
* data in modsecurity core via new variables
*
* \param L Pointer to Lua state
*
* \retval -1 On failure
* \retval 0 On Collection failure
* \retval 1 On Success
*/
static int l_setvar(lua_State *L) {
modsec_rec *msr = NULL;
msre_rule *rule = NULL;
const char *var_value = NULL;
const char *var_name = NULL;
int nargs = lua_gettop(L);
char *chr = NULL;
lua_getglobal(L, "__msr");
msr = (modsec_rec *)lua_topointer(L, -1);
lua_getglobal(L, "__rule");
rule = (msre_rule *)lua_topointer(L, -1);
if(nargs != 2) {
msr_log(msr, 8, "m.setvar: Failed m.setvar funtion must has 2 arguments");
return -1;
}
var_value = luaL_checkstring (L, 2);
var_name = luaL_checkstring (L, 1);
lua_pop(L,2);
if(var_value == NULL || var_name == NULL)
return -1;
chr = strchr((char *)var_name,0x2e);
if(chr == NULL) {
msr_log(msr, 8, "m.setvar: Must specify a collection using dot character - ie m.setvar(tx.myvar,mydata)");
return -1;
}
return msre_action_setvar_execute(msr,msr->msc_rule_mptmp,rule,(char *)var_name,(char *)var_value);
}
static const struct luaL_Reg mylib[] = {
{ "log", l_log },
{ "getvar", l_getvar },
{ "getvars", l_getvars },
{ "setvar", l_setvar },
{ NULL, NULL }
};
/**
*
*/
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;
*error_msg = NULL;
if (msr->txcfg->debuglog_level >= 8) {
msr_log(msr, 8, "Lua: Executing script: %s", script->name);
}
time_before = apr_time_now();
#ifdef CACHE_LUA
L = msr->L;
rc = lua_gettop(L);
if(rc)
lua_pop(L, rc);
#else
/* Create new state. */
#if LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503 || LUA_VERSION_NUM == 504 || LUA_VERSION_NUM == 505 || 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, 5.3, 5.4 or 5.5.
#endif
luaL_openlibs(L);
#endif
if(L == NULL)
return -1;
luaL_newmetatable(L, "luaL_msc");
lua_newtable(L);
/* Associate msr with the state. */
lua_pushlightuserdata(L, (void *)msr);
lua_setglobal(L, "__msr");
/* Associate rule with the state. */
if (rule != NULL) {
lua_pushlightuserdata(L, (void *)rule);
lua_setglobal(L, "__rule");
}
/* 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 || LUA_VERSION_NUM == 504 || LUA_VERSION_NUM == 505
luaL_setfuncs(L, mylib, 0);
#else
#error We are only tested under Lua 5.0, 5.1, 5.2, 5.3, 5.4 or 5.5.
#endif
lua_setglobal(L, "m");
rc = lua_restore(L, script);
if (rc) {
*error_msg = apr_psprintf(msr->mp, "Lua: Failed to restore script with %i.", rc);
return -1;
}
/* Execute the chunk so that the functions are defined. */
lua_pcall(L, 0, 0, 0);
/* Execute main() */
lua_getglobal(L, "main");
/* Put the parameter on the stack. */
if (param != NULL) {
lua_pushlstring(L, param, strlen(param));
}
if (lua_pcall(L, ((param != NULL) ? 1 : 0), 1, 0)) {
*error_msg = apr_psprintf(msr->mp, "Lua: Script execution failed: %s", lua_tostring(L, -1));
if (msr->txcfg->debuglog_level >= 8) {
msr_log(msr, 8, "Lua: Script execution failed: %s", lua_tostring(L, -1));
}
return -1;
}
/* Get the response from the script. */
*error_msg = (char *)lua_tostring(L, -1);
if (*error_msg != NULL) {
*error_msg = apr_pstrdup(msr->mp, *error_msg);
}
/* Destroy state. */
lua_pop(L, 1);
#ifndef CACHE_LUA
lua_close(L);
#endif
/* Returns status code to caller. */
if (msr->txcfg->debuglog_level >= 8) {
msr_log(msr, 8, "Lua: Script completed in %" APR_TIME_T_FMT " usec, returning: %s.",
(apr_time_now() - time_before), *error_msg);
}
return ((*error_msg != NULL) ? RULE_MATCH : RULE_NO_MATCH);
}
#endif /* WITH_LUA */
modsecurity-apache-2.9.13/apache2/msc_lua.h 0000664 0000000 0000000 00000002421 15174655764 0020516 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#if defined(WITH_LUA)
#ifndef _MSC_LUA_H_
#define _MSC_LUA_H_
typedef struct msc_script msc_script;
typedef struct msc_script_part msc_script_part;
#include
#include
#include
#include "apr_general.h"
#include "apr_tables.h"
#include "modsecurity.h"
struct msc_script {
const char *name;
apr_array_header_t *parts;
};
struct msc_script_part {
const void *data;
size_t len;
};
char DSOLOCAL *lua_compile(msc_script **script, const char *filename, apr_pool_t *pool);
int DSOLOCAL lua_execute(msc_script *script, char *param, modsec_rec *msr, msre_rule *rule, char **error_msg);
apr_status_t DSOLOCAL msre_action_setvar_execute(modsec_rec *r, apr_pool_t *, msre_rule *, char *, char *);
#endif
#endif /* WITH_LUA */
modsecurity-apache-2.9.13/apache2/msc_multipart.c 0000664 0000000 0000000 00000155062 15174655764 0021763 0 ustar 00root root 0000000 0000000 /*
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
* 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
*
* 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 "modsecurity.h"
#include
#include
#include "msc_multipart.h"
#include "msc_util.h"
#include "msc_parsers.h"
void validate_quotes(modsec_rec *msr, char *data, char quote) {
assert(msr != NULL);
int i, len;
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++) {
if(data[i] == '\'') {
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Multipart: Invalid quoting detected: %s length %d bytes",
log_escape_nq(msr->mp, data), len);
}
msr->mpd->flag_invalid_quoting = 1;
}
}
}
#if 0
static char *multipart_construct_filename(modsec_rec *msr) {
char c, *p, *q = msr->mpd->mpp->filename;
char *filename;
/* find the last backward slash and consider the
* filename to be only what's right from it
*/
p = strrchr(q, '\\');
if (p != NULL) q = p + 1;
/* do the same for the forward slash */
p = strrchr(q, '/');
if (p != NULL) q = p + 1;
/* allow letters, digits and dots, replace
* everything else with underscores
*/
p = filename = apr_pstrdup(msr->mp, q);
while((c = *p) != 0) {
if (!( isalnum(c) || (c == '.') )) *p = '_';
p++;
}
return filename;
}
#endif
/**
*
*/
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 */
if (strncmp(c_d_value, "form-data", 9) != 0) {
return -1;
}
/* see if there are any other parts to parse */
p = c_d_value + 9;
while((*p == '\t') || (*p == ' ')) p++;
if (*p == '\0') return 1; /* this is OK */
if (*p != ';') return -2;
p++;
/* parse the appended parts */
while(*p != '\0') {
char *name = NULL, *value = NULL, *start = NULL;
/* go over the whitespace */
while((*p == '\t') || (*p == ' ')) p++;
if (*p == '\0') return -3;
start = p;
while((*p != '\0') && (*p != '=') && (*p != '\t') && (*p != ' ')) p++;
if (*p == '\0') return -4;
name = apr_pstrmemdup(msr->mp, start, (p - start));
while((*p == '\t') || (*p == ' ')) p++;
if (*p == '\0') return -5;
if (*p != '=') return -13;
p++;
while((*p == '\t') || (*p == ' ')) p++;
if (*p == '\0') return -6;
/* Accept both quotes as some backends will accept them, but
* technically "'" is invalid and so flag_invalid_quoting is
* set so the user can deal with it in the rules if they so wish.
*/
char quote = '\0';
if ((*p == '"') || (*p == '\'')) {
/* quoted */
quote = *p; // remember which quote character was used for the value
if (quote == '\'') {
msr->mpd->flag_invalid_quoting = 1;
}
p++;
if (*p == '\0') return -7;
start = p;
value = apr_pstrdup(msr->mp, p);
t = value;
while(*p != '\0') {
if (*p == '\\') {
if (*(p + 1) == '\0') {
/* improper escaping */
return -8;
}
/* only quote and \ can be escaped */
if ((*(p + 1) == quote) || (*(p + 1) == '\\')) {
p++;
}
else {
/* improper escaping */
/* We allow for now because IE sends
* improperly escaped content and there's
* nothing we can do about it.
*
* return -9;
*/
}
}
else if (*p == quote) {
*t = '\0';
break;
}
*(t++) = *(p++);
}
if (*p == '\0') return -10;
p++; /* go over the quote at the end */
} else {
/* not quoted */
start = p;
while((*p != '\0') && (is_token_char(*p))) p++;
value = apr_pstrmemdup(msr->mp, start, (p - start));
}
/* evaluate part */
if (strcmp(name, "name") == 0) {
validate_quotes(msr, value, quote);
msr->multipart_name = apr_pstrdup(msr->mp, value);
if (msr->mpd->mpp->name != NULL) {
msr_log(msr, 4, "Multipart: Warning: Duplicate Content-Disposition name: %s",
log_escape_nq(msr->mp, value));
return -14;
}
msr->mpd->mpp->name = value;
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Multipart: Content-Disposition name: %s",
log_escape_nq(msr->mp, value));
}
}
else
if (strcmp(name, "filename") == 0) {
validate_quotes(msr, value, quote);
msr->multipart_filename = apr_pstrdup(msr->mp, value);
if (msr->mpd->mpp->filename != NULL) {
msr_log(msr, 4, "Multipart: Warning: Duplicate Content-Disposition filename: %s",
log_escape_nq(msr->mp, value));
return -15;
}
msr->mpd->mpp->filename = value;
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Multipart: Content-Disposition filename: %s",
log_escape_nq(msr->mp, value));
}
}
else return -11;
if (*p != '\0') {
while((*p == '\t') || (*p == ' ')) p++;
/* the next character must be a zero or a semi-colon */
if (*p == '\0') return 1; /* this is OK */
if (*p != ';') {
p--;
if(*p == '\'' || *p == '\"') {
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Multipart: Invalid quoting detected: %s length %zu bytes",
log_escape_nq(msr->mp, p), strlen(p));
}
msr->mpd->flag_invalid_quoting = 1;
}
p++;
return -12;
}
p++; /* move over the semi-colon */
}
/* loop will stop when (*p == '\0') */
}
return 1;
}
/**
*
*/
static int multipart_process_part_header(modsec_rec *msr, char **error_msg) {
assert(msr != NULL);
assert(error_msg != NULL);
int i, len, rc;
*error_msg = NULL;
/* Check for nul bytes. */
len = MULTIPART_BUF_SIZE - msr->mpd->bufleft;
for(i = 0; i < len; i++) {
if (msr->mpd->buf[i] == '\0') {
*error_msg = apr_psprintf(msr->mp, "Multipart: Nul byte in part headers.");
return -1;
}
}
/* The buffer is data so increase the data length counter. */
msr->msc_reqbody_no_files_length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft);
if (len > 1) {
if (msr->mpd->buf[len - 2] == '\r') {
msr->mpd->flag_crlf_line = 1;
} else {
msr->mpd->flag_lf_line = 1;
}
} else {
msr->mpd->flag_lf_line = 1;
}
/* Is this an empty line? */
if ( ((msr->mpd->buf[0] == '\r')
&&(msr->mpd->buf[1] == '\n')
&&(msr->mpd->buf[2] == '\0') )
|| ((msr->mpd->buf[0] == '\n')
&&(msr->mpd->buf[1] == '\0') ) )
{ /* Empty line. */
char *header_value = NULL;
header_value = (char *)apr_table_get(msr->mpd->mpp->headers, "Content-Disposition");
if (header_value == NULL) {
*error_msg = apr_psprintf(msr->mp, "Multipart: Part missing Content-Disposition header.");
return -1;
}
rc = multipart_parse_content_disposition(msr, header_value);
if (rc < 0) {
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid Content-Disposition header (%d): %s.",
rc, log_escape_nq(msr->mp, header_value));
return -1;
}
if (msr->mpd->mpp->name == NULL) {
*error_msg = apr_psprintf(msr->mp, "Multipart: Content-Disposition header missing name field.");
return -1;
}
if (msr->mpd->mpp->filename != NULL) {
/* Some parsers use crude methods to extract the name and filename
* values from the C-D header. We need to check for the case where they
* didn't understand C-D but we did.
*/
if (strstr(header_value, "filename=") == NULL) {
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid Content-Disposition header (filename).");
return -1;
}
msr->mpd->mpp->type = MULTIPART_FILE;
} else {
msr->mpd->mpp->type = MULTIPART_FORMDATA;
}
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. */
if (isspace(msr->mpd->buf[0])) {
char *header_value, *new_value, *data;
/* header folding, add data to the header we are building */
msr->mpd->flag_header_folding = 1;
/* RFC-2557 states header folding is SP / HTAB, but PHP and
* perhaps others will take any whitespace. So, we accept,
* but with a flag set.
*/
if ((msr->mpd->buf[0] != '\t') && (msr->mpd->buf[0] != ' ')) {
msr->mpd->flag_invalid_header_folding = 1;
}
if (msr->mpd->mpp->last_header_name == NULL) {
/* we are not building a header at this moment */
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid part header (folding error).");
return -1;
}
/* locate the beginning of data */
data = msr->mpd->buf;
while(isspace(*data)) {
/* Flag invalid header folding if an invalid RFC-2557 character is used anywhere
* in the folding prefix.
*/
if ((*data != '\t') && (*data != ' ')) {
msr->mpd->flag_invalid_header_folding = 1;
}
data++;
}
new_value = apr_pstrdup(msr->mp, data);
remove_lf_crlf_inplace(new_value);
/* update the header value in the table */
header_value = (char *)apr_table_get(msr->mpd->mpp->headers, msr->mpd->mpp->last_header_name);
new_value = apr_pstrcat(msr->mp, header_value, " ", new_value, NULL);
apr_table_set(msr->mpd->mpp->headers, msr->mpd->mpp->last_header_name, new_value);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Multipart: Continued folder header \"%s\" with \"%s\"",
log_escape(msr->mp, msr->mpd->mpp->last_header_name),
log_escape(msr->mp, data));
}
if (strlen(new_value) > MULTIPART_BUF_SIZE) {
*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.",
log_escape_nq(msr->mp, msr->mpd->buf));
return -1;
}
/* extract header name */
header_name = apr_pstrmemdup(msr->mp, msr->mpd->buf, (data - msr->mpd->buf));
if (data == msr->mpd->buf) {
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid part header (header name missing).");
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++;
header_value = apr_pstrdup(msr->mp, data);
remove_lf_crlf_inplace(header_value);
/* error if the name already exists */
if (apr_table_get(msr->mpd->mpp->headers, header_name) != NULL) {
*error_msg = apr_psprintf(msr->mp, "Multipart: Duplicate part header: %s.",
log_escape_nq(msr->mp, header_name));
return -1;
}
apr_table_setn(msr->mpd->mpp->headers, header_name, header_value);
msr->mpd->mpp->last_header_name = header_name;
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Multipart: Added part header \"%s\" \"%s\"",
log_escape(msr->mp, header_name),
log_escape(msr->mp, header_value));
}
}
}
return 1;
}
/**
*
*/
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;
*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') )
{
if ( ((MULTIPART_BUF_SIZE - msr->mpd->bufleft) >= 2)
&& (*(p - 2) == '\r') )
{
/* Two bytes. */
bytes_reserved = 2;
localreserve[0] = *(p - 2);
localreserve[1] = *(p - 1);
msr->mpd->bufleft += 2;
*(p - 2) = 0;
} else {
/* Only one byte. */
bytes_reserved = 1;
localreserve[0] = *(p - 1);
localreserve[1] = 0;
msr->mpd->bufleft += 1;
*(p - 1) = 0;
}
}
/* add data to the part we are building */
if (msr->mpd->mpp->type == MULTIPART_FILE) {
int extract = msr->upload_extract_files;
/* remember where we started */
if (msr->mpd->mpp->length == 0) {
msr->mpd->mpp->offset = msr->mpd->buf_offset;
}
/* check if the file limit has been reached */
if (extract && (msr->mpd->nfiles >= msr->txcfg->upload_file_limit)) {
if (msr->mpd->flag_file_limit_exceeded == 0) {
*error_msg = apr_psprintf(msr->mp,
"Multipart: Upload file limit exceeded "
"SecUploadFileLimit %d.",
msr->txcfg->upload_file_limit);
msr_log(msr, 3, "%s", *error_msg);
msr->mpd->flag_file_limit_exceeded = 1;
}
extract = 0;
}
/* only store individual files on disk if we are going
* to keep them or if we need to have them approved later
*/
if (extract) {
/* first create a temporary file if we don't have it already */
if (msr->mpd->mpp->tmp_file_fd == 0) {
/* construct temporary file name */
msr->mpd->mpp->tmp_file_name = apr_psprintf(msr->mp, "%s/%s-%s-file-XXXXXX",
msr->txcfg->tmp_dir, current_filetime(msr->mp), msr->txid);
msr->mpd->mpp->tmp_file_fd = msc_mkstemp_ex(msr->mpd->mpp->tmp_file_name, msr->txcfg->upload_filemode);
/* do we have an opened file? */
if (msr->mpd->mpp->tmp_file_fd < 0) {
*error_msg = apr_psprintf(msr->mp, "Multipart: Failed to create file: %s",
log_escape_nq(msr->mp, msr->mpd->mpp->tmp_file_name));
return -1;
}
/* keep track of the files count */
msr->mpd->nfiles++;
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4,
"Multipart: Created temporary file %d (mode %04o): %s",
msr->mpd->nfiles,
(unsigned int)msr->txcfg->upload_filemode,
log_escape_nq(msr->mp, msr->mpd->mpp->tmp_file_name));
}
}
/* write the reserve first */
if (msr->mpd->reserve[0] != 0) {
if (write(msr->mpd->mpp->tmp_file_fd, &msr->mpd->reserve[1], msr->mpd->reserve[0]) != msr->mpd->reserve[0]) {
*error_msg = apr_psprintf(msr->mp, "Multipart: writing to \"%s\" failed",
log_escape(msr->mp, msr->mpd->mpp->tmp_file_name));
return -1;
}
msr->mpd->mpp->tmp_file_size += msr->mpd->reserve[0];
msr->mpd->mpp->length += msr->mpd->reserve[0];
}
/* write data to the file */
if (write(msr->mpd->mpp->tmp_file_fd, msr->mpd->buf, MULTIPART_BUF_SIZE - msr->mpd->bufleft)
!= (MULTIPART_BUF_SIZE - msr->mpd->bufleft))
{
*error_msg = apr_psprintf(msr->mp, "Multipart: writing to \"%s\" failed",
log_escape(msr->mp, msr->mpd->mpp->tmp_file_name));
return -1;
}
msr->mpd->mpp->tmp_file_size += (MULTIPART_BUF_SIZE - msr->mpd->bufleft);
msr->mpd->mpp->length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft);
} else {
/* just keep track of the file size */
msr->mpd->mpp->tmp_file_size += (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0];
msr->mpd->mpp->length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0];
}
}
else if (msr->mpd->mpp->type == MULTIPART_FORMDATA) {
value_part_t *value_part = apr_pcalloc(msr->mp, sizeof(value_part_t));
/* The buffer contains data so increase the data length counter. */
msr->msc_reqbody_no_files_length += (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0];
/* add this part to the list of parts */
/* remember where we started */
if (msr->mpd->mpp->length == 0) {
msr->mpd->mpp->offset = msr->mpd->buf_offset;
}
if (msr->mpd->reserve[0] != 0) {
value_part->data = apr_palloc(msr->mp, (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0]);
memcpy(value_part->data, &(msr->mpd->reserve[1]), msr->mpd->reserve[0]);
memcpy(value_part->data + msr->mpd->reserve[0], msr->mpd->buf, (MULTIPART_BUF_SIZE - msr->mpd->bufleft));
value_part->length = (MULTIPART_BUF_SIZE - msr->mpd->bufleft) + msr->mpd->reserve[0];
msr->mpd->mpp->length += value_part->length;
} else {
value_part->length = (MULTIPART_BUF_SIZE - msr->mpd->bufleft);
value_part->data = apr_pstrmemdup(msr->mp, msr->mpd->buf, value_part->length);
msr->mpd->mpp->length += value_part->length;
}
*(value_part_t **)apr_array_push(msr->mpd->mpp->value_parts) = value_part;
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Multipart: Added data to variable: %s",
log_escape_nq_ex(msr->mp, value_part->data, value_part->length));
}
}
else {
*error_msg = apr_psprintf(msr->mp, "Multipart: unknown part type %d", msr->mpd->mpp->type);
return -1;
}
/* store the reserved bytes to the multipart
* context so that they don't get lost
*/
if (bytes_reserved) {
msr->mpd->reserve[0] = bytes_reserved;
msr->mpd->reserve[1] = localreserve[0];
msr->mpd->reserve[2] = localreserve[1];
msr->mpd->buf_offset += bytes_reserved;
}
else {
msr->mpd->buf_offset -= msr->mpd->reserve[0];
msr->mpd->reserve[0] = 0;
}
return 1;
}
/**
*
*/
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;
int i;
if (rval == NULL) return NULL;
offset = 0;
parts = (value_part_t **)value_parts->elts;
for(i = 0; i < value_parts->nelts; i++) {
if (offset + parts[i]->length <= msr->mpd->mpp->length) {
memcpy(rval + offset, parts[i]->data, parts[i]->length);
offset += parts[i]->length;
}
}
rval[offset] = '\0';
return rval;
}
/**
*
*/
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 */
if ((msr->mpd->mpp->type == MULTIPART_FILE)
&&(msr->mpd->mpp->tmp_file_name != NULL)
&&(msr->mpd->mpp->tmp_file_fd != 0))
{
close(msr->mpd->mpp->tmp_file_fd);
msr->mpd->mpp->tmp_file_fd = -1;
}
if (msr->mpd->mpp->type != MULTIPART_FILE) {
/* now construct a single string out of the parts */
msr->mpd->mpp->value = multipart_combine_value_parts(msr, msr->mpd->mpp->value_parts);
if (msr->mpd->mpp->value == NULL) return -1;
}
if (msr->mpd->mpp->name) {
/* add the part to the list of parts */
*(multipart_part **)apr_array_push(msr->mpd->parts) = msr->mpd->mpp;
if (msr->mpd->mpp->type == MULTIPART_FILE) {
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Multipart: Added file part %pp to the list: name \"%s\" "
"file name \"%s\" (offset %u, length %u)",
msr->mpd->mpp, log_escape(msr->mp, msr->mpd->mpp->name),
log_escape(msr->mp, msr->mpd->mpp->filename),
msr->mpd->mpp->offset, msr->mpd->mpp->length);
}
}
else {
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Multipart: Added part %pp to the list: name \"%s\" "
"(offset %u, length %u)", msr->mpd->mpp, log_escape(msr->mp, msr->mpd->mpp->name),
msr->mpd->mpp->offset, msr->mpd->mpp->length);
}
}
}
else {
msr->mpd->flag_invalid_part = 1;
msr_log(msr, 3, "Multipart: Skipping invalid part %pp (part name missing): "
"(offset %u, length %u)", msr->mpd->mpp,
msr->mpd->mpp->offset, msr->mpd->mpp->length);
}
msr->mpd->mpp = NULL;
}
if (last_part == 0) {
/* start building a new part */
msr->mpd->mpp = (multipart_part *)apr_pcalloc(msr->mp, sizeof(multipart_part));
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;
msr->mpd->reserve[2] = 0;
msr->mpd->reserve[3] = 0;
msr->mpd->mpp->value_parts = apr_array_make(msr->mp, 10, sizeof(value_part_t *));
}
return 1;
}
static int multipart_boundary_characters_valid(char *boundary) {
unsigned char *p = (unsigned char *)boundary;
unsigned char c;
if (p == NULL) return -1;
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;
}
p++;
}
return 1;
}
static int multipart_count_boundary_params(apr_pool_t *mp, const char *header_value) {
char *duplicate = NULL;
char *s = NULL;
int count = 0;
if (header_value == NULL) return -1;
duplicate = apr_pstrdup(mp, header_value);
if (duplicate == NULL) return -1;
/* Performing a case-insensitive search. */
strtolower_inplace((unsigned char *)duplicate);
s = duplicate;
while((s = strstr(s, "boundary")) != NULL) {
s += 8;
if (strchr(s, '=') != NULL) {
count++;
}
}
return count;
}
/**
*
*/
int multipart_init(modsec_rec *msr, char **error_msg) {
assert(msr != NULL);
assert(error_msg != NULL);
*error_msg = NULL;
msr->mpd = (multipart_data *)apr_pcalloc(msr->mp, sizeof(multipart_data));
if (msr->mpd == NULL) return -1;
msr->mpd->parts = apr_array_make(msr->mp, 10, sizeof(multipart_part *));
msr->mpd->bufleft = MULTIPART_BUF_SIZE;
msr->mpd->bufptr = msr->mpd->buf;
msr->mpd->buf_contains_line = 1;
msr->mpd->mpp = NULL;
if (msr->request_content_type == NULL) {
msr->mpd->flag_error = 1;
*error_msg = apr_psprintf(msr->mp, "Multipart: Content-Type header not available.");
return -1;
}
if (strlen(msr->request_content_type) > 1024) {
msr->mpd->flag_error = 1;
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (length).");
return -1;
}
if (strncasecmp(msr->request_content_type, "multipart/form-data", 19) != 0) {
msr->mpd->flag_error = 1;
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid MIME type.");
return -1;
}
/* Count how many times the word "boundary" appears in the C-T header. */
if (multipart_count_boundary_params(msr->mp, msr->request_content_type) > 1) {
msr->mpd->flag_error = 1;
*error_msg = apr_psprintf(msr->mp, "Multipart: Multiple boundary parameters in C-T.");
return -1;
}
msr->mpd->boundary = strstr(msr->request_content_type, "boundary");
if (msr->mpd->boundary != NULL) {
char *p = NULL;
char *b = NULL;
int seen_semicolon = 0;
int len = 0;
/* Check for extra characters before the boundary. */
for (p = (char *)(msr->request_content_type + 19); p < msr->mpd->boundary; p++) {
if (!isspace(*p)) {
if ((seen_semicolon == 0) && (*p == ';')) {
seen_semicolon = 1; /* It is OK to have one semicolon. */
} else {
msr->mpd->flag_error = 1;
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (malformed).");
return -1;
}
}
}
/* Have we seen the semicolon in the header? */
if (seen_semicolon == 0) {
msr->mpd->flag_missing_semicolon = 1;
}
b = strchr(msr->mpd->boundary + 8, '=');
if (b == NULL) {
msr->mpd->flag_error = 1;
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (malformed).");
return -1;
}
/* Check parameter name ends well. */
if (b != (msr->mpd->boundary + 8)) {
/* Check all characters between the end of the boundary
* and the = character.
*/
for (p = msr->mpd->boundary + 8; p < b; p++) {
if (isspace(*p)) {
/* Flag for whitespace after parameter name. */
msr->mpd->flag_boundary_whitespace = 1;
} else {
msr->mpd->flag_error = 1;
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (parameter name).");
return -1;
}
}
}
b++; /* Go over the = character. */
len = strlen(b);
/* Flag for whitespace before parameter value. */
if (isspace(*b)) {
msr->mpd->flag_boundary_whitespace = 1;
}
/* Is the boundary quoted? */
if ((len >= 2) && (*b == '"') && (*(b + len - 1) == '"')) {
/* Quoted. */
msr->mpd->boundary = apr_pstrndup(msr->mp, b + 1, len - 2);
if (msr->mpd->boundary == NULL) return -1;
msr->mpd->flag_boundary_quoted = 1;
} else {
/* Not quoted. */
/* Test for partial quoting. */
if ( (*b == '"')
|| ((len >= 2) && (*(b + len - 1) == '"')) )
{
msr->mpd->flag_error = 1;
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (quote).");
return -1;
}
msr->mpd->boundary = apr_pstrdup(msr->mp, b);
if (msr->mpd->boundary == NULL) return -1;
msr->mpd->flag_boundary_quoted = 0;
}
/* Case-insensitive test for the string "boundary" in the boundary. */
if (multipart_count_boundary_params(msr->mp, msr->mpd->boundary) != 0) {
msr->mpd->flag_error = 1;
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (content).");
return -1;
}
/* Validate the characters used in the boundary. */
if (multipart_boundary_characters_valid(msr->mpd->boundary) != 1) {
msr->mpd->flag_error = 1;
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (characters).");
return -1;
}
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Multipart: Boundary%s: %s",
(msr->mpd->flag_boundary_quoted ? " (quoted)" : ""),
log_escape_nq(msr->mp, msr->mpd->boundary));
}
if (strlen(msr->mpd->boundary) == 0) {
msr->mpd->flag_error = 1;
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (empty).");
return -1;
}
}
else { /* Could not find boundary in the C-T header. */
msr->mpd->flag_error = 1;
/* Test for case-insensitive boundary. Allowed by the RFC but highly unusual. */
if (multipart_count_boundary_params(msr->mp, msr->request_content_type) > 0) {
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary in C-T (case sensitivity).");
return -1;
}
*error_msg = apr_psprintf(msr->mp, "Multipart: Boundary not found in C-T.");
return -1;
}
return 1;
}
/**
* Finalise multipart processing. This method is invoked at the end, when it
* 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) {
if (msr->mpd->flag_data_before) {
msr_log(msr, 4, "Multipart: Warning: seen data before first boundary.");
}
if (msr->mpd->flag_data_after) {
msr_log(msr, 4, "Multipart: Warning: seen data after last boundary.");
}
if (msr->mpd->flag_boundary_quoted) {
msr_log(msr, 4, "Multipart: Warning: boundary was quoted.");
}
if (msr->mpd->flag_boundary_whitespace) {
msr_log(msr, 4, "Multipart: Warning: boundary whitespace in C-T header.");
}
if (msr->mpd->flag_header_folding) {
msr_log(msr, 4, "Multipart: Warning: header folding used.");
}
if (msr->mpd->flag_crlf_line && msr->mpd->flag_lf_line) {
msr_log(msr, 4, "Multipart: Warning: mixed line endings used (CRLF/LF).");
}
else if (msr->mpd->flag_lf_line) {
msr_log(msr, 4, "Multipart: Warning: incorrect line endings used (LF).");
}
if (msr->mpd->flag_missing_semicolon) {
msr_log(msr, 4, "Multipart: Warning: missing semicolon in C-T header.");
}
if (msr->mpd->flag_invalid_quoting) {
msr_log(msr, 4, "Multipart: Warning: invalid quoting used.");
}
if (msr->mpd->flag_invalid_part) {
msr_log(msr, 4, "Multipart: Warning: invalid part parsing.");
}
if (msr->mpd->flag_invalid_header_folding) {
msr_log(msr, 4, "Multipart: Warning: invalid header folding used.");
}
}
if ((msr->mpd->seen_data != 0) && (msr->mpd->is_complete == 0)) {
if (msr->mpd->boundary_count > 0) {
/* Check if we have the final boundary (that we haven't
* processed yet) in the buffer.
*/
if (msr->mpd->buf_contains_line) {
if ( ((unsigned int)(MULTIPART_BUF_SIZE - msr->mpd->bufleft) == (4 + strlen(msr->mpd->boundary)))
&& (*(msr->mpd->buf) == '-')
&& (*(msr->mpd->buf + 1) == '-')
&& (strncmp(msr->mpd->buf + 2, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0)
&& (*(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;
return -1;
}
/* The payload is complete after all. */
msr->mpd->is_complete = 1;
}
}
if (msr->mpd->is_complete == 0) {
*error_msg = apr_psprintf(msr->mp, "Multipart: Final boundary missing.");
return -1;
}
} else {
*error_msg = apr_psprintf(msr->mp, "Multipart: No boundaries found in payload.");
return -1;
}
}
return 1;
}
/**
*
*/
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;
*error_msg = NULL;
if (size == 0) return 1;
msr->mpd->seen_data = 1;
if (msr->mpd->is_complete) {
msr->mpd->flag_data_before = 1;
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Multipart: Ignoring data after last boundary (received %u bytes)", size);
}
return 1;
}
if (msr->mpd->bufleft == 0) {
msr->mpd->flag_error = 1;
*error_msg = apr_psprintf(msr->mp,
"Multipart: Internal error in process_chunk: no space left in the buffer");
return -1;
}
/* here we loop through the available data, one byte at a time */
while(inleft > 0) {
char c = *inptr;
int process_buffer = 0;
if ((c == '\r') && (msr->mpd->bufleft == 1)) {
/* we don't want to take \r as the last byte in the buffer */
process_buffer = 1;
} else {
inptr++;
inleft = inleft - 1;
*(msr->mpd->bufptr) = c;
msr->mpd->bufptr++;
msr->mpd->bufleft--;
}
/* until we either reach the end of the line
* or the end of our internal buffer
*/
if ((c == '\n') || (msr->mpd->bufleft == 0) || (process_buffer)) {
int processed_as_boundary = 0;
*(msr->mpd->bufptr) = 0;
/* Do we have something that looks like a boundary? */
if ( msr->mpd->buf_contains_line
&& (strlen(msr->mpd->buf) > 3)
&& (*(msr->mpd->buf) == '-')
&& (*(msr->mpd->buf + 1) == '-') )
{
/* Does it match our boundary? */
if ( (strlen(msr->mpd->buf) >= strlen(msr->mpd->boundary) + 2)
&& (strncmp(msr->mpd->buf + 2, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 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;
}
}
/* 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 (multipart_process_boundary(msr, (is_final ? 1 : 0), error_msg) < 0) {
msr->mpd->flag_error = 1;
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;
/* Check if an attempt to use quotes around the boundary was made. */
if ( (msr->mpd->flag_boundary_quoted)
&& (strlen(msr->mpd->buf) >= strlen(msr->mpd->boundary) + 3)
&& (*(msr->mpd->buf + 2) == '"')
&& (strncmp(msr->mpd->buf + 3, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0)
) {
msr->mpd->flag_error = 1;
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary (quotes).");
return -1;
}
/* Check the beginning of the boundary for whitespace. */
p = msr->mpd->buf + 2;
while(isspace(*p)) {
p++;
}
if ( (p != msr->mpd->buf + 2)
&& (strncmp(p, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0)
) {
/* Found whitespace in front of a boundary. */
msr->mpd->flag_error = 1;
*error_msg = apr_psprintf(msr->mp, "Multipart: Invalid boundary (whitespace).");
return -1;
}
msr->mpd->flag_unmatched_boundary = 1;
}
} else { /* We do not think the buffer contains a boundary. */
/* Look into the buffer to see if there's anything
* there that resembles a boundary.
*/
if (msr->mpd->buf_contains_line) {
int i, len = (MULTIPART_BUF_SIZE - msr->mpd->bufleft);
char *p = msr->mpd->buf;
for(i = 0; i < len; i++) {
if ((p[i] == '-') && (i + 1 < len) && (p[i + 1] == '-'))
{
if (strncmp(p + i + 2, msr->mpd->boundary, strlen(msr->mpd->boundary)) == 0) {
msr->mpd->flag_unmatched_boundary = 1;
break;
}
}
}
}
}
/* Process as data if it was not a boundary. */
if (processed_as_boundary == 0) {
if (msr->mpd->mpp == NULL) {
msr->mpd->flag_data_before = 1;
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Multipart: Ignoring data before first boundary.");
}
} else {
if (msr->mpd->mpp_state == 0) {
if ((msr->mpd->bufleft == 0) || (process_buffer)) {
/* part header lines must be shorter than
* MULTIPART_BUF_SIZE bytes
*/
msr->mpd->flag_error = 1;
*error_msg = apr_psprintf(msr->mp,
"Multipart: Part header line over %d bytes long",
MULTIPART_BUF_SIZE);
return -1;
}
if (multipart_process_part_header(msr, error_msg) < 0) {
msr->mpd->flag_error = 1;
return -1;
}
} else {
if (multipart_process_part_data(msr, error_msg) < 0) {
msr->mpd->flag_error = 1;
return -1;
}
}
}
}
/* Update the offset of the data we are about
* to process. This is to allow us to know the
* offsets of individual files and variables.
*/
msr->mpd->buf_offset += (MULTIPART_BUF_SIZE - msr->mpd->bufleft);
/* reset the pointer to the beginning of the buffer
* and continue to accept input data
*/
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)) {
msr->mpd->flag_data_after = 1;
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Multipart: Ignoring data after last boundary (%u bytes left)", inleft);
}
return 1;
}
}
return 1;
}
/**
*
*/
apr_status_t multipart_cleanup(modsec_rec *msr) {
assert(msr != NULL);
int keep_files = 0;
if (msr->mpd == NULL) return -1;
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Multipart: Cleanup started (remove files %d).", msr->upload_remove_files);
}
if (msr->upload_remove_files == 0) {
if (msr->txcfg->upload_dir == NULL) {
msr_log(msr, 1, "Input filter: SecUploadDir is undefined, unable to store "
"multipart files.");
} else {
keep_files = 1;
}
}
/* Loop through the list of parts
* and delete the temporary files, but only if
* file storage was not requested, or if storage
* of relevant files was requested and this isn't
* such a request.
*/
if (keep_files == 0) {
multipart_part **parts;
int i;
parts = (multipart_part **)msr->mpd->parts->elts;
for(i = 0; i < msr->mpd->parts->nelts; i++) {
if (parts[i]->type == MULTIPART_FILE) {
if (parts[i]->tmp_file_name != NULL) {
/* make sure it is closed first */
if (parts[i]->tmp_file_fd > 0) {
close(parts[i]->tmp_file_fd);
parts[i]->tmp_file_fd = -1;
}
if (unlink(parts[i]->tmp_file_name) < 0) {
msr_log(msr, 1, "Multipart: Failed to delete file (part) \"%s\" because %d(%s)",
log_escape(msr->mp, parts[i]->tmp_file_name), errno, strerror(errno));
} else {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Multipart: Deleted file (part) \"%s\"",
log_escape(msr->mp, parts[i]->tmp_file_name));
}
}
}
}
}
} else {
/* delete empty files, move the others to the upload dir */
multipart_part **parts;
int i;
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_size == 0))
{
/* Delete empty file. */
if (parts[i]->tmp_file_name != NULL) {
/* make sure it is closed first */
if (parts[i]->tmp_file_fd > 0) {
close(parts[i]->tmp_file_fd);
parts[i]->tmp_file_fd = -1;
}
if (unlink(parts[i]->tmp_file_name) < 0) {
msr_log(msr, 1, "Multipart: Failed to delete empty file (part) \"%s\" because %d(%s)",
log_escape(msr->mp, parts[i]->tmp_file_name), errno, strerror(errno));
} else {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Multipart: Deleted empty file (part) \"%s\"",
log_escape(msr->mp, parts[i]->tmp_file_name));
}
}
}
} 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;
}
/* make sure it is closed first */
if (parts[i]->tmp_file_fd > 0) {
close(parts[i]->tmp_file_fd);
parts[i]->tmp_file_fd = -1;
}
new_basename = file_basename(msr->mp, parts[i]->tmp_file_name);
if (new_basename == NULL) return -1;
new_filename = apr_psprintf(msr->mp, "%s/%s", msr->txcfg->upload_dir,
new_basename);
if (new_filename == NULL) return -1;
if (apr_file_rename(parts[i]->tmp_file_name, new_filename,
msr->msc_reqbody_mp) != APR_SUCCESS)
{
msr_log(msr, 1, "Input filter: Failed to rename file from \"%s\" to \"%s\".",
log_escape(msr->mp, parts[i]->tmp_file_name),
log_escape(msr->mp, new_filename));
return -1;
} else {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Input filter: Moved file from \"%s\" to \"%s\".",
log_escape(msr->mp, parts[i]->tmp_file_name),
log_escape(msr->mp, new_filename));
}
}
}
}
}
}
return 1;
}
/**
*
*/
int multipart_get_arguments(modsec_rec *msr, char *origin, apr_table_t *arguments) {
assert(msr != NULL);
multipart_part **parts;
int i;
parts = (multipart_part **)msr->mpd->parts->elts;
for(i = 0; i < msr->mpd->parts->nelts; i++) {
if (parts[i]->type == MULTIPART_FORMDATA) {
msc_arg *arg = (msc_arg *)apr_pcalloc(msr->mp, sizeof(msc_arg));
if (arg == NULL) return -1;
arg->name = parts[i]->name;
arg->name_len = strlen(parts[i]->name);
arg->value = parts[i]->value;
arg->value_len = parts[i]->length;
arg->value_origin_offset = parts[i]->offset;
arg->value_origin_len = parts[i]->length;
arg->origin = origin;
add_argument(msr, arguments, arg);
}
}
return 1;
}
/**
*
*/
char *multipart_reconstruct_urlencoded_body_sanitize(modsec_rec *msr) {
multipart_part **parts;
char *body;
unsigned int body_len;
int i;
if (msr->mpd == NULL) return NULL;
/* calculate the size of the buffer */
body_len = 1;
parts = (multipart_part **)msr->mpd->parts->elts;
for(i = 0; i < msr->mpd->parts->nelts; i++) {
if (parts[i]->type == MULTIPART_FORMDATA) {
body_len += 4;
body_len += strlen(parts[i]->name) * 3;
body_len += strlen(parts[i]->value) * 3;
}
}
/* allocate the buffer */
body = apr_palloc(msr->mp, body_len + 1);
if ((body == NULL) || (body_len + 1 == 0)) return NULL;
*body = 0;
parts = (multipart_part **)msr->mpd->parts->elts;
for(i = 0; i < msr->mpd->parts->nelts; i++) {
if (parts[i]->type == MULTIPART_FORMDATA) {
if (*body != 0) {
strncat(body, "&", body_len - strlen(body));
}
strnurlencat(body, parts[i]->name, body_len - strlen(body));
strncat(body, "=", body_len - strlen(body));
/* Sanitise the variable. Since we are only doing this for
* the logging we will actually write over the data we keep
* in the memory.
*/
if (msr->phase >= PHASE_LOGGING) {
if (apr_table_get(msr->arguments_to_sanitize, parts[i]->name) != NULL) {
memset(parts[i]->value, '*', strlen(parts[i]->value));
}
}
strnurlencat(body, parts[i]->value, body_len - strlen(body));
}
}
return body;
}
modsecurity-apache-2.9.13/apache2/msc_multipart.h 0000664 0000000 0000000 00000012011 15174655764 0021752 0 ustar 00root root 0000000 0000000 /*
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
* 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
*
* 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.
*/
#ifndef _MSC_MULTIPART_H_
#define _MSC_MULTIPART_H_
#define MULTIPART_BUF_SIZE 4096
#define MULTIPART_FORMDATA 1
#define MULTIPART_FILE 2
typedef struct multipart_part multipart_part;
typedef struct multipart_data multipart_data;
#include "apr_general.h"
#include "apr_tables.h"
#include "modsecurity.h"
typedef struct value_part_t value_part_t;
struct value_part_t {
char *data;
long int length;
};
struct multipart_part {
/* part type, can be MULTIPART_FORMDATA or MULTIPART_FILE */
int type;
/* the name */
char *name;
/* variables only, variable value */
char *value;
apr_array_header_t *value_parts;
/* files only, the content type (where available) */
char *content_type;
/* files only, the name of the temporary file holding data */
char *tmp_file_name;
int tmp_file_fd;
unsigned int tmp_file_size;
/* files only, filename as supplied by the browser */
char *filename;
char *last_header_name;
apr_table_t *headers;
char *last_header_line;
apr_array_header_t *header_lines;
unsigned int offset;
unsigned int length;
};
struct multipart_data {
/* this array keeps parts */
apr_array_header_t *parts;
/* Number of parts that are files */
int nfiles;
/* mime boundary used to detect when
* parts end and begin
*/
char *boundary;
int boundary_count;
/* internal buffer and other variables
* used while parsing
*/
char buf[MULTIPART_BUF_SIZE + 2];
int buf_contains_line;
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
* it is being built
*/
multipart_part *mpp;
/* part parsing state; 0 means we are reading
* headers, 1 means we are collecting 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
* later if the next data chunk proves to be
* a boundary; the first byte is an indicator
* 0 - no content, 1 - two data bytes available
*/
char reserve[4];
int seen_data;
int is_complete;
int flag_error;
int flag_data_before;
int flag_data_after;
int flag_header_folding;
int flag_boundary_quoted;
int flag_lf_line;
int flag_crlf_line;
int flag_unmatched_boundary;
int flag_boundary_whitespace;
int flag_missing_semicolon;
int flag_invalid_quoting;
int flag_invalid_part;
int flag_invalid_header_folding;
int flag_file_limit_exceeded;
};
/* Functions */
int DSOLOCAL multipart_init(modsec_rec *msr, char **error_msg);
int DSOLOCAL multipart_complete(modsec_rec *msr, char **error_msg);
int DSOLOCAL multipart_process_chunk(modsec_rec *msr, const char *buf,
unsigned int size, char **error_msg);
apr_status_t DSOLOCAL multipart_cleanup(modsec_rec *msr);
int DSOLOCAL multipart_get_arguments(modsec_rec *msr, char *origin, apr_table_t *arguments);
char DSOLOCAL *multipart_reconstruct_urlencoded_body_sanitize(modsec_rec *msr);
#endif
modsecurity-apache-2.9.13/apache2/msc_parsers.c 0000664 0000000 0000000 00000026101 15174655764 0021410 0 ustar 00root root 0000000 0000000 /*
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
* 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
*
* 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 "msc_parsers.h"
#include
/**
*
*/
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;
int cookie_count = 0;
char *p = NULL;
if (_cookie_header == NULL) {
msr_log(msr, 1, "Cookie parser: Received null for argument.");
return -1;
}
cookie_header = strdup(_cookie_header);
if (cookie_header == NULL) return -1;
if(msr->txcfg->cookiev0_separator == NULL) {
p = apr_strtok(cookie_header, delim, &saveptr);
} else {
p = apr_strtok(cookie_header, msr->txcfg->cookiev0_separator, &saveptr);
}
while(p != NULL) {
attr_name = NULL;
attr_value = NULL;
/* ignore whitespace at the beginning of cookie name */
while(isspace(*p)) p++;
attr_name = p;
attr_value = strstr(p, "=");
if (attr_value != NULL) {
/* terminate cookie name */
*attr_value = 0;
/* move over to the beginning of the value */
attr_value++;
}
/* we ignore cookies with empty names */
if ((attr_name != NULL)&&(strlen(attr_name) != 0)) {
if (attr_value != NULL) {
if (msr->txcfg->debuglog_level >= 5) {
msr_log(msr, 5, "Adding request cookie: name \"%s\", value \"%s\"",
log_escape(msr->mp, attr_name), log_escape(msr->mp, attr_value));
}
apr_table_add(cookies, attr_name, attr_value);
} else {
if (msr->txcfg->debuglog_level >= 5) {
msr_log(msr, 5, "Adding request cookie: name \"%s\", value empty",
log_escape(msr->mp, attr_name));
}
apr_table_add(cookies, attr_name, "");
}
cookie_count++;
}
if(msr->txcfg->cookiev0_separator == NULL) {
p = apr_strtok(NULL, delim, &saveptr);
} else {
p = apr_strtok(NULL, msr->txcfg->cookiev0_separator, &saveptr);
}
}
free(cookie_header);
return cookie_count;
}
/**
*
*/
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;
int cookie_count = 0;
if (_cookie_header == NULL) return -1;
// XXX Should it not match _v0 parser?
//if (_cookie_header == NULL) {
// msr_log(msr, 1, "Cookie parser: Received null for argument.");
// return -1;
//}
cookie_header = strdup(_cookie_header);
if (cookie_header == NULL) return -1;
p = cookie_header;
while(*p != 0) {
attr_name = NULL;
attr_value = NULL;
/* attribute name */
/* remove space from the beginning */
while((isspace(*p))&&(*p != 0)) p++;
attr_name = p;
while((*p != 0)&&(*p != '=')&&(*p != ';')&&(*p != ',')) p++;
/* if we've reached the end of string */
if (*p == 0) goto add_cookie;
/* if there is no cookie value supplied */
if ((*p == ';')||(*p == ',')) {
*p++ = 0; /* terminate the name */
goto add_cookie;
}
/* terminate the attribute name,
* writing over the = character
*/
*p++ = 0;
/* attribute value */
/* skip over the whitespace at the beginning */
while((isspace(*p))&&(*p != 0)) p++;
/* no value supplied */
if (*p == 0) goto add_cookie;
if (*p == '"') {
if (*++p == 0) goto add_cookie;
attr_value = p;
while((*p != 0)&&(*p != '"')) p++;
if (*p != 0) *p++ = 0;
else {
/* Do nothing about this. */
}
} else {
attr_value = p;
while((*p != 0)&&(*p != ',')&&(*p != ';')) p++;
if (*p != 0) *p++ = 0;
/* remove the whitespace from the end of cookie value */
if (attr_value != NULL) {
char *t = attr_value;
int i = 0;
while(*t != 0) {
t++;
i++;
}
while((i-- > 0)&&(isspace(*(--t)))) *t = 0;
}
}
add_cookie:
/* remove the whitespace from the end of cookie name */
if (attr_name != NULL) {
char *t = attr_name;
int i = 0;
while(*t != 0) {
t++;
i++;
}
while((i-- > 0)&&(isspace(*(--t)))) *t = 0;
}
/* add the cookie to the list now */
if ((attr_name != NULL)&&(strlen(attr_name) != 0)) {
/* handle special attribute names */
if (attr_name[0] == '$') {
if (prev_attr_name != NULL) {
/* cookie keyword, we change the name we use
* so they can have a unique name in the cookie table
*/
attr_name = apr_psprintf(msr->mp, "$%s_%s", prev_attr_name, attr_name + 1);
}
}
if (attr_value != NULL) {
if (msr->txcfg->debuglog_level >= 5) {
msr_log(msr, 5, "Adding request cookie: name \"%s\", value \"%s\"",
log_escape(msr->mp, attr_name), log_escape(msr->mp, attr_value));
}
apr_table_add(cookies, attr_name, attr_value);
} else {
if (msr->txcfg->debuglog_level >= 5) {
msr_log(msr, 5, "Adding request cookie: name \"%s\", value empty",
log_escape(msr->mp, attr_name));
}
apr_table_add(cookies, attr_name, "");
}
cookie_count++;
/* only keep the cookie names for later */
if (attr_name[0] != '$') prev_attr_name = attr_name;
}
/* at this point the *p is either 0 (in which case we exit), or
* right after the current cookie ended - we need to look for
* the next cookie
*/
while( (*p != 0)&&( (*p == ',')||(*p == ';')||(isspace(*p)) ) ) p++;
}
free(cookie_header);
return cookie_count;
}
/**
*
*/
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;
char *buf;
int status;
int changed;
if (s == NULL) return -1;
if (inputlength == 0) return 1;
/* Check that adding one will not overflow */
if (inputlength + 1 <= 0) return -1;
buf = (char *)malloc(inputlength + 1);
if (buf == NULL) return -1;
arg = (msc_arg *)apr_pcalloc(msr->mp, sizeof(msc_arg));
arg->origin = origin;
i = 0;
j = 0;
status = 0;
*invalid_count = 0;
while (i < inputlength) {
if (status == 0) {
/* parameter name */
arg->name_origin_offset = i;
while ((i < inputlength) && (s[i] != '=') && (s[i] != argument_separator)) {
buf[j] = s[i];
j++;
i++;
}
buf[j++] = '\0';
arg->name_origin_len = i - arg->name_origin_offset;
} else {
/* parameter value */
arg->value_origin_offset = i;
while ((s[i] != argument_separator) && (i < inputlength)) {
buf[j] = s[i];
j++;
i++;
}
buf[j++] = '\0';
arg->value_origin_len = i - arg->value_origin_offset;
}
if (status == 0) {
arg->name_len = urldecode_nonstrict_inplace_ex((unsigned char *)buf, arg->name_origin_len, invalid_count, &changed);
arg->name = apr_pstrmemdup(msr->mp, buf, arg->name_len);
if (s[i] == argument_separator) {
/* Empty parameter */
arg->value_len = 0;
arg->value = "";
add_argument(msr, arguments, arg);
arg = (msc_arg *)apr_pcalloc(msr->mp, sizeof(msc_arg));
arg->origin = origin;
status = 0; /* unchanged */
j = 0;
} else {
status = 1;
value = &buf[j];
}
}
else {
arg->value_len = urldecode_nonstrict_inplace_ex((unsigned char *)value, arg->value_origin_len, invalid_count, &changed);
arg->value = apr_pstrmemdup(msr->mp, value, arg->value_len);
add_argument(msr, arguments, arg);
arg = (msc_arg *)apr_pcalloc(msr->mp, sizeof(msc_arg));
arg->origin = origin;
status = 0;
j = 0;
}
i++; /* skip over the separator */
}
/* the last parameter was empty */
if (status == 1) {
arg->value_len = 0;
arg->value = "";
add_argument(msr, arguments, arg);
}
free(buf);
return 1;
}
/**
*
*/
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));
}
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);
}
}
modsecurity-apache-2.9.13/apache2/msc_parsers.h 0000664 0000000 0000000 00000002070 15174655764 0021414 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#ifndef _MSC_PARSERS_H_
#define _MSC_PARSERS_H_
#include "modsecurity.h"
int DSOLOCAL parse_cookies_v0(modsec_rec *msr, char *_cookie_header, apr_table_t *cookies,
const char *delim);
int DSOLOCAL parse_cookies_v1(modsec_rec *msr, char *_cookie_header, apr_table_t *cookies);
int DSOLOCAL 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);
void DSOLOCAL add_argument(modsec_rec *msr, apr_table_t *arguments, msc_arg *arg);
#endif
modsecurity-apache-2.9.13/apache2/msc_pcre.c 0000664 0000000 0000000 00000025050 15174655764 0020664 0 ustar 00root root 0000000 0000000 /*
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
* 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
*
* 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 "msc_pcre.h"
#include "apr_strings.h"
/**
* Releases the resources used by a single regular expression pattern.
*/
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) {
pcre_free(regex->pe);
regex->pe = NULL;
}
if (regex->re != NULL) {
pcre_free(regex->re);
regex->re = NULL;
}
#endif
}
return APR_SUCCESS;
}
/**
* Compiles the provided regular expression pattern. The _err*
* parameters are optional, but if they are provided and an error
* occurs they will contain the error message and the offset in
* the pattern where the offending part of the pattern begins. The
* match_limit* parameters are optional and if >0, then will set
* match limits.
*/
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;
msc_regex_t *regex;
pcre_extra *pe = NULL;
regex = apr_pcalloc(pool, sizeof(msc_regex_t));
if (regex == NULL) return NULL;
regex->pattern = pattern;
if ((_errptr == NULL)||(_erroffset == NULL)) {
regex->re = pcre_compile(pattern, options, &errptr, &erroffset, NULL);
} else {
regex->re = pcre_compile(pattern, options, _errptr, _erroffset, NULL);
}
if (regex->re == NULL) return NULL;
#ifdef WITH_PCRE_STUDY
#ifdef WITH_PCRE_JIT
pe = pcre_study(regex->re, PCRE_STUDY_JIT_COMPILE, &errptr);
#else
pe = pcre_study(regex->re, 0, &errptr);
#endif
#endif
/* Setup the pcre_extra record if pcre_study did not already do it */
if (pe == NULL) {
pe = (pcre_extra*)pcre_malloc(sizeof(pcre_extra));
if (pe == NULL) {
return NULL;
}
memset(pe, 0, sizeof(pcre_extra));
}
#ifdef PCRE_EXTRA_MATCH_LIMIT
/* If match limit is available, then use it */
/* Use ModSecurity runtime defaults */
if (match_limit > 0) {
pe->match_limit = match_limit;
pe->flags |= PCRE_EXTRA_MATCH_LIMIT;
}
#ifdef MODSEC_PCRE_MATCH_LIMIT
/* Default to ModSecurity compiled defaults */
else {
pe->match_limit = MODSEC_PCRE_MATCH_LIMIT;
pe->flags |= PCRE_EXTRA_MATCH_LIMIT;
}
#endif /* MODSEC_PCRE_MATCH_LIMIT */
#else
#pragma message ( "This PCRE version does not support match limits! Upgrade to at least PCRE v6.5." )
#endif /* PCRE_EXTRA_MATCH_LIMIT */
#ifdef PCRE_EXTRA_MATCH_LIMIT_RECURSION
/* If match limit recursion is available, then use it */
/* Use ModSecurity runtime defaults */
if (match_limit_recursion > 0) {
pe->match_limit_recursion = match_limit_recursion;
pe->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
}
#ifdef MODSEC_PCRE_MATCH_LIMIT_RECURSION
/* Default to ModSecurity compiled defaults */
else {
pe->match_limit_recursion = MODSEC_PCRE_MATCH_LIMIT_RECURSION;
pe->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
}
#endif /* MODSEC_PCRE_MATCH_LIMIT_RECURSION */
#else
#pragma message ( "This PCRE version does not support match recursion limits! Upgrade to at least PCRE v6.5." )
#endif /* PCRE_EXTRA_MATCH_LIMIT_RECURSION */
regex->pe = pe;
apr_pool_cleanup_register(pool, (void *)regex,
(apr_status_t (*)(void *))msc_pcre_cleanup, apr_pool_cleanup_null);
return regex;
}
#endif /* WITH_PCRE2 */
/**
* Compiles the provided regular expression pattern. Calls msc_pregcomp_ex()
* with default limits.
*/
void *msc_pregcomp(apr_pool_t *pool, const char *pattern, int options,
const char **_errptr, int *_erroffset)
{
return msc_pregcomp_ex(pool, pattern, options, _errptr, _erroffset, 0, 0);
}
/**
* 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)
{
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
}
/**
* Executes regular expression, capturing subexpressions in the given
* vector. Returns PCRE_ERROR_NOMATCH when there is no match, error code < -1
* on errors, and a value > 0 when there is a match.
*/
int msc_regexec_capture(msc_regex_t *regex, const char *s, unsigned int slen,
int *ovector, int ovecsize, char **error_msg)
{
if (error_msg == NULL) return -1000; /* To differentiate from PCRE as it already uses -1. */
*error_msg = NULL;
return msc_regexec_ex(regex, s, slen, 0, 0, ovector, ovecsize, error_msg);
}
/**
* Executes regular expression but ignores any of the subexpression
* captures. See above for the return codes.
*/
int msc_regexec(msc_regex_t *regex, const char *s, unsigned int slen,
char **error_msg)
{
if (error_msg == NULL) return -1000; /* To differentiate from PCRE as it already uses -1. */
*error_msg = NULL;
return msc_regexec_ex(regex, s, slen, 0, 0, NULL, 0, error_msg);
}
/**
* Gets info on a compiled regex.
*/
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
}
modsecurity-apache-2.9.13/apache2/msc_pcre.h 0000664 0000000 0000000 00000004547 15174655764 0020701 0 ustar 00root root 0000000 0000000 /*
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
* 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
*
* 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.
*/
#ifndef _MSC_PCRE_H_
#define _MSC_PCRE_H_
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. */
#define PCRE_ERROR_MATCHLIMIT (-8)
#endif /* PCRE_ERROR_MATCHLIMIT */
#ifndef PCRE_ERROR_RECURSIONLIMIT
/* Define for compile, but not valid in this version of PCRE. */
#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;
};
void DSOLOCAL *msc_pregcomp_ex(apr_pool_t *pool, const char *pattern, int options,
const char **_errptr, int *_erroffset,
int match_limit, int match_limit_recursion);
void DSOLOCAL *msc_pregcomp(apr_pool_t *pool, const char *pattern, int options,
const char **_errptr, int *_erroffset);
int DSOLOCAL msc_regexec_ex(msc_regex_t *regex, const char *s,
unsigned int slen, int startoffset, int options,
int *ovector, int ovecsize, char **error_msg);
int DSOLOCAL msc_regexec_capture(msc_regex_t *regex, const char *s,
unsigned int slen, int *ovector,
int ovecsize, char **error_msg);
int DSOLOCAL msc_regexec(msc_regex_t *regex, const char *s,
unsigned int slen, char **error_msg);
int DSOLOCAL msc_fullinfo(msc_regex_t *regex, int what, void *where);
#endif
modsecurity-apache-2.9.13/apache2/msc_release.c 0000664 0000000 0000000 00000002372 15174655764 0021355 0 ustar 00root root 0000000 0000000 /*
* 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 "msc_release.h"
static const struct modsec_build_type_rec {
char name[12]; /* pads at 16 bytes with val */
int val;
} modsec_build_type[] = {
{ "-dev", 1 }, /* Development build */
{ "-rc", 3 }, /* Release Candidate build */
{ "", 9 }, /* Production build */
{ "-tw", 9 }, /* Truswave Holdings build */
{ "-trunk", 9 } /* Trunk build */
};
int get_modsec_build_type(const char *name)
{
size_t i;
for (i = 0; i < sizeof(modsec_build_type)/sizeof(modsec_build_type[0]); i++) {
if (strcmp(((name == NULL) ? MODSEC_VERSION_TYPE : name), modsec_build_type[i].name) == 0) {
return modsec_build_type[i].val;
}
}
return 9; /* so no warning */
}
modsecurity-apache-2.9.13/apache2/msc_release.h 0000664 0000000 0000000 00000004021 15174655764 0021353 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#ifndef _MSC_RELEASE_H_
#define _MSC_RELEASE_H_
#include
#include
/* ENH: Clean this mess up by detecting this is possible */
#if !(defined(_AIX) || defined(WIN32) || defined(CYGWIN) || defined(NETWARE) || defined(SOLARIS2) || defined(OSF1))
#define DSOLOCAL __attribute__((visibility("hidden")))
#else
#define DSOLOCAL
#endif
#if defined(DEBUG_MEM)
/* Nothing Yet */
#endif
/* For GNU C, tell the compiler to check printf like formatters */
#if (defined(__GNUC__) && !defined(SOLARIS2))
#define PRINTF_ATTRIBUTE(a,b) __attribute__((format (printf, a, b)))
#else
#define PRINTF_ATTRIBUTE(a,b)
#endif
#define MODSEC_VERSION_MAJOR "2"
#define MODSEC_VERSION_MINOR "9"
#define MODSEC_VERSION_MAINT "13"
#define MODSEC_VERSION_TYPE ""
#define MODSEC_VERSION_RELEASE ""
#define MODSEC_VERSION_SUFFIX MODSEC_VERSION_TYPE MODSEC_VERSION_RELEASE
#define MODSEC_VERSION \
MODSEC_VERSION_MAJOR "." MODSEC_VERSION_MINOR "." MODSEC_VERSION_MAINT \
MODSEC_VERSION_SUFFIX
/* Apache Module Defines */
#if defined(VERSION_IIS)
#define MODSEC_MODULE_NAME "ModSecurity for IIS (STABLE)"
#elif defined(VERSION_STANDALONE)
#define MODSEC_MODULE_NAME "ModSecurity Standalone (STABLE)"
#else
#define MODSEC_MODULE_NAME "ModSecurity for Apache"
#endif
#define MODSEC_MODULE_VERSION MODSEC_VERSION
#define MODSEC_MODULE_NAME_FULL MODSEC_MODULE_NAME "/" MODSEC_MODULE_VERSION " (http://www.modsecurity.org/)"
int DSOLOCAL get_modsec_build_type(const char *name);
#endif /* _MSC_RELEASE_H_ */
modsecurity-apache-2.9.13/apache2/msc_remote_rules.c 0000664 0000000 0000000 00000060573 15174655764 0022451 0 ustar 00root root 0000000 0000000 /*
* 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 "msc_remote_rules.h"
#include "msc_status_engine.h"
#include
#ifdef WITH_CURL
#include
#endif
#include
#ifdef WITH_REMOTE_RULES
#ifdef WITH_APU_CRYPTO
#include
#endif
#include
#endif
#ifndef AP_MAX_ARGC
#define AP_MAX_ARGC 64
#endif
/**
* @brief Find command in a list.
*
* This is a duplicate of `ap_find_command', which is part of the standalone module.
* Apache versions does not include the standalone, thus, this is necessary for
* the Apache versions. Once it is here it may not be necessary to be part of
* the standalone module, but, for this version both function will co-exist
* avoiding problems with 3rd parties that are extending the standalone module.
*
* @note Prefer this function instead of `ap_finc_command` which is part of the
* standalone module.
*
* @param parms char pointer, function name.
* @param cmds pointer to command_rec[].
* @retval NULL if command was not found.
*
*/
const command_rec *msc_remote_find_command(const char *name, const command_rec *cmds)
{
while (cmds->name) {
if (!strcasecmp(name, cmds->name))
return cmds;
++cmds;
}
return NULL;
}
/**
* @brief Insert a new SecRule to be processed by ModSecurity
*
* This is a duplicate of `invoke_cmd', which is part of the standalone module.
* Apache versions does not include the standalone, thus, this is necessary for
* the Apache versions. Once it is here it may not be necessary to be part of
* the standalone module, but, for this version both function will co-exist
* avoiding problems with 3rd parties that are extending the standalone module.
*
* @note Prefer this function instead of `invoke_cmd` which is part of the
* standalone module.
*
* @param cmd pointer to command_rec structure.
* @param parms pointer to cmd_params strucutre.
* @param mconfig pointer to main config structure.
* @param args SecRule arguments.
* @retval NULL if everything worked as expected otherwise an error message.
*
*/
const char *msc_remote_invoke_cmd(const command_rec *cmd, cmd_parms *parms,
void *mconfig, const char *args)
{
char *w, *w2, *w3;
const char *errmsg = NULL;
if ((parms->override & cmd->req_override) == 0)
return apr_pstrcat(parms->pool, cmd->name, " not allowed here", NULL);
parms->info = cmd->cmd_data;
parms->cmd = cmd;
switch (cmd->args_how) {
case RAW_ARGS:
#ifdef RESOLVE_ENV_PER_TOKEN
args = ap_resolve_env(parms->pool,args);
#endif
return cmd->AP_RAW_ARGS(parms, mconfig, args);
case TAKE_ARGV:
{
char *argv[AP_MAX_ARGC];
int argc = 0;
do {
w = ap_getword_conf(parms->pool, &args);
if (*w == '\0' && *args == '\0') {
break;
}
argv[argc] = w;
argc++;
} while (argc < AP_MAX_ARGC && *args != '\0');
return cmd->AP_TAKE_ARGV(parms, mconfig, argc, argv);
}
case NO_ARGS:
if (*args != 0)
return apr_pstrcat(parms->pool, cmd->name, " takes no arguments",
NULL);
return cmd->AP_NO_ARGS(parms, mconfig);
case TAKE1:
w = ap_getword_conf(parms->pool, &args);
if (*w == '\0' || *args != 0)
return apr_pstrcat(parms->pool, cmd->name, " takes one argument",
cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
return cmd->AP_TAKE1(parms, mconfig, w);
case TAKE2:
w = ap_getword_conf(parms->pool, &args);
w2 = ap_getword_conf(parms->pool, &args);
if (*w == '\0' || *w2 == '\0' || *args != 0)
return apr_pstrcat(parms->pool, cmd->name, " takes two arguments",
cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
return cmd->AP_TAKE2(parms, mconfig, w, w2);
case TAKE12:
w = ap_getword_conf(parms->pool, &args);
w2 = ap_getword_conf(parms->pool, &args);
if (*w == '\0' || *args != 0)
return apr_pstrcat(parms->pool, cmd->name, " takes 1-2 arguments",
cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
return cmd->AP_TAKE2(parms, mconfig, w, *w2 ? w2 : NULL);
case TAKE3:
w = ap_getword_conf(parms->pool, &args);
w2 = ap_getword_conf(parms->pool, &args);
w3 = ap_getword_conf(parms->pool, &args);
if (*w == '\0' || *w2 == '\0' || *w3 == '\0' || *args != 0)
return apr_pstrcat(parms->pool, cmd->name,
" takes three arguments",
cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
case TAKE23:
w = ap_getword_conf(parms->pool, &args);
w2 = ap_getword_conf(parms->pool, &args);
w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
if (*w == '\0' || *w2 == '\0' || *args != 0)
return apr_pstrcat(parms->pool, cmd->name,
" takes two or three arguments",
cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
case TAKE123:
w = ap_getword_conf(parms->pool, &args);
w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
if (*w == '\0' || *args != 0)
return apr_pstrcat(parms->pool, cmd->name,
" takes one, two or three arguments",
cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
case TAKE13:
w = ap_getword_conf(parms->pool, &args);
w2 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
w3 = *args ? ap_getword_conf(parms->pool, &args) : NULL;
if (*w == '\0' || (w2 && *w2 && !w3) || *args != 0)
return apr_pstrcat(parms->pool, cmd->name,
" takes one or three arguments",
cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
return cmd->AP_TAKE3(parms, mconfig, w, w2, w3);
case ITERATE:
while (*(w = ap_getword_conf(parms->pool, &args)) != '\0') {
errmsg = cmd->AP_TAKE1(parms, mconfig, w);
if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0)
return errmsg;
}
return errmsg;
case ITERATE2:
w = ap_getword_conf(parms->pool, &args);
if (*w == '\0' || *args == 0)
return apr_pstrcat(parms->pool, cmd->name,
" requires at least two arguments",
cmd->errmsg ? ", " : NULL, cmd->errmsg, NULL);
while (*(w2 = ap_getword_conf(parms->pool, &args)) != '\0') {
errmsg = cmd->AP_TAKE2(parms, mconfig, w, w2);
if (errmsg && strcmp(errmsg, DECLINE_CMD) != 0)
return errmsg;
}
return errmsg;
case FLAG:
w = ap_getword_conf(parms->pool, &args);
if (*w == '\0' || (strcasecmp(w, "on") && strcasecmp(w, "off")))
return apr_pstrcat(parms->pool, cmd->name, " must be On or Off",
NULL);
return cmd->AP_FLAG(parms, mconfig, strcasecmp(w, "off") != 0);
default:
return apr_pstrcat(parms->pool, cmd->name,
" is improperly configured internally (server bug)",
NULL);
}
}
/**
* @brief Fetch an URL and fill the content into a memory buffer.
*
* Fill an msc_curl_memory_buffer_t structure with the content of an given
* URL.
*
* @note While fetching the content, it will present the ModSecurity instance
* to the remote server, trough: ModSecurity Unique ID, ModSecurity
* status line and also, if given, key that can be used to
* authentication. Such data is presented in the following HTTP headers:
* - ModSec-status
* - ModSec-unique-id
* - ModSec-key
*
* @warning Cleanup the memory after use it.
*
* @param mp pointer to the memory pool.
* @param uri URI to be fetched.
* @param key KEY to be present as ModSec-key.
* @param chunk pointer to an msc_curl_memory_buffer_t struct.
* @param error_msg pointer an char pointer, filled is something went wrong.
*
* @retval n>=0 everything went fine.
* @retval n<-1 Something wrong happened, further details on error_msg.
* n=-2 Download failed, but operation should not be aborted.
* n=-3 ModSecurity was not compiled with curl support.
*
*/
int msc_remote_download_content(apr_pool_t *mp, const char *uri, const char *key,
struct msc_curl_memory_buffer_t *chunk, char **error_msg)
{
#ifdef WITH_CURL
CURL *curl;
CURLcode res;
char id[(APR_SHA1_DIGESTSIZE*2) + 1];
char *apr_id = NULL;
char *beacon_str = NULL;
char *beacon_apr = NULL;
int beacon_str_len = 0;
int ret = 0;
chunk->size = 0;
memset(id, '\0', sizeof(id));
if (msc_status_engine_unique_id(id))
{
sprintf(id, "no unique id");
}
apr_id = apr_psprintf(mp, "ModSec-unique-id: %s", id);
curl = curl_easy_init();
beacon_str_len = msc_beacon_string(NULL, 0);
beacon_str = malloc(sizeof(char) * beacon_str_len + 1);
if (beacon_str == NULL)
{
beacon_str = "Failed to retrieve beacon string";
beacon_apr = apr_psprintf(mp, "ModSec-status: %s", beacon_str);
}
else
{
msc_beacon_string(beacon_str, beacon_str_len);
beacon_apr = apr_psprintf(mp, "ModSec-status: %s", beacon_str);
free(beacon_str);
}
if (curl)
{
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
curl_easy_setopt(curl, CURLOPT_URL, uri);
headers_chunk = curl_slist_append(headers_chunk, apr_id);
headers_chunk = curl_slist_append(headers_chunk, beacon_apr);
if (key != NULL)
{
char *header_key = NULL;
header_key = apr_psprintf(mp, "ModSec-key: %s", key);
headers_chunk = curl_slist_append(headers_chunk, header_key);
}
/* 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);
if (res_len > 0) {
curl_easy_setopt(curl, CURLOPT_CAINFO, strdup(buf));
}
free(buf);
#endif
/* those are the default options, but lets make sure */
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1);
/* send all data to this function */
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, msc_curl_write_memory_cb);
/* we pass our 'chunk' struct to the callback function */
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)chunk);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "modesecurity");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers_chunk);
/* We want Curl to return error in case there is an HTTP error code */
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
if (remote_rules_fail_action == REMOTE_RULES_WARN_ON_FAIL)
{
if (remote_rules_fail_message == NULL)
{
remote_rules_fail_message = "";
}
remote_rules_fail_message = apr_psprintf(mp, "%sFailed to " \
"download: \"%s\" error: %s. ",
remote_rules_fail_message, uri,
curl_easy_strerror(res));
ret = -2;
goto failed;
}
else
{
*error_msg = apr_psprintf(mp, "Failed to download: \"%s\" " \
"error: %s ",
uri, curl_easy_strerror(res));
ret = -1;
goto failed;
}
}
curl_slist_free_all(headers_chunk);
}
failed:
curl_easy_cleanup(curl);
return ret;
#else
return -3;
#endif
}
/**
* @brief Setup an apr_crypto_key_t from a given password and salt.
*
* apr_crypto_* demands the key to be in a format of an apr_crypto_key_t which
* is an structure that may be defined depending on the crypto provider, thus,
* making necessary for us to create this structure using apr internal
* functions.
*
* @warning We trust that the paramenter used in apr, such as the algorithm,
* key size and other parameters won't change, if they do it may
* break the interoperability with this function with others
* implementations, as the key will end up with a different value
* than the one expected.
*
* @param pool pointer to the memory pool to be used.
* @param key password to be used while creating the key.
* @param apr_key pointer to the pointer of an apr_crypto_key_t structure.
* @param f pointer to apr_crypto_t.
* @param salt string to be used as salt of the key generation
* @param error_msg pointer to char pointer, which is filled if something
* went wrong.
*
* @retval n>=0 everything went fine.
* @retval n<-1 Something wrong happened, check error_msg for further details.
*
*/
#ifdef WITH_APU_CRYPTO
int msc_remote_enc_key_setup(apr_pool_t *pool,
const char *key,
apr_crypto_key_t **apr_key,
apr_crypto_t *f,
unsigned char *salt,
char **error_msg)
{
apr_size_t key_len = strlen(key);
apr_size_t salt_len = 16; //FIXME: salt_len should not be hard coded.
const int do_pad = 1;
apr_status_t rv;
rv = apr_crypto_passphrase(
(apr_crypto_key_t **) apr_key,
NULL,
(const char *) key,
(apr_size_t) key_len,
(const unsigned char *) salt,
(apr_size_t) salt_len,
APR_KEY_AES_256,
APR_MODE_CBC,
(const int) do_pad,
(const int) 4096,
(const apr_crypto_t *) f,
(apr_pool_t *) pool);
if (rv == APR_ENOKEY)
{
*error_msg = "Internal error - apr_crypto_passphrase: Missing key";
return -1;
}
else if (rv == APR_EPADDING)
{
*error_msg = "Internal error - apr_crypto_passphrase: APR_EPADDING";
return -1;
}
else if (rv == APR_EKEYTYPE)
{
*error_msg = "Internal error - apr_crypto_passphrase: APR_EKEYTYPE";
return -1;
}
else if (rv != APR_SUCCESS)
{
*error_msg = "Internal error - apr_crypto_passphrase: Unknown error";
return -1;
}
return 0;
}
#endif
/**
* @brief Decrypt an buffer into a memory buffer.
*
* Decrypt an msc_curl_memory_buffer_t structure into another
* msc_curl_memory_buffer_t.
*
* Using the key provided, it creates and apr_key and uses it to decript the
* content provided on chunk. The plain text content is saved under
* chunk_plain.
*
* @warning Cleanup memory after usage.
*
* @param pool pointer to the memory pool to be used.
* @param key pointer to the char array to be used as the key.
* @param chunk msc_curl_memory_buffer_t that contains the encrypted content.
* @param plain_text unsigned char which will hold the content of an url
* @param plain_text_len size of the plain_text buffer
* @param error_msg pointer to char pointer that is filled if something went
* wrong.
*
* @retval n>=0 everything went fine.
* @retval n<-1 Something wrong happened, further details on error_msg.
*
*/
#ifdef WITH_APU_CRYPTO
int msc_remote_decrypt(apr_pool_t *pool,
const char *key,
struct msc_curl_memory_buffer_t *chunk,
unsigned char **plain_text,
apr_size_t *plain_text_len,
char **error_msg)
{
apr_crypto_key_t *apr_key = NULL;
apr_crypto_t *f = NULL;
const apr_crypto_driver_t *driver = NULL;
const apu_err_t *err = NULL;
apr_status_t rv;
unsigned char *iv = NULL;
unsigned char *ciphered_text = NULL;
unsigned char *salt = NULL;
apr_crypto_block_t *block = NULL;
apr_size_t block_size = 0;
apr_size_t len = 0;
// FIXME: size should not be hardcoded.
// at least size of IV + Salt
if (chunk->size < 16+16+1)
{
*error_msg = "Failed to download rules from a remote server: " \
"Unexpected content.";
return -1;
}
iv = chunk->memory;
salt = chunk->memory + 16;
ciphered_text = chunk->memory + (16 + 16);
rv = apr_crypto_init(pool);
if (rv != APR_SUCCESS)
{
*error_msg = "Internal error: failed to init crypto";
return -1;
}
rv = apr_crypto_get_driver(&driver, APU_CRYPTO_RECOMMENDED_DRIVER, NULL,
&err, pool);
if (rv != APR_SUCCESS || driver == NULL)
{
*error_msg = "Internal error - apr_crypto_get_driver: Unknown error";
return -1;
}
rv = apr_crypto_make(&f, driver, NULL, pool);
if (rv != APR_SUCCESS)
{
*error_msg = "Internal error - apr_crypto_make: Unknown error";
return -1;
}
msc_remote_enc_key_setup(pool, key, &apr_key, f, salt, error_msg);
if (*error_msg != NULL)
{
return -1;
}
rv = apr_crypto_block_decrypt_init(&block, &block_size, iv, apr_key, pool);
if (rv == APR_ENOKEY)
{
*error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
"Missing key";
return -1;
}
else if (rv == APR_ENOIV)
{
*error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
"Missing IV";
return -1;
}
else if (rv == APR_EKEYTYPE)
{
*error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
"Wrong key type";
return -1;
}
else if (rv == APR_EKEYLENGTH)
{
*error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
"Wrong key length";
return -1;
}
else if (rv != APR_SUCCESS)
{
*error_msg = "Internal error - apr_crypto_block_decrypt_init: " \
"Unknown error";
return -1;
}
//FIXME: size should not be hardcoded like that.
// 32 = iv + salt size.
rv = apr_crypto_block_decrypt(plain_text, plain_text_len,
ciphered_text, (apr_size_t) chunk->size - (16 + 16) ,
block);
if (rv != APR_SUCCESS)
{
*error_msg = "Internal error - apr_crypto_block_decrypt: Failed to " \
"decrypt";
return -1;
}
/* finalise the decryption */
rv = apr_crypto_block_decrypt_finish(*plain_text + *plain_text_len, &len,
block);
if (rv != APR_SUCCESS)
{
*error_msg = "Internal error - apr_crypto_block_decrypt_finish: " \
"Failed to decrypt";
return -1;
}
apr_crypto_block_cleanup(block);
apr_crypto_cleanup(f);
// Shutdown the apr_crypto seems to be the correct thing to do.
// However it seems to add instability especially if mod_ssl is enabled.
// apr_crypto_shutdown(driver);
return 0;
}
#endif
/**
* @brief Add SecRules from a given URI.
*
* Fetch the URI and using the key provided into params decrypt and install
* the downloaded set of rules.
*
* @warning Cleanup the memory may be necessary.
*
* @param orig_parms origin parms used at SecRemoteRule
* @param remote_rules_server pointer to the filled msc_remote_rules_server
* structure.
* @param error_msg pointer to char pointer that will be filled if something
* went wrong.
*
*
* @retval n>=0 everything went fine.
* @retval n<-1 Something wrong happened, further details on error_msg.
*
*/
int msc_remote_add_rules_from_uri(cmd_parms *orig_parms,
msc_remote_rules_server *remote_rules_server,
char **error_msg)
{
#ifdef WITH_REMOTE_RULES
struct msc_curl_memory_buffer_t downloaded_content;
unsigned char *plain_text = NULL;
int len = 0;
int start = 0;
int end = 0;
int added_rules = 0;
int res = 0;
apr_size_t plain_text_len = 0;
apr_pool_t *mp = orig_parms->pool;
downloaded_content.size = 0;
downloaded_content.memory = NULL;
res = msc_remote_download_content(mp, remote_rules_server->uri,
remote_rules_server->key, &downloaded_content, error_msg);
if (*error_msg != NULL)
{
return -1;
}
/* error_msg is not filled when the user set SecRemoteRulesFailAction
* to warn
*/
if (res != 0)
{
return res;
}
if (remote_rules_server->crypto == 1)
{
#ifdef WITH_APU_CRYPTO
msc_remote_decrypt(mp, remote_rules_server->key, &downloaded_content,
&plain_text,
&plain_text_len,
error_msg);
if (*error_msg != NULL)
{
msc_remote_clean_chunk(&downloaded_content);
return -1;
}
#else
*error_msg = "ModSecurity was not compiled with crypto support.\n";
msc_remote_clean_chunk(&downloaded_content);
return -1;
#endif
msc_remote_clean_chunk(&downloaded_content);
}
else
{
plain_text = downloaded_content.memory;
plain_text_len = strlen(plain_text);
}
len = 0;
plain_text_len = strlen(plain_text);
while (len < plain_text_len)
{
if (plain_text[len] == '\n')
{
const char *rule = NULL;
char *cmd_name = NULL;
const command_rec *cmd;
ap_directive_t *newdir;
cmd_parms *parms = apr_pcalloc(mp, sizeof (cmd_parms));
rule = plain_text + start;
end = len;
plain_text[len] = '\0';
memcpy(parms, orig_parms, sizeof(cmd_parms));
if (*rule == '#' || *rule == '\0')
{
goto next;
}
cmd_name = ap_getword_conf(mp, &rule);
cmd = msc_remote_find_command(cmd_name, security2_module.cmds);
if (cmd == NULL)
{
*error_msg = apr_pstrcat(mp, "Unknown command in config: ",
cmd_name, NULL);
return -1;
}
newdir = apr_pcalloc(mp, sizeof(ap_directive_t));
newdir->filename = "remote server";
newdir->line_num = -1;
newdir->directive = cmd_name;
newdir->args = apr_pstrdup(mp, rule);
parms->directive = newdir;
#ifdef WIN32
// some config commands fail in APR when there are file
// permission issues or other OS-specific problems
//
__try
{
#endif
*error_msg = (char *) msc_remote_invoke_cmd(cmd, parms,
remote_rules_server->context, rule);
if (*error_msg != NULL)
{
return -1;
}
added_rules++;
#ifdef WIN32
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
*error_msg = "Command failed to execute (check file/folder" \
"permissions, syntax, etc.).";
return -1;
}
#endif
next:
start = end + 1;
}
len++;
}
remote_rules_server->amount_of_rules = added_rules;
if (remote_rules_server->crypto != 1)
{
msc_remote_clean_chunk(&downloaded_content);
}
return 0;
#else
*error_msg = "SecRemoteRules was not enabled during ModSecurity " \
"compilation.";
return -1;
#endif
}
int msc_remote_clean_chunk(struct msc_curl_memory_buffer_t *chunk)
{
if (chunk->size == 0)
{
goto end;
}
if (chunk->memory == NULL)
{
goto end;
}
free(chunk->memory);
chunk->size = 0;
end:
return 0;
}
modsecurity-apache-2.9.13/apache2/msc_remote_rules.h 0000664 0000000 0000000 00000004032 15174655764 0022442 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#ifndef MSC_REMOTE_RULES_H
#define MSC_REMOTE_RULES_H
/* forward declarations */
typedef struct msc_remote_rules_server msc_remote_rules_server;
struct msc_curl_memory_buffer_t;
#include "modsecurity.h"
#include
#include
#include
#include
#include "http_core.h"
#include "http_config.h"
#ifdef WITH_APU_CRYPTO
#include
#endif
struct msc_remote_rules_server {
directory_config *context;
const char *context_label;
const char *uri;
const char *key;
int amount_of_rules;
int crypto;
};
const char *msc_remote_invoke_cmd(const command_rec *cmd, cmd_parms *parms,
void *mconfig, const char *args);
int msc_remote_download_content(apr_pool_t *mp, const char *uri, const char *key,
struct msc_curl_memory_buffer_t *chunk, char **error_msg);
#ifdef WITH_APU_CRYPTO
int msc_remote_enc_key_setup(apr_pool_t *pool,
const char *key,
apr_crypto_key_t **apr_key,
apr_crypto_t *f,
unsigned char *salt,
char **error_msg);
int msc_remote_decrypt(apr_pool_t *pool,
const char *key,
struct msc_curl_memory_buffer_t *chunk,
unsigned char **plain_text,
apr_size_t *plain_text_len,
char **error_msg);
#endif
int msc_remote_add_rules_from_uri(cmd_parms *orig_parms,
msc_remote_rules_server *remote_rules_server,
char **error_msg);
int msc_remote_clean_chunk(struct msc_curl_memory_buffer_t *chunk);
#endif
modsecurity-apache-2.9.13/apache2/msc_reqbody.c 0000664 0000000 0000000 00000115524 15174655764 0021406 0 ustar 00root root 0000000 0000000 /*
* 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 "modsecurity.h"
#include "re.h"
#include "msc_parsers.h"
#define CHUNK_CAPACITY 8192
/**
*
*/
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));
if (metadata == NULL) return;
metadata->name = name;
metadata->init = fn_init;
metadata->process = fn_process;
metadata->complete = fn_complete;
apr_table_setn(engine->reqbody_processors, name, (void *)metadata);
}
/**
* 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) {
/* Prepare to store request body in memory. */
msr->msc_reqbody_chunks = apr_array_make(msr->msc_reqbody_mp,
32, sizeof(msc_data_chunk *));
if (msr->msc_reqbody_chunks == NULL) {
*error_msg = apr_pstrdup(msr->mp, "Input filter: Failed to prepare in-memory storage.");
return -1;
}
} else {
/* Prepare to store request body on disk. */
msr->msc_reqbody_filename = apr_psprintf(msr->mp, "%s/%s-%s-request_body-XXXXXX",
msr->txcfg->tmp_dir, current_filetime(msr->mp), msr->txid);
if (msr->msc_reqbody_filename == NULL) {
*error_msg = apr_pstrdup(msr->mp, "Input filter: Failed to generate an on-disk filename.");
return -1;
}
msr->msc_reqbody_fd = msc_mkstemp((char *)msr->msc_reqbody_filename);
if (msr->msc_reqbody_fd < 0) {
*error_msg = apr_psprintf(msr->mp, "Input filter: Failed to create temporary file: %s",
msr->msc_reqbody_filename);
return -1;
}
msr_log(msr, 4, "Input filter: Created temporary file to store request body: %s",
msr->msc_reqbody_filename);
}
return 1;
}
/**
* 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;
/* Create a separate memory pool that will be used
* to allocate structures from (not data, which is allocated
* via malloc).
*/
apr_pool_create(&msr->msc_reqbody_mp, NULL);
/* Initialise request body processors, if any. */
if (msr->msc_reqbody_processor != NULL) {
char *my_error_msg = NULL;
msre_reqbody_processor_metadata *metadata =
(msre_reqbody_processor_metadata *)apr_table_get(msr->modsecurity->msre->reqbody_processors, msr->msc_reqbody_processor);
if (metadata != NULL) {
if ( (metadata->init != NULL)
&& (metadata->init(msr, &my_error_msg) < 0))
{
*error_msg = apr_psprintf(msr->mp,
"%s parsing error (init): %s",
msr->msc_reqbody_processor,
my_error_msg);
msr->msc_reqbody_error = 1;
msr->msc_reqbody_error_msg = my_error_msg;
msr_log(msr, 2, "%s", *error_msg);
}
}
// TODO: All these below need to be registered in the same way as above
else if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) {
if (multipart_init(msr, &my_error_msg) < 0) {
*error_msg = apr_psprintf(msr->mp, "Multipart parsing error (init): %s", my_error_msg);
msr->msc_reqbody_error = 1;
msr->msc_reqbody_error_msg = my_error_msg;
msr_log(msr, 2, "%s", *error_msg);
}
}
else if (strcmp(msr->msc_reqbody_processor, "XML") == 0) {
if (xml_init(msr, &my_error_msg) < 0) {
*error_msg = apr_psprintf(msr->mp, "XML parsing error (init): %s", my_error_msg);
msr->msc_reqbody_error = 1;
msr->msc_reqbody_error_msg = my_error_msg;
msr_log(msr, 2, "%s", *error_msg);
}
}
else if (strcmp(msr->msc_reqbody_processor, "JSON") == 0) {
#ifdef WITH_YAJL
if (json_init(msr, &my_error_msg) < 0) {
*error_msg = apr_psprintf(msr->mp, "JSON parsing error (init): %s", my_error_msg);
msr->msc_reqbody_error = 1;
msr->msc_reqbody_error_msg = my_error_msg;
msr_log(msr, 2, "%s", *error_msg);
}
#else
*error_msg = apr_psprintf(msr->mp, "JSON support was not enabled");
msr->msc_reqbody_error = 1;
msr->msc_reqbody_error_msg = my_error_msg;
msr_log(msr, 2, "%s", *error_msg);
#endif
}
else if (strcmp(msr->msc_reqbody_processor, "URLENCODED") == 0) {
/* Do nothing, URLENCODED processor does not support streaming yet. */
}
else {
*error_msg = apr_psprintf(msr->mp, "Unknown request body processor: %s",
msr->msc_reqbody_processor);
return -1;
}
}
return modsecurity_request_body_start_init(msr, error_msg);
}
/**
* Stores a chunk of request body data to disk.
*/
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;
i = write(msr->msc_reqbody_fd, data, length);
if (i != length) {
*error_msg = apr_psprintf(msr->mp, "Input filter: Failed writing %" APR_SIZE_T_FMT
" bytes to temporary file (rc %" APR_SIZE_T_FMT ").", length, i);
return -1;
}
return 1;
}
/**
* Stores one chunk of request body data in memory.
*/
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? */
if ((msr->msc_reqbody_spilltodisk)
&& (msr->txcfg->reqbody_buffering != REQUEST_BODY_FORCEBUF_ON)
&& (msr->msc_reqbody_length + length > (apr_size_t)msr->txcfg->reqbody_inmemory_limit))
{
msc_data_chunk **chunks;
unsigned int disklen = 0;
int i;
msr_log(msr, 4, "Input filter: Request too large to store in memory, switching to disk.");
/* NOTE Must use modsecurity_request_body_store_disk() here
* to prevent data to be sent to the streaming
* processors again.
*/
/* Initialise disk storage */
msr->msc_reqbody_storage = MSC_REQBODY_DISK;
if (modsecurity_request_body_start_init(msr, error_msg) < 0) return -1;
/* Write the data we keep in memory */
chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) {
disklen += chunks[i]->length;
if (modsecurity_request_body_store_disk(msr, chunks[i]->data, chunks[i]->length, error_msg) < 0) {
return -1;
}
free(chunks[i]->data);
chunks[i]->data = NULL;
}
/* Clear the memory pool as we no longer need the bits. */
/* IMP1 But since we only used apr_pool_clear memory might
* not be released back to the OS straight away?
*/
msr->msc_reqbody_chunks = NULL;
apr_pool_clear(msr->msc_reqbody_mp);
msr_log(msr, 4, "Input filter: Wrote %u bytes from memory to disk.", disklen);
/* Continue with disk storage from now on */
return modsecurity_request_body_store_disk(msr, data, length, error_msg);
}
/* If we're here that means we are not over the
* request body in-memory limit yet.
*/
{
unsigned long int bucket_offset, bucket_left;
bucket_offset = 0;
bucket_left = length;
/* Although we store the request body in chunks we don't
* want to use the same chunk sizes as the incoming memory
* buffers. They are often of very small sizes and that
* would make us waste a lot of memory. That's why we
* use our own chunks of CHUNK_CAPACITY sizes.
*/
/* Loop until we empty this bucket into our chunks. */
while(bucket_left > 0) {
/* Allocate a new chunk if we have to. */
if (msr->msc_reqbody_chunk_current == NULL) {
msr->msc_reqbody_chunk_current = (msc_data_chunk *)
apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk));
if (msr->msc_reqbody_chunk_current == NULL) {
*error_msg = apr_psprintf(msr->mp, "Input filter: Failed to allocate %lu bytes "
"for request body chunk.", (unsigned long)sizeof(msc_data_chunk));
return -1;
}
msr->msc_reqbody_chunk_current->data = malloc(CHUNK_CAPACITY);
if (msr->msc_reqbody_chunk_current->data == NULL) {
*error_msg = apr_psprintf(msr->mp, "Input filter: Failed to allocate %d bytes "
"for request body chunk data.", CHUNK_CAPACITY);
return -1;
}
msr->msc_reqbody_chunk_current->length = 0;
msr->msc_reqbody_chunk_current->is_permanent = 1;
*(const msc_data_chunk **)apr_array_push(msr->msc_reqbody_chunks)
= msr->msc_reqbody_chunk_current;
}
if (bucket_left < (CHUNK_CAPACITY - msr->msc_reqbody_chunk_current->length)) {
/* There's enough space in the current chunk. */
memcpy(msr->msc_reqbody_chunk_current->data +
msr->msc_reqbody_chunk_current->length, data + bucket_offset, bucket_left);
msr->msc_reqbody_chunk_current->length += bucket_left;
bucket_left = 0;
} else {
/* Fill the existing chunk. */
unsigned long int copy_length = CHUNK_CAPACITY -
msr->msc_reqbody_chunk_current->length;
memcpy(msr->msc_reqbody_chunk_current->data +
msr->msc_reqbody_chunk_current->length, data + bucket_offset, copy_length);
bucket_offset += copy_length;
bucket_left -= copy_length;
msr->msc_reqbody_chunk_current->length += copy_length;
/* We're done with this chunk. Setting the pointer
* to NULL is going to force a new chunk to be allocated
* on the next go.
*/
msr->msc_reqbody_chunk_current = NULL;
}
}
msr->msc_reqbody_length += length;
}
return 1;
}
/**
* Stores one chunk of request body data. Returns -1 on error.
*/
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
* data to it first (but only if it did not report an
* error on previous invocations).
*/
if ((msr->msc_reqbody_processor != NULL) && (msr->msc_reqbody_error == 0)) {
char *my_error_msg = NULL;
msre_reqbody_processor_metadata *metadata =
(msre_reqbody_processor_metadata *)apr_table_get(msr->modsecurity->msre->reqbody_processors, msr->msc_reqbody_processor);
if (metadata != NULL) {
if ( (metadata->process != NULL)
&& (metadata->process(msr, data, length, &my_error_msg) < 0))
{
*error_msg = apr_psprintf(msr->mp,
"%s parsing error: %s",
msr->msc_reqbody_processor,
my_error_msg);
msr->msc_reqbody_error = 1;
msr->msc_reqbody_error_msg = my_error_msg;
msr_log(msr, 2, "%s", *error_msg);
}
}
// TODO: All these below need to be registered in the same way as above
else if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) {
/* The per-request data length counter will
* be updated by the multipart parser.
*/
/* Process data as multipart/form-data. */
if (multipart_process_chunk(msr, data, length, &my_error_msg) < 0) {
*error_msg = apr_psprintf(msr->mp, "Multipart parsing error: %s", my_error_msg);
msr->msc_reqbody_error = 1;
msr->msc_reqbody_error_msg = *error_msg;
msr_log(msr, 2, "%s", *error_msg);
}
}
else if (strcmp(msr->msc_reqbody_processor, "XML") == 0) {
/* Increase per-request data length counter. */
msr->msc_reqbody_no_files_length += length;
/* Process data as XML. */
if (xml_process_chunk(msr, data, length, &my_error_msg) < 0) {
*error_msg = apr_psprintf(msr->mp, "XML parsing error: %s", my_error_msg);
msr->msc_reqbody_error = 1;
msr->msc_reqbody_error_msg = *error_msg;
msr_log(msr, 2, "%s", *error_msg);
}
}
else if (strcmp(msr->msc_reqbody_processor, "JSON") == 0) {
/* Increase per-request data length counter. */
msr->msc_reqbody_no_files_length += length;
/* Process data as JSON. */
#ifdef WITH_YAJL
if (json_process_chunk(msr, data, length, &my_error_msg) < 0) {
*error_msg = apr_psprintf(msr->mp, "JSON parsing error: %s", my_error_msg);
msr->msc_reqbody_error = 1;
msr->msc_reqbody_error_msg = *error_msg;
msr_log(msr, 2, "%s", *error_msg);
}
#else
*error_msg = apr_psprintf(msr->mp, "JSON support was not enabled");
msr->msc_reqbody_error = 1;
msr->msc_reqbody_error_msg = *error_msg;
msr_log(msr, 2, "%s", *error_msg);
#endif
}
else if (strcmp(msr->msc_reqbody_processor, "URLENCODED") == 0) {
/* Increase per-request data length counter. */
msr->msc_reqbody_no_files_length += length;
/* Do nothing else, URLENCODED processor does not support streaming. */
}
else {
*error_msg = apr_psprintf(msr->mp, "Unknown request body processor: %s",
msr->msc_reqbody_processor);
return -1;
}
} else if (msr->txcfg->reqbody_buffering != REQUEST_BODY_FORCEBUF_OFF) {
/* Increase per-request data length counter if forcing buffering. */
msr->msc_reqbody_no_files_length += length;
}
/* 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) {
if(msr->txcfg->is_enabled == MODSEC_ENABLED)
return -5;
}
}
/* Store data. */
if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) {
return modsecurity_request_body_store_memory(msr, data, length, error_msg);
}
else
if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
return modsecurity_request_body_store_disk(msr, data, length, error_msg);
}
/* Should never happen. */
*error_msg = apr_psprintf(msr->mp, "Internal error, unknown value for msc_reqbody_storage: %u",
msr->msc_reqbody_storage);
return -1;
}
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;
}
else {
data = (char *)malloc(msr->stream_input_length + 1 - buflen);
if(data == NULL)
return -1;
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);
msr->stream_input_data = (char *)stream_input_body;
}
if (msr->stream_input_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;
}
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);
#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;
*error_msg = NULL;
/* Allocate a buffer large enough to hold the request body. */
if (msr->msc_reqbody_length + 1 == 0) {
*error_msg = apr_psprintf(msr->mp, "Internal error, request body length will overflow: %u",
msr->msc_reqbody_length);
return -1;
}
msr->msc_reqbody_buffer = malloc(msr->msc_reqbody_length + 1);
if (msr->msc_reqbody_buffer == NULL) {
*error_msg = apr_psprintf(msr->mp, "Unable to allocate memory to hold request body. Asked for %u bytes.",
msr->msc_reqbody_length + 1);
return -1;
}
msr->msc_reqbody_buffer[msr->msc_reqbody_length] = '\0';
/* Copy the data we keep in chunks into the new buffer. */
sofar = 0;
d = msr->msc_reqbody_buffer;
chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) {
if (sofar + chunks[i]->length <= msr->msc_reqbody_length) {
memcpy(d, chunks[i]->data, chunks[i]->length);
d += chunks[i]->length;
sofar += chunks[i]->length;
} else {
*error_msg = apr_psprintf(msr->mp, "Internal error, request body buffer overflow.");
return -1;
}
}
/* Now free the memory used by the chunks. */
chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) {
free(chunks[i]->data);
chunks[i]->data = NULL;
}
/* Create a new array with only one chunk in it. */
msr->msc_reqbody_chunks = apr_array_make(msr->msc_reqbody_mp, 2, sizeof(msc_data_chunk *));
if (msr->msc_reqbody_chunks == NULL) {
*error_msg = apr_pstrdup(msr->mp, "Failed to create structure to hold request body.");
return -1;
}
one_chunk = (msc_data_chunk *)apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk));
one_chunk->data = msr->msc_reqbody_buffer;
one_chunk->length = msr->msc_reqbody_length;
one_chunk->is_permanent = 1;
*(const msc_data_chunk **)apr_array_push(msr->msc_reqbody_chunks) = one_chunk;
if(msr->txcfg->reqbody_limit > 0 && msr->txcfg->reqbody_limit < msr->msc_reqbody_length) {
msr->msc_reqbody_length = msr->txcfg->reqbody_limit;
}
return 1;
}
/**
*
*/
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;
/* Create the raw buffer */
if (modsecurity_request_body_end_raw(msr, error_msg) != 1) {
return -1;
}
/* Parse URL-encoded arguments in the request body. */
if (parse_arguments(msr, msr->msc_reqbody_buffer, msr->msc_reqbody_length,
msr->txcfg->argument_separator, "BODY", msr->arguments, &invalid_count) < 0)
{
*error_msg = apr_pstrdup(msr->mp, "Initialisation: Error occurred while parsing BODY arguments.");
return -1;
}
if (invalid_count) {
msr->urlencoded_error = 1;
}
return 1;
}
/**
* 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. */
if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
if (msr->msc_reqbody_fd > 0) {
close(msr->msc_reqbody_fd);
msr->msc_reqbody_fd = -1;
}
}
/* 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;
msre_reqbody_processor_metadata *metadata =
(msre_reqbody_processor_metadata *)apr_table_get(msr->modsecurity->msre->reqbody_processors, msr->msc_reqbody_processor);
if (metadata != NULL) {
if ( (metadata->complete != NULL)
&& (metadata->complete(msr, &my_error_msg) < 0))
{
*error_msg = apr_psprintf(msr->mp,
"%s parsing error (complete): %s",
msr->msc_reqbody_processor,
my_error_msg);
msr->msc_reqbody_error = 1;
msr->msc_reqbody_error_msg = my_error_msg;
msr_log(msr, 2, "%s", *error_msg);
}
}
// TODO: All these below need to be registered in the same way as above
else if (strcmp(msr->msc_reqbody_processor, "MULTIPART") == 0) {
if (multipart_complete(msr, &my_error_msg) < 0) {
*error_msg = apr_psprintf(msr->mp, "Multipart parsing error: %s", my_error_msg);
msr->msc_reqbody_error = 1;
msr->msc_reqbody_error_msg = *error_msg;
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "%s", *error_msg);
}
return -1;
}
if (multipart_get_arguments(msr, "BODY", msr->arguments) < 0) {
*error_msg = "Multipart parsing error: Failed to retrieve arguments.";
msr->msc_reqbody_error = 1;
msr->msc_reqbody_error_msg = *error_msg;
msr_log(msr, 2, "%s", *error_msg);
return -1;
}
}
else if (strcmp(msr->msc_reqbody_processor, "JSON") == 0) {
#ifdef WITH_YAJL
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;
msr_log(msr, 2, "%s", *error_msg);
return -1;
}
#else
*error_msg = apr_psprintf(msr->mp, "JSON support was not enabled");
msr->msc_reqbody_error = 1;
msr->msc_reqbody_error_msg = *error_msg;
msr_log(msr, 2, "%s", *error_msg);
return -1;
#endif
}
else if (strcmp(msr->msc_reqbody_processor, "URLENCODED") == 0) {
return modsecurity_request_body_end_urlencoded(msr, error_msg);
}
else if (strcmp(msr->msc_reqbody_processor, "XML") == 0) {
if (xml_complete(msr, &my_error_msg) < 0) {
*error_msg = apr_psprintf(msr->mp, "XML parser error: %s", my_error_msg);
msr->msc_reqbody_error = 1;
msr->msc_reqbody_error_msg = *error_msg;
msr_log(msr, 2, "%s", *error_msg);
return -1;
}
}
} else if (msr->txcfg->reqbody_buffering != REQUEST_BODY_FORCEBUF_OFF) {
/* Convert to a single continous buffer, but don't do anything else. */
return modsecurity_request_body_end_raw(msr, error_msg);
}
/* Note the request body no files length. */
msr_log(msr, 4, "Request body no files length: %" APR_SIZE_T_FMT, msr->msc_reqbody_no_files_length);
return 1;
}
/**
* 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) {
msr->msc_reqbody_chunk_position = 0;
msr->msc_reqbody_chunk_offset = 0;
msr->msc_reqbody_disk_chunk = apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk));
if (msr->msc_reqbody_disk_chunk == NULL) {
*error_msg = apr_psprintf(msr->mp, "Failed to allocate %lu bytes for request body disk chunk.",
(unsigned long)sizeof(msc_data_chunk));
return -1;
}
msr->msc_reqbody_disk_chunk->is_permanent = 1;
}
else
if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
msr->msc_reqbody_disk_chunk = apr_pcalloc(msr->msc_reqbody_mp, sizeof(msc_data_chunk));
if (msr->msc_reqbody_disk_chunk == NULL) {
*error_msg = apr_psprintf(msr->mp, "Failed to allocate %lu bytes for request body disk chunk.",
(unsigned long)sizeof(msc_data_chunk));
return -1;
}
msr->msc_reqbody_disk_chunk->is_permanent = 0;
msr->msc_reqbody_disk_chunk->data = apr_palloc(msr->msc_reqbody_mp, CHUNK_CAPACITY);
if (msr->msc_reqbody_disk_chunk->data == NULL) {
*error_msg = apr_psprintf(msr->mp, "Failed to allocate %d bytes for request body disk chunk data.",
CHUNK_CAPACITY);
return -1;
}
msr->msc_reqbody_fd = open(msr->msc_reqbody_filename, O_RDONLY | O_BINARY);
if (msr->msc_reqbody_fd < 0) {
*error_msg = apr_psprintf(msr->mp, "Failed to open temporary file for reading: %s",
msr->msc_reqbody_filename);
return -1;
}
}
return 1;
}
/**
*
*/
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);
msr->msc_reqbody_fd = -1;
}
}
return 1;
}
/**
* Returns one chunk of request body data. It stores a NULL
* in the chunk pointer when there is no data to return. The
* return code is 1 if more calls can be made to retrieve more
* data, 0 if there is no more data to retrieve, or -1 on error.
*
* The caller can limit the amount of data returned by providing
* a non-negative value in nbytes.
*/
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;
if (chunk == NULL) {
*error_msg = apr_pstrdup(msr->mp, "Internal error, retrieving request body chunk.");
return -1;
}
*chunk = NULL;
if (msr->msc_reqbody_storage == MSC_REQBODY_MEMORY) {
/* Are there any chunks left? */
if (msr->msc_reqbody_chunk_position >= msr->msc_reqbody_chunks->nelts) {
/* No more chunks. */
return 0;
}
/* We always respond with the same chunk, just different information in it. */
*chunk = msr->msc_reqbody_disk_chunk;
/* Advance to the current chunk and position on the
* next byte we need to send.
*/
chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
msr->msc_reqbody_disk_chunk->data = chunks[msr->msc_reqbody_chunk_position]->data
+ msr->msc_reqbody_chunk_offset;
if (nbytes < 0) {
/* Send what's left in this chunk as there is no limit on the size. */
msr->msc_reqbody_disk_chunk->length = chunks[msr->msc_reqbody_chunk_position]->length;
msr->msc_reqbody_chunk_position++;
msr->msc_reqbody_chunk_offset = 0;
} else {
/* We have a limit we must obey. */
if (chunks[msr->msc_reqbody_chunk_position]->length -
msr->msc_reqbody_chunk_offset <= (unsigned int)nbytes)
{
/* If what's left in our chunk is less than the limit
* then send it all back.
*/
msr->msc_reqbody_disk_chunk->length =
chunks[msr->msc_reqbody_chunk_position]->length -
msr->msc_reqbody_chunk_offset;
msr->msc_reqbody_chunk_position++;
msr->msc_reqbody_chunk_offset = 0;
} else {
/* If we have more data in our chunk, send the
* maximum bytes we can (nbytes).
*/
msr->msc_reqbody_disk_chunk->length = nbytes;
msr->msc_reqbody_chunk_offset += nbytes;
}
}
/* If we've advanced beyond our last chunk then
* we have no more data to send.
*/
if (msr->msc_reqbody_chunk_position >= msr->msc_reqbody_chunks->nelts) {
return 0; /* No more chunks. */
}
/* More data available. */
return 1;
}
if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
long int my_nbytes = CHUNK_CAPACITY;
int i;
/* Send CHUNK_CAPACITY bytes at a time unless a lower limit was requested. */
if ((nbytes != -1)&&(my_nbytes > nbytes)) {
my_nbytes = nbytes;
}
i = read(msr->msc_reqbody_fd, msr->msc_reqbody_disk_chunk->data, my_nbytes);
if (i < 0) {
*error_msg = apr_psprintf(msr->mp, "Input filter: Error reading from temporary file: %s",
strerror(errno));
return -1;
}
*chunk = msr->msc_reqbody_disk_chunk;
msr->msc_reqbody_disk_chunk->length = i;
if (i == 0) return 0; /* No more data available. */
return 1; /* More data available. */
}
/* Should never happen. */
*error_msg = apr_psprintf(msr->mp, "Internal error, invalid msc_reqbody_storage value: %u",
msr->msc_reqbody_storage);
return -1;
}
/**
*
*/
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. */
if (msr->msc_reqbody_chunks != NULL) {
msc_data_chunk **chunks = (msc_data_chunk **)msr->msc_reqbody_chunks->elts;
int i;
for(i = 0; i < msr->msc_reqbody_chunks->nelts; i++) {
if (chunks[i]->data != NULL) {
free(chunks[i]->data);
chunks[i]->data = NULL;
}
}
}
if (msr->msc_reqbody_storage == MSC_REQBODY_DISK) {
int keep_body = 0;
/* Should we keep the body? This normally
* happens when a PUT method was used, which
* means the body is actually a file.
*/
if ((msr->upload_remove_files == 0)&&(strcasecmp(msr->request_method, "PUT") == 0)) {
if (msr->txcfg->upload_dir != NULL) {
keep_body = 1;
} else {
*error_msg = apr_psprintf(msr->mp, "Input filter: SecUploadDir is undefined, "
"unable to store PUT file.");
}
}
/* Deal with a request body stored in a file. */
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;
}
/* Construct the new filename. */
put_basename = file_basename(msr->msc_reqbody_mp, msr->msc_reqbody_filename);
if (put_basename == NULL) {
*error_msg = apr_psprintf(msr->mp, "Input filter: Failed to generate basename to PUT file \"%s\"", log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename));
return -1;
}
put_filename = apr_psprintf(msr->msc_reqbody_mp, "%s/%s",
msr->txcfg->upload_dir, put_basename);
if (put_filename == NULL) {
*error_msg = apr_psprintf(msr->mp, "Input filter: Failed to generate filename to PUT file \"%s\"", log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename));
return -1;
}
if (apr_file_rename(msr->msc_reqbody_filename, put_filename,
msr->msc_reqbody_mp) != APR_SUCCESS)
{
*error_msg = apr_psprintf(msr->mp, "Input filter: Failed to rename file from \"%s\" to \"%s\".",
log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename),
log_escape(msr->msc_reqbody_mp, put_filename));
return -1;
} else {
msr_log(msr, 4, "Input filter: Moved file from \"%s\" to \"%s\".",
log_escape(msr->msc_reqbody_mp, msr->msc_reqbody_filename),
log_escape(msr->msc_reqbody_mp, put_filename));
}
} else {
/* make sure it is closed first */
if (msr->msc_reqbody_fd > 0) {
close(msr->msc_reqbody_fd);
msr->msc_reqbody_fd = -1;
}
/* We do not want to keep the request body. */
if (apr_file_remove(msr->msc_reqbody_filename,
msr->msc_reqbody_mp) != APR_SUCCESS)
{
*error_msg = apr_psprintf(msr->mp, "Input filter: Failed to delete temporary file: %s",
log_escape(msr->mp, msr->msc_reqbody_filename));
return -1;
}
msr_log(msr, 4, "Input filter: Removed temporary file: %s",
msr->msc_reqbody_filename);
}
nullify:
msr->msc_reqbody_filename = NULL;
}
}
if (msr->msc_reqbody_mp != NULL) {
apr_pool_destroy(msr->msc_reqbody_mp);
msr->msc_reqbody_mp = NULL;
}
return 1;
}
modsecurity-apache-2.9.13/apache2/msc_status_engine.c 0000664 0000000 0000000 00000033123 15174655764 0022603 0 ustar 00root root 0000000 0000000 /*
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
* 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
*
* 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 "msc_status_engine.h"
#include "apr_sha1.h"
#include "modsecurity_config.h"
#ifdef WIN32
#include
#include
#else
#include
#include
#endif
#ifdef DARWIN
#include
#include
#include
#include
#include
#include
#include
#ifndef IFT_ETHER
#define IFT_ETHER 0x6 /* Ethernet CSMACD */
#endif
#endif
#if (defined(__linux__) || defined(__gnu_linux__))
#include
#include
#endif
#ifdef HAVE_SYS_UTSNAME_H
#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,
const char *data, int len) {
int buffer;
int count = 0;
char *result = encoded;
int length = strlen(data);
buffer = data[0];
if (encoded == NULL && len == 0) {
len = length * 3;
count++;
}
if (length > 0) {
int next = 1;
int bitsLeft = 8;
while (count < len && (bitsLeft > 0 || next < length)) {
int index;
if (bitsLeft < 5) {
if (next < length) {
buffer <<= 8;
buffer |= data[next++] & 0xff;
bitsLeft += 8;
} else {
int pad = 5 - bitsLeft;
buffer <<= pad;
bitsLeft += pad;
}
}
index = 0x1f & (buffer >> (bitsLeft - 5));
bitsLeft -= 5;
if (encoded != NULL) {
result[count] = msc_status_engine_basis_32[index];
}
count++;
}
}
if (count < len && encoded != NULL) {
result[count] = '\000';
}
return count;
}
int DSOLOCAL msc_status_engine_fill_with_dots(char *encoded_with_dots,
const char *data, int len, int space)
{
int i;
int count = 0;
if (encoded_with_dots == NULL) {
if (len == 0 && data != NULL) {
len = strlen(data);
}
else if (len == 0 && data == NULL) {
count = -1;
goto return_length;
}
count = len/space + len + 1;
goto return_length;
}
for (i = 0; i < strlen(data) && i < len; i++) {
if (i % space == 0 && i != 0) {
encoded_with_dots[count++] = '.';
}
encoded_with_dots[count++] = data[i];
}
encoded_with_dots[count] = '\0';
return_length:
return count;
}
// Based on:
// http://stackoverflow.com/questions/16858782/how-to-obtain-almost-unique-system-identifier-in-a-cross-platform-way
int DSOLOCAL msc_status_engine_machine_name(char *machine_name, size_t len) {
#ifdef WIN32
DWORD lenComputerName = len;
#endif
memset(machine_name, '\0', sizeof(char) * len);
#ifdef WIN32
if (GetComputerName(machine_name, &lenComputerName) == 0) {
goto failed;
}
#endif
#ifdef HAVE_SYS_UTSNAME_H
static struct utsname u;
if ( uname( &u ) < 0 ) {
goto failed;
}
apr_snprintf(machine_name, len-1, "%s", u.nodename);
#endif
return 0;
failed:
return -1;
}
int DSOLOCAL msc_status_engine_mac_address (unsigned char *mac)
{
#ifdef DARWIN
struct ifaddrs* ifaphead;
struct ifaddrs* ifap;
if ( getifaddrs( &ifaphead ) != 0 ) {
goto failed;
}
// iterate over the net interfaces
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]) {
apr_snprintf(mac, MAC_ADDRESS_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
(unsigned char)LLADDR(sdl)[0],
(unsigned char)LLADDR(sdl)[1],
(unsigned char)LLADDR(sdl)[2],
(unsigned char)LLADDR(sdl)[3],
(unsigned char)LLADDR(sdl)[4],
(unsigned char)LLADDR(sdl)[5]);
break;
}
}
freeifaddrs( ifaphead );
#endif
#if (defined(__linux__) || defined(__gnu_linux__))
struct ifconf conf;
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP );
struct ifreq* ifr;
if ( sock < 0 ) {
goto failed;
}
char ifconfbuf[ 128 * sizeof(struct ifreq) ];
memset( ifconfbuf, 0, sizeof( ifconfbuf ));
conf.ifc_buf = ifconfbuf;
conf.ifc_len = sizeof( ifconfbuf );
if ( ioctl( sock, SIOCGIFCONF, &conf )) {
close(sock);
goto failed;
}
for ( ifr = conf.ifc_req; ifr < conf.ifc_req + conf.ifc_len; ifr++ ) {
if ( ioctl( sock, SIOCGIFFLAGS, ifr )) {
continue; // failed to get flags, skip it
}
if ( ioctl( sock, SIOCGIFHWADDR, ifr ) == 0 ) {
if (!ifr->ifr_addr.sa_data[0] && !ifr->ifr_addr.sa_data[1]
&& !ifr->ifr_addr.sa_data[2]) {
continue;
}
apr_snprintf(mac, MAC_ADDRESS_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
(unsigned char)ifr->ifr_addr.sa_data[0],
(unsigned char)ifr->ifr_addr.sa_data[1],
(unsigned char)ifr->ifr_addr.sa_data[2],
(unsigned char)ifr->ifr_addr.sa_data[3],
(unsigned char)ifr->ifr_addr.sa_data[4],
(unsigned char)ifr->ifr_addr.sa_data[5]);
break;
}
}
close( sock );
#endif
#if WIN32
PIP_ADAPTER_INFO pAdapterInfo;
PIP_ADAPTER_INFO pAdapter = NULL;
DWORD dwRetVal = 0;
ULONG ulOutBufLen = sizeof (IP_ADAPTER_INFO);
pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof (IP_ADAPTER_INFO));
if (!pAdapterInfo) {
goto failed;
}
if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
free(pAdapterInfo);
pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen);
if (!pAdapterInfo) {
goto failed;
}
}
dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
if (dwRetVal != NO_ERROR) {
free(pAdapterInfo);
goto failed;
}
pAdapter = pAdapterInfo;
while (pAdapter && !mac[0] && !mac[1] && !mac[2])
{
if (pAdapter->AddressLength > 4)
{
apr_snprintf(mac, MAC_ADDRESS_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x",
(unsigned char)pAdapter->Address[0],
(unsigned char)pAdapter->Address[1],
(unsigned char)pAdapter->Address[2],
(unsigned char)pAdapter->Address[3],
(unsigned char)pAdapter->Address[4],
(unsigned char)pAdapter->Address[5]);
break;
}
pAdapter = pAdapter->Next;
}
free(pAdapterInfo);
#endif
return 0;
failed:
return -1;
}
int DSOLOCAL msc_status_engine_unique_id (unsigned char *digest)
{
unsigned char hex_digest[APR_SHA1_DIGESTSIZE];
unsigned char *mac_address = NULL, *digptr;
char *machine_name = NULL;
int ret = 0;
int i = 0;
apr_sha1_ctx_t context;
mac_address = malloc(sizeof(char)*(MAC_ADDRESS_SIZE));
if (!mac_address) {
ret = -1;
goto failed_mac_address;
}
memset(mac_address, '\0', sizeof(char)*(MAC_ADDRESS_SIZE));
if (msc_status_engine_mac_address(mac_address)) {
ret = -1;
goto failed_set_mac_address;
}
machine_name = malloc(sizeof(char)*MAX_MACHINE_NAME_SIZE);
if (!machine_name) {
ret = -1;
goto failed_machine_name;
}
memset(machine_name, '\0', sizeof(char)*(MAX_MACHINE_NAME_SIZE));
if (msc_status_engine_machine_name(machine_name, MAC_ADDRESS_SIZE)) {
ret = -1;
goto failed_set_machine_name;
}
apr_sha1_init(&context);
apr_sha1_update(&context, machine_name, strlen(machine_name));
apr_sha1_update(&context, mac_address, strlen(mac_address));
apr_sha1_final(hex_digest, &context);
for (i = 0, digptr = digest; i < APR_SHA1_DIGESTSIZE; i++)
{
sprintf(digptr, "%02x", hex_digest[i]);
digptr += 2;
}
failed_set_machine_name:
free(machine_name);
failed_machine_name:
failed_set_mac_address:
free(mac_address);
failed_mac_address:
return ret;
}
int DSOLOCAL msc_beacon_string (char *beacon_string, int beacon_string_max_len) {
char *apr = NULL;
const char *apr_loaded = NULL;
char pcre[7];
const char *pcre_loaded = NULL;
char *lua = NULL;
char *libxml = NULL;
char *modsec = NULL;
const char *apache = NULL;
char id[(APR_SHA1_DIGESTSIZE*2) + 1];
int beacon_string_len = -1;
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
libxml = LIBXML_DOTTED_VERSION;
modsec = MODSEC_VERSION;
#ifdef VERSION_IIS
apache = "IIS";
#else
apache = real_server_signature;
#endif
/* 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) + strlen(pcre) +
(pcre_loaded ? strlen(pcre_loaded) : 6) + (lua ? strlen(lua) : 6) +
(libxml ? strlen(libxml) : 6) + (APR_SHA1_DIGESTSIZE * 2);
beacon_string_len = beacon_string_len + /* null terminator: */ 1 +
/* comma: */ 6 +
/* slash: */ 2;
if (beacon_string == NULL || beacon_string_max_len == 0) {
goto return_length;
}
memset(id, '\0', sizeof(id));
if (msc_status_engine_unique_id(id)) {
sprintf(id, "no unique id");
}
apr_snprintf(beacon_string, beacon_string_max_len,
"%.25s,%.25s,%s/%s,%s/%s,%s,%s,%s",
modsec, apache, apr, apr_loaded, pcre, pcre_loaded, lua, libxml, id);
return_length:
return beacon_string_len;
}
int DSOLOCAL msc_status_engine_prepare_hostname (char *hostname, const char *plain_data,
int max_length)
{
int str_enc_len = 0;
int str_enc_spl_len = 0;
char *tmp = NULL;
int length = -1;
time_t ltime;
str_enc_len = msc_status_engine_base32_encode(NULL, plain_data, 0);
str_enc_spl_len = msc_status_engine_fill_with_dots(NULL, NULL, str_enc_len,
STATUS_ENGINE_DNS_IN_BETWEEN_DOTS);
if (str_enc_spl_len < 0) {
goto failed_enc_spl_len;
}
length = str_enc_spl_len + strlen(STATUS_ENGINE_DNS_SUFFIX) +
/* epoch: */ 10 + /* dots: */ 2 + /* terminator: */ 1 -
/* removed unsed terminators from str_enc and str_enc_spl: */ 2;
if (hostname == NULL || max_length == 0) {
goto return_length;
}
memset(hostname, '\0', sizeof(char) * max_length);
msc_status_engine_base32_encode(hostname, plain_data, str_enc_len);
tmp = strdup(hostname);
if (tmp == NULL) {
length = -1;
goto failed_strdup;
}
str_enc_spl_len = msc_status_engine_fill_with_dots(hostname, tmp, max_length,
STATUS_ENGINE_DNS_IN_BETWEEN_DOTS);
if (str_enc_spl_len < 0) {
length = -1;
goto failed_enc_spl;
}
time ( <ime );
apr_snprintf(hostname, max_length, "%s.%ld.%s", hostname,
(long) ltime, STATUS_ENGINE_DNS_SUFFIX);
failed_enc_spl:
free(tmp);
failed_strdup:
return_length:
failed_enc_spl_len:
return length;
}
int msc_status_engine_call (void) {
char *beacon_str = NULL;
int beacon_str_len = 0;
char *hostname = NULL;
int hostname_len = 0;
int ret = -1;
/* Retrieve the beacon string */
beacon_str_len = msc_beacon_string(NULL, 0);
beacon_str = malloc(sizeof(char) * beacon_str_len);
if (beacon_str == NULL) {
goto failed_beacon_string_malloc;
}
msc_beacon_string(beacon_str, beacon_str_len);
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
"ModSecurity: StatusEngine call: \"%s\"", beacon_str);
/* Get beacon string in the format of a hostname */
hostname_len = msc_status_engine_prepare_hostname(NULL, beacon_str, 0);
if (hostname_len < 0) {
goto failed_hostname_len;
}
hostname = malloc(sizeof(char) * hostname_len);
if (hostname == NULL) {
goto failed_hostname_malloc;
}
hostname_len = msc_status_engine_prepare_hostname(hostname, beacon_str,
hostname_len);
if (hostname_len < 0) {
goto failed_hostname;
}
/* Perform the DNS query. */
if (gethostbyname(hostname)) {
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
"ModSecurity: StatusEngine call successfully sent. For more " \
"information visit: http://%s/", STATUS_ENGINE_DNS_SUFFIX);
} else {
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
"ModSecurity: StatusEngine call failed. Query: %s",
hostname);
}
ret = 0;
failed_hostname:
free(hostname);
failed_hostname_malloc:
failed_hostname_len:
free(beacon_str);
failed_beacon_string_malloc:
return ret;
}
modsecurity-apache-2.9.13/apache2/msc_status_engine.h 0000664 0000000 0000000 00000002277 15174655764 0022616 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#ifndef MSC_STATUS_ENGINE_H
#define MSC_STATUS_ENGINE_H
#include "apr.h"
#include "apr_pools.h"
#include "apr_version.h"
#include "apr_optional.h"
#include "msc_pcre.h"
#ifndef WIN32
#define STATUS_ENGINE_DNS_IN_BETWEEN_DOTS 32
#else
#define STATUS_ENGINE_DNS_IN_BETWEEN_DOTS 30
#endif
#define STATUS_ENGINE_DNS_SUFFIX "status.modsecurity.org"
#define MAX_MACHINE_NAME_SIZE 100
#define MAC_ADDRESS_SIZE 20
static const char msc_status_engine_basis_32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
int msc_status_engine_call(void);
int DSOLOCAL msc_status_engine_unique_id (unsigned char *digest);
int DSOLOCAL msc_beacon_string (char *beacon_string, int beacon_string_max_len);
#endif
modsecurity-apache-2.9.13/apache2/msc_tree.c 0000664 0000000 0000000 00000061331 15174655764 0020674 0 ustar 00root root 0000000 0000000 /*
* 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
#include
#if APR_HAVE_STDINT_H
#include
#endif
#include
#if APR_HAVE_NETINET_IN_H
#include
#endif
#if APR_HAVE_ARPA_INET_H
#include
#endif
#include "apr_lib.h"
#include "msc_util.h"
#include "msc_tree.h"
CPTTree *CPTCreateRadixTree(apr_pool_t *pool) {
CPTTree *tree = NULL;
tree = apr_palloc(pool, sizeof(CPTTree));
if(tree == NULL)
return NULL;
memset(tree, 0, sizeof(CPTTree));
tree->pool = pool;
return tree;
}
void ConvertIPNetmask(unsigned char *buffer, unsigned char netmask, unsigned int ip_bitmask) {
int aux = 0, bytes = 0;
int mask = 0, mask_bit = 0;
bytes = ip_bitmask/8;
while(aux < bytes) {
mask_bit = ((1+aux) * 8);
if (mask_bit > netmask) {
mask = 0;
if ((mask_bit - netmask) < 8) mask = SHIFT_LEFT_MASK(mask_bit - netmask);
} else {
mask = -1;
}
buffer[aux] &= mask;
aux++;
}
return;
}
TreeNode *CPTCreateNode(apr_pool_t *pool) {
TreeNode *node = NULL;
node = apr_palloc(pool, sizeof(TreeNode));
if(node == NULL)
return NULL;
memset(node, 0, sizeof(TreeNode));
return node;
}
CPTData *CPTCreateCPTData(unsigned char netmask, apr_pool_t *pool) {
CPTData *prefix_data = apr_palloc(pool, sizeof(CPTData));
if (prefix_data == NULL) {
return NULL;
}
memset(prefix_data, 0, sizeof(CPTData));
prefix_data->netmask = netmask;
return prefix_data;
}
TreePrefix *InsertDataPrefix(TreePrefix *prefix, unsigned char *ipdata, unsigned int ip_bitmask,
unsigned char netmask, apr_pool_t *pool) {
if(prefix == NULL)
return NULL;
memcpy(prefix->buffer, ipdata, ip_bitmask/8);
prefix->bitlen = ip_bitmask;
prefix->prefix_data = CPTCreateCPTData(netmask, pool);
if(prefix->prefix_data == NULL)
return NULL;
return prefix;
}
TreePrefix *CPTCreatePrefix(unsigned char *ipdata, unsigned int ip_bitmask,
unsigned char netmask, apr_pool_t *pool) {
TreePrefix *prefix = NULL;
int bytes = ip_bitmask/8;
if ((ip_bitmask % 8 != 0) || (ipdata == NULL)) {
return NULL;
}
prefix = apr_palloc(pool, sizeof(TreePrefix));
if (prefix == NULL)
return NULL;
memset(prefix, 0, sizeof(TreePrefix));
prefix->buffer = apr_palloc(pool, bytes);
if(prefix->buffer == NULL)
return NULL;
memset(prefix->buffer, 0, bytes);
return InsertDataPrefix(prefix, ipdata, ip_bitmask, netmask, pool);
}
void CPTAppendToCPTDataList(CPTData *new, CPTData **list) {
CPTData *temp = NULL, *prev = NULL;
if (new == NULL) {
return;
}
if (list == NULL) {
return;
}
prev = *list;
temp = *list;
while (temp != NULL) {
if (new->netmask > temp->netmask)
break;
prev = temp;
temp = temp->next;
}
if (temp == *list) {
new->next = *list;
*list = new;
} else {
new->next = prev->next;
prev->next = new;
}
return;
}
int TreePrefixContainNetmask(TreePrefix *prefix, unsigned char netmask) {
CPTData *prefix_data = NULL;
if (prefix == NULL) {
return 0;
}
prefix_data = prefix->prefix_data;
while (prefix_data != NULL) {
if (prefix_data->netmask == netmask)
return 1;
prefix_data = prefix_data->next;
}
return 0;
}
int CheckBitmask(unsigned char netmask, unsigned int ip_bitmask) {
switch(netmask) {
case 0xff:
return 1;
case 0x20:
if(ip_bitmask == 0x20)
return 1;
break;
case 0x80:
if(ip_bitmask == 0x80)
return 1;
break;
}
return 0;
}
TreeNode *CPTCreateHead(TreePrefix *prefix, TreeNode *node, CPTTree *tree, unsigned char netmask, unsigned int ip_bitmask) {
if(tree == NULL)
return NULL;
if(prefix == NULL)
return NULL;
if (node != NULL) {
node->prefix = prefix;
node->bit = prefix->bitlen;
tree->head = node;
if(CheckBitmask(netmask, ip_bitmask))
return node;
node->count++;
node->netmasks = apr_palloc(tree->pool, (node->count * sizeof(unsigned char)));
if(node->netmasks)
node->netmasks[0] = netmask;
return node;
} else {
return NULL;
}
return NULL;
}
TreeNode *SetParentNode(TreeNode *node, TreeNode *new_node, CPTTree *tree) {
if (node->parent == NULL)
tree->head = new_node;
else if (node->parent->right == node)
node->parent->right = new_node;
else
node->parent->left = new_node;
return new_node;
}
int InsertNetmask(TreeNode *node, TreeNode *parent, TreeNode *new_node,
CPTTree *tree, unsigned char netmask, unsigned char bitlen) {
int i;
if (netmask != NETMASK_256-1 && netmask != NETMASK_128) {
if ((netmask != NETMASK_32 || (netmask == NETMASK_32 && bitlen != NETMASK_32))) {
node = new_node;
parent = new_node->parent;
while (parent != NULL && netmask < (parent->bit + 1)) {
node = parent;
parent = parent->parent;
}
node->count++;
node->netmasks = apr_palloc(tree->pool, (node->count * sizeof(unsigned char)));
if(node->netmasks == NULL)
return 0;
if ((node->count-1) == 0) {
node->netmasks[0] = netmask;
return 1;
}
node->netmasks[node->count - 1] = netmask;
i = node->count - 2;
while (i >= 0) {
if (netmask < node->netmasks[i]) {
node->netmasks[i + 1] = netmask;
break;
}
node->netmasks[i + 1] = node->netmasks[i];
node->netmasks[i] = netmask;
i--;
}
}
}
return 0;
}
TreeNode *CPTAddElement(unsigned char *ipdata, unsigned int ip_bitmask, CPTTree *tree, unsigned char netmask) {
unsigned char *buffer = NULL;
unsigned char bitlen = 0;
int bit_validation = 0, test_bit = 0;
int i = 0, j = 0, temp = 0;
unsigned int x, y;
TreeNode *node = NULL, *new_node = NULL;
TreeNode *parent = NULL, *i_node = NULL;
TreeNode *bottom_node = NULL;
TreePrefix *prefix = NULL;
if (tree == NULL) {
return NULL;
}
ConvertIPNetmask(ipdata, netmask, ip_bitmask);
prefix = CPTCreatePrefix(ipdata, ip_bitmask, netmask, tree->pool);
if (prefix == NULL) {
return NULL;
}
if (tree->head == NULL) {
node = CPTCreateNode(tree->pool);
return CPTCreateHead(prefix, node, tree, netmask, ip_bitmask);
}
node = tree->head;
buffer = prefix->buffer;
bitlen = prefix->bitlen;
while (node->bit < bitlen || node->prefix == NULL) {
if (bitlen < node->bit) {
if (node->right == NULL)
break;
else
node = node->right;
} else {
x = SHIFT_RIGHT_MASK(node->bit, 3); y = SHIFT_RIGHT_MASK(NETMASK_128, (node->bit % 8));
if (TREE_CHECK(buffer[x],y)) {
if (node->right == NULL)
break;
node = node->right;
} else {
if (node->left == NULL)
break;
else
node = node->left;
}
}
}
bottom_node = node;
if(node->bit < bitlen)
bit_validation = node->bit;
else
bit_validation = bitlen;
for (i = 0; (i * NETMASK_8) < bit_validation; i++) {
int net = 0, div = 0;
int cnt = 0;
if ((temp = (buffer[i] ^ bottom_node->prefix->buffer[i])) == 0) {
test_bit = (i + 1) * NETMASK_8;
continue;
}
temp += temp;
for(cnt = 0, net = NETMASK_256, div = 2; net >= NETMASK_2; net = NETMASK_256/div,
div += div, cnt++) {
if(temp >= net) {
test_bit = (i * NETMASK_8) + cnt;
break;
}
}
break;
}
if (bit_validation < test_bit)
test_bit = bit_validation;
parent = node->parent;
while (parent && test_bit <= parent->bit) {
node = parent;
parent = node->parent;
}
if (test_bit == bitlen && node->bit == bitlen) {
if (node->prefix != NULL) {
int found = 0;
CPTData *prefix_data = NULL;
prefix_data = node->prefix->prefix_data;
while(prefix_data != NULL) {
if (prefix_data->netmask == netmask)
++found;
prefix_data = prefix_data->next;
}
if (found != 0) {
CPTData *prefix_data = CPTCreateCPTData(netmask, tree->pool);
CPTAppendToCPTDataList(prefix_data, &prefix->prefix_data);
if(CheckBitmask(netmask, ip_bitmask))
return node;
parent = node->parent;
while (parent != NULL && netmask < (parent->bit + 1)) {
node = parent;
parent = parent->parent;
}
node->count++;
new_node = node;
node->netmasks = apr_palloc(tree->pool, (node->count * sizeof(unsigned char)));
if ((node->count -1) == 0) {
node->netmasks[0] = netmask;
return new_node;
}
node->netmasks[node->count - 1] = netmask;
i = node->count - 2;
while (i >= 0) {
if (netmask < node->netmasks[i]) {
node->netmasks[i + 1] = netmask;
break;
}
node->netmasks[i + 1] = node->netmasks[i];
node->netmasks[i] = netmask;
i--;
}
}
} else {
node->prefix = CPTCreatePrefix(prefix->buffer, prefix->bitlen,
NETMASK_256-1, tree->pool);
}
return node;
}
new_node = CPTCreateNode(tree->pool);
if(new_node == NULL)
return NULL;
new_node->prefix = prefix;
new_node->bit = prefix->bitlen;
if (test_bit == bitlen) {
x = SHIFT_RIGHT_MASK(test_bit, 3); y = SHIFT_RIGHT_MASK(NETMASK_128, (test_bit % 8));
if (TREE_CHECK(bottom_node->prefix->buffer[x],y)) {
new_node->right = node;
} else {
new_node->left = node;
}
new_node->parent = node->parent;
node->parent = SetParentNode(node, new_node, tree);
} else {
i_node = CPTCreateNode(tree->pool);
if(i_node == NULL)
return NULL;
//i_node->prefix = NULL;
i_node->bit = test_bit;
i_node->parent = node->parent;
if (node->netmasks != NULL) {
i = 0;
while(i < node->count) {
if (node->netmasks[i] < test_bit + 1)
break;
i++;
}
i_node->netmasks = apr_palloc(tree->pool, (node->count - i) * sizeof(unsigned char));
if(i_node->netmasks == NULL) {
return NULL;
}
j = 0;
while (j < (node->count - i)) {
i_node->netmasks[j] = node->netmasks[i + j];
j++;
}
i_node->count = (node->count - i);
node->count = i;
if (node->count == 0) {
node->netmasks = NULL;
}
}
x = SHIFT_RIGHT_MASK(test_bit, 3); y = SHIFT_RIGHT_MASK(NETMASK_128, (test_bit % 8));
if (TREE_CHECK(buffer[x],y)) {
i_node->left = node;
i_node->right = new_node;
} else {
i_node->left = new_node;
i_node->right = node;
}
new_node->parent = i_node;
node->parent = SetParentNode(node, i_node, tree);
}
if (InsertNetmask(node, parent, new_node, tree, netmask, bitlen))
return new_node;
return new_node;
}
int TreeCheckData(TreePrefix *prefix, CPTData *prefix_data, unsigned int netmask) {
while(prefix_data != NULL) {
if (prefix_data->netmask == netmask) {
return 1;
}
prefix_data = prefix_data->next;
}
return 0;
}
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;
if (prefix == NULL) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "TreePrefixNetmask: prefix is NULL.");
}
return 0;
}
prefix_data = prefix->prefix_data;
if (flag == 1) {
if(prefix_data == NULL) return 0;
if (prefix_data->netmask != netmask) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "TreePrefixNetmask: Cannot find a prefix with correct netmask.");
}
return 0;
} else {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "TreePrefixNetmask: Found a prefix with correct netmask.");
}
return 1;
}
}
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "TreePrefixNetmask: Check if a prefix has a the correct netmask");
}
ret = TreeCheckData(prefix, prefix_data, netmask);
return ret;
}
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) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTRetriveNode: Node tree is NULL.");
}
return NULL;
}
if(buffer == NULL) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTRetriveNode: Empty ip address. Nothing to search for.");
}
return NULL;
}
while (node->bit < ip_bitmask) {
x = SHIFT_RIGHT_MASK(node->bit, 3); y = SHIFT_RIGHT_MASK(NETMASK_128, (node->bit % 8));
if (TREE_CHECK(buffer[x], y)) {
node = node->right;
if (node == NULL) return NULL;
} else {
node = node->left;
if (node == NULL) return NULL;
}
}
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTRetriveNode: Found the node for provided ip address.");
}
return node;
}
TreeNode *CPTRetriveParentNode(TreeNode *node) {
while (node != NULL && node->netmasks == NULL)
node = node->parent;
return 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;
int mask_bits = 0;
node = CPTRetriveParentNode(node);
if (node == NULL) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElementIPNetblock: Node tree is NULL.");
}
return NULL;
}
netmask_node = node;
while(j < netmask_node->count) {
bytes = ip_bitmask / 8;
while( i < bytes ) {
mask = -1;
mask_bits = ((i + 1) * 8);
if (mask_bits > netmask_node->netmasks[j]) {
if ((mask_bits - netmask_node->netmasks[j]) < 8)
mask = SHIFT_LEFT_MASK(mask_bits - netmask_node->netmasks[j]);
else
mask = 0;
}
ipdata[i] &= mask;
i++;
}
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->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->prefix == NULL) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElementIPNetblock: Found a tree node but prefix is NULL.");
}
return NULL;
}
if (memcmp(node->prefix->buffer, ipdata, bytes) == 0) {
mask = SHIFT_LEFT_MASK(8 - ip_bitmask % 8);
if ((ip_bitmask % 8) == 0) {
if (TreePrefixNetmask(msr, node->prefix, netmask_node->netmasks[j], FALSE)) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElementIPNetblock: Node found for provided ip address");
}
return node;
}
}
if ((node->prefix->buffer[bytes] & mask) == (ipdata[bytes] & mask)) {
if (TreePrefixNetmask(msr, node->prefix, netmask_node->netmasks[j], FALSE)) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElementIPNetblock: Node found for provided ip address");
}
return node;
}
}
}
j++;
}
return CPTFindElementIPNetblock(msr, ipdata, ip_bitmask, netmask_node->parent);
}
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];
if (tree == NULL) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElement: Tree is NULL. Cannot proceed searching the ip.");
}
return node;
}
if (tree->head == NULL) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElement: Tree head is NULL. Cannot proceed searching the ip.");
}
return node;
}
node = tree->head;
if (ip_bitmask > (NETMASK_256-1)) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElement: Netmask cannot be greater than 255");
}
return NULL;
}
bytes = ip_bitmask/8;
memset(temp_data, 0, NETMASK_256-1);
memcpy(temp_data, ipdata, bytes);
node = CPTRetriveNode(msr, temp_data, ip_bitmask, node);
if (node && (node->bit != ip_bitmask)) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElement: Found a tree node but netmask is different.");
}
return NULL;
}
if(node == NULL) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElement: Node tree is NULL.");
}
return node;
}
if(node->prefix == NULL) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElement: Found a tree node but prefix is NULL.");
}
return node;
}
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) {
if (TreePrefixNetmask(msr, node->prefix, ip_bitmask, TRUE)) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElement: Node found for provided ip address");
}
return node;
}
}
if ((node->prefix->buffer[bytes] & mask) == (temp_data[bytes] & mask)) {
if (TreePrefixNetmask(msr, node->prefix, ip_bitmask, TRUE)) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElement: Node found for provided ip address");
}
return node;
}
}
}
return CPTFindElementIPNetblock(msr, temp_data, ip_bitmask, node);
}
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) {
msr_log(msr, 9, "CPTIpMatch: Tree is NULL. Cannot proceed searching the ip.");
}
return NULL;
}
if(ipdata == NULL) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTIpMatch: Empty ip address. Nothing to search for.");
}
return NULL;
}
switch(type) {
case IPV4_TREE:
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTIpMatch: Searching ip type 0x%x", type);
}
return CPTFindElement(msr, ipdata, NETMASK_32, tree);
case IPV6_TREE:
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTIpMatch: Searching ip type 0x%x", type);
}
return CPTFindElement(msr, ipdata, NETMASK_128, tree);
default:
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTIpMatch: Unknown ip type 0x%x", type);
}
return NULL;
}
}
TreeNode *TreeAddIP(const char *buffer, CPTTree *tree, int type) {
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;
struct in6_addr addr6;
int pos = 0;
char *ptr = NULL;
if(tree == NULL)
return NULL;
pos = strchr(buffer, '/') - buffer;
switch(type) {
case IPV4_TREE:
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) {
free(ptr);
ptr = NULL;
return NULL;
}
if(ptr != NULL) {
free(ptr);
ptr = NULL;
}
if(netmask_v4 == 0) {
return NULL;
}
else if (netmask_v4 != NETMASK_32 && pos < strlen(ip_strv4)) {
ip_strv4[pos] = '\0';
}
ret = inet_pton(AF_INET, ip_strv4, &(addr4.s_addr));
if (ret <= 0) {
return NULL;
}
tree->count++;
return CPTAddElement((unsigned char *)&(addr4.s_addr), NETMASK_32, tree, netmask_v4);
case IPV6_TREE:
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) {
free(ptr);
ptr = NULL;
return NULL;
}
if(ptr != NULL) {
free(ptr);
ptr = NULL;
}
if(netmask_v6 == 0) {
return NULL;
}
else if (netmask_v6 != NETMASK_128 && pos < strlen(ip_strv6)) {
ip_strv6[pos] = '\0';
}
ret = inet_pton(AF_INET6, ip_strv6, &(addr6.s6_addr));
if (ret <= 0)
{
return NULL;
}
tree->count++;
return CPTAddElement((unsigned char *)&(addr6.s6_addr), NETMASK_128, tree, netmask_v6);
default:
return NULL;
}
return NULL;
}
modsecurity-apache-2.9.13/apache2/msc_tree.h 0000664 0000000 0000000 00000003665 15174655764 0020707 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#ifndef __MSC_TREE_H__
#define __MSC_TREE_H__
#include "modsecurity.h"
typedef struct CPTData CPTData;
typedef struct TreePrefix TreePrefix;
typedef struct TreeNode TreeNode;
typedef struct CPTTree CPTTree;
typedef struct TreeRoot TreeRoot;
#define IPV4_TREE 0x1
#define IPV6_TREE 0x2
#define IPV4_LEN 0x20
#define IPV6_LEN 0x80
#define TREE_CHECK(x, y) ((x) & (y))
#define MASK_BITS(x) ((x + 1) * 8)
#define SHIFT_LEFT_MASK(x) ((int)(~0U << (x)))
#define SHIFT_RIGHT_MASK(x,y) ((x) >> (y))
#define NETMASK_256 0x100
#define NETMASK_128 0x80
#define NETMASK_64 0x40
#define NETMASK_32 0x20
#define NETMASK_16 0x10
#define NETMASK_8 0x8
#define NETMASK_4 0x4
#define NETMASK_2 0x2
struct CPTData {
unsigned char netmask;
struct CPTData *next;
};
struct TreePrefix {
unsigned char *buffer;
unsigned int bitlen;
CPTData *prefix_data;
};
struct TreeNode {
unsigned int bit;
int count;
unsigned char *netmasks;
TreePrefix *prefix;
struct TreeNode *left, *right;
struct TreeNode *parent;
};
struct CPTTree {
int count;
apr_pool_t *pool;
TreeNode *head;
};
struct TreeRoot {
CPTTree *ipv4_tree;
CPTTree *ipv6_tree;
};
CPTTree DSOLOCAL *CPTCreateRadixTree(apr_pool_t *pool);
TreeNode DSOLOCAL *CPTIpMatch(modsec_rec *msr, unsigned char *ipdata, CPTTree *tree, int type);
TreeNode DSOLOCAL *TreeAddIP(const char *buffer, CPTTree *tree, int type);
#endif /*__MSC_TREE_H__ */
modsecurity-apache-2.9.13/apache2/msc_unicode.c 0000664 0000000 0000000 00000011112 15174655764 0021353 0 ustar 00root root 0000000 0000000 /*
* 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 "msc_unicode.h"
#define CODEPAGE_SEPARATORS " \t\n\r"
/** \brief Load Unicode file
*
* \param dcfg Pointer to directory configuration
* \param error_msg Error message
*
* \retval 1 On Success
* \retval 0 On Fail
*/
static int unicode_map_create(directory_config *dcfg, char **error_msg)
{
char errstr[1024];
apr_pool_t *mp = dcfg->mp;
unicode_map *u_map = dcfg->u_map;
apr_int32_t wanted = APR_FINFO_SIZE;
apr_finfo_t finfo;
apr_status_t rc;
apr_size_t nbytes;
unsigned int codepage = 0;
char *buf = NULL, *p = NULL, *savedptr = NULL;
char *ucode = NULL, *hmap = NULL;
int found = 0, processing = 0;
int Code = 0, Map = 0;
if(unicode_map_table != NULL) {
free(unicode_map_table);
unicode_map_table = NULL;
}
if ((rc = apr_file_open(&u_map->map, u_map->mapfn, APR_READ, APR_OS_DEFAULT, mp)) != APR_SUCCESS) {
*error_msg = apr_psprintf(mp, "Could not open unicode map file \"%s\": %s", u_map->mapfn, apr_strerror(rc, errstr, 1024));
return 0;
}
if ((rc = apr_file_info_get(&finfo, wanted, u_map->map)) != APR_SUCCESS) {
*error_msg = apr_psprintf(mp, "Could not cannot get unicode map file information \"%s\": %s", u_map->mapfn, apr_strerror(rc, errstr, 1024));
apr_file_close(u_map->map);
return 0;
}
buf = (char *)malloc(finfo.size+1);
if (buf == NULL) {
*error_msg = apr_psprintf(mp, "Could not alloc memory for unicode map");
apr_file_close(u_map->map);
return 0;
}
rc = apr_file_read_full(u_map->map, buf, finfo.size, &nbytes);
if (unicode_map_table != NULL) {
memset(unicode_map_table, -1, (sizeof(int)*65536));
} else {
unicode_map_table = (int *)malloc(sizeof(int) * 65536);
if(unicode_map_table == NULL) {
*error_msg = apr_psprintf(mp, "Could not alloc memory for unicode map");
free(buf);
buf = NULL;
apr_file_close(u_map->map);
return 0;
}
memset(unicode_map_table, -1, (sizeof(int)*65536));
}
/* Setting some unicode values - http://tools.ietf.org/html/rfc3490#section-3.1 */
/* Set 0x3002 -> 0x2e */
unicode_map_table[0x3002] = 0x2e;
/* Set 0xFF61 -> 0x2e */
unicode_map_table[0xff61] = 0x2e;
/* Set 0xFF0E -> 0x2e */
unicode_map_table[0xff0e] = 0x2e;
/* Set 0x002E -> 0x2e */
unicode_map_table[0x002e] = 0x2e;
p = apr_strtok(buf,CODEPAGE_SEPARATORS,&savedptr);
while (p != NULL) {
codepage = atol(p);
if (codepage == unicode_codepage) {
found = 1;
}
if (found == 1 && (strchr(p,':') != NULL)) {
char *mapping = strdup(p);
processing = 1;
if(mapping != NULL) {
ucode = apr_strtok(mapping,":", &hmap);
sscanf(ucode,"%x",&Code);
sscanf(hmap,"%x",&Map);
if(Code >= 0 && Code <= 65535) {
unicode_map_table[Code] = Map;
}
free(mapping);
mapping = NULL;
}
}
if (processing == 1 && (strchr(p,':') == NULL)) {
free(buf);
buf = NULL;
break;
}
p = apr_strtok(NULL,CODEPAGE_SEPARATORS,&savedptr);
}
apr_file_close(u_map->map);
if(buf) {
free(buf);
buf = NULL;
}
return 1;
}
/** \brief Init unicode map
*
* \param dcfg Pointer to directory configuration
* \param mapfn Unicode map filename
* \param error_msg Error message
*
* \retval unicode_map_create On Success
* \retval -1 On Fail
*/
int unicode_map_init(directory_config *dcfg, const char *mapfn, char **error_msg)
{
*error_msg = NULL;
if ((dcfg->u_map == NULL) || (dcfg->u_map == NOT_SET_P)) {
dcfg->u_map = apr_pcalloc(dcfg->mp, sizeof(unicode_map));
if (dcfg->u_map == NULL) {
return -1;
}
}
dcfg->u_map->map = NULL;
dcfg->u_map->mapfn = apr_pstrdup(dcfg->mp, mapfn);
return unicode_map_create(dcfg, error_msg);
}
modsecurity-apache-2.9.13/apache2/msc_unicode.h 0000664 0000000 0000000 00000001532 15174655764 0021365 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#ifndef _MSC_UNICODE_H_
#define _MSC_UNICODE_H_
typedef struct unicode_map unicode_map;
#include
#include "modsecurity.h"
#include "apr_hash.h"
struct unicode_map {
apr_file_t *map;
const char *mapfn;
};
int DSOLOCAL unicode_map_init(directory_config *dcfg, const char *mapfn, char **error_msg);
#endif
modsecurity-apache-2.9.13/apache2/msc_util.c 0000664 0000000 0000000 00000226470 15174655764 0020721 0 ustar 00root root 0000000 0000000 /*
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
* 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
*
* 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 "modsecurity.h"
#include
#include
#include
#include
#include
#include "msc_release.h"
#include "msc_util.h"
#include
#if APR_HAVE_ARPA_INET_H
#include
#endif
#include
#include
#include "modsecurity_config.h"
#include "msc_remote_rules.h"
#ifdef WITH_CURL
#include "curl/curl.h"
#endif
/**
* NOTE: Be careful as these can ONLY be used on static values for X.
* (i.e. VALID_HEX(c++) will NOT work)
*/
#define VALID_HEX(X) (((X >= '0')&&(X <= '9')) || ((X >= 'a')&&(X <= 'f')) || ((X >= 'A')&&(X <= 'F')))
#define ISODIGIT(X) ((X >= '0')&&(X <= '7'))
#if (defined(WIN32) || defined(NETWARE))
/** Windows does not define all the octal modes */
#define S_IXOTH 00001
#define S_IWOTH 00002
#define S_IROTH 00004
#define S_IXGRP 00010
#define S_IWGRP 00020
#define S_IRGRP 00040
#define S_IXUSR 00100
#define S_IWUSR 00200
#define S_IRUSR 00400
#define S_ISVTX 01000
#define S_ISGID 02000
#define S_ISUID 04000
#endif /* defined(WIN32 || NETWARE) */
/* Base64 tables used in decodeBase64Ext */
static const char b64_pad = '=';
static const short b64_reverse_t[256] = {
-2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -2, -1, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -2, -2, -2,
-2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2,
-2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
};
static unsigned char *c2x(unsigned what, unsigned char *where);
static unsigned char x2c(unsigned char *what);
static unsigned char xsingle2c(unsigned char *what);
#ifdef LINUX_S390
int swap_int32(int x) {
int swap = ((x>>24)&0xff) | ((x<<8)&0xff0000) |
((x>>8)&0xff00) | ((x<<24)&0xff000000);
return swap;
}
#endif
/** \brief Decode utf-8 to unicode format.
*
* \param mp Pointer to memory pool
* \param input Pointer to input data
* \param input_len Input data length
* \param changed Set if data is changed
*
* \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;
unsigned int d = 0;
unsigned char c, *utf;
char *rval, *data;
unsigned int i, len;
unsigned int bytes_left = input_len;
assert(input != NULL);
*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;
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 may be single byte encoding */
if ((c & 0x80) == 0) {
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 {
unicode_len = 2;
/* compute character number */
d = ((c & 0x1F) << 6) | (utf[1] & 0x3F);
}
}
/* If first byte begins with binary 1110 it is three byte encoding */
else if ((c & 0xF0) == 0xE0) {
/* check we have at least three bytes */
if (bytes_left < 3) 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;
/* check third byte starts with binary 10 */
else if (((*(utf + 2)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
else {
unicode_len = 3;
/* compute character number */
d = ((c & 0x0F) << 12) | ((utf[1] & 0x3F) << 6) | (*(utf + 2) & 0x3F);
}
}
/* If first byte begins with binary 11110 it is four byte encoding */
else if ((c & 0xF8) == 0xF0) {
/* restrict characters to UTF-8 range (U+0000 - U+10FFFF)*/
if (c >= 0xF5) unicode_len = UNICODE_ERROR_RESTRICTED_CHARACTER;
/* check we have at least four bytes */
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;
/* 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;
/* compute character number */
d = ((c & 0x07) << 18) | ((utf[1] & 0x3F) << 12) | ((*(utf + 2) & 0x3F) << 6) | (*(utf + 3) & 0x3F);
}
}
/* invalid UTF-8 character number range (RFC 3629) */
if ((d >= 0xD800) && (d <= 0xDFFF)) unicode_len = UNICODE_ERROR_RESTRICTED_CHARACTER;
/* check for overlong */
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) {
i += unicode_len;
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';
return rval;
}
/** \brief Validate IPv4 Netmask
*
* \param ip_strv6 Pointer to ipv6 address
*
* \retval netmask_v4 On Success
*/
unsigned char is_netmask_v4(char *ip_strv4) {
unsigned char netmask_v4 = 32;
char *mask_str = NULL;
int cidr;
if(ip_strv4 == NULL)
return netmask_v4;
if ((mask_str = strchr(ip_strv4, '/'))) {
*(mask_str++) = '\0';
if (strchr(mask_str, '.') != NULL) {
return 0;
}
cidr = atoi(mask_str);
if ((cidr < 0) || (cidr > 32)) {
return 0;
}
netmask_v4 = (unsigned char)cidr;
}
return netmask_v4;
}
/** \brief Validate IPv6 Netmask
*
* \param ip_strv6 Pointer to ipv6 address
*
* \retval netmask_v6 On Success
*/
unsigned char is_netmask_v6(char *ip_strv6) {
unsigned char netmask_v6 = 128;
char *mask_str = NULL;
int cidr;
if(ip_strv6 == NULL)
return netmask_v6;
if ((mask_str = strchr(ip_strv6, '/'))) {
*(mask_str++) = '\0';
if (strchr(mask_str, ':') != NULL) {
return 0;
}
cidr = atoi(mask_str);
if ((cidr < 0) || (cidr > 128)) {
return 0;
}
netmask_v6 = (unsigned char)cidr;
}
return netmask_v6;
}
/** \brief Interpret |HEX| syntax
*
* \param op_parm Pointer to operator input
* \param op_len Operator input lenght
* \param rule Pointer to rule struct
* \param error_msg Pointer to error message
*
* \retval string On Success
*/
char *parse_pm_content(const char *op_parm, unsigned short int op_len, msre_rule *rule, char **error_msg) {
char *parm = NULL;
char *content = NULL;
unsigned short int offset = 0;
char converted = 0;
int i, x;
unsigned char bin = 0, esc = 0, bin_offset = 0;
unsigned char c = 0;
unsigned char bin_parm[3] = { 0 };
char *processed = NULL;
content = apr_pstrdup(rule->ruleset->mp, op_parm);
if (content == NULL) {
*error_msg = apr_psprintf(rule->ruleset->mp, "Error allocating memory for pattern matching content.");
return NULL;
}
while (offset < op_len && apr_isspace(content[offset])) {
offset++;
};
op_len = strlen(content);
if (content[offset] == '\"' && content[op_len-1] == '\"') {
parm = apr_pstrdup(rule->ruleset->mp, content + offset + 1);
if (parm == NULL) {
*error_msg = apr_psprintf(rule->ruleset->mp, "Error allocating memory for pattern matching content.");
return NULL;
}
parm[op_len - offset - 2] = '\0';
} else {
parm = apr_pstrdup(rule->ruleset->mp, content + offset);
if (parm == NULL) {
*error_msg = apr_psprintf(rule->ruleset->mp, "Error allocating memory for pattern matching content.");
return NULL;
}
}
op_len = strlen(parm);
if (op_len == 0) {
*error_msg = apr_psprintf(rule->ruleset->mp, "Content length is 0.");
return NULL;
}
for (i = 0, x = 0; i < op_len; i++) {
if (parm[i] == '|') {
if (bin) {
bin = 0;
} else {
bin = 1;
}
} else if(!esc && parm[i] == '\\') {
esc = 1;
} else {
if (bin) {
if (apr_isdigit(parm[i]) ||
parm[i] == 'A' || parm[i] == 'a' ||
parm[i] == 'B' || parm[i] == 'b' ||
parm[i] == 'C' || parm[i] == 'c' ||
parm[i] == 'D' || parm[i] == 'd' ||
parm[i] == 'E' || parm[i] == 'e' ||
parm[i] == 'F' || parm[i] == 'f')
{
bin_parm[bin_offset] = (char)parm[i];
bin_offset++;
if (bin_offset == 2) {
c = strtol((char *)bin_parm, (char **) NULL, 16) & 0xFF;
bin_offset = 0;
parm[x] = c;
x++;
converted = 1;
}
} else if (parm[i] == ' ') {
}
} else if (esc) {
if (parm[i] == ':' ||
parm[i] == ';' ||
parm[i] == '\\' ||
parm[i] == '\"')
{
parm[x] = parm[i];
x++;
} else {
*error_msg = apr_psprintf(rule->ruleset->mp, "Unsupported escape sequence.");
return NULL;
}
esc = 0;
converted = 1;
} else {
parm[x] = parm[i];
x++;
}
}
}
if (converted) {
op_len = x;
}
processed = apr_pstrmemdup(rule->ruleset->mp, parm, op_len);
if (processed == NULL) {
*error_msg = apr_psprintf(rule->ruleset->mp, "Error allocating memory for pattern matching content.");
return NULL;
}
return processed;
}
/** \brief Remove quotes
*
* \param mptmp Pointer to the pool
* \param input Pointer to input string
* \param input_len Input data length
*
* \retval string On Success
*/
char *remove_quotes(apr_pool_t *mptmp, const char *input, int input_len) {
char *parm = apr_palloc(mptmp, input_len);
char *ret = parm;
int len = input_len;
for(; *input !='\0' && len >=0; input++, len--) {
if(*input != '\'' && *input != '\"') {
*parm++ = *input;
}
}
*parm = '\0';
return ret;
}
/** \brief Remove escape char
*
* \param mptmp Pointer to the pool
* \param input Pointer to input string
* \param input_len Input data length
*
* \retval string On Success
*/
char *remove_escape(apr_pool_t *mptmp, const char *input, int input_len) {
char *parm = apr_palloc(mptmp, input_len);
char *ret = parm;
int len = input_len;
for(; *input !='\0' && len >=0; input++, len--) {
if(*input != '\\') {
*parm++ = *input;
}
}
*parm = '\0';
return ret;
}
/**
*
*/
int parse_boolean(const char *input) {
if (input == NULL) return -1;
if (strcasecmp(input, "on") == 0) return 1;
if (strcasecmp(input, "true") == 0) return 1;
if (strcasecmp(input, "1") == 0) return 1;
if (strcasecmp(input, "off") == 0) return 0;
if (strcasecmp(input, "false") == 0) return 0;
if (strcasecmp(input, "0") == 0) return 0;
return -1;
}
/** \brief Decode Base64 data with special chars
*
* \param plain_text Pointer to plain text data
* \param input Pointer to input data
* \param input_len Input data length
*
* \retval 0 On failure
* \retval string length On Success
*/
int decode_base64_ext(char *plain_text, const unsigned char *input, int input_len) {
const unsigned char *encoded = input;
int i = 0, j = 0, k = 0;
int ch = 0;
while ((ch = *encoded++) != '\0' && input_len-- > 0) {
if (ch == b64_pad) {
if (*encoded != '=' && (i % 4) == 1) {
return 0;
}
continue;
}
ch = b64_reverse_t[ch];
if (ch < 0 || ch == -1) {
continue;
} else if (ch == -2) {
return 0;
}
switch(i % 4) {
case 0:
plain_text[j] = ch << 2;
break;
case 1:
plain_text[j++] |= ch >> 4;
plain_text[j] = (ch & 0x0f) << 4;
break;
case 2:
plain_text[j++] |= ch >>2;
plain_text[j] = (ch & 0x03) << 6;
break;
case 3:
plain_text[j++] |= ch;
break;
}
i++;
}
k = j;
if (ch == b64_pad) {
switch(i % 4) {
case 1:
return 0;
case 2:
k++;
case 3:
plain_text[k] = 0;
}
}
plain_text[j] = '\0';
return j;
}
/** \brief Convert const char to int
*
* \param c number string
*
* \retval n The converted number
*/
int convert_to_int(const char c)
{
int n;
if ((c>='0') && (c<='9'))
n = c - '0';
else if ((c>='A') && (c<='F'))
n = c - 'A' + 10;
else if ((c>='a') && (c<='f'))
n = c - 'a' + 10;
else
n = 0;
return n;
}
/** \brief Set a match to tx.N
*
* \param msr Pointer to modsec resource
* \param capture If ON match will be saved
* \param match Pointer to captured string
* \param tx_n The tx number to save the data
*
* \retval 0 On success
* \retval -1 On allocation failure while saving the captured match
*/
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));
if (s == NULL) return -1;
s->name = apr_psprintf(msr->mp,"%d", tx_n);
s->name_len = strlen(s->name);
s->value = apr_pstrdup(msr->mp, match);
if (s->value == NULL) return -1;
s->value_len = strlen(s->value);
apr_table_setn(msr->tx_vars, s->name, (void *)s);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Added phrase match to TX.%d: %s",
tx_n, log_escape_nq_ex(msr->mp, s->value, s->value_len));
}
}
return 0;
}
/** \brief Set a match to tx.N safe mode
*
* \param msr Pointer to modsec resource
* \param capture If ON match will be saved
* \param match Pointer to captured string
* \param match_len Length of the captured string even if it contains NUL bytes
* \param tx_n The tx number to save the data
*
* \retval 0 On success
* \retval -1 On allocation failure while saving the captured match
*/
int set_match_to_tx_safe(modsec_rec *msr, int capture, const char *match, unsigned int match_len, int tx_n) {
assert(msr != NULL);
if (capture) {
msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
if (s == NULL) return -1;
s->name = apr_psprintf(msr->mp,"%d", tx_n);
s->name_len = strlen(s->name);
if (match) {
s->value = apr_pstrmemdup(msr->mp, match, match_len);
if (s->value == NULL) return -1;
s->value_len = match_len;
}
else {
return -1;
}
apr_table_setn(msr->tx_vars, s->name, (void *)s);
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "Added phrase match to TX.%d: %s",
tx_n, log_escape_nq_ex(msr->mp, s->value, s->value_len));
}
}
return 0;
}
/**
* Parses a string that contains a name-value pair in the form "name=value".
* IMP1 It does not check for whitespace between tokens.
*/
int parse_name_eq_value(apr_pool_t *mp, const char *input, char **name, char **value) {
char *p = NULL;
if ((name == NULL)||(value == NULL)) return -1;
if (input == NULL) return 0;
*name = NULL;
*value = NULL;
p = (char *)input;
while((*p != '=')&&(*p != '\0')) p++;
if (*p == '\0') {
*name = (char *)input;
return 1;
}
*name = apr_pstrmemdup(mp, input, p - input);
if (*name == NULL) return -1;
p++;
*value = apr_pstrdup(mp, p);
if (*value == NULL) return -1;
return 1;
}
/**
*
* IMP1 Assumes NUL-terminated
*/
char *url_encode(apr_pool_t *mp, char *input, unsigned int input_len, int *changed) {
char *rval, *d;
unsigned int i, len;
*changed = 0;
len = input_len * 3 + 1;
d = rval = apr_palloc(mp, len);
if (rval == NULL) return NULL;
/* ENH Only encode the characters that really need to be encoded. */
for(i = 0; i < input_len; i++) {
unsigned char c = input[i];
if (c == ' ') {
*d++ = '+';
*changed = 1;
} else
if ( (c == 42) || ((c >= 48)&&(c <= 57)) || ((c >= 65)&&(c <= 90))
|| ((c >= 97)&&(c <= 122))
) {
*d++ = c;
} else {
*d++ = '%';
c2x(c, (unsigned char *)d);
d += 2;
*changed = 1;
}
}
*d = '\0';
return rval;
}
/**
* Appends an URL-encoded version of the source string to the
* destination string, but makes sure that no more than "maxlen"
* bytes are added.
*/
char *strnurlencat(char *destination, char *source, unsigned int maxlen) {
char *s = source;
char *d = destination;
/* ENH Only encode the characters that really need to be encoded. */
/* Advance to the end of destination string. */
while(*d != '\0') d++;
/* Loop while there's bytes in the source string or
* until we reach the output limit.
*/
while((*s != '\0')&&(maxlen > 0)) {
unsigned char c = *s;
if (c == ' ') {
*d++ = '+';
maxlen--;
} else
if ( (c == 42) || ((c >= 48)&&(c <= 57)) || ((c >= 65)&&(c <= 90))
|| ((c >= 97)&&(c <= 122))
) {
*d++ = c;
maxlen--;
} else {
if (maxlen >= 3) {
*d++ = '%';
c2x(c, (unsigned char *)d);
d += 2;
maxlen -= 3;
} else {
/* If there's not enough room for the encoded
* byte we ignore it.
*/
maxlen = 0;
}
}
s++;
}
*d++ = '\0';
return destination;
}
/**
*
*/
char *file_basename(apr_pool_t *mp, const char *filename) {
char *d, *p;
if (filename == NULL) return NULL;
d = apr_pstrdup(mp, filename);
if (d == NULL) return NULL;
p = strrchr(d, '/');
if (p != NULL) d = p + 1;
p = strrchr(d, '\\');
if (p != NULL) d = p + 1;
return d;
}
char *m_strcasestr(const char *haystack, const char *needle) {
char aux, lower_aux;
int length;
if ((aux = *needle++) != 0) {
aux = (char)tolower((unsigned char)aux);
length = strlen(needle);
do {
do {
if ((lower_aux = *haystack++) == 0)
return NULL;
} while ((char)tolower((unsigned char)lower_aux) != aux);
} while (strncasecmp(haystack, needle, length) != 0);
haystack--;
}
return ((char *)haystack);
}
#ifdef WIN32
#if !(NTDDI_VERSION >= NTDDI_VISTA)
int inet_pton(int family, const char *src, void *dst) {
struct addrinfo addr;
struct sockaddr_in *in = NULL;
#if APR_HAVE_IPV6
struct sockaddr_in6 *in6 = NULL;
#endif
struct addrinfo *addr_info = NULL;
memset(&addr, 0, sizeof(struct addrinfo));
addr.ai_family = family;
if (getaddrinfo(src, NULL, &addr, &addr_info) != 0)
return -1;
if (addr_info) {
if (addr_info->ai_family == AF_INET) {
in = (struct sockaddr_in*)addr_info->ai_addr;
if(in != NULL)
memcpy(dst, &in->sin_addr, 4);
}
#if APR_HAVE_IPV6
else if (addr_info->ai_family == AF_INET6) {
in6 = (struct sockaddr_in6*)addr_info->ai_addr;
if(in6 != NULL)
memcpy(dst, &in6->sin6_addr, 16);
}
#endif
else {
freeaddrinfo(addr_info);
return -1;
}
freeaddrinfo(addr_info);
return 1;
}
return -1;
}
#endif
#endif
/**
*
*/
#ifdef WIN32
char *file_dirname(apr_pool_t *p, const char *filename) {
char *b, *c, *d;
if (filename == NULL) return NULL;
b = apr_pstrdup(p, filename);
if (b == NULL) return NULL;
c = strrchr(b, '/');
if (c != NULL) {
d = strrchr(c, '\\');
if (d != NULL) *d = '\0';
else *c = '\0';
} else {
d = strrchr(b, '\\');
if (d != NULL) *d = '\0';
}
return b;
}
#else
char *file_dirname(apr_pool_t *p, const char *filename) {
char *b, *c;
if (filename == NULL) return NULL;
b = apr_pstrdup(p, filename);
if (b == NULL) return NULL;
c = strrchr(b, '/');
if (c != NULL) *c = '\0';
return b;
}
#endif
/**
*
*/
int sql_hex2bytes_inplace(unsigned char *data, int len) {
unsigned char *d, *begin = data;
if ((data == NULL)||(len == 0)) return 0;
for( d = data; *data; *d++ = *data++) {
if ( *data != '0' ) continue;
if ( tolower(*++data) != 'x' ) {
data--;
continue;
}
data++;
// Do we need to keep "0x" if no hexa after?
if ( !VALID_HEX(data[0]) || !VALID_HEX(data[1]) ) {
data-=2;
continue;
}
while ( VALID_HEX(data[0]) && VALID_HEX(data[1]) ) {
*d++ = x2c(data);
data += 2;
}
}
*d = '\0';
return strlen((char *)begin);
}
/**
*
*
*/
int hex2bytes_inplace(unsigned char *data, int len) {
unsigned char *d = data;
int i, count = 0;
if ((data == NULL)||(len == 0)) return 0;
for(i = 0; i <= len - 2; i += 2) {
*d++ = x2c(&data[i]);
count++;
}
*d = '\0';
return count;
}
/**
* Converts a series of bytes into its hexadecimal
* representation.
*/
char *bytes2hex(apr_pool_t *pool, unsigned char *data, int len) {
static const unsigned char b2hex[] = "0123456789abcdef";
char *hex = NULL;
int i, j;
hex = apr_palloc(pool, (len * 2) + 1);
if (hex == NULL) return NULL;
j = 0;
for(i = 0; i < len; i++) {
hex[j++] = b2hex[data[i] >> 4];
hex[j++] = b2hex[data[i] & 0x0f];
}
hex[j] = 0;
return hex;
}
/**
*
*/
int is_token_char(unsigned char c) {
/* ENH Is the performance important at all? We could use a table instead. */
/* CTLs not allowed */
if ((c <= 32)||(c >= 127)) return 0;
switch(c) {
case '(' :
case ')' :
case '<' :
case '>' :
case '@' :
case ',' :
case ';' :
case ':' :
case '\\' :
case '"' :
case '/' :
case '[' :
case ']' :
case '?' :
case '=' :
return 0;
}
return 1;
}
/**
*
*/
int remove_lf_crlf_inplace(char *text) {
char *p = text;
int count = 0;
if (text == NULL) return -1;
while(*p != '\0') {
count++;
p++;
}
if (count > 0) {
if (*(p - 1) == '\n') {
*(p - 1) = '\0';
if (count > 1) {
if (*(p - 2) == '\r') {
*(p - 2) = '\0';
}
}
}
}
return 1;
}
/**
* Converts a byte given as its hexadecimal representation
* into a proper byte. Handles uppercase and lowercase letters
* but does not check for overflows.
*/
static unsigned char x2c(unsigned char *what) {
register unsigned char digit;
digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
digit *= 16;
digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
return digit;
}
/**
* Converts a single hexadecimal digit into a decimal value.
*/
static unsigned char xsingle2c(unsigned char *what) {
register unsigned char digit;
digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
return digit;
}
/**
*
*/
char *guess_tmp_dir(apr_pool_t *p) {
char *filename = NULL;
/* ENH Use apr_temp_dir_get instead. */
#ifdef WIN32
filename = apr_pcalloc(p, 256);
if (filename == NULL) return "";
if (GetTempPath(255, filename) != 0) return filename;
#endif
filename = getenv("TMPDIR");
if (filename != NULL) return filename;
filename = getenv("TEMP");
if (filename != NULL) return filename;
filename = getenv("TMP");
if (filename != NULL) return filename;
#if defined NETWARE
return("sys:/tmp/");
#elif defined WIN32
return("");
#else
return("/tmp/");
#endif
}
/**
*
*/
char *current_logtime(apr_pool_t *mp) {
apr_time_exp_t t;
char tstr[100];
apr_size_t len;
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), "%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);
}
/**
*
*/
char *current_filetime(apr_pool_t *mp) {
apr_time_exp_t t;
char tstr[100];
apr_size_t len;
apr_time_exp_lt(&t, apr_time_now());
apr_strftime(tstr, &len, 80, "%Y%m%d-%H%M%S", &t);
return apr_pstrdup(mp, tstr);
}
/**
*
*/
int msc_mkstemp_ex(char *templat, int mode) {
int fd = -1;
/* ENH Use apr_file_mktemp instead. */
#if !(defined(WIN32)||defined(NETWARE))
fd = mkstemp(templat);
#ifdef HAVE_FCHMOD
if ((fd != -1) && (mode != 0)) {
if (fchmod(fd, mode) == -1) {
return -1;
}
}
#endif /* HAVE_FCHMOD */
#else
if (mktemp(templat) == NULL) return -1;
fd = open(templat, O_WRONLY | O_APPEND | O_CREAT | O_BINARY, mode);
#endif /* !(defined(WIN32)||defined(NETWARE)) */
return fd;
}
/**
*
*/
int msc_mkstemp(char *templat) {
return msc_mkstemp_ex(templat, CREATEMODE_UNISTD);
}
/**
* Converts the input string to lowercase (in-place).
*/
char *strtolower_inplace(unsigned char *str) {
unsigned char *c = str;
if (str == NULL) return NULL;
while(*c != 0) {
*c = tolower(*c);
c++;
}
return (char *)str;
}
/**
* Converts a single byte into its hexadecimal representation.
* Will overwrite two bytes at the destination.
*/
static unsigned char *c2x(unsigned what, unsigned char *where) {
static const char c2x_table[] = "0123456789abcdef";
what = what & 0xff;
*where++ = c2x_table[what >> 4];
*where++ = c2x_table[what & 0x0f];
return where;
}
static char *_log_escape(apr_pool_t *p, const unsigned char *input,
unsigned long int input_length, int escape_quotes, int escape_colon, int escape_re);
char *log_escape_re(apr_pool_t *mp, const char *text) {
return _log_escape(mp, (const unsigned char *)text, text ? strlen(text) : 0, 1, 1, 1);
}
char *log_escape(apr_pool_t *mp, const char *text) {
return _log_escape(mp, (const unsigned char *)text, text ? strlen(text) : 0, 1, 0, 0);
}
char *log_escape_nq(apr_pool_t *mp, const char *text) {
#ifdef VERSION_IIS
int l = 0;
// this is a workaround for unknown bug that causes 'text' sometimes to lack zero-termination
//
__try
{
l = text ? strlen(text) : 0;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
l = -1;
}
if(l < 0)
return _log_escape(mp, "BUG: see log_escape_nq()", 24, 0, 0, 0);
return _log_escape(mp, (const unsigned char *)text, l, 0, 0, 0);
#else
return _log_escape(mp, (const unsigned char *)text, text ? strlen(text) : 0, 0, 0, 0);
#endif
}
char *log_escape_ex(apr_pool_t *mp, const char *text, unsigned long int text_length) {
return _log_escape(mp, (const unsigned char *)text, text_length, 1, 0, 0);
}
char *log_escape_nq_ex(apr_pool_t *mp, const char *text, unsigned long int text_length) {
return _log_escape(mp, (const unsigned char *)text, text_length, 0, 0, 0);
}
char *log_escape_raw(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length) {
unsigned char *ret = apr_palloc(mp, text_length * 4 + 1);
unsigned long int i, j;
for (i = 0, j = 0; i < text_length; i++, j += 4) {
ret[j] = '\\';
ret[j+1] = 'x';
c2x(text[i], ret+j+2);
}
ret[text_length * 4] = '\0';
return (char *)ret;
}
char *log_escape_nul(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length) {
unsigned char *ret = apr_palloc(mp, text_length * 4 + 1);
unsigned long int i, j;
for (i = 0, j = 0; i < text_length; i++) {
if (text[i] == '\0') {
ret[j] = '\\';
ret[j+1] = 'x';
c2x(text[i], ret+j+2);
j += 4;
}
else {
ret[j] = text[i];
j++;
}
}
ret[j] = '\0';
return (char *)ret;
}
/**
* Transform text to ASCII printable or hex escaped
*/
char *log_escape_hex(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length) {
unsigned char *ret = apr_palloc(mp, text_length * 4 + 1);
unsigned long int i, j;
for (i = 0, j = 0; i < text_length; i++) {
if ( (text[i] == '"')
||(text[i] == '\\')
||(text[i] <= 0x1f)
||(text[i] >= 0x7f))
{
ret[j] = '\\';
ret[j+1] = 'x';
c2x(text[i], ret+j+2);
j += 4;
}
else {
ret[j] = text[i];
j ++;
}
}
ret[j] = '\0';
return (char *)ret;
}
/**
* Transform input into a form safe for logging.
*/
static char *_log_escape(apr_pool_t *mp, const unsigned char *input, unsigned long int input_len,
int escape_quotes, int escape_colon, int escape_re)
{
unsigned char *d = NULL;
char *ret = NULL;
unsigned long int i;
if (input == NULL) return NULL;
ret = apr_palloc(mp, input_len * 4 + 1);
if (ret == NULL) return NULL;
d = (unsigned char *)ret;
i = 0;
while(i < input_len) {
switch(input[i]) {
case ':' :
if (escape_colon) {
*d++ = '\\';
*d++ = ':';
} else {
*d++ = input[i];
}
break;
case '"' :
if (escape_quotes) {
*d++ = '\\';
*d++ = '"';
} else {
*d++ = input[i];
}
break;
case '+' :
if (escape_re) {
*d++ = '\\';
*d++ = '+';
} else {
*d++ = input[i];
}
break;
case '.' :
if (escape_re) {
*d++ = '\\';
*d++ = '.';
} else {
*d++ = input[i];
}
break;
case ']' :
if (escape_re) {
*d++ = '\\';
*d++ = ']';
} else {
*d++ = input[i];
}
break;
case '[' :
if (escape_re) {
*d++ = '\\';
*d++ = '[';
} else {
*d++ = input[i];
}
break;
case '(' :
if (escape_re) {
*d++ = '\\';
*d++ = '(';
} else {
*d++ = input[i];
}
break;
case ')' :
if (escape_re) {
*d++ = '\\';
*d++ = ')';
} else {
*d++ = input[i];
}
break;
case '?' :
if (escape_re) {
*d++ = '\\';
*d++ = '?';
} else {
*d++ = input[i];
}
break;
case '/' :
if (escape_re) {
*d++ = '\\';
*d++ = '/';
} else {
*d++ = input[i];
}
break;
case '\b' :
*d++ = '\\';
*d++ = 'b';
break;
case '\n' :
*d++ = '\\';
*d++ = 'n';
break;
case '\r' :
*d++ = '\\';
*d++ = 'r';
break;
case '\t' :
*d++ = '\\';
*d++ = 't';
break;
case '\v' :
*d++ = '\\';
*d++ = 'v';
break;
case '\\' :
*d++ = '\\';
*d++ = '\\';
break;
default :
if ((input[i] <= 0x1f)||(input[i] >= 0x7f)) {
*d++ = '\\';
*d++ = 'x';
c2x(input[i], d);
d += 2;
} else {
*d++ = input[i];
}
break;
}
i++;
}
*d = 0;
return ret;
}
/**
* JavaScript decoding.
* IMP1 Assumes NUL-terminated
*/
int js_decode_nonstrict_inplace(unsigned char *input, long int input_len) {
unsigned char *d = (unsigned char *)input;
long int i, count;
if (input == NULL) return -1;
i = count = 0;
while (i < input_len) {
if (input[i] == '\\') {
/* Character is an escape. */
if ( (i + 5 < input_len) && (input[i + 1] == 'u')
&& (VALID_HEX(input[i + 2])) && (VALID_HEX(input[i + 3]))
&& (VALID_HEX(input[i + 4])) && (VALID_HEX(input[i + 5])) )
{
/* \uHHHH */
/* Use only the lower byte. */
*d = x2c(&input[i + 4]);
/* Full width ASCII (ff01 - ff5e) needs 0x20 added */
if ( (*d > 0x00) && (*d < 0x5f)
&& ((input[i + 2] == 'f') || (input[i + 2] == 'F'))
&& ((input[i + 3] == 'f') || (input[i + 3] == 'F')))
{
(*d) += 0x20;
}
d++;
count++;
i += 6;
}
else if ( (i + 3 < input_len) && (input[i + 1] == 'x')
&& VALID_HEX(input[i + 2]) && VALID_HEX(input[i + 3])) {
/* \xHH */
*d++ = x2c(&input[i + 2]);
count++;
i += 4;
}
else if ((i + 1 < input_len) && ISODIGIT(input[i + 1])) {
/* \OOO (only one byte, \000 - \377) */
char buf[4];
int j = 0;
while((i + 1 + j < input_len)&&(j < 3)) {
buf[j] = input[i + 1 + j];
j++;
if (!ISODIGIT(input[i + 1 + j])) break;
}
buf[j] = '\0';
if (j > 0) {
/* Do not use 3 characters if we will be > 1 byte */
if ((j == 3) && (buf[0] > '3')) {
j = 2;
buf[j] = '\0';
}
*d++ = (unsigned char)strtol(buf, NULL, 8);
i += 1 + j;
count++;
}
}
else if (i + 1 < input_len) {
/* \C */
unsigned char c = input[i + 1];
switch(input[i + 1]) {
case 'a' :
c = '\a';
break;
case 'b' :
c = '\b';
break;
case 'f' :
c = '\f';
break;
case 'n' :
c = '\n';
break;
case 'r' :
c = '\r';
break;
case 't' :
c = '\t';
break;
case 'v' :
c = '\v';
break;
/* The remaining (\?,\\,\',\") are just a removal
* of the escape char which is default.
*/
}
*d++ = c;
i += 2;
count++;
}
else {
/* Not enough bytes */
while(i < input_len) {
*d++ = input[i++];
count++;
}
}
}
else {
*d++ = input[i++];
count++;
}
}
*d = '\0';
return count;
}
/**
*
* IMP1 Assumes NUL-terminated
*/
int urldecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_len, int *changed) {
unsigned char *d = input;
long int i, count, fact, j, xv;
int Code, hmap = -1;
*changed = 0;
if (input == NULL) return -1;
i = count = 0;
while (i < input_len) {
if (input[i] == '%') {
/* Character is a percent sign. */
if ((i + 1 < input_len)&&( (input[i + 1] == 'u')||(input[i + 1] == 'U') )) {
/* IIS-specific %u encoding. */
if (i + 5 < input_len) {
/* We have at least 4 data bytes. */
if ( (VALID_HEX(input[i + 2]))&&(VALID_HEX(input[i + 3]))
&&(VALID_HEX(input[i + 4]))&&(VALID_HEX(input[i + 5])) )
{
Code = 0;
fact = 1;
if (unicode_map_table != NULL && unicode_codepage > 0) {
for(j=5; j>=2; j--) {
if (isxdigit((input[i+j]))) {
if ((input[i+j])>=97) {
xv = ( (input[i+j]) - 97) + 10;
} else if ( (input[i+j]) >= 65) {
xv = ((input[i+j]) - 65) + 10;
} else {
xv = (input[i+j]) - 48;
}
Code += (xv * fact);
fact *= 16;
}
}
if(Code >= 0 && Code <= 65535) {
hmap = unicode_map_table[Code];
}
}
if(hmap != -1) {
*d = hmap;
} else {
/* We first make use of the lower byte here, ignoring the higher byte. */
*d = x2c(&input[i + 4]);
/* Full width ASCII (ff01 - ff5e) needs 0x20 added */
if ( (*d > 0x00) && (*d < 0x5f)
&& ((input[i + 2] == 'f') || (input[i + 2] == 'F'))
&& ((input[i + 3] == 'f') || (input[i + 3] == 'F')))
{
(*d) += 0x20;
}
}
d++;
count++;
i += 6;
*changed = 1;
} else {
/* Invalid data, skip %u. */
*d++ = input[i++];
*d++ = input[i++];
count += 2;
}
} else {
/* Not enough bytes (4 data bytes), skip %u. */
*d++ = input[i++];
*d++ = input[i++];
count += 2;
}
}
else {
/* Standard URL encoding. */
/* Are there enough bytes available? */
if (i + 2 < input_len) {
/* Yes. */
/* Decode a %xx combo only if it is valid.
*/
char c1 = input[i + 1];
char c2 = input[i + 2];
if (VALID_HEX(c1) && VALID_HEX(c2)) {
*d++ = x2c(&input[i + 1]);
count++;
i += 3;
*changed = 1;
} else {
/* Not a valid encoding, skip this % */
*d++ = input[i++];
count++;
}
} else {
/* Not enough bytes available, skip this % */
*d++ = input[i++];
count++;
}
}
}
else {
/* Character is not a percent sign. */
if (input[i] == '+') {
*d++ = ' ';
*changed = 1;
} else {
*d++ = input[i];
}
count++;
i++;
}
}
*d = '\0';
return count;
}
/**
*
* IMP1 Assumes NUL-terminated
*/
int urldecode_nonstrict_inplace_ex(unsigned char *input, long int input_len, int *invalid_count, int *changed) {
unsigned char *d = (unsigned char *)input;
long int i, count;
*changed = 0;
if (input == NULL) return -1;
i = count = 0;
while (i < input_len) {
if (input[i] == '%') {
/* Character is a percent sign. */
/* Are there enough bytes available? */
if (i + 2 < input_len) {
char c1 = input[i + 1];
char c2 = input[i + 2];
if (VALID_HEX(c1) && VALID_HEX(c2)) {
/* Valid encoding - decode it. */
*d++ = x2c(&input[i + 1]);
count++;
i += 3;
*changed = 1;
} else {
/* Not a valid encoding, skip this % */
*d++ = input[i++];
count ++;
(*invalid_count)++;
}
} else {
/* Not enough bytes available, copy the raw bytes. */
*d++ = input[i++];
count ++;
(*invalid_count)++;
}
} else {
/* Character is not a percent sign. */
if (input[i] == '+') {
*d++ = ' ';
*changed = 1;
} else {
*d++ = input[i];
}
count++;
i++;
}
}
*d = '\0';
return count;
}
/**
*
* IMP1 Assumes NUL-terminated
*/
int html_entities_decode_inplace(apr_pool_t *mp, unsigned char *input, int input_len) {
unsigned char *d = input;
int i, count;
if ((input == NULL)||(input_len <= 0)) return 0;
i = count = 0;
while((i < input_len)&&(count < input_len)) {
int z, copy = 1;
/* Require an ampersand and at least one character to
* start looking into the entity.
*/
if ((input[i] == '&')&&(i + 1 < input_len)) {
int k, j = i + 1;
if (input[j] == '#') {
/* Numerical entity. */
copy++;
if (!(j + 1 < input_len)) goto HTML_ENT_OUT; /* Not enough bytes. */
j++;
if ((input[j] == 'x')||(input[j] == 'X')) {
/* Hexadecimal entity. */
copy++;
if (!(j + 1 < input_len)) goto HTML_ENT_OUT; /* Not enough bytes. */
j++; /* j is the position of the first digit now. */
k = j;
while((j < input_len)&&(isxdigit(input[j]))) j++;
if (j > k) { /* Do we have at least one digit? */
/* Decode the entity. */
char *x = apr_pstrmemdup(mp, (const char *)&input[k], j - k);
*d++ = (unsigned char)strtol(x, NULL, 16);
count++;
/* Skip over the semicolon if it's there. */
if ((j < input_len)&&(input[j] == ';')) i = j + 1;
else i = j;
continue;
} else {
goto HTML_ENT_OUT;
}
} else {
/* Decimal entity. */
k = j;
while((j < input_len)&&(isdigit(input[j]))) j++;
if (j > k) { /* Do we have at least one digit? */
/* Decode the entity. */
char *x = apr_pstrmemdup(mp, (const char *)&input[k], j - k);
*d++ = (unsigned char)strtol(x, NULL, 10);
count++;
/* Skip over the semicolon if it's there. */
if ((j < input_len)&&(input[j] == ';')) i = j + 1;
else i = j;
continue;
} else {
goto HTML_ENT_OUT;
}
}
} else {
/* Text entity. */
k = j;
while((j < input_len)&&(isalnum(input[j]))) j++;
if (j > k) { /* Do we have at least one digit? */
char *x = apr_pstrmemdup(mp, (const char *)&input[k], j - k);
/* Decode the entity. */
/* ENH What about others? */
if (strcasecmp(x, "quot") == 0) *d++ = '"';
else
if (strcasecmp(x, "amp") == 0) *d++ = '&';
else
if (strcasecmp(x, "lt") == 0) *d++ = '<';
else
if (strcasecmp(x, "gt") == 0) *d++ = '>';
else
if (strcasecmp(x, "nbsp") == 0) *d++ = NBSP;
else {
/* We do no want to convert this entity, copy the raw data over. */
copy = j - k + 1;
goto HTML_ENT_OUT;
}
count++;
/* Skip over the semicolon if it's there. */
if ((j < input_len)&&(input[j] == ';')) i = j + 1;
else i = j;
continue;
}
}
}
HTML_ENT_OUT:
for(z = 0; ((z < copy) && (count < input_len)); z++) {
*d++ = input[i++];
count++;
}
}
*d = '\0';
return count;
}
/**
*
* IMP1 Assumes NUL-terminated
*/
int ansi_c_sequences_decode_inplace(unsigned char *input, int input_len) {
unsigned char *d = input;
int i, count;
i = count = 0;
while(i < input_len) {
if ((input[i] == '\\')&&(i + 1 < input_len)) {
int c = -1;
switch(input[i + 1]) {
case 'a' :
c = '\a';
break;
case 'b' :
c = '\b';
break;
case 'f' :
c = '\f';
break;
case 'n' :
c = '\n';
break;
case 'r' :
c = '\r';
break;
case 't' :
c = '\t';
break;
case 'v' :
c = '\v';
break;
case '\\' :
c = '\\';
break;
case '?' :
c = '?';
break;
case '\'' :
c = '\'';
break;
case '"' :
c = '"';
break;
}
if (c != -1) i += 2;
/* Hexadecimal or octal? */
if (c == -1) {
if ((input[i + 1] == 'x')||(input[i + 1] == 'X')) {
/* Hexadecimal. */
if ((i + 3 < input_len)&&(isxdigit(input[i + 2]))&&(isxdigit(input[i + 3]))) {
/* Two digits. */
c = x2c(&input[i + 2]);
i += 4;
} else {
/* Invalid encoding, do nothing. */
}
}
else
if (ISODIGIT(input[i + 1])) { /* Octal. */
char buf[4];
int j = 0;
while((i + 1 + j < input_len)&&(j < 3)) {
buf[j] = input[i + 1 + j];
j++;
if (!ISODIGIT(input[i + 1 + j])) break;
}
buf[j] = '\0';
if (j > 0) {
c = strtol(buf, NULL, 8);
i += 1 + j;
}
}
}
if (c == -1) {
/* Didn't recognise encoding, copy raw bytes. */
*d++ = input[i + 1];
count++;
i += 2;
} else {
/* Converted the encoding. */
*d++ = c;
count++;
}
} else {
/* Input character not a backslash, copy it. */
*d++ = input[i++];
count++;
}
}
*d = '\0';
return count;
}
/**
*
* IMP1 Assumes NUL-terminated
*/
int normalize_path_inplace(unsigned char *input, int input_len, int win, int *changed) {
unsigned char *src;
unsigned char *dst;
unsigned char *end;
int ldst = 0;
int hitroot = 0;
int done = 0;
int relative;
int trailing;
*changed = 0;
/* Need at least one byte to normalize */
if (input_len <= 0) return 0;
/*
* ENH: Deal with UNC and drive letters?
*/
src = dst = input;
end = input + (input_len - 1);
ldst = 1;
relative = ((*input == '/') || (win && (*input == '\\'))) ? 0 : 1;
trailing = ((*end == '/') || (win && (*end == '\\'))) ? 1 : 0;
while (!done && (src <= end) && (dst <= end)) {
/* Convert backslash to forward slash on Windows only. */
if (win) {
if (*src == '\\') {
*src = '/';
*changed = 1;
}
if ((src < end) && (*(src + 1) == '\\')) {
*(src + 1) = '/';
*changed = 1;
}
}
/* Always normalize at the end of the input. */
if (src == end) {
done = 1;
}
/* Skip normalization if this is NOT the end of the path segment. */
else if (*(src + 1) != '/') {
goto copy; /* Skip normalization. */
}
/*** Normalize the path segment. ***/
/* Could it be an empty path segment? */
if ((src != end) && *src == '/') {
/* Ignore */
*changed = 1;
goto copy; /* Copy will take care of this. */
}
/* Could it be a back or self reference? */
else if (*src == '.') {
/* Back-reference? */
if ((dst > input) && (*(dst - 1) == '.')) {
/* If a relative path and either our normalization has
* already hit the rootdir, or this is a backref with no
* previous path segment, then mark that the rootdir was hit
* and just copy the backref as no normilization is possible.
*/
if (relative && (hitroot || ((dst - 2) <= input))) {
hitroot = 1;
goto copy; /* Skip normalization. */
}
/* Remove backreference and the previous path segment. */
dst -= 3;
while ((dst > input) && (*dst != '/')) {
dst--;
}
/* But do not allow going above rootdir. */
if (dst <= input) {
hitroot = 1;
dst = input;
/* Need to leave the root slash if this
* is not a relative path and the end was reached
* on a backreference.
*/
if (!relative && (src == end)) {
dst++;
}
}
if (done) goto length; /* Skip the copy. */
src++;
*changed = 1;
}
/* Relative Self-reference? */
else if (dst == input) {
*changed = 1;
/* Ignore. */
if (done) goto length; /* Skip the copy. */
src++;
}
/* Self-reference? */
else if (*(dst - 1) == '/') {
*changed = 1;
/* Ignore. */
if (done) goto length; /* Skip the copy. */
dst--;
src++;
}
}
/* Found a regular path segment. */
else if (dst > input) {
hitroot = 0;
}
copy:
/*** Copy the byte if required. ***/
/* Skip to the last forward slash when multiple are used. */
if (*src == '/') {
unsigned char *oldsrc = src;
while ( (src < end)
&& ((*(src + 1) == '/') || (win && (*(src + 1) == '\\'))) )
{
src++;
}
if (oldsrc != src) *changed = 1;
/* Do not copy the forward slash to the root
* if it is not a relative path. Instead
* move over the slash to the next segment.
*/
if (relative && (dst == input)) {
src++;
goto length; /* Skip the copy */
}
}
*(dst++) = *(src++);
length:
ldst = (dst - input);
}
/* Make sure that there is not a trailing slash in the
* normalized form if there was not one in the original form.
*/
if (!trailing && (dst > input) && *(dst - 1) == '/') {
ldst--;
dst--;
}
/* Always NUL terminate */
*dst = '\0';
return ldst;
}
char *modsec_build(apr_pool_t *mp) {
return apr_psprintf(mp, "%02i%02i%02i%1i%02i",
atoi(MODSEC_VERSION_MAJOR),
atoi(MODSEC_VERSION_MINOR),
atoi(MODSEC_VERSION_MAINT),
get_modsec_build_type(NULL),
atoi(MODSEC_VERSION_RELEASE));
}
int is_empty_string(const char *string) {
unsigned int i;
if (string == NULL) return 1;
for(i = 0; string[i] != '\0'; i++) {
if (!isspace(string[i])) {
return 0;
}
}
return 1;
}
char *resolve_relative_path(apr_pool_t *pool, const char *parent_filename, const char *filename) {
if (filename == NULL) return NULL;
// TODO Support paths on operating systems other than Unix.
if (filename[0] == '/') return (char *)filename;
return apr_pstrcat(pool, apr_pstrndup(pool, parent_filename,
strlen(parent_filename) - strlen(apr_filepath_name_get(parent_filename))),
filename, NULL);
}
/**
* Decode a string that contains CSS-escaped characters.
*
* References:
* http://www.w3.org/TR/REC-CSS2/syndata.html#q4
* http://www.unicode.org/roadmaps/
*/
int css_decode_inplace(unsigned char *input, long int input_len) {
unsigned char *d = (unsigned char *)input;
long int i, j, count;
if (input == NULL) return -1;
i = count = 0;
while (i < input_len) {
/* Is the character a backslash? */
if (input[i] == '\\') {
/* Is there at least one more byte? */
if (i + 1 < input_len) {
i++; /* We are not going to need the backslash. */
/* Check for 1-6 hex characters following the backslash */
j = 0;
while ( (j < 6)
&& (i + j < input_len)
&& (VALID_HEX(input[i + j])))
{
j++;
}
if (j > 0) { /* We have at least one valid hexadecimal character. */
int fullcheck = 0;
/* For now just use the last two bytes. */
switch (j) {
/* Number of hex characters */
case 1:
*d++ = xsingle2c(&input[i]);
break;
case 2:
case 3:
/* Use the last two from the end. */
*d++ = x2c(&input[i + j - 2]);
break;
case 4:
/* Use the last two from the end, but request
* a full width check.
*/
*d = x2c(&input[i + j - 2]);
fullcheck = 1;
break;
case 5:
/* Use the last two from the end, but request
* a full width check if the number is greater
* or equal to 0xFFFF.
*/
*d = x2c(&input[i + j - 2]);
/* Do full check if first byte is 0 */
if (input[i] == '0') {
fullcheck = 1;
}
else {
d++;
}
break;
case 6:
/* Use the last two from the end, but request
* a full width check if the number is greater
* or equal to 0xFFFF.
*/
*d = x2c(&input[i + j - 2]);
/* Do full check if first/second bytes are 0 */
if ( (input[i] == '0')
&& (input[i + 1] == '0')
) {
fullcheck = 1;
}
else {
d++;
}
break;
}
/* Full width ASCII (0xff01 - 0xff5e) needs 0x20 added */
if (fullcheck) {
if ( (*d > 0x00) && (*d < 0x5f)
&& ((input[i + j - 3] == 'f') ||
(input[i + j - 3] == 'F'))
&& ((input[i + j - 4] == 'f') ||
(input[i + j - 4] == 'F')))
{
(*d) += 0x20;
}
d++;
}
/* We must ignore a single whitespace after a hex escape */
if ((i + j < input_len) && isspace(input[i + j])) {
j++;
}
/* Move over. */
count++;
i += j;
}
/* No hexadecimal digits after backslash */
else if (input[i] == '\n') {
/* A newline character following backslash is ignored. */
i++;
}
/* The character after backslash is not a hexadecimal digit, nor a newline. */
else {
/* Use one character after backslash as is. */
*d++ = input[i++];
count++;
}
}
/* No characters after backslash. */
else {
/* Do not include backslash in output (continuation to nothing) */
i++;
}
}
/* Character is not a backslash. */
else {
/* Copy one normal character to output. */
*d++ = input[i++];
count++;
}
}
/* Terminate output string. */
*d = '\0';
return count;
}
/**
* Translate UNIX octal umask/mode to APR apr_fileperms_t
*/
apr_fileperms_t mode2fileperms(int mode) {
apr_fileperms_t perms = 0;
if (mode & S_IXOTH) perms |= APR_WEXECUTE;
if (mode & S_IWOTH) perms |= APR_WWRITE;
if (mode & S_IROTH) perms |= APR_WREAD;
if (mode & S_IXGRP) perms |= APR_GEXECUTE;
if (mode & S_IWGRP) perms |= APR_GWRITE;
if (mode & S_IRGRP) perms |= APR_GREAD;
if (mode & S_IXUSR) perms |= APR_UEXECUTE;
if (mode & S_IWUSR) perms |= APR_UWRITE;
if (mode & S_IRUSR) perms |= APR_UREAD;
if (mode & S_ISVTX) perms |= APR_WSTICKY;
if (mode & S_ISGID) perms |= APR_GSETID;
if (mode & S_ISUID) perms |= APR_USETID;
return perms;
}
/**
* 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;
msre_var *vx = NULL;
char *my_error_msg = NULL;
/* 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';
param++;
}
/* Resolve variable. */
var = msre_create_var_ex(msr->mp, msr->modsecurity->msre,
varname, param, msr, &my_error_msg);
if (var == NULL) return NULL;
/* Generate variable. */
vx = generate_single_var(msr, var, NULL, NULL, msr->msc_rule_mptmp);
if (vx == NULL) return NULL;
return (char *)vx->value;
}
/**
* @brief Transforms an apr_array_header_t to a text buffer
*
* Converts an apr_array_header_t into a plain/text buffer in a Key: Pair
* format. The generated buffer is not null terminated.
*
* If called with `buffer_length` set to 0 or with `buffer` set to NULL,
* it will _not_ fill any buffer, instead, it will return the length, that
* will be needed to save the entire content of `arr` into a buffer.
*
* @warning return is not NULL-terminated.
* @note memory management is in the responsibility of the caller.
*
* @param arr apr_array_header_t to be iterated.
* @param buffer pointer to the destination buffer.
* @param buffer_length length that will fully fill the buffer.
* @retval -1 Something went wrong in the process. Do not trust in
* buffer content.
* @retval n>0 size of the [needed|] buffer.
*
*/
int msc_headers_to_buffer(const apr_array_header_t *arr, char *buffer,
int buffer_length)
{
int headers_length = 0;
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;
for (i = 0; i < arr->nelts; i++) {
char *value = te[i].val;
char *key = te[i].key;
headers_length = headers_length + strlen(value) + strlen(key) + /* \n: */ 1 +
/* colum */ 1 + /* space: */ 1 ;
if (write_to_buffer == 1) {
if (buffer_length < headers_length) {
headers_length = -1;
goto not_enough_memory;
}
assert(ptr && ptr < buffer + buffer_length);
sprintf(ptr, "%s: %s\n", key, value);
ptr = buffer + headers_length; /* for the next entry. */
}
}
headers_length++; /* Save space for an extra '\n' between the hedaers and the request body */
if (write_to_buffer) {
if (buffer_length < headers_length) {
headers_length = -1;
goto not_enough_memory;
}
buffer[headers_length-1] = '\n';
}
not_enough_memory:
return headers_length;
}
int read_line(char *buf, int len, FILE *fp)
{
if (buf == NULL) return -1;
if (fgets(buf, len, fp) == NULL) {
*buf = '\0';
return 0;
}
char* tmp;
if ((tmp = strrchr(buf, '\n')) != NULL) *tmp = '\0';
return 1;
}
int create_radix_tree(apr_pool_t *mp, TreeRoot **rtree, char **error_msg)
{
*rtree = apr_palloc(mp, sizeof(TreeRoot));
if (*rtree == NULL)
{
*error_msg = apr_psprintf(mp, "Failed allocating " \
"memory to TreeRoot.");
goto root_node_failed;
}
memset(*rtree, 0, sizeof(TreeRoot));
(*rtree)->ipv4_tree = CPTCreateRadixTree(mp);
if ((*rtree)->ipv4_tree == NULL)
{
*error_msg = apr_psprintf(mp, "IPmatch: Tree initialization " \
"failed.");
goto ipv4_tree_failed;
}
(*rtree)->ipv6_tree = CPTCreateRadixTree(mp);
if ((*rtree)->ipv6_tree == NULL)
{
*error_msg = apr_psprintf(mp, "IPmatch: Tree initialization " \
"failed.");
goto ipv6_tree_failed;
}
return 0;
ipv6_tree_failed:
ipv4_tree_failed:
root_node_failed:
return -1;
}
int ip_tree_from_file(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;
char *end;
char buf[HUGE_STRING_LEN + 1]; // FIXME: 2013-10-29 zimmerle: dynamic?
char errstr[1024]; //
if (create_radix_tree(mp, rtree, error_msg))
{
return -1;
}
rc = apr_file_open(&fd, uri, APR_READ | APR_BUFFERED | APR_FILE_NOCLEANUP,
0, mp);
if (rc != APR_SUCCESS)
{
*error_msg = apr_psprintf(mp, "Could not open ipmatch file \"%s\": %s",
uri, apr_strerror(rc, errstr, 1024));
return -1;
}
while ((rc = apr_file_gets(buf, HUGE_STRING_LEN, fd)) != APR_EOF)
{
line++;
if (rc != APR_SUCCESS)
{
*error_msg = apr_psprintf(mp, "Could not read \"%s\" line %d: %s",
uri, line, apr_strerror(rc, errstr, 1024));
return -1;
}
start = buf;
while ((apr_isspace(*start) != 0) && (*start != '\0'))
{
start++;
}
for (end = start; end != NULL || *end != '\0' || *end != '\n'; end++)
{
if (apr_isxdigit(*end) || *end == '.' || *end == '/' || *end == ':')
{
continue;
}
if (*end != '\n')
{
*error_msg = apr_psprintf(mp, "Invalid char \"%c\" in line %d " \
"of file %s", *end, line, uri);
}
break;
}
*end = '\0';
if ((start == end) || (*start == '#'))
{
continue;
}
if (strchr(start, ':') == NULL)
{
tnode = TreeAddIP(start, (*rtree)->ipv4_tree, IPV4_TREE);
}
#if APR_HAVE_IPV6
else
{
tnode = TreeAddIP(start, (*rtree)->ipv6_tree, IPV6_TREE);
}
#endif
if (tnode == NULL)
{
*error_msg = apr_psprintf(mp, "Could not add entry " \
"\"%s\" in line %d of file %s to IP list", start, line, uri);
return -1;
}
}
if (fd != NULL)
{
apr_file_close(fd);
}
return 0;
}
#ifdef WITH_CURL
int ip_tree_from_uri(TreeRoot **rtree, char *uri,
apr_pool_t *mp, char **error_msg)
{
TreeNode *tnode = NULL;
int line = 0;
int res;
struct msc_curl_memory_buffer_t chunk;
char *word = NULL;
char *brkt = NULL;
char *sep = "\n";
if (create_radix_tree(mp, rtree, error_msg))
{
return -1;
}
res = msc_remote_download_content(mp, uri, NULL, &chunk, error_msg);
if (res)
{
return res;
}
for (word = strtok_r(chunk.memory, sep, &brkt);
word;
word = strtok_r(NULL, sep, &brkt))
{
int i = 0;
line++;
/* Ignore empty lines and comments */
if (*word == '#') continue;
for (i = 0; i < strlen(word); i++)
{
if (apr_isxdigit(word[i]) || word[i] == '.' || word[i] == '/' || word[i] == ':' || word[i] == '\n')
{
continue;
}
*error_msg = apr_psprintf(mp, "Invalid char \"%c\" in line %d " \
"of uri %s", word[i], line, uri);
return -1;
}
if (strchr(word, ':') == NULL)
{
tnode = TreeAddIP(word, (*rtree)->ipv4_tree, IPV4_TREE);
}
#if APR_HAVE_IPV6
else
{
tnode = TreeAddIP(word, (*rtree)->ipv6_tree, IPV6_TREE);
}
#endif
if (tnode == NULL)
{
*error_msg = apr_psprintf(mp, "Could not add entry " \
"\"%s\" in line %d of file %s to IP list", word, line, uri);
return -1;
}
}
msc_remote_clean_chunk(&chunk);
return 0;
}
#endif
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;
#endif
if (rtree == NULL)
{
return 0;
}
if (strchr(value, ':') == NULL) {
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,
IPV4_TREE) != NULL) {
return 1;
}
}
#if APR_HAVE_IPV6
else {
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,
IPV6_TREE) != NULL) {
return 1;
}
}
#endif
return 0;
}
int ip_tree_from_param(apr_pool_t *mp,
char *param, TreeRoot **rtree, char **error_msg)
{
char *saved = NULL;
char *str = NULL;
TreeNode *tnode = NULL;
if (create_radix_tree(mp, rtree, error_msg))
{
return -1;
}
str = apr_strtok(param, ",", &saved);
while (str != NULL)
{
if (strchr(str, ':') == NULL)
{
tnode = TreeAddIP(str, (*rtree)->ipv4_tree, IPV4_TREE);
}
#if APR_HAVE_IPV6
else
{
tnode = TreeAddIP(str, (*rtree)->ipv6_tree, IPV6_TREE);
}
#endif
if (tnode == NULL)
{
*error_msg = apr_psprintf(mp, "Could not add entry " \
"\"%s\" from: %s.", str, param);
return -1;
}
str = apr_strtok(NULL, ",", &saved);
}
return 0;
}
#ifdef WITH_CURL
size_t msc_curl_write_memory_cb(void *contents, size_t size,
size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
struct msc_curl_memory_buffer_t *mem = (struct msc_curl_memory_buffer_t *)userp;
if (mem->size == 0)
{
mem->memory = malloc(realsize + 1);
if (mem->memory == NULL) {
return 0;
}
memset(mem->memory, '\0', sizeof(realsize + 1));
}
else
{
mem->memory = realloc(mem->memory, mem->size + realsize + 1);
memset(mem->memory + mem->size, '\0', sizeof(realsize + 1));
}
if (mem->memory == NULL) {
/* out of memory! */
return 0;
}
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}
#endif
#ifdef WIN32
char* strtok_r(
char *str,
const char *delim,
char **nextp)
{
char *ret;
if (str == NULL)
{
str = *nextp;
}
str += strspn(str, delim);
if (*str == '\0')
{
return NULL;
}
ret = str;
str += strcspn(str, delim);
if (*str)
{
*str++ = '\0';
}
*nextp = str;
return ret;
}
#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;
}
modsecurity-apache-2.9.13/apache2/msc_util.h 0000664 0000000 0000000 00000014210 15174655764 0020711 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#ifndef _UTIL_H_
#define _UTIL_H_
#include
#include
#include
#ifndef APR_WSTICKY
/* Add extra flags added to APR in 0.9.5 */
#define APR_USETID 0x8000 /**< Set user id */
#define APR_GSETID 0x4000 /**< Set group id */
#define APR_WSTICKY 0x2000 /**< Sticky bit */
#endif
#include "modsecurity.h"
#include "re.h"
#include "msc_tree.h"
#ifdef WIN32
#include
// This is a trick: for ModSecurity modules this will declare inet_pton,
// but for mymodule.cpp (IIS module) this will skip, because we include
// windows.h before including msc_util.h
// Without the trick we have redefinition conflict.
//
#if !(NTDDI_VERSION >= NTDDI_VISTA)
int DSOLOCAL inet_pton(int family, const char *src, void *dst);
#endif
#endif
#define UNICODE_ERROR_CHARACTERS_MISSING -1
#define UNICODE_ERROR_INVALID_ENCODING -2
#define UNICODE_ERROR_OVERLONG_CHARACTER -3
#define UNICODE_ERROR_RESTRICTED_CHARACTER -4
#define UNICODE_ERROR_DECODING_ERROR -5
#ifdef LINUX_S390
int DSOLOCAL swap_int32(int x);
#endif
char DSOLOCAL *utf8_unicode_inplace_ex(apr_pool_t *mp, unsigned char *input, long int input_len, int *changed);
char DSOLOCAL *m_strcasestr(const char *haystack, const char *needle);
int DSOLOCAL normalize_path_inplace(unsigned char *input, int len, int win, int *changed);
int DSOLOCAL parse_boolean(const char *input);
char DSOLOCAL *remove_quotes(apr_pool_t *mptmp, const char *input, int input_len);
char DSOLOCAL *parse_pm_content(const char *op_parm, unsigned short int op_len, msre_rule *rule, char **error_msg);
char DSOLOCAL *remove_escape(apr_pool_t *mptmp, const char *input, int input_len);
int DSOLOCAL parse_name_eq_value(apr_pool_t *mp, const char *input, char **name, char **value);
char DSOLOCAL *url_encode(apr_pool_t *mp, char *input, unsigned int input_len, int *changed);
char DSOLOCAL *strnurlencat(char *destination, char *source, unsigned int maxlen);
char DSOLOCAL *file_dirname(apr_pool_t *p, const char *filename);
char DSOLOCAL *file_basename(apr_pool_t *p, const char *filename);
int DSOLOCAL sql_hex2bytes_inplace(unsigned char *data, int len);
int DSOLOCAL hex2bytes_inplace(unsigned char *data, int len);
char DSOLOCAL *bytes2hex(apr_pool_t *pool, unsigned char *data, int len);
int DSOLOCAL is_token_char(unsigned char c);
int DSOLOCAL remove_lf_crlf_inplace(char *text);
char DSOLOCAL *guess_tmp_dir(apr_pool_t *p);
char DSOLOCAL *current_logtime(apr_pool_t *mp);
char DSOLOCAL *current_filetime(apr_pool_t *mp);
int DSOLOCAL msc_mkstemp_ex(char *templat, int mode);
int DSOLOCAL msc_mkstemp(char *templat);
char DSOLOCAL *strtolower_inplace(unsigned char *str);
char DSOLOCAL *log_escape_re(apr_pool_t *p, const char *text);
char DSOLOCAL *log_escape(apr_pool_t *p, const char *text);
char DSOLOCAL *log_escape_nq(apr_pool_t *p, const char *text);
char DSOLOCAL *log_escape_ex(apr_pool_t *p, const char *text, unsigned long int text_length);
char DSOLOCAL *log_escape_nq_ex(apr_pool_t *p, const char *text, unsigned long int text_length);
char DSOLOCAL *log_escape_hex(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length);
char DSOLOCAL *log_escape_raw(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length);
char DSOLOCAL *log_escape_nul(apr_pool_t *mp, const unsigned char *text, unsigned long int text_length);
int DSOLOCAL decode_base64_ext(char *plain_text, const unsigned char *input, int input_len);
int DSOLOCAL convert_to_int(const char c);
int DSOLOCAL set_match_to_tx(modsec_rec *msr, int capture, const char *match, int tx_n);
int DSOLOCAL set_match_to_tx_safe(modsec_rec *msr, int capture, const char *match, unsigned int match_len, int tx_n);
int DSOLOCAL js_decode_nonstrict_inplace(unsigned char *input, long int input_len);
int DSOLOCAL urldecode_uni_nonstrict_inplace_ex(unsigned char *input, long int input_length, int * changed);
int DSOLOCAL urldecode_nonstrict_inplace_ex(unsigned char *input, long int input_length, int *invalid_count, int *changed);
int DSOLOCAL html_entities_decode_inplace(apr_pool_t *mp, unsigned char *input, int len);
int DSOLOCAL ansi_c_sequences_decode_inplace(unsigned char *input, int len);
char DSOLOCAL *modsec_build(apr_pool_t *mp);
int DSOLOCAL is_empty_string(const char *string);
char DSOLOCAL *resolve_relative_path(apr_pool_t *pool, const char *parent_filename, const char *filename);
int DSOLOCAL css_decode_inplace(unsigned char *input, long int input_len);
apr_fileperms_t DSOLOCAL mode2fileperms(int mode);
char DSOLOCAL *construct_single_var(modsec_rec *msr, char *name);
char DSOLOCAL *format_all_performance_variables(modsec_rec *msr, apr_pool_t *mp);
unsigned char DSOLOCAL is_netmask_v4(char *ip_strv4);
unsigned char DSOLOCAL is_netmask_v6(char *ip_strv6);
int DSOLOCAL msc_headers_to_buffer(const apr_array_header_t *arr, char *buffer, int max_length);
int DSOLOCAL ip_tree_from_file(TreeRoot **rtree, char *uri,
apr_pool_t *mp, char **error_msg);
int DSOLOCAL tree_contains_ip(apr_pool_t *mp, TreeRoot *rtree,
const char *value, modsec_rec *msr, char **error_msg);
int DSOLOCAL ip_tree_from_param(apr_pool_t *pool,
char *param, TreeRoot **rtree, char **error_msg);
#ifdef WITH_CURL
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,
size_t nmemb, void *userp);
struct msc_curl_memory_buffer_t
{
char *memory;
size_t size;
};
#ifdef WIN32
char *strtok_r(char *str, const char *delim, char **nextp);
#endif
#endif
modsecurity-apache-2.9.13/apache2/msc_xml.c 0000664 0000000 0000000 00000033762 15174655764 0020544 0 ustar 00root root 0000000 0000000 /*
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
* 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
*
* 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 "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;
}
/**
* Initialise XML parser.
*/
int xml_init(modsec_rec *msr, char **error_msg) {
assert(msr != NULL);
assert(error_msg != NULL);
xmlParserInputBufferCreateFilenameFunc entity;
*error_msg = NULL;
msr->xml = apr_pcalloc(msr->mp, sizeof(xml_data));
if (msr->xml == NULL) return -1;
if(msr->txcfg->xml_external_entity == 0) {
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;
}
#if 0
static void xml_receive_sax_error(void *data, const char *msg, ...) {
modsec_rec *msr = (modsec_rec *)data;
char message[256];
if (msr == NULL) return;
apr_snprintf(message, sizeof(message), "%s (line %d offset %d)",
log_escape_nq(msr->mp, msr->xml->parsing_ctx->lastError.message),
msr->xml->parsing_ctx->lastError.line,
msr->xml->parsing_ctx->lastError.int2);
msr_log(msr, 5, "XML: Parsing error: %s", message);
}
#endif
/**
* 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) {
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 && msr->xml->parsing_ctx_arg == NULL) {
/* First invocation. */
msr_log(msr, 4, "XML: Initialising parser.");
/* NOTE When Sax interface is used libxml will not
* create the document object, but we need it.
msr->xml->sax_handler = (xmlSAXHandler *)apr_pcalloc(msr->mp, sizeof(xmlSAXHandler));
if (msr->xml->sax_handler == NULL) return -1;
msr->xml->sax_handler->error = xml_receive_sax_error;
msr->xml->sax_handler->warning = xml_receive_sax_error;
msr->xml->parsing_ctx = xmlCreatePushParserCtxt(msr->xml->sax_handler, msr,
buf, size, "body.xml");
*/
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;
}
}
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;
}
}
return 1;
}
/**
* Finalise XML parsing.
*/
int xml_complete(modsec_rec *msr, char **error_msg) {
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 || 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;
/* 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 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;
}
/**
* 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;
}
return 1;
}
modsecurity-apache-2.9.13/apache2/msc_xml.h 0000664 0000000 0000000 00000003507 15174655764 0020543 0 ustar 00root root 0000000 0000000 /*
* 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.
*/
#ifndef _MSC_XML_H_
#define _MSC_XML_H_
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 */
int DSOLOCAL xml_init(modsec_rec *msr, char **error_msg);
int DSOLOCAL xml_process_chunk(modsec_rec *msr, const char *buf,
unsigned int size, char **error_msg);
int DSOLOCAL xml_complete(modsec_rec *msr, char **error_msg);
apr_status_t DSOLOCAL xml_cleanup(modsec_rec *msr);
#endif
modsecurity-apache-2.9.13/apache2/others/ 0000775 0000000 0000000 00000000000 15174655764 0020227 5 ustar 00root root 0000000 0000000 modsecurity-apache-2.9.13/apache2/others/libinjection/ 0000775 0000000 0000000 00000000000 15174655764 0022700 5 ustar 00root root 0000000 0000000 modsecurity-apache-2.9.13/apache2/others/libinjection/.clang-format 0000664 0000000 0000000 00000000074 15174655764 0025254 0 ustar 00root root 0000000 0000000 ---
Language: Cpp
BasedOnStyle: LLVM
IndentWidth: 4
modsecurity-apache-2.9.13/apache2/others/libinjection/.github/ 0000775 0000000 0000000 00000000000 15174655764 0024240 5 ustar 00root root 0000000 0000000 modsecurity-apache-2.9.13/apache2/others/libinjection/.github/workflows/ 0000775 0000000 0000000 00000000000 15174655764 0026275 5 ustar 00root root 0000000 0000000 modsecurity-apache-2.9.13/apache2/others/libinjection/.github/workflows/ci.yml 0000664 0000000 0000000 00000007366 15174655764 0027427 0 ustar 00root root 0000000 0000000 name: CI
on:
push:
pull_request:
jobs:
lint:
name: Format check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # ratchet:actions/checkout@v5
- name: Run clang-format style check for C/C++ programs.
uses: jidicula/clang-format-action@6cd220de46c89139a0365edae93eee8eb30ca8fe # ratchet:jidicula/clang-format-action@v4.16.0
with:
clang-format-version: '16'
check-path: 'src'
cppcheck:
runs-on: ubuntu-latest
name: cppcheck
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # ratchet:actions/checkout@v5
name: checkout repo
- name: Setup Dependencies
run: |
sudo apt-get update -y -qq
sudo apt-get install cppcheck
- name: cppcheck
run: |
cppcheck --std=c89 \
--enable=all \
--inline-suppr \
--inconclusive \
--suppress=variableScope \
--disable=missingInclude \
--quiet \
--error-exitcode=1 \
--template='{file}:{line} {id} {severity} {message}' \
.
clang-static:
runs-on: ubuntu-latest
name: clang static check
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # ratchet:actions/checkout@v5
name: checkout repo
- name: make analyze
run: |
./autogen.sh
./configure
make -C src analyze
build-linux:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-22.04, ubuntu-24.04]
platform: [x32, x64]
compiler: [gcc, clang]
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # ratchet:actions/checkout@v5
- name: Setup Dependencies
run: |
sudo apt-get update -y -qq
sudo apt-get install valgrind
- name: build
run: |
./autogen.sh
./configure
make
- name: test
run: make -e check
- name: clean
run: make clean
- name: build pedantic
run: make
env:
CFLAGS: '-Wall -Wextra -Werror -pedantic -ansi -g -O1'
- name: test valgrind
run: make -e check
env:
VALGRIND: 'valgrind --gen-suppressions=no --leak-check=full --show-leak-kinds=all --read-var-info=yes --error-exitcode=1 --track-origins=yes --suppressions=/home/runner/work/libinjection/libinjection/src/alpine.supp'
build-macos:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-14, macos-15]
compiler: [gcc, clang]
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # ratchet:actions/checkout@v5
- name: Cache brew deps
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # ratchet:actions/cache@v4
with:
# Paths to cache:
# /usr/local/Homebrew - installation folder of Homebrew
# /usr/local/Cellar - installation folder of Homebrew formulae
# /usr/local/Frameworks, /usr/local/bin, /usr/local/opt - contain (links to) binaries installed by Homebrew formulae
path: |
/usr/local/Homebrew
/usr/local/Cellar
/usr/local/Frameworks
/usr/local/bin
/usr/local/opt
key: macos-build-cache-${{ hashFiles('./scripts/builder_setup.sh') }}
- name: build
run: |
brew install automake autoconf libtool
./autogen.sh
./configure
make
- name: test
run: make -e check
- name: clean
run: make clean
- name: build pedantic
run: make all
env:
CFLAGS: '-Wall -Wextra -Werror -pedantic -ansi -g -O1'
modsecurity-apache-2.9.13/apache2/others/libinjection/.github/workflows/codeql.yml 0000664 0000000 0000000 00000006353 15174655764 0030276 0 ustar 00root root 0000000 0000000 # For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: ["main"]
pull_request:
# The branches below must be a subset of the branches above
branches: ["main"]
schedule:
- cron: '19 6 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: ['cpp', 'python']
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Use only 'java' to analyze code written in Java, Kotlin or both
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # ratchet:actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@b8d3b6e8af63cde30bdc382c0bc28114f4346c88 # ratchet:github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality
uses: github/codeql-action/autobuild@b8d3b6e8af63cde30bdc382c0bc28114f4346c88 # ratchet:github/codeql-action/autobuild@v2
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@b8d3b6e8af63cde30bdc382c0bc28114f4346c88 # ratchet:github/codeql-action/analyze@v2
with:
category: "/language:${{matrix.language}}"
modsecurity-apache-2.9.13/apache2/others/libinjection/.github/workflows/fuzzing.yml 0000664 0000000 0000000 00000001535 15174655764 0030520 0 ustar 00root root 0000000 0000000 name: Fuzzing
on:
push:
pull_request:
jobs:
fuzzing:
runs-on: ubuntu-latest
name: fuzzing
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # ratchet:actions/checkout@v5
- name: build
run: |
./autogen.sh
CC=clang CXX=clang++ ./configure --enable-sanitizers --enable-fuzzers
make
- name: fuzz
run: "mkdir out\n./src/fuzz/fuzzer \\\n -max_total_time=\"${MAX_TOTAL_TIME:-300}\" \\\n -print_pcs=1 \\\n -workers=\"${FUZZY_WORKERS:-0}\" \\\n -jobs=\"${FUZZY_JOBS:-0}\" \\\n -artifact_prefix=./out/ ./src/fuzz/corpus \n"
- name: Upload Crash
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # ratchet:actions/upload-artifact@v5
if: failure()
with:
name: artifacts
path: ./out
modsecurity-apache-2.9.13/apache2/others/libinjection/.gitignore 0000664 0000000 0000000 00000001055 15174655764 0024671 0 ustar 00root root 0000000 0000000 *~
*.pyc
*.dSYM
c/#*
*.plist
*.info
*.gch
*.gcov
# gnu autotest
*.trs
Makefile
src/Makefile
libinjection.pc
aclocal.m4
app.info
autom4te.cache
compile
config.h
config.h.in
config.guess
config.log
config.status
config.sub
configure
configure.scan
coverage_report
depcomp
install-sh
libtool
ltmain.sh
Makefile.in
#m4
missing
stamp-h1
*~
*.html
*.log
*.o
*.la
*.so*
*.a
.deps
*.tar*
*.zip
*.lo
*.gcno
*.gcda
src/.libs
src/version.h
src/teststackxss
src/testerrorhandling
src/fuzz/fuzzer
src/fuzz/corpus
src/libinjection_sqli_data.h
src/sqlparse_data.json
modsecurity-apache-2.9.13/apache2/others/libinjection/CHANGELOG 0000664 0000000 0000000 00000000022 15174655764 0024104 0 ustar 00root root 0000000 0000000 see CHANGELOG.md
modsecurity-apache-2.9.13/apache2/others/libinjection/CHANGELOG.md 0000664 0000000 0000000 00000036650 15174655764 0024523 0 ustar 00root root 0000000 0000000 # v4.0.0 - 2026-03-24
## MAJOR API BREAKING CHANGE
This is a major version release with **breaking API changes**. Applications using libinjection will need to be updated and recompiled.
### New Error Handling Model
* [#65](https://github.com/libinjection/libinjection/pull/65) [#27](https://github.com/libinjection/libinjection/issues/27) **BREAKING:** Changed return type from `int` to `injection_result_t` enum for all detection functions
* **CRITICAL:** libinjection no longer calls `abort()` and terminates the process on parser errors
* **NEW:** Functions now return `LIBINJECTION_RESULT_ERROR` (-1) when parser encounters an invalid state, allowing graceful error handling
* **BACKWARD COMPATIBLE:** `LIBINJECTION_RESULT_FALSE` (0) and `LIBINJECTION_RESULT_TRUE` (1) maintain numeric compatibility with old boolean return values
* **IMPORTANT:** Applications must be updated to check for and handle `LIBINJECTION_RESULT_ERROR` to avoid treating parser errors as benign input
### Affected Functions
* `libinjection_xss()` - now returns `injection_result_t`
* `libinjection_sqli()` - now returns `injection_result_t`
* `libinjection_is_xss()` - now returns `injection_result_t`
* `libinjection_h5_next()` - now returns `injection_result_t`
### New Files
* `src/libinjection_error.h` - defines `injection_result_t` enum
### Migration
See [MIGRATION.md](MIGRATION.md) for detailed migration instructions.
### Why This Change?
Previously, when libinjection's parser encountered an invalid state (e.g., string cursor position exceeding string length), it would call `assert()` or `abort()`, immediately terminating the entire process. This was problematic for:
- Web application servers that would crash on malformed input
- Embedded systems that require high availability
- Applications that need graceful degradation
The new error handling model allows applications to detect and handle parser errors appropriately without process termination.
### Bug Fixes
* [#70](https://github.com/libinjection/libinjection/pull/70) [#72](https://github.com/libinjection/libinjection/pull/72) Fix buffer overread in `html_decode_char_at()` — incorrect length check could read past allocated buffer
* [#62](https://github.com/libinjection/libinjection/pull/62) Fix length check for added argument in SQLi parser
* [#60](https://github.com/libinjection/libinjection/pull/60) Fix detection of `1.e` and `1.E` SQLi patterns
### XSS Detection
* [#57](https://github.com/libinjection/libinjection/pull/57) Update HTML event attributes from upstream WebKit
### Build and Embedding
* Source files now compile standalone without autotools — `LIBINJECTION_VERSION` defaults to `"4.0.0"` when not overridden
* [#74](https://github.com/libinjection/libinjection/pull/74) Updated README with build options and embedding instructions
* [#67](https://github.com/libinjection/libinjection/pull/67) [#68](https://github.com/libinjection/libinjection/pull/68) Fix fuzzing build configuration
* [#47](https://github.com/libinjection/libinjection/pull/47) Add missing parenthesis variations in SQLi insertion samples
### Other Changes
* Removed all `assert()` calls in parser code, replaced with proper error handling
* Updated SWIG bindings for Python, PHP, and Lua to support new return type
* Added comprehensive documentation and migration guide
* [#76](https://github.com/libinjection/libinjection/pull/76) Updated copyright to libinjection Contributors
* [#73](https://github.com/libinjection/libinjection/pull/73) [#64](https://github.com/libinjection/libinjection/pull/64) CI updates: newer runners, action versions, clang-format and cppcheck fixes
# v3.9.2 - 2016-05-21
* Release of whatever changes have been made over the last 2.5 years.
# v3.9.1 - 2013-12-26
Day-After-Christmas Edition
* No functional changes
* Code reverted to strict C90 style to allow builds on embedded systems, Windows and FreeBSD
* For gcc this means `-std=c90 -pedantic`, which seems to simulate Windows behavior on Linux
* Other minor style changes to header files.
# v3.9.0 - 2013-11-29
Black Friday Edition
* Big API Change!! everything in `libinjection.h` is now `libinjection_sqli.h`. And a new super simple API is in `libinjection.h`
* Improvements to folder to prevent bypasses using SQL types (casts). This eliminated about 400 fingerprints as well.
* Blacklisted a very degenerate MySQL ODBC case, that is highly unlike to be used in 'real inputs'. thanks to @LightOS foreporting.. not clear who found it originally.
* Over 400 unit tests now!
* Compiles clean under clang with `-Weverything -Wno-padded` `-Wno-padded` is excluded since it's architecture dependant. See `clang.sh` to see how to invoke.
* PHP documentation fixes, thanks @LightOS
# v3.8.0 - 2013-10-18
LAMP Special Edition: MySQL and PHP improvements
* [Issue #33](https://github.com/client9/libinjection/issues/54) Fixes MySQL in latin1-mode use of `%A0` as whitespace. This was tricky since `%A0` might be part of larger UTF-8 encoding as well. Or perhaps `%C2%A0` (utf-8 encoding) might be treated as whitespace. Fortunately, MySQL only seems to treat `%A0` as whitespace in latin1 mode. HT [@ru_raz0r](https://twitter.com/ru_raz0r)
* Fixes to Lua testdriver and portability fixes
* Much improved PHP build and test. It now uses `phpize` and builds and tests like a real module.
* API CHANGE: the macro `LIBINJECTION_VERSION` has been replaced by `const char* libinjection_version()`. This allows us to increment the version number without having to regenerate SWIG (or other) bindings for minor releases.
NOTE:
Pregenerated [SWIG](http://www.swig.org/) bindings are removed. You'll need to install SWIG before running `make`. SWIG is packaged on virtually every OS so this should not be a problem.
Here's why:
* Latest versions of swig appear to generate poor quality bindings for LUA and Python. Bugs are filed upstream [1341](https://sourceforge.net/p/swig/bugs/1341/), [1343](https://sourceforge.net/p/swig/bugs/1343/), [1345](https://sourceforge.net/p/swig/bugs/1345/). These are fixed or will be fixed in swig 3.0.0.
* In addition, I've received a number of reports of generated code failing various static analysis
* I can't triangulate which SWIG for which language for which OS will work for you
* I may be switching to [libffi](http://cffi.readthedocs.org/) for python, and [luajit.ffi](http://luajit.org/ext_ffi.html) for lua(jit) in the future, anyways.
# v3.7.1 -- 2013-10-13
* Remove un-needed code
# v3.7.0 -- 2013-10-13
Major Release
* [Issue #54](https://github.com/client9/libinjection/issues/54): Add test vectors from [Arne Swinnen](http://www.arneswinnen.net/2013/09/automated-sql-injection-detection/). Thanks [qerub@github](https://github.com/qerub)
* Minor fingerprint update for [Issue #54](https://github.com/client9/libinjection/issues/54). I don't really think it's valid SQL but it's safe enough to detect without false positives.
* [Issue #55](https://github.com/client9/libinjection/issues/55): Parse MS SQLSERVER use of \[brackets\] for column and table names. This is a big one that closes a lot of holes. Thanks [nroggle@github](https://github.com/nroggel)
* [Issue #56](https://github.com/client9/libinjection/issues/56): fix buffer over-read. Thanks [safe3@github](https://github.com/Safe3) and [flily@github](https://github.com/flily)
* Remove use of `-fstack-protector` as it breaks valgrind detecting memory problems
Read more about it http://blog.client9.com/2013/10/12/gcc-valgrind-stackprotector.html
* Fixed folding issue where `1,-sin(1))` would be folded as `1 (1)`
* Add more test cases and improved test coverage to [98.8%](https://libinjection.client9.com/cicada/artifacts/libinjection-coverage-unittest/lcov-html/c/libinjection_sqli.c.gcov.html)
# v3.6.0 -- 2013-09-11
* New PHP API
* Big fingerprint update
** about 500 new fingerprints added based on fuzzing tests by Reto Ischi
** about 700 impossible, dead fingerprints removed
** adding folding rule for "sqltype sqltype -> sqltype" since
`select binary binary binary 1` is valid
* Other minor fingerprints added
* -maybe- API change as typedefs and structs were re-arranged for SWIG
# v3.5.3 -- 2013-08-25
* Fingerprint update -- `BETWEEN` operation bypasses
* Fingerprint update -- `ANY/SOME` quasi-function bypasses
* Fixed issue with folding where `1-(2-3)` would fold to "nothing" instead of `1`
* Improved test coverage to [98.0%](https://libinjection.client9.com/cicada/artifacts/libinjection-coverage-unittest/lcov-html/c/libinjection_sqli.c.gcov.html)
* More adjustments to the PHP/MYSQL backtick to reduce false positives
# v3.5.2 -- 2013-08-21
* Fingerprint update. Credit: Reto Ischi
# v3.5.1 -- 2013-08-21
* found regression in handling of PHP/MySQL backticks. Tests added
* Dead code removed.
* Improved test coverage to [97.7%](https://libinjection.client9.com/cicada/artifacts/libinjection-coverage-unittest/lcov-html/c/libinjection_sqli.c.gcov.html)
# v3.5.0 -- 2013-08-21
* Bug fix for libinjection_sqli_reset @brianrectanus
https://github.com/client9/libinjection/pull/50
* Non-critical parser fix for numbers with oracle's ending
suffix. "SELECT 1FROM .." -> (SELECT, 1, FROM) not
(SELECT, 1F, ROM)
* Yet another fix for disambiguating Oracle's "f" suffix for numbers HT @LightOS
* Better parsing of generated number forms of "10.e" and "10.10e"
(these are actually table specifiers!) HT @LightOS
* Change sizing of some static arrays to have a length >= 8
For GCC based applications, this allows -fstack-protector to work
and -Wstack-protector will now not emit errors.
* Added '-fstack-protector-all -D_FORTIFY_SOURCE=2' to default CFLAGS.
About 10% performance loss with -fstack-protector-all
* Improvements in reducing false positives, HT modsecurity team
* Add fingerprint, HT @FluxReiners
* Support for parsing of old ODBC-style typing, e.g. 'select {foo 1};' (valid in MySQL)
* Fix tokenization of "IF EXISTS(....", "IF NOT EXISTS(..."
* Fi possible stack over-read, and improve detection of "sp_password" flag
in short sqli HT modsecurity team
# v3.4.1 2013-07-18
* Fingerprint update only HT @LightOS
# v3.4.0 2013-07-18
* Fix regression with COLLATE
* Handle "procedure analyze" under MySQL
* Make API most robust when setting flags
* Add folding API
* Add new all-C test driver to improve testing speed
* Makefile cleanups
* Fired Jenkins! Using in-house system.
* Fixed bypass reported by @FluxReiners
# v3.3.0 2013-07-13
* change how backslash is handled to catch old MSSQL servers sqli
See http://websec.ca/kb/sql_injection#MSSQL_Allowed_Intermediary_Chars_AND-OR
for details
* Reworking of COLLATE to handle MySQL, TSQL types automatically
* Handle bizarro world TSQL '\%1' which is parsed as "0 % 1"
* Better stacked query detection, fixing some regressions
* Folding improvements
* False positive improvements
# v3.2.0 2013-07-12
* Parse binary litterals "0b010101" used by at least mysql and pgsql
* Add fingerprints '1&EUE', '1&EkU' to work around ambiguous parsing rules
"-1.for" == '-1.f OR' vs. '-1. FOR' CREDIT @LightOS
* Add parsing rules for COLLATION in MySQL, CREDIT @LightOS
* Reduce false positives by removing all fingerprints that contained "sn"
* Improvement in handling MySQL 'binary' quasi-operator/type
* Improvements in folding
* Removed dependency on SWIG for installing python module
# v3.1.0 2013-07-02
* Fix for parsing Oracle numeric literals
* Fix for oracle whitespace with null char.
* Add unusual SQL join types to keywords lists
* Minor fixes to python API examples
# v3.0.0 2013-06-23
Big Release and Big Engine change. Highly recommend
* Numerous evasions and false positives fixed!
* Tokenizer is now really dumb, and publically exposed. See `libinjection_sqli_tokenize`.
* Folding engine completely rewritten to be simpler and easier to extend, debug, port.
* MySQL `backticks` now handled correctly
* @"var" and @'var' parsed correctly (mysql)
* ":=" operator parsed correctly
* non-ascii SQL variables and barewords handled correctly
* less false positives and those that are false positives
are more "indeterminate cases" and are only in a few
fingerprints
* autogeneration of fingerprints with trivial SQL variations
* support for pgsql $ strings
* support for oracle's q and nq strings
* support for mysql's n strings
* parsing stats exposed
* new swig bindings for python and lua, with callbacks into original scripting
language for accept/reject of fingerprints (i.e. manage fingerprints in
script, not C code)
* Improved parsing of various special cases in MySQL
* Ban MySQL conditional comments. If we find them, it's marked as SQLi immediately.
* Probably a bunch of other stuff too
# v2.0.4 2013-05-21 IMPORTANT
All users are advised to upgrade due to risk of DOS
## security
* more fingerprints, more tests
* Issue 34: fix infinite loop
# v2.0.3 2013-05-21
## security
* Add variations on '1U(((', thanks @LightOS
* Add automatically all variations on other cases of
'parens padding'
# v2.0.2 2013-05-21
## security
* Added fingerprint 'nU(kn' and variations, thanks to
discussion with @ModSecurity .
# v2.0.1 2013-05-21
## security
* Added fingerprint knknk, thanks @d0znpp
# v2.0.0 2013-05-17
Version 2 is more a software engineering release than SQLi.
The API, the code, and filenames are improved for embedded
use. Please see the README.md file for details on use.
## security
* Fix Issue30: detection of more small sqli forms with fingerprint "1c".
* Fix Issue32: false positive of '*/*' of type 'oc' Thanks to @brianrectanus
## API Changes
BIG CHANGES
* File name changes. These are the only relevant files:
* `c/libinjection.h`
* `c/libinjection_sqli.c`
* `c/libinjection_sqli_data.h`
* `COPYING`
* Just need to include `libinjection.h` and link with `libinjection_sqli_.c`
* `sqlparse_private.h` and `sqli_fingerprints.h` are deprecated.
Only use `#include "libinjection.h"`
* API name changes `is_sqli` and `is_string_sqli` are now
`libinjection_is_sqli` and `libinjection_is_string_sqli`
* API change, `libinjection_is_sqli` now takes a 5th arg for callback data
* API change, `libinjection_is_sqli` accepts `NULL` for arg4 and arg5
in which case, a default lookup of fingerprints is used.
* `sqlmap_data.json` now includes fingerprint information, so people making
ports only need to parse one file.
## other
* Allow `clang` compiler (also in Jenkins, a build with clang and
make-scan is done)
* Optimizations should result in > 10% performance improvement
for normal workloads
* Add `sqlite3` special functions and keywords (since why not)
# v1.2.0 2013-05-06
## security
* fix regression in detecting SQLi of type '1c'
##
* improved documentation, comments, edits.
# v1.1.0 2013-05-04
## security
* Fix for nested c-style comments used by postgresql and transact-sql.
Thanks to @Kanatoko for the report.
* Numerous additions to SQL functions lists (in particular pgsql, transact-sql
and ms-access functions)
Thanks to Christoffer Sawicki (GitHub "qerub") for report on cut-n-paste error.
Thanks to @ryancbarnett for reminder that MS-ACCESS exists ;-)
* Adding of fingerprints to detect HPP attacks.
* Algorihmically added new fingerprints to detect new _future_ sqli attacks. All of these
new fingerprints have no been seen 'in the wild' yet.
## other
* Replaced BSD memmem with optimzed version. This eliminates all 3rd party code.
* Added alpha python module (python setup.py install)
* Added sqlparse_fingerprints.h and sqlparse_data.json to aid porting and embeddeding.
* Added version number in sqlparse.h, based on
http://www.python.org/dev/peps/pep-0386/#normalizedversion
# v1.0.0 2013-04-24
* retroactive initial release
* all memory issues fixed
modsecurity-apache-2.9.13/apache2/others/libinjection/COPYING 0000664 0000000 0000000 00000003137 15174655764 0023737 0 ustar 00root root 0000000 0000000 Copyright (c) 2012-2016, Nick Galbreath
Copyright (c) 2017-2024, libinjection Contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
https://github.com/libinjection/libinjection
http://opensource.org/licenses/BSD-3-Clause
modsecurity-apache-2.9.13/apache2/others/libinjection/MIGRATION.md 0000664 0000000 0000000 00000023014 15174655764 0024553 0 ustar 00root root 0000000 0000000 # Migration Guide: libinjection v3.x to v4.0
This guide helps you migrate from libinjection v3.x to v4.0, which introduces a breaking API change in error handling.
## Overview of Changes
Version 4.0 introduces a new error handling model that **prevents process termination** on parser errors. Instead of calling `abort()` when encountering invalid parser states, the library now returns an error code that your application can handle gracefully.
## What Changed
### Return Type Change
All detection functions now return `injection_result_t` enum instead of `int`:
```c
typedef enum injection_result_t {
LIBINJECTION_RESULT_FALSE = 0, // No injection detected (benign input)
LIBINJECTION_RESULT_TRUE = 1, // Injection detected
LIBINJECTION_RESULT_ERROR = -1 // Parser error (invalid state)
} injection_result_t;
```
### Affected Functions
- `libinjection_xss()`
- `libinjection_sqli()`
- `libinjection_is_xss()`
- `libinjection_h5_next()`
### New Header File
Include the new error header:
```c
#include "libinjection_error.h" // New in v4.0
```
## Migration Strategies
### Strategy 1: Full Migration (Recommended)
Update your code to explicitly check all three return values:
**Before (v3.x):**
```c
#include "libinjection.h"
#include "libinjection_sqli.h"
int check_input(const char *input) {
struct libinjection_sqli_state state;
char fingerprint[8];
int result;
libinjection_sqli_init(&state, input, strlen(input), FLAG_NONE);
result = libinjection_is_sqli(&state);
if (result) {
log("SQLi detected: %s", state.fingerprint);
return 1; // Block request
}
return 0; // Allow request
}
```
**After (v4.0):**
```c
#include "libinjection.h"
#include "libinjection_error.h"
#include "libinjection_sqli.h"
int check_input(const char *input) {
struct libinjection_sqli_state state;
char fingerprint[8];
injection_result_t result;
libinjection_sqli_init(&state, input, strlen(input), FLAG_NONE);
result = libinjection_is_sqli(&state);
if (result == LIBINJECTION_RESULT_ERROR) {
log_error("Parser error - treating as suspicious");
return 1; // Block on error (fail-safe)
} else if (result == LIBINJECTION_RESULT_TRUE) {
log("SQLi detected: %s", state.fingerprint);
return 1; // Block request
}
return 0; // Allow request
}
```
### Strategy 2: Minimal Migration (Quick Fix)
For quick migration with minimal code changes, treat errors as detections:
```c
injection_result_t result;
result = libinjection_sqli(input, len, fingerprint);
// Treat error as detection (fail-safe approach)
if (result == LIBINJECTION_RESULT_TRUE || result == LIBINJECTION_RESULT_ERROR) {
// Block request
return 1;
}
return 0; // Allow only if LIBINJECTION_RESULT_FALSE
```
### Strategy 3: Backward Compatible Check
If you need to maintain compatibility during transition:
```c
injection_result_t result;
result = libinjection_xss(input, len);
// Old style: simple truthy check still works for detection
// but won't distinguish error from injection
if (result) {
// Handles both LIBINJECTION_RESULT_TRUE and LIBINJECTION_RESULT_ERROR (since -1 is truthy)
return 1;
}
return 0;
```
**Warning:** This approach treats errors as benign input when checking `!result`, which is unsafe.
## Migration by Use Case
### Web Application Firewall (WAF)
Recommended approach: **Fail-safe** - block on both detection and error.
```c
injection_result_t sqli_result = libinjection_sqli(input, len, fp);
injection_result_t xss_result = libinjection_xss(input, len);
if (sqli_result == LIBINJECTION_RESULT_TRUE || sqli_result == LIBINJECTION_RESULT_ERROR ||
xss_result == LIBINJECTION_RESULT_TRUE || xss_result == LIBINJECTION_RESULT_ERROR) {
log_security_event(input, sqli_result, xss_result);
return HTTP_403_FORBIDDEN;
}
return HTTP_200_OK;
```
### Logging/Monitoring System
Recommended approach: **Distinguish** between detection and errors.
```c
injection_result_t result = libinjection_xss(input, len);
switch (result) {
case LIBINJECTION_RESULT_TRUE:
log_metric("xss.detected", 1);
alert_security_team(input);
break;
case LIBINJECTION_RESULT_ERROR:
log_metric("xss.parser_error", 1);
log_error("Parser error on input: %.*s", (int)len, input);
// Continue processing - may be benign malformed input
break;
case LIBINJECTION_RESULT_FALSE:
log_metric("xss.clean", 1);
break;
}
```
### Embedded System
Recommended approach: **Graceful degradation** with error recovery.
```c
injection_result_t result = libinjection_sqli(input, len, fp);
if (result == LIBINJECTION_RESULT_ERROR) {
// Log error but don't block - keep system running
error_count++;
if (error_count > ERROR_THRESHOLD) {
// Too many errors - may indicate attack or bug
enter_safe_mode();
}
return ALLOW; // Degrade gracefully
}
return (result == LIBINJECTION_RESULT_TRUE) ? BLOCK : ALLOW;
```
## Testing Your Migration
### 1. Compile-time Checks
After migration, your code should compile with the new type:
```c
injection_result_t result; // Not 'int'
result = libinjection_xss(input, len);
```
### 2. Runtime Testing
Test with these scenarios:
```c
// Normal detection - should return LIBINJECTION_RESULT_TRUE
libinjection_xss("", 28);
// Benign input - should return LIBINJECTION_RESULT_FALSE
libinjection_xss("hello world", 11);
// Edge cases that might trigger LIBINJECTION_RESULT_ERROR
libinjection_xss("", 0); // Empty string
// Very long inputs, deeply nested structures, etc.
```
### 3. Error Handling Test
Verify your error handling:
```c
injection_result_t result = libinjection_xss(test_input, len);
assert(result == LIBINJECTION_RESULT_FALSE ||
result == LIBINJECTION_RESULT_TRUE ||
result == LIBINJECTION_RESULT_ERROR); // Only valid values
// Ensure you handle all three cases
switch (result) {
case LIBINJECTION_RESULT_FALSE: /* ... */ break;
case LIBINJECTION_RESULT_TRUE: /* ... */ break;
case LIBINJECTION_RESULT_ERROR: /* ... */ break;
}
```
## Common Pitfalls
### ❌ Don't: Ignore errors
```c
// WRONG - error becomes benign
if (result == LIBINJECTION_RESULT_TRUE) {
block();
}
// LIBINJECTION_RESULT_ERROR falls through as allowed!
```
### ✅ Do: Explicitly handle errors
```c
// CORRECT
if (result == LIBINJECTION_RESULT_ERROR) {
handle_error();
} else if (result == LIBINJECTION_RESULT_TRUE) {
block();
}
```
### ❌ Don't: Use simple equality for "not detected"
```c
// WRONG - error will pass this check
if (result == 0) { // Only catches LIBINJECTION_RESULT_FALSE
allow();
}
```
### ✅ Do: Use enum constants
```c
// CORRECT
if (result == LIBINJECTION_RESULT_FALSE) {
allow();
}
```
## Language Bindings
### Python
The enum values are accessible as integers in the Python binding:
```python
import libinjection
result = libinjection.xss(test_input)
if result == -1: # LIBINJECTION_RESULT_ERROR
handle_error()
elif result == 1: # LIBINJECTION_RESULT_TRUE
block_request()
else: # result == 0, LIBINJECTION_RESULT_FALSE
allow_request()
```
### PHP
```php
len >= hs->pos); // CRASH if violated
```
This was problematic for:
- **Web servers**: Malicious input could crash the server
- **Embedded systems**: Required high availability, no crashes
- **Production services**: Needed graceful degradation
### The Solution in v4.0
Parser errors now return `LIBINJECTION_RESULT_ERROR` instead of terminating:
```c
// v4.0 behavior
if (hs->len < hs->pos) {
return LIBINJECTION_RESULT_ERROR; // Graceful error return
}
```
Your application can now:
- Log the error for debugging
- Continue running other requests
- Implement custom error policies
- Monitor error rates
## Need Help?
- **Issues**: https://github.com/libinjection/libinjection/issues
- **Pull Request**: https://github.com/libinjection/libinjection/pull/65
- **Documentation**: See README.md for updated examples
## Checklist
Use this checklist to verify your migration:
- [ ] Updated all `int` return types to `injection_result_t`
- [ ] Added `#include "libinjection_error.h"`
- [ ] Explicitly handle `LIBINJECTION_RESULT_ERROR` in all code paths
- [ ] Updated language binding code (if applicable)
- [ ] Added tests for error conditions
- [ ] Verified fail-safe behavior (block on error in security contexts)
- [ ] Updated logging/monitoring to track error rates
- [ ] Tested with edge cases (empty strings, very long inputs, etc.)
- [ ] Reviewed all `if (result)` checks for proper error handling
- [ ] Updated internal documentation and team guidelines
---
**Version**: 4.0.0
**Last Updated**: 2025
**Status**: Current
modsecurity-apache-2.9.13/apache2/others/libinjection/Makefile.am 0000664 0000000 0000000 00000001723 15174655764 0024737 0 ustar 00root root 0000000 0000000 ACLOCAL_AMFLAGS = -I build-aux/m4
MAINTAINERCLEANFILES = Makefile.in aclocal.m4 build-aux/compile \
build-aux/config.guess build-aux/config.sub build-aux/depcomp \
build-aux/install-sh build-aux/ltmain.sh build-aux/missing \
config.h.in config.h.in~ configure build-aux/m4/libtool.m4 \
build-aux/m4/ltoptions.m4 build-aux/m4/ltsugar.m4 \
build-aux/m4/ltversion.m4 build-aux/m4/lt~obsolete.m4
SUBDIRS = src
if BUILD_FUZZTARGETS
SUBDIRS += src/fuzz
endif
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libinjection.pc
# Standard stuff.
EXTRA_DIST = bootstrap COPYING README.md CHANGELOG.md
LIBTOOL_DEPS = @LIBTOOL_DEPS@
libtool: $(LIBTOOL_DEPS)
$(SHELL) ./config.status libtool
tests: all
@cd src && $(MAKE) $(AM_MAKEFLAGS) check
# Some helpers for compat
clang-asan: all
@cd src && $(MAKE) $(AM_MAKEFLAGS) clang-asan
cppcheck: $(CPPCHECK)
@cd src && $(MAKE) $(AM_MAKEFLAGS) cppcheck
ci: all
@cd src && $(MAKE) $(AM_MAKEFLAGS) tests analyze cppcheck
modsecurity-apache-2.9.13/apache2/others/libinjection/README.md 0000664 0000000 0000000 00000017362 15174655764 0024170 0 ustar 00root root 0000000 0000000

[](https://raw.githubusercontent.com/libinjection/libinjection/main/COPYING)
SQL / SQLI tokenizer parser analyzer. For
* C and C++
* [PHP](https://libinjection.client9.com/doc-sqli-php)
* [Python](https://libinjection.client9.com/doc-sqli-python)
* [Lua](/lua)
* [Java](https://github.com/jeonglee/Libinjection) (external port)
* [LuaJIT/FFI](https://github.com/p0pr0ck5/lua-ffi-libinjection) (external port)
See [https://www.client9.com/](https://www.client9.com/)
for details and presentations.
Simple example:
```c
#include
#include
#include
#include "libinjection.h"
#include "libinjection_sqli.h"
int main(int argc, const char* argv[])
{
struct libinjection_sqli_state state;
injection_result_t result;
const char* input = argv[1];
size_t slen = strlen(input);
/* in real-world, you would url-decode the input, etc */
libinjection_sqli_init(&state, input, slen, FLAG_NONE);
result = libinjection_is_sqli(&state);
if (result == LIBINJECTION_RESULT_ERROR) {
fprintf(stderr, "error: parser encountered an error\n");
return 2;
} else if (result == LIBINJECTION_RESULT_TRUE) {
fprintf(stderr, "sqli detected with fingerprint of '%s'\n", state.fingerprint);
return 1;
}
/* LIBINJECTION_RESULT_FALSE - no SQLi detected */
return 0;
}
```
```
$ gcc -Wall -Wextra examples.c libinjection_sqli.c
$ ./a.out "-1' and 1=1 union/* foo */select load_file('/etc/passwd')--"
sqli detected with fingerprint of 's&1UE'
```
More advanced samples:
* [sqli_cli.c](/src/sqli_cli.c)
* [reader.c](/src/reader.c)
* [fptool](/src/fptool.c)
VERSION INFORMATION
===================
See [CHANGELOG](CHANGELOG.md) for details.
Versions are listed as "major.minor.point"
Major are significant changes to the API and/or fingerprint format.
Applications will need recompiling and/or refactoring.
Minor are C code changes. These may include
* logical change to detect or suppress
* optimization changes
* code refactoring
Point releases are purely data changes. These may be safely applied.
ERROR HANDLING
==============
As of version 4.0.0, libinjection uses an `injection_result_t` enum for return values instead of `int`:
```c
typedef enum injection_result_t {
LIBINJECTION_RESULT_FALSE = 0, // No injection detected (benign input)
LIBINJECTION_RESULT_TRUE = 1, // Injection detected
LIBINJECTION_RESULT_ERROR = -1 // Parser error (invalid state)
} injection_result_t;
```
**Important:** Prior to v4.0.0, libinjection would call `abort()` and terminate the process when encountering parser errors. Now it returns `LIBINJECTION_RESULT_ERROR` instead, allowing your application to handle errors gracefully.
**Backward Compatibility:** The enum values `LIBINJECTION_RESULT_FALSE` (0) and `LIBINJECTION_RESULT_TRUE` (1) maintain backward compatibility with code that checks for true/false values. However, applications should be updated to handle `LIBINJECTION_RESULT_ERROR` (-1) to prevent treating parser errors as benign input.
**Migration:** See [MIGRATION.md](MIGRATION.md) for guidance on updating existing code.
QUALITY AND DIAGNOSITICS
========================
The continuous integration results at GitHub tests the following:
- [x] build and unit-tests under GCC
- [x] build and unit-tests under Clang
- [x] static analysis using [clang static analyzer](http://clang-analyzer.llvm.org)
- [x] static analysis using [cppcheck](https://github.com/danmar/cppcheck)
- [x] checks for memory errors using [valgrind](http://valgrind.org/)
LICENSE
=============
Copyright (c) 2012-2016, Nick Galbreath
Copyright (c) 2017-2024, libinjection Contributors
Licensed under the standard [BSD 3-Clause](http://opensource.org/licenses/BSD-3-Clause) open source
license. See [COPYING](/COPYING) for details.
## BUILD TARGETS
Some of the previous help runners have been merged into the Makefile. E.g.:
* run-clang-asan.sh -> `make clan-asan`
* make-ci.sh -> `make ci`
### Building and Installing Libraries
The autotools build system uses **libtool**, which places built libraries in the `src/.libs/` directory (not directly in `src/` like older versions).
**Basic build:**
```bash
./autogen.sh
./configure
make
# Built libraries are located at:
# - src/.libs/libinjection.a (static library)
# - src/.libs/libinjection.so (Linux shared) or src/.libs/libinjection.dylib (macOS shared)
```
**Building static libraries:**
```bash
./configure --enable-static
make
find . -name "*.a"
# Output: ./src/.libs/libinjection.a
```
**Installing to a specific location:**
```bash
./configure --prefix=/path/to/install
make
make install
# This installs:
# - Headers: /path/to/install/include/libinjection*.h
# - Libraries: /path/to/install/lib/libinjection.{a,so,dylib}
# - pkg-config: /path/to/install/lib/pkgconfig/libinjection.pc
```
**Linking against libinjection:**
Option 1 - Link against installed library (via pkg-config):
```bash
./configure --prefix=/usr/local
make install
gcc myapp.c $(pkg-config --cflags --libs libinjection)
```
Option 2 - Link against build tree without installing:
```bash
gcc myapp.c -I/path/to/libinjection/src -L/path/to/libinjection/src/.libs -linjection
```
Option 3 - Static linking from build tree:
```bash
gcc myapp.c -I/path/to/libinjection/src /path/to/libinjection/src/.libs/libinjection.a
```
### Migrating from Older Versions (client9/libinjection)
If you're upgrading from the old Makefile-based build system where `libinjection.a` was in `src/`, note that libraries are now in `src/.libs/`.
**Update your build scripts:**
For Makefiles:
```makefile
LIBINJECTION_DIR = ../libinjection
CFLAGS += -I$(LIBINJECTION_DIR)/src
LDFLAGS += -L$(LIBINJECTION_DIR)/src/.libs -linjection
```
For CMake:
```cmake
target_include_directories(myapp PRIVATE ../libinjection/src)
target_link_directories(myapp PRIVATE ../libinjection/src/.libs)
target_link_libraries(myapp injection)
```
If your build system requires libraries in a specific location:
```bash
./configure && make
cp src/.libs/libinjection.a /desired/location/
```
For more information, see:
- GNU Libtool documentation: https://www.gnu.org/software/libtool/manual/html_node/Linking-libraries.html
- GitHub issue #54
### Static Analysis
If you run `make cppcheck` you will see this warning printed:
```
nofile:0 information missingIncludeSystem Cppcheck cannot find all the include files (use --check-config for details)
```
You can safely ignore it as it is just saying that standard include files are being ignored (which is the recommended option):
```
example1.c:1:0: information: Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results. [missingIncludeSystem]
```
EMBEDDING
=============
The [src](/src)
directory contains everything, but you only need to copy the following
into your source tree:
* [src/libinjection.h](/src/libinjection.h)
* [src/libinjection_error.h](/src/libinjection_error.h)
* [src/libinjection_sqli.h](/src/libinjection_sqli.h)
* [src/libinjection_sqli.c](/src/libinjection_sqli.c)
* [src/libinjection_sqli_data.h](/src/libinjection_sqli_data.h)
* [COPYING](/COPYING)
For XSS detection, also copy:
* [src/libinjection_xss.h](/src/libinjection_xss.h)
* [src/libinjection_xss.c](/src/libinjection_xss.c)
* [src/libinjection_html5.h](/src/libinjection_html5.h)
* [src/libinjection_html5.c](/src/libinjection_html5.c)
The source includes a default version string (`"4.0.0"`).
You can override this at build time, for example by defining `LIBINJECTION_VERSION`:
```
CFLAGS="-DLIBINJECTION_VERSION=\"4.0.0-custom\""
```
modsecurity-apache-2.9.13/apache2/others/libinjection/RELEASE-HOWTO.md 0000664 0000000 0000000 00000003537 15174655764 0025210 0 ustar 00root root 0000000 0000000 # libinjection release howto
Comments and improvements welcome.
## Prerequisites
- GNU autotools (`autoconf`, `automake`, `libtool`)
- `clang-format` (for code formatting checks)
- `cppcheck` (for static analysis)
- `valgrind` (optional, for memory checks)
## Update the version fallback
In `src/libinjection_sqli.c`, update the default version used by embedders:
```c
#ifndef LIBINJECTION_VERSION
#define LIBINJECTION_VERSION "X.Y.Z"
#endif
```
The autotools build overrides this via `-DLIBINJECTION_VERSION=...` using
the version derived from git tags by `build-aux/git-version-gen`. This
fallback only affects users who copy the source files directly.
## Update libtool library version
In `configure.ac`, update the libtool version info (`LT_CURRENT`, `LT_REVISION`,
`LT_AGE`) following the [libtool versioning rules](https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html):
- **Interfaces removed or changed (breaking):** increment `LT_CURRENT`, reset `LT_REVISION` and `LT_AGE` to 0
- **Interfaces added (backward-compatible):** increment `LT_CURRENT` and `LT_AGE`, reset `LT_REVISION` to 0
- **Implementation-only changes:** increment `LT_REVISION`
## Update the CHANGELOG.md file
Replace `TBD` with the release date. Make sure it looks good in markdown.
## Run the full test suite
```sh
./autogen.sh
./configure
make ci
```
This runs unit tests, static analysis, and format checks.
## Commit and tag
```sh
git add -A
git commit -m 'Release vX.Y.Z'
git tag -a vX.Y.Z -m 'vX.Y.Z'
git push origin main --tags
```
The version string is derived from the git tag by `build-aux/git-version-gen`.
## Create a GitHub release
Use the GitHub UI or CLI to create a release from the tag:
```sh
gh release create vX.Y.Z --title "vX.Y.Z" --notes "See CHANGELOG.md for details."
```
Consider attaching a source tarball (`make dist` generates one).
modsecurity-apache-2.9.13/apache2/others/libinjection/autogen.sh 0000775 0000000 0000000 00000000075 15174655764 0024703 0 ustar 00root root 0000000 0000000 #!/bin/sh
set -e
autoreconf -i --force
rm -rf autom4te.cache
modsecurity-apache-2.9.13/apache2/others/libinjection/build-aux/ 0000775 0000000 0000000 00000000000 15174655764 0024572 5 ustar 00root root 0000000 0000000 modsecurity-apache-2.9.13/apache2/others/libinjection/build-aux/ar-lib 0000775 0000000 0000000 00000013363 15174655764 0025674 0 ustar 00root root 0000000 0000000 #! /bin/sh
# Wrapper for Microsoft lib.exe
me=ar-lib
scriptversion=2019-07-04.01; # UTC
# Copyright (C) 2010-2021 Free Software Foundation, Inc.
# Written by Peter Rosin .
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# 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, see .
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to or send patches to
# .
# func_error message
func_error ()
{
echo "$me: $1" 1>&2
exit 1
}
file_conv=
# func_file_conv build_file
# Convert a $build file to $host form and store it in $file
# Currently only supports Windows hosts.
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 in
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_at_file at_file operation archive
# Iterate over all members in AT_FILE performing OPERATION on ARCHIVE
# for each of them.
# When interpreting the content of the @FILE, do NOT use func_file_conv,
# since the user would need to supply preconverted file names to
# binutils ar, at least for MinGW.
func_at_file ()
{
operation=$2
archive=$3
at_file_contents=`cat "$1"`
eval set x "$at_file_contents"
shift
for member
do
$AR -NOLOGO $operation:"$member" "$archive" || exit $?
done
}
case $1 in
'')
func_error "no command. Try '$0 --help' for more information."
;;
-h | --h*)
cat <