From 75b5b2e88c1ed0313d7898e603304d113f7f5567 Mon Sep 17 00:00:00 2001
From: an7s <an7s@git.zephyr-software.com>
Date: Wed, 18 Jul 2012 18:17:19 +0000
Subject: [PATCH] Stash away prototype SQL firewall

Former-commit-id: 4135814133e5b4bc0d6f5c7fcf9f263ca4cbe249
---
 .gitattributes    |   4 +
 sql/test/Makefile |  27 +++++
 sql/test/sqlfw.c  | 251 ++++++++++++++++++++++++++++++++++++++++++++++
 sql/test/test1.c  |  68 +++++++++++++
 sql/test/test2.c  |  69 +++++++++++++
 5 files changed, 419 insertions(+)
 create mode 100644 sql/test/Makefile
 create mode 100644 sql/test/sqlfw.c
 create mode 100644 sql/test/test1.c
 create mode 100644 sql/test/test2.c

diff --git a/.gitattributes b/.gitattributes
index 8ae740217..8d853a1d4 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -375,6 +375,10 @@ libtransform/tests/signed_mul.c -text
 libtransform/tests/simpletest.c -text
 libtransform/tests/unsigned_add.c -text
 libtransform/tests/unsigned_mul.c -text
+sql/test/Makefile -text
+sql/test/sqlfw.c -text
+sql/test/test1.c -text
+sql/test/test2.c -text
 tests/coreutils/Makefile -text
 tests/coreutils/bzip2-1.0.6/CHANGES -text
 tests/coreutils/bzip2-1.0.6/LICENSE -text
diff --git a/sql/test/Makefile b/sql/test/Makefile
new file mode 100644
index 000000000..d19cf7a25
--- /dev/null
+++ b/sql/test/Makefile
@@ -0,0 +1,27 @@
+
+all: libsqlfw test1.exe test2.exe
+
+test2.exe: test2.o sqlfw.c
+	gcc test2.o sqlite3.o -o test2.exe -L. -lsqlfw
+	
+test1.exe: test1.o sqlfw.c
+	gcc test1.o sqlite3.o -o test1.exe -L. -lsqlfw
+#	gcc test1.o sqlite3.o -o test1.exe -lpthread
+
+test1.o: test1.c
+	gcc -c test1.c
+
+test2.o: test2.c
+	gcc -c test2.c
+
+sqlite3.o: sqlite3.c sqlite3.h sqlfw.c
+	cp ../sqlite-autoconf-3071300/sqlite3.c .
+	cp ../sqlite-autoconf-3071300/sqlite3.h .
+	cat sqlfw.c >> sqlite3.c
+	gcc -fPIC -DSQLITE_OS_UNIX=1 -DSQLITE_THREADSAFE=0 -DSQLITE_DEBUG=1 -DSQLITE_OMIT_LOAD_EXTENSION -c sqlite3.c
+
+libsqlfw: sqlite3.o
+	gcc -shared -o libsqlfw.so sqlite3.o 
+
+clean:
+	rm *.o *.tmp
diff --git a/sql/test/sqlfw.c b/sql/test/sqlfw.c
new file mode 100644
index 000000000..0d6321838
--- /dev/null
+++ b/sql/test/sqlfw.c
@@ -0,0 +1,251 @@
+/*
+** Add the following functions 
+**
+*/
+
+sqlite3 *peasoup_db = NULL;
+
+int fw_numPatterns = 0;
+char **fw_sigs;
+
+void sqlfw_init(const char *dbname, const char *signatureFile)
+{
+  int numSigs = 0;
+  sqlite3_open(dbname, &peasoup_db);
+
+  fw_sigs = sqlite3MallocZero(sizeof(char*) * 10000); // allow for 10000 signature patterns
+
+  FILE *sigF = fopen(signatureFile, "r");
+  char buf[1024];
+  while (fgets(buf, 512, sigF) != NULL)
+  {
+	fw_sigs[numSigs] = (char *) sqlite3MallocZero(strlen(buf) + 128);
+	strncpy(fw_sigs[numSigs], buf, strlen(buf));
+	fw_sigs[numSigs][strlen(buf)-1] = '\0';
+
+	fprintf(stderr,"signature #%d: [%s]\n", numSigs, fw_sigs[numSigs]);
+    numSigs++;
+  }
+
+  fw_numPatterns = numSigs;
+  fclose(sigF);
+}
+
+void sqlfw_taint_range(char *taint, char taintValue, int from, int len)
+{
+  int i;
+  for (i = from; i < from+len; ++i)
+    taint[i] = taintValue;
+}
+
+void sqlfw_taint(const char *query, char *taint)
+{
+  int i, j, pos;
+  int tainted_marking = 1;
+  int not_tainted_marking = 0;
+  int patternFound;
+
+  // set taint markings to 'tainted' by default
+  sqlfw_taint_range(taint, tainted_marking, 0, strlen(query));
+
+  // use simple linear scan for now
+  // list of signature patterns are sorted in reverse length order already
+  // unset taint when match is found
+  pos = 0;
+  while (pos < strlen(query))
+  {
+    fprintf(stderr,"starting at position #%d\n", pos);
+
+	// when we'll have reg. expressions for patterns
+	// we'd need to make sure we go through all the regex patterns first
+	patternFound = 0;
+    for (i = 0; i < fw_numPatterns && !patternFound; ++i)
+	{
+	  int length_signature = strlen(fw_sigs[i]);
+	  if (length_signature >= 1 && length_signature <= (strlen(query) - pos))
+	  {
+	    if (strncmp(&query[pos], fw_sigs[i], strlen(fw_sigs[i])) == 0)
+	    {
+		  sqlfw_taint_range(taint, not_tainted_marking, pos, strlen(fw_sigs[i]));
+		  fprintf(stderr,"matched pattern #%d: pos: %d [%s]\n", i, pos, fw_sigs[i]);
+	      patternFound = 1;
+		}
+      }
+	}
+
+    pos++;
+  }
+}
+
+/*
+** Run the original sqlite parser on the given SQL string.  
+** Look for tainted SQL tokens/keywords
+*/
+int sqlfw_verify(const char *zSql, char **pzErrMsg){
+  Parse *pParse;
+  int nErr = 0;                   /* Number of errors encountered */
+  int i;                          /* Loop counter */
+  void *pEngine;                  /* The LEMON-generated LALR(1) parser */
+  int tokenType;                  /* type of the next token */
+  int lastTokenParsed = -1;       /* type of the previous token */
+  int mxSqlLen = 2048;            /* Max length of an SQL string */
+
+  char tainted[1024];
+  int j, k;
+  int beg, end;
+
+  sqlfw_taint(zSql, tainted);
+
+//  pParse = sqlite3StackAllocZero(db, sizeof(*pParse));
+  pParse = sqlite3MallocZero(sizeof(*pParse));
+
+  pParse->db = peasoup_db;
+
+  fprintf(stderr, "sqlite3_fw(): enter: query string length: %d\n", strlen(zSql));
+
+  // show query
+  fprintf(stderr, "%s\n", zSql);
+
+  // show taint
+  for (i = 0; i < strlen(zSql); ++i)
+    if (tainted[i])
+      fprintf(stderr, "X");
+	else
+      fprintf(stderr, "-");
+  fprintf(stderr,"\n");
+
+  pParse->rc = SQLITE_OK;
+  pParse->zTail = zSql;
+  i = 0;
+  pEngine = sqlite3ParserAlloc((void*(*)(size_t))sqlite3Malloc);
+  if( pEngine==0 ){
+    fprintf(stderr,"Failed to allocated space for pEngine\n");
+    return 0;
+  }
+
+  while( zSql[i]!=0 ){
+    pParse->sLastToken.z = &zSql[i];
+    pParse->sLastToken.n = sqlite3GetToken((unsigned char*)&zSql[i],&tokenType);
+
+	beg = i;
+    i += pParse->sLastToken.n;
+	end = i-1;
+
+    if( i>mxSqlLen ){
+      pParse->rc = SQLITE_TOOBIG;
+      break;
+    }
+
+    switch( tokenType ){
+      case TK_SPACE: {
+	    fprintf(stderr, "token: [ ] type: %d [%d..%d]\n", tokenType, beg, end);
+        break;
+      }
+      case TK_ILLEGAL: {
+        goto abort_parse;
+      }
+      case TK_SEMI: {
+        pParse->zTail = &zSql[i];
+		if (tainted[i])
+		{
+          fprintf(stderr,"TAINTED SEMI-COLON DETECTED -- VERY BAD\n");
+		  goto abort_parse;
+		}
+
+        /* Fall thru into the default case */
+      }
+      default: {
+	  // show token info
+        fprintf(stderr, "token: [");
+        for (k = beg; k <= end; ++k)
+		  fprintf(stderr,"%c", zSql[k]);
+		fprintf(stderr, "] type: %d  [%d..%d]\n", tokenType, beg, end);
+
+        sqlite3Parser(pEngine, tokenType, pParse->sLastToken, pParse);
+
+        lastTokenParsed = tokenType;
+        if( pParse->rc!=SQLITE_OK ){
+    		fprintf(stderr,"fwsql_verify(): Error in parsing:"); 
+          goto abort_parse;
+        }
+
+// GOOD:
+//
+// SELECT * FROM foobar where one='3' and two=20;
+// --------------------------------X----------XX  (taint markings)
+//
+// BAD:
+// SELECT * FROM foobar where one='' OR 1=1; -- and two=20
+// ----------------------------------XXXXXX--XX---------XX
+//
+// This would be detected b/c:
+//   OR is tainted
+//   -- is tainted
+//   = is tainted
+                   
+        switch (tokenType) {
+		// so here we would need to add all the token types that should not be tainted
+		// this would be any SQL keywords
+		  case TK_OR:
+		  case TK_AND:
+		  case TK_FROM:
+		  case TK_LIKE_KW:
+		  case TK_TABLE:
+		  case TK_DROP:
+		  case TK_INSERT:
+		  case TK_REPLACE:
+		  case TK_LP:
+		  case TK_RP:
+		  case TK_INTO:
+		  case TK_EQ:
+		  case TK_UPDATE:
+		  case TK_IF:
+		  case TK_SELECT:
+		  case TK_CONCAT:
+		  case TK_UNION:
+		  case TK_ALL:
+		  case TK_HAVING:
+		  case TK_ORDER:
+		  case TK_WHERE:
+		  case TK_LIMIT:
+		  case TK_NOT:
+		  case TK_EXISTS:
+		  case TK_DELETE:
+		  case TK_NULL:
+		  case TK_PLUS:
+		  case TK_GROUP:
+		  case TK_JOIN:
+		  case TK_USING:
+		    for (j = beg; j <= end; ++j)
+			{
+              if (tainted[j])
+			  {
+		          fprintf(stderr, "TAINTED -- VERY VERY BAD\n");
+				  break;
+			  }
+			}
+		  break;
+		}
+        break;
+      }
+    }
+
+	if (end + 2 < strlen(zSql))
+	{
+	  if (zSql[end+1] == '-' && zSql[end+2] == '-' &&
+	     (tainted[end+1] || tainted[end+2]))
+	  {
+        fprintf(stderr, "TAINTED COMMENT -- VERY BAD\n");
+		goto abort_parse;
+	  }
+	}
+
+  } // end while
+
+fprintf(stderr,"end of fwsql_verify(): good parse\n");
+  return 1; // this is good
+
+abort_parse:
+fprintf(stderr,"end of fwsql_verify(): bad parse\n");
+  return 0;
+}
diff --git a/sql/test/test1.c b/sql/test/test1.c
new file mode 100644
index 000000000..8234a2464
--- /dev/null
+++ b/sql/test/test1.c
@@ -0,0 +1,68 @@
+#include <stdio.h>
+#include "sqlite3.h"
+
+static int callback(void *NotUsed, int argc, char **argv, char **azColName){
+  int i;
+  for(i=0; i<argc; i++){
+    printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
+  }
+  printf("\n");
+  return 0;
+}
+
+
+void test_query(sqlite3 *db, char *userid, char *userpassword)
+{
+  int i, rc;
+  char *zErrMsg = 0;
+  char query[1024];
+
+ // sprintf(query, "SELECT * from users where id='%s' and password='%s'", userid, userpassword);
+  sprintf(query, "SELECT * from users where id='%s' and password='%s'", userid, userpassword);
+
+/*
+  char *select="SELECT * from users where id='";
+  char *and="' and password='";
+  sprintf(query, "%s%s%s%s'  ; select * from users;", select, userid, and, userpassword);
+ */
+
+  fprintf(stderr,"query: %s\n", query);
+
+  sqlfw_verify(query, &zErrMsg);
+
+  rc = sqlite3_exec(db, query, callback, 0, &zErrMsg);
+  if( rc!=SQLITE_OK ){
+    fprintf(stderr, "SQL error: %s\n", zErrMsg);
+    sqlite3_free(zErrMsg);
+  }
+}
+
+
+// DB: testdb
+// table: users
+// fields: name, password
+//         john, john
+int main(int argc, char **argv){
+  sqlite3 *db;
+  char *zErrMsg = 0;
+  int rc;
+
+  sqlfw_init("peasoup", argv[1]); // argv[1] is the signature file
+
+  rc = sqlite3_open("testdb", &db);
+  if( rc ){
+    fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
+    sqlite3_close(db);
+    return(1);
+  }
+
+  char *id = argv[2];
+  char *password = argv[3];
+
+  test_query(db, id, password);
+
+  sqlite3_close(db);
+  return 0;
+}
+
+// ------------------------------------------
diff --git a/sql/test/test2.c b/sql/test/test2.c
new file mode 100644
index 000000000..4539f7fe4
--- /dev/null
+++ b/sql/test/test2.c
@@ -0,0 +1,69 @@
+#include <stdio.h>
+#include "sqlite3.h"
+
+static int callback(void *NotUsed, int argc, char **argv, char **azColName){
+fprintf(stderr,"\n--callback function:\n");
+  int i;
+  for(i=0; i<argc; i++){
+    printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
+  }
+  printf("\n");
+  return 0;
+}
+
+
+void test_query(sqlite3 *db, char *userid, char *userpassword)
+{
+  int i, rc;
+  char *zErrMsg = 0;
+  char query[1024];
+
+ // sprintf(query, "SELECT * from users where id='%s' and password='%s'", userid, userpassword);
+  sprintf(query, "SELECT * from users where id='%s'", userid, userpassword);
+
+/*
+  char *select="SELECT * from users where id='";
+  char *and="' and password='";
+  sprintf(query, "%s%s%s%s'  ; select * from users;", select, userid, and, userpassword);
+ */
+
+  fprintf(stderr,"query: %s\n", query);
+
+  sqlfw_verify(query, &zErrMsg);
+
+  rc = sqlite3_exec(db, query, callback, 0, &zErrMsg);
+  if( rc!=SQLITE_OK ){
+    fprintf(stderr, "SQL error: %s\n", zErrMsg);
+    sqlite3_free(zErrMsg);
+  }
+}
+
+
+// DB: testdb
+// table: users
+// fields: name, password
+//         john, john
+int main(int argc, char **argv){
+  sqlite3 *db;
+  char *zErrMsg = 0;
+  int rc;
+
+  sqlfw_init("peasoup", argv[1]); // argv[1] is the signature file
+
+  rc = sqlite3_open("testdb", &db);
+  if( rc ){
+    fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
+    sqlite3_close(db);
+    return(1);
+  }
+
+  char *id = argv[2];
+  char *password = argv[3];
+
+  test_query(db, id, password);
+
+  sqlite3_close(db);
+  return 0;
+}
+
+// ------------------------------------------
-- 
GitLab