From 4af03f208ee5ee5ca652b80fbb4f96031ed21c1c Mon Sep 17 00:00:00 2001 From: Nick Galbreath Date: Tue, 4 Jun 2013 10:14:52 +0900 Subject: [PATCH] Update to libinjection 3.0.0-pre8 --- apache2/libinjection/libinjection.h | 86 +- apache2/libinjection/libinjection_sqli.c | 426 ++++-- apache2/libinjection/libinjection_sqli_data.h | 867 ++++++++--- apache2/libinjection/sqlparse.c | 1340 ----------------- 4 files changed, 1106 insertions(+), 1613 deletions(-) delete mode 100644 apache2/libinjection/sqlparse.c diff --git a/apache2/libinjection/libinjection.h b/apache2/libinjection/libinjection.h index 497fb2fe..9bc96e23 100644 --- a/apache2/libinjection/libinjection.h +++ b/apache2/libinjection/libinjection.h @@ -37,7 +37,7 @@ extern "C" { * See python's normalized version * http://www.python.org/dev/peps/pep-0386/#normalizedversion */ -#define LIBINJECTION_VERSION "3.0.0-pre2" +#define LIBINJECTION_VERSION "3.0.0-pre8" #define ST_MAX_SIZE 32 #define MAX_TOKENS 5 @@ -46,7 +46,13 @@ extern "C" { #define CHAR_SINGLE '\'' #define CHAR_DOUBLE '"' +#define COMMENTS_ANSI 0 +#define COMMENTS_MYSQL 1 + typedef struct { +#ifdef SWIG +%immutable; +#endif char type; char str_open; char str_close; @@ -55,6 +61,10 @@ typedef struct { } stoken_t; typedef struct { +#ifdef SWIG +%immutable; +#endif + /* input */ const char *s; size_t slen; @@ -73,13 +83,40 @@ typedef struct { /* +1 for ending null */ char pat[MAX_TOKENS + 1]; char delim; + char comment_style; + int reason; + + /* Number of ddw (dash-dash-white) comments + * These comments are in the form of + * '--[whitespace]' or '--[EOF]' + * + * All databases treat this as a comment. + */ + int stats_comment_ddw; + + /* Number of ddx (dash-dash-[notwhite]) comments + * + * ANSI SQL treats these are comments, MySQL treats this as + * two unary operators '-' '-' + * + * If you are parsing result returns FALSE and + * stats_comment_dd > 0, you should reparse with + * COMMENT_MYSQL + * + */ + int stats_comment_ddx; + + int stats_comment_c; + + int stats_folds; + } sfilter; /** * Pointer to function, takes cstr input, returns 1 for true, 0 for false */ -typedef int (*ptr_fingerprints_fn)(const char*, void* callbackarg); +typedef int (*ptr_fingerprints_fn)(sfilter*, void* callbackarg); /** * Main API: tests for SQLi in three possible contexts, no quotes, @@ -112,28 +149,51 @@ int libinjection_is_sqli(sfilter * sql_state, * CHAR_SINGLE ('), single quote context * CHAR_DOUBLE ("), double quote context * Other values will likely be ignored. - * \param ptr_fingerprints_fn is a pointer to a function - * that determines if a fingerprint is a match or not. - * \param callbackarg passed to function above * - * - * \return 1 (true) if SQLi or 0 (false) if not SQLi **in this context** + * \return pointer to sfilter.pat as convience. + * do not free! * */ -int libinjection_is_string_sqli(sfilter * sql_state, - const char *s, size_t slen, - const char delim, - ptr_fingerprints_fn fn, void* callbackarg); +const char* libinjection_sqli_fingerprint(sfilter * sql_state, + const char *s, size_t slen, + char delim, + char comment_style); /* FOR H@CKERS ONLY * */ -void libinjection_sqli_init(sfilter* sql_state, const char* str, - size_t slen, char delim); +void libinjection_sqli_init(sfilter* sql_state, + const char* s, size_t slen, + char delim, char comment_style); int libinjection_sqli_tokenize(sfilter * sql_state, stoken_t *ouput); +/** The built-in default function to match fingerprints + * and do false negative/positive analysis. This calls the following + * two functions. With this, you other-ride one part or the other. + * + * return libinjection_sqli_blacklist(sql_state, callbackarg) && + * libinject_sqli_not_whitelist(sql_state, callbackarg); + * + * \param sql_state should be filled out after libinjection_sqli_fingerprint is called + * \param callbackarg is unused but here to be used with API. + */ +int libinjection_sqli_check_fingerprint(sfilter *sql_state, void* callbackarg); + +/* Given a pattern determine if it's a SQLi pattern. + * + * \return TRUE if sqli, false otherwise + */ +int libinjection_sqli_blacklist(sfilter* sql_state); + +/* Given a positive match for a pattern (i.e. pattern is SQLi), this function + * does additional analysis to reduce false positives. + * + * \return TRUE if sqli, false otherwise + */ +int libinjection_sqli_not_whitelist(sfilter* sql_state); + #ifdef __cplusplus } #endif diff --git a/apache2/libinjection/libinjection_sqli.c b/apache2/libinjection/libinjection_sqli.c index 4067c223..022e8dc5 100644 --- a/apache2/libinjection/libinjection_sqli.c +++ b/apache2/libinjection/libinjection_sqli.c @@ -47,16 +47,13 @@ memchr2(const char *haystack, size_t haystack_len, char c0, char c1) if (haystack_len < 2) { return NULL; } - if (c0 == c1) { - return NULL; - } while (cur < last) { if (cur[0] == c0) { if (cur[1] == c1) { return cur; } else { - cur += 2; + cur += 2; //(c0 == c1) ? 1 : 2; } } else { cur += 1; @@ -66,6 +63,24 @@ memchr2(const char *haystack, size_t haystack_len, char c0, char c1) return NULL; } +/** + */ +static const char * +my_memmem(const char* haystack, size_t hlen, const char* needle, size_t nlen) +{ + assert(haystack); + assert(needle); + assert(nlen > 1); + const char* cur; + const char* last = haystack + hlen - nlen; + for (cur = haystack; cur <= last; ++cur) { + if (cur[0] == needle[0] && memcmp(cur, needle, nlen) == 0) { + return cur; + } + } + return NULL; +} + /** Find largest string containing certain characters. * * C Standard library 'strspn' only works for 'c-strings' (null terminated) @@ -107,6 +122,17 @@ strlencspn(const char *s, size_t len, const char *accept) } return len; } +static int char_is_white(char ch) { + /* ' ' space is 0x32 + '\t 0x09 \011 horizontal tab + '\n' 0x0a \012 new line + '\v' 0x0b \013 verical tab + '\f' 0x0c \014 new page + '\r' 0x0d \015 carriage return + 0xa0 \240 is latin1 + */ + return strchr(" \t\n\v\f\r\240", ch) != NULL; +} /* * ASCII half-case-insenstive compare! @@ -203,17 +229,6 @@ static int bsearch_cstrcase(const char *key, const char *base[], size_t nmemb) } } -/** - * - */ -#define UNUSED(x) (void)(x) - -static int is_sqli_pattern(const char* key, void* callbackarg) -{ - UNUSED(callbackarg); - return bsearch_cstr(key, sql_fingerprints, sqli_fingerprints_sz); -} - /** * * @@ -262,10 +277,7 @@ static char is_keyword(const char* key) static void st_clear(stoken_t * st) { - st->type = CHAR_NULL; - st->str_open = CHAR_NULL; - st->str_close = CHAR_NULL; - st->val[0] = CHAR_NULL; + memset(st, 0, sizeof(stoken_t)); } static void st_assign_char(stoken_t * st, const char stype, const char value) @@ -275,8 +287,8 @@ static void st_assign_char(stoken_t * st, const char stype, const char value) st->val[1] = CHAR_NULL; } -static void st_assign(stoken_t * st, const char stype, const char *value, - size_t len) +static void st_assign(stoken_t * st, const char stype, + const char *value, size_t len) { size_t last = len < ST_MAX_SIZE ? len : (ST_MAX_SIZE - 1); st->type = stype; @@ -367,13 +379,28 @@ static size_t parse_dash(sfilter * sf) const size_t slen = sf->slen; size_t pos = sf->pos; + /* + * five cases + * 1) --[white] this is always a SQL comment + * 2) --[EOF] this is a comment + * 3) --[notwhite] in MySQL this is NOT a comment but two unary operators + * 4) --[notwhite] everyone else thinks this is a comment + * 5) -[not dash] '-' is a unary operator + */ - size_t pos1 = pos + 1; - if (pos1 < slen && cs[pos1] == '-') { + if (pos + 2 < slen && cs[pos + 1] == '-' && char_is_white(cs[pos+2]) ) { + return parse_eol_comment(sf); + } else if (pos +2 == slen && cs[pos + 1] == '-') { + return parse_eol_comment(sf); + } else if (pos + 1 < slen && cs[pos + 1] == '-' && sf->comment_style == COMMENTS_ANSI) { + /* --[not-white] not-white case: + * + */ + sf->stats_comment_ddx += 1; return parse_eol_comment(sf); } else { st_assign_char(sf->current, 'o', '-'); - return pos1; + return pos + 1; } } @@ -582,7 +609,7 @@ static size_t parse_string_core(const char *cs, const size_t len, size_t pos, st_assign(st, 's', cs + pos + offset, len - pos - offset); st->str_close = CHAR_NULL; return len; - } else if (*(qpos - 1) != '\\') { + } else if (qpos == cs || *(qpos - 1) != '\\') { /* * ending quote is not escaped.. copy and end */ @@ -673,42 +700,156 @@ static size_t parse_string_tick(sfilter *sf) } } -static size_t parse_word(sfilter * sf) +/** MySQL ad-hoc character encoding + * + * if something starts with a underscore + * check to see if it's in this form + * _[a-z0-9] and if it's a character encoding + * If not, let the normal 'word parser' + * handle it. + */ +static size_t parse_underscore(sfilter *sf) { const char *cs = sf->s; + size_t slen = sf->slen; size_t pos = sf->pos; - char *dot; char ch; - size_t slen = - strlencspn(cs + pos, sf->slen - pos, - " .`<>:\\?=@!#~+-*/&|^%(),';\r\n\t\"\013\014"); - st_assign(sf->current, 'n', cs + pos, slen); + size_t xlen = strlenspn(cs + pos + 1, slen - pos - 1, + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + if (xlen == 0) { + return parse_word(sf); + } + st_assign(sf->current, 'n', cs + pos, xlen); + ch = is_keyword(sf->current->val); + if (ch == 't') { + sf->current->type = 't'; + return xlen + 1; + } + return parse_word(sf); +} - dot = strchr(sf->current->val, '.'); - if (dot != NULL) { - *dot = '\0'; +static size_t parse_ustring(sfilter * sf) +{ + const char *cs = sf->s; + size_t slen = sf->slen; + size_t pos = sf->pos; - ch = is_keyword(sf->current->val); + if (pos + 2 < slen && cs[pos+1] == '&' && cs[pos+2] == '\'') { + sf->pos += 2; + pos = parse_string(sf); + sf->current->str_open = 'u'; + if (sf->current->str_close == '\'') { + sf->current->str_close = 'u'; + } + return pos; + } else { + return parse_word(sf); + } +} - if (ch == 'k' || ch == 'o' || ch == 'E') { - /* - * we got something like "SELECT.1" - */ - sf->current->type = ch; - return pos + strlen(sf->current->val); - } else { - /* - * something else, put back dot - */ - *dot = '.'; +static size_t parse_qstring_core(sfilter * sf, int offset) +{ + char ch; + const char *strend; + const char *cs = sf->s; + size_t slen = sf->slen; + size_t pos = sf->pos + offset; + + /* if we are already at end of string.. + if current char is not q or Q + if we don't have 2 more chars + if char2 != a single quote + then, just treat as word + */ + if (pos >= slen || + (cs[pos] != 'q' && cs[pos] != 'Q') || + pos + 2 >= slen || + cs[pos + 1] != '\'') { + return parse_word(sf); + } + + ch = cs[pos + 2]; + if (ch < 33 && ch > 127) { + return parse_word(sf); + } + switch (ch) { + case '(' : ch = ')'; break; + case '[' : ch = ']'; break; + case '{' : ch = '}'; break; + case '<' : ch = '>'; break; + } + + strend = memchr2(cs + pos + 3, slen - pos - 3, ch, '\''); + if (strend == NULL) { + st_assign(sf->current, 's', cs + pos + 3, slen - pos - 3); + sf->current->str_open = 'q'; + sf->current->str_close = CHAR_NULL; + return slen; + } else { + st_assign(sf->current, 's', cs + pos + 3, strend - cs - pos - 3); + sf->current->str_open = 'q'; + sf->current->str_close = 'q'; + return (strend - cs) + 2; + } +} + +/* + * Oracle's q string + */ +static size_t parse_qstring(sfilter * sf) +{ + return parse_qstring_core(sf, 0); +} + +/* + * Oracle's nq string + */ +static size_t parse_nqstring(sfilter * sf) +{ + return parse_qstring_core(sf, 1); +} + +static size_t parse_word(sfilter * sf) +{ + char ch; + char delim; + size_t i; + const char *cs = sf->s; + size_t pos = sf->pos; + size_t wlen = strlencspn(cs + pos, sf->slen - pos, + " <>:\\?=@!#~+-*/&|^%(),';\t\n\v\f\r\""); + + st_assign(sf->current, 'n', cs + pos, wlen); + + /* now we need to look inside what we good for "." and "`" + * and see if what is before is a keyword or not + */ + for (i =0; i < strlen(sf->current->val); ++i) { + delim = sf->current->val[i]; + if (delim == '.' || delim == '`') { + sf->current->val[i] = CHAR_NULL; + ch = is_keyword(sf->current->val); + if (ch == 'k' || ch == 'o' || ch == 'E') { + /* needed for swig */ + st_clear(sf->current); + /* + * we got something like "SELECT.1" + * or SELECT`column` + */ + st_assign(sf->current, ch, cs + pos, i); + return pos + i; + } else { + /* restore character */ + sf->current->val[i] = delim; + } } } /* * do normal lookup with word including '.' */ - if (slen < ST_MAX_SIZE) { + if (wlen < ST_MAX_SIZE) { ch = is_keyword(sf->current->val); @@ -717,7 +858,7 @@ static size_t parse_word(sfilter * sf) } sf->current->type = ch; } - return pos + slen; + return pos + wlen; } /* MySQL backticks are a cross between string and @@ -794,8 +935,7 @@ static size_t parse_var(sfilter * sf) xlen = strlencspn(cs + pos, slen - pos, - " <>:\\?=@!#~+-*/&|^%(),';\r\n\t\"\013\014"); -// "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.$"); + " <>:\\?=@!#~+-*/&|^%(),';\t\n\v\f\r'`\""); if (xlen == 0) { st_assign(sf->current, 'v', cs + pos, 0); return pos; @@ -807,21 +947,73 @@ static size_t parse_var(sfilter * sf) static size_t parse_money(sfilter *sf) { + const char* strend; const char *cs = sf->s; const size_t slen = sf->slen; size_t pos = sf->pos; size_t xlen; + if (pos + 1 == slen) { + /* end of line */ + st_assign_char(sf->current, 'n', '$'); + return slen; + } + /* * $1,000.00 or $1.000,00 ok! * This also parses $....,,,111 but that's ok */ + xlen = strlenspn(cs + pos + 1, slen - pos - 1, "0123456789.,"); if (xlen == 0) { - /* - * just ignore '$' - */ - return pos + 1; + if (cs[pos + 1] == '$') { + /* we have $$ .. find ending $$ and make string */ + strend = memchr2(cs + pos + 2, slen - pos -2, '$', '$'); + if (strend == NULL) { + /* fell off edge */ + st_assign(sf->current, 's', cs + pos + 2, slen - (pos + 2)); + sf->current->str_open = '$'; + sf->current->str_close = CHAR_NULL; + return slen; + } else { + st_assign(sf->current, 's', cs + pos + 2, strend - (cs + pos + 2)); + sf->current->str_open = '$'; + sf->current->str_close = '$'; + return strend - cs + 2; + } + } else { + /* ok it's not a number or '$$', but maybe it's pgsql "$ quoted strings" */ + xlen = strlenspn(cs + pos + 1, slen - pos - 1, "abcdefghjiklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + if (xlen == 0) { + /* hmm it's "$" _something_ .. just add $ and keep going*/ + st_assign_char(sf->current, 'n', '$'); + return pos + 1; + } + /* we have $foobar????? */ + /* is it $foobar$ */ + if (pos + xlen + 1 == slen || cs[pos+xlen+1] != '$') { + /* not $foobar$, or fell off edge */ + st_assign_char(sf->current, 'n', '$'); + return pos + 1; + } + + /* we have $foobar$ ... find it again */ + strend = my_memmem(cs+xlen+2, slen - (pos+xlen+2), cs + pos, xlen+2); + + if (strend == NULL) { + /* fell off edge */ + st_assign(sf->current, 's', cs+pos+xlen+2, slen - pos - xlen - 2); + sf->current->str_open = '$'; + sf->current->str_close = CHAR_NULL; + return slen; + } else { + /* got one */ + st_assign(sf->current, 's', cs+pos+xlen+2, strend - (cs + pos + xlen + 2)); + sf->current->str_open = '$'; + sf->current->str_close = '$'; + return (strend + xlen + 2) - cs; + } + } } else { st_assign(sf->current, '1', cs + pos, 1 + xlen); return pos + 1 + xlen; @@ -950,12 +1142,13 @@ int libinjection_sqli_tokenize(sfilter * sf, stoken_t *current) * Initializes parsing state * */ -void libinjection_sqli_init(sfilter * sf, const char *s, size_t len, char delim) +void libinjection_sqli_init(sfilter * sf, const char *s, size_t len, char delim, char comment_style) { memset(sf, 0, sizeof(sfilter)); sf->s = s; sf->slen = len; sf->delim = delim; + sf->comment_style = comment_style; } /** See if two tokens can be merged since they are compound SQL phrases. @@ -984,7 +1177,7 @@ static int syntax_merge_words(stoken_t * a, stoken_t * b) if (! (a->type == 'k' || a->type == 'n' || a->type == 'o' - || a->type == 'U' || a->type == 'E')) { + || a->type == 'U' || a->type == 'E' || a->type == 't')) { return FALSE; } @@ -1083,15 +1276,18 @@ int filter_fold(sfilter * sf) */ if (sf->tokenvec[left].type == 's' && sf->tokenvec[left+1].type == 's') { pos -= 1; + sf->stats_folds += 1; continue; } else if (sf->tokenvec[left].type =='o' && st_is_unary_op(&sf->tokenvec[left+1])) { pos -= 1; + sf->stats_folds += 1; if (left > 0) { left -= 1; } continue; } else if (sf->tokenvec[left].type =='(' && st_is_unary_op(&sf->tokenvec[left+1])) { pos -= 1; + sf->stats_folds += 1; if (left > 0) { left -= 1; } @@ -1121,8 +1317,16 @@ int filter_fold(sfilter * sf) sf->tokenvec[left].type = 'f'; continue; #endif + } else if (sf->tokenvec[left].type == 't' && + (sf->tokenvec[left+1].type == 'n' || sf->tokenvec[left+1].type == '1' || + sf->tokenvec[left+1].type == 'v' || sf->tokenvec[left+1].type == 's')) { + st_copy(&sf->tokenvec[left], &sf->tokenvec[left+1]); + pos -= 1; + sf->stats_folds += 1; + continue; } + /* all cases of handing 2 tokens is done and nothing matched. Get one more token */ @@ -1171,13 +1375,18 @@ int filter_fold(sfilter * sf) (sf->tokenvec[left+2].type == '1' || sf->tokenvec[left+2].type == 'n')) { pos -= 2; continue; -#if 0 - } else if ((sf->tokenvec[left].type == 'n' || sf->tokenvec[left].type == '1') && + } else if ((sf->tokenvec[left].type == 'n' || sf->tokenvec[left].type == '1' || + sf->tokenvec[left].type == 'v' || sf->tokenvec[left].type == 's') && + sf->tokenvec[left+1].type == 'o' && + sf->tokenvec[left+2].type == 't') { + pos -= 2; + sf->stats_folds += 2; + continue; + } else if ((sf->tokenvec[left].type == 'n' || sf->tokenvec[left].type == '1' || sf->tokenvec[left].type == 's') && sf->tokenvec[left+1].type == ',' && - (sf->tokenvec[left+2].type == '1' || sf->tokenvec[left+2].type == 'n')) { + (sf->tokenvec[left+2].type == '1' || sf->tokenvec[left+2].type == 'n' || sf->tokenvec[left+2].type == 's')) { pos -= 2; continue; -#endif } else if ((sf->tokenvec[left].type == 'k' || sf->tokenvec[left].type == 'E') && st_is_unary_op(&sf->tokenvec[left+1]) && (sf->tokenvec[left+2].type == '1' || sf->tokenvec[left+2].type == 'n' || sf->tokenvec[left+2].type == 'v' || sf->tokenvec[left+2].type == 's' || sf->tokenvec[left+2].type == 'f' )) { @@ -1234,17 +1443,15 @@ int filter_fold(sfilter * sf) * double quote. * */ -int libinjection_is_string_sqli(sfilter * sql_state, - const char *s, size_t slen, - const char delim, - ptr_fingerprints_fn fn, void* callbackarg) +const char* +libinjection_sqli_fingerprint(sfilter * sql_state, + const char *s, size_t slen, + char delim, char comment_style) { int i; int tlen = 0; - char ch; - int patmatch; - libinjection_sqli_init(sql_state, s, slen, delim); + libinjection_sqli_init(sql_state, s, slen, delim, comment_style); tlen = filter_fold(sql_state); for (i = 0; i < tlen; ++i) { @@ -1257,17 +1464,45 @@ int libinjection_is_string_sqli(sfilter * sql_state, sql_state->pat[tlen] = CHAR_NULL; /* - * check for 'X' in pattern + * check for 'X' in pattern, and then + * clear out all tokens + * * this means parsing could not be done * accurately due to pgsql's double comments - * or other syntax that isn't consistent - * should be very rare false positive + * or other syntax that isn't consistent. + * Should be very rare false positive */ if (strchr(sql_state->pat, 'X')) { - return TRUE; + /* needed for SWIG */ + memset((void*)sql_state->pat, 0, MAX_TOKENS + 1); + sql_state->pat[0] = 'X'; + + sql_state->tokenvec[0].type = 'X'; + sql_state->tokenvec[0].val[0] = 'X'; + sql_state->tokenvec[0].val[1] = '\0'; + sql_state->tokenvec[1].type = CHAR_NULL; } - patmatch = fn(sql_state->pat, callbackarg); + return sql_state->pat; +} + + +/** + * + */ +#define UNUSED(x) (void)(x) + +int libinjection_sqli_check_fingerprint(sfilter* sql_state, void* callbackarg) +{ + UNUSED(callbackarg); + + return libinjection_sqli_blacklist(sql_state) && + libinjection_sqli_not_whitelist(sql_state); +} + +int libinjection_sqli_blacklist(sfilter* sql_state) +{ + int patmatch = bsearch_cstr(sql_state->pat, sql_fingerprints, sqli_fingerprints_sz); /* * No match. @@ -1280,11 +1515,22 @@ int libinjection_is_string_sqli(sfilter * sql_state, return FALSE; } + return TRUE; +} + +/* + * return TRUE if sqli, false is benign + */ +int libinjection_sqli_not_whitelist(sfilter* sql_state) +{ /* - * We got a SQLi match + * We assume we got a SQLi match * This next part just helps reduce false positives. * */ + char ch; + size_t tlen = strlen(sql_state->pat); + switch (tlen) { case 2:{ /* @@ -1431,7 +1677,6 @@ int libinjection_is_string_sqli(sfilter * sql_state, int libinjection_is_sqli(sfilter * sql_state, const char *s, size_t slen, ptr_fingerprints_fn fn, void* callbackarg) { - /* * no input? not sqli */ @@ -1440,17 +1685,21 @@ int libinjection_is_sqli(sfilter * sql_state, const char *s, size_t slen, } if (fn == NULL) { - fn = is_sqli_pattern; + fn = libinjection_sqli_check_fingerprint; } /* * test input "as-is" */ - if (libinjection_is_string_sqli(sql_state, s, slen, CHAR_NULL, - fn, callbackarg)) { + libinjection_sqli_fingerprint(sql_state, s, slen, CHAR_NULL, COMMENTS_ANSI); + if (fn(sql_state, callbackarg)) { return TRUE; + } else if (sql_state->stats_comment_ddx) { + libinjection_sqli_fingerprint(sql_state, s, slen, CHAR_NULL, COMMENTS_MYSQL); + if (fn(sql_state, callbackarg)) { + return TRUE; + } } - /* * if input has a single_quote, then * test as if input was actually ' @@ -1460,19 +1709,26 @@ int libinjection_is_sqli(sfilter * sql_state, const char *s, size_t slen, * is_string_sqli(sql_state, "'" + s, slen+1, NULL, fn, arg) * */ - if (memchr(s, CHAR_SINGLE, slen) - && libinjection_is_string_sqli(sql_state, s, slen, CHAR_SINGLE, - fn, callbackarg)) { + if (memchr(s, CHAR_SINGLE, slen)) { + libinjection_sqli_fingerprint(sql_state, s, slen, CHAR_SINGLE, COMMENTS_ANSI); + if (fn(sql_state, callbackarg)) { return TRUE; + } else if (sql_state->stats_comment_ddx) { + libinjection_sqli_fingerprint(sql_state, s, slen, CHAR_SINGLE, COMMENTS_MYSQL); + if (fn(sql_state, callbackarg)) { + return TRUE; + } + } } /* * same as above but with a double-quote " */ - if (memchr(s, CHAR_DOUBLE, slen) - && libinjection_is_string_sqli(sql_state, s, slen, CHAR_DOUBLE, - fn, callbackarg)) { - return TRUE; + if (memchr(s, CHAR_DOUBLE, slen)) { + libinjection_sqli_fingerprint(sql_state, s, slen, CHAR_DOUBLE, COMMENTS_MYSQL); + if (fn(sql_state, callbackarg)) { + return TRUE; + } } /* diff --git a/apache2/libinjection/libinjection_sqli_data.h b/apache2/libinjection/libinjection_sqli_data.h index 1fcea59b..6708dad0 100644 --- a/apache2/libinjection/libinjection_sqli_data.h +++ b/apache2/libinjection/libinjection_sqli_data.h @@ -24,6 +24,10 @@ static size_t parse_word(sfilter * sf); static size_t parse_var(sfilter * sf); static size_t parse_number(sfilter * sf); static size_t parse_tick(sfilter * sf); +static size_t parse_underscore(sfilter * sf); +static size_t parse_ustring(sfilter * sf); +static size_t parse_qstring(sfilter * sf); +static size_t parse_nqstring(sfilter * sf); static const char* operators2[] = { @@ -31,7 +35,6 @@ static const char* operators2[] = { "!<", "!=", "!>", - "!~", "%=", "&&", "&=", @@ -39,6 +42,7 @@ static const char* operators2[] = { "+=", "-=", "/=", + "::", ":=", "<<", "<=", @@ -71,6 +75,9 @@ static const keyword_t sql_keywords[] = { {"ALTER", 'k'}, {"ANALYZE", 'k'}, {"AND", '&'}, + {"ANYARRAY", 't'}, + {"ANYELEMENT", 't'}, + {"ANYNONARRY", 't'}, {"APPLOCK_MODE", 'f'}, {"APPLOCK_TEST", 'f'}, {"APP_NAME", 'f'}, @@ -100,7 +107,8 @@ static const keyword_t sql_keywords[] = { {"BEGIN", 'E'}, {"BENCHMARK", 'f'}, {"BETWEEN", 'k'}, - {"BIGINT", 'k'}, + {"BIGINT", 't'}, + {"BIGSERIAL", 't'}, {"BIN", 'f'}, {"BINARY", 'k'}, {"BINARY_DOUBLE_INFINITY", '1'}, @@ -114,12 +122,13 @@ static const keyword_t sql_keywords[] = { {"BIT_OR", 'f'}, {"BIT_XOR", 'f'}, {"BLOB", 'k'}, - {"BOOLEAN", 'k'}, + {"BOOLEAN", 't'}, {"BOOL_AND", 'f'}, {"BOOL_OR", 'f'}, {"BOTH", 'k'}, {"BTRIM", 'f'}, {"BY", 'n'}, + {"BYTEA", 't'}, {"CALL", 'k'}, {"CASCADE", 'k'}, {"CASE", 'E'}, @@ -139,7 +148,7 @@ static const keyword_t sql_keywords[] = { {"CHANGE", 'k'}, {"CHANGES", 'f'}, {"CHAR", 'f'}, - {"CHARACTER", 'k'}, + {"CHARACTER", 't'}, {"CHARACTER_LENGTH", 'f'}, {"CHARINDEX", 'f'}, {"CHARSET", 'f'}, @@ -183,6 +192,7 @@ static const keyword_t sql_keywords[] = { {"CREATE", 'E'}, {"CROSS", 'n'}, {"CSNG", 'f'}, + {"CSTRING", 't'}, {"CTXSYS.DRITHSX.SN", 'f'}, {"CUME_DIST", 'f'}, {"CURDATE", 'f'}, @@ -239,7 +249,7 @@ static const keyword_t sql_keywords[] = { {"DB_NAME", 'f'}, {"DCOUNT", 'f'}, {"DEC", 'k'}, - {"DECIMAL", 'k'}, + {"DECIMAL", 't'}, {"DECLARE", 'E'}, {"DECODE", 'f'}, {"DECRYPTBYASMKEY", 'f'}, @@ -267,6 +277,7 @@ static const keyword_t sql_keywords[] = { {"DLOOKUP", 'f'}, {"DMAX", 'f'}, {"DMIN", 'f'}, + {"DOUBLE", 't'}, {"DROP", 'E'}, {"DSUM", 'f'}, {"DUAL", 'k'}, @@ -311,6 +322,9 @@ static const keyword_t sql_keywords[] = { {"FILE_NAME", 'f'}, {"FIND_IN_SET", 'f'}, {"FIRST_VALUE", 'f'}, + {"FLOAT", 't'}, + {"FLOAT4", 't'}, + {"FLOAT8", 't'}, {"FLOOR", 'f'}, {"FN_VIRTUALFILESTATS", 'f'}, {"FOR", 'n'}, @@ -380,7 +394,7 @@ static const keyword_t sql_keywords[] = { {"INT3", 'k'}, {"INT4", 'k'}, {"INT8", 'k'}, - {"INTEGER", 'k'}, + {"INTEGER", 't'}, {"INTERVAL", 'k'}, {"INTO", 'k'}, {"IS", 'o'}, @@ -466,6 +480,7 @@ static const keyword_t sql_keywords[] = { {"MOD", 'o'}, {"MODE", 'n'}, {"MODIFIES", 'k'}, + {"MONEY", 't'}, {"MONTH", 'f'}, {"MONTHNAME", 'f'}, {"NAME_CONST", 'f'}, @@ -480,7 +495,7 @@ static const keyword_t sql_keywords[] = { {"NTILE", 'f'}, {"NULL", 'v'}, {"NULLIF", 'f'}, - {"NUMERIC", 'k'}, + {"NUMERIC", 't'}, {"NZ", 'f'}, {"OBJECTPROPERTY", 'f'}, {"OBJECTPROPERTYEX", 'f'}, @@ -491,6 +506,7 @@ static const keyword_t sql_keywords[] = { {"OCT", 'f'}, {"OCTET_LENGTH", 'f'}, {"OFFSET", 'k'}, + {"OID", 't'}, {"OLD_PASSWORD", 'f'}, {"ONE_SHOT", 'k'}, {"OPEN", 'k'}, @@ -574,13 +590,21 @@ static const keyword_t sql_keywords[] = { {"READ", 'k'}, {"READS", 'k'}, {"READ_WRITE", 'k'}, - {"REAL", 'n'}, + {"REAL", 't'}, {"REFERENCES", 'k'}, + {"REGCLASS", 't'}, + {"REGCONFIG", 't'}, + {"REGDICTIONARY", 't'}, {"REGEXP", 'o'}, {"REGEXP_MATCHES", 'f'}, {"REGEXP_REPLACE", 'f'}, {"REGEXP_SPLIT_TO_ARRAY", 'f'}, {"REGEXP_SPLIT_TO_TABLE", 'f'}, + {"REGOPER", 't'}, + {"REGOPERATOR", 't'}, + {"REGPROC", 't'}, + {"REGPROCEDURE", 't'}, + {"REGTYPE", 't'}, {"RELEASE", 'k'}, {"RELEASE_LOCK", 'f'}, {"RENAME", 'k'}, @@ -612,6 +636,10 @@ static const keyword_t sql_keywords[] = { {"SELECT", 'E'}, {"SENSITIVE", 'k'}, {"SEPARATOR", 'k'}, + {"SERIAL", 't'}, + {"SERIAL2", 't'}, + {"SERIAL4", 't'}, + {"SERIAL8", 't'}, {"SESSION_USER", 'f'}, {"SET", 'E'}, {"SETATTR", 'f'}, @@ -634,7 +662,8 @@ static const keyword_t sql_keywords[] = { {"SIN", 'f'}, {"SLEEP", 'f'}, {"SMALLDATETIMEFROMPARTS", 'f'}, - {"SMALLINT", 'k'}, + {"SMALLINT", 't'}, + {"SMALLSERIAL", 't'}, {"SOUNDEX", 'f'}, {"SOUNDS", 'o'}, {"SPACE", 'f'}, @@ -692,6 +721,7 @@ static const keyword_t sql_keywords[] = { {"TAN", 'f'}, {"TERMINATED", 'k'}, {"TERTIARY_WEIGHTS", 'f'}, + {"TEXT", 't'}, {"TEXTPOS", 'f'}, {"TEXTPTR", 'f'}, {"TEXTVALID", 'f'}, @@ -701,7 +731,7 @@ static const keyword_t sql_keywords[] = { {"TIMEFROMPARTS", 'f'}, {"TIMEOFDAY", 'f'}, {"TIMESERIAL", 'f'}, - {"TIMESTAMP", 'f'}, + {"TIMESTAMP", 't'}, {"TIMESTAMPADD", 'f'}, {"TIMEVALUE", 'f'}, {"TIME_FORMAT", 'f'}, @@ -738,6 +768,7 @@ static const keyword_t sql_keywords[] = { {"TYPE_ID", 'f'}, {"TYPE_NAME", 'f'}, {"UCASE", 'f'}, + {"UESCAPE", 'o'}, {"UNCOMPRESS", 'f'}, {"UNCOMPRESS_LENGTH", 'f'}, {"UNDO", 'k'}, @@ -747,7 +778,7 @@ static const keyword_t sql_keywords[] = { {"UNIQUE", 'n'}, {"UNIX_TIMESTAMP", 'f'}, {"UNI_ON", 'U'}, - {"UNKNOWN", 'k'}, + {"UNKNOWN", 'v'}, {"UNLOCK", 'k'}, {"UNNEST", 'f'}, {"UNSIGNED", 'k'}, @@ -771,7 +802,7 @@ static const keyword_t sql_keywords[] = { {"VALUES", 'k'}, {"VAR", 'f'}, {"VARBINARY", 'k'}, - {"VARCHAR", 'k'}, + {"VARCHAR", 't'}, {"VARCHARACTER", 'k'}, {"VARIANCE", 'f'}, {"VARP", 'f'}, @@ -781,6 +812,7 @@ static const keyword_t sql_keywords[] = { {"VERIFYSIGNEDBYASMKEY", 'f'}, {"VERIFYSIGNEDBYCERT", 'f'}, {"VERSION", 'f'}, + {"VOID", 't'}, {"WAITFOR", 'n'}, {"WEEK", 'f'}, {"WEEKDAY", 'f'}, @@ -811,15 +843,53 @@ static const keyword_t sql_keywords[] = { {"YEAR_MONTH", 'k'}, {"ZEROBLOB", 'f'}, {"ZEROFILL", 'k'}, + {"_ARMSCII8", 't'}, + {"_ASCII", 't'}, + {"_BIG5", 't'}, + {"_BINARY", 't'}, + {"_CP1250", 't'}, + {"_CP1251", 't'}, + {"_CP1257", 't'}, + {"_CP850", 't'}, + {"_CP852", 't'}, + {"_CP866", 't'}, + {"_CP932", 't'}, + {"_DEC8", 't'}, + {"_EUCJPMS", 't'}, + {"_EUCKR", 't'}, + {"_GB2312", 't'}, + {"_GBK", 't'}, + {"_GEOSTD8", 't'}, + {"_GREEK", 't'}, + {"_HEBREW", 't'}, + {"_HP8", 't'}, + {"_KEYBCS2", 't'}, + {"_KOI8R", 't'}, + {"_KOI8U", 't'}, + {"_LATIN1", 't'}, + {"_LATIN2", 't'}, + {"_LATIN5", 't'}, + {"_LATIN7", 't'}, + {"_MACCE", 't'}, + {"_MACROMAN", 't'}, + {"_SJIS", 't'}, + {"_SWE7", 't'}, + {"_TIS620", 't'}, + {"_UJIS", 't'}, + {"_USC2", 't'}, + {"_UTF8", 't'}, }; -static const size_t sql_keywords_sz = 755; +static const size_t sql_keywords_sz = 818; static const char* multikeywords_start[] = { "ALTER", "AT", "AT TIME", + "CHARACTER", "CREATE", "CREATE OR", "CROSS", + "DOUBLE", + "FOR", "FULL", "GROUP", "IN", @@ -829,8 +899,11 @@ static const char* multikeywords_start[] = { "IS", "IS DISTINCT", "IS NOT", + "IS NOT DISTINCT", "LEFT", "LOCK", + "LOCK IN", + "LOCK IN SHARE", "NATURAL", "NEXT", "NEXT VALUE", @@ -846,14 +919,17 @@ static const char* multikeywords_start[] = { "UNION", "WAITFOR", }; -static const size_t multikeywords_start_sz = 31; +static const size_t multikeywords_start_sz = 37; static const keyword_t multikeywords[] = { {"ALTER DOMAIN", 'k'}, {"ALTER TABLE", 'k'}, {"AT TIME", 'n'}, {"AT TIME ZONE", 'k'}, + {"CHARACTER VARYING", 't'}, {"CREATE OR REPLACE", 'E'}, {"CROSS JOIN", 'k'}, + {"DOUBLE PRECISION", 't'}, + {"FOR UPDATE", 'k'}, {"FULL OUTER", 'k'}, {"GROUP BY", 'B'}, {"IN BOOLEAN", 'n'}, @@ -867,6 +943,9 @@ static const keyword_t multikeywords[] = { {"IS NOT DISTINCT FROM", 'k'}, {"LEFT JOIN", 'k'}, {"LEFT OUTER", 'k'}, + {"LOCK IN", 'n'}, + {"LOCK IN SHARE", 'n'}, + {"LOCK IN SHARE MODE", 'k'}, {"LOCK TABLE", 'k'}, {"LOCK TABLES", 'k'}, {"NATURAL FULL", 'k'}, @@ -897,7 +976,7 @@ static const keyword_t multikeywords[] = { {"WAITFOR RECEIVE", 'E'}, {"WAITFOR TIME", 'E'}, }; -static const size_t multikeywords_sz = 48; +static const size_t multikeywords_sz = 54; typedef size_t (*pt2Function)(sfilter *sf); static const pt2Function char_parse_map[] = { @@ -979,14 +1058,14 @@ static const pt2Function char_parse_map[] = { &parse_word, /* 75 */ &parse_word, /* 76 */ &parse_word, /* 77 */ - &parse_word, /* 78 */ + &parse_nqstring, /* 78 */ &parse_word, /* 79 */ &parse_word, /* 80 */ - &parse_word, /* 81 */ + &parse_qstring, /* 81 */ &parse_word, /* 82 */ &parse_word, /* 83 */ &parse_word, /* 84 */ - &parse_word, /* 85 */ + &parse_ustring, /* 85 */ &parse_word, /* 86 */ &parse_word, /* 87 */ &parse_word, /* 88 */ @@ -996,7 +1075,7 @@ static const pt2Function char_parse_map[] = { &parse_backslash, /* 92 */ &parse_other, /* 93 */ &parse_operator1, /* 94 */ - &parse_word, /* 95 */ + &parse_underscore, /* 95 */ &parse_tick, /* 96 */ &parse_word, /* 97 */ &parse_word, /* 98 */ @@ -1011,14 +1090,14 @@ static const pt2Function char_parse_map[] = { &parse_word, /* 107 */ &parse_word, /* 108 */ &parse_word, /* 109 */ - &parse_word, /* 110 */ + &parse_nqstring, /* 110 */ &parse_word, /* 111 */ &parse_word, /* 112 */ - &parse_word, /* 113 */ + &parse_qstring, /* 113 */ &parse_word, /* 114 */ &parse_word, /* 115 */ &parse_word, /* 116 */ - &parse_word, /* 117 */ + &parse_ustring, /* 117 */ &parse_word, /* 118 */ &parse_word, /* 119 */ &parse_word, /* 120 */ @@ -1051,6 +1130,7 @@ static const char* sql_fingerprints[] = { "&f((s", "&f((v", "&f())", + "&f()U", "&f()o", "&f(1)", "&f(1o", @@ -1104,6 +1184,7 @@ static const char* sql_fingerprints[] = { "1&(1)", "1&(1,", "1&(1o", + "1&(E(", "1&(E1", "1&(Ef", "1&(Ek", @@ -1125,6 +1206,8 @@ static const char* sql_fingerprints[] = { "1&1&n", "1&1&s", "1&1&v", + "1&1;", + "1&1;c", "1&1B1", "1&1Bf", "1&1Bs", @@ -1132,6 +1215,7 @@ static const char* sql_fingerprints[] = { "1&1En", "1&1U", "1&1U(", + "1&1U;", "1&1UE", "1&1Uc", "1&1c", @@ -1193,6 +1277,8 @@ static const char* sql_fingerprints[] = { "1&n&n", "1&n&s", "1&n&v", + "1&n;", + "1&n;c", "1&nc", "1&nk1", "1&nkf", @@ -1207,14 +1293,17 @@ static const char* sql_fingerprints[] = { "1&o(s", "1&o(v", "1&o1", + "1&o1;", "1&o1c", "1&o1o", "1&of(", "1&oko", "1&os", + "1&os;", "1&osc", "1&oso", "1&ov", + "1&ov;", "1&ovc", "1&ovo", "1&s", @@ -1225,6 +1314,8 @@ static const char* sql_fingerprints[] = { "1&s&s", "1&s&v", "1&s1o", + "1&s;", + "1&s;c", "1&sB1", "1&sBf", "1&sBs", @@ -1232,6 +1323,7 @@ static const char* sql_fingerprints[] = { "1&sEn", "1&sU", "1&sU(", + "1&sU;", "1&sUE", "1&sUc", "1&sc", @@ -1256,6 +1348,8 @@ static const char* sql_fingerprints[] = { "1&v&n", "1&v&s", "1&v&v", + "1&v;", + "1&v;c", "1&vB1", "1&vBf", "1&vBs", @@ -1263,6 +1357,7 @@ static const char* sql_fingerprints[] = { "1&vEn", "1&vU", "1&vU(", + "1&vU;", "1&vUE", "1&vUc", "1&vc", @@ -1292,10 +1387,12 @@ static const char* sql_fingerprints[] = { "1)&(1", "1)&(E", "1)&(f", + "1)&(n", "1)&(s", "1)&(v", "1)&1", "1)&1&", + "1)&1;", "1)&1B", "1)&1U", "1)&1c", @@ -1305,6 +1402,7 @@ static const char* sql_fingerprints[] = { "1)&o(", "1)&s", "1)&s&", + "1)&s;", "1)&sB", "1)&sU", "1)&sc", @@ -1312,6 +1410,7 @@ static const char* sql_fingerprints[] = { "1)&so", "1)&v", "1)&v&", + "1)&v;", "1)&vB", "1)&vU", "1)&vc", @@ -1371,15 +1470,18 @@ static const char* sql_fingerprints[] = { "1);Ev", "1)B1", "1)B1&", + "1)B1;", "1)B1c", "1)B1o", "1)Bf(", "1)Bs", "1)Bs&", + "1)Bs;", "1)Bsc", "1)Bso", "1)Bv", "1)Bv&", + "1)Bv;", "1)Bvc", "1)Bvo", "1)E1c", @@ -1419,9 +1521,11 @@ static const char* sql_fingerprints[] = { "1)kvU", "1)kvo", "1)o((", + "1)o(E", "1)o(n", "1)o1", "1)o1)", + "1)o1;", "1)o1U", "1)o1c", "1)o1o", @@ -1429,14 +1533,17 @@ static const char* sql_fingerprints[] = { "1)of(", "1)on", "1)on&", + "1)on;", "1)onc", "1)os", "1)os)", + "1)os;", "1)osU", "1)osc", "1)oso", "1)ov", "1)ov)", + "1)ov;", "1)ovU", "1)ovc", "1)ovo", @@ -1449,35 +1556,13 @@ static const char* sql_fingerprints[] = { "1,(Es", "1,(Ev", "1,(f(", - "1,1))", - "1,1),", - "1,1)o", - "1,1B1", - "1,1Bf", - "1,1Bs", - "1,1Bv", - "1,1UE", - "1,1of", - "1,1os", - "1,1ov", "1,f((", + "1,f()", "1,f(1", "1,f(f", "1,f(s", + "1,f(t", "1,f(v", - "1,s))", - "1,s),", - "1,s)o", - "1,sB1", - "1,sBf", - "1,sBs", - "1,sBv", - "1,sUE", - "1,so1", - "1,sof", - "1,son", - "1,sos", - "1,sov", "1,v))", "1,v),", "1,v)o", @@ -1532,11 +1617,10 @@ static const char* sql_fingerprints[] = { "1B1&f", "1B1&s", "1B1&v", - "1B1,1", "1B1,f", - "1B1,n", - "1B1,s", "1B1,v", + "1B1;", + "1B1;c", "1B1UE", "1B1c", "1B1k1", @@ -1552,11 +1636,12 @@ static const char* sql_fingerprints[] = { "1BE(s", "1BE(v", "1Bf((", + "1Bf()", "1Bf(1", "1Bf(f", "1Bf(s", "1Bf(v", - "1Bn,n", + "1BnUE", "1Bnk1", "1Bnkf", "1Bnks", @@ -1566,11 +1651,10 @@ static const char* sql_fingerprints[] = { "1Bs&f", "1Bs&s", "1Bs&v", - "1Bs,1", "1Bs,f", - "1Bs,n", - "1Bs,s", "1Bs,v", + "1Bs;", + "1Bs;c", "1BsUE", "1Bsc", "1Bsk1", @@ -1592,6 +1676,8 @@ static const char* sql_fingerprints[] = { "1Bv,n", "1Bv,s", "1Bv,v", + "1Bv;", + "1Bv;c", "1BvUE", "1Bvc", "1Bvk1", @@ -1608,14 +1694,18 @@ static const char* sql_fingerprints[] = { "1E1os", "1E1ov", "1EU1,", + "1EU1c", "1EU1o", "1EUEf", "1EUf(", "1EUs,", + "1EUsc", "1EUso", "1EUv,", + "1EUvc", "1EUvo", "1Ef((", + "1Ef()", "1Ef(1", "1Ef(f", "1Ef(s", @@ -1643,32 +1733,43 @@ static const char* sql_fingerprints[] = { "1U(En", "1U(Es", "1U(Ev", - "1U1,1", "1U1,f", - "1U1,s", "1U1,v", + "1U1c", "1U1of", "1U1os", "1U1ov", + "1U;", + "1U;c", "1UE", "1UE((", "1UE(1", "1UE(E", "1UE(f", + "1UE(n", "1UE(s", "1UE(v", "1UE1", "1UE1&", + "1UE1(", + "1UE1)", "1UE1,", + "1UE1;", + "1UE1U", "1UE1c", "1UE1f", "1UE1k", "1UE1n", "1UE1o", + "1UE1s", + "1UE1v", + "1UE;", + "1UE;c", "1UEc", "1UEf", "1UEf(", "1UEf,", + "1UEf;", "1UEfc", "1UEk1", "1UEkf", @@ -1676,42 +1777,55 @@ static const char* sql_fingerprints[] = { "1UEks", "1UEkv", "1UEn&", + "1UEn(", "1UEn,", "1UEn1", "1UEnc", "1UEnf", "1UEnk", + "1UEnn", "1UEno", "1UEns", "1UEok", "1UEs", "1UEs&", + "1UEs(", + "1UEs)", "1UEs,", + "1UEs1", + "1UEs;", + "1UEsU", "1UEsc", "1UEsf", "1UEsk", "1UEsn", "1UEso", + "1UEsv", "1UEv", "1UEv&", + "1UEv(", + "1UEv)", "1UEv,", + "1UEv;", + "1UEvU", "1UEvc", "1UEvf", "1UEvk", "1UEvn", "1UEvo", + "1UEvs", "1Uc", "1Uf((", + "1Uf()", "1Uf(1", "1Uf(f", "1Uf(s", "1Uf(v", "1Uk((", "1Uk(E", - "1Un,1", "1Un,f", - "1Un,s", "1Un,v", + "1Un1(", "1Un1,", "1Un1o", "1UnE1", @@ -1720,14 +1834,17 @@ static const char* sql_fingerprints[] = { "1UnEv", "1Unc", "1Unf(", + "1Uns(", + "1Uns,", + "1Unso", "1Uo((", "1Uo(E", "1Uon1", "1Uonf", - "1Us,1", + "1Uons", "1Us,f", - "1Us,s", "1Us,v", + "1Usc", "1Uso1", "1Usof", "1Uson", @@ -1737,6 +1854,7 @@ static const char* sql_fingerprints[] = { "1Uv,f", "1Uv,s", "1Uv,v", + "1Uvc", "1Uvo1", "1Uvof", "1Uvon", @@ -1751,7 +1869,13 @@ static const char* sql_fingerprints[] = { "1f((s", "1f((v", "1f())", + "1f()1", + "1f():", + "1f()f", "1f()k", + "1f()o", + "1f()s", + "1f()v", "1f(1)", "1f(1o", "1f(f(", @@ -1803,7 +1927,9 @@ static const char* sql_fingerprints[] = { "1k1&o", "1k1&s", "1k1&v", + "1k1;", "1k1;E", + "1k1;c", "1k1B1", "1k1Bf", "1k1Bs", @@ -1819,7 +1945,13 @@ static const char* sql_fingerprints[] = { "1k1of", "1k1os", "1k1ov", + "1kUE1", + "1kUEf", + "1kUEn", + "1kUEs", + "1kUEv", "1kf((", + "1kf()", "1kf(1", "1kf(f", "1kf(s", @@ -1832,7 +1964,9 @@ static const char* sql_fingerprints[] = { "1ks&o", "1ks&s", "1ks&v", + "1ks;", "1ks;E", + "1ks;c", "1ksB1", "1ksBf", "1ksBs", @@ -1857,7 +1991,9 @@ static const char* sql_fingerprints[] = { "1kv&o", "1kv&s", "1kv&v", + "1kv;", "1kv;E", + "1kv;c", "1kvB1", "1kvBf", "1kvBs", @@ -1881,6 +2017,8 @@ static const char* sql_fingerprints[] = { "1n))n", "1n)UE", "1n,f(", + "1n1;", + "1n1;c", "1n1c", "1n1of", "1n1os", @@ -1899,6 +2037,7 @@ static const char* sql_fingerprints[] = { "1nUEs", "1nUEv", "1nf((", + "1nf()", "1nf(1", "1nf(f", "1nf(s", @@ -1951,6 +2090,7 @@ static const char* sql_fingerprints[] = { "1of((", "1of()", "1of(1", + "1of(E", "1of(f", "1of(s", "1of(v", @@ -1997,9 +2137,7 @@ static const char* sql_fingerprints[] = { "1os)k", "1os)o", "1os,(", - "1os,1", "1os,f", - "1os,s", "1os,v", "1os1:", "1os1f", @@ -2007,7 +2145,9 @@ static const char* sql_fingerprints[] = { "1os1s", "1os1v", "1os:n", + "1os;", "1os;E", + "1os;c", "1os;n", "1osB1", "1osBE", @@ -2025,6 +2165,7 @@ static const char* sql_fingerprints[] = { "1osU", "1osU(", "1osU1", + "1osU;", "1osUE", "1osUc", "1osUf", @@ -2038,6 +2179,7 @@ static const char* sql_fingerprints[] = { "1osk(", "1osk)", "1osk1", + "1oskU", "1oskf", "1oskn", "1osks", @@ -2092,7 +2234,9 @@ static const char* sql_fingerprints[] = { "1ov,s", "1ov,v", "1ov:n", + "1ov;", "1ov;E", + "1ov;c", "1ov;n", "1ovB1", "1ovBE", @@ -2110,6 +2254,7 @@ static const char* sql_fingerprints[] = { "1ovU", "1ovU(", "1ovU1", + "1ovU;", "1ovUE", "1ovUc", "1ovUf", @@ -2123,6 +2268,7 @@ static const char* sql_fingerprints[] = { "1ovk(", "1ovk)", "1ovk1", + "1ovkU", "1ovkf", "1ovkn", "1ovks", @@ -2150,6 +2296,8 @@ static const char* sql_fingerprints[] = { "1ovso", "1ovsv", "1s1", + "1s1;", + "1s1;c", "1s1c", "1s1of", "1s1os", @@ -2162,6 +2310,7 @@ static const char* sql_fingerprints[] = { "1s:v:", "1s:vo", "1sf((", + "1sf()", "1sf(1", "1sf(f", "1sf(s", @@ -2181,6 +2330,8 @@ static const char* sql_fingerprints[] = { "1sovo", "1sovs", "1sv", + "1sv;", + "1sv;c", "1svc", "1svo1", "1svof", @@ -2195,6 +2346,7 @@ static const char* sql_fingerprints[] = { "1v:v:", "1v:vo", "1vf((", + "1vf()", "1vf(1", "1vf(f", "1vf(s", @@ -2214,6 +2366,8 @@ static const char* sql_fingerprints[] = { "1vovo", "1vovs", "1vs", + "1vs;", + "1vs;c", "1vsc", "1vso1", "1vsof", @@ -2257,6 +2411,7 @@ static const char* sql_fingerprints[] = { "E(1vo", "E(Ek(", "E(f((", + "E(f()", "E(f(1", "E(f(f", "E(f(s", @@ -2292,28 +2447,25 @@ static const char* sql_fingerprints[] = { "E1))", "E1)))", "E1))1", + "E1));", "E1))c", "E1))f", "E1))s", "E1))v", + "E1);", + "E1);c", "E1)c", "E1,((", "E1,(1", "E1,(f", "E1,(s", "E1,(v", - "E1,1,", - "E1,1k", - "E1,1o", "E1,f(", - "E1,n,", - "E1,s,", - "E1,sk", - "E1,so", "E1,v,", "E1,vk", "E1,vo", "E1f((", + "E1f()", "E1f(1", "E1f(f", "E1f(s", @@ -2323,11 +2475,17 @@ static const char* sql_fingerprints[] = { "E1k1k", "E1k1o", "E1kf(", + "E1kn", + "E1kn;", + "E1knc", "E1knk", "E1ksk", "E1kso", "E1kvk", "E1kvo", + "E1n;", + "E1n;c", + "E1nc", "E1o((", "E1o(1", "E1o(f", @@ -2341,16 +2499,45 @@ static const char* sql_fingerprints[] = { "E1ovf", "E1ovo", "E1ovs", + "E1s", + "E1s;", + "E1s;c", + "E1sc", + "E1so1", + "E1sof", + "E1son", + "E1sos", + "E1sov", + "E1v", + "E1v;", + "E1v;c", + "E1vc", + "E1vo1", + "E1vof", + "E1von", + "E1vos", + "E1vov", "EE(((", "EE((f", "EE(f(", "Ef(((", + "Ef(()", "Ef((1", "Ef((f", "Ef((n", "Ef((o", "Ef((s", "Ef((v", + "Ef()&", + "Ef())", + "Ef(),", + "Ef()1", + "Ef()f", + "Ef()k", + "Ef()n", + "Ef()o", + "Ef()s", + "Ef()v", "Ef(1)", "Ef(1,", "Ef(1o", @@ -2381,10 +2568,16 @@ static const char* sql_fingerprints[] = { "Ek1vk", "Ek1vo", "Ekf((", + "Ekf()", "Ekf(1", "Ekf(f", "Ekf(s", "Ekf(v", + "Ekn((", + "Ekn(1", + "Ekn(f", + "Ekn(s", + "Ekn(v", "Eks1f", "Eks1k", "Eks1o", @@ -2419,9 +2612,9 @@ static const char* sql_fingerprints[] = { "Ekvsk", "Ekvso", "En,f(", - "En,n,", "Enk((", "Enk(E", + "Enkn,", "Enknk", "Eo(((", "Eo((1", @@ -2443,28 +2636,32 @@ static const char* sql_fingerprints[] = { "Es))", "Es)))", "Es))1", + "Es));", "Es))c", "Es))f", "Es))s", "Es))v", + "Es);", + "Es);c", "Es)c", "Es,((", "Es,(1", "Es,(f", "Es,(s", "Es,(v", - "Es,1,", - "Es,1k", - "Es,1o", "Es,f(", - "Es,n,", - "Es,s,", - "Es,sk", - "Es,so", "Es,v,", "Es,vk", "Es,vo", + "Es1", + "Es1;", + "Es1;c", + "Es1c", + "Es1of", + "Es1os", + "Es1ov", "Esf((", + "Esf()", "Esf(1", "Esf(f", "Esf(s", @@ -2474,11 +2671,18 @@ static const char* sql_fingerprints[] = { "Esk1k", "Esk1o", "Eskf(", + "Eskn", + "Eskn;", + "Esknc", "Esknk", "Esksk", "Eskso", "Eskvk", "Eskvo", + "Esn", + "Esn;", + "Esn;c", + "Esnc", "Eso((", "Eso(1", "Eso(f", @@ -2498,16 +2702,28 @@ static const char* sql_fingerprints[] = { "Esovf", "Esovo", "Esovs", + "Esv", + "Esv;", + "Esv;c", + "Esvc", + "Esvo1", + "Esvof", + "Esvon", + "Esvos", + "Esvov", "Ev&((", "Ev&(E", "Ev)", "Ev))", "Ev)))", "Ev))1", + "Ev));", "Ev))c", "Ev))f", "Ev))s", "Ev))v", + "Ev);", + "Ev);c", "Ev)c", "Ev,((", "Ev,(1", @@ -2526,6 +2742,7 @@ static const char* sql_fingerprints[] = { "Ev,vk", "Ev,vo", "Evf((", + "Evf()", "Evf(1", "Evf(f", "Evf(s", @@ -2535,11 +2752,18 @@ static const char* sql_fingerprints[] = { "Evk1k", "Evk1o", "Evkf(", + "Evkn", + "Evkn;", + "Evknc", "Evknk", "Evksk", "Evkso", "Evkvk", "Evkvo", + "Evn", + "Evn;", + "Evn;c", + "Evnc", "Evo((", "Evo(1", "Evo(f", @@ -2559,6 +2783,15 @@ static const char* sql_fingerprints[] = { "Evovf", "Evovo", "Evovs", + "Evs", + "Evs;", + "Evs;c", + "Evsc", + "Evso1", + "Evsof", + "Evson", + "Evsos", + "Evsov", "U((((", "U(((E", "U((En", @@ -2568,32 +2801,34 @@ static const char* sql_fingerprints[] = { "UE((f", "UE((s", "UE((v", + "UE(1)", "UE(1,", "UE(1o", "UE(f(", + "UE(s)", "UE(s,", "UE(so", + "UE(v)", "UE(v,", "UE(vo", - "UE1,1", "UE1,f", - "UE1,n", - "UE1,s", "UE1,v", + "UE1c", + "UE1kn", "UE1of", "UE1os", "UE1ov", "UEf((", + "UEf()", "UEf(1", "UEf(f", "UEf(s", "UEf(v", "UEnkn", - "UEs,1", "UEs,f", - "UEs,n", - "UEs,s", "UEs,v", + "UEsc", + "UEskn", "UEso1", "UEsof", "UEson", @@ -2604,6 +2839,8 @@ static const char* sql_fingerprints[] = { "UEv,n", "UEv,s", "UEv,v", + "UEvc", + "UEvkn", "UEvo1", "UEvof", "UEvon", @@ -2621,6 +2858,7 @@ static const char* sql_fingerprints[] = { "Uf(so", "Uf(v)", "Uf(vo", + "X", "f((((", "f((()", "f(((1", @@ -2628,10 +2866,24 @@ static const char* sql_fingerprints[] = { "f(((f", "f(((k", "f(((s", + "f(((t", "f(((v", "f(()&", "f(())", + "f((),", + "f(()1", + "f(():", + "f(();", + "f(()B", + "f(()E", + "f(()U", + "f(()c", + "f(()f", + "f(()k", + "f(()n", "f(()o", + "f(()s", + "f(()v", "f((1)", "f((1,", "f((1o", @@ -2643,14 +2895,104 @@ static const char* sql_fingerprints[] = { "f((s)", "f((s,", "f((so", + "f((t)", + "f((t,", "f((v)", "f((v,", "f((vo", + "f()&(", + "f()&1", + "f()&E", "f()&f", + "f()&k", + "f()&n", + "f()&o", + "f()&s", + "f()&v", "f())&", "f()))", + "f()),", + "f());", + "f())B", + "f())E", + "f())U", + "f())k", "f())o", + "f(),(", + "f(),1", + "f(),f", + "f(),s", + "f(),v", + "f()1:", + "f()1f", + "f()1o", + "f()1s", + "f()1v", + "f():n", + "f();E", + "f();n", + "f()B1", + "f()BE", + "f()Bf", + "f()Bn", + "f()Bs", + "f()Bv", + "f()E1", + "f()EU", + "f()Ef", + "f()En", + "f()Eo", + "f()Es", + "f()Ev", + "f()U", + "f()U(", + "f()U1", + "f()U;", + "f()UE", + "f()Uc", + "f()Uf", + "f()Uk", + "f()Un", + "f()Uo", + "f()Us", + "f()Uv", + "f()c", + "f()f(", + "f()k(", + "f()k)", + "f()k1", + "f()kU", + "f()kf", + "f()kn", + "f()ks", + "f()kv", + "f()n&", + "f()n)", + "f()n,", + "f()n1", + "f()nE", + "f()nU", + "f()nf", + "f()no", + "f()o(", + "f()o1", + "f()oE", + "f()oU", + "f()of", "f()ok", + "f()on", + "f()os", + "f()ov", + "f()s1", + "f()s:", + "f()sf", + "f()so", + "f()sv", + "f()v:", + "f()vf", + "f()vo", + "f()vs", + "f(1)", "f(1)&", "f(1))", "f(1),", @@ -2667,10 +3009,7 @@ static const char* sql_fingerprints[] = { "f(1)o", "f(1)s", "f(1)v", - "f(1,1", "f(1,f", - "f(1,n", - "f(1,s", "f(1,v", "f(1of", "f(1os", @@ -2685,6 +3024,7 @@ static const char* sql_fingerprints[] = { "f(k()", "f(k,(", "f(k,f", + "f(s)", "f(s)&", "f(s))", "f(s),", @@ -2701,16 +3041,21 @@ static const char* sql_fingerprints[] = { "f(s)o", "f(s)s", "f(s)v", - "f(s,1", "f(s,f", - "f(s,n", - "f(s,s", "f(s,v", "f(so1", "f(sof", "f(son", "f(sos", "f(sov", + "f(t))", + "f(t),", + "f(t,(", + "f(t,1", + "f(t,f", + "f(t,s", + "f(t,v", + "f(v)", "f(v)&", "f(v))", "f(v),", @@ -2749,11 +3094,7 @@ static const char* sql_fingerprints[] = { "ff(so", "ff(v)", "ff(vo", - "k1,1c", - "k1,1o", "k1,f(", - "k1,sc", - "k1,so", "k1,vc", "k1,vo", "k1of(", @@ -2765,10 +3106,14 @@ static const char* sql_fingerprints[] = { "k1ovo", "k1ovs", "kf(((", + "kf(()", "kf((1", "kf((f", "kf((s", "kf((v", + "kf())", + "kf(),", + "kf()o", "kf(1)", "kf(1o", "kf(f(", @@ -2776,11 +3121,7 @@ static const char* sql_fingerprints[] = { "kf(so", "kf(v)", "kf(vo", - "ks,1c", - "ks,1o", "ks,f(", - "ks,sc", - "ks,so", "ks,vc", "ks,vo", "kso1f", @@ -2845,6 +3186,8 @@ static const char* sql_fingerprints[] = { "n&(vo", "n&1", "n&1&n", + "n&1;", + "n&1;c", "n&1Bf", "n&1UE", "n&1c", @@ -2860,6 +3203,7 @@ static const char* sql_fingerprints[] = { "n&E(s", "n&E(v", "n&f((", + "n&f()", "n&f(1", "n&f(f", "n&f(s", @@ -2869,17 +3213,22 @@ static const char* sql_fingerprints[] = { "n&nos", "n&nov", "n&o1", + "n&o1;", "n&o1c", "n&o1o", "n&of(", "n&os", + "n&os;", "n&osc", "n&oso", "n&ov", + "n&ov;", "n&ovc", "n&ovo", "n&s", "n&s&n", + "n&s;", + "n&s;c", "n&sBf", "n&sUE", "n&sc", @@ -2893,6 +3242,8 @@ static const char* sql_fingerprints[] = { "n&sov", "n&v", "n&v&n", + "n&v;", + "n&v;c", "n&vBf", "n&vUE", "n&vc", @@ -2908,17 +3259,20 @@ static const char* sql_fingerprints[] = { "n)&(E", "n)&1", "n)&1&", + "n)&1;", "n)&1c", "n)&1f", "n)&1o", "n)&f(", "n)&s", "n)&s&", + "n)&s;", "n)&sc", "n)&sf", "n)&so", "n)&v", "n)&v&", + "n)&v;", "n)&vc", "n)&vf", "n)&vo", @@ -3012,27 +3366,12 @@ static const char* sql_fingerprints[] = { "n,(Es", "n,(Ev", "n,(f(", - "n,1,1", - "n,1,f", - "n,1,s", - "n,1,v", - "n,1of", - "n,1os", - "n,1ov", "n,f((", + "n,f()", "n,f(1", "n,f(f", "n,f(s", "n,f(v", - "n,s,1", - "n,s,f", - "n,s,s", - "n,s,v", - "n,so1", - "n,sof", - "n,son", - "n,sos", - "n,sov", "n,v,1", "n,v,f", "n,v,s", @@ -3071,6 +3410,7 @@ static const char* sql_fingerprints[] = { "nB1os", "nB1ov", "nBf((", + "nBf()", "nBf(1", "nBf(f", "nBf(s", @@ -3092,6 +3432,7 @@ static const char* sql_fingerprints[] = { "nE1os", "nE1ov", "nEf((", + "nEf()", "nEf(1", "nEf(f", "nEf(s", @@ -3119,14 +3460,18 @@ static const char* sql_fingerprints[] = { "nUE(E", "nUE1,", "nUE1c", + "nUE1k", "nUE1o", "nUEf(", "nUEn,", + "nUEnk", "nUEs,", "nUEsc", + "nUEsk", "nUEso", "nUEv,", "nUEvc", + "nUEvk", "nUEvo", "nc", "nf(((", @@ -3157,6 +3502,7 @@ static const char* sql_fingerprints[] = { "nk1os", "nk1ov", "nkf((", + "nkf()", "nkf(1", "nkf(f", "nkf(s", @@ -3207,6 +3553,7 @@ static const char* sql_fingerprints[] = { "noE(s", "noE(v", "nof((", + "nof()", "nof(1", "nof(f", "nof(s", @@ -3258,6 +3605,7 @@ static const char* sql_fingerprints[] = { "s&(1)", "s&(1,", "s&(1o", + "s&(E(", "s&(E1", "s&(Ef", "s&(Ek", @@ -3279,6 +3627,8 @@ static const char* sql_fingerprints[] = { "s&1&n", "s&1&s", "s&1&v", + "s&1;", + "s&1;c", "s&1B1", "s&1Bf", "s&1Bs", @@ -3286,6 +3636,7 @@ static const char* sql_fingerprints[] = { "s&1En", "s&1U", "s&1U(", + "s&1U;", "s&1UE", "s&1Uc", "s&1c", @@ -3348,6 +3699,8 @@ static const char* sql_fingerprints[] = { "s&n&n", "s&n&s", "s&n&v", + "s&n;", + "s&n;c", "s&nc", "s&nk1", "s&nkf", @@ -3362,14 +3715,17 @@ static const char* sql_fingerprints[] = { "s&o(s", "s&o(v", "s&o1", + "s&o1;", "s&o1c", "s&o1o", "s&of(", "s&oko", "s&os", + "s&os;", "s&osc", "s&oso", "s&ov", + "s&ov;", "s&ovc", "s&ovo", "s&s", @@ -3380,6 +3736,8 @@ static const char* sql_fingerprints[] = { "s&s&s", "s&s&v", "s&s1o", + "s&s;", + "s&s;c", "s&sB1", "s&sBf", "s&sBs", @@ -3387,6 +3745,7 @@ static const char* sql_fingerprints[] = { "s&sEn", "s&sU", "s&sU(", + "s&sU;", "s&sUE", "s&sUc", "s&sc", @@ -3411,6 +3770,8 @@ static const char* sql_fingerprints[] = { "s&v&n", "s&v&s", "s&v&v", + "s&v;", + "s&v;c", "s&vB1", "s&vBf", "s&vBs", @@ -3418,6 +3779,7 @@ static const char* sql_fingerprints[] = { "s&vEn", "s&vU", "s&vU(", + "s&vU;", "s&vUE", "s&vUc", "s&vc", @@ -3447,10 +3809,12 @@ static const char* sql_fingerprints[] = { "s)&(1", "s)&(E", "s)&(f", + "s)&(n", "s)&(s", "s)&(v", "s)&1", "s)&1&", + "s)&1;", "s)&1B", "s)&1U", "s)&1c", @@ -3460,6 +3824,7 @@ static const char* sql_fingerprints[] = { "s)&o(", "s)&s", "s)&s&", + "s)&s;", "s)&sB", "s)&sU", "s)&sc", @@ -3467,6 +3832,7 @@ static const char* sql_fingerprints[] = { "s)&so", "s)&v", "s)&v&", + "s)&v;", "s)&vB", "s)&vU", "s)&vc", @@ -3526,15 +3892,18 @@ static const char* sql_fingerprints[] = { "s);Ev", "s)B1", "s)B1&", + "s)B1;", "s)B1c", "s)B1o", "s)Bf(", "s)Bs", "s)Bs&", + "s)Bs;", "s)Bsc", "s)Bso", "s)Bv", "s)Bv&", + "s)Bv;", "s)Bvc", "s)Bvo", "s)E1c", @@ -3574,9 +3943,11 @@ static const char* sql_fingerprints[] = { "s)kvU", "s)kvo", "s)o((", + "s)o(E", "s)o(n", "s)o1", "s)o1)", + "s)o1;", "s)o1U", "s)o1c", "s)o1o", @@ -3584,14 +3955,17 @@ static const char* sql_fingerprints[] = { "s)of(", "s)on", "s)on&", + "s)on;", "s)onc", "s)os", "s)os)", + "s)os;", "s)osU", "s)osc", "s)oso", "s)ov", "s)ov)", + "s)ov;", "s)ovU", "s)ovc", "s)ovo", @@ -3604,35 +3978,13 @@ static const char* sql_fingerprints[] = { "s,(Es", "s,(Ev", "s,(f(", - "s,1))", - "s,1),", - "s,1)o", - "s,1B1", - "s,1Bf", - "s,1Bs", - "s,1Bv", - "s,1UE", - "s,1of", - "s,1os", - "s,1ov", "s,f((", + "s,f()", "s,f(1", "s,f(f", "s,f(s", + "s,f(t", "s,f(v", - "s,s))", - "s,s),", - "s,s)o", - "s,sB1", - "s,sBf", - "s,sBs", - "s,sBv", - "s,sUE", - "s,so1", - "s,sof", - "s,son", - "s,sos", - "s,sov", "s,v))", "s,v),", "s,v)o", @@ -3654,6 +4006,7 @@ static const char* sql_fingerprints[] = { "s1:v:", "s1:vo", "s1f((", + "s1f()", "s1f(1", "s1f(f", "s1f(s", @@ -3666,6 +4019,8 @@ static const char* sql_fingerprints[] = { "s1ovf", "s1ovo", "s1ovs", + "s1s;", + "s1s;c", "s1sc", "s1so1", "s1sof", @@ -3673,6 +4028,8 @@ static const char* sql_fingerprints[] = { "s1sos", "s1sov", "s1v", + "s1v;", + "s1v;c", "s1vc", "s1vo1", "s1vof", @@ -3720,11 +4077,10 @@ static const char* sql_fingerprints[] = { "sB1&f", "sB1&s", "sB1&v", - "sB1,1", "sB1,f", - "sB1,n", - "sB1,s", "sB1,v", + "sB1;", + "sB1;c", "sB1UE", "sB1c", "sB1k1", @@ -3740,11 +4096,12 @@ static const char* sql_fingerprints[] = { "sBE(s", "sBE(v", "sBf((", + "sBf()", "sBf(1", "sBf(f", "sBf(s", "sBf(v", - "sBn,n", + "sBnUE", "sBnk1", "sBnkf", "sBnks", @@ -3754,11 +4111,10 @@ static const char* sql_fingerprints[] = { "sBs&f", "sBs&s", "sBs&v", - "sBs,1", "sBs,f", - "sBs,n", - "sBs,s", "sBs,v", + "sBs;", + "sBs;c", "sBsUE", "sBsc", "sBsk1", @@ -3780,6 +4136,8 @@ static const char* sql_fingerprints[] = { "sBv,n", "sBv,s", "sBv,v", + "sBv;", + "sBv;c", "sBvUE", "sBvc", "sBvk1", @@ -3796,14 +4154,18 @@ static const char* sql_fingerprints[] = { "sE1os", "sE1ov", "sEU1,", + "sEU1c", "sEU1o", "sEUEf", "sEUf(", "sEUs,", + "sEUsc", "sEUso", "sEUv,", + "sEUvc", "sEUvo", "sEf((", + "sEf()", "sEf(1", "sEf(f", "sEf(s", @@ -3831,32 +4193,43 @@ static const char* sql_fingerprints[] = { "sU(En", "sU(Es", "sU(Ev", - "sU1,1", "sU1,f", - "sU1,s", "sU1,v", + "sU1c", "sU1of", "sU1os", "sU1ov", + "sU;", + "sU;c", "sUE", "sUE((", "sUE(1", "sUE(E", "sUE(f", + "sUE(n", "sUE(s", "sUE(v", "sUE1", "sUE1&", + "sUE1(", + "sUE1)", "sUE1,", + "sUE1;", + "sUE1U", "sUE1c", "sUE1f", "sUE1k", "sUE1n", "sUE1o", + "sUE1s", + "sUE1v", + "sUE;", + "sUE;c", "sUEc", "sUEf", "sUEf(", "sUEf,", + "sUEf;", "sUEfc", "sUEk1", "sUEkf", @@ -3864,41 +4237,55 @@ static const char* sql_fingerprints[] = { "sUEks", "sUEkv", "sUEn&", + "sUEn(", "sUEn,", "sUEn1", "sUEnc", "sUEnf", "sUEnk", + "sUEnn", "sUEno", + "sUEns", "sUEok", "sUEs", "sUEs&", + "sUEs(", + "sUEs)", "sUEs,", + "sUEs1", + "sUEs;", + "sUEsU", "sUEsc", "sUEsf", "sUEsk", "sUEsn", "sUEso", + "sUEsv", "sUEv", "sUEv&", + "sUEv(", + "sUEv)", "sUEv,", + "sUEv;", + "sUEvU", "sUEvc", "sUEvf", "sUEvk", "sUEvn", "sUEvo", + "sUEvs", "sUc", "sUf((", + "sUf()", "sUf(1", "sUf(f", "sUf(s", "sUf(v", "sUk((", "sUk(E", - "sUn,1", "sUn,f", - "sUn,s", "sUn,v", + "sUn1(", "sUn1,", "sUn1o", "sUnE1", @@ -3907,14 +4294,17 @@ static const char* sql_fingerprints[] = { "sUnEv", "sUnc", "sUnf(", + "sUns(", + "sUns,", + "sUnso", "sUo((", "sUo(E", "sUon1", "sUonf", - "sUs,1", + "sUons", "sUs,f", - "sUs,s", "sUs,v", + "sUsc", "sUso1", "sUsof", "sUson", @@ -3924,6 +4314,7 @@ static const char* sql_fingerprints[] = { "sUv,f", "sUv,s", "sUv,v", + "sUvc", "sUvo1", "sUvof", "sUvon", @@ -3938,7 +4329,13 @@ static const char* sql_fingerprints[] = { "sf((s", "sf((v", "sf())", + "sf()1", + "sf():", + "sf()f", "sf()k", + "sf()o", + "sf()s", + "sf()v", "sf(1)", "sf(1o", "sf(f(", @@ -3990,7 +4387,9 @@ static const char* sql_fingerprints[] = { "sk1&o", "sk1&s", "sk1&v", + "sk1;", "sk1;E", + "sk1;c", "sk1B1", "sk1Bf", "sk1Bs", @@ -4006,7 +4405,13 @@ static const char* sql_fingerprints[] = { "sk1of", "sk1os", "sk1ov", + "skUE1", + "skUEf", + "skUEn", + "skUEs", + "skUEv", "skf((", + "skf()", "skf(1", "skf(f", "skf(s", @@ -4019,7 +4424,9 @@ static const char* sql_fingerprints[] = { "sks&o", "sks&s", "sks&v", + "sks;", "sks;E", + "sks;c", "sksB1", "sksBf", "sksBs", @@ -4044,7 +4451,9 @@ static const char* sql_fingerprints[] = { "skv&o", "skv&s", "skv&v", + "skv;", "skv;E", + "skv;c", "skvB1", "skvBf", "skvBs", @@ -4069,6 +4478,8 @@ static const char* sql_fingerprints[] = { "sn)UE", "sn,f(", "sn1", + "sn1;", + "sn1;c", "sn1c", "sn1of", "sn1os", @@ -4087,6 +4498,7 @@ static const char* sql_fingerprints[] = { "snUEs", "snUEv", "snf((", + "snf()", "snf(1", "snf(f", "snf(s", @@ -4152,12 +4564,12 @@ static const char* sql_fingerprints[] = { "so1)k", "so1)o", "so1,(", - "so1,1", "so1,f", - "so1,s", "so1,v", "so1:n", + "so1;", "so1;E", + "so1;c", "so1;n", "so1B1", "so1BE", @@ -4175,6 +4587,7 @@ static const char* sql_fingerprints[] = { "so1U", "so1U(", "so1U1", + "so1U;", "so1UE", "so1Uc", "so1Uf", @@ -4188,6 +4601,7 @@ static const char* sql_fingerprints[] = { "so1k(", "so1k)", "so1k1", + "so1kU", "so1kf", "so1kn", "so1ks", @@ -4226,6 +4640,7 @@ static const char* sql_fingerprints[] = { "sof((", "sof()", "sof(1", + "sof(E", "sof(f", "sof(s", "sof(v", @@ -4272,9 +4687,7 @@ static const char* sql_fingerprints[] = { "son)k", "son)o", "son,(", - "son,1", "son,f", - "son,s", "son,v", "son1:", "son1f", @@ -4300,6 +4713,7 @@ static const char* sql_fingerprints[] = { "sonU", "sonU(", "sonU1", + "sonU;", "sonUE", "sonUc", "sonUf", @@ -4313,6 +4727,7 @@ static const char* sql_fingerprints[] = { "sonk(", "sonk)", "sonk1", + "sonkU", "sonkf", "sonkn", "sonks", @@ -4347,9 +4762,7 @@ static const char* sql_fingerprints[] = { "sos)k", "sos)o", "sos,(", - "sos,1", "sos,f", - "sos,s", "sos,v", "sos1:", "sos1f", @@ -4357,7 +4770,9 @@ static const char* sql_fingerprints[] = { "sos1s", "sos1v", "sos:n", + "sos;", "sos;E", + "sos;c", "sos;n", "sosB1", "sosBE", @@ -4375,6 +4790,7 @@ static const char* sql_fingerprints[] = { "sosU", "sosU(", "sosU1", + "sosU;", "sosUE", "sosUc", "sosUf", @@ -4388,6 +4804,7 @@ static const char* sql_fingerprints[] = { "sosk(", "sosk)", "sosk1", + "soskU", "soskf", "soskn", "sosks", @@ -4442,7 +4859,9 @@ static const char* sql_fingerprints[] = { "sov,s", "sov,v", "sov:n", + "sov;", "sov;E", + "sov;c", "sov;n", "sovB1", "sovBE", @@ -4460,6 +4879,7 @@ static const char* sql_fingerprints[] = { "sovU", "sovU(", "sovU1", + "sovU;", "sovUE", "sovUc", "sovUf", @@ -4473,6 +4893,7 @@ static const char* sql_fingerprints[] = { "sovk(", "sovk)", "sovk1", + "sovkU", "sovkf", "sovkn", "sovks", @@ -4507,6 +4928,7 @@ static const char* sql_fingerprints[] = { "sv:v:", "sv:vo", "svf((", + "svf()", "svf(1", "svf(f", "svf(s", @@ -4526,6 +4948,8 @@ static const char* sql_fingerprints[] = { "svovo", "svovs", "svs", + "svs;", + "svs;c", "svsc", "svso1", "svsof", @@ -4541,6 +4965,7 @@ static const char* sql_fingerprints[] = { "v&(1)", "v&(1,", "v&(1o", + "v&(E(", "v&(E1", "v&(Ef", "v&(Ek", @@ -4562,6 +4987,8 @@ static const char* sql_fingerprints[] = { "v&1&n", "v&1&s", "v&1&v", + "v&1;", + "v&1;c", "v&1B1", "v&1Bf", "v&1Bs", @@ -4569,6 +4996,7 @@ static const char* sql_fingerprints[] = { "v&1En", "v&1U", "v&1U(", + "v&1U;", "v&1UE", "v&1Uc", "v&1c", @@ -4631,6 +5059,8 @@ static const char* sql_fingerprints[] = { "v&n&n", "v&n&s", "v&n&v", + "v&n;", + "v&n;c", "v&nc", "v&nk1", "v&nkf", @@ -4645,14 +5075,17 @@ static const char* sql_fingerprints[] = { "v&o(s", "v&o(v", "v&o1", + "v&o1;", "v&o1c", "v&o1o", "v&of(", "v&oko", "v&os", + "v&os;", "v&osc", "v&oso", "v&ov", + "v&ov;", "v&ovc", "v&ovo", "v&s", @@ -4663,6 +5096,8 @@ static const char* sql_fingerprints[] = { "v&s&s", "v&s&v", "v&s1o", + "v&s;", + "v&s;c", "v&sB1", "v&sBf", "v&sBs", @@ -4670,6 +5105,7 @@ static const char* sql_fingerprints[] = { "v&sEn", "v&sU", "v&sU(", + "v&sU;", "v&sUE", "v&sUc", "v&sc", @@ -4694,6 +5130,8 @@ static const char* sql_fingerprints[] = { "v&v&n", "v&v&s", "v&v&v", + "v&v;", + "v&v;c", "v&vB1", "v&vBf", "v&vBs", @@ -4701,6 +5139,7 @@ static const char* sql_fingerprints[] = { "v&vEn", "v&vU", "v&vU(", + "v&vU;", "v&vUE", "v&vUc", "v&vc", @@ -4730,10 +5169,12 @@ static const char* sql_fingerprints[] = { "v)&(1", "v)&(E", "v)&(f", + "v)&(n", "v)&(s", "v)&(v", "v)&1", "v)&1&", + "v)&1;", "v)&1B", "v)&1U", "v)&1c", @@ -4743,6 +5184,7 @@ static const char* sql_fingerprints[] = { "v)&o(", "v)&s", "v)&s&", + "v)&s;", "v)&sB", "v)&sU", "v)&sc", @@ -4750,6 +5192,7 @@ static const char* sql_fingerprints[] = { "v)&so", "v)&v", "v)&v&", + "v)&v;", "v)&vB", "v)&vU", "v)&vc", @@ -4809,15 +5252,18 @@ static const char* sql_fingerprints[] = { "v);Ev", "v)B1", "v)B1&", + "v)B1;", "v)B1c", "v)B1o", "v)Bf(", "v)Bs", "v)Bs&", + "v)Bs;", "v)Bsc", "v)Bso", "v)Bv", "v)Bv&", + "v)Bv;", "v)Bvc", "v)Bvo", "v)E1c", @@ -4857,9 +5303,11 @@ static const char* sql_fingerprints[] = { "v)kvU", "v)kvo", "v)o((", + "v)o(E", "v)o(n", "v)o1", "v)o1)", + "v)o1;", "v)o1U", "v)o1c", "v)o1o", @@ -4867,14 +5315,17 @@ static const char* sql_fingerprints[] = { "v)of(", "v)on", "v)on&", + "v)on;", "v)onc", "v)os", "v)os)", + "v)os;", "v)osU", "v)osc", "v)oso", "v)ov", "v)ov)", + "v)ov;", "v)ovU", "v)ovc", "v)ovo", @@ -4899,9 +5350,11 @@ static const char* sql_fingerprints[] = { "v,1os", "v,1ov", "v,f((", + "v,f()", "v,f(1", "v,f(f", "v,f(s", + "v,f(t", "v,f(v", "v,s))", "v,s),", @@ -4970,11 +5423,10 @@ static const char* sql_fingerprints[] = { "vB1&f", "vB1&s", "vB1&v", - "vB1,1", "vB1,f", - "vB1,n", - "vB1,s", "vB1,v", + "vB1;", + "vB1;c", "vB1UE", "vB1c", "vB1k1", @@ -4990,11 +5442,12 @@ static const char* sql_fingerprints[] = { "vBE(s", "vBE(v", "vBf((", + "vBf()", "vBf(1", "vBf(f", "vBf(s", "vBf(v", - "vBn,n", + "vBnUE", "vBnk1", "vBnkf", "vBnks", @@ -5004,11 +5457,10 @@ static const char* sql_fingerprints[] = { "vBs&f", "vBs&s", "vBs&v", - "vBs,1", "vBs,f", - "vBs,n", - "vBs,s", "vBs,v", + "vBs;", + "vBs;c", "vBsUE", "vBsc", "vBsk1", @@ -5030,6 +5482,8 @@ static const char* sql_fingerprints[] = { "vBv,n", "vBv,s", "vBv,v", + "vBv;", + "vBv;c", "vBvUE", "vBvc", "vBvk1", @@ -5046,14 +5500,18 @@ static const char* sql_fingerprints[] = { "vE1os", "vE1ov", "vEU1,", + "vEU1c", "vEU1o", "vEUEf", "vEUf(", "vEUs,", + "vEUsc", "vEUso", "vEUv,", + "vEUvc", "vEUvo", "vEf((", + "vEf()", "vEf(1", "vEf(f", "vEf(s", @@ -5081,32 +5539,43 @@ static const char* sql_fingerprints[] = { "vU(En", "vU(Es", "vU(Ev", - "vU1,1", "vU1,f", - "vU1,s", "vU1,v", + "vU1c", "vU1of", "vU1os", "vU1ov", + "vU;", + "vU;c", "vUE", "vUE((", "vUE(1", "vUE(E", "vUE(f", + "vUE(n", "vUE(s", "vUE(v", "vUE1", "vUE1&", + "vUE1(", + "vUE1)", "vUE1,", + "vUE1;", + "vUE1U", "vUE1c", "vUE1f", "vUE1k", "vUE1n", "vUE1o", + "vUE1s", + "vUE1v", + "vUE;", + "vUE;c", "vUEc", "vUEf", "vUEf(", "vUEf,", + "vUEf;", "vUEfc", "vUEk1", "vUEkf", @@ -5114,41 +5583,55 @@ static const char* sql_fingerprints[] = { "vUEks", "vUEkv", "vUEn&", + "vUEn(", "vUEn,", "vUEn1", "vUEnc", "vUEnf", "vUEnk", + "vUEnn", "vUEno", + "vUEns", "vUEok", "vUEs", "vUEs&", + "vUEs(", + "vUEs)", "vUEs,", + "vUEs1", + "vUEs;", + "vUEsU", "vUEsc", "vUEsf", "vUEsk", "vUEsn", "vUEso", + "vUEsv", "vUEv", "vUEv&", + "vUEv(", + "vUEv)", "vUEv,", + "vUEv;", + "vUEvU", "vUEvc", "vUEvf", "vUEvk", "vUEvn", "vUEvo", + "vUEvs", "vUc", "vUf((", + "vUf()", "vUf(1", "vUf(f", "vUf(s", "vUf(v", "vUk((", "vUk(E", - "vUn,1", "vUn,f", - "vUn,s", "vUn,v", + "vUn1(", "vUn1,", "vUn1o", "vUnE1", @@ -5157,14 +5640,17 @@ static const char* sql_fingerprints[] = { "vUnEv", "vUnc", "vUnf(", + "vUns(", + "vUns,", + "vUnso", "vUo((", "vUo(E", "vUon1", "vUonf", - "vUs,1", + "vUons", "vUs,f", - "vUs,s", "vUs,v", + "vUsc", "vUso1", "vUsof", "vUson", @@ -5174,6 +5660,7 @@ static const char* sql_fingerprints[] = { "vUv,f", "vUv,s", "vUv,v", + "vUvc", "vUvo1", "vUvof", "vUvon", @@ -5188,7 +5675,13 @@ static const char* sql_fingerprints[] = { "vf((s", "vf((v", "vf())", + "vf()1", + "vf():", + "vf()f", "vf()k", + "vf()o", + "vf()s", + "vf()v", "vf(1)", "vf(1o", "vf(f(", @@ -5240,7 +5733,9 @@ static const char* sql_fingerprints[] = { "vk1&o", "vk1&s", "vk1&v", + "vk1;", "vk1;E", + "vk1;c", "vk1B1", "vk1Bf", "vk1Bs", @@ -5256,7 +5751,13 @@ static const char* sql_fingerprints[] = { "vk1of", "vk1os", "vk1ov", + "vkUE1", + "vkUEf", + "vkUEn", + "vkUEs", + "vkUEv", "vkf((", + "vkf()", "vkf(1", "vkf(f", "vkf(s", @@ -5269,7 +5770,9 @@ static const char* sql_fingerprints[] = { "vks&o", "vks&s", "vks&v", + "vks;", "vks;E", + "vks;c", "vksB1", "vksBf", "vksBs", @@ -5294,7 +5797,9 @@ static const char* sql_fingerprints[] = { "vkv&o", "vkv&s", "vkv&v", + "vkv;", "vkv;E", + "vkv;c", "vkvB1", "vkvBf", "vkvBs", @@ -5319,6 +5824,8 @@ static const char* sql_fingerprints[] = { "vn)UE", "vn,f(", "vn1", + "vn1;", + "vn1;c", "vn1c", "vn1of", "vn1os", @@ -5337,6 +5844,7 @@ static const char* sql_fingerprints[] = { "vnUEs", "vnUEv", "vnf((", + "vnf()", "vnf(1", "vnf(f", "vnf(s", @@ -5402,12 +5910,12 @@ static const char* sql_fingerprints[] = { "vo1)k", "vo1)o", "vo1,(", - "vo1,1", "vo1,f", - "vo1,s", "vo1,v", "vo1:n", + "vo1;", "vo1;E", + "vo1;c", "vo1;n", "vo1B1", "vo1BE", @@ -5425,6 +5933,7 @@ static const char* sql_fingerprints[] = { "vo1U", "vo1U(", "vo1U1", + "vo1U;", "vo1UE", "vo1Uc", "vo1Uf", @@ -5438,6 +5947,7 @@ static const char* sql_fingerprints[] = { "vo1k(", "vo1k)", "vo1k1", + "vo1kU", "vo1kf", "vo1kn", "vo1ks", @@ -5477,6 +5987,7 @@ static const char* sql_fingerprints[] = { "vof((", "vof()", "vof(1", + "vof(E", "vof(f", "vof(s", "vof(v", @@ -5523,9 +6034,7 @@ static const char* sql_fingerprints[] = { "von)k", "von)o", "von,(", - "von,1", "von,f", - "von,s", "von,v", "von1:", "von1f", @@ -5551,6 +6060,7 @@ static const char* sql_fingerprints[] = { "vonU", "vonU(", "vonU1", + "vonU;", "vonUE", "vonUc", "vonUf", @@ -5564,6 +6074,7 @@ static const char* sql_fingerprints[] = { "vonk(", "vonk)", "vonk1", + "vonkU", "vonkf", "vonkn", "vonks", @@ -5599,9 +6110,7 @@ static const char* sql_fingerprints[] = { "vos)k", "vos)o", "vos,(", - "vos,1", "vos,f", - "vos,s", "vos,v", "vos1:", "vos1f", @@ -5609,7 +6118,9 @@ static const char* sql_fingerprints[] = { "vos1s", "vos1v", "vos:n", + "vos;", "vos;E", + "vos;c", "vos;n", "vosB1", "vosBE", @@ -5627,6 +6138,7 @@ static const char* sql_fingerprints[] = { "vosU", "vosU(", "vosU1", + "vosU;", "vosUE", "vosUc", "vosUf", @@ -5640,6 +6152,7 @@ static const char* sql_fingerprints[] = { "vosk(", "vosk)", "vosk1", + "voskU", "voskf", "voskn", "vosks", @@ -5694,7 +6207,9 @@ static const char* sql_fingerprints[] = { "vov,s", "vov,v", "vov:n", + "vov;", "vov;E", + "vov;c", "vov;n", "vovB1", "vovBE", @@ -5712,6 +6227,7 @@ static const char* sql_fingerprints[] = { "vovU", "vovU(", "vovU1", + "vovU;", "vovUE", "vovUc", "vovUf", @@ -5725,6 +6241,7 @@ static const char* sql_fingerprints[] = { "vovk(", "vovk)", "vovk1", + "vovkU", "vovkf", "vovkn", "vovks", @@ -5752,6 +6269,6 @@ static const char* sql_fingerprints[] = { "vovso", "vovsv", }; -static const size_t sqli_fingerprints_sz = 4719; +static const size_t sqli_fingerprints_sz = 5157; #endif diff --git a/apache2/libinjection/sqlparse.c b/apache2/libinjection/sqlparse.c deleted file mode 100644 index 43c51343..00000000 --- a/apache2/libinjection/sqlparse.c +++ /dev/null @@ -1,1340 +0,0 @@ -/** - * Copyright 2012,2013 Nick Galbreath - * nickg@client9.com - * BSD License -- see COPYING.txt for details - * - * (setq-default indent-tabs-mode nil) - * (setq c-default-style "k&r" - * c-basic-offset 4) - * indent -kr -nut - */ - -#include -#include -#include -#include -#include - -#ifndef TRUE -#define TRUE 1 -#endif -#ifndef FALSE -#define FALSE 0 -#endif - -#if 0 -#define FOLD_DEBUG printf("%d: Fold state = %d, current=%c, last=%c\n", __LINE__, sf->fold_state, current->type, last->type == CHAR_NULL ? '~': last->type) -#else -#define FOLD_DEBUG -#endif - -/* order is important here */ -#include "sqlparse_private.h" -#include "sqlparse_data.h" - -/* memchr2 finds a string of 2 characters inside another string - * This a specialized version of "memmem" or "memchr". - * 'memmem' doesn't exist on all platforms - * - * Porting notes: this is just a special version of - * astring.find("AB") - * - */ -const char * -memchr2(const char *haystack, size_t haystack_len, char c0, char c1) -{ - const char *cur = haystack; - const char *last = haystack + haystack_len - 1; - - if (haystack_len < 2) { - return NULL; - } - if (c0 == c1) { - return NULL; - } - - while (cur < last) { - if (cur[0] == c0) { - if (cur[1] == c1) { - return cur; - } else { - cur += 2; - } - } else { - cur += 1; - } - } - - return NULL; -} - -/** Find largest string containing certain characters. - * - * C Standard library 'strspn' only works for 'c-strings' (null terminated) - * This works on arbitrary length. - * - * Porting notes: - * if accept is 'ABC', then this function would be similar to - * a_regexp.match(a_str, '[ABC]*'), - */ -size_t strlenspn(const char *s, size_t len, const char *accept) -{ - size_t i; - for (i = 0; i < len; ++i) { - /* likely we can do better by inlining this function - * but this works for now - */ - if (strchr(accept, s[i]) == NULL) { - return i; - } - } - return len; -} - -/* - * ASCII case insenstive compare only! - */ -int cstrcasecmp(const char *a, const char *b) -{ - int ca, cb; - - do { - ca = *a++ & 0xff; - cb = *b++ & 0xff; - if (ca >= 'a' && ca <= 'z') - ca -= 0x20; - if (cb >= 'a' && cb <= 'z') - cb -= 0x20; - } while (ca == cb && ca != '\0'); - - return ca - cb; -} - -/** - * Case insentive string compare. - * Here only to make code more readable - */ -int streq(const char *a, const char *b) -{ - return cstrcasecmp(a, b) == 0; -} - -/* - * Case-sensitive binary search. - * - */ -int bsearch_cstr(const char *key, const char *base[], size_t nmemb) -{ - int left = 0; - int right = (int) nmemb - 1; - - while (left <= right) { - int pos = (left + right) / 2; - int cmp = strcmp(base[pos], key); - if (cmp == 0) { - return TRUE; - } else if (cmp < 0) { - left = pos + 1; - } else { - right = pos - 1; - } - } - return FALSE; -} - -/* - * Case-insensitive binary search - */ -int bsearch_cstrcase(const char *key, const char *base[], size_t nmemb) -{ - int left = 0; - int right = (int) nmemb - 1; - - while (left <= right) { - int pos = (left + right) / 2; - int cmp = cstrcasecmp(base[pos], key); - if (cmp == 0) { - return TRUE; - } else if (cmp < 0) { - left = pos + 1; - } else { - right = pos - 1; - } - } - return FALSE; -} - -/** - * - * - * - * Porting Notes: - * given a mapping/hash of string to char - * this is just - * mapping[key.upper()] - */ -char bsearch_keyword_type(const char *key, const keyword_t * keywords, - size_t numb) -{ - int left = 0; - int right = (int) numb - 1; - - while (left <= right) { - int pos = (left + right) / 2; - int cmp = cstrcasecmp(keywords[pos].word, key); - if (cmp == 0) { - return keywords[pos].type; - } else if (cmp < 0) { - left = pos + 1; - } else { - right = pos - 1; - } - } - return CHAR_NULL; -} - -/* st_token methods - * - * The folow just manipulates the stoken_t type - * - * - */ - -void st_clear(stoken_t * st) -{ - st->type = CHAR_NULL; - st->str_open = CHAR_NULL; - st->str_close = CHAR_NULL; - st->val[0] = CHAR_NULL; -} - -int st_is_empty(const stoken_t * st) -{ - return st->type == CHAR_NULL; -} - -void st_assign_char(stoken_t * st, const char stype, const char value) -{ - st->type = stype; - st->val[0] = value; - st->val[1] = CHAR_NULL; -} - -void st_assign(stoken_t * st, const char stype, const char *value, - size_t len) -{ - size_t last = len < ST_MAX_SIZE ? len : (ST_MAX_SIZE - 1); - st->type = stype; - memcpy(st->val, value, last); - st->val[last] = CHAR_NULL; -} - -void st_copy(stoken_t * dest, const stoken_t * src) -{ - memcpy(dest, src, sizeof(stoken_t)); -} - -int st_is_multiword_start(const stoken_t * st) -{ - return bsearch_cstrcase(st->val, - multikeywords_start, - multikeywords_start_sz); -} - -int st_is_unary_op(const stoken_t * st) -{ - return (st->type == 'o' && !(strcmp(st->val, "+") && - strcmp(st->val, "-") && - strcmp(st->val, "!") && - strcmp(st->val, "!!") && - cstrcasecmp(st->val, "NOT") && - strcmp(st->val, "~"))); -} - -int st_is_arith_op(const stoken_t * st) -{ - return (st->type == 'o' && !(strcmp(st->val, "-") && - strcmp(st->val, "+") && - strcmp(st->val, "~") && - strcmp(st->val, "!") && - strcmp(st->val, "/") && - strcmp(st->val, "%") && - strcmp(st->val, "*") && - strcmp(st->val, "|") && - strcmp(st->val, "&") && - cstrcasecmp(st->val, "MOD") && - cstrcasecmp(st->val, "DIV"))); -} - -/* Parsers - * - * - */ - - -size_t parse_white(sfilter * sf) -{ - return sf->pos + 1; -} - -size_t parse_operator1(sfilter * sf) -{ - stoken_t *current = &sf->syntax_current; - const char *cs = sf->s; - size_t pos = sf->pos; - - st_assign_char(current, 'o', cs[pos]); - return pos + 1; -} - -size_t parse_other(sfilter * sf) -{ - stoken_t *current = &sf->syntax_current; - const char *cs = sf->s; - size_t pos = sf->pos; - - st_assign_char(current, '?', cs[pos]); - return pos + 1; -} - -size_t parse_char(sfilter * sf) -{ - stoken_t *current = &sf->syntax_current; - const char *cs = sf->s; - size_t pos = sf->pos; - - st_assign_char(current, cs[pos], cs[pos]); - return pos + 1; -} - -size_t parse_eol_comment(sfilter * sf) -{ - stoken_t *current = &sf->syntax_current; - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - - const char *endpos = - (const char *) memchr((const void *) (cs + pos), '\n', slen - pos); - if (endpos == NULL) { - st_assign(current, 'c', cs + pos, slen - pos); - return slen; - } else { - st_assign(current, 'c', cs + pos, endpos - cs - pos); - return (endpos - cs) + 1; - } -} - -size_t parse_dash(sfilter * sf) -{ - stoken_t *current = &sf->syntax_current; - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - - - size_t pos1 = pos + 1; - if (pos1 < slen && cs[pos1] == '-') { - return parse_eol_comment(sf); - } else { - st_assign_char(current, 'o', '-'); - return pos1; - } -} - -size_t is_mysql_comment(const char *cs, const size_t len, size_t pos) -{ - size_t i; - - if (pos + 2 >= len) { - return 0; - } - if (cs[pos + 2] != '!') { - return 0; - } - /* - * this is a mysql comment - * got "/x!" - */ - if (pos + 3 >= len) { - return 3; - } - - if (!isdigit(cs[pos + 3])) { - return 3; - } - /* - * handle odd case of /x!0SELECT - */ - if (!isdigit(cs[pos + 4])) { - return 4; - } - - if (pos + 7 >= len) { - return 4; - } - - for (i = pos + 5; i <= pos + 7; ++i) { - if (!isdigit(cs[i])) { - return 3; - } - } - return 8; -} - -size_t parse_slash(sfilter * sf) -{ - stoken_t *current = &sf->syntax_current; - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - const char* cur = cs + pos; - size_t inc; - - size_t pos1 = pos + 1; - if (pos1 == slen || cs[pos1] != '*') { - return parse_operator1(sf); - } - - inc = is_mysql_comment(cs, slen, pos); - if (inc == 0) { - - /* - * skip over initial '/x' - */ - const char *ptr = memchr2(cur + 2, slen - (pos + 2), '*', '/'); - if (ptr == NULL) { - /* - * unterminated comment - */ - st_assign(current, 'c', cs + pos, slen - pos); - return slen; - } else { - /* - * postgresql allows nested comments which makes - * this is incompatible with parsing so - * if we find a '/x' inside the coment, then - * make a new token. - */ - char ctype = 'c'; - const size_t clen = (ptr + 2) - (cur); - if (memchr2(cur + 2, ptr - (cur + 1), '/', '*') != NULL) { - ctype = 'X'; - } - st_assign(current, ctype, cs + pos, clen); - - return pos + clen; - } - } else { - /* - * MySQL Comment - */ - sf->in_comment = TRUE; - st_clear(current); - return pos + inc; - } -} - -size_t parse_backslash(sfilter * sf) -{ - stoken_t *current = &sf->syntax_current; - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - - /* - * Weird MySQL alias for NULL, "\N" (capital N only) - */ - if (pos + 1 < slen && cs[pos + 1] == 'N') { - st_assign(current, '1', "NULL", 4); - return pos + 2; - } else { - return parse_other(sf); - } -} - -/** Is input a 2-char operator? - * - */ -int is_operator2(const char *key) -{ - return bsearch_cstr(key, operators2, operators2_sz); -} - -size_t parse_operator2(sfilter * sf) -{ - stoken_t *current = &sf->syntax_current; - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - char op2[3]; - - if (pos + 1 >= slen) { - return parse_operator1(sf); - } - - op2[0] = cs[pos]; - op2[1] = cs[pos + 1]; - op2[2] = CHAR_NULL; - - /* - * Special Hack for MYSQL style comments - * instead of turning: - * /x! FOO x/ into FOO by rewriting the string, we - * turn it into FOO x/ and ignore the ending comment - */ - if (sf->in_comment && op2[0] == '*' && op2[1] == '/') { - sf->in_comment = FALSE; - st_clear(current); - return pos + 2; - } else if (pos + 2 < slen && op2[0] == '<' && op2[1] == '=' - && cs[pos + 2] == '>') { - /* - * special 3-char operator - */ - st_assign(current, 'o', "<=>", 3); - return pos + 3; - } else if (is_operator2(op2)) { - if (streq(op2, "&&") || streq(op2, "||")) { - st_assign(current, '&', op2, 2); - } else { - /* - * normal 2 char operator - */ - st_assign(current, 'o', op2, 2); - } - return pos + 2; - } else { - /* - * must be a single char operator - */ - return parse_operator1(sf); - } -} - -size_t parse_string_core(const char *cs, const size_t len, size_t pos, - stoken_t * st, char delim, size_t offset) -{ - /* - * offset is to skip the perhaps first quote char - */ - const char *qpos = - (const char *) memchr((const void *) (cs + pos + offset), delim, - len - pos - offset); - - /* - * then keep string open/close info - */ - if (offset == 1) { - /* - * this is real quote - */ - st->str_open = delim; - } else { - /* - * this was a simulated quote - */ - st->str_open = CHAR_NULL; - } - - while (TRUE) { - if (qpos == NULL) { - /* - * string ended with no trailing quote - * assign what we have - */ - st_assign(st, 's', cs + pos + offset, len - pos - offset); - st->str_close = CHAR_NULL; - return len; - } else if (*(qpos - 1) != '\\') { - /* - * ending quote is not escaped.. copy and end - */ - st_assign(st, 's', cs + pos + offset, - qpos - (cs + pos + offset)); - st->str_close = delim; - return qpos - cs + 1; - } else { - qpos = - (const char *) memchr((const void *) (qpos + 1), delim, - (cs + len) - (qpos + 1)); - } - } -} - -/** - * Used when first char is a ' or " - */ -size_t parse_string(sfilter * sf) -{ - stoken_t *current = &sf->syntax_current; - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - - /* - * assert cs[pos] == single or double quote - */ - return parse_string_core(cs, slen, pos, current, cs[pos], 1); -} - -size_t parse_word(sfilter * sf) -{ - stoken_t *current = &sf->syntax_current; - const char *cs = sf->s; - size_t pos = sf->pos; - char *dot; - char ch; - size_t slen = - strlenspn(cs + pos, sf->slen - pos, - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$."); - - st_assign(current, 'n', cs + pos, slen); - - dot = strchr(current->val, '.'); - if (dot != NULL) { - *dot = '\0'; - - ch = bsearch_keyword_type(current->val, sql_keywords, - sql_keywords_sz); - if (ch == 'k' || ch == 'o') { - /* - * we got something like "SELECT.1" - */ - current->type = ch; - return pos + strlen(current->val); - } else { - /* - * something else, put back dot - */ - *dot = '.'; - } - } - - /* - * do normal lookup with word including '.' - */ - if (slen < ST_MAX_SIZE) { - ch = bsearch_keyword_type(current->val, sql_keywords, - sql_keywords_sz); - if (ch == CHAR_NULL) { - ch = 'n'; - } - current->type = ch; - } - return pos + slen; -} - -size_t parse_var(sfilter * sf) -{ - stoken_t *current = &sf->syntax_current; - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - size_t pos1 = pos + 1; - size_t xlen; - - /* - * move past optional other '@' - */ - if (pos1 < slen && cs[pos1] == '@') { - pos1 += 1; - } - - xlen = strlenspn(cs + pos1, slen - pos1, - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.$"); - if (xlen == 0) { - st_assign(current, 'v', cs + pos, (pos1 - pos)); - return pos1; - } else { - st_assign(current, 'v', cs + pos, xlen + (pos1 - pos)); - return pos1 + xlen; - } -} - -size_t parse_money(sfilter *sf) -{ - stoken_t *current = &sf->syntax_current; - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - size_t xlen; - - /* - * $1,000.00 or $1.000,00 ok! - * This also parses $....,,,111 but that's ok - */ - xlen = strlenspn(cs + pos + 1, slen - pos - 1, "0123456789.,"); - if (xlen == 0) { - /* - * just ignore '$' - */ - return pos + 1; - } else { - st_assign(current, '1', cs + pos, 1 + xlen); - return pos + 1 + xlen; - } -} - -size_t parse_number(sfilter * sf) -{ - stoken_t *current = &sf->syntax_current; - const char *cs = sf->s; - const size_t slen = sf->slen; - size_t pos = sf->pos; - size_t xlen; - size_t start; - - if (pos + 1 < slen && cs[pos] == '0' && (cs[pos + 1] == 'X' || cs[pos + 1] == 'x')) { - /* - * TBD compare if isxdigit - */ - xlen = - strlenspn(cs + pos + 2, slen - pos - 2, "0123456789ABCDEFabcdef"); - if (xlen == 0) { - st_assign(current, 'n', "0X", 2); - return pos + 2; - } else { - st_assign(current, '1', cs + pos, 2 + xlen); - return pos + 2 + xlen; - } - } - - start = pos; - while (pos < slen && isdigit(cs[pos])) { - pos += 1; - } - if (pos < slen && cs[pos] == '.') { - pos += 1; - while (pos < slen && isdigit(cs[pos])) { - pos += 1; - } - if (pos - start == 1) { - st_assign_char(current, 'n', '.'); - return pos; - } - } - - if (pos < slen) { - if (cs[pos] == 'E' || cs[pos] == 'e') { - pos += 1; - if (pos < slen && (cs[pos] == '+' || cs[pos] == '-')) { - pos += 1; - } - while (pos < slen && isdigit(cs[pos])) { - pos += 1; - } - } else if (isalpha(cs[pos])) { - /* - * oh no, we have something like '6FOO' - * use microsoft style parsing and take just - * the number part and leave the rest to be - * parsed later - */ - st_assign(current, '1', cs + start, pos - start); - return pos; - } - } - - st_assign(current, '1', cs + start, pos - start); - return pos; -} - -int parse_token(sfilter * sf) -{ - stoken_t *current = &sf->syntax_current; - const char *s = sf->s; - const size_t slen = sf->slen; - size_t *pos = &sf->pos; - pt2Function fnptr; - - st_clear(current); - - /* - * if we are at beginning of string - * and in single-quote or double quote mode - * then pretend the input starts with a quote - */ - if (*pos == 0 && sf->delim != CHAR_NULL) { - *pos = parse_string_core(s, slen, 0, current, sf->delim, 0); - return TRUE; - } - - while (*pos < slen) { - /* - * get current character - */ - const int ch = (int) (s[*pos]); - - /* - * if not ascii, then continue... - * actually probably need to just assuming - * it's a string - */ - if (ch < 0 || ch > 127) { - *pos += 1; - continue; - } - - /* - * look up the parser, and call it - * - * Porting Note: this is mapping of char to function - * charparsers[ch]() - */ - fnptr = char_parse_map[ch]; - *pos = (*fnptr) (sf); - - /* - * - */ - if (current->type != CHAR_NULL) { - return TRUE; - } - } - return FALSE; -} - -void sfilter_reset(sfilter * sf, const char *s, size_t len) -{ - memset(sf, 0, sizeof(sfilter)); - sf->s = s; - sf->slen = len; -} - -int syntax_merge_words(stoken_t * a, stoken_t * b) -{ - size_t sz1; - size_t sz2; - size_t sz3; - char tmp[ST_MAX_SIZE]; - char ch; - - if (! - (a->type == 'k' || a->type == 'n' || a->type == 'o' - || a->type == 'U')) { - return FALSE; - } - - sz1 = strlen(a->val); - sz2 = strlen(b->val); - sz3 = sz1 + sz2 + 1; - if (sz3 >= ST_MAX_SIZE) { - return FALSE; - } - /* - * oddly annoying last.val + ' ' + current.val - */ - memcpy(tmp, a->val, sz1); - tmp[sz1] = ' '; - memcpy(tmp + sz1 + 1, b->val, sz2); - tmp[sz3] = CHAR_NULL; - - ch = bsearch_keyword_type(tmp, multikeywords, multikeywords_sz); - if (ch != CHAR_NULL) { - /* - * -1, don't copy the null byte - */ - st_assign(a, ch, tmp, sz3); - return TRUE; - } else { - return FALSE; - } -} - -/* This does some simple syntax cleanup based on the token - * - * - */ -int sqli_tokenize(sfilter * sf, stoken_t * sout) -{ - stoken_t *last = &sf->syntax_last; - stoken_t *current = &sf->syntax_current; - - while (parse_token(sf)) { - char ttype = current->type; - - /* - * TBD: hmm forgot logic here. - */ - if (ttype == 'c') { - st_copy(&sf->syntax_comment, current); - continue; - } - st_clear(&sf->syntax_comment); - - /* - * If we don't have a saved token, and we have - * a string: save it. if the next token is also a string - * then merge them. e.g. "A" "B" in SQL is actually "AB" - * a n/k/U/o type: save since next token my be merged together - * for example: "LEFT" + "JOIN" = "LEFT JOIN" - * a o/& type: TBD need to review. - * - */ - if (last->type == CHAR_NULL) { - switch (ttype) { - - /* - * items that have special needs - */ - case 's': - st_copy(last, current); - continue; - case 'n': - case 'k': - case 'U': - case '&': - case 'o': - if (st_is_multiword_start(current)) { - st_copy(last, current); - continue; - } else if (current->type == 'o' || current->type == '&') { - /* } else if (st_is_unary_op(current)) { */ - st_copy(last, current); - continue; - } else { - /* - * copy to out - */ - st_copy(sout, current); - return TRUE; - } - default: - /* - * copy to out - */ - st_copy(sout, current); - return TRUE; - } - } - /* - * We have a saved token - */ - - switch (ttype) { - case 's': - if (last->type == 's') { - /* - * "FOO" "BAR" == "FOO" (skip second string) - */ - continue; - } else { - st_copy(sout, last); - st_copy(last, current); - return TRUE; - } - break; - - case 'o': - /* - * first case to handle "IS" + "NOT" - */ - if (syntax_merge_words(last, current)) { - continue; - } else if (st_is_unary_op(current) - && (last->type == 'o' || last->type == '&' - || last->type == 'U')) { - /* - * if an operator is followed by a unary operator, skip it. - * 1, + ==> "+" is not unary, it's arithmetic - * AND, + ==> "+" is unary - */ - continue; - } else { - /* - * no match - */ - st_copy(sout, last); - st_copy(last, current); - return TRUE; - } - break; - - case 'n': - case 'k': - if (syntax_merge_words(last, current)) { - continue; - } else { - /* - * total no match - */ - st_copy(sout, last); - st_copy(last, current); - return TRUE; - } - break; - - default: - /* - * fix up for ambigous "IN" - * handle case where IN is typically a function - * but used in compound "IN BOOLEAN MODE" jive - */ - if (last->type == 'n' && !cstrcasecmp(last->val, "IN")) { - st_copy(last, current); - st_assign(sout, 'f', "IN", 2); - return TRUE; - } else { - /* - * no match at all - */ - st_copy(sout, last); - st_copy(last, current); - return TRUE; - } - break; - } - } - - /* - * final cleanup - */ - if (last->type) { - st_copy(sout, last); - st_clear(last); - return TRUE; - } else if (sf->syntax_comment.type) { - /* - * TBD - */ - st_copy(sout, &sf->syntax_comment); - st_clear(&sf->syntax_comment); - return TRUE; - } else { - return FALSE; - } -} - -/* - * My apologies, this code is a mess - */ -int filter_fold(sfilter * sf, stoken_t * sout) -{ - stoken_t *last = &sf->fold_last; - stoken_t *current = &sf->fold_current; - - if (sf->fold_state == 4 && !st_is_empty(last)) { - st_copy(sout, last); - sf->fold_state = 2; - st_clear(last); - return FALSE; - } - - while (sqli_tokenize(sf, current)) { - /* - * 0 = start of statement - * skip ( and unary ops - */ - if (sf->fold_state == 0) { - if (current->type == '(') { - continue; - } - if (st_is_unary_op(current)) { - continue; - } - sf->fold_state = 1; - } - - if (st_is_empty(last)) { - FOLD_DEBUG; - if (current->type == '1' || current->type == 'n' - || current->type == '(') { - sf->fold_state = 2; - st_copy(last, current); - } - st_copy(sout, current); - return FALSE; - } else if (last->type == '(' && st_is_unary_op(current)) { - /* - * similar to beginning of statement - * an opening '(' resets state, and we should skip all - * unary operators - */ - continue; - } else if (last->type == '(' && current->type == '(') { - /* if we get another '(' after another - * emit 1, but keep state - */ - st_copy(sout, current); - return FALSE; - } else if ((last->type == '1' || last->type == 'n') - && st_is_arith_op(current)) { - FOLD_DEBUG; - st_copy(last, current); - } else if (last->type == 'o' - && (current->type == '1' || current->type == 'n')) { - FOLD_DEBUG; - st_copy(last, current); - } else { - if (sf->fold_state == 2) { - if (last->type != '1' && last->type != '(' - && last->type != 'n') { - FOLD_DEBUG; - st_copy(sout, last); - st_copy(last, current); - sf->fold_state = 4; - } else { - FOLD_DEBUG; - st_copy(sout, current); - st_clear(last); - } - return FALSE; - } else { - if (last->type == 'o') { - st_copy(sout, last); - st_copy(last, current); - sf->fold_state = 4; - } else { - sf->fold_state = 2; - st_copy(sout, current); - st_clear(last); - } - return FALSE; - } - } - } - - if (!st_is_empty(last)) { - if (st_is_arith_op(last)) { - st_copy(sout, last); - st_clear(last); - return FALSE; - } else { - st_clear(last); - } - } - - /* - * all done: nothing more to parse - */ - return TRUE; -} - -/* secondary api: detects SQLi in a string, GIVEN a context. - * - * A context can be: - * * CHAR_NULL (\0), process as is - * * CHAR_SINGLE ('), process pretending input started with a - * single quote. - * * CHAR_DOUBLE ("), process pretending input started with a - * double quote. - * - */ -int is_string_sqli(sfilter * sql_state, const char *s, size_t slen, - const char delim, ptr_fingerprints_fn fn) -{ - int tlen = 0; - char ch; - int patmatch; - int all_done; - - sfilter_reset(sql_state, s, slen); - sql_state->delim = delim; - - while (tlen < MAX_TOKENS) { - all_done = filter_fold(sql_state, &(sql_state->tokenvec[tlen])); - if (all_done) { - break; - } - - sql_state->pat[tlen] = sql_state->tokenvec[tlen].type; - tlen += 1; - } - - /* - * make the fingerprint pattern a c-string (null delimited) - */ - sql_state->pat[tlen] = CHAR_NULL; - - /* - * check for 'X' in pattern - * this means parsing could not be done - * accurately due to pgsql's double comments - * or other syntax that isn't consistent - * should be very rare false positive - */ - if (strchr(sql_state->pat, 'X')) { - return TRUE; - } - - patmatch = fn(sql_state->pat); - - /* - * No match. - * - * Set sql_state->reason to current line number - * only for debugging purposes. - */ - if (!patmatch) { - sql_state->reason = __LINE__; - return FALSE; - } - - /* - * We got a SQLi match - * This next part just helps reduce false positives. - * - */ - switch (tlen) { - case 2:{ - /* - * if 'comment' is '#' ignore.. too many FP - */ - if (sql_state->tokenvec[1].val[0] == '#') { - sql_state->reason = __LINE__; - return FALSE; - } - - /* - * for fingerprint like 'nc', only comments of /x are treated - * as SQL... ending comments of "--" and "#" are not sqli - */ - if (sql_state->tokenvec[0].type == 'n' && - sql_state->tokenvec[1].type == 'c' && - sql_state->tokenvec[1].val[0] != '/') { - sql_state->reason = __LINE__; - return FALSE; - } - - /** - * there are some odd base64-looking query string values - * 1234-ABCDEFEhfhihwuefi-- - * which evaluate to "1c"... these are not SQLi - * but 1234-- probably is. - * Make sure the "1" in "1c" is actually a true decimal number - * - * Need to check -original- string since the folding step - * may have merged tokens, e.g. "1+FOO" is folded into "1" - */ - if (sql_state->tokenvec[0].type == '1'&& sql_state->tokenvec[1].type == 'c') { - /* - * we check that next character after the number is either whitespace, - * or '/' or a '-' ==> sqli. - */ - ch = sql_state->s[strlen(sql_state->tokenvec[0].val)]; - if ( ch <= 32 ) { - /* next char was whitespace,e.g. "1234 --" - * this isn't exactly correct.. ideally we should skip over all whitespace - * but this seems to be ok for now - */ - return TRUE; - } - if (ch == '/' && sql_state->s[strlen(sql_state->tokenvec[0].val) + 1] == '*') { - return TRUE; - } - if (ch == '-' && sql_state->s[strlen(sql_state->tokenvec[0].val) + 1] == '-') { - return TRUE; - } - - sql_state->reason = __LINE__; - return FALSE; - } - - /* - * detect obvious sqli scans.. many people put '--' in plain text - * so only detect if input ends with '--', e.g. 1-- but not 1-- foo - */ - if ((strlen(sql_state->tokenvec[1].val) > 2) - && sql_state->tokenvec[1].val[0] == '-') { - sql_state->reason = __LINE__; - return FALSE; - } - - break; - } /* case 2 */ - case 3:{ - /* - * ...foo' + 'bar... - * no opening quote, no closing quote - * and each string has data - */ - if (streq(sql_state->pat, "sos") - || streq(sql_state->pat, "s&s")) { - if ((sql_state->tokenvec[0].str_open == CHAR_NULL) - && (sql_state->tokenvec[2].str_close == CHAR_NULL)) { - /* - * if ....foo" + "bar.... - */ - return TRUE; - } else { - /* - * not sqli - */ - sql_state->reason = __LINE__; - return FALSE; - } - break; - } - } /* case 3 */ - case 5: { - if (streq(sql_state->pat, "sosos")) { - if (sql_state->tokenvec[0].str_open == CHAR_NULL) { - /* - * if ....foo" + "bar.... - */ - return TRUE; - } else { - /* - * not sqli - */ - sql_state->reason = __LINE__; - return FALSE; - } - break; - } - } /* case 5 */ - } /* end switch */ - - return TRUE; -} - -/** Main API, detects SQLi in an input. - * - * - */ -int is_sqli(sfilter * sql_state, const char *s, size_t slen, - ptr_fingerprints_fn fn) -{ - - /* - * no input? not sqli - */ - if (slen == 0) { - return FALSE; - } - - /* - * test input "as-is" - */ - if (is_string_sqli(sql_state, s, slen, CHAR_NULL, fn)) { - return TRUE; - } - - /* - * if input has a single_quote, then - * test as if input was actually ' - * example: if input if "1' = 1", then pretend it's - * "'1' = 1" - * Porting Notes: example the same as doing - * is_string_sqli(sql_state, "'" + s, slen+1, NULL, fn) - * - */ - if (memchr(s, CHAR_SINGLE, slen) - && is_string_sqli(sql_state, s, slen, CHAR_SINGLE, fn)) { - return TRUE; - } - - /* - * same as above but with a double-quote " - */ - if (memchr(s, CHAR_DOUBLE, slen) - && is_string_sqli(sql_state, s, slen, CHAR_DOUBLE, fn)) { - return TRUE; - } - - /* - * Hurray, input is not SQLi - */ - return FALSE; -}