Merge branch 'develop' of https://github.com/mariadb-corporation/MaxScale into develop
This commit is contained in:
@ -1,4 +1,4 @@
|
|||||||
[Search page for MaxScale Documentation]((http://mariadb-corporation.github.io/MaxScale/Search/)
|
[Search page for MaxScale Documentation](http://mariadb-corporation.github.io/MaxScale/Search/)
|
||||||
|
|
||||||
# Contents
|
# Contents
|
||||||
|
|
||||||
|
@ -18,39 +18,48 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @file fwfilter.c
|
* @file fwfilter.c
|
||||||
* Firewall Filter
|
* @author Markus Mäkelä
|
||||||
|
* @date 13.2.2015
|
||||||
|
* @version 1.0.0
|
||||||
|
* @copyright GPLv2
|
||||||
|
* @section secDesc Firewall Filter
|
||||||
*
|
*
|
||||||
* A filter that acts as a firewall, denying queries that do not meet a set of rules.
|
* A filter that acts as a firewall, denying queries that do not meet a set of rules.
|
||||||
*
|
*
|
||||||
* Filter configuration parameters:
|
* Filter configuration parameters:
|
||||||
*
|
*@code{.unparsed}
|
||||||
* rules=<path to file> Location of the rule file
|
* rules=<path to file> Location of the rule file
|
||||||
*
|
*@endcode
|
||||||
* Rules are defined in a separate rule file that lists all the rules and the users to whom the rules are applied.
|
* Rules are defined in a separate rule file that lists all the rules and the users to whom the rules are applied.
|
||||||
* Rules follow a simple syntax that denies the queries that meet the requirements of the rules.
|
* Rules follow a simple syntax that denies the queries that meet the requirements of the rules.
|
||||||
* For example, to define a rule denying users from accessing the column 'salary' between
|
* For example, to define a rule denying users from accessing the column 'salary' between
|
||||||
* the times 15:00 and 17:00, the following rule is to be configured into the configuration file:
|
* the times 15:00 and 17:00, the following rule is to be configured into the configuration file:
|
||||||
*
|
*@code{.unparsed}
|
||||||
* rule block_salary deny columns salary at_times 15:00:00-17:00:00
|
* rule block_salary deny columns salary at_times 15:00:00-17:00:00
|
||||||
*
|
*@endcode
|
||||||
* The users are matched by username and network address. Wildcard values can be provided by using the '%' character.
|
* The users are matched by username and network address. Wildcard values can be provided by using the '%' character.
|
||||||
* For example, to apply this rule to users John, connecting from any address
|
* For example, to apply this rule to users John, connecting from any address
|
||||||
* that starts with the octets 198.168.%, and Jane, connecting from the address 192.168.0.1:
|
* that starts with the octets 198.168.%, and Jane, connecting from the address 192.168.0.1:
|
||||||
*
|
*@code{.unparsed}
|
||||||
* users John@192.168.% Jane@192.168.0.1 match any rules block_salary
|
* users John@192.168.% Jane@192.168.0.1 match any rules block_salary
|
||||||
|
*@endcode
|
||||||
*
|
*
|
||||||
*
|
* The 'match' keyword controls the way rules are matched. If it is set to
|
||||||
* The 'match' keyword controls the way rules are matched. If it is set to 'any' the first active rule that is triggered will cause the query to be denied.
|
* 'any' the first active rule that is triggered will cause the query to be denied.
|
||||||
* If it is set to 'all' all the active rules need to match before the query is denied.
|
* If it is set to 'all' all the active rules need to match before the query is denied.
|
||||||
*
|
*
|
||||||
* Rule syntax
|
* @subsection secRule Rule syntax
|
||||||
*
|
* This is the syntax used when defining rules.
|
||||||
|
*@code{.unparsed}
|
||||||
* rule NAME deny [wildcard | columns VALUE ... | regex REGEX | limit_queries COUNT TIMEPERIOD HOLDOFF | no_where_clause] [at_times VALUE...] [on_queries [select|update|insert|delete]]
|
* rule NAME deny [wildcard | columns VALUE ... | regex REGEX | limit_queries COUNT TIMEPERIOD HOLDOFF | no_where_clause] [at_times VALUE...] [on_queries [select|update|insert|delete]]
|
||||||
*
|
*@endcode
|
||||||
* User syntax
|
* @subsection secUser User syntax
|
||||||
*
|
* This is the syntax used when linking users to rules. It takes one or more
|
||||||
|
* combinations of username and network, either the value any or all,
|
||||||
|
* depending on how you want to match the rules, and one or more rule names.
|
||||||
|
*@code{.unparsed}
|
||||||
* users NAME ... match [any|all] rules RULE ...
|
* users NAME ... match [any|all] rules RULE ...
|
||||||
*
|
*@endcode
|
||||||
*/
|
*/
|
||||||
#include <my_config.h>
|
#include <my_config.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@ -109,13 +118,13 @@ static FILTER_OBJECT MyObject = {
|
|||||||
* Rule types
|
* Rule types
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
RT_UNDEFINED = 0x00,
|
RT_UNDEFINED = 0x00, /*< Undefined rule */
|
||||||
RT_COLUMN,
|
RT_COLUMN, /*< Column name rule*/
|
||||||
RT_THROTTLE,
|
RT_THROTTLE, /*< Query speed rule */
|
||||||
RT_PERMISSION,
|
RT_PERMISSION, /*< Simple denying rule */
|
||||||
RT_WILDCARD,
|
RT_WILDCARD, /*< Wildcard denial rule */
|
||||||
RT_REGEX,
|
RT_REGEX, /*< Regex matching rule */
|
||||||
RT_CLAUSE
|
RT_CLAUSE /*< WHERE-clause requirement rule */
|
||||||
}ruletype_t;
|
}ruletype_t;
|
||||||
|
|
||||||
const char* rule_names[] = {
|
const char* rule_names[] = {
|
||||||
@ -133,25 +142,31 @@ const char* rule_names[] = {
|
|||||||
* Linked list of strings.
|
* Linked list of strings.
|
||||||
*/
|
*/
|
||||||
typedef struct strlink_t{
|
typedef struct strlink_t{
|
||||||
struct strlink_t *next;
|
struct strlink_t *next; /*< Next node in the list */
|
||||||
char* value;
|
char* value; /*< Value of the current node */
|
||||||
}STRLINK;
|
}STRLINK;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A structure defining a range of time
|
||||||
|
*/
|
||||||
typedef struct timerange_t{
|
typedef struct timerange_t{
|
||||||
struct timerange_t* next;
|
struct timerange_t* next; /*< Next node in the list */
|
||||||
struct tm start;
|
struct tm start; /*< Start of the time range */
|
||||||
struct tm end;
|
struct tm end; /*< End of the time range */
|
||||||
}TIMERANGE;
|
}TIMERANGE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query speed measurement and limitation structure
|
||||||
|
*/
|
||||||
typedef struct queryspeed_t{
|
typedef struct queryspeed_t{
|
||||||
time_t first_query;
|
time_t first_query; /*< Time when the first query occurred */
|
||||||
time_t triggered;
|
time_t triggered; /*< Time when the limit was exceeded */
|
||||||
double period;
|
double period; /*< Measurement interval in seconds */
|
||||||
double cooldown;
|
double cooldown;/*< Time the user is denied access for */
|
||||||
int count;
|
int count; /*< Number of queries done */
|
||||||
int limit;
|
int limit; /*< Maximum number of queries */
|
||||||
long id;
|
long id; /*< Unique id of the rule */
|
||||||
struct queryspeed_t* next;
|
struct queryspeed_t* next; /*< Next node in the list */
|
||||||
}QUERYSPEED;
|
}QUERYSPEED;
|
||||||
|
|
||||||
|
|
||||||
@ -162,65 +177,70 @@ typedef struct queryspeed_t{
|
|||||||
* This allows to match an arbitrary set of rules against a user.
|
* This allows to match an arbitrary set of rules against a user.
|
||||||
*/
|
*/
|
||||||
typedef struct rule_t{
|
typedef struct rule_t{
|
||||||
void* data;
|
void* data; /*< Actual implementation of the rule */
|
||||||
char* name;
|
char* name; /*< Name of the rule */
|
||||||
ruletype_t type;
|
ruletype_t type;/*< Type of the rule */
|
||||||
skygw_query_op_t on_queries;
|
skygw_query_op_t on_queries;/*< Types of queries to inspect */
|
||||||
bool allow;
|
bool allow;/*< Allow or deny the query if this rule matches */
|
||||||
int times_matched;
|
int times_matched;/*< Number of times this rule has been matched */
|
||||||
TIMERANGE* active;
|
TIMERANGE* active;/*< List of times when this rule is active */
|
||||||
}RULE;
|
}RULE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Linked list of pointers to a global pool of RULE structs
|
* Linked list of pointers to a global pool of RULE structs
|
||||||
*/
|
*/
|
||||||
typedef struct rulelist_t{
|
typedef struct rulelist_t{
|
||||||
RULE* rule;
|
RULE* rule; /*< The rule structure */
|
||||||
struct rulelist_t* next;
|
struct rulelist_t* next;/*< Next node in the list */
|
||||||
}RULELIST;
|
}RULELIST;
|
||||||
|
|
||||||
typedef struct user_t{
|
typedef struct user_t{
|
||||||
char* name;
|
char* name;/*< Name of the user */
|
||||||
SPINLOCK* lock;
|
SPINLOCK* lock;/*< User spinlock */
|
||||||
QUERYSPEED* qs_limit;
|
QUERYSPEED* qs_limit;/*< The query speed structure unique to this user */
|
||||||
RULELIST* rules_or;
|
RULELIST* rules_or;/*< If any of these rules match the action is triggered */
|
||||||
RULELIST* rules_and;
|
RULELIST* rules_and;/*< All of these rules must match for the action to trigger */
|
||||||
}USER;
|
}USER;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Linked list of IP adresses and subnet masks
|
* Linked list of IP adresses and subnet masks
|
||||||
*/
|
*/
|
||||||
typedef struct iprange_t{
|
typedef struct iprange_t{
|
||||||
struct iprange_t* next;
|
struct iprange_t* next;/*< Next node in the list */
|
||||||
uint32_t ip;
|
uint32_t ip;/*< IP address */
|
||||||
uint32_t mask;
|
uint32_t mask;/*< Network mask */
|
||||||
}IPRANGE;
|
}IPRANGE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Firewall filter instance.
|
* The Firewall filter instance.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
HASHTABLE* htable;
|
HASHTABLE* htable; /*< User hashtable */
|
||||||
RULELIST* rules;
|
RULELIST* rules;/*< List of all the rules */
|
||||||
STRLINK* userstrings;
|
STRLINK* userstrings;/*< Temporary list of raw strings of users */
|
||||||
bool def_op;
|
bool def_op;/*< Default operation mode, defaults to deny */
|
||||||
SPINLOCK* lock;
|
SPINLOCK* lock;/*< Instance spinlock */
|
||||||
long idgen; /**UID generator*/
|
long idgen; /*< UID generator */
|
||||||
} FW_INSTANCE;
|
} FW_INSTANCE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The session structure for Firewall filter.
|
* The session structure for Firewall filter.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
SESSION* session;
|
SESSION* session;/*< Client session structure */
|
||||||
char* errmsg;
|
char* errmsg;/*< Rule specific error message */
|
||||||
DOWNSTREAM down;
|
DOWNSTREAM down;/*< Next object in the downstream chain */
|
||||||
UPSTREAM up;
|
UPSTREAM up;/*< Next object in the upstream chain */
|
||||||
} FW_SESSION;
|
} FW_SESSION;
|
||||||
|
|
||||||
static int hashkeyfun(void* key);
|
static int hashkeyfun(void* key);
|
||||||
static int hashcmpfun (void *, void *);
|
static int hashcmpfun (void *, void *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hashtable key hashing function. Uses a simple string hashing algorithm.
|
||||||
|
* @param key Key to hash
|
||||||
|
* @return The hash value of the key
|
||||||
|
*/
|
||||||
static int hashkeyfun(
|
static int hashkeyfun(
|
||||||
void* key)
|
void* key)
|
||||||
{
|
{
|
||||||
@ -232,9 +252,16 @@ static int hashkeyfun(
|
|||||||
while((c = *ptr++)){
|
while((c = *ptr++)){
|
||||||
hash = c + (hash << 6) + (hash << 16) - hash;
|
hash = c + (hash << 6) + (hash << 16) - hash;
|
||||||
}
|
}
|
||||||
return (int)hash > 0 ? hash : -hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Hashtable entry comparison function. Does a string matching operation on the
|
||||||
|
* two keys. This function assumes the values are pointers to null-terminated
|
||||||
|
* character arrays.
|
||||||
|
* @param v1 The first key
|
||||||
|
* @param v2 The second key
|
||||||
|
* @return Zero if the values are equal. Non-zero in other cases.
|
||||||
|
*/
|
||||||
static int hashcmpfun(
|
static int hashcmpfun(
|
||||||
void* v1,
|
void* v1,
|
||||||
void* v2)
|
void* v2)
|
||||||
@ -245,19 +272,6 @@ static int hashcmpfun(
|
|||||||
return strcmp(i1,i2);
|
return strcmp(i1,i2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void* hstrdup(void* fval)
|
|
||||||
{
|
|
||||||
char* str = (char*)fval;
|
|
||||||
return strdup(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void* hstrfree(void* fval)
|
|
||||||
{
|
|
||||||
free (fval);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void* rlistdup(void* fval)
|
void* rlistdup(void* fval)
|
||||||
{
|
{
|
||||||
@ -588,7 +602,7 @@ RULE* find_rule(char* tok, FW_INSTANCE* instance)
|
|||||||
}
|
}
|
||||||
rlist = rlist->next;
|
rlist = rlist->next;
|
||||||
}
|
}
|
||||||
skygw_log_write(LOGFILE_ERROR, "fwfilter: Rule not found: %s",tok);
|
skygw_log_write(LOGFILE_ERROR, "Error : Rule not found: %s",tok);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,6 +616,10 @@ void add_users(char* rule, FW_INSTANCE* instance)
|
|||||||
assert(rule != NULL && instance != NULL);
|
assert(rule != NULL && instance != NULL);
|
||||||
|
|
||||||
STRLINK* link = calloc(1,sizeof(STRLINK));
|
STRLINK* link = calloc(1,sizeof(STRLINK));
|
||||||
|
if(link == NULL){
|
||||||
|
skygw_log_write(LOGFILE_ERROR,"Error : Memory allocation failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
link->next = instance->userstrings;
|
link->next = instance->userstrings;
|
||||||
link->value = strdup(rule);
|
link->value = strdup(rule);
|
||||||
instance->userstrings = link;
|
instance->userstrings = link;
|
||||||
@ -838,20 +856,21 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
|
|||||||
regex_t *re;
|
regex_t *re;
|
||||||
char* start, *str;
|
char* start, *str;
|
||||||
tok = strtok(NULL," ");
|
tok = strtok(NULL," ");
|
||||||
|
char delim = '\'';
|
||||||
while(*tok == '\'' || *tok == '"'){
|
while(*tok == '\'' || *tok == '"'){
|
||||||
|
delim = *tok;
|
||||||
tok++;
|
tok++;
|
||||||
}
|
}
|
||||||
|
|
||||||
start = tok;
|
start = tok;
|
||||||
|
|
||||||
while(isspace(*tok) || *tok == '\'' || *tok == '"'){
|
while(isspace(*tok) || *tok == delim){
|
||||||
tok++;
|
tok++;
|
||||||
}
|
}
|
||||||
|
|
||||||
while(true){
|
while(true){
|
||||||
|
|
||||||
if((*tok == '\'' || *tok == '"') && !escaped){
|
if((*tok == delim) && !escaped){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
escaped = (*tok == '\\');
|
escaped = (*tok == '\\');
|
||||||
@ -896,11 +915,23 @@ void parse_rule(char* rule, FW_INSTANCE* instance)
|
|||||||
spinlock_release(instance->lock);
|
spinlock_release(instance->lock);
|
||||||
|
|
||||||
tok = strtok(NULL," ");
|
tok = strtok(NULL," ");
|
||||||
|
if(tok == NULL){
|
||||||
|
free(qs);
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
qs->limit = atoi(tok);
|
qs->limit = atoi(tok);
|
||||||
|
|
||||||
tok = strtok(NULL," ");
|
tok = strtok(NULL," ");
|
||||||
|
if(tok == NULL){
|
||||||
|
free(qs);
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
qs->period = atof(tok);
|
qs->period = atof(tok);
|
||||||
tok = strtok(NULL," ");
|
tok = strtok(NULL," ");
|
||||||
|
if(tok == NULL){
|
||||||
|
free(qs);
|
||||||
|
goto retblock;
|
||||||
|
}
|
||||||
qs->cooldown = atof(tok);
|
qs->cooldown = atof(tok);
|
||||||
ruledef->type = RT_THROTTLE;
|
ruledef->type = RT_THROTTLE;
|
||||||
ruledef->data = (void*)qs;
|
ruledef->data = (void*)qs;
|
||||||
@ -958,13 +989,13 @@ createInstance(char **options, FILTER_PARAMETER **params)
|
|||||||
|
|
||||||
spinlock_init(my_instance->lock);
|
spinlock_init(my_instance->lock);
|
||||||
|
|
||||||
if((ht = hashtable_alloc(7, hashkeyfun, hashcmpfun)) == NULL){
|
if((ht = hashtable_alloc(100, hashkeyfun, hashcmpfun)) == NULL){
|
||||||
skygw_log_write(LOGFILE_ERROR, "Unable to allocate hashtable.");
|
skygw_log_write(LOGFILE_ERROR, "Unable to allocate hashtable.");
|
||||||
free(my_instance);
|
free(my_instance);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
hashtable_memory_fns(ht,hstrdup,NULL,hstrfree,hrulefree);
|
hashtable_memory_fns(ht,(HASHMEMORYFN)strdup,NULL,(HASHMEMORYFN)free,hrulefree);
|
||||||
|
|
||||||
my_instance->htable = ht;
|
my_instance->htable = ht;
|
||||||
my_instance->def_op = true;
|
my_instance->def_op = true;
|
||||||
@ -979,18 +1010,22 @@ createInstance(char **options, FILTER_PARAMETER **params)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(filename == NULL)
|
if(filename == NULL)
|
||||||
{
|
{
|
||||||
skygw_log_write(LOGFILE_ERROR, "Unable to find rule file for firewall filter.");
|
skygw_log_write(LOGFILE_ERROR, "Unable to find rule file for firewall filter. Please provide the path with"
|
||||||
|
" rules=<path to file>");
|
||||||
|
hashtable_free(my_instance->htable);
|
||||||
free(my_instance);
|
free(my_instance);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if((file = fopen(filename,"rb")) == NULL ){
|
if((file = fopen(filename,"rb")) == NULL ){
|
||||||
skygw_log_write(LOGFILE_ERROR, "Error while opening rule file for firewall filter.");
|
skygw_log_write(LOGFILE_ERROR, "Error while opening rule file for firewall filter.");
|
||||||
free(my_instance);
|
hashtable_free(my_instance->htable);
|
||||||
free(filename);
|
free(my_instance);
|
||||||
return NULL;
|
free(filename);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(filename);
|
free(filename);
|
||||||
@ -1002,6 +1037,7 @@ createInstance(char **options, FILTER_PARAMETER **params)
|
|||||||
if(ferror(file)){
|
if(ferror(file)){
|
||||||
skygw_log_write(LOGFILE_ERROR, "Error while reading rule file for firewall filter.");
|
skygw_log_write(LOGFILE_ERROR, "Error while reading rule file for firewall filter.");
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
hashtable_free(my_instance->htable);
|
||||||
free(my_instance);
|
free(my_instance);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -1037,9 +1073,7 @@ createInstance(char **options, FILTER_PARAMETER **params)
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associate a new session with this instance of the filter and opens
|
* Associate a new session with this instance of the filter.
|
||||||
* a connection to the server and prepares the exchange and the queue for use.
|
|
||||||
*
|
|
||||||
*
|
*
|
||||||
* @param instance The filter instance data
|
* @param instance The filter instance data
|
||||||
* @param session The session itself
|
* @param session The session itself
|
||||||
@ -1370,15 +1404,17 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
|
|||||||
|
|
||||||
spinlock_acquire(user->lock);
|
spinlock_acquire(user->lock);
|
||||||
queryspeed = user->qs_limit;
|
queryspeed = user->qs_limit;
|
||||||
|
spinlock_release(user->lock);
|
||||||
|
|
||||||
while(queryspeed){
|
while(queryspeed){
|
||||||
if(queryspeed->id == rule_qs->id){
|
if(queryspeed->id == rule_qs->id){
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
queryspeed = queryspeed->next;
|
queryspeed = queryspeed->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if(queryspeed == NULL){
|
if(queryspeed == NULL){
|
||||||
|
|
||||||
/**No match found*/
|
/**No match found*/
|
||||||
@ -1427,7 +1463,7 @@ bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *queue
|
|||||||
{
|
{
|
||||||
queryspeed->first_query = time_now;
|
queryspeed->first_query = time_now;
|
||||||
}
|
}
|
||||||
spinlock_release(user->lock);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RT_CLAUSE:
|
case RT_CLAUSE:
|
||||||
|
Reference in New Issue
Block a user