version 2.0.0-b6 - fixed FD leak in accept() loop

This commit is contained in:
Gerhard Rieger 2013-05-26 20:24:04 +02:00
parent 2d24bc3157
commit c22863e4fb
4 changed files with 74 additions and 3 deletions

10
CHANGES
View file

@ -1,4 +1,14 @@
####################### V 2.0.0-b6:
security:
after refusing a client connection due to bad source address or source
port socat shutdown() the socket but did not close() it, resulting in
a file descriptor leak in the listening process, visible with lsof and
possibly resulting in EMFILE Too many open files. This issue could be
misused for a denial of service attack.
Full credits to Catalin Mitrofan for finding and reporting this issue.
####################### V 2.0.0-b5: ####################### V 2.0.0-b5:
security: security:

View file

@ -1 +1 @@
"2.0.0-b5" "2.0.0-b6"

62
test.sh
View file

@ -1,6 +1,6 @@
#! /bin/bash #! /bin/bash
# source: test.sh # source: test.sh
# Copyright Gerhard Rieger 2001-2012 # Copyright Gerhard Rieger
# Published under the GNU General Public License V.2, see file COPYING # Published under the GNU General Public License V.2, see file COPYING
# perform lots of tests on socat # perform lots of tests on socat
@ -10626,6 +10626,66 @@ PORT=$((PORT+1))
N=$((N+1)) N=$((N+1))
# socat up to 1.7.2.1 did only shutdown() but not close() an accept() socket
# that was rejected due to range, tcpwrap, lowport, or sourceport option.
# This file descriptor leak could be used for a denial of service attack.
NAME=FDLEAK
case "$TESTS" in
*%functions%*|*%bugs%*|*%security%*|*%socket%*|*%$NAME%*)
TEST="$NAME: file descriptor leak with range option"
# have a TCP-LISTEN with range option; connect with wrong source address until
# "open files" limit would exceed. When server continues operation the bug is
# not present.
if ! eval $NUMCOND; then :; else
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
RLIMIT_NOFILE="$(ulimit -n)"
if ! [[ "$RLIMIT_NOFILE" =~ ^[0-9][0-9]*$ ]]; then
$PRINTF "${YELLOW}cannot determine ulimit -n"
else
CMD0="$SOCAT $opts TCP-LISTEN:$PORT,reuseaddr,range=$LOCALHOST:255.255.255.255 PIPE"
CMD1="$SOCAT $opts -t 0 /dev/null TCP:$SECONDADDR:$PORT"
CMD2="$SOCAT $opts - TCP:$LOCALHOST:$PORT"
printf "test $F_n $TEST... " $N
$CMD0 >/dev/null 2>"${te}0" &
pid0=$!
waittcp4port $PORT 1
while [ $RLIMIT_NOFILE -gt 0 ]; do
$CMD1 >/dev/null 2>>"${te}1"
let RLIMIT_NOFILE=RLIMIT_NOFILE-1
done
echo "$da" |$CMD2 >"${tf}2" 2>"${te}2"
rc2=$?
kill $pid0 2>/dev/null; wait
echo -e "$da" |diff "${tf}2" - >$tdiff
if [ $rc2 -ne 0 ]; then
$PRINTF "$FAILED\n"
echo "$CMD2 &"
cat "${te}2"
numFAIL=$((numFAIL+1))
elif [ -f "$tdiff" -a ! -s "$tdiff" ]; then
$PRINTF "$OK\n"
numOK=$((numOK+1))
else
$PRINTF "$FAILED\n"
echo "$CMD0 &"
echo "$CMD1"
echo "$CMD2"
cat "${te}0"
cat "${te}1"
cat "${te}2"
numFAIL=$((numFAIL+1))
fi
fi # ulimit -n
fi # NUMCOND
;;
esac
PORT=$((PORT+1))
N=$((N+1))
echo "summary: $((N-1)) tests; $numOK ok, $numFAIL failed, $numCANT could not be performed" echo "summary: $((N-1)) tests; $numOK ok, $numFAIL failed, $numCANT could not be performed"

View file

@ -1,5 +1,5 @@
/* source: xio-listen.c */ /* source: xio-listen.c */
/* Copyright Gerhard Rieger 2001-2009 */ /* Copyright Gerhard Rieger */
/* Published under the GNU General Public License V.2, see file COPYING */ /* Published under the GNU General Public License V.2, see file COPYING */
/* this file contains the source for listen socket options */ /* this file contains the source for listen socket options */
@ -270,6 +270,7 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl
if (Shutdown(ps, 2) < 0) { if (Shutdown(ps, 2) < 0) {
Info2("shutdown(%d, 2): %s", ps, strerror(errno)); Info2("shutdown(%d, 2): %s", ps, strerror(errno));
} }
Close(ps);
continue; continue;
} }