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:
```
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|grant|revoke|drop|create|alter|use|load]]
rule NAME deny RULE [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.
You can add comments to the rule files by adding the `#` character at
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
```
#### `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`
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;
TemplateList templates;
ValueList values;
ValueList auxiliary_values;
std::string name;
};
@ -714,6 +715,13 @@ void push_value(void* scanner, char* 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
* @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)));
}
/**
* 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
* @param scanner Current scanner

View File

@ -37,12 +37,14 @@ void dbfw_yyerror(void* scanner, const char* error);
/** Rule creation and definition functions */
bool set_rule_name(void* scanner, char* name);
void push_value(void* scanner, char* value);
void push_auxiliary_value(void* scanner, char* value);
void define_wildcard_rule(void* scanner);
void define_where_clause_rule(void* scanner);
bool define_regex_rule(void* scanner, char* pattern);
void define_columns_rule(void* scanner);
void define_function_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);
bool add_at_times_rule(void* scanner, const char* range);
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_FUNCTION valuelist {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);}
;
@ -140,6 +141,17 @@ valuelist
| 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
: FWTOK_AT_TIMES timelist

View File

@ -239,6 +239,47 @@ bool FunctionUsageRule::matches_query(DbfwSession* session, GWBUF* buffer, char*
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
{
QuerySpeed* queryspeed = session->query_speed();

View File

@ -158,7 +158,6 @@ public:
bool matches_query(DbfwSession* session, GWBUF* buffer, char** msg) const;
};
/**
* Matches if a query uses any functions
*/
@ -176,6 +175,26 @@ public:
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