Added query operation type requirements on where clauses and fixed a bug with regex rules.

This commit is contained in:
Markus Makela
2014-11-14 15:59:15 +02:00
parent 171af0fc03
commit ca13e18f53
3 changed files with 196 additions and 117 deletions

View File

@ -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;
} }

View File

@ -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 {

View File

@ -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,9 +781,14 @@ 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,6 +1163,7 @@ 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){ switch(rulelist->rule->type){
case RT_UNDEFINED: case RT_UNDEFINED:
skygw_log_write_flush(LOGFILE_ERROR, "Error: Undefined rule type found."); skygw_log_write_flush(LOGFILE_ERROR, "Error: Undefined rule type found.");
break; break;
case RT_REGEX: case RT_REGEX:
if(fullquery && regexec(rulelist->rule->data,fullquery,0,NULL,0) == 0){ if(fullquery && regexec(rulelist->rule->data,fullquery,0,NULL,0) == 0){
accept = rulelist->rule->allow; 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)
{
double blocked_for = queryspeed->cooldown - difftime(time_now,queryspeed->triggered);
sprintf(emsg,"Queries denied for %f seconds",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;
}
else if(difftime(time_now,queryspeed->first_query) < queryspeed->period)
{
queryspeed->count++;
}
else
{
queryspeed->first_query = time_now;
}
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;
} }
break;
case RT_THROTTLE:
queryspeed = (QUERYSPEED*)rulelist->rule->data;
if(queryspeed->count > queryspeed->limit)
{
queryspeed->triggered = time_now;
queryspeed->count = 0;
accept = false;
skygw_log_write(LOGFILE_TRACE,
"fwfilter: rule '%s': query limit triggered (%d queries in %f seconds), denying queries from user %s for %f seconds.",
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;
}
else if(difftime(time_now,queryspeed->first_query) < queryspeed->period)
{
queryspeed->count++;
}
else
{
queryspeed->first_query = time_now;
}
break;
default:
break;
} }
rulelist = rulelist->next; rulelist = rulelist->next;
} }