From 226c555edb82f6901d7d7448d93e6d09b1132c73 Mon Sep 17 00:00:00 2001
From: Gerhard Rieger <gerhard@dest-unreach.org>
Date: Tue, 26 Jan 2016 19:08:18 +0100
Subject: [PATCH] socat security advisory 8: stack overflow in nestlex()

---
 CHANGES   | 15 ++++++++++++++-
 nestlex.c | 49 +++++++++++++++++++++++++++++++++++++++++--------
 nestlex.h |  2 +-
 test.sh   | 36 ++++++++++++++++++++++++++++++++++++
 4 files changed, 92 insertions(+), 10 deletions(-)

Index: socat-1.7.0.0/nestlex.c
===================================================================
--- socat-1.7.0.0.orig/nestlex.c	2016-02-04 12:02:02.431809762 +0100
+++ socat-1.7.0.0/nestlex.c	2016-02-04 12:02:04.283837971 +0100
@@ -9,6 +9,17 @@
 
 #include "sysincludes.h"
 
+static int _nestlex(const char **addr,
+		    char **token,
+		    ptrdiff_t *len,
+		    const char *ends[],
+		    const char *hquotes[],
+		    const char *squotes[],
+		    const char *nests[],
+		    bool dropquotes,
+		    bool c_esc,
+		    bool html_esc
+		    );
 
 /* sub: scan a string and copy its value to output string
    end scanning when an unescaped, unnested string from ends array is found
@@ -33,6 +44,22 @@ int nestlex(const char **addr,	/* input
 	    bool c_esc,		/* solve C char escapes: \n \t \0 etc */
 	    bool html_esc	/* solve HTML char escapes: %0d %08 etc */
 	    ) {
+   return
+      _nestlex(addr, token, (ptrdiff_t *)len, ends, hquotes, squotes, nests,
+	       dropquotes, c_esc, html_esc);
+}
+
+static int _nestlex(const char **addr,
+		    char **token,
+		    ptrdiff_t *len,
+		    const char *ends[],
+		    const char *hquotes[],
+		    const char *squotes[],
+		    const char *nests[],
+		    bool dropquotes,
+		    bool c_esc,
+		    bool html_esc
+		    ) {
    const char *in = *addr;	/* pointer into input string */
    const char **endx;	/* loops over end patterns */
    const char **quotx;	/* loops over quote patterns */
@@ -77,16 +104,18 @@ int nestlex(const char **addr,	/* input
 		  if (--*len <= 0) { *addr = in; *token = out; return -1; }
 	       }
 	    }
-	    /* we call nestlex recursively */
+	    /* we call _nestlex recursively */
 	    endnest[0] = *quotx;
 	    endnest[1] = NULL;
 	    result =
-	       nestlex(&in, &out, len, endnest, NULL/*hquotes*/,
+	       _nestlex(&in, &out, len, endnest, NULL/*hquotes*/,
 		       NULL/*squotes*/, NULL/*nests*/,
 		       false, c_esc, html_esc);
 	    if (result == 0 && dropquotes) {
 	       /* we strip this quote */
 	       in += strlen(*quotx);
+	    } else if (result < 0) {
+	       *addr = in; *token = out; return result;
 	    } else {
 	       /* we copy the trailing quote */
 	       for (i = strlen(*quotx); i > 0; --i) {
@@ -110,7 +139,7 @@ int nestlex(const char **addr,	/* input
 	 if (!strncmp(in, *quotx, strlen(*quotx))) {
 	    /* this quote pattern matches */
 	    /* we strip this quote */
-	    /* we call nestlex recursively */
+	    /* we call _nestlex recursively */
 	    const char *endnest[2];
 	    if (dropquotes) {
 	       /* we strip this quote */
@@ -124,13 +153,15 @@ int nestlex(const char **addr,	/* input
 	    endnest[0] = *quotx;
 	    endnest[1] = NULL;
 	    result =
-	       nestlex(&in, &out, len, endnest, hquotes,
+	       _nestlex(&in, &out, len, endnest, hquotes,
 		       squotes, nests,
 		       false, c_esc, html_esc);
 
 	    if (result == 0 && dropquotes) {
 	       /* we strip the trailing quote */
 	       in += strlen(*quotx);
+	    } else if (result < 0) {
+	       *addr = in; *token = out; return result;
 	    } else {
 	       /* we copy the trailing quote */
 	       for (i = strlen(*quotx); i > 0; --i) {
@@ -162,7 +193,7 @@ int nestlex(const char **addr,	/* input
 	    }
 
 	    result =
-	       nestlex(&in, &out, len, endnest, hquotes, squotes, nests,
+	       _nestlex(&in, &out, len, endnest, hquotes, squotes, nests,
 		       false, c_esc, html_esc);
 	    if (result == 0) {
 	       /* copy endnest */
@@ -175,6 +206,8 @@ int nestlex(const char **addr,	/* input
 		  }
 		  --i;
 	       }
+	    } else if (result < 0) {
+	       *addr = in; *token = out; return result;
 	    }
 	    break;
 	 }
@@ -211,7 +244,7 @@ int nestlex(const char **addr,	/* input
 	 }
 	 *out++ = c;
 	 --*len;
-	 if (*len == 0) {
+	 if (*len <= 0) {
 	    *addr = in;
 	    *token = out;
 	    return -1;	/* output overflow */
@@ -222,7 +255,7 @@ int nestlex(const char **addr,	/* input
       /* just a simple char */
       *out++ = c;
       --*len;
-      if (*len == 0) {
+      if (*len <= 0) {
 	 *addr = in;
 	 *token = out;
 	 return -1;	/* output overflow */
Index: socat-1.7.0.0/nestlex.h
===================================================================
--- socat-1.7.0.0.orig/nestlex.h	2016-02-04 12:02:02.431809762 +0100
+++ socat-1.7.0.0/nestlex.h	2016-02-04 12:02:04.283837971 +0100
@@ -1,5 +1,5 @@
 /* source: nestlex.h */
-/* Copyright Gerhard Rieger 2006 */
+/* Copyright Gerhard Rieger */
 /* Published under the GNU General Public License V.2, see file COPYING */
 
 #ifndef __nestlex_h_included
Index: socat-1.7.0.0/test.sh
===================================================================
--- socat-1.7.0.0.orig/test.sh	2016-02-04 12:02:02.432809778 +0100
+++ socat-1.7.0.0/test.sh	2016-02-04 12:02:04.284837986 +0100
@@ -10161,4 +10161,40 @@ PORT=$((PORT+1))
 N=$((N+1))
 fi	# false
 
+# socat up to 1.7.3.0 had a stack overflow vulnerability that occurred when
+# command line arguments (whole addresses, host names, file names) were longer
+# than 512 bytes and specially crafted.
+NAME=NESTEDOVFL
+case "$TESTS" in
+*%$N%*|*%functions%*|*%bugs%*|*%security%*|*%exec%*|*%$NAME%*)
+TEST="$NAME: stack overflow on overly long nested arg"
+# provide a long host name to TCP-CONNECT and check socats exit code
+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"
+# prepare long data - perl might not be installed
+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="$TRACE $SOCAT $opts EXEC:[$(cat "$td/test$N.dat")] STDIO"
+printf "test $F_n $TEST... " $N
+$CMD0 </dev/null 1>&0 2>"${te}0"
+rc0=$?
+if [ $rc0 -lt 128 ] || [ $rc0 -eq 255 ]; then
+    $PRINTF "$OK\n"
+    numOK=$((numOK+1))
+else
+    $PRINTF "$FAILED\n"
+    echo "$CMD0"
+    cat "${te}0"
+    numFAIL=$((numFAIL+1))
+    listFAIL="$listFAIL $N"
+fi
+fi # NUMCOND
+ ;;
+esac
+PORT=$((PORT+1))
+N=$((N+1))
+
 
