Merge branch '2.3' into develop

This commit is contained in:
Johan Wikman 2019-05-03 13:48:57 +03:00
commit fb0745e3de
19 changed files with 517 additions and 21 deletions

View File

@ -146,6 +146,32 @@ query itself is logged. The log messages are logged at the notice level.
Log all queries that do not match a rule. The matched user and the query is
logged. The log messages are logged at the notice level.
#### `treat_string_as_field`
This optional parameter specifies how the database firewall should treat
strings. If true, they will be handled as fields, which will cause column
blocking rules to match even if `ANSI_QUOTES` has been enabled and `"` is
used instead of backtick.
```
treat_string_as_field=false
```
The default value is `true`.
Note that this may cause a false positive, if a "true" string contains the
name of a column to be blocked.
#### `treat_string_arg_as_field`
This optional parameter specifies how the database firewall should treat
strings used as arguments to functions. If true, they will be handled
as fields, which will cause function column blocking rules to match even
even if `ANSI_QUOTES` has been enabled and `"` is used instead of backtick.
```
treat_string_arg_as_field=false
```
The default value is `true`.
Note that this may cause a false positive, if a "true" string contains the
name of a column to be blocked.
## Rule syntax
The rules are defined by using the following syntax:

View File

@ -96,6 +96,26 @@ Please see the configuration parameter
[require_fully_parsed](#require_fully_parsed)
for how to change the default behaviour.
From MaxScale 2.3.7 onwards, the masking filter will treat any strings
passed to functions as if they were fields. The reason is that as the
MaxScale query classifier is not aware of whether `ANSI_QUOTES` is
enabled or not, it is possible to bypass the masking by turning that
option on.
```
mysql> set @@sql_mode = 'ANSI_QUOTES';
mysql> select concat("ssn") from managers;
```
Before this change, the content of the field `ssn` would have been
returned in clear text even if the column should have been masked.
Note that this change will mean that there may be false positives
if `ANSI_QUOTES` is not enabled and a string argument happens to
be the same as the name of a field to be masked.
Please see the configuration parameter
[treat_string_arg_as_field(#treat_string_arg_as_field)
for how to change the default behaviour.
## Limitations
The masking filter can _only_ be used for masking columns of the following
@ -215,6 +235,17 @@ Note that if this parameter is set to false, then `prevent_function_usage`,
less effective, as it with a statement that can not be fully parsed may be
possible to bypass the protection that they are intended to provide.
#### `treat_string_arg_as_field`
This optional parameter specifies how the masking filter should treat
strings used as arguments to functions. If true, they will be handled
as fields, which will cause fields to be masked even if `ANSI_QUOTES` has
been enabled and `"` is used instead of backtick.
```
treat_string_arg_as_field=false
```
The default value is `true`.
#### `check_user_variables`
This optional parameter specifies how the masking filter should

View File

@ -29,6 +29,17 @@ enum qc_init_kind_t
QC_INIT_BOTH = 0x03
};
/**
* qc_option_t defines options that affect the classification.
*/
enum qc_option_t
{
QC_OPTION_STRING_ARG_AS_FIELD = (1 << 0), /*< Report a string argument to a function as a field. */
QC_OPTION_STRING_AS_FIELD = (1 << 1), /*< Report strings as fields. */
};
const uint32_t QC_OPTION_MASK = QC_OPTION_STRING_ARG_AS_FIELD | QC_OPTION_STRING_AS_FIELD;
/**
* qc_sql_mode_t specifies what should be assumed of the statements
* that will be parsed.
@ -452,6 +463,22 @@ struct QUERY_CLASSIFIER
*/
void (* qc_info_close)(QC_STMT_INFO* info);
/**
* Gets the options of the *calling* thread.
*
* @return Bit mask of values from qc_option_t.
*/
uint32_t (* qc_get_options)();
/**
* Sets the options for the *calling* thread.
*
* @param options Bits from qc_option_t.
*
* @return QC_RESULT_OK if @c options is valid, otherwise QC_RESULT_ERROR.
*/
int32_t (* qc_set_options)(uint32_t options);
/**
* Get result from info.
*
@ -971,6 +998,22 @@ json_t* qc_get_cache_stats_as_json();
*/
const char* qc_result_to_string(qc_parse_result_t result);
/**
* Gets the options of the *calling* thread.
*
* @return Bit mask of values from qc_option_t.
*/
uint32_t qc_get_options();
/**
* Sets the options for the *calling* thread.
*
* @param options Bits from qc_option_t.
*
* @return true if the options were valid, false otherwise.
*/
bool qc_set_options(uint32_t options);
/**
* Public interface to query classifier cache state.
*/

View File

@ -23,6 +23,7 @@ module=masking
rules=/home/vagrant/masking_auto_firewall.json
warn_type_mismatch=always
large_payload=ignore
treat_string_arg_as_field=false
[RWS]
type=service

View File

@ -0,0 +1,2 @@
SELECT CONCAT(x1) FROM t1;
SELECT CONCAT("x1") FROM t1;

View File

@ -1,3 +1,4 @@
update t1 set x1=1 where fl=0;
SELECT x1 FROM t1;
select t1.x1 as 'something' from t1 as t1 limit 1;
select t1.x1 as 'something' from t1 as t1 limit 1;
SELECT "x1" FROM t1;

View File

@ -0,0 +1,6 @@
DROP TABLE IF EXISTS t1;
CREATE TABLE t1 (x1 TEXT, x2 TEXT);
select sleep(5);
SELECT x1 FROM t1;
SELECT x2 FROM t1;
SELECT CONCAT(x2) FROM t1;

View File

@ -0,0 +1,2 @@
rule test19 match function concat columns x1
users %@% match any rules test19

View File

@ -44,7 +44,7 @@ int main(int argc, char* argv[])
Test->maxscales->access_homedir[0]);
sprintf(rules_dir, "%s/fw/", test_dir);
int N = 18;
int N = 19;
int i;
for (i = 1; i < N + 1; i++)

View File

@ -74,8 +74,6 @@ void test_one_ps(TestConnections& test, const char* zQuery, Expect expect)
void run(TestConnections& test)
{
init(test);
MYSQL* pMysql = test.maxscales->conn_rwsplit[0];
int rv;
@ -130,6 +128,38 @@ void run(TestConnections& test)
test_one(test, "select * FROM (select * from masking_auto_firewall) tbl", Expect::FAILURE);
}
void run_ansi_quotes(TestConnections& test)
{
// This SHOULD go through as we have 'treat_string_arg_as_field=false"
test_one(test, "select concat(\"a\") from masking_auto_firewall", Expect::SUCCESS);
Connection c = test.maxscales->rwsplit();
c.connect();
test.expect(c.query("SET @@SQL_MODE = CONCAT(@@SQL_MODE, ',ANSI_QUOTES')"),
"Could not turn on 'ANSI_QUOTES'");
// This SHOULD still go through as we still have 'treat_string_arg_as_field=false"
test_one(test, "select concat(\"a\") from masking_auto_firewall", Expect::SUCCESS);
// Let's turn on 'treat_string_arg_as_field=true'
test.maxscales->ssh_node(0,
"sed -i -e "
"'s/treat_string_arg_as_field=false/treat_string_arg_as_field=true/' "
"/etc/maxscale.cnf",
true);
// and restart MaxScale
test.maxscales->restart();
// This should NOT go through as we have 'treat_string_arg_as_field=true" and ANSI_QUOTES.
test_one(test, "select concat(\"a\") from masking_auto_firewall", Expect::FAILURE);
// Have to reconnect as we restarted MaxScale.
c.connect();
test.expect(c.query("SET @@SQL_MODE = REPLACE(@@SQL_MODE, 'ANSI_QUOTES', '')"),
"Could not turn off 'ANSI_QUOTES'");
}
}
int main(int argc, char* argv[])
@ -151,7 +181,9 @@ int main(int argc, char* argv[])
if (test.maxscales->connect_rwsplit() == 0)
{
init(test);
run(test);
run_ansi_quotes(test);
}
else
{

View File

@ -195,6 +195,7 @@ static struct
static thread_local struct
{
qc_sql_mode_t sql_mode;
uint32_t options;
NAME_MAPPING* function_name_mappings;
// The version information is not used; the embedded library parses according
// to the version of the embedded library it has been linked with. However, we
@ -203,6 +204,7 @@ static thread_local struct
} this_thread =
{
QC_SQL_MODE_DEFAULT,
0,
function_name_mappings_default,
0
};
@ -2276,7 +2278,6 @@ static void unalias_names(st_select_lex* select,
}
static void add_field_info(parsing_info_t* info,
st_select_lex* select,
const char* database,
const char* table,
const char* column,
@ -2284,8 +2285,6 @@ static void add_field_info(parsing_info_t* info,
{
mxb_assert(column);
unalias_names(select, database, table, &database, &table);
QC_FIELD_INFO item = {(char*)database, (char*)table, (char*)column};
size_t i;
@ -2361,6 +2360,20 @@ static void add_field_info(parsing_info_t* info,
}
}
static void add_field_info(parsing_info_t* info,
st_select_lex* select,
const char* database,
const char* table,
const char* column,
List<Item>* excludep)
{
mxb_assert(column);
unalias_names(select, database, table, &database, &table);
add_field_info(info, database, table, column, excludep);
}
static void add_function_field_usage(const char* database,
const char* table,
const char* column,
@ -2485,6 +2498,19 @@ static void add_function_field_usage(st_select_lex* select,
add_function_field_usage(select, static_cast<Item_field*>(item), fi);
break;
case Item::STRING_ITEM:
if (this_thread.options & QC_OPTION_STRING_ARG_AS_FIELD)
{
String* s = item->val_str();
int len = s->length();
char tmp[len + 1];
memcpy(tmp, s->ptr(), len);
tmp[len] = 0;
add_function_field_usage(nullptr, nullptr, tmp, fi);
}
break;
default:
// mxb_assert(!true);
;
@ -3058,6 +3084,19 @@ static void update_field_infos(parsing_info_t* pi,
}
break;
case Item::STRING_ITEM:
if (this_thread.options & QC_OPTION_STRING_AS_FIELD)
{
String* s = item->val_str();
int len = s->length();
char tmp[len + 1];
memcpy(tmp, s->ptr(), len);
tmp[len] = 0;
add_field_info(pi, nullptr, nullptr, tmp, excludep);
}
break;
default:
break;
}
@ -3536,6 +3575,27 @@ int32_t qc_mysql_set_sql_mode(qc_sql_mode_t sql_mode)
return rv;
}
uint32_t qc_mysql_get_options()
{
return this_thread.options;
}
int32_t qc_mysql_set_options(uint32_t options)
{
int32_t rv = QC_RESULT_OK;
if ((options & ~QC_OPTION_MASK) == 0)
{
this_thread.options = options;
}
else
{
rv = QC_RESULT_ERROR;
}
return rv;
}
/**
* EXPORTS
*/
@ -3571,6 +3631,8 @@ extern "C"
qc_mysql_set_sql_mode,
nullptr, // qc_info_dup not supported.
nullptr, // qc_info_close not supported.
qc_mysql_get_options,
qc_mysql_set_options,
nullptr, // qc_get_result_from_info not supported
};

View File

@ -155,6 +155,7 @@ static thread_local struct
bool initialized; // Whether the thread specific data has been initialized.
sqlite3* pDb; // Thread specific database handle.
qc_sql_mode_t sql_mode; // What sql_mode is used.
uint32_t options; // Options affecting classification.
QcSqliteInfo* pInfo; // The information for the current statement being classified.
uint64_t version; // Encoded version number
uint32_t version_major;
@ -842,6 +843,14 @@ public:
update_field_infos_from_expr(pAliases, context, pExpr, pExclude);
break;
case TK_STRING: // select "a" ..., for @@sql_mode containing 'ANSI_QUOTES'
if (this_thread.options & QC_OPTION_STRING_AS_FIELD)
{
const char* zColumn = pExpr->u.zToken;
update_field_infos_from_column(pAliases, context, zColumn, pExclude);
}
break;
case TK_VARIABLE:
{
if (zToken[0] == '@')
@ -1152,6 +1161,13 @@ public:
}
}
}
else if (pExpr->op == TK_STRING)
{
if (this_thread.options & QC_OPTION_STRING_ARG_AS_FIELD)
{
zColumn = pExpr->u.zToken;
}
}
if (zColumn)
{
@ -1187,6 +1203,17 @@ public:
}
}
void update_field_infos_from_column(QcAliases* pAliases,
uint32_t context,
const char* zColumn,
const ExprList* pExclude)
{
if (must_check_sequence_related_functions() || must_collect_fields())
{
update_field_info(pAliases, context, nullptr, nullptr, zColumn, pExclude);
}
}
void update_field_infos_from_exprlist(QcAliases* pAliases,
uint32_t context,
const ExprList* pEList,
@ -4577,6 +4604,8 @@ static int32_t qc_sqlite_get_sql_mode(qc_sql_mode_t* sql_mode);
static int32_t qc_sqlite_set_sql_mode(qc_sql_mode_t sql_mode);
static QC_STMT_INFO* qc_sqlite_info_dup(QC_STMT_INFO* info);
static void qc_sqlite_info_close(QC_STMT_INFO* info);
static uint32_t qc_sqlite_get_options();
static int32_t qc_sqlite_set_options(uint32_t options);
static QC_STMT_RESULT qc_sqlite_get_result_from_info(const QC_STMT_INFO* pInfo);
@ -5261,6 +5290,27 @@ void qc_sqlite_info_close(QC_STMT_INFO* info)
static_cast<QcSqliteInfo*>(info)->dec_ref();
}
uint32_t qc_sqlite_get_options()
{
return this_thread.options;
}
int32_t qc_sqlite_set_options(uint32_t options)
{
int32_t rv = QC_RESULT_OK;
if ((options & ~QC_OPTION_MASK) == 0)
{
this_thread.options = options;
}
else
{
rv = QC_RESULT_ERROR;
}
return rv;
}
QC_STMT_RESULT qc_sqlite_get_result_from_info(const QC_STMT_INFO* pInfo)
{
return static_cast<const QcSqliteInfo*>(pInfo)->get_result();
@ -5301,6 +5351,8 @@ extern "C"
qc_sqlite_set_sql_mode,
qc_sqlite_info_dup,
qc_sqlite_info_close,
qc_sqlite_get_options,
qc_sqlite_set_options,
qc_sqlite_get_result_from_info,
};

View File

@ -96,9 +96,11 @@ class QCInfoCache;
static thread_local struct
{
QCInfoCache* pInfo_cache;
uint32_t options;
} this_thread =
{
nullptr
nullptr,
0
};
@ -147,7 +149,8 @@ public:
{
Entry& entry = i->second;
if (entry.sql_mode == this_unit.qc_sql_mode)
if ((entry.sql_mode == this_unit.qc_sql_mode) &&
(entry.options == this_thread.options))
{
mxb_assert(this_unit.classifier);
this_unit.classifier->qc_info_dup(entry.pInfo);
@ -158,7 +161,7 @@ public:
}
else
{
// If the sql_mode has changed, we discard the existing result.
// If the sql_mode or options has changed, we discard the existing result.
erase(i);
++m_stats.misses;
@ -198,7 +201,7 @@ public:
{
this_unit.classifier->qc_info_dup(pInfo);
m_infos.emplace(canonical_stmt, Entry(pInfo, this_unit.qc_sql_mode));
m_infos.emplace(canonical_stmt, Entry(pInfo, this_unit.qc_sql_mode, this_thread.options));
++m_stats.inserts;
m_stats.size += size;
@ -248,15 +251,17 @@ public:
private:
struct Entry
{
Entry(QC_STMT_INFO* pInfo, qc_sql_mode_t sql_mode)
Entry(QC_STMT_INFO* pInfo, qc_sql_mode_t sql_mode, uint32_t options)
: pInfo(pInfo)
, sql_mode(sql_mode)
, options(options)
, hits(0)
{
}
QC_STMT_INFO* pInfo;
qc_sql_mode_t sql_mode;
uint32_t options;
int64_t hits;
};
@ -1319,6 +1324,29 @@ void qc_set_sql_mode(qc_sql_mode_t sql_mode)
}
}
uint32_t qc_get_options()
{
QC_TRACE();
mxb_assert(this_unit.classifier);
return this_unit.classifier->qc_get_options();
}
bool qc_set_options(uint32_t options)
{
QC_TRACE();
mxb_assert(this_unit.classifier);
int32_t rv = this_unit.classifier->qc_set_options(options);
if (rv == QC_RESULT_OK)
{
this_thread.options = options;
}
return rv == QC_RESULT_OK;
}
void qc_get_cache_properties(QC_CACHE_PROPERTIES* properties)
{
properties->max_size = this_unit.cache_max_size();
@ -1330,6 +1358,11 @@ bool qc_set_cache_properties(const QC_CACHE_PROPERTIES* properties)
if (properties->max_size >= 0)
{
if (properties->max_size == 0)
{
MXS_NOTICE("Query classifier cache disabled.");
}
this_unit.set_cache_max_size(properties->max_size);
rv = true;
}

View File

@ -128,6 +128,48 @@ private:
};
thread_local DbfwThread* this_thread = NULL;
// TODO: In 2.4 move to query_classifier.hh.
class EnableOption
{
public:
EnableOption(const EnableOption&) = delete;
EnableOption& operator=(const EnableOption&) = delete;
EnableOption(uint32_t option)
: m_option(option)
, m_options(0)
, m_disable(false)
{
if (m_option)
{
m_options = qc_get_options();
if (!(m_options & m_option))
{
uint32_t options = (m_options | m_option);
MXB_AT_DEBUG(bool rv = )qc_set_options(options);
mxb_assert(rv);
m_disable = true;
}
}
}
~EnableOption()
{
if (m_disable)
{
MXB_AT_DEBUG(bool rv = )qc_set_options(m_options);
mxb_assert(rv);
}
}
private:
uint32_t m_option;
uint32_t m_options;
bool m_disable;
};
}
bool parse_at_times(const char** tok, char** saveptr, Rule* ruledef);
@ -584,6 +626,16 @@ MXS_MODULE* MXS_CREATE_MODULE()
MXS_MODULE_OPT_ENUM_UNIQUE,
action_values
},
{
"treat_string_arg_as_field",
MXS_MODULE_PARAM_BOOL,
"true"
},
{
"treat_string_as_field",
MXS_MODULE_PARAM_BOOL,
"true"
},
{MXS_END_MODULE_PARAMS}
}
};
@ -1199,6 +1251,8 @@ int global_version = 1;
Dbfw::Dbfw(MXS_CONFIG_PARAMETER* params)
: m_action((enum fw_actions)params->get_enum("action", action_values))
, m_log_match(0)
, m_treat_string_as_field(params->get_bool("treat_string_as_field"))
, m_treat_string_arg_as_field(params->get_bool("treat_string_arg_as_field"))
, m_filename(params->get_string("rules"))
, m_version(atomic_add(&global_version, 1))
{
@ -1227,6 +1281,26 @@ Dbfw* Dbfw::create(const char* zName, MXS_CONFIG_PARAMETER* pParams)
if (process_rule_file(file, &rules, &users))
{
rval = new(std::nothrow) Dbfw(pParams);
if (rval)
{
if (rval->treat_string_as_field() || rval->treat_string_arg_as_field())
{
QC_CACHE_PROPERTIES cache_properties;
qc_get_cache_properties(&cache_properties);
if (cache_properties.max_size != 0)
{
MXS_NOTICE("The parameter 'treat_string_arg_as_field' or(and) "
"'treat_string_as_field' is enabled for %s, "
"disabling the query classifier cache.",
zName);
cache_properties.max_size = 0;
qc_set_cache_properties(&cache_properties);
}
}
}
}
return rval;
@ -1468,6 +1542,19 @@ int DbfwSession::routeQuery(GWBUF* buffer)
}
else
{
uint32_t option = 0;
if (m_instance->treat_string_as_field())
{
option |= QC_OPTION_STRING_AS_FIELD;
}
if (m_instance->treat_string_arg_as_field())
{
option |= QC_OPTION_STRING_ARG_AS_FIELD;
}
EnableOption enable(option);
GWBUF* analyzed_queue = buffer;
// QUERY_TYPE_PREPARE_STMT need not be handled separately as the

View File

@ -224,6 +224,26 @@ public:
*/
fw_actions get_action() const;
/**
* Should strings be treated as fields?
*
* @return True, if they should, false otherwise.
*/
bool treat_string_as_field() const
{
return m_treat_string_as_field;
}
/**
* Should string args be treated as fields?
*
* @return True, if they should, false otherwise.
*/
bool treat_string_arg_as_field() const
{
return m_treat_string_arg_as_field;
}
/**
* Get logging option bitmask
*
@ -267,6 +287,8 @@ public:
private:
fw_actions m_action; /*< Default operation mode, defaults to deny */
int m_log_match; /*< Log matching and/or non-matching queries */
bool m_treat_string_as_field;
bool m_treat_string_arg_as_field;
mutable std::mutex m_lock; /*< Instance spinlock */
std::string m_filename; /*< Path to the rule file */
int m_version; /*< Latest rule file version, incremented on reload */

View File

@ -145,6 +145,12 @@ extern "C" MXS_MODULE* MXS_CREATE_MODULE()
Config::require_fully_parsed_default,
MXS_MODULE_OPT_NONE,
},
{
Config::treat_string_arg_as_field_name,
MXS_MODULE_PARAM_BOOL,
Config::treat_string_arg_as_field_default,
MXS_MODULE_OPT_NONE
},
{MXS_END_MODULE_PARAMS}
}
};
@ -179,6 +185,22 @@ MaskingFilter* MaskingFilter::create(const char* zName, MXS_CONFIG_PARAMETER* pP
if (sRules.get())
{
pFilter = new MaskingFilter(config, sRules);
if (config.treat_string_arg_as_field())
{
QC_CACHE_PROPERTIES cache_properties;
qc_get_cache_properties(&cache_properties);
if (cache_properties.max_size != 0)
{
MXS_NOTICE("The parameter 'treat_string_arg_as_field' is enabled for %s, "
"disabling the query classifier cache.",
zName);
cache_properties.max_size = 0;
qc_set_cache_properties(&cache_properties);
}
}
}
return pFilter;

View File

@ -17,14 +17,15 @@
namespace
{
const char config_name_check_subqueries[] = "check_subqueries";
const char config_name_check_unions[] = "check_unions";
const char config_name_check_user_variables[] = "check_user_variables";
const char config_name_large_payload[] = "large_payload";
const char config_name_prevent_function_usage[] = "prevent_function_usage";
const char config_name_require_fully_parsed[] = "require_fully_parsed";
const char config_name_rules[] = "rules";
const char config_name_warn_type_mismatch[] = "warn_type_mismatch";
const char config_name_check_subqueries[] = "check_subqueries";
const char config_name_check_unions[] = "check_unions";
const char config_name_check_user_variables[] = "check_user_variables";
const char config_name_large_payload[] = "large_payload";
const char config_name_prevent_function_usage[] = "prevent_function_usage";
const char config_name_require_fully_parsed[] = "require_fully_parsed";
const char config_name_rules[] = "rules";
const char config_name_warn_type_mismatch[] = "warn_type_mismatch";
const char config_name_treat_string_arg_as_field[] = "treat_string_arg_as_field";
const char config_value_abort[] = "abort";
@ -105,7 +106,11 @@ const char* MaskingFilterConfig::check_subqueries_default = config_value_true;
const char* MaskingFilterConfig::require_fully_parsed_name = config_name_require_fully_parsed;
const char* MaskingFilterConfig::require_fully_parsed_default = config_name_require_fully_parsed;
/*
* PARAM treat_string_arg_as_field
*/
const char* MaskingFilterConfig::treat_string_arg_as_field_name = config_name_treat_string_arg_as_field;
const char* MaskingFilterConfig::treat_string_arg_as_field_default = config_value_true;
/*
* MaskingFilterConfig
*/
@ -161,3 +166,9 @@ bool MaskingFilterConfig::get_require_fully_parsed(const MXS_CONFIG_PARAMETER* p
{
return pParams->get_bool(require_fully_parsed_name);
}
// static
bool MaskingFilterConfig::get_treat_string_arg_as_field(const MXS_CONFIG_PARAMETER* pParams)
{
return pParams->get_bool(treat_string_arg_as_field_name);
}

View File

@ -57,6 +57,9 @@ public:
static const char* require_fully_parsed_name;
static const char* require_fully_parsed_default;
static const char* treat_string_arg_as_field_name;
static const char* treat_string_arg_as_field_default;
MaskingFilterConfig(const char* zName, const MXS_CONFIG_PARAMETER* pParams)
: m_name(zName)
, m_large_payload(get_large_payload(pParams))
@ -67,6 +70,7 @@ public:
, m_check_unions(get_check_unions(pParams))
, m_check_subqueries(get_check_subqueries(pParams))
, m_require_fully_parsed(get_require_fully_parsed(pParams))
, m_treat_string_arg_as_field(get_treat_string_arg_as_field(pParams))
{
}
@ -119,6 +123,11 @@ public:
return m_require_fully_parsed;
}
bool treat_string_arg_as_field() const
{
return m_treat_string_arg_as_field;
}
void set_large_payload(large_payload_t l)
{
m_large_payload = l;
@ -158,6 +167,11 @@ public:
m_require_fully_parsed = b;
}
void set_treat_string_arg_as_field(bool b)
{
m_treat_string_arg_as_field = b;
}
bool is_parsing_needed() const
{
return prevent_function_usage() || check_user_variables() || check_unions() || check_subqueries();
@ -171,6 +185,7 @@ public:
static bool get_check_unions(const MXS_CONFIG_PARAMETER* pParams);
static bool get_check_subqueries(const MXS_CONFIG_PARAMETER* pParams);
static bool get_require_fully_parsed(const MXS_CONFIG_PARAMETER* pParams);
static bool get_treat_string_arg_as_field(const MXS_CONFIG_PARAMETER* pParams);
private:
std::string m_name;
@ -182,4 +197,5 @@ private:
bool m_check_unions;
bool m_check_subqueries;
bool m_require_fully_parsed;
bool m_treat_string_arg_as_field;
};

View File

@ -48,6 +48,47 @@ GWBUF* create_parse_error_response()
return create_error_response(zMessage);
}
// TODO: In 2.4 move to query_classifier.hh.
class EnableOption
{
public:
EnableOption(const EnableOption&) = delete;
EnableOption& operator=(const EnableOption&) = delete;
EnableOption(uint32_t option)
: m_option(option)
, m_options(0)
, m_disable(false)
{
if (m_option)
{
m_options = qc_get_options();
if (!(m_options & m_option))
{
uint32_t options = (m_options | m_option);
MXB_AT_DEBUG(bool rv = )qc_set_options(options);
mxb_assert(rv);
m_disable = true;
}
}
}
~EnableOption()
{
if (m_disable)
{
MXB_AT_DEBUG(bool rv = )qc_set_options(m_options);
mxb_assert(rv);
}
}
private:
uint32_t m_option;
uint32_t m_options;
bool m_disable;
};
}
MaskingFilterSession::MaskingFilterSession(MXS_SESSION* pSession, const MaskingFilter* pFilter)
@ -127,6 +168,9 @@ bool MaskingFilterSession::check_textual_query(GWBUF* pPacket)
{
bool rv = false;
uint32_t option = m_filter.config().treat_string_arg_as_field() ? QC_OPTION_STRING_ARG_AS_FIELD : 0;
EnableOption enable(option);
if (qc_parse(pPacket, QC_COLLECT_FIELDS | QC_COLLECT_FUNCTIONS) == QC_QUERY_PARSED
|| !m_filter.config().require_fully_parsed())
{
@ -166,6 +210,9 @@ bool MaskingFilterSession::check_binary_query(GWBUF* pPacket)
{
bool rv = false;
uint32_t option = m_filter.config().treat_string_arg_as_field() ? QC_OPTION_STRING_ARG_AS_FIELD : 0;
EnableOption enable(option);
if (qc_parse(pPacket, QC_COLLECT_FIELDS | QC_COLLECT_FUNCTIONS) == QC_QUERY_PARSED
|| !m_filter.config().require_fully_parsed())
{