MXS-2392 MXS-2390 Handle prepared statements in masking filter
- Check textual prepared statements for use of columns to be masked in conjunction with functions. - Check binary prepared statements for use of columns to be masked in conjunction with functions. - Prevent creation of textual prepared statement from variable.
This commit is contained in:
@ -31,6 +31,25 @@ using std::ostream;
|
|||||||
using std::string;
|
using std::string;
|
||||||
using std::stringstream;
|
using std::stringstream;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
GWBUF* create_error_response(const char* zMessage)
|
||||||
|
{
|
||||||
|
return modutil_create_mysql_err_msg(1, 0, 1141, "HY000", zMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
GWBUF* create_parse_error_response()
|
||||||
|
{
|
||||||
|
const char* zMessage =
|
||||||
|
"The statement could not be fully parsed and will hence be "
|
||||||
|
"rejected (masking filter).";
|
||||||
|
|
||||||
|
return create_error_response(zMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
MaskingFilterSession::MaskingFilterSession(MXS_SESSION* pSession, const MaskingFilter* pFilter)
|
MaskingFilterSession::MaskingFilterSession(MXS_SESSION* pSession, const MaskingFilter* pFilter)
|
||||||
: maxscale::FilterSession(pSession)
|
: maxscale::FilterSession(pSession)
|
||||||
, m_filter(*pFilter)
|
, m_filter(*pFilter)
|
||||||
@ -48,6 +67,49 @@ MaskingFilterSession* MaskingFilterSession::create(MXS_SESSION* pSession, const
|
|||||||
return new MaskingFilterSession(pSession, pFilter);
|
return new MaskingFilterSession(pSession, pFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MaskingFilterSession::check_query(GWBUF* pPacket)
|
||||||
|
{
|
||||||
|
if (qc_parse(pPacket, QC_COLLECT_FIELDS | QC_COLLECT_FUNCTIONS) == QC_QUERY_PARSED)
|
||||||
|
{
|
||||||
|
if (qc_query_is_type(qc_get_type_mask(pPacket), QUERY_TYPE_PREPARE_NAMED_STMT))
|
||||||
|
{
|
||||||
|
GWBUF* pP = qc_get_preparable_stmt(pPacket);
|
||||||
|
|
||||||
|
if (pP)
|
||||||
|
{
|
||||||
|
check_query(pP);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If pP is NULL, it indicates that we have a "prepare ps from @a". It must
|
||||||
|
// be rejected as we currently have no means for checking what columns are
|
||||||
|
// referred to.
|
||||||
|
const char* zMessage =
|
||||||
|
"A statement prepared from a variable is rejected (masking filter).";
|
||||||
|
|
||||||
|
set_response(create_error_response(zMessage));
|
||||||
|
m_state = EXPECTING_NOTHING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (reject_if_function_used(pPacket))
|
||||||
|
{
|
||||||
|
m_state = EXPECTING_NOTHING;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_state = EXPECTING_RESPONSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
set_response(create_parse_error_response());
|
||||||
|
m_state = EXPECTING_NOTHING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int MaskingFilterSession::routeQuery(GWBUF* pPacket)
|
int MaskingFilterSession::routeQuery(GWBUF* pPacket)
|
||||||
{
|
{
|
||||||
ComRequest request(pPacket);
|
ComRequest request(pPacket);
|
||||||
@ -58,9 +120,9 @@ int MaskingFilterSession::routeQuery(GWBUF* pPacket)
|
|||||||
case MXS_COM_QUERY:
|
case MXS_COM_QUERY:
|
||||||
m_res.reset(request.command(), m_filter.rules());
|
m_res.reset(request.command(), m_filter.rules());
|
||||||
|
|
||||||
if (m_filter.config().prevent_function_usage() && reject_if_function_used(pPacket))
|
if (m_filter.config().prevent_function_usage())
|
||||||
{
|
{
|
||||||
m_state = EXPECTING_NOTHING;
|
check_query(pPacket);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -68,6 +130,32 @@ int MaskingFilterSession::routeQuery(GWBUF* pPacket)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MXS_COM_STMT_PREPARE:
|
||||||
|
if (m_filter.config().prevent_function_usage())
|
||||||
|
{
|
||||||
|
if (qc_parse(pPacket, QC_COLLECT_FIELDS | QC_COLLECT_FUNCTIONS) == QC_QUERY_PARSED)
|
||||||
|
{
|
||||||
|
if (reject_if_function_used(pPacket))
|
||||||
|
{
|
||||||
|
m_state = EXPECTING_NOTHING;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_state = IGNORING_RESPONSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
set_response(create_parse_error_response());
|
||||||
|
m_state = EXPECTING_NOTHING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_state = IGNORING_RESPONSE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case MXS_COM_STMT_EXECUTE:
|
case MXS_COM_STMT_EXECUTE:
|
||||||
m_res.reset(request.command(), m_filter.rules());
|
m_res.reset(request.command(), m_filter.rules());
|
||||||
m_state = EXPECTING_RESPONSE;
|
m_state = EXPECTING_RESPONSE;
|
||||||
@ -389,53 +477,38 @@ bool MaskingFilterSession::reject_if_function_used(GWBUF* pPacket)
|
|||||||
zHost = "";
|
zHost = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (qc_parse(pPacket, QC_COLLECT_FIELDS | QC_COLLECT_FUNCTIONS) == QC_QUERY_PARSED)
|
auto pred1 = [&sRules, zUser, zHost](const QC_FIELD_INFO& field_info) {
|
||||||
{
|
const MaskingRules::Rule* pRule = sRules->get_rule_for(field_info, zUser, zHost);
|
||||||
auto pred1 = [&sRules, zUser, zHost](const QC_FIELD_INFO& field_info) {
|
|
||||||
const MaskingRules::Rule* pRule = sRules->get_rule_for(field_info, zUser, zHost);
|
|
||||||
|
|
||||||
return pRule ? true : false;
|
return pRule ? true : false;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto pred2 = [&sRules, zUser, zHost, &pred1](const QC_FUNCTION_INFO& function_info) {
|
auto pred2 = [&sRules, zUser, zHost, &pred1](const QC_FUNCTION_INFO& function_info) {
|
||||||
const QC_FIELD_INFO* begin = function_info.fields;
|
const QC_FIELD_INFO* begin = function_info.fields;
|
||||||
const QC_FIELD_INFO* end = begin + function_info.n_fields;
|
const QC_FIELD_INFO* end = begin + function_info.n_fields;
|
||||||
|
|
||||||
auto i = std::find_if(begin, end, pred1);
|
auto i = std::find_if(begin, end, pred1);
|
||||||
|
|
||||||
return i != end;
|
return i != end;
|
||||||
};
|
};
|
||||||
|
|
||||||
const QC_FUNCTION_INFO* pInfos;
|
const QC_FUNCTION_INFO* pInfos;
|
||||||
size_t nInfos;
|
size_t nInfos;
|
||||||
|
|
||||||
qc_get_function_info(pPacket, &pInfos, &nInfos);
|
qc_get_function_info(pPacket, &pInfos, &nInfos);
|
||||||
|
|
||||||
const QC_FUNCTION_INFO* begin = pInfos;
|
const QC_FUNCTION_INFO* begin = pInfos;
|
||||||
const QC_FUNCTION_INFO* end = begin + nInfos;
|
const QC_FUNCTION_INFO* end = begin + nInfos;
|
||||||
|
|
||||||
auto i = std::find_if(begin, end, pred2);
|
auto i = std::find_if(begin, end, pred2);
|
||||||
|
|
||||||
if (i != end)
|
if (i != end)
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "The function " << i->name << " is used in conjunction with a field "
|
|
||||||
<< "that should be masked for '" << zUser << "'@'" << zHost << "', access is denied.";
|
|
||||||
|
|
||||||
GWBUF* pResponse = modutil_create_mysql_err_msg(1, 0, 1141, "HY000", ss.str().c_str());
|
|
||||||
set_response(pResponse);
|
|
||||||
|
|
||||||
rejected = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "The statement could not be fully parsed and will hence be "
|
ss << "The function " << i->name << " is used in conjunction with a field "
|
||||||
<< "rejected (masking filter).";
|
<< "that should be masked for '" << zUser << "'@'" << zHost << "', access is denied.";
|
||||||
|
|
||||||
GWBUF* pResponse = modutil_create_mysql_err_msg(1, 0, 1141, "HY000", ss.str().c_str());
|
set_response(create_error_response(ss.str().c_str()));
|
||||||
set_response(pResponse);
|
|
||||||
|
|
||||||
rejected = true;
|
rejected = true;
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,8 @@ private:
|
|||||||
SUPPRESSING_RESPONSE
|
SUPPRESSING_RESPONSE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void check_query(GWBUF* pPacket);
|
||||||
|
|
||||||
void handle_response(GWBUF* pPacket);
|
void handle_response(GWBUF* pPacket);
|
||||||
void handle_field(GWBUF* pPacket);
|
void handle_field(GWBUF* pPacket);
|
||||||
void handle_row(GWBUF* pPacket);
|
void handle_row(GWBUF* pPacket);
|
||||||
|
Reference in New Issue
Block a user