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