diff --git a/Documentation/Documentation-Contents.md b/Documentation/Documentation-Contents.md index 3090c5205..1d281a674 100644 --- a/Documentation/Documentation-Contents.md +++ b/Documentation/Documentation-Contents.md @@ -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 diff --git a/server/modules/filter/fwfilter.c b/server/modules/filter/fwfilter.c index 9c316b03a..34fd4e17f 100644 --- a/server/modules/filter/fwfilter.c +++ b/server/modules/filter/fwfilter.c @@ -18,39 +18,48 @@ /** * @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. * * Filter configuration parameters: - * + *@code{.unparsed} * rules= 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 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 * 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 - * + *@endcode * 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 * 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 + *@endcode * - * - * 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. + * 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. * 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]] - * - * User syntax - * + *@endcode + * @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 ... - * + *@endcode */ #include #include @@ -109,13 +118,13 @@ static FILTER_OBJECT MyObject = { * Rule types */ typedef enum { - RT_UNDEFINED = 0x00, - RT_COLUMN, - RT_THROTTLE, - RT_PERMISSION, - RT_WILDCARD, - RT_REGEX, - RT_CLAUSE + RT_UNDEFINED = 0x00, /*< Undefined rule */ + RT_COLUMN, /*< Column name rule*/ + RT_THROTTLE, /*< Query speed rule */ + RT_PERMISSION, /*< Simple denying rule */ + RT_WILDCARD, /*< Wildcard denial rule */ + RT_REGEX, /*< Regex matching rule */ + RT_CLAUSE /*< WHERE-clause requirement rule */ }ruletype_t; const char* rule_names[] = { @@ -133,25 +142,31 @@ const char* rule_names[] = { * Linked list of strings. */ typedef struct strlink_t{ - struct strlink_t *next; - char* value; + struct strlink_t *next; /*< Next node in the list */ + char* value; /*< Value of the current node */ }STRLINK; +/** + * A structure defining a range of time + */ typedef struct timerange_t{ - struct timerange_t* next; - struct tm start; - struct tm end; + struct timerange_t* next; /*< Next node in the list */ + struct tm start; /*< Start of the time range */ + struct tm end; /*< End of the time range */ }TIMERANGE; +/** + * Query speed measurement and limitation structure + */ typedef struct queryspeed_t{ - time_t first_query; - time_t triggered; - double period; - double cooldown; - int count; - int limit; - long id; - struct queryspeed_t* next; + time_t first_query; /*< Time when the first query occurred */ + time_t triggered; /*< Time when the limit was exceeded */ + double period; /*< Measurement interval in seconds */ + double cooldown;/*< Time the user is denied access for */ + int count; /*< Number of queries done */ + int limit; /*< Maximum number of queries */ + long id; /*< Unique id of the rule */ + struct queryspeed_t* next; /*< Next node in the list */ }QUERYSPEED; @@ -162,65 +177,70 @@ typedef struct queryspeed_t{ * This allows to match an arbitrary set of rules against a user. */ typedef struct rule_t{ - void* data; - char* name; - ruletype_t type; - skygw_query_op_t on_queries; - bool allow; - int times_matched; - TIMERANGE* active; + void* data; /*< Actual implementation of the rule */ + char* name; /*< Name of the rule */ + ruletype_t type;/*< Type of the rule */ + skygw_query_op_t on_queries;/*< Types of queries to inspect */ + bool allow;/*< Allow or deny the query if this rule matches */ + int times_matched;/*< Number of times this rule has been matched */ + TIMERANGE* active;/*< List of times when this rule is active */ }RULE; /** * Linked list of pointers to a global pool of RULE structs */ typedef struct rulelist_t{ - RULE* rule; - struct rulelist_t* next; + RULE* rule; /*< The rule structure */ + struct rulelist_t* next;/*< Next node in the list */ }RULELIST; typedef struct user_t{ - char* name; - SPINLOCK* lock; - QUERYSPEED* qs_limit; - RULELIST* rules_or; - RULELIST* rules_and; + char* name;/*< Name of the user */ + SPINLOCK* lock;/*< User spinlock */ + QUERYSPEED* qs_limit;/*< The query speed structure unique to this user */ + RULELIST* rules_or;/*< If any of these rules match the action is triggered */ + RULELIST* rules_and;/*< All of these rules must match for the action to trigger */ }USER; /** * Linked list of IP adresses and subnet masks */ typedef struct iprange_t{ - struct iprange_t* next; - uint32_t ip; - uint32_t mask; + struct iprange_t* next;/*< Next node in the list */ + uint32_t ip;/*< IP address */ + uint32_t mask;/*< Network mask */ }IPRANGE; /** * The Firewall filter instance. */ typedef struct { - HASHTABLE* htable; - RULELIST* rules; - STRLINK* userstrings; - bool def_op; - SPINLOCK* lock; - long idgen; /**UID generator*/ + HASHTABLE* htable; /*< User hashtable */ + RULELIST* rules;/*< List of all the rules */ + STRLINK* userstrings;/*< Temporary list of raw strings of users */ + bool def_op;/*< Default operation mode, defaults to deny */ + SPINLOCK* lock;/*< Instance spinlock */ + long idgen; /*< UID generator */ } FW_INSTANCE; /** * The session structure for Firewall filter. */ typedef struct { - SESSION* session; - char* errmsg; - DOWNSTREAM down; - UPSTREAM up; + SESSION* session;/*< Client session structure */ + char* errmsg;/*< Rule specific error message */ + DOWNSTREAM down;/*< Next object in the downstream chain */ + UPSTREAM up;/*< Next object in the upstream chain */ } FW_SESSION; static int hashkeyfun(void* key); 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( void* key) { @@ -232,9 +252,16 @@ static int hashkeyfun( while((c = *ptr++)){ 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( void* v1, void* v2) @@ -245,19 +272,6 @@ static int hashcmpfun( 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) { @@ -588,7 +602,7 @@ RULE* find_rule(char* tok, FW_INSTANCE* instance) } 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; } @@ -602,6 +616,10 @@ void add_users(char* rule, FW_INSTANCE* instance) assert(rule != NULL && instance != NULL); STRLINK* link = calloc(1,sizeof(STRLINK)); + if(link == NULL){ + skygw_log_write(LOGFILE_ERROR,"Error : Memory allocation failed"); + return; + } link->next = instance->userstrings; link->value = strdup(rule); instance->userstrings = link; @@ -838,20 +856,21 @@ void parse_rule(char* rule, FW_INSTANCE* instance) regex_t *re; char* start, *str; tok = strtok(NULL," "); - + char delim = '\''; while(*tok == '\'' || *tok == '"'){ + delim = *tok; tok++; } start = tok; - while(isspace(*tok) || *tok == '\'' || *tok == '"'){ + while(isspace(*tok) || *tok == delim){ tok++; } while(true){ - if((*tok == '\'' || *tok == '"') && !escaped){ + if((*tok == delim) && !escaped){ break; } escaped = (*tok == '\\'); @@ -896,11 +915,23 @@ void parse_rule(char* rule, FW_INSTANCE* instance) spinlock_release(instance->lock); tok = strtok(NULL," "); + if(tok == NULL){ + free(qs); + goto retblock; + } qs->limit = atoi(tok); tok = strtok(NULL," "); + if(tok == NULL){ + free(qs); + goto retblock; + } qs->period = atof(tok); tok = strtok(NULL," "); + if(tok == NULL){ + free(qs); + goto retblock; + } qs->cooldown = atof(tok); ruledef->type = RT_THROTTLE; ruledef->data = (void*)qs; @@ -958,13 +989,13 @@ createInstance(char **options, FILTER_PARAMETER **params) 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."); free(my_instance); 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->def_op = true; @@ -979,18 +1010,22 @@ createInstance(char **options, FILTER_PARAMETER **params) } } + 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="); + hashtable_free(my_instance->htable); free(my_instance); return NULL; } if((file = fopen(filename,"rb")) == NULL ){ skygw_log_write(LOGFILE_ERROR, "Error while opening rule file for firewall filter."); - free(my_instance); - free(filename); - return NULL; + hashtable_free(my_instance->htable); + free(my_instance); + free(filename); + return NULL; } free(filename); @@ -1002,6 +1037,7 @@ createInstance(char **options, FILTER_PARAMETER **params) if(ferror(file)){ skygw_log_write(LOGFILE_ERROR, "Error while reading rule file for firewall filter."); fclose(file); + hashtable_free(my_instance->htable); free(my_instance); return NULL; } @@ -1037,9 +1073,7 @@ createInstance(char **options, FILTER_PARAMETER **params) /** - * Associate a new session with this instance of the filter and opens - * a connection to the server and prepares the exchange and the queue for use. - * + * Associate a new session with this instance of the filter. * * @param instance The filter instance data * @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); queryspeed = user->qs_limit; - - + spinlock_release(user->lock); + while(queryspeed){ if(queryspeed->id == rule_qs->id){ break; } queryspeed = queryspeed->next; } - + + + if(queryspeed == NULL){ /**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; } - spinlock_release(user->lock); + break; case RT_CLAUSE: