/** * Copyright 2012-2016 Nick Galbreath * nickg@client9.com * BSD License -- see `COPYING.txt` for details * * https://libinjection.client9.com/ * */ #ifndef LIBINJECTION_SQLI_H #define LIBINJECTION_SQLI_H #ifdef __cplusplus extern "C" { #endif /* * Pull in size_t */ #include enum sqli_flags { FLAG_NONE = 0 , FLAG_QUOTE_NONE = 1 /* 1 << 0 */ , FLAG_QUOTE_SINGLE = 2 /* 1 << 1 */ , FLAG_QUOTE_DOUBLE = 4 /* 1 << 2 */ , FLAG_SQL_ANSI = 8 /* 1 << 3 */ , FLAG_SQL_MYSQL = 16 /* 1 << 4 */ }; enum lookup_type { LOOKUP_WORD = 1 , LOOKUP_TYPE = 2 , LOOKUP_OPERATOR = 3 , LOOKUP_FINGERPRINT = 4 }; struct libinjection_sqli_token { #ifdef SWIG %immutable; #endif /* * position and length of token * in original string */ size_t pos; size_t len; /* count: * in type 'v', used for number of opening '@' * but maybe used in other contexts */ int count; char type; char str_open; char str_close; char val[32]; }; typedef struct libinjection_sqli_token stoken_t; /** * Pointer to function, takes c-string input, * returns '\0' for no match, else a char */ struct libinjection_sqli_state; typedef char (*ptr_lookup_fn)(struct libinjection_sqli_state*, int lookuptype, const char* word, size_t len); struct libinjection_sqli_state { #ifdef SWIG %immutable; #endif /* * input, does not need to be null terminated. * it is also not modified. */ const char *s; /* * input length */ size_t slen; /* * How to lookup a word or fingerprint */ ptr_lookup_fn lookup; void* userdata; /* * */ int flags; /* * pos is the index in the string during tokenization */ size_t pos; #ifndef SWIG /* for SWIG.. don't use this.. use functional API instead */ /* MAX TOKENS + 1 since we use one extra token * to determine the type of the previous token */ struct libinjection_sqli_token tokenvec[8]; #endif /* * Pointer to token position in tokenvec, above */ struct libinjection_sqli_token *current; /* * fingerprint pattern c-string * +1 for ending null * Minimum of 8 bytes to add gcc's -fstack-protector to work */ char fingerprint[8]; /* * Line number of code that said decided if the input was SQLi or * not. Most of the time it's line that said "it's not a matching * fingerprint" but there is other logic that sometimes approves * an input. This is only useful for debugging. * */ 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; /* * c-style comments found /x .. x/ */ int stats_comment_c; /* '#' operators or MySQL EOL comments found * */ int stats_comment_hash; /* * number of tokens folded away */ int stats_folds; /* * total tokens processed */ int stats_tokens; }; typedef struct libinjection_sqli_state sfilter; struct libinjection_sqli_token* libinjection_sqli_get_token( struct libinjection_sqli_state* sqlistate, int i); /* * Version info. * * This is moved into a function to allow SWIG and other auto-generated * binding to not be modified during minor release changes. We change * change the version number in the c source file, and not regenerated * the binding * * See python's normalized version * http://www.python.org/dev/peps/pep-0386/#normalizedversion */ const char* libinjection_version(void); /** * */ void libinjection_sqli_init(struct libinjection_sqli_state* sql_state, const char* s, size_t slen, int flags); /** * Main API: tests for SQLi in three possible contexts, no quotes, * single quote and double quote * * \param sql_state core data structure * * \return 1 (true) if SQLi, 0 (false) if benign */ int libinjection_is_sqli(struct libinjection_sqli_state* sql_state); /* FOR HACKERS ONLY * provides deep hooks into the decision making process */ void libinjection_sqli_callback(struct libinjection_sqli_state* sql_state, ptr_lookup_fn fn, void* userdata); /* * Resets state, but keeps initial string and callbacks */ void libinjection_sqli_reset(struct libinjection_sqli_state* sql_state, int flags); /** * */ /** * This detects SQLi in a single context, mostly useful for custom * logic and debugging. * * \param sql_state Main data structure * \param flags flags to adjust parsing * * \returns a pointer to sfilter.fingerprint as convenience * do not free! * */ const char* libinjection_sqli_fingerprint(struct libinjection_sqli_state* sql_state, int flags); /** * The default "word" to token-type or fingerprint function. This * uses a ASCII case-insensitive binary tree. */ char libinjection_sqli_lookup_word(struct libinjection_sqli_state* sql_state, int lookup_type, const char* s, size_t slen); /* Streaming tokenization interface. * * sql_state->current is updated with the current token. * * \returns 1, has a token, keep going, or 0 no tokens * */ int libinjection_sqli_tokenize(struct libinjection_sqli_state * sql_state); /** * parses and folds input, up to 5 tokens * */ int libinjection_sqli_fold(struct libinjection_sqli_state * sql_state); /** The built-in default function to match fingerprints * and do false negative/positive analysis. This calls the following * two functions. With this, you over-ride one part or the other. * * return libinjection_sqli_blacklist(sql_state) && * libinjection_sqli_not_whitelist(sql_state); * * \param sql_state should be filled out after libinjection_sqli_fingerprint is called */ int libinjection_sqli_check_fingerprint(struct libinjection_sqli_state * sql_state); /* Given a pattern determine if it's a SQLi pattern. * * \return TRUE if sqli, false otherwise */ int libinjection_sqli_blacklist(struct libinjection_sqli_state* 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(struct libinjection_sqli_state * sql_state); #ifdef __cplusplus } #endif #endif /* LIBINJECTION_SQLI_H */