MXS-1346: Allow combination of function and columns rules

The `function` type rule can now be combined with the `columns` type rule
to form a new rule which matches if specific columns use specific
functions.
This commit is contained in:
Markus Mäkelä
2017-09-05 09:20:55 +03:00
parent a955e4a623
commit 1fcf4ef59a
6 changed files with 113 additions and 4 deletions

View File

@ -104,11 +104,11 @@ logged. The log messages are logged at the notice level.
The rules are defined by using the following syntax: The rules are defined by using the following syntax:
``` ```
rule NAME deny { wildcard | columns VALUE... | rule NAME deny RULE [at_times VALUE...] [on_queries {select|update|insert|delete|grant|revoke|drop|create|alter|use|load}]
regex REGEX | limit_queries COUNT TIMEPERIOD HOLDOFF |
no_where_clause} [at_times VALUE...] [on_queries [select|update|insert|delete|grant|revoke|drop|create|alter|use|load]]
``` ```
Where _NAME_ is the identifier for this rule and _RULE_ is the mandatory rule definition.
Rules are identified by their name and have mandatory parts and optional parts. Rules are identified by their name and have mandatory parts and optional parts.
You can add comments to the rule files by adding the `#` character at You can add comments to the rule files by adding the `#` character at
the beginning of the line. Trailing comments are not supported. the beginning of the line. Trailing comments are not supported.
@ -197,6 +197,20 @@ Deny function usage with _name_ and _address_ columns:
rule examplerule deny uses_function name address rule examplerule deny uses_function name address
``` ```
#### `function` and `columns`
This rule combines the `function` and `columns` type rules to match if one
of the listed columns uses one of the listed functions. The rule expects
the `function` and `columns` keywords both followed by a list of values.
##### Example
Deny use of the _sum_ function with _name_ or _address_ columns:
```
rule examplerule deny function sum columns name address
```
#### `regex` #### `regex`
This rule blocks all queries matching a regex enclosed in single or double This rule blocks all queries matching a regex enclosed in single or double

View File

@ -627,6 +627,7 @@ struct parser_stack
enum match_type active_mode; enum match_type active_mode;
TemplateList templates; TemplateList templates;
ValueList values; ValueList values;
ValueList auxiliary_values;
std::string name; std::string name;
}; };
@ -714,6 +715,13 @@ void push_value(void* scanner, char* value)
rstack->values.push_back(strip_backticks(value)); rstack->values.push_back(strip_backticks(value));
} }
void push_auxiliary_value(void* scanner, char* value)
{
struct parser_stack* rstack = (struct parser_stack*)dbfw_yyget_extra((yyscan_t)scanner);
ss_dassert(rstack);
rstack->auxiliary_values.push_back(strip_backticks(value));
}
/** /**
* Add a user to the current rule linking expression * Add a user to the current rule linking expression
* @param scanner Current scanner * @param scanner Current scanner
@ -849,6 +857,19 @@ void define_function_usage_rule(void* scanner)
rstack->rule.push_front(SRule(new FunctionUsageRule(rstack->name, rstack->values))); rstack->rule.push_front(SRule(new FunctionUsageRule(rstack->name, rstack->values)));
} }
/**
* Define the current rule as a function rule
* @param scanner Current scanner
* @param columns List of function names
*/
void define_column_function_rule(void* scanner)
{
struct parser_stack* rstack = (struct parser_stack*)dbfw_yyget_extra((yyscan_t) scanner);
ss_dassert(rstack);
rstack->rule.push_front(SRule(new ColumnFunctionRule(rstack->name, rstack->values,
rstack->auxiliary_values)));
}
/** /**
* Define the topmost rule as a no_where_clause rule * Define the topmost rule as a no_where_clause rule
* @param scanner Current scanner * @param scanner Current scanner

View File

@ -37,12 +37,14 @@ void dbfw_yyerror(void* scanner, const char* error);
/** Rule creation and definition functions */ /** Rule creation and definition functions */
bool set_rule_name(void* scanner, char* name); bool set_rule_name(void* scanner, char* name);
void push_value(void* scanner, char* value); void push_value(void* scanner, char* value);
void push_auxiliary_value(void* scanner, char* value);
void define_wildcard_rule(void* scanner); void define_wildcard_rule(void* scanner);
void define_where_clause_rule(void* scanner); void define_where_clause_rule(void* scanner);
bool define_regex_rule(void* scanner, char* pattern); bool define_regex_rule(void* scanner, char* pattern);
void define_columns_rule(void* scanner); void define_columns_rule(void* scanner);
void define_function_rule(void* scanner); void define_function_rule(void* scanner);
void define_function_usage_rule(void* scanner); void define_function_usage_rule(void* scanner);
void define_column_function_rule(void* scanner);
void define_limit_queries_rule(void* scanner, int max, int timeperiod, int holdoff); void define_limit_queries_rule(void* scanner, int max, int timeperiod, int holdoff);
bool add_at_times_rule(void* scanner, const char* range); bool add_at_times_rule(void* scanner, const char* range);
void add_on_queries_rule(void* scanner, const char* sql); void add_on_queries_rule(void* scanner, const char* sql);

View File

@ -126,6 +126,7 @@ mandatory
| FWTOK_COLUMNS valuelist {define_columns_rule(scanner);} | FWTOK_COLUMNS valuelist {define_columns_rule(scanner);}
| FWTOK_FUNCTION valuelist {define_function_rule(scanner);} | FWTOK_FUNCTION valuelist {define_function_rule(scanner);}
| FWTOK_FUNCTION {define_function_rule(scanner);} | FWTOK_FUNCTION {define_function_rule(scanner);}
| FWTOK_FUNCTION valuelist FWTOK_COLUMNS auxiliaryvaluelist {define_column_function_rule(scanner);}
| FWTOK_USES_FUNCTION valuelist {define_function_usage_rule(scanner);} | FWTOK_USES_FUNCTION valuelist {define_function_usage_rule(scanner);}
; ;
@ -140,6 +141,17 @@ valuelist
| valuelist value | valuelist value
; ;
auxiliaryvalue
: FWTOK_CMP {push_auxiliary_value(scanner, $1);}
| FWTOK_STR {push_auxiliary_value(scanner, $1);}
| FWTOK_BTSTR {push_auxiliary_value(scanner, $1);}
;
auxiliaryvaluelist
: auxiliaryvalue
| auxiliaryvaluelist auxiliaryvalue
;
/** Optional parts of a rule */ /** Optional parts of a rule */
optional optional
: FWTOK_AT_TIMES timelist : FWTOK_AT_TIMES timelist

View File

@ -239,6 +239,47 @@ bool FunctionUsageRule::matches_query(DbfwSession* session, GWBUF* buffer, char*
return false; return false;
} }
bool ColumnFunctionRule::matches_query(DbfwSession* session, GWBUF* buffer, char** msg) const
{
if (query_is_sql(buffer))
{
const QC_FUNCTION_INFO* infos;
size_t n_infos;
qc_get_function_info(buffer, &infos, &n_infos);
for (size_t i = 0; i < n_infos; ++i)
{
ValueList::const_iterator func_it = std::find(m_values.begin(),
m_values.end(),
infos[i].name);
if (func_it != m_values.end())
{
/** The function matches, now check if the column matches */
for (size_t j = 0; j < infos[i].n_fields; j++)
{
ValueList::const_iterator col_it = std::find(m_columns.begin(),
m_columns.end(),
infos[i].fields[j].column);
if (col_it != m_columns.end())
{
MXS_NOTICE("rule '%s': query uses function '%s' with forbidden column: %s",
name().c_str(), func_it->c_str(), col_it->c_str());
*msg = create_error("Permission denied to column '%s' with function '%s'.",
col_it->c_str(), func_it->c_str());
return true;
}
}
}
}
}
return false;
}
bool LimitQueriesRule::matches_query(DbfwSession* session, GWBUF* buffer, char** msg) const bool LimitQueriesRule::matches_query(DbfwSession* session, GWBUF* buffer, char** msg) const
{ {
QuerySpeed* queryspeed = session->query_speed(); QuerySpeed* queryspeed = session->query_speed();

View File

@ -158,7 +158,6 @@ public:
bool matches_query(DbfwSession* session, GWBUF* buffer, char** msg) const; bool matches_query(DbfwSession* session, GWBUF* buffer, char** msg) const;
}; };
/** /**
* Matches if a query uses any functions * Matches if a query uses any functions
*/ */
@ -176,6 +175,26 @@ public:
bool matches_query(DbfwSession* session, GWBUF* buffer, char** msg) const; bool matches_query(DbfwSession* session, GWBUF* buffer, char** msg) const;
}; };
/**
* Matches if a query uses a function with a specific column
*/
class ColumnFunctionRule: public ValueListRule
{
ColumnFunctionRule(const ColumnFunctionRule&);
ColumnFunctionRule& operator=(const ColumnFunctionRule&);
public:
ColumnFunctionRule(std::string name, const ValueList& values, const ValueList& columns):
ValueListRule(name, "COLUMN_FUNCTION", values),
m_columns(columns)
{
}
bool matches_query(DbfwSession* session, GWBUF* buffer, char** msg) const;
private:
ValueList m_columns; /*< List of columns to match */
};
/** /**
* Matches if a queries are executed too quickly * Matches if a queries are executed too quickly