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:
parent
a955e4a623
commit
1fcf4ef59a
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user