diff --git a/CHANGES b/CHANGES index 93af46b..61520c3 100644 --- a/CHANGES +++ b/CHANGES @@ -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: security: diff --git a/VERSION b/VERSION index 713ed4f..d40637d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -"2.0.0-b5" +"2.0.0-b6" diff --git a/test.sh b/test.sh index 7f03573..3699562 100755 --- a/test.sh +++ b/test.sh @@ -1,6 +1,6 @@ #! /bin/bash # source: test.sh -# Copyright Gerhard Rieger 2001-2012 +# Copyright Gerhard Rieger # Published under the GNU General Public License V.2, see file COPYING # perform lots of tests on socat @@ -10626,6 +10626,66 @@ PORT=$((PORT+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" diff --git a/xio-listen.c b/xio-listen.c index 2358d78..684b5f6 100644 --- a/xio-listen.c +++ b/xio-listen.c @@ -1,5 +1,5 @@ /* source: xio-listen.c */ -/* Copyright Gerhard Rieger 2001-2009 */ +/* Copyright Gerhard Rieger */ /* Published under the GNU General Public License V.2, see file COPYING */ /* 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) { Info2("shutdown(%d, 2): %s", ps, strerror(errno)); } + Close(ps); continue; }