Added query operation type requirements on where clauses and fixed a bug with regex rules.
This commit is contained in:
@ -1284,6 +1284,7 @@ char* skygw_get_affected_fields(GWBUF* buf)
|
|||||||
bool skygw_query_has_clause(GWBUF* buf)
|
bool skygw_query_has_clause(GWBUF* buf)
|
||||||
{
|
{
|
||||||
LEX* lex;
|
LEX* lex;
|
||||||
|
SELECT_LEX* current;
|
||||||
bool clause = false;
|
bool clause = false;
|
||||||
|
|
||||||
if(!query_is_parsed(buf)){
|
if(!query_is_parsed(buf)){
|
||||||
@ -1294,15 +1295,15 @@ bool skygw_query_has_clause(GWBUF* buf)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
lex->current_select = lex->all_selects_list;
|
current = lex->all_selects_list;
|
||||||
|
|
||||||
while(lex->current_select)
|
while(current)
|
||||||
{
|
{
|
||||||
if(lex->current_select->where || lex->current_select->having){
|
if(current->where || current->having){
|
||||||
clause = true;
|
clause = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
lex->current_select = lex->current_select->next_select_in_list();
|
current = current->next_select_in_list();
|
||||||
}
|
}
|
||||||
return clause;
|
return clause;
|
||||||
}
|
}
|
||||||
|
@ -61,10 +61,18 @@ typedef enum {
|
|||||||
} skygw_query_type_t;
|
} skygw_query_type_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
QUERY_OP_UNDEFINED, QUERY_OP_SELECT, QUERY_OP_CREATE_TABLE, QUERY_OP_CREATE_INDEX,
|
QUERY_OP_UNDEFINED = 0,
|
||||||
QUERY_OP_ALTER_TABLE, QUERY_OP_UPDATE, QUERY_OP_INSERT, QUERY_OP_INSERT_SELECT,
|
QUERY_OP_SELECT = 1,
|
||||||
QUERY_OP_DELETE, QUERY_OP_TRUNCATE, QUERY_OP_DROP_TABLE, QUERY_OP_DROP_INDEX,
|
QUERY_OP_UPDATE = (1 << 1),
|
||||||
|
QUERY_OP_INSERT = (1 << 2),
|
||||||
|
QUERY_OP_DELETE = (1 << 3),
|
||||||
|
QUERY_OP_INSERT_SELECT = (1 << 4),
|
||||||
|
QUERY_OP_TRUNCATE = (1 << 5),
|
||||||
|
QUERY_OP_ALTER_TABLE = (1 << 6),
|
||||||
|
QUERY_OP_CREATE_TABLE = (1 << 7),
|
||||||
|
QUERY_OP_CREATE_INDEX = (1 << 8),
|
||||||
|
QUERY_OP_DROP_TABLE = (1 << 9),
|
||||||
|
QUERY_OP_DROP_INDEX = (1 << 10)
|
||||||
}skygw_query_op_t;
|
}skygw_query_op_t;
|
||||||
|
|
||||||
typedef struct parsing_info_st {
|
typedef struct parsing_info_st {
|
||||||
|
@ -36,9 +36,9 @@
|
|||||||
*
|
*
|
||||||
* users John@% Jane@192.168.0.1 match any rules block_salary
|
* users John@% Jane@192.168.0.1 match any rules block_salary
|
||||||
*
|
*
|
||||||
* Rule syntax TODO: query type restrictions, update the documentation
|
* Rule syntax TODO: update the documentation
|
||||||
*
|
*
|
||||||
* rule NAME deny|allow [wildcard | columns VALUE ... | regex REGEX | limit_queries COUNT TIMEPERIOD HOLDOFF] [at_times VALUE...]
|
* rule NAME deny|allow [wildcard | columns VALUE ... | regex REGEX | limit_queries COUNT TIMEPERIOD HOLDOFF|no_where_clause [select|insert|delete|update]] [at_times VALUE...]
|
||||||
*/
|
*/
|
||||||
#include <my_config.h>
|
#include <my_config.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@ -96,7 +96,7 @@ static FILTER_OBJECT MyObject = {
|
|||||||
/**
|
/**
|
||||||
* Query types
|
* Query types
|
||||||
*/
|
*/
|
||||||
typedef enum{
|
/*typedef enum{
|
||||||
NONE = 0,
|
NONE = 0,
|
||||||
ALL = (1),
|
ALL = (1),
|
||||||
SELECT = (1<<1),
|
SELECT = (1<<1),
|
||||||
@ -104,7 +104,7 @@ typedef enum{
|
|||||||
UPDATE = (1<<3),
|
UPDATE = (1<<3),
|
||||||
DELETE = (1<<4)
|
DELETE = (1<<4)
|
||||||
}querytype_t;
|
}querytype_t;
|
||||||
|
*/
|
||||||
/**
|
/**
|
||||||
* Rule types
|
* Rule types
|
||||||
*/
|
*/
|
||||||
@ -114,7 +114,8 @@ typedef enum {
|
|||||||
RT_THROTTLE,
|
RT_THROTTLE,
|
||||||
RT_PERMISSION,
|
RT_PERMISSION,
|
||||||
RT_WILDCARD,
|
RT_WILDCARD,
|
||||||
RT_REGEX
|
RT_REGEX,
|
||||||
|
RT_CLAUSE
|
||||||
}ruletype_t;
|
}ruletype_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -151,7 +152,7 @@ typedef struct rule_t{
|
|||||||
void* data;
|
void* data;
|
||||||
char* name;
|
char* name;
|
||||||
ruletype_t type;
|
ruletype_t type;
|
||||||
querytype_t on_queries;
|
skygw_query_op_t on_queries;
|
||||||
bool allow;
|
bool allow;
|
||||||
TIMERANGE* active;
|
TIMERANGE* active;
|
||||||
}RULE;
|
}RULE;
|
||||||
@ -354,6 +355,42 @@ char* next_ip_class(char* str)
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool parse_querytypes(char* str,RULE* rule)
|
||||||
|
{
|
||||||
|
char buffer[512];
|
||||||
|
char *ptr,*dest;
|
||||||
|
bool done = false;
|
||||||
|
rule->on_queries = 0;
|
||||||
|
ptr = str;
|
||||||
|
dest = buffer;
|
||||||
|
|
||||||
|
while(ptr - buffer < 512)
|
||||||
|
{
|
||||||
|
if(*ptr == '|' || (done = *ptr == '\0')){
|
||||||
|
*dest = '\0';
|
||||||
|
if(strcmp(buffer,"select") == 0){
|
||||||
|
rule->on_queries |= QUERY_OP_SELECT;
|
||||||
|
}else if(strcmp(buffer,"insert") == 0){
|
||||||
|
rule->on_queries |= QUERY_OP_INSERT;
|
||||||
|
}else if(strcmp(buffer,"update") == 0){
|
||||||
|
rule->on_queries |= QUERY_OP_UPDATE;
|
||||||
|
}else if(strcmp(buffer,"delete") == 0){
|
||||||
|
rule->on_queries |= QUERY_OP_DELETE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(done){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest = buffer;
|
||||||
|
ptr++;
|
||||||
|
}else{
|
||||||
|
*dest++ = *ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether a null-terminated string contains two ISO-8601 compliant times separated
|
* Checks whether a null-terminated string contains two ISO-8601 compliant times separated
|
||||||
* by a single dash.
|
* by a single dash.
|
||||||
@ -677,7 +714,7 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
|
|||||||
rlist = (RULELIST*)calloc(1,sizeof(RULELIST));
|
rlist = (RULELIST*)calloc(1,sizeof(RULELIST));
|
||||||
ruledef->name = strdup(tok);
|
ruledef->name = strdup(tok);
|
||||||
ruledef->type = RT_UNDEFINED;
|
ruledef->type = RT_UNDEFINED;
|
||||||
ruledef->on_queries = ALL;
|
ruledef->on_queries = QUERY_OP_UNDEFINED;
|
||||||
rlist->rule = ruledef;
|
rlist->rule = ruledef;
|
||||||
rlist->next = instance->rules;
|
rlist->next = instance->rules;
|
||||||
instance->rules = rlist;
|
instance->rules = rlist;
|
||||||
@ -744,10 +781,15 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
|
|||||||
{
|
{
|
||||||
bool escaped = false;
|
bool escaped = false;
|
||||||
regex_t *re;
|
regex_t *re;
|
||||||
char* start = tok, *str;
|
char* start, *str;
|
||||||
|
tok = strtok(NULL," ");
|
||||||
tok += 6;
|
|
||||||
|
while(*tok == '\'' || *tok == '"'){
|
||||||
|
tok++;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = tok;
|
||||||
|
|
||||||
while(isspace(*tok) || *tok == '\'' || *tok == '"'){
|
while(isspace(*tok) || *tok == '\'' || *tok == '"'){
|
||||||
tok++;
|
tok++;
|
||||||
}
|
}
|
||||||
@ -797,6 +839,21 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
|
|||||||
ruledef->type = RT_THROTTLE;
|
ruledef->type = RT_THROTTLE;
|
||||||
ruledef->data = (void*)qs;
|
ruledef->data = (void*)qs;
|
||||||
}
|
}
|
||||||
|
else if(strcmp(tok,"no_where_clause") == 0)
|
||||||
|
{
|
||||||
|
ruledef->type = RT_CLAUSE;
|
||||||
|
ruledef->data = (void*)mode;
|
||||||
|
}
|
||||||
|
else if(strcmp(tok,"on_operations") == 0)
|
||||||
|
{
|
||||||
|
tok = strtok(NULL," ");
|
||||||
|
if(!parse_querytypes(tok,ruledef)){
|
||||||
|
skygw_log_write(LOGFILE_ERROR,
|
||||||
|
"fwfilter: Invalid query type"
|
||||||
|
"requirements on where/having clauses: %s."
|
||||||
|
,tok);
|
||||||
|
}
|
||||||
|
}
|
||||||
tok = strtok(NULL," ,");
|
tok = strtok(NULL," ,");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1106,7 +1163,8 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
|||||||
STRLINK* strln = NULL;
|
STRLINK* strln = NULL;
|
||||||
QUERYSPEED* queryspeed;
|
QUERYSPEED* queryspeed;
|
||||||
int qlen;
|
int qlen;
|
||||||
|
skygw_query_op_t optype;
|
||||||
|
|
||||||
ipaddr = strdup(dcb->remote);
|
ipaddr = strdup(dcb->remote);
|
||||||
sprintf(uname_addr,"%s@%s",dcb->user,ipaddr);
|
sprintf(uname_addr,"%s@%s",dcb->user,ipaddr);
|
||||||
|
|
||||||
@ -1145,6 +1203,7 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
|||||||
if(!query_is_parsed(queue)){
|
if(!query_is_parsed(queue)){
|
||||||
parse_query(queue);
|
parse_query(queue);
|
||||||
}
|
}
|
||||||
|
optype = query_classifier_get_operation(queue);
|
||||||
modutil_extract_SQL(queue, &ptr, &qlen);
|
modutil_extract_SQL(queue, &ptr, &qlen);
|
||||||
fullquery = malloc((qlen + 1) * sizeof(char));
|
fullquery = malloc((qlen + 1) * sizeof(char));
|
||||||
memcpy(fullquery,ptr,qlen);
|
memcpy(fullquery,ptr,qlen);
|
||||||
@ -1160,138 +1219,149 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
|||||||
rulelist = rulelist->next;
|
rulelist = rulelist->next;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if(rulelist->rule->on_queries == QUERY_OP_UNDEFINED || rulelist->rule->on_queries & optype){
|
||||||
switch(rulelist->rule->type){
|
|
||||||
|
|
||||||
case RT_UNDEFINED:
|
|
||||||
skygw_log_write_flush(LOGFILE_ERROR, "Error: Undefined rule type found.");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RT_REGEX:
|
|
||||||
|
|
||||||
if(fullquery && regexec(rulelist->rule->data,fullquery,0,NULL,0) == 0){
|
switch(rulelist->rule->type){
|
||||||
|
|
||||||
|
case RT_UNDEFINED:
|
||||||
|
skygw_log_write_flush(LOGFILE_ERROR, "Error: Undefined rule type found.");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RT_REGEX:
|
||||||
|
|
||||||
accept = rulelist->rule->allow;
|
if(fullquery && regexec(rulelist->rule->data,fullquery,0,NULL,0) == 0){
|
||||||
|
|
||||||
|
accept = rulelist->rule->allow;
|
||||||
|
|
||||||
|
if(!rulelist->rule->allow){
|
||||||
|
msg = strdup("Permission denied, query matched regular expression.");
|
||||||
|
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': regex matched on query",rulelist->rule->name);
|
||||||
|
goto queryresolved;
|
||||||
|
}else{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RT_PERMISSION:
|
||||||
if(!rulelist->rule->allow){
|
if(!rulelist->rule->allow){
|
||||||
msg = strdup("Permission denied, query matched regular expression.");
|
accept = false;
|
||||||
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': regex matched on query",rulelist->rule->name);
|
msg = strdup("Permission denied at this time.");
|
||||||
|
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': query denied at: %s",rulelist->rule->name,asctime(tm_now));
|
||||||
goto queryresolved;
|
goto queryresolved;
|
||||||
}else{
|
}else{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RT_PERMISSION:
|
|
||||||
if(!rulelist->rule->allow){
|
|
||||||
accept = false;
|
|
||||||
msg = strdup("Permission denied at this time.");
|
|
||||||
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': query denied at: %s",rulelist->rule->name,asctime(tm_now));
|
|
||||||
goto queryresolved;
|
|
||||||
}else{
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RT_COLUMN:
|
case RT_COLUMN:
|
||||||
|
|
||||||
if(is_sql && is_real){
|
if(is_sql && is_real){
|
||||||
|
|
||||||
strln = (STRLINK*)rulelist->rule->data;
|
strln = (STRLINK*)rulelist->rule->data;
|
||||||
where = skygw_get_affected_fields(queue);
|
where = skygw_get_affected_fields(queue);
|
||||||
|
|
||||||
if(where != NULL){
|
if(where != NULL){
|
||||||
|
|
||||||
while(strln){
|
while(strln){
|
||||||
if(strstr(where,strln->value)){
|
if(strstr(where,strln->value)){
|
||||||
|
|
||||||
|
accept = rulelist->rule->allow;
|
||||||
|
|
||||||
|
if(!rulelist->rule->allow){
|
||||||
|
sprintf(emsg,"Permission denied to column '%s'.",strln->value);
|
||||||
|
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': query targets forbidden column: %s",rulelist->rule->name,strln->value);
|
||||||
|
msg = strdup(emsg);
|
||||||
|
goto queryresolved;
|
||||||
|
}else{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strln = strln->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RT_WILDCARD:
|
||||||
|
|
||||||
|
|
||||||
|
if(is_sql && is_real){
|
||||||
|
|
||||||
|
where = skygw_get_affected_fields(queue);
|
||||||
|
|
||||||
|
if(where != NULL){
|
||||||
|
if(strchr(where,'*')){
|
||||||
|
|
||||||
accept = rulelist->rule->allow;
|
accept = rulelist->rule->allow;
|
||||||
|
|
||||||
if(!rulelist->rule->allow){
|
if(!rulelist->rule->allow){
|
||||||
sprintf(emsg,"Permission denied to column '%s'.",strln->value);
|
msg = strdup("Usage of wildcard denied.");
|
||||||
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': query targets forbidden column: %s",rulelist->rule->name,strln->value);
|
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': query contains a wildcard.",rulelist->rule->name);
|
||||||
msg = strdup(emsg);
|
|
||||||
goto queryresolved;
|
goto queryresolved;
|
||||||
}else{
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
strln = strln->next;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RT_WILDCARD:
|
case RT_THROTTLE:
|
||||||
|
queryspeed = (QUERYSPEED*)rulelist->rule->data;
|
||||||
|
if(queryspeed->count > queryspeed->limit)
|
||||||
|
{
|
||||||
|
queryspeed->triggered = time_now;
|
||||||
|
queryspeed->count = 0;
|
||||||
|
accept = false;
|
||||||
|
|
||||||
|
|
||||||
if(is_sql && is_real){
|
skygw_log_write(LOGFILE_TRACE,
|
||||||
|
"fwfilter: rule '%s': query limit triggered (%d queries in %f seconds), denying queries from user %s for %f seconds.",
|
||||||
where = skygw_get_affected_fields(queue);
|
rulelist->rule->name,
|
||||||
|
queryspeed->limit,
|
||||||
if(where != NULL){
|
queryspeed->period,
|
||||||
if(strchr(where,'*')){
|
uname_addr,
|
||||||
|
queryspeed->cooldown);
|
||||||
accept = rulelist->rule->allow;
|
|
||||||
|
|
||||||
if(!rulelist->rule->allow){
|
|
||||||
msg = strdup("Usage of wildcard denied.");
|
|
||||||
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': query contains a wildcard.",rulelist->rule->name);
|
|
||||||
goto queryresolved;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
else if(difftime(time_now,queryspeed->triggered) < queryspeed->cooldown)
|
||||||
}
|
{
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RT_THROTTLE:
|
double blocked_for = queryspeed->cooldown - difftime(time_now,queryspeed->triggered);
|
||||||
queryspeed = (QUERYSPEED*)rulelist->rule->data;
|
|
||||||
if(queryspeed->count > queryspeed->limit)
|
|
||||||
{
|
|
||||||
queryspeed->triggered = time_now;
|
|
||||||
queryspeed->count = 0;
|
|
||||||
accept = false;
|
|
||||||
|
|
||||||
|
sprintf(emsg,"Queries denied for %f seconds",blocked_for);
|
||||||
skygw_log_write(LOGFILE_TRACE,
|
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': user %s denied for %f seconds",rulelist->rule->name,uname_addr,blocked_for);
|
||||||
"fwfilter: rule '%s': query limit triggered (%d queries in %f seconds), denying queries from user %s for %f seconds.",
|
msg = strdup(emsg);
|
||||||
rulelist->rule->name,
|
|
||||||
queryspeed->limit,
|
|
||||||
queryspeed->period,
|
|
||||||
uname_addr,
|
|
||||||
queryspeed->cooldown);
|
|
||||||
}
|
|
||||||
else if(difftime(time_now,queryspeed->triggered) < queryspeed->cooldown)
|
|
||||||
{
|
|
||||||
|
|
||||||
double blocked_for = queryspeed->cooldown - difftime(time_now,queryspeed->triggered);
|
|
||||||
|
|
||||||
sprintf(emsg,"Queries denied for %f seconds",uname_addr,blocked_for);
|
|
||||||
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': user %s denied for %f seconds",rulelist->rule->name,uname_addr,blocked_for);
|
|
||||||
msg = strdup(emsg);
|
|
||||||
|
|
||||||
accept = false;
|
accept = false;
|
||||||
}
|
}
|
||||||
else if(difftime(time_now,queryspeed->first_query) < queryspeed->period)
|
else if(difftime(time_now,queryspeed->first_query) < queryspeed->period)
|
||||||
{
|
{
|
||||||
queryspeed->count++;
|
queryspeed->count++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
queryspeed->first_query = time_now;
|
queryspeed->first_query = time_now;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
case RT_CLAUSE:
|
||||||
|
if(is_sql && is_real && !skygw_query_has_clause(queue))
|
||||||
|
{
|
||||||
|
accept = false;
|
||||||
|
msg = strdup("Required WHERE/HAVING clause is missing.");
|
||||||
|
skygw_log_write(LOGFILE_TRACE, "fwfilter: rule '%s': query has no where/having clause, query is denied.",
|
||||||
|
rulelist->rule->name);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rulelist = rulelist->next;
|
rulelist = rulelist->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user