diff --git a/server/modules/filter/dbfwfilter/CMakeLists.txt b/server/modules/filter/dbfwfilter/CMakeLists.txt index 64c31a80b..4bcb40deb 100644 --- a/server/modules/filter/dbfwfilter/CMakeLists.txt +++ b/server/modules/filter/dbfwfilter/CMakeLists.txt @@ -7,13 +7,13 @@ if(BISON_FOUND AND FLEX_FOUND) include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) add_library(dbfwfilter-core STATIC ${BISON_ruleparser_OUTPUTS} ${FLEX_token_OUTPUTS}) - add_library(dbfwfilter SHARED dbfwfilter.cc) + add_library(dbfwfilter SHARED dbfwfilter.cc rules.cc) target_link_libraries(dbfwfilter maxscale-common MySQLCommon dbfwfilter-core) set_target_properties(dbfwfilter PROPERTIES VERSION "1.0.0") install_module(dbfwfilter core) # The offline rule check utility - add_executable(dbfwchk dbfw_rule_check.cc) + add_executable(dbfwchk dbfw_rule_check.cc rules.cc) target_link_libraries(dbfwchk maxscale-common MySQLCommon dbfwfilter-core) install_executable(dbfwchk core) diff --git a/server/modules/filter/dbfwfilter/dbfwfilter.cc b/server/modules/filter/dbfwfilter/dbfwfilter.cc index c8bdbf593..573734033 100644 --- a/server/modules/filter/dbfwfilter/dbfwfilter.cc +++ b/server/modules/filter/dbfwfilter/dbfwfilter.cc @@ -60,32 +60,24 @@ *@endcode */ -#define MXS_MODULE_NAME "dbfwfilter" -#include +#include "dbfwfilter.hh" #include #include -#include #include -#include -#include -#include -#include -#include #include #include #include #include -#include #include #include -#include #include #include #include -#include "dbfwfilter.h" +#include "rules.hh" +#include "users.hh" MXS_BEGIN_DECLS #include "ruleparser.yy.h" @@ -110,117 +102,7 @@ static void diagnostic(MXS_FILTER *instance, MXS_FILTER_SESSION *fsession, DCB * static json_t* diagnostic_json(const MXS_FILTER *instance, const MXS_FILTER_SESSION *fsession); static uint64_t getCapabilities(MXS_FILTER* instance); -/** - * Rule types - */ -typedef enum -{ - RT_UNDEFINED = 0x00, /*< Undefined rule */ - RT_COLUMN, /*< Column name rule*/ - RT_FUNCTION, /*< Function name rule*/ - RT_USES_FUNCTION, /*< Function usage 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; - -/** - * What operator a rule should apply to. - * - * Note that each operator is represented by a unique bit, so that they - * can be combined as a bitmask, while query_op_t enumeration of the query - * classifier consists of a sequence of unique numbers. - */ -typedef enum fw_op -{ - FW_OP_UNDEFINED = 0, - - // NOTE: If you add something here, check the 'qc_op_to_fw_op' function below. - FW_OP_ALTER = (1 << 0), - FW_OP_CHANGE_DB = (1 << 1), - FW_OP_CREATE = (1 << 2), - FW_OP_DELETE = (1 << 3), - FW_OP_DROP = (1 << 4), - FW_OP_GRANT = (1 << 5), - FW_OP_INSERT = (1 << 6), - FW_OP_LOAD = (1 << 7), - FW_OP_REVOKE = (1 << 8), - FW_OP_SELECT = (1 << 9), - FW_OP_UPDATE = (1 << 10), -} fw_op_t; - -/** - * Convert a qc_query_op_t to the equivalent fw_op_t. - * - * @param op A query classifier operator. - * - * @return The corresponding bit value. - */ -static inline fw_op_t qc_op_to_fw_op(qc_query_op_t op) -{ - switch (op) - { - case QUERY_OP_ALTER: - return FW_OP_ALTER; - - case QUERY_OP_CHANGE_DB: - return FW_OP_CHANGE_DB; - - case QUERY_OP_CREATE: - return FW_OP_CREATE; - - case QUERY_OP_DELETE: - return FW_OP_DELETE; - - case QUERY_OP_DROP: - return FW_OP_DROP; - - case QUERY_OP_GRANT: - return FW_OP_GRANT; - - case QUERY_OP_INSERT: - return FW_OP_INSERT; - - case QUERY_OP_LOAD: - return FW_OP_LOAD; - - case QUERY_OP_REVOKE: - return FW_OP_REVOKE; - - case QUERY_OP_SELECT: - return FW_OP_SELECT; - - case QUERY_OP_UPDATE: - return FW_OP_UPDATE; - - default: - return FW_OP_UNDEFINED; - }; -} - -/** - * Possible actions to take when the query matches a rule - */ -enum fw_actions -{ - FW_ACTION_ALLOW, - FW_ACTION_BLOCK, - FW_ACTION_IGNORE -}; - -/** - * Logging options for matched queries - */ -#define FW_LOG_NONE 0x00 -#define FW_LOG_MATCH 0x01 -#define FW_LOG_NO_MATCH 0x02 - -/** Maximum length of the match/nomatch messages */ -#define FW_MAX_SQL_LEN 400 - -const char* rule_names[] = +static const char* rule_names[] = { "UNDEFINED", "COLUMN", @@ -234,190 +116,6 @@ const char* rule_names[] = const int rule_names_len = sizeof(rule_names) / sizeof(char**); -/** - * Linked list of strings. - */ -typedef struct strlink_t -{ - 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; /*< 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 when the first query occurred */ - time_t triggered; /*< Time when the limit was exceeded */ - int period; /*< Measurement interval in seconds */ - int 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 */ - bool active; /*< If the rule has been triggered */ -} QUERYSPEED; - -/** - * A structure used to identify individual rules and to store their contents - * - * Each type of rule has different requirements that are expressed as void pointers. - * This allows to match an arbitrary set of rules against a user. - */ -struct Rule -{ - Rule(std::string name): - data(NULL), - name(name), - type(RT_PERMISSION), - on_queries(FW_OP_UNDEFINED), - times_matched(0), - active(NULL) - { - } - - virtual ~Rule() - { - } - - virtual bool matches_query(GWBUF* buffer, char** msg) - { - *msg = create_error("Permission denied at this time."); - MXS_NOTICE("rule '%s': query denied at this time.", rule->name.c_str()); - return true; - } - - virtual bool need_full_parsing(GWBUF* buffer) const - { - bool rval = false; - qc_query_op_t optype = qc_get_operation(buffer); - - if (type == RT_COLUMN || - type == RT_FUNCTION || - type == RT_USES_FUNCTION || - type == RT_WILDCARD || - type == RT_CLAUSE) - { - switch (optype) - { - case QUERY_OP_SELECT: - case QUERY_OP_UPDATE: - case QUERY_OP_INSERT: - case QUERY_OP_DELETE: - rval = true; - break; - - default: - break; - } - } - - return rval; - } - - bool matches_query_type(GWBUF* buffer) - { - qc_query_op_t optype = qc_get_operation(buffer); - - return on_queries == FW_OP_UNDEFINED || - (on_queries & qc_op_to_fw_op(optype)) || - (MYSQL_IS_COM_INIT_DB(GWBUF_DATA(buffer)) && - (on_queries & FW_OP_CHANGE_DB)); - } - - void* data; /*< Actual implementation of the rule */ - std::string name; /*< Name of the rule */ - ruletype_t type; /*< Type of the rule */ - uint32_t on_queries; /*< Types of queries to inspect */ - int times_matched; /*< Number of times this rule has been matched */ - TIMERANGE* active; /*< List of times when this rule is active */ -}; - -typedef std::tr1::shared_ptr SRule; -typedef std::list RuleList; - -/** Typedef for a list of strings */ -typedef std::list ValueList; - -/** - * A temporary template structure used in the creation of actual users. - * This is also used to link the user definitions with the rules. - * @see struct user_t - */ -struct UserTemplate -{ - std::string name; /** Name of the user */ - enum match_type type; /** Matching type */ - ValueList rulenames; /** Names of the rules */ -}; - -typedef std::tr1::shared_ptr SUserTemplate; -typedef std::list TemplateList; - -/** - * A user definition - */ -struct User -{ - User(std::string name): - name(name), - lock(SPINLOCK_INIT), - qs_limit(NULL) - { - } - - ~User() - { - MXS_FREE(qs_limit); - } - - std::string 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 */ - RuleList rules_strict_and; /*< rules that skip the rest of the rules if one of them - * fails. This is only for rules paired with 'match strict_all'. */ -}; - -typedef std::tr1::shared_ptr SUser; -typedef std::tr1::unordered_map UserMap; - -/** - * The Firewall filter instance. - */ -typedef struct -{ - enum fw_actions action; /*< Default operation mode, defaults to deny */ - int log_match; /*< Log matching and/or non-matching queries */ - SPINLOCK lock; /*< Instance spinlock */ - int idgen; /*< UID generator */ - char *rulefile; /*< Path to the rule file */ - int rule_version; /*< Latest rule file version, incremented on reload */ -} FW_INSTANCE; - -/** - * The session structure for Firewall filter. - */ -typedef struct -{ - MXS_SESSION *session; /*< Client session structure */ - char *errmsg; /*< Rule specific error message */ - QUERYSPEED *query_speed; /*< How fast the user has executed queries */ - MXS_DOWNSTREAM down; /*< Next object in the downstream chain */ - MXS_UPSTREAM up; /*< Next object in the upstream chain */ -} FW_SESSION; - /** The rules and users for each thread */ thread_local struct { @@ -432,6 +130,11 @@ static void rule_free_all(Rule* rule); static bool process_rule_file(const char* filename, RuleList* rules, UserMap* users); bool replace_rules(FW_INSTANCE* instance); +static inline bool query_is_sql(GWBUF* query) +{ + return modutil_is_SQL(query) || modutil_is_SQL_prepare(query); +} + static void print_rule(Rule *rules, char *dest) { int type = 0; @@ -1846,7 +1549,7 @@ bool rule_is_active(SRule rule) * * @return Pointer to newly allocated and formatted string */ -static char* create_error(const char* format, ...) +char* create_error(const char* format, ...) { va_list valist; va_start(valist, format); diff --git a/server/modules/filter/dbfwfilter/dbfwfilter.h b/server/modules/filter/dbfwfilter/dbfwfilter.h index 87a2fbb8b..9c21aa1ff 100644 --- a/server/modules/filter/dbfwfilter/dbfwfilter.h +++ b/server/modules/filter/dbfwfilter/dbfwfilter.h @@ -20,7 +20,6 @@ */ #include -#include MXS_BEGIN_DECLS diff --git a/server/modules/filter/dbfwfilter/dbfwfilter.hh b/server/modules/filter/dbfwfilter/dbfwfilter.hh new file mode 100644 index 000000000..839374e08 --- /dev/null +++ b/server/modules/filter/dbfwfilter/dbfwfilter.hh @@ -0,0 +1,203 @@ +#pragma once +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2020-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#define MXS_MODULE_NAME "dbfwfilter" +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "dbfwfilter.h" + +/** + * Rule types + */ +typedef enum +{ + RT_UNDEFINED = 0x00, /*< Undefined rule */ + RT_COLUMN, /*< Column name rule*/ + RT_FUNCTION, /*< Function name rule*/ + RT_USES_FUNCTION, /*< Function usage 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; + +/** + * What operator a rule should apply to. + * + * Note that each operator is represented by a unique bit, so that they + * can be combined as a bitmask, while query_op_t enumeration of the query + * classifier consists of a sequence of unique numbers. + */ +typedef enum fw_op +{ + FW_OP_UNDEFINED = 0, + + // NOTE: If you add something here, check the 'qc_op_to_fw_op' function below. + FW_OP_ALTER = (1 << 0), + FW_OP_CHANGE_DB = (1 << 1), + FW_OP_CREATE = (1 << 2), + FW_OP_DELETE = (1 << 3), + FW_OP_DROP = (1 << 4), + FW_OP_GRANT = (1 << 5), + FW_OP_INSERT = (1 << 6), + FW_OP_LOAD = (1 << 7), + FW_OP_REVOKE = (1 << 8), + FW_OP_SELECT = (1 << 9), + FW_OP_UPDATE = (1 << 10), +} fw_op_t; + +/** + * Convert a qc_query_op_t to the equivalent fw_op_t. + * + * @param op A query classifier operator. + * + * @return The corresponding bit value. + */ +static inline fw_op_t qc_op_to_fw_op(qc_query_op_t op) +{ + switch (op) + { + case QUERY_OP_ALTER: + return FW_OP_ALTER; + + case QUERY_OP_CHANGE_DB: + return FW_OP_CHANGE_DB; + + case QUERY_OP_CREATE: + return FW_OP_CREATE; + + case QUERY_OP_DELETE: + return FW_OP_DELETE; + + case QUERY_OP_DROP: + return FW_OP_DROP; + + case QUERY_OP_GRANT: + return FW_OP_GRANT; + + case QUERY_OP_INSERT: + return FW_OP_INSERT; + + case QUERY_OP_LOAD: + return FW_OP_LOAD; + + case QUERY_OP_REVOKE: + return FW_OP_REVOKE; + + case QUERY_OP_SELECT: + return FW_OP_SELECT; + + case QUERY_OP_UPDATE: + return FW_OP_UPDATE; + + default: + return FW_OP_UNDEFINED; + }; +} + +/** + * Possible actions to take when the query matches a rule + */ +enum fw_actions +{ + FW_ACTION_ALLOW, + FW_ACTION_BLOCK, + FW_ACTION_IGNORE +}; + +/** + * Logging options for matched queries + */ +#define FW_LOG_NONE 0x00 +#define FW_LOG_MATCH 0x01 +#define FW_LOG_NO_MATCH 0x02 + +/** Maximum length of the match/nomatch messages */ +#define FW_MAX_SQL_LEN 400 + +/** + * Linked list of strings. + */ +typedef struct strlink_t +{ + 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; /*< 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 when the first query occurred */ + time_t triggered; /*< Time when the limit was exceeded */ + int period; /*< Measurement interval in seconds */ + int 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 */ + bool active; /*< If the rule has been triggered */ +} QUERYSPEED; + +/** + * The Firewall filter instance. + */ +typedef struct +{ + enum fw_actions action; /*< Default operation mode, defaults to deny */ + int log_match; /*< Log matching and/or non-matching queries */ + SPINLOCK lock; /*< Instance spinlock */ + int idgen; /*< UID generator */ + char *rulefile; /*< Path to the rule file */ + int rule_version; /*< Latest rule file version, incremented on reload */ +} FW_INSTANCE; + +/** + * The session structure for Firewall filter. + */ +typedef struct +{ + MXS_SESSION *session; /*< Client session structure */ + char *errmsg; /*< Rule specific error message */ + QUERYSPEED *query_speed; /*< How fast the user has executed queries */ + MXS_DOWNSTREAM down; /*< Next object in the downstream chain */ + MXS_UPSTREAM up; /*< Next object in the upstream chain */ +} FW_SESSION; + +/** Typedef for a list of strings */ +typedef std::list ValueList; + +/** Helper function for strdup'ing in printf style */ +char* create_error(const char* format, ...); diff --git a/server/modules/filter/dbfwfilter/rules.cc b/server/modules/filter/dbfwfilter/rules.cc new file mode 100644 index 000000000..8b66a07d4 --- /dev/null +++ b/server/modules/filter/dbfwfilter/rules.cc @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2020-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include "rules.hh" + +#include +#include +#include + +Rule::Rule(std::string name): + data(NULL), + name(name), + type(RT_PERMISSION), + on_queries(FW_OP_UNDEFINED), + times_matched(0), + active(NULL) +{ +} + +Rule::~Rule() +{ +} + +bool Rule::matches_query(GWBUF* buffer, char** msg) +{ + *msg = create_error("Permission denied at this time."); + MXS_NOTICE("rule '%s': query denied at this time.", name.c_str()); + return true; +} + +bool Rule::need_full_parsing(GWBUF* buffer) const +{ + bool rval = false; + + if (type == RT_COLUMN || + type == RT_FUNCTION || + type == RT_USES_FUNCTION || + type == RT_WILDCARD || + type == RT_CLAUSE) + { + switch (qc_get_operation(buffer)) + { + case QUERY_OP_SELECT: + case QUERY_OP_UPDATE: + case QUERY_OP_INSERT: + case QUERY_OP_DELETE: + rval = true; + break; + + default: + break; + } + } + + return rval; +} + +bool Rule::matches_query_type(GWBUF* buffer) +{ + qc_query_op_t optype = qc_get_operation(buffer); + + return on_queries == FW_OP_UNDEFINED || + (on_queries & qc_op_to_fw_op(optype)) || + (MYSQL_IS_COM_INIT_DB(GWBUF_DATA(buffer)) && + (on_queries & FW_OP_CHANGE_DB)); +} diff --git a/server/modules/filter/dbfwfilter/rules.hh b/server/modules/filter/dbfwfilter/rules.hh new file mode 100644 index 000000000..5ba6dfff2 --- /dev/null +++ b/server/modules/filter/dbfwfilter/rules.hh @@ -0,0 +1,40 @@ +#pragma once +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2020-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include "dbfwfilter.hh" + +/** + * A structure used to identify individual rules and to store their contents + * + * Each type of rule has different requirements that are expressed as void pointers. + * This allows to match an arbitrary set of rules against a user. + */ +struct Rule +{ + Rule(std::string name); + virtual ~Rule(); + virtual bool matches_query(GWBUF* buffer, char** msg); + virtual bool need_full_parsing(GWBUF* buffer) const; + bool matches_query_type(GWBUF* buffer); + + void* data; /*< Actual implementation of the rule */ + std::string name; /*< Name of the rule */ + ruletype_t type; /*< Type of the rule */ + uint32_t on_queries; /*< Types of queries to inspect */ + int times_matched; /*< Number of times this rule has been matched */ + TIMERANGE* active; /*< List of times when this rule is active */ +}; + +typedef std::tr1::shared_ptr SRule; +typedef std::list RuleList; diff --git a/server/modules/filter/dbfwfilter/users.hh b/server/modules/filter/dbfwfilter/users.hh new file mode 100644 index 000000000..52b487b1f --- /dev/null +++ b/server/modules/filter/dbfwfilter/users.hh @@ -0,0 +1,60 @@ +#pragma once +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl11. + * + * Change Date: 2020-01-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include "dbfwfilter.hh" +#include "rules.hh" + +/** + * A temporary template structure used in the creation of actual users. + * This is also used to link the user definitions with the rules. + * @see struct user_t + */ +struct UserTemplate +{ + std::string name; /** Name of the user */ + enum match_type type; /** Matching type */ + ValueList rulenames; /** Names of the rules */ +}; + +typedef std::tr1::shared_ptr SUserTemplate; +typedef std::list TemplateList; + +/** + * A user definition + */ +struct User +{ + User(std::string name): + name(name), + lock(SPINLOCK_INIT), + qs_limit(NULL) + { + } + + ~User() + { + delete qs_limit; + } + + std::string 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 */ + RuleList rules_strict_and; /*< rules that skip the rest of the rules if one of them + * fails. This is only for rules paired with 'match strict_all'. */ +}; + +typedef std::tr1::shared_ptr SUser; +typedef std::tr1::unordered_map UserMap;