openGauss-server/contrib/security_plugin/gs_mask_policy.cpp

743 lines
24 KiB
C++

/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* gs_mask_policy.h
*
* IDENTIFICATION
* contrib/security_plugin/gs_mask_policy.h
*
* -------------------------------------------------------------------------
*/
#include "utils/relcache.h"
#include "utils/lsyscache.h"
#include "utils/acl.h"
#include "utils/atomic.h"
#include "utils/builtins.h"
#include "catalog/gs_policy_label.h"
#include "catalog/gs_masking_policy.h"
#include "catalog/gs_masking_policy_actions.h"
#include "catalog/gs_masking_policy_filters.h"
#include "catalog/namespace.h"
#include "commands/user.h"
#include "storage/lock/lock.h"
#include "storage/spin.h"
#include "access/heapam.h"
#include "access/hash.h"
#include "commands/tablespace.h"
#include "commands/dbcommands.h"
#include "gs_policy_labels.h"
#include "gs_policy_plugin.h"
#include "pgaudit.h"
#include "gs_mask_policy.h"
#define CMP_STR(a, b) \
int res = strcasecmp(a, b); \
if (res < 0)return true; \
if (res > 0)return false;
#define BUFFSIZE 2048
static THR_LOCAL gs_policy_set *loaded_policies = NULL;
static THR_LOCAL bool masking_policy_reloaded = false;
static THR_LOCAL pg_masking_action_map *loaded_action = NULL;
static THR_LOCAL gs_policy_filter_map *loaded_masking_filters = NULL;
static THR_LOCAL global_roles_in_use *masking_roles_in_use = NULL;
using StrMap = gs_stl::gs_map<gs_stl::gs_string, masking_result>;
using PrepareStmtMap = gs_stl::gs_map<gs_stl::gs_string, bool>;
static pg_atomic_uint64 mask_global_version = 1;
static pg_atomic_uint64 action_global_version = 1;
static pg_atomic_uint64 mask_filter_global_version = 1;
static THR_LOCAL pg_atomic_uint64 mask_local_version = 0;
static THR_LOCAL pg_atomic_uint64 action_local_version = 0;
static THR_LOCAL pg_atomic_uint64 filter_local_version = 0;
static THR_LOCAL PrepareStmtMap *prepared_stamts_state = NULL;
/* masking policy is changed, need to recompile all prepare stmts */
void set_reload_for_all_stmts()
{
if (prepared_stamts_state == NULL) {
return;
}
PrepareStmtMap::iterator it = prepared_stamts_state->begin();
PrepareStmtMap::iterator eit = prepared_stamts_state->end();
for (; it != eit; ++it) {
*(it->second) = true;
}
}
/* check and reset state of prepare stmt (loaded) */
bool prepare_stmt_is_reload(const char* name)
{
if (prepared_stamts_state == NULL) {
return false;
}
bool res = false;
PrepareStmtMap::iterator it = prepared_stamts_state->find(name);
if (it != prepared_stamts_state->end()) {
res = it->second;
*(it->second) = false;
}
return res;
}
/* delete prepare stmt state */
void unprepare_stmt(const char* name)
{
if (prepared_stamts_state == NULL) {
return;
}
if (!strcasecmp(name, "all")) {
prepared_stamts_state->clear();
} else {
prepared_stamts_state->erase(name);
}
}
/* initilize prepare stmt state */
void prepare_stmt(const char* name)
{
if (prepared_stamts_state == NULL) {
prepared_stamts_state = new PrepareStmtMap();
}
(*prepared_stamts_state)[name] = false;
}
/* we allways get the func name all lowercase from parser so no need to change the compare function. */
typedef struct MaskingFuncsInfo
{
const char* func;
MaskBehaviour type;
} MaskingFuncsInfo;
static MaskingFuncsInfo masking_funcs_infos[] =
{
{ "creditcardmasking", M_CREDIT_CARD },
{ "maskall", M_MASKALL },
{ "basicemailmasking", M_BASICEMAIL },
{ "fullemailmasking", M_FULLEMAIL },
{ "alldigitsmasking", M_ALLDIGITS },
{ "shufflemasking", M_SHUFFLE },
{ "randommasking", M_RANDOM },
{ NULL, M_UNKNOWN }
};
static bool get_function_behavious(const char *func_name, int *masking_behavious)
{
(*masking_behavious) = M_UNKNOWN;
for (int i = 0; masking_funcs_infos[i].func != NULL; ++i) {
if (strcmp(masking_funcs_infos[i].func, func_name) == 0) {
(*masking_behavious) = masking_funcs_infos[i].type;
return true;
}
}
return false;
}
bool validate_function_name(const char *func_name)
{
int masking_behavious_dummy = M_UNKNOWN;
return get_function_behavious(func_name, &masking_behavious_dummy);
}
bool load_masking_policies(bool reload)
{
if (!OidIsValid(u_sess->proc_cxt.MyDatabaseId)) {
return false;
}
if (!reload) {
pg_atomic_add_fetch_u64(&mask_global_version, 1);
}
if (pg_atomic_compare_exchange_u64(&mask_global_version, (uint64*)&mask_local_version, mask_global_version)) {
/* Latest masking policy, changes nothing */
return false;
}
Relation rel = NULL;
rel = heap_open(GsMaskingPolicyRelationId, AccessShareLock);
TableScanDesc scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
HeapTuple rtup = NULL;
Form_gs_masking_policy rel_data = NULL;
gs_policy_set* tmp_policies = new gs_policy_set;
while ((rtup = heap_getnext(scan, ForwardScanDirection))) {
rel_data = (Form_gs_masking_policy)GETSTRUCT(rtup);
if (rel_data == NULL || !rel_data->polenabled) {
continue;
}
gs_base_policy item;
item.m_id = HeapTupleGetOid(rtup);
item.m_name = rel_data->polname.data;
item.m_modify_date = rel_data->modifydate;
item.m_enabled = true;
tmp_policies->insert(item);
}
heap_endscan(scan);
heap_close(rel, AccessShareLock);
reset_masking_policy_filters(); /* must reload filters */
/* swap masking policy container */
if (loaded_policies != NULL) {
gs_policy_set *curpol = loaded_policies;
loaded_policies = tmp_policies;
delete curpol;
curpol = NULL;
} else {
loaded_policies = tmp_policies;
}
set_reload_for_all_stmts();
return true;
}
gs_policy_set* get_masking_policies(const char *dbname)
{
load_masking_policies(true);
return loaded_policies;
}
static void parse_params(const gs_stl::gs_string arg, gs_stl::gs_vector<gs_stl::gs_string> *params)
{
params->clear();
if (arg.empty()) {
return;
}
size_t pos = 0;
size_t start = 0;
while ((pos = arg.find(',', start)) != gs_stl::gs_string::npos) {
gs_stl::gs_string tmp(arg.c_str() + start, pos - start);
params->push_back(tmp.c_str());
start = ++pos;
}
gs_stl::gs_string tmp(arg.c_str() + start, arg.size() - start);
params->push_back(tmp.c_str());
}
size_t PolicyAccessHash::operator()(const GsMaskingAction& k) const
{
size_t seed = 0;
seed = policy_hash_combine(seed, hash_uint32(k.m_func_id));
seed = policy_hash_combine(seed, policy_str_hash(k.m_label_name));
return seed;
}
bool EqualToPolicyAccess::operator()(const GsMaskingAction& l, const GsMaskingAction& r) const
{
return l.m_func_id == r.m_func_id && !strcasecmp(l.m_label_name.c_str(), r.m_label_name.c_str());
}
bool load_masking_actions(bool reload)
{
if (!OidIsValid(u_sess->proc_cxt.MyDatabaseId)) {
return false;
}
if (!reload) {
pg_atomic_add_fetch_u64(&action_global_version, 1);
}
if (pg_atomic_compare_exchange_u64(&action_global_version, (uint64*)&action_local_version, action_global_version)) {
/* Latest masking action, changes nothing */
return false;
}
Relation rel = NULL;
rel = heap_open(GsMaskingPolicyActionsId, AccessShareLock);
TableScanDesc scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
HeapTuple rtup = NULL;
Form_gs_masking_policy_actions rel_data = NULL;
pg_masking_action_map* tmp_actions = new pg_masking_action_map;
while ((rtup = heap_getnext(scan, ForwardScanDirection))) {
rel_data = (Form_gs_masking_policy_actions)GETSTRUCT(rtup);
if (rel_data == NULL) {
continue;
}
GsMaskingAction item;
get_function_behavious(rel_data->actiontype.data, &item.m_func_id);
item.m_label_name = rel_data->actlabelname.data;
item.m_modify_date = rel_data->actmodifydate;
item.m_policy_id = rel_data->policyoid;
parse_params(rel_data->actparams.data, &item.m_params);
(*tmp_actions)[item.m_policy_id].insert(item);
}
heap_endscan(scan);
heap_close(rel, AccessShareLock);
if (loaded_action != NULL) {
pg_masking_action_map *curact = loaded_action;
loaded_action = tmp_actions;
delete curact;
curact = NULL;
} else {
loaded_action = tmp_actions;
}
masking_policy_reloaded = true;
set_reload_for_all_stmts();
return true;
}
pg_masking_action_map* get_masking_actions()
{
load_masking_actions(true);
return loaded_action;
}
static inline int parse_operator_type(const char* type)
{
if (!strcasecmp(type, "string"))
return T_String;
if (!strcasecmp(type, "float"))
return T_Float;
return T_Integer;
}
void reset_masking_policy_filters()
{
pg_atomic_exchange_u64(&filter_local_version, 0);
}
bool load_masking_policy_filters(bool reload)
{
if (!OidIsValid(u_sess->proc_cxt.MyDatabaseId)) {
return false;
}
if (!reload) {
pg_atomic_add_fetch_u64(&mask_filter_global_version, 1);
}
if (pg_atomic_compare_exchange_u64(&mask_filter_global_version, (uint64*)&filter_local_version,
mask_filter_global_version)) {
/* Latest masking filter, changes nothing */
return false;
}
Relation rel = NULL;
rel = heap_open(GsMaskingPolicyFiltersId, AccessShareLock);
TableScanDesc scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
HeapTuple rtup = NULL;
Form_gs_masking_policy_filters rel_data = NULL;
gs_policy_filter_map* tmp_filters = new gs_policy_filter_map;
global_roles_in_use* masking_roles_in_use_tmp = new global_roles_in_use;
while ((rtup = heap_getnext(scan, ForwardScanDirection))) {
rel_data = (Form_gs_masking_policy_filters)GETSTRUCT(rtup);
if (rel_data == NULL)
continue;
bool isNull = true;
Datum logical_operator_datum = heap_getattr(rtup, Anum_gs_masking_policy_fltr_logical_operator,
RelationGetDescr(rel), &isNull);
const char* logical_operator = "";
if (!isNull)
logical_operator = TextDatumGetCString(logical_operator_datum);
PolicyLogicalTree ltree;
ltree.parse_logical_expression(logical_operator);
ltree.get_roles(masking_roles_in_use_tmp);
GsPolicyFilter item(ltree, rel_data->policyoid, rel_data->modifydate);
set_filter(&item, tmp_filters);
}
heap_endscan(scan);
heap_close(rel, AccessShareLock);
/* add policies without filter */
gs_policy_set* all_policies = get_masking_policies();
if (all_policies) {
gs_policy_set::const_iterator it = all_policies->begin();
gs_policy_set::const_iterator eit = all_policies->end();
for (; it != eit; ++it) {
(*tmp_filters)[it->m_id];
}
}
if (loaded_masking_filters != NULL) {
gs_policy_filter_map *curfilter = loaded_masking_filters;
loaded_masking_filters = tmp_filters;
delete curfilter;
curfilter = NULL;
} else {
loaded_masking_filters = tmp_filters;
}
if (masking_roles_in_use != NULL) {
global_roles_in_use *cur = masking_roles_in_use;
masking_roles_in_use = masking_roles_in_use_tmp;
delete cur;
cur = NULL;
} else {
masking_roles_in_use = masking_roles_in_use_tmp;
}
set_reload_for_all_stmts();
return true;
}
gs_policy_filter_map* get_masking_filters()
{
load_masking_policy_filters(true);
return loaded_masking_filters;
}
bool reload_masking_policy()
{
if (!OidIsValid(u_sess->proc_cxt.MyDatabaseId)) {
return false;
}
load_masking_actions(true);
load_masking_policies(true);
/* load filters must be last */
load_masking_policy_filters(true);
if (masking_policy_reloaded) {
set_reload_for_all_stmts();
masking_policy_reloaded = false;
}
return masking_policy_reloaded;
}
bool check_masking_policy_filter(const FilterData *arg, policy_set *policy_ids)
{
return check_policy_filter(arg, policy_ids, get_masking_policies(), get_masking_filters());
}
static bool table_base_policy(const typed_labels *labels, long long func_id,
const PolicyLabelItem *tbl_name, int *masking_behavious, int obj_type)
{
typed_labels::const_iterator tit = labels->find(obj_type);
if (tit != labels->end()) {
const gs_policy_label_set& found_labels = *(tit->second);
if (found_labels.find(*tbl_name) != found_labels.end()) {
(*masking_behavious) = func_id;
return true;
}
}
return false;
}
static bool verify_policy_object(const typed_labels *labels, const GsMaskingAction *arg,
const PolicyLabelItem *col_name, int *masking_behavious,
gs_stl::gs_vector<gs_stl::gs_string> *params, int obj_type, long long polid)
{
if (table_base_policy(labels, arg->m_func_id, col_name, masking_behavious, O_COLUMN)) {
if (arg->m_params.size()) {
(*params) = arg->m_params;
}
return true;
}
PolicyLabelItem tbl_name(col_name->m_schema, col_name->m_object, obj_type);
if (table_base_policy(labels, arg->m_func_id, &tbl_name, masking_behavious, obj_type)) {
if (arg->m_params.size()) {
(*params) = arg->m_params;
}
return true;
}
return false;
}
static bool verify_policy(const policy_set *policy_ids, long long *polid,
gs_stl::gs_vector<gs_stl::gs_string> *params,
const PolicyLabelItem *col_name, const PolicyLabelItem *view_name, int *masking_behavious)
{
pg_masking_action_map* tmp_action = get_masking_actions();
if (tmp_action == NULL || tmp_action->empty()) {
return false;
}
loaded_labels* tmp_labels = get_policy_labels();
if (tmp_labels == NULL || tmp_labels->empty()) {
return false;
}
/* find by table */
PolicyLabelItem tbl_name(col_name->m_schema, col_name->m_object, O_TABLE);
policy_set::const_iterator pol_it = policy_ids->begin();
policy_set::const_iterator pol_eit = policy_ids->end();
for (; pol_it != pol_eit; ++pol_it) {
pg_masking_action_map::const_iterator acc_it = tmp_action->find(pol_it->m_id);
if (acc_it == tmp_action->end()) {
continue;
}
const pg_masking_action_set& actions = *(acc_it.second);
pg_masking_action_set::const_iterator ait = actions.begin();
pg_masking_action_set::const_iterator aeit = actions.end();
for (; ait != aeit; ++ait) {
/* check label */
loaded_labels::const_iterator lit = tmp_labels->find(ait->m_label_name);
if (lit != tmp_labels->end()) {
/* find by view */
if (view_name->m_object) {
if (verify_policy_object(lit->second, &(*ait), view_name, masking_behavious,
params, O_VIEW, pol_it->m_id)) {
(*polid) = pol_it->m_id;
return true;
}
}
if (verify_policy_object(lit->second, &(*ait), col_name, masking_behavious, params,
O_TABLE, pol_it->m_id)) {
(*polid) = pol_it->m_id;
return true;
}
}
}
}
return false;
}
bool check_masking_policy_action(const policy_set *policy_ids, const PolicyLabelItem *col_name,
const PolicyLabelItem *view_name, int *masking_behavious, long long *polid,
gs_stl::gs_vector<gs_stl::gs_string> *params)
{
return verify_policy(policy_ids, polid, params, col_name, view_name, masking_behavious);
}
static void get_behaviour(int mtype, char* buffer, size_t buffer_size, int *ret_size)
{
errno_t rc = EOK;
switch (mtype) {
case M_CREDIT_CARD:
rc = snprintf_s(buffer + (*ret_size), buffer_size - (*ret_size),
buffer_size - (*ret_size) - 1, ", behavior: [CREDIT CARD]");
securec_check_ss(rc, "\0", "\0");
(*ret_size) += rc;
break;
case M_MASKALL:
rc = snprintf_s(buffer + (*ret_size), buffer_size - (*ret_size),
buffer_size - (*ret_size) - 1, ", behavior: [MASK ALL]");
securec_check_ss(rc, "\0", "\0");
(*ret_size) += rc;
break;
case M_BASICEMAIL:
rc = snprintf_s(buffer + (*ret_size), buffer_size - (*ret_size),
buffer_size - (*ret_size) - 1, ", behavior: [BASIC EMAIL]");
securec_check_ss(rc, "\0", "\0");
(*ret_size) += rc;
break;
case M_FULLEMAIL:
rc = snprintf_s(buffer + (*ret_size), buffer_size - (*ret_size),
buffer_size - (*ret_size) - 1, ", behavior: [FULL EMAIL]");
securec_check_ss(rc, "\0", "\0");
(*ret_size) += rc;
break;
case M_ALLDIGITS:
rc = snprintf_s(buffer + (*ret_size), buffer_size - (*ret_size),
buffer_size - (*ret_size) - 1, ", behavior: [ALL DIGITS]");
securec_check_ss(rc, "\0", "\0");
(*ret_size) += rc;
break;
case M_SHUFFLE:
rc = snprintf_s(buffer + (*ret_size), buffer_size - (*ret_size),
buffer_size - (*ret_size) - 1, ", behavior: [SHUFFLE]");
securec_check_ss(rc, "\0", "\0");
(*ret_size) += rc;
break;
case M_RANDOM:
rc = snprintf_s(buffer + (*ret_size), buffer_size - (*ret_size),
buffer_size - (*ret_size) - 1, ", behavior: [RANDOM]");
securec_check_ss(rc, "\0", "\0");
(*ret_size) += rc;
break;
}
}
void flush_masking_result(const masking_result *result)
{
if (result->empty()) {
return;
}
char user_name[USERNAME_LEN];
errno_t rc = EOK;
masking_result::const_iterator pit = result->begin();
masking_result::const_iterator peit = result->end();
for (; pit != peit; ++pit) {
char buff[BUFFSIZE] = {0};
char session_ip[MAX_IP_LEN] = {0};
get_session_ip(session_ip, MAX_IP_LEN);
int printed_size = snprintf_s(buff, sizeof(buff), sizeof(buff) - 1,
"MASKING EVENT:user name: [%s], app_name: [%s], client_ip: [%s], policy id: [%lld]",
GetUserName(user_name, sizeof(user_name)), get_session_app_name(), session_ip, (*pit->first));
securec_check_ss(printed_size, "\0", "\0");
masking_policy_result::const_iterator bit = pit->second->begin();
masking_policy_result::const_iterator beit = pit->second->end();
int tmp_size = printed_size;
for (; bit != beit; ++bit) {
get_behaviour(*(bit->first), buff, sizeof(buff), &tmp_size);
if (bit->second->size()) {
rc = snprintf_s(buff + tmp_size, sizeof(buff) - tmp_size, sizeof(buff) - tmp_size - 1, ", columns: [");
securec_check_ss(rc, "\0", "\0");
tmp_size += rc;
masking_column_set::iterator cit = bit->second->begin();
masking_column_set::iterator ceit = bit->second->end();
for (int i = 0; cit != ceit; ++cit, ++i) {
rc = snprintf_s(buff + tmp_size, sizeof(buff) - tmp_size, sizeof(buff) - tmp_size - 1,
"%s%s", (i > 0) ? ", " : "", cit->c_str());
securec_check_ss(rc, "\0", "\0");
tmp_size += rc;
}
rc = snprintf_s(buff + tmp_size, sizeof(buff) - tmp_size, sizeof(buff) - tmp_size - 1, "]");
securec_check_ss(rc, "\0", "\0");
tmp_size += rc;
}
}
gs_audit_issue_syslog_message("PGMASKING", buff, MASKING_POLICY_EVENT, AUDIT_OK);
}
}
bool is_masking_role_in_use(Oid roleid)
{
reload_masking_policy();
global_roles_in_use *tmp = masking_roles_in_use;
return (tmp != NULL && tmp->find(roleid) != tmp->end());
}
bool is_masking_has_object(bool column_type_is_changed, const gs_stl::gs_string labelname)
{
if (!column_type_is_changed) {
return true;
}
pg_masking_action_map* mask_action = get_masking_actions();
if (!mask_action) {
return true;
}
pg_masking_action_map::const_iterator ait = mask_action->begin();
pg_masking_action_map::const_iterator aeit = mask_action->end();
for (; ait != aeit; ++ait) {
pg_masking_action_set::const_iterator mit = ait.second->begin();
pg_masking_action_set::const_iterator meit = ait.second->end();
for (; mit != meit; ++mit) {
if (!strcasecmp(labelname.c_str(), mit->m_label_name.c_str())) {
return true;
}
}
}
return false;
}
bool check_masking_policy_actions_for_label(const policy_labels_map *labels_to_drop)
{
Relation rel = NULL;
rel = heap_open(GsMaskingPolicyActionsId, RowExclusiveLock);
TableScanDesc scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
HeapTuple rtup = NULL;
Form_gs_masking_policy_actions rel_data = NULL;
bool is_found = false;
while ((rtup = heap_getnext(scan, ForwardScanDirection)) && !is_found) {
rel_data = (Form_gs_masking_policy_actions)GETSTRUCT(rtup);
if (rel_data == NULL) {
continue;
}
is_found = (labels_to_drop->find(rel_data->actlabelname.data) != labels_to_drop->end());
}
heap_endscan(scan);
heap_close(rel, RowExclusiveLock);
return is_found;
}
/*
* validate_masking_function_name
*
* validate function is part of predefined masking function
* dont allow drop/replace/alter/rename/owner to/set schema
*/
void validate_masking_function_name(List* full_funcname)
{
/* Ignore checking when upgrade */
if (u_sess->attr.attr_common.IsInplaceUpgrade) {
return;
}
char* funcname = NULL;
char* nspname = NULL;
errno_t rc = EOK;
DeconstructQualifiedName(full_funcname, &nspname, &funcname);
if ((nspname == NULL || (nspname != NULL && !strcmp(nspname, "pg_catalog"))) && validate_function_name(funcname)) {
char buff[BUFFSIZE] = {0};
rc = snprintf_s(buff, sizeof(buff), sizeof(buff) - 1,
"function: %s is part of predefined masking functions.", funcname);
securec_check_ss(rc, "\0", "\0");
gs_audit_issue_syslog_message("PGAUDIT", buff, AUDIT_POLICY_EVENT, AUDIT_FAILED);
ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\"", buff)));
return;
}
}
bool get_masking_policy_name_by_oid(Oid polid, gs_stl::gs_string *polname)
{
gs_policy_set *masking_policies = get_masking_policies();
if (masking_policies == NULL || masking_policies->empty()) {
return false;
}
gs_base_policy target_policy(polid);
gs_policy_set::const_iterator polit = masking_policies->find(target_policy);
if (polit != masking_policies->end()) {
*polname = polit->m_name;
return true;
}
return false;
}
int gs_maksing_action_cmp(const void *key1, const void *key2)
{
GsMaskingAction *l = (GsMaskingAction *)key1;
GsMaskingAction *r = (GsMaskingAction *)key2;
if (l->m_func_id < r->m_func_id) {
return -1;
}
if (r->m_func_id < l->m_func_id) {
return 1;
}
return strcasecmp(l->m_label_name.c_str(), r->m_label_name.c_str());
}
void clear_thread_local_masking()
{
if (loaded_policies != NULL) {
delete loaded_policies;
loaded_policies = NULL;
}
if (loaded_action != NULL) {
delete loaded_action;
loaded_action = NULL;
}
if (loaded_masking_filters != NULL) {
delete loaded_masking_filters;
loaded_masking_filters = NULL;
}
if (masking_roles_in_use != NULL) {
delete masking_roles_in_use;
masking_roles_in_use = NULL;
}
if (prepared_stamts_state != NULL) {
delete prepared_stamts_state;
prepared_stamts_state = NULL;
}
}