diff --git a/CHANGES b/CHANGES index 210d726..93af46b 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,17 @@ +####################### V 2.0.0-b5: + +security: + fixed a possible heap buffer overflow in the readline address. This bug + could be exploited when all of the following conditions were met: + 1) one of the addresses is READLINE without the noprompt and without the + prompt options. + 2) the other (almost arbitrary address) reads malicious data (which is + then transferred by socat to READLINE). + Workaround: when using the READLINE address apply option prompt or + noprompt. + Full credits to Johan Thillemann for finding and reporting this issue. + ####################### V 2.0.0-b4: security: diff --git a/VERSION b/VERSION index 933d0bd..713ed4f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -"2.0.0-b4" +"2.0.0-b5" diff --git a/test.sh b/test.sh index 880de5a..7f03573 100755 --- a/test.sh +++ b/test.sh @@ -1,6 +1,6 @@ #! /bin/bash # source: test.sh -# Copyright Gerhard Rieger 2001-2009 +# Copyright Gerhard Rieger 2001-2012 # Published under the GNU General Public License V.2, see file COPYING # perform lots of tests on socat @@ -10528,7 +10528,7 @@ te="$td/test$N.stderr" tdiff="$td/test$N.diff" da="test$N $(date) $RANDOM" # prepare long data - perl might not be installed -rm -f "$td/terst$N.dat" +rm -f "$td/test$N.dat" i=0; while [ $i -lt 64 ]; do echo -n "AAAAAAAAAAAAAAAA" >>"$td/test$N.dat"; i=$((i+1)); done CMD0="$SOCAT $opts TCP-CONNECT:$(cat "$td/test$N.dat"):$PORT STDIO" printf "test $F_n $TEST... " $N @@ -10584,6 +10584,48 @@ PORT=$((PORT+1)) N=$((N+1)) +# socat up to 1.7.2.0 and 2.0.0-b4 had a bug in xioscan_readline() that could +# be exploited +# to overflow a heap based buffer (socat security advisory 3) +# problem reported by Johan Thillemann +NAME=READLINE_OVFL +case "$TESTS" in +*%functions%*|*%bugs%*|*%security%*|*%$NAME%*) +TEST="$NAME: test for buffer overflow in readline prompt handling" +# address 1 is the readline where write data was handled erroneous +# address 2 provides data to trigger the buffer overflow +# when no SIGSEGV or so occurs the test succeeded (bug fixed) +if ! eval $NUMCOND; then :; else +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +ti="$td/test$N.data" +CMD0="$SOCAT $opts READLINE $ti" +printf "test $F_n $TEST... " $N +# prepare long data - perl might not be installed +#perl -e 'print "\r","Z"x513' >"$ti" +echo $E -n "\rA" >"$ti" +i=0; while [ $i -lt 32 ]; do echo -n "AAAAAAAAAAAAAAAA" >>"$ti"; let i=i+1; done +$SOCAT - system:"$CMD0; echo rc=\$? >&2",pty >/dev/null 2>"${te}0" +rc=$? +rc0="$(grep ^rc= "${te}0" |sed 's/.*=//')" +if [ $rc -ne 0 ]; then + $PRINTF "${YELLOW}framework failed${NORMAL}\n" +elif [ $rc0 -eq 0 ]; then + $PRINTF "$OK\n" + numOK=$((numOK+1)) +else + $PRINTF "$FAILED\n" + echo "$CMD0" + grep -v ^rc= "${te}0" + numFAIL=$((numFAIL+1)) +fi +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-readline.c b/xio-readline.c index acd6eab..059f451 100644 --- a/xio-readline.c +++ b/xio-readline.c @@ -1,5 +1,5 @@ /* source: xio-readline.c */ -/* Copyright Gerhard Rieger 2002-2009 */ +/* Copyright Gerhard Rieger 2002-2012 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* this file contains the source for opening the readline address */ @@ -224,25 +224,26 @@ void xioscan_readline(struct single *pipe, const void *buff, size_t bytes) { if (pipe->dtype == XIODATA_READLINE && pipe->para.readline.dynprompt) { /* we save the last part of the output as possible prompt */ const void *ptr = buff; - const void *pcr = memrchr(buff, '\r', bytes); - const void *plf = memrchr(buff, '\n', bytes); + const void *pcr; + const void *plf; size_t len; + if (bytes > pipe->para.readline.dynbytes) { ptr = (const char *)buff + bytes - pipe->para.readline.dynbytes; + len = pipe->para.readline.dynbytes; + } else { + len = bytes; } - if (pcr) { + pcr = memrchr(ptr, '\r', len); + plf = memrchr(ptr, '\n', len); + if (pcr != NULL || plf != NULL) { + const void *peol = Max(pcr, plf); /* forget old prompt */ pipe->para.readline.dynend = pipe->para.readline.dynprompt; + len -= (peol+1 - ptr); /* new prompt starts here */ - ptr = (const char *)pcr+1; + ptr = (const char *)peol+1; } - if (plf && plf >= ptr) { - /* forget old prompt */ - pipe->para.readline.dynend = pipe->para.readline.dynprompt; - /* new prompt starts here */ - ptr = (const char *)plf+1; - } - len = (const char *)buff-(const char *)ptr+bytes; if (pipe->para.readline.dynend - pipe->para.readline.dynprompt + len > pipe->para.readline.dynbytes) { memmove(pipe->para.readline.dynprompt, @@ -253,7 +254,6 @@ void xioscan_readline(struct single *pipe, const void *buff, size_t bytes) { pipe->para.readline.dynprompt + pipe->para.readline.dynbytes - len; } memcpy(pipe->para.readline.dynend, ptr, len); - /*pipe->para.readline.dynend = pipe->para.readline.dynprompt + len;*/ pipe->para.readline.dynend = pipe->para.readline.dynend + len; } return;