Queries can be matched against any matching rule or only when all rules match.
This commit is contained in:
@ -20,7 +20,7 @@
|
|||||||
* @file fwfilter.c
|
* @file fwfilter.c
|
||||||
* Firewall Filter
|
* Firewall Filter
|
||||||
*
|
*
|
||||||
* A filter that acts as a firewall, denying queries that do not meet the set requirements.
|
* A filter that acts as a firewall, denying queries that do not meet a set requirements.
|
||||||
*
|
*
|
||||||
* This filter uses "rules" to define the blcking parameters. To configure rules into the configuration file,
|
* This filter uses "rules" to define the blcking parameters. To configure rules into the configuration file,
|
||||||
* give each rule a unique name and assing the rule contents by passing a string enclosed in quotes.
|
* give each rule a unique name and assing the rule contents by passing a string enclosed in quotes.
|
||||||
@ -97,11 +97,12 @@ static FILTER_OBJECT MyObject = {
|
|||||||
* Query types
|
* Query types
|
||||||
*/
|
*/
|
||||||
typedef enum{
|
typedef enum{
|
||||||
ALL,
|
NONE = 0,
|
||||||
SELECT,
|
ALL = (1),
|
||||||
INSERT,
|
SELECT = (1<<1),
|
||||||
UPDATE,
|
INSERT = (1<<2),
|
||||||
DELETE
|
UPDATE = (1<<3),
|
||||||
|
DELETE = (1<<4)
|
||||||
}querytype_t;
|
}querytype_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -123,7 +124,6 @@ typedef struct strlink_t{
|
|||||||
char* value;
|
char* value;
|
||||||
}STRLINK;
|
}STRLINK;
|
||||||
|
|
||||||
|
|
||||||
typedef struct timerange_t{
|
typedef struct timerange_t{
|
||||||
struct timerange_t* next;
|
struct timerange_t* next;
|
||||||
struct tm start;
|
struct tm start;
|
||||||
@ -140,6 +140,7 @@ typedef struct rule_t{
|
|||||||
void* data;
|
void* data;
|
||||||
char* name;
|
char* name;
|
||||||
ruletype_t type;
|
ruletype_t type;
|
||||||
|
querytype_t on_queries;
|
||||||
bool allow;
|
bool allow;
|
||||||
TIMERANGE* active;
|
TIMERANGE* active;
|
||||||
}RULE;
|
}RULE;
|
||||||
@ -152,6 +153,12 @@ typedef struct rulelist_t{
|
|||||||
struct rulelist_t* next;
|
struct rulelist_t* next;
|
||||||
}RULELIST;
|
}RULELIST;
|
||||||
|
|
||||||
|
typedef struct user_t{
|
||||||
|
char* name;
|
||||||
|
RULELIST* rules_or;
|
||||||
|
RULELIST* rules_and;
|
||||||
|
}USER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Linked list of IP adresses and subnet masks
|
* Linked list of IP adresses and subnet masks
|
||||||
*/
|
*/
|
||||||
@ -165,13 +172,10 @@ typedef struct iprange_t{
|
|||||||
* The Firewall filter instance.
|
* The Firewall filter instance.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
HASHTABLE* htable; /**Usernames and forbidden columns*/
|
HASHTABLE* htable;
|
||||||
RULELIST* rules;
|
RULELIST* rules;
|
||||||
IPRANGE* networks;
|
|
||||||
TIMERANGE* times;
|
|
||||||
STRLINK* userstrings;
|
STRLINK* userstrings;
|
||||||
bool require_where[QUERY_TYPES];
|
bool def_op;
|
||||||
bool deny_wildcard, whitelist_users,whitelist_networks,whitelist_times,def_op;
|
|
||||||
|
|
||||||
} FW_INSTANCE;
|
} FW_INSTANCE;
|
||||||
|
|
||||||
@ -224,16 +228,14 @@ static void* hstrfree(void* fval)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void* hruledup(void* fval)
|
|
||||||
{
|
|
||||||
|
|
||||||
if(fval == NULL){
|
void* rlistdup(void* fval)
|
||||||
return NULL;
|
{
|
||||||
}
|
|
||||||
|
|
||||||
RULELIST *rule = NULL,
|
RULELIST *rule = NULL,
|
||||||
*ptr = (RULELIST*)fval;
|
*ptr = (RULELIST*)fval;
|
||||||
|
|
||||||
|
|
||||||
while(ptr){
|
while(ptr){
|
||||||
RULELIST* tmp = (RULELIST*)malloc(sizeof(RULELIST));
|
RULELIST* tmp = (RULELIST*)malloc(sizeof(RULELIST));
|
||||||
tmp->next = rule;
|
tmp->next = rule;
|
||||||
@ -242,18 +244,33 @@ static void* hruledup(void* fval)
|
|||||||
ptr = ptr->next;
|
ptr = ptr->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rule;
|
return (void*)rule;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
static void* hruledup(void* fval)
|
||||||
|
{
|
||||||
|
return fval;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
static void* hrulefree(void* fval)
|
static void* hrulefree(void* fval)
|
||||||
{
|
{
|
||||||
RULELIST *ptr = (RULELIST*)fval,*tmp;
|
USER* user = (USER*)fval;
|
||||||
|
RULELIST *ptr = user->rules_or,*tmp;
|
||||||
while(ptr){
|
while(ptr){
|
||||||
tmp = ptr;
|
tmp = ptr;
|
||||||
ptr = ptr->next;
|
ptr = ptr->next;
|
||||||
free(tmp);
|
free(tmp);
|
||||||
}
|
}
|
||||||
|
ptr = user->rules_and;
|
||||||
|
while(ptr){
|
||||||
|
tmp = ptr;
|
||||||
|
ptr = ptr->next;
|
||||||
|
free(tmp);
|
||||||
|
}
|
||||||
|
free(user->name);
|
||||||
|
free(user);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,6 +414,33 @@ int get_octet(char* str)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char* next_ip_class(char* str)
|
||||||
|
{
|
||||||
|
assert(str != NULL);
|
||||||
|
|
||||||
|
char* ptr = strchr(str,'\0');
|
||||||
|
|
||||||
|
if(ptr == NULL){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(ptr > str){
|
||||||
|
ptr--;
|
||||||
|
if(*ptr == '.' && *(ptr+1) != '%'){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ptr == str){
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*++ptr = '%';
|
||||||
|
*++ptr = '\0';
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*Convert string with IP address to an unsigned 32-bit integer
|
*Convert string with IP address to an unsigned 32-bit integer
|
||||||
* @param str String to convert
|
* @param str String to convert
|
||||||
@ -492,7 +536,7 @@ TIMERANGE* parse_time(char* str, FW_INSTANCE* instance)
|
|||||||
|
|
||||||
assert(str != NULL && instance != NULL);
|
assert(str != NULL && instance != NULL);
|
||||||
|
|
||||||
tr = (TIMERANGE*)malloc(sizeof(TIMERANGE));
|
tr = (TIMERANGE*)calloc(1,sizeof(TIMERANGE));
|
||||||
|
|
||||||
if(tr == NULL){
|
if(tr == NULL){
|
||||||
skygw_log_write(LOGFILE_ERROR, "fwfilter: malloc returned NULL.");
|
skygw_log_write(LOGFILE_ERROR, "fwfilter: malloc returned NULL.");
|
||||||
@ -550,7 +594,7 @@ TIMERANGE* split_reverse_time(TIMERANGE* tr)
|
|||||||
TIMERANGE* tmp = NULL;
|
TIMERANGE* tmp = NULL;
|
||||||
|
|
||||||
if(IS_RVRS_TIME(tr)){
|
if(IS_RVRS_TIME(tr)){
|
||||||
tmp = (TIMERANGE*)malloc(sizeof(TIMERANGE));
|
tmp = (TIMERANGE*)calloc(1,sizeof(TIMERANGE));
|
||||||
tmp->next = tr;
|
tmp->next = tr;
|
||||||
tmp->start.tm_hour = 0;
|
tmp->start.tm_hour = 0;
|
||||||
tmp->start.tm_min = 0;
|
tmp->start.tm_min = 0;
|
||||||
@ -634,20 +678,38 @@ void link_rules(char* rule, FW_INSTANCE* instance)
|
|||||||
|
|
||||||
/**Apply rules to users*/
|
/**Apply rules to users*/
|
||||||
|
|
||||||
char *tok, *ruleptr, *userptr;
|
bool match_any;
|
||||||
|
char *tok, *ruleptr, *userptr, *modeptr;
|
||||||
RULELIST* rulelist = NULL;
|
RULELIST* rulelist = NULL;
|
||||||
|
|
||||||
userptr = strstr(rule,"users ");
|
userptr = strstr(rule,"users ");
|
||||||
|
modeptr = strstr(rule," match ");
|
||||||
ruleptr = strstr(rule," rules ");
|
ruleptr = strstr(rule," rules ");
|
||||||
|
|
||||||
if(userptr == NULL || ruleptr == NULL) {
|
if((userptr == NULL || ruleptr == NULL || modeptr == NULL)||
|
||||||
/**No rules to apply were found*/
|
(userptr > modeptr || userptr > ruleptr || modeptr > ruleptr)) {
|
||||||
skygw_log_write(LOGFILE_TRACE, "Rule syntax was not proper, 'user' or 'rules' was found but not the other: %s",rule);
|
skygw_log_write(LOGFILE_ERROR, "fwfilter: Rule syntax incorrect, right keywords not found in the correct order: %s",rule);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tok = strtok(ruleptr," ,");
|
*modeptr++ = '\0';
|
||||||
tok = strtok(NULL," ,");
|
*ruleptr++ = '\0';
|
||||||
|
|
||||||
|
tok = strtok(modeptr," ");
|
||||||
|
if(strcmp(tok,"match") == 0){
|
||||||
|
tok = strtok(NULL," ");
|
||||||
|
if(strcmp(tok,"any") == 0){
|
||||||
|
match_any = true;
|
||||||
|
}else if(strcmp(tok,"all") == 0){
|
||||||
|
match_any = false;
|
||||||
|
}else{
|
||||||
|
skygw_log_write(LOGFILE_ERROR, "fwfilter: Rule syntax incorrect, 'match' was not followed by 'any' or 'all': %s",rule);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tok = strtok(ruleptr," ");
|
||||||
|
tok = strtok(NULL," ");
|
||||||
|
|
||||||
while(tok)
|
while(tok)
|
||||||
{
|
{
|
||||||
@ -661,7 +723,7 @@ void link_rules(char* rule, FW_INSTANCE* instance)
|
|||||||
rulelist = tmp_rl;
|
rulelist = tmp_rl;
|
||||||
|
|
||||||
}
|
}
|
||||||
tok = strtok(NULL," ,");
|
tok = strtok(NULL," ");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -669,19 +731,44 @@ void link_rules(char* rule, FW_INSTANCE* instance)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
*(ruleptr) = '\0';
|
*(ruleptr) = '\0';
|
||||||
userptr = strtok(rule," ,");
|
userptr = strtok(rule," ");
|
||||||
userptr = strtok(NULL," ,");
|
userptr = strtok(NULL," ");
|
||||||
|
|
||||||
while(userptr)
|
while(userptr)
|
||||||
{
|
{
|
||||||
if(hashtable_add(instance->htable,
|
USER* user;
|
||||||
(void *)userptr,
|
RULELIST *tl = NULL,*tail = NULL;
|
||||||
(void *)rulelist) == 0)
|
|
||||||
{
|
if((user = hashtable_fetch(instance->htable,userptr)) == NULL){
|
||||||
skygw_log_write(LOGFILE_TRACE, "fwfilter: Name conflict (%s was found twice)",tok);
|
|
||||||
|
/**New user*/
|
||||||
|
user = calloc(1,sizeof(USER));
|
||||||
|
if(user == NULL){
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
userptr = strtok(NULL," ,");
|
user->name = strdup(userptr);
|
||||||
|
tl = rlistdup(rulelist);
|
||||||
|
tail = tl;
|
||||||
|
while(tail->next){
|
||||||
|
tail = tail->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(match_any){
|
||||||
|
tail->next = user->rules_or;
|
||||||
|
user->rules_or = tl;
|
||||||
|
}else{
|
||||||
|
tail->next = user->rules_and;
|
||||||
|
user->rules_and = tl;
|
||||||
|
}
|
||||||
|
|
||||||
|
hashtable_add(instance->htable,
|
||||||
|
(void *)userptr,
|
||||||
|
(void *)user);
|
||||||
|
|
||||||
|
userptr = strtok(NULL," ");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -798,7 +885,7 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
|
|||||||
|
|
||||||
ruledef->type = RT_REGEX;
|
ruledef->type = RT_REGEX;
|
||||||
ruledef->data = (void*) re;
|
ruledef->data = (void*) re;
|
||||||
|
free(str);
|
||||||
|
|
||||||
}
|
}
|
||||||
tok = strtok(NULL," ,");
|
tok = strtok(NULL," ,");
|
||||||
@ -837,7 +924,7 @@ createInstance(char **options, FILTER_PARAMETER **params)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
hashtable_memory_fns(ht,hstrdup,hruledup,hstrfree,hrulefree);
|
hashtable_memory_fns(ht,hstrdup,NULL,hstrfree,hrulefree);
|
||||||
|
|
||||||
my_instance->htable = ht;
|
my_instance->htable = ht;
|
||||||
my_instance->def_op = true;
|
my_instance->def_op = true;
|
||||||
@ -1025,6 +1112,26 @@ bool inside_timerange(TIMERANGE* comp)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool rule_is_active(RULE* rule)
|
||||||
|
{
|
||||||
|
TIMERANGE* times;
|
||||||
|
if(rule->active != NULL){
|
||||||
|
|
||||||
|
times = (TIMERANGE*)rule->active;
|
||||||
|
|
||||||
|
while(times){
|
||||||
|
|
||||||
|
if(inside_timerange(times)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
times = times->next;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The routeQuery entry point. This is passed the query buffer
|
* The routeQuery entry point. This is passed the query buffer
|
||||||
* to which the filter should be applied. Once processed the
|
* to which the filter should be applied. Once processed the
|
||||||
@ -1043,42 +1150,47 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
|||||||
time_t time_now;
|
time_t time_now;
|
||||||
bool accept = my_instance->def_op,
|
bool accept = my_instance->def_op,
|
||||||
is_sql = false,
|
is_sql = false,
|
||||||
is_real = false;
|
is_real = false,rule_match;
|
||||||
char *where, *msg = NULL, *fullquery,*ptr;
|
char *where, *msg = NULL, *fullquery = NULL,*ptr,*ipaddr;
|
||||||
char uname[128];
|
|
||||||
char uname_addr[128];
|
char uname_addr[128];
|
||||||
char addr[128];
|
|
||||||
char emsg[1024];
|
char emsg[1024];
|
||||||
DCB* dcb = my_session->session->client;
|
DCB* dcb = my_session->session->client;
|
||||||
RULELIST *rulelist = NULL;
|
RULELIST *rulelist = NULL;
|
||||||
|
USER* user = NULL;
|
||||||
STRLINK* strln = NULL;
|
STRLINK* strln = NULL;
|
||||||
TIMERANGE *times;
|
|
||||||
int qlen;
|
int qlen;
|
||||||
|
|
||||||
sprintf(uname_addr,"%s@%s",dcb->user,dcb->remote);
|
ipaddr = strdup(dcb->remote);
|
||||||
sprintf(uname,"%s@%%",dcb->user);
|
sprintf(uname_addr,"%s@%s",dcb->user,ipaddr);
|
||||||
sprintf(addr,"%%@%s",dcb->remote);
|
|
||||||
|
|
||||||
time(&time_now);
|
time(&time_now);
|
||||||
|
|
||||||
|
if((user = (USER*)hashtable_fetch(my_instance->htable, uname_addr)) == NULL){
|
||||||
|
while(user == NULL && next_ip_class(ipaddr)){
|
||||||
|
sprintf(uname_addr,"%s@%s",dcb->user,ipaddr);
|
||||||
|
user = (USER*)hashtable_fetch(my_instance->htable, uname_addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(user == NULL){
|
||||||
|
strcpy(ipaddr,dcb->remote);
|
||||||
|
|
||||||
|
do{
|
||||||
|
sprintf(uname_addr,"%%@%s",ipaddr);
|
||||||
|
user = (USER*)hashtable_fetch(my_instance->htable, uname_addr);
|
||||||
|
}while(user == NULL && next_ip_class(ipaddr));
|
||||||
|
}
|
||||||
|
|
||||||
if((rulelist = (RULELIST*)hashtable_fetch(my_instance->htable, uname_addr)) == NULL &&
|
if(user == NULL){
|
||||||
(rulelist = (RULELIST*)hashtable_fetch(my_instance->htable, uname)) == NULL &&
|
|
||||||
(rulelist = (RULELIST*)hashtable_fetch(my_instance->htable, addr)) == NULL &&
|
|
||||||
(rulelist = (RULELIST*)hashtable_fetch(my_instance->htable, "%@%")) == NULL)
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*No rules matched, do default operation.
|
*No rules matched, do default operation.
|
||||||
* TODO: add incremental wildcard search of network addresses
|
|
||||||
* i.e. iteration from 127.0.0.1 all the way up to 127.%
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
goto queryresolved;
|
goto queryresolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
is_sql = modutil_is_SQL(queue);
|
is_sql = modutil_is_SQL(queue);
|
||||||
|
|
||||||
if(is_sql){
|
if(is_sql){
|
||||||
@ -1092,26 +1204,14 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
|||||||
is_real = skygw_is_real_query(queue);
|
is_real = skygw_is_real_query(queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rulelist = user->rules_or;
|
||||||
|
|
||||||
while(rulelist){
|
while(rulelist){
|
||||||
|
|
||||||
if(rulelist->rule->active != NULL){
|
if(!rule_is_active(rulelist->rule)){
|
||||||
bool rule_active = false;
|
|
||||||
times = (TIMERANGE*)rulelist->rule->active;
|
|
||||||
|
|
||||||
while(times){
|
|
||||||
|
|
||||||
if(inside_timerange(times)){
|
|
||||||
rule_active = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
times = times->next;
|
|
||||||
}
|
|
||||||
if(!rule_active){
|
|
||||||
rulelist = rulelist->next;
|
rulelist = rulelist->next;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
switch(rulelist->rule->type){
|
switch(rulelist->rule->type){
|
||||||
|
|
||||||
@ -1200,8 +1300,87 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
|||||||
rulelist = rulelist->next;
|
rulelist = rulelist->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
rulelist = user->rules_and;
|
||||||
|
rule_match = (rulelist != NULL);
|
||||||
|
while(rulelist && rule_match){
|
||||||
|
|
||||||
|
if(!rule_is_active(rulelist->rule)){
|
||||||
|
rule_match = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(rulelist->rule->type){
|
||||||
|
|
||||||
|
case RT_UNDEFINED:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RT_REGEX:
|
||||||
|
|
||||||
|
if(fullquery && regexec(rulelist->rule->data,fullquery,0,NULL,0) != 0){
|
||||||
|
rule_match = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RT_PERMISSION:
|
||||||
|
if(!rulelist->rule->allow){
|
||||||
|
rule_match = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RT_COLUMN:
|
||||||
|
|
||||||
|
if(is_sql && is_real){
|
||||||
|
|
||||||
|
strln = (STRLINK*)rulelist->rule->data;
|
||||||
|
where = skygw_get_affected_fields(queue);
|
||||||
|
|
||||||
|
if(where != NULL){
|
||||||
|
|
||||||
|
while(strln){
|
||||||
|
if(strstr(where,strln->value)){
|
||||||
|
rule_match = false;
|
||||||
|
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,'*')){
|
||||||
|
rule_match = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
rulelist = rulelist->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(rule_match == true){
|
||||||
|
/**AND rules match TODO: add a way to control what happens if AND matches*/
|
||||||
|
accept = false;
|
||||||
|
}
|
||||||
|
|
||||||
queryresolved:
|
queryresolved:
|
||||||
|
|
||||||
|
free(ipaddr);
|
||||||
|
free(fullquery);
|
||||||
if(accept){
|
if(accept){
|
||||||
|
|
||||||
return my_session->down.routeQuery(my_session->down.instance,
|
return my_session->down.routeQuery(my_session->down.instance,
|
||||||
|
Reference in New Issue
Block a user