From b26e2d81896e0c66985e8ec695f7a976ec3a2ff3 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Fri, 7 Jul 2017 15:02:59 +0200 Subject: [PATCH 1/2] MXS-1302: masking filter value replacement is done by new ReplaceRule, derived form base class Rule The value replacement is now done by new ReplaceRule, derived form base class Rule --- server/modules/filter/masking/maskingrules.cc | 59 +++++++----- server/modules/filter/masking/maskingrules.hh | 93 +++++++++++++------ 2 files changed, 103 insertions(+), 49 deletions(-) diff --git a/server/modules/filter/masking/maskingrules.cc b/server/modules/filter/masking/maskingrules.cc index c46636498..c0987e21d 100644 --- a/server/modules/filter/masking/maskingrules.cc +++ b/server/modules/filter/masking/maskingrules.cc @@ -330,13 +330,13 @@ bool get_accounts(const char* zName, * * @return A Rule instance or NULL in case of error. */ -auto_ptr create_rule_from_elements(json_t* pColumn, - json_t* pTable, - json_t* pDatabase, - json_t* pValue, - json_t* pFill, - json_t* pApplies_to, - json_t* pExempted) +auto_ptr create_rule_from_elements(json_t* pColumn, + json_t* pTable, + json_t* pDatabase, + json_t* pValue, + json_t* pFill, + json_t* pApplies_to, + json_t* pExempted) { ss_dassert(pColumn && json_is_string(pColumn)); ss_dassert(!pTable || json_is_string(pTable)); @@ -347,7 +347,7 @@ auto_ptr create_rule_from_elements(json_t* pColumn, ss_dassert(!pApplies_to || json_is_array(pApplies_to)); ss_dassert(!pExempted || json_is_array(pExempted)); - auto_ptr sRule; + auto_ptr sRule; string column(json_string_value(pColumn)); string table(pTable ? json_string_value(pTable) : ""); @@ -371,9 +371,9 @@ auto_ptr create_rule_from_elements(json_t* pColumn, if (ok) { - sRule = auto_ptr(new MaskingRules::Rule(column, table, database, - value, fill, - applies_to, exempted)); + sRule = auto_ptr(new MaskingRules::ReplaceRule(column, table, database, + applies_to, exempted, + value, fill)); } return sRule; @@ -389,17 +389,17 @@ auto_ptr create_rule_from_elements(json_t* pColumn, * * @return A Rule instance or NULL in case of error. */ -auto_ptr create_rule_from_elements(json_t* pReplace, - json_t* pWith, - json_t* pApplies_to, - json_t* pExempted) +auto_ptr create_rule_from_elements(json_t* pReplace, + json_t* pWith, + json_t* pApplies_to, + json_t* pExempted) { ss_dassert(pReplace && json_is_object(pReplace)); ss_dassert(pWith && json_is_object(pWith)); ss_dassert(!pApplies_to || json_is_array(pApplies_to)); ss_dassert(!pExempted || json_is_array(pExempted)); - auto_ptr sRule; + auto_ptr sRule; json_t* pDatabase = json_object_get(pReplace, KEY_DATABASE); json_t* pTable = json_object_get(pReplace, KEY_TABLE); @@ -478,7 +478,7 @@ bool create_rules_from_array(json_t* pRules, vector sRule = MaskingRules::Rule::create_from(pRule); + auto_ptr sRule = MaskingRules::ReplaceRule::create_from(pRule); if (sRule.get()) { @@ -550,26 +550,39 @@ MaskingRules::Rule::Account::~Account() MaskingRules::Rule::Rule(const std::string& column, const std::string& table, const std::string& database, - const std::string& value, - const std::string& fill, const std::vector& applies_to, const std::vector& exempted) : m_column(column) , m_table(table) , m_database(database) - , m_value(value) - , m_fill(fill) , m_applies_to(applies_to) , m_exempted(exempted) { } +MaskingRules::ReplaceRule::ReplaceRule(const std::string& column, + const std::string& table, + const std::string& database, + const std::vector& applies_to, + const std::vector& exempted, + const std::string& value, + const std::string& fill) + : MaskingRules::Rule::Rule(column, table, database, applies_to, exempted) + , m_value(value) + , m_fill(fill) +{ +} + MaskingRules::Rule::~Rule() { } +MaskingRules::ReplaceRule::~ReplaceRule() +{ +} + //static -auto_ptr MaskingRules::Rule::create_from(json_t* pRule) +auto_ptr MaskingRules::ReplaceRule::create_from(json_t* pRule) { ss_dassert(json_is_object(pRule)); @@ -704,7 +717,7 @@ bool MaskingRules::Rule::matches(const ComQueryResponse::ColumnDef& column_def, return match; } -void MaskingRules::Rule::rewrite(LEncString& s) const +void MaskingRules::ReplaceRule::rewrite(LEncString& s) const { bool rewritten = false; diff --git a/server/modules/filter/masking/maskingrules.hh b/server/modules/filter/masking/maskingrules.hh index 823559e92..9f3fbaf65 100644 --- a/server/modules/filter/masking/maskingrules.hh +++ b/server/modules/filter/masking/maskingrules.hh @@ -67,13 +67,11 @@ public: typedef std::tr1::shared_ptr SAccount; /** - * Constructor + * Constructor of base Rule class * * @param column The column value from the json file. * @param table The table value from the json file. * @param database The database value from the json file. - * @param value The value value from the json file. - * @param fill The file value from the json file. * @param applies_to Account instances corresponding to the * accounts listed in 'applies_to' in the json file. * @param exempted Account instances corresponding to the @@ -82,8 +80,6 @@ public: Rule(const std::string& column, const std::string& table, const std::string& database, - const std::string& value, - const std::string& fill, const std::vector& applies_to, const std::vector& exempted); ~Rule(); @@ -102,14 +98,6 @@ public: { return m_database; } - const std::string& value() const - { - return m_value; - } - const std::string& fill() const - { - return m_fill; - } const std::vector& applies_to() const { return m_applies_to; @@ -119,16 +107,6 @@ public: return m_exempted; } - /** - * Create a Rule instance - * - * @param pRule A json object corresponding to a single - * rule in the rules json file. - * - * @return A Rule instance or NULL. - */ - static std::auto_ptr create_from(json_t* pRule); - /** * Establish whether a rule matches a column definition and user/host. * @@ -142,7 +120,12 @@ public: const char* zUser, const char* zHost) const; - void rewrite(LEncString& s) const; + /** + * Mask the column content with value or fill. + * + * @param s The current value to be rewritten. + */ + virtual void rewrite(LEncString& s) const = 0; private: Rule(const Rule&); @@ -152,12 +135,70 @@ public: std::string m_column; std::string m_table; std::string m_database; - std::string m_value; - std::string m_fill; std::vector m_applies_to; std::vector m_exempted; }; + class ReplaceRule : public Rule + { + public: + /** + * Constructor of ReplaceRule + * + * @param column The column value from the json file. + * @param table The table value from the json file. + * @param database The database value from the json file. + * @param applies_to Account instances corresponding to the + * accounts listed in 'applies_to' in the json file. + * @param exempted Account instances corresponding to the + * accounts listed in 'exempted' in the json file. + * @param value The replace value from the json file. + * @param fill The fill value from the json file. + */ + ReplaceRule(const std::string& column, + const std::string& table, + const std::string& database, + const std::vector& applies_to, + const std::vector& exempted, + const std::string& value, + const std::string& fill); + + ~ReplaceRule(); + + const std::string& value() const + { + return m_value; + } + const std::string& fill() const + { + return m_fill; + } + + /** + * Create a ReplaceRule instance + * + * @param pRule A json object corresponding to a single + * rule in the rules json file. + * + * @return A Rule instance or NULL. + */ + static std::auto_ptr create_from(json_t* pRule); + + /** + * Rewrite the column value based on rules + * + * @param s The column value to rewrite. + */ + void rewrite(LEncString& s) const; + + private: + std::string m_value; + std::string m_fill; + private: + ReplaceRule(const ReplaceRule&); + ReplaceRule& operator = (const ReplaceRule&); + }; + ~MaskingRules(); /** From 9490af2fb1e20c0dd9442895565a19272df99701 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Fri, 7 Jul 2017 17:31:37 +0200 Subject: [PATCH 2/2] MXS-1302: Addition of ObfuscateRule derived class and 'obfuscation' rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New rule ‘obfuscate’ is being added: { "obfuscate": { "column": “p_name”, "database": "test", "table": "masking" } }, "applies_to": [ ... ], "exempted": [ ... ] --- server/modules/filter/masking/maskingrules.cc | 169 +++++++++++++++++- server/modules/filter/masking/maskingrules.hh | 44 +++++ 2 files changed, 212 insertions(+), 1 deletion(-) diff --git a/server/modules/filter/masking/maskingrules.cc b/server/modules/filter/masking/maskingrules.cc index c0987e21d..ba039f8af 100644 --- a/server/modules/filter/masking/maskingrules.cc +++ b/server/modules/filter/masking/maskingrules.cc @@ -42,6 +42,7 @@ static const char KEY_RULES[] = "rules"; static const char KEY_TABLE[] = "table"; static const char KEY_VALUE[] = "value"; static const char KEY_WITH[] = "with"; +static const char KEY_OBFUSCATE[] = "obfuscate"; /** * @class AccountVerbatim @@ -478,7 +479,18 @@ bool create_rules_from_array(json_t* pRules, vector sRule = MaskingRules::ReplaceRule::create_from(pRule); + auto_ptr sRule; + json_t* pObfuscate = json_object_get(pRule, KEY_OBFUSCATE); + json_t* pReplace = json_object_get(pRule, KEY_REPLACE); + + if (pObfuscate) + { + sRule = MaskingRules::ObfuscateRule::create_from(pRule); + } + else + { + sRule = MaskingRules::ReplaceRule::create_from(pRule); + } if (sRule.get()) { @@ -573,6 +585,15 @@ MaskingRules::ReplaceRule::ReplaceRule(const std::string& column, { } +MaskingRules::ObfuscateRule::ObfuscateRule(const std::string& column, + const std::string& table, + const std::string& database, + const std::vector& applies_to, + const std::vector& exempted) + : MaskingRules::Rule::Rule(column, table, database, applies_to, exempted) +{ +} + MaskingRules::Rule::~Rule() { } @@ -581,6 +602,42 @@ MaskingRules::ReplaceRule::~ReplaceRule() { } +MaskingRules::ObfuscateRule::~ObfuscateRule() +{ +} + +/** Check the Json array for user rules + * + * @param pApplies_to The array of users the rule is applied to + * @param pExempted The array of users the rule is NOT applied to + * + * @return False on errors, True otherwise + */ +static bool validate_user_rules(json_t* pApplies_to, json_t* pExempted) +{ + const char *err = NULL; + // Check for pApplies_to and pExempted + if (pApplies_to && !json_is_array(pApplies_to)) + { + err = KEY_APPLIES_TO; + } + + if (pExempted && !json_is_array(pExempted)) + { + err = KEY_EXEMPTED; + } + + if (err) + { + MXS_ERROR("A masking rule contains a '%s' key, " + "but the value is not an array.", + err); + return false; + } + + return true; +} + //static auto_ptr MaskingRules::ReplaceRule::create_from(json_t* pRule) { @@ -638,6 +695,81 @@ auto_ptr MaskingRules::ReplaceRule::create_from(json_t* pRul return sRule; } +//static +auto_ptr MaskingRules::ObfuscateRule::create_from(json_t* pRule) +{ + ss_dassert(json_is_object(pRule)); + + auto_ptr sRule; + + // Get obfuscate + json_t* pObfuscate = json_object_get(pRule, KEY_OBFUSCATE); + // Get applies_to + json_t* pApplies_to = json_object_get(pRule, KEY_APPLIES_TO); + // Get applies_to + json_t* pExempted = json_object_get(pRule, KEY_EXEMPTED); + + // Check the pObfuscate object + if (pObfuscate && !json_is_object(pObfuscate)) + { + MXS_ERROR("A masking rule contains a '%s' key, " + "but the value is not an object.", + KEY_OBFUSCATE); + return sRule; + } + + // Check for pApplies_to and pExempted + if (!validate_user_rules(pApplies_to, pExempted)) + { + return sRule; + } + + vector > applies_to; + vector > exempted; + + // Set the account rules + if (pApplies_to && pExempted && + (!get_accounts(KEY_APPLIES_TO, pApplies_to, applies_to) || + (!get_accounts(KEY_EXEMPTED, pExempted, exempted)))) + { + return sRule; + } + + // Get database, table and column from obfuscate object + json_t* pDatabase = json_object_get(pObfuscate, KEY_DATABASE); + json_t* pTable = json_object_get(pObfuscate, KEY_TABLE); + json_t* pColumn = json_object_get(pObfuscate, KEY_COLUMN); + + // A column is mandatory; both table and database are optional. + if ((pColumn && json_is_string(pColumn)) && + (!pTable || json_is_string(pTable)) && + (!pDatabase || json_is_string(pDatabase))) + { + // Instantiate the ObfuscateRule class + string column(json_string_value(pColumn)); + string table(pTable ? json_string_value(pTable) : ""); + string database(pDatabase ? json_string_value(pDatabase) : ""); + + sRule = auto_ptr(new MaskingRules::ObfuscateRule(column, + table, + database, + applies_to, + exempted)); + } + else + { + MXS_ERROR("The '%s' object of a masking rule does not have a '%s' key, or " + "the values of that key and/or possible '%s' and '%s' keys are " + "not strings.", + KEY_OBFUSCATE, + KEY_COLUMN, + KEY_TABLE, + KEY_DATABASE); + } + + return sRule; +} + string MaskingRules::Rule::match() const { string s; @@ -717,6 +849,41 @@ bool MaskingRules::Rule::matches(const ComQueryResponse::ColumnDef& column_def, return match; } +/** + * Basic obfuscation routine + * + * @param c The bye to obfuscate + * + * @return The obfuscated byte + */ +static inline char maxscale_basic_obfuscation(const char c) +{ + if (c >= 'a' && c <= 'z') + { + return (c - 'a' + 13) % 26 + 'a'; + } + else if (c >= 'A' && c <= 'Z') + { + return (c - 'A' + 13) % 26 + 'A'; + } + else + { + char d = c + 32; + d = d > 127 ? 127 : d; + return d; + } + return c; +} + +void MaskingRules::ObfuscateRule::rewrite(LEncString& s) const +{ + // Basic Obfuscation routine + std::transform(s.begin(), + s.end(), + s.begin(), + maxscale_basic_obfuscation); +} + void MaskingRules::ReplaceRule::rewrite(LEncString& s) const { bool rewritten = false; diff --git a/server/modules/filter/masking/maskingrules.hh b/server/modules/filter/masking/maskingrules.hh index 9f3fbaf65..f499f1e56 100644 --- a/server/modules/filter/masking/maskingrules.hh +++ b/server/modules/filter/masking/maskingrules.hh @@ -199,6 +199,50 @@ public: ReplaceRule& operator = (const ReplaceRule&); }; + class ObfuscateRule : public Rule + { + public: + /** + * Constructor of ObfuscateRule + * + * @param column The column value from the json file. + * @param table The table value from the json file. + * @param database The database value from the json file. + * @param applies_to Account instances corresponding to the + * accounts listed in 'applies_to' in the json file. + * @param exempted Account instances corresponding to the + * accounts listed in 'exempted' in the json file. + */ + ObfuscateRule(const std::string& column, + const std::string& table, + const std::string& database, + const std::vector& applies_to, + const std::vector& exempted); + + ~ObfuscateRule(); + + /** + * Create a ObfuscateRule instance + * + * @param pRule A json object corresponding to a single + * rule in the rules json file. + * + * @return A Rule instance or NULL. + */ + static std::auto_ptr create_from(json_t* pRule); + + /** + * Obfuscate the column value based on rules + * + * @param s The column value to obfuscate. + */ + void rewrite(LEncString& s) const; + + private: + ObfuscateRule(const ObfuscateRule&); + ObfuscateRule& operator = (const ObfuscateRule&); + }; + ~MaskingRules(); /**