Merge commit 'a78f0fbe2537542dc7f3f0dd8b19b93ac8d9d7f8' into develop
This commit is contained in:
@ -405,6 +405,12 @@ static bool should_skip_query(const BinlogConfig& config, const std::string& sql
|
||||
qc_free_table_names(names, n);
|
||||
}
|
||||
|
||||
// Also check for the default database in case the query has no tables in it
|
||||
if (!rval && should_skip(config, db))
|
||||
{
|
||||
rval = true;
|
||||
}
|
||||
|
||||
gwbuf_free(buf);
|
||||
return rval;
|
||||
}
|
||||
|
||||
@ -127,6 +127,18 @@ extern "C" MXS_MODULE* MXS_CREATE_MODULE()
|
||||
Config::check_user_variables_default,
|
||||
MXS_MODULE_OPT_NONE,
|
||||
},
|
||||
{
|
||||
Config::check_unions_name,
|
||||
MXS_MODULE_PARAM_BOOL,
|
||||
Config::check_unions_default,
|
||||
MXS_MODULE_OPT_NONE,
|
||||
},
|
||||
{
|
||||
Config::check_subqueries_name,
|
||||
MXS_MODULE_PARAM_BOOL,
|
||||
Config::check_subqueries_default,
|
||||
MXS_MODULE_OPT_NONE,
|
||||
},
|
||||
{MXS_END_MODULE_PARAMS}
|
||||
}
|
||||
};
|
||||
|
||||
@ -28,6 +28,8 @@ const char config_value_always[] = "always";
|
||||
|
||||
const char config_name_prevent_function_usage[] = "prevent_function_usage";
|
||||
const char config_check_user_variables[] = "check_user_variables";
|
||||
const char config_check_unions[] = "check_unions";
|
||||
const char config_check_subqueries[] = "check_subqueries";
|
||||
|
||||
const char config_value_true[] = "true";
|
||||
}
|
||||
@ -88,11 +90,31 @@ const char* MaskingFilterConfig::prevent_function_usage_default = config_value_t
|
||||
/*
|
||||
* PARAM check_user_variables
|
||||
*/
|
||||
// static
|
||||
const char* MaskingFilterConfig::check_user_variables_name = config_check_user_variables;
|
||||
|
||||
// static
|
||||
const char* MaskingFilterConfig::check_user_variables_default = config_value_true;
|
||||
|
||||
/*
|
||||
* PARAM check_unions
|
||||
*/
|
||||
// static
|
||||
const char* MaskingFilterConfig::check_unions_name = config_check_unions;
|
||||
|
||||
// static
|
||||
const char* MaskingFilterConfig::check_unions_default = config_value_true;
|
||||
|
||||
/*
|
||||
* PARAM check_subqueries
|
||||
*/
|
||||
// static
|
||||
const char* MaskingFilterConfig::check_subqueries_name = config_check_subqueries;
|
||||
|
||||
// static
|
||||
const char* MaskingFilterConfig::check_subqueries_default = config_value_true;
|
||||
|
||||
|
||||
/*
|
||||
* MaskingFilterConfig
|
||||
*/
|
||||
@ -130,3 +152,15 @@ bool MaskingFilterConfig::get_check_user_variables(const MXS_CONFIG_PARAMETER* p
|
||||
{
|
||||
return pParams->get_bool(check_user_variables_name);
|
||||
}
|
||||
|
||||
// static
|
||||
bool MaskingFilterConfig::get_check_unions(const MXS_CONFIG_PARAMETER* pParams)
|
||||
{
|
||||
return pParams->get_bool(check_unions_name);
|
||||
}
|
||||
|
||||
// static
|
||||
bool MaskingFilterConfig::get_check_subqueries(const MXS_CONFIG_PARAMETER* pParams)
|
||||
{
|
||||
return pParams->get_bool(check_subqueries_name);
|
||||
}
|
||||
|
||||
@ -48,6 +48,12 @@ public:
|
||||
static const char* check_user_variables_name;
|
||||
static const char* check_user_variables_default;
|
||||
|
||||
static const char* check_unions_name;
|
||||
static const char* check_unions_default;
|
||||
|
||||
static const char* check_subqueries_name;
|
||||
static const char* check_subqueries_default;
|
||||
|
||||
MaskingFilterConfig(const char* zName, const MXS_CONFIG_PARAMETER* pParams)
|
||||
: m_name(zName)
|
||||
, m_large_payload(get_large_payload(pParams))
|
||||
@ -55,8 +61,11 @@ public:
|
||||
, m_warn_type_mismatch(get_warn_type_mismatch(pParams))
|
||||
, m_prevent_function_usage(get_prevent_function_usage(pParams))
|
||||
, m_check_user_variables(get_check_user_variables(pParams))
|
||||
, m_check_unions(get_check_unions(pParams))
|
||||
, m_check_subqueries(get_check_subqueries(pParams))
|
||||
{
|
||||
}
|
||||
|
||||
~MaskingFilterConfig()
|
||||
{
|
||||
}
|
||||
@ -91,6 +100,16 @@ public:
|
||||
return m_check_user_variables;
|
||||
}
|
||||
|
||||
bool check_unions() const
|
||||
{
|
||||
return m_check_unions;
|
||||
}
|
||||
|
||||
bool check_subqueries() const
|
||||
{
|
||||
return m_check_subqueries;
|
||||
}
|
||||
|
||||
void set_large_payload(large_payload_t l)
|
||||
{
|
||||
m_large_payload = l;
|
||||
@ -115,9 +134,19 @@ public:
|
||||
m_check_user_variables = b;
|
||||
}
|
||||
|
||||
void set_check_unions(bool b)
|
||||
{
|
||||
m_check_unions = b;
|
||||
}
|
||||
|
||||
void set_check_subqueries(bool b)
|
||||
{
|
||||
m_check_subqueries = b;
|
||||
}
|
||||
|
||||
bool is_parsing_needed() const
|
||||
{
|
||||
return prevent_function_usage() || check_user_variables();
|
||||
return prevent_function_usage() || check_user_variables() || check_unions() || check_subqueries();
|
||||
}
|
||||
|
||||
static large_payload_t get_large_payload(const MXS_CONFIG_PARAMETER* pParams);
|
||||
@ -125,6 +154,8 @@ public:
|
||||
static warn_type_mismatch_t get_warn_type_mismatch(const MXS_CONFIG_PARAMETER* pParams);
|
||||
static bool get_prevent_function_usage(const MXS_CONFIG_PARAMETER* pParams);
|
||||
static bool get_check_user_variables(const MXS_CONFIG_PARAMETER* pParams);
|
||||
static bool get_check_unions(const MXS_CONFIG_PARAMETER* pParams);
|
||||
static bool get_check_subqueries(const MXS_CONFIG_PARAMETER* pParams);
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
@ -133,4 +164,6 @@ private:
|
||||
warn_type_mismatch_t m_warn_type_mismatch;
|
||||
bool m_prevent_function_usage;
|
||||
bool m_check_user_variables;
|
||||
bool m_check_unions;
|
||||
bool m_check_subqueries;
|
||||
};
|
||||
|
||||
@ -82,25 +82,45 @@ bool MaskingFilterSession::check_query(GWBUF* pPacket)
|
||||
zHost = "";
|
||||
}
|
||||
|
||||
bool rv = true;
|
||||
bool acceptable = true;
|
||||
|
||||
if (rv && m_filter.config().prevent_function_usage())
|
||||
const MaskingFilter::Config& config = m_filter.config();
|
||||
|
||||
if (qc_query_is_type(qc_get_type_mask(pPacket), QUERY_TYPE_USERVAR_WRITE))
|
||||
{
|
||||
if (is_function_used(pPacket, zUser, zHost))
|
||||
if (config.check_user_variables())
|
||||
{
|
||||
rv = false;
|
||||
if (is_variable_defined(pPacket, zUser, zHost))
|
||||
{
|
||||
acceptable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qc_query_op_t op = qc_get_operation(pPacket);
|
||||
|
||||
if (op == QUERY_OP_SELECT)
|
||||
{
|
||||
if (config.check_unions() || config.check_subqueries())
|
||||
{
|
||||
if (is_union_or_subquery_used(pPacket, zUser, zHost))
|
||||
{
|
||||
acceptable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (acceptable && config.prevent_function_usage())
|
||||
{
|
||||
if (is_function_used(pPacket, zUser, zHost))
|
||||
{
|
||||
acceptable = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rv && m_filter.config().check_user_variables())
|
||||
{
|
||||
if (is_variable_defined(pPacket, zUser, zHost))
|
||||
{
|
||||
rv = false;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
return acceptable;
|
||||
}
|
||||
|
||||
bool MaskingFilterSession::check_textual_query(GWBUF* pPacket)
|
||||
@ -551,19 +571,26 @@ bool MaskingFilterSession::is_function_used(GWBUF* pPacket, const char* zUser, c
|
||||
|
||||
bool MaskingFilterSession::is_variable_defined(GWBUF* pPacket, const char* zUser, const char* zHost)
|
||||
{
|
||||
if (!qc_query_is_type(qc_get_type_mask(pPacket), QUERY_TYPE_USERVAR_WRITE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
mxb_assert(qc_query_is_type(qc_get_type_mask(pPacket), QUERY_TYPE_USERVAR_WRITE));
|
||||
|
||||
bool is_defined = false;
|
||||
|
||||
SMaskingRules sRules = m_filter.rules();
|
||||
|
||||
auto pred = [&sRules, zUser, zHost](const QC_FIELD_INFO& field_info) {
|
||||
const MaskingRules::Rule* pRule = sRules->get_rule_for(field_info, zUser, zHost);
|
||||
bool rv = false;
|
||||
|
||||
return pRule ? true : false;
|
||||
if (strcmp(field_info.column, "*") == 0)
|
||||
{
|
||||
// If "*" is used, then we must block if there is any rule for the current user.
|
||||
rv = sRules->has_rule_for(zUser, zHost);
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = sRules->get_rule_for(field_info, zUser, zHost) ? true : false;
|
||||
}
|
||||
|
||||
return rv;
|
||||
};
|
||||
|
||||
const QC_FIELD_INFO* pInfos;
|
||||
@ -578,14 +605,121 @@ bool MaskingFilterSession::is_variable_defined(GWBUF* pPacket, const char* zUser
|
||||
|
||||
if (i != end)
|
||||
{
|
||||
const char* zColumn = i->column;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "The field " << i->column << " that should be masked for '" << zUser << "'@'" << zHost
|
||||
<< "' is used when defining a variable, access is denied.";
|
||||
|
||||
if (strcmp(zColumn, "*") == 0)
|
||||
{
|
||||
ss << "'*' is used in the definition of a variable and there are masking rules "
|
||||
<< "for '" << zUser << "'@'" << zHost << "', access is denied.";
|
||||
}
|
||||
else
|
||||
{
|
||||
ss << "The field " << i->column << " that should be masked for '" << zUser << "'@'" << zHost
|
||||
<< "' is used when defining a variable, access is denied.";
|
||||
}
|
||||
|
||||
set_response(create_error_response(ss.str().c_str()));
|
||||
|
||||
is_defined = true;
|
||||
}
|
||||
|
||||
return is_defined;
|
||||
}
|
||||
|
||||
bool MaskingFilterSession::is_union_or_subquery_used(GWBUF* pPacket, const char* zUser, const char* zHost)
|
||||
{
|
||||
mxb_assert(qc_get_operation(pPacket) == QUERY_OP_SELECT);
|
||||
|
||||
const MaskingFilter::Config& config = m_filter.config();
|
||||
|
||||
mxb_assert(config.check_unions() || config.check_subqueries());
|
||||
|
||||
bool is_used = false;
|
||||
|
||||
SMaskingRules sRules = m_filter.rules();
|
||||
|
||||
uint32_t mask = 0;
|
||||
|
||||
if (config.check_unions())
|
||||
{
|
||||
mask |= QC_FIELD_UNION;
|
||||
}
|
||||
|
||||
if (config.check_subqueries())
|
||||
{
|
||||
mask |= QC_FIELD_SUBQUERY;
|
||||
}
|
||||
|
||||
auto pred = [&sRules, mask, zUser, zHost](const QC_FIELD_INFO& field_info) {
|
||||
bool rv = false;
|
||||
|
||||
if (field_info.context & mask)
|
||||
{
|
||||
if (strcmp(field_info.column, "*") == 0)
|
||||
{
|
||||
// If "*" is used, then we must block if there is any rule for the current user.
|
||||
rv = sRules->has_rule_for(zUser, zHost);
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = sRules->get_rule_for(field_info, zUser, zHost) ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
};
|
||||
|
||||
const QC_FIELD_INFO* pInfos;
|
||||
size_t nInfos;
|
||||
|
||||
qc_get_field_info(pPacket, &pInfos, &nInfos);
|
||||
|
||||
const QC_FIELD_INFO* begin = pInfos;
|
||||
const QC_FIELD_INFO* end = begin + nInfos;
|
||||
|
||||
auto i = std::find_if(begin, end, pred);
|
||||
|
||||
if (i != end)
|
||||
{
|
||||
const char* zColumn = i->column;
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
if (config.check_unions() && (i->context & QC_FIELD_UNION))
|
||||
{
|
||||
if (strcmp(zColumn, "*") == 0)
|
||||
{
|
||||
ss << "'*' is used in the second or subsequent SELECT of a UNION and there are "
|
||||
<< "masking rules for '" << zUser << "'@'" << zHost << "', access is denied.";
|
||||
}
|
||||
else
|
||||
{
|
||||
ss << "The field " << zColumn << " that should be masked for '" << zUser << "'@'" << zHost
|
||||
<< "' is used in the second or subsequent SELECT of a UNION, access is denied.";
|
||||
}
|
||||
}
|
||||
else if (config.check_subqueries() && (i->context & QC_FIELD_SUBQUERY))
|
||||
{
|
||||
if (strcmp(zColumn, "*") == 0)
|
||||
{
|
||||
ss << "'*' is used in a subquery and there are masking rules for '"
|
||||
<< zUser << "'@'" << zHost << "', access is denied.";
|
||||
}
|
||||
else
|
||||
{
|
||||
ss << "The field " << zColumn << " that should be masked for '"
|
||||
<< zUser << "'@'" << zHost << "' is used in a subquery, access is denied.";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mxb_assert(!true);
|
||||
}
|
||||
|
||||
set_response(create_error_response(ss.str().c_str()));
|
||||
is_used = true;
|
||||
}
|
||||
|
||||
return is_used;
|
||||
}
|
||||
|
||||
@ -67,6 +67,7 @@ private:
|
||||
|
||||
bool is_function_used(GWBUF* pPacket, const char* zUser, const char* zHost);
|
||||
bool is_variable_defined(GWBUF* pPacket, const char* zUser, const char* zHost);
|
||||
bool is_union_or_subquery_used(GWBUF* pPacket, const char* zUser, const char* zHost);
|
||||
|
||||
private:
|
||||
typedef std::shared_ptr<MaskingRules> SMaskingRules;
|
||||
|
||||
@ -1467,3 +1467,12 @@ const MaskingRules::Rule* MaskingRules::get_rule_for(const QC_FIELD_INFO& field_
|
||||
|
||||
return pRule;
|
||||
}
|
||||
|
||||
bool MaskingRules::has_rule_for(const char* zUser, const char* zHost) const
|
||||
{
|
||||
auto i = std::find_if(m_rules.begin(), m_rules.end(), [zUser, zHost](SRule sRule) {
|
||||
return sRule->matches_account(zUser, zHost);
|
||||
});
|
||||
|
||||
return i != m_rules.end();
|
||||
}
|
||||
|
||||
@ -143,13 +143,21 @@ public:
|
||||
*/
|
||||
virtual void rewrite(LEncString& s) const = 0;
|
||||
|
||||
/**
|
||||
* Does this rule apply to a specific account.
|
||||
*
|
||||
* @param zUser The current user.
|
||||
* @param zHost The current host.
|
||||
*
|
||||
* @return True, if the rule applies.
|
||||
*/
|
||||
bool matches_account(const char* zUser,
|
||||
const char* zHost) const;
|
||||
|
||||
private:
|
||||
Rule(const Rule&);
|
||||
Rule& operator=(const Rule&);
|
||||
|
||||
bool matches_account(const char* zUser,
|
||||
const char* zHost) const;
|
||||
|
||||
private:
|
||||
std::string m_column;
|
||||
std::string m_table;
|
||||
@ -396,6 +404,16 @@ public:
|
||||
|
||||
typedef std::shared_ptr<Rule> SRule;
|
||||
|
||||
/**
|
||||
* Is there any rule for the specified user.
|
||||
*
|
||||
* @param zUser The current user.
|
||||
* @param zHost The current host.
|
||||
*
|
||||
* @return True, if there is a rule for that user/host combination.
|
||||
*/
|
||||
bool has_rule_for(const char* zUser, const char* zHost) const;
|
||||
|
||||
private:
|
||||
MaskingRules(json_t* pRoot, const std::vector<SRule>& rules);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user