Files
ocserv/tests/test-http-limits
Nikos Mavrogiannopoulos 4303a12f60 worker: harden HTTP request header size limits
Bound memory growth in the worker for unauthenticated connections by
enforcing HTTP headers limit in addition to HTTP body limit.

Resolves: #712

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
2026-05-06 13:54:21 +02:00

115 lines
3.6 KiB
Bash
Executable File

#!/bin/sh
#
# Copyright (C) 2026 Nikos Mavrogiannopoulos
#
# This file is part of ocserv.
#
# ocserv 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 of the License, or (at
# your option) any later version.
#
# ocserv 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 GnuTLS; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
SERV="${SERV:-../src/ocserv}"
srcdir=${srcdir:-.}
NO_NEED_ROOT=1
. `dirname $0`/common.sh
eval "${GETPORT}"
echo "Testing HTTP body and header size limits..."
update_config test-user-cert.config
launch_simple_sr_server -d 1 -f -c ${CONFIG}
PID=$!
wait_server $PID
BODY_LIMIT=$((256 * 1024))
HDR_LIMIT=$((16 * 1024))
HDRFILE=test-http-limits.$$.tmp
CURL_OUT=test-http-limits.$$.curl
# Run a POST via socket_wrapper; print HTTP status code to stdout.
# Returns curl's exit code (28 = timeout, 52 = empty reply, 0 = got response).
curl_post() {
LD_PRELOAD=libsocket_wrapper.so \
curl --silent --insecure --max-time 10 --user-agent "AnyConnect" \
--write-out "%{http_code}" \
"$@" -o "$CURL_OUT"
}
echo -n "Testing body within limit (${BODY_LIMIT} bytes)... "
http_code=$(head -c ${BODY_LIMIT} /dev/zero | \
curl_post -X POST https://$ADDRESS:$PORT/auth --data-binary @-)
rc=$?
if [ $rc -ne 0 ] || [ "$http_code" = "000" ]; then
echo "FAILED (curl exit=$rc, HTTP=$http_code)"
cat "$CURL_OUT"
fail $PID "Body within limit was rejected"
fi
echo "ok (HTTP $http_code)"
echo -n "Testing body over limit ($((BODY_LIMIT + 1)) bytes)... "
http_code=$(head -c $((BODY_LIMIT + 1)) /dev/zero | \
curl_post -X POST https://$ADDRESS:$PORT/auth --data-binary @-)
rc=$?
if [ $rc -eq 0 ] && [ "$http_code" != "413" ] && [ "$http_code" != "431" ]; then
echo "FAILED (curl exit=$rc, HTTP=$http_code)"
cat "$CURL_OUT"
fail $PID "Body over limit was not rejected (HTTP $http_code)"
fi
echo "ok (HTTP $http_code)"
# Generate N headers of 1 KB each into $HDRFILE for curl's -H @file.
# With a 16 KB limit: 3 headers (3 KB) is within; 20 headers (20 KB) is over.
HDR_CHUNK=$((1 * 1024))
gen_headers() {
n=$1; rm -f "$HDRFILE"; i=0
while [ $i -lt $n ]; do
{ printf 'X-Test-%d: ' $i
head -c ${HDR_CHUNK} /dev/zero | tr '\0' 'A'
printf '\n'; } >> "$HDRFILE"
i=$((i+1))
done
}
echo -n "Testing headers within limit ($((3 * HDR_CHUNK)) bytes)... "
gen_headers 3
# Explicit Content-Length: 0 is required: older curl (e.g. 7.76.1 on CentOS 9)
# omits it for empty bodies, leaving the server unable to determine
# message_complete and causing a timeout.
http_code=$(curl_post -X POST -H "Content-Length: 0" -H @"$HDRFILE" \
https://$ADDRESS:$PORT/auth)
rc=$?
if [ $rc -ne 0 ] || [ "$http_code" = "000" ]; then
echo "FAILED (curl exit=$rc, HTTP=$http_code)"
cat "$CURL_OUT"
fail $PID "Headers within limit were rejected"
fi
echo "ok (HTTP $http_code)"
echo -n "Testing headers over limit ($((20 * HDR_CHUNK)) bytes)... "
gen_headers 20
http_code=$(curl_post -X POST -H "Content-Length: 0" -H @"$HDRFILE" \
https://$ADDRESS:$PORT/auth)
rc=$?
if [ $rc -eq 0 ] && [ "$http_code" != "413" ] && [ "$http_code" != "431" ]; then
echo "FAILED (curl exit=$rc, HTTP=$http_code)"
cat "$CURL_OUT"
fail $PID "Headers over limit were not rejected (HTTP $http_code)"
fi
echo "ok (HTTP $http_code)"
rm -f "$HDRFILE" "$CURL_OUT"
cleanup
exit 0