openGauss-server/contrib/security_plugin/gs_audit_policy.cpp

746 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_audit_policy.cpp
* auditing flags and objects information for the policy.
*
* IDENTIFICATION
* contrib/security_plugin/gs_audit_policy.cpp
*
* -------------------------------------------------------------------------
*/
#include <ctype.h>
#include "utils/relcache.h"
#include "utils/syscache.h"
#include "utils/lsyscache.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "catalog/gs_auditing_policy.h"
#include "catalog/gs_auditing_policy_acc.h"
#include "catalog/gs_auditing_policy_filter.h"
#include "catalog/gs_auditing_policy_priv.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_namespace.h"
#include "catalog/namespace.h"
#include "storage/lock/lock.h"
#include "storage/spin.h"
#include "access/heapam.h"
#include "commands/tablespace.h"
#include "commands/dbcommands.h"
#include "utils/atomic.h"
#include "utils/snapmgr.h"
#include "gs_audit_policy.h"
#include "gs_policy_plugin.h"
#include "gs_policy_logical_tree.h"
/* audit policy */
static THR_LOCAL gs_policy_set *loaded_auditing_policies = NULL;
/* audit access */
static THR_LOCAL gs_policy_base_map *loaded_access = NULL;
static THR_LOCAL access_privilege_set *loaded_audited_access = NULL;
/* audit filter */
static THR_LOCAL gs_policy_filter_map *loaded_policy_filters = NULL;
static THR_LOCAL global_roles_in_use *audit_roles_in_use = NULL;
/* audit priv */
static THR_LOCAL gs_policy_base_map *loaded_privileges = NULL;
static THR_LOCAL access_privilege_set *loaded_audited_privileges = NULL;
static pg_atomic_uint64 audit_global_version = 1;
static pg_atomic_uint64 access_global_version = 1;
static pg_atomic_uint64 audit_filter_global_version = 1;
static pg_atomic_uint64 priv_global_version = 1;
THR_LOCAL pg_atomic_uint64 audit_local_version = 0;
THR_LOCAL pg_atomic_uint64 access_local_version = 0;
THR_LOCAL pg_atomic_uint64 filter_local_version = 0;
THR_LOCAL pg_atomic_uint64 priv_local_version = 0;
const char* get_access_name(int type)
{
switch (type) {
case CMD_SELECT:
return "SELECT";
case CMD_INSERT:
return "INSERT";
case CMD_UPDATE:
return "UPDATE";
case CMD_DELETE:
return "DELETE";
case CMD_TRUNCATE:
return "TRUNCATE";
case CMD_PREPARE:
return "PREPARE";
case CMD_DEALLOCATE:
return "DEALLOCATE";
case CMD_EXECUTE:
return "EXECUTE";
case CMD_REINDEX:
return "REINDEX";
default:
return "NONE";
}
return "NONE";
}
int get_access_type(const char *name)
{
if (!strcasecmp(name, "SELECT")) {
return CMD_SELECT;
}
if (!strcasecmp(name, "INSERT")) {
return CMD_INSERT;
}
if (!strcasecmp(name, "UPDATE")) {
return CMD_UPDATE;
}
if (!strcasecmp(name, "DELETE")) {
return CMD_DELETE;
}
if (!strcasecmp(name, "TRUNCATE")) {
return CMD_TRUNCATE;
}
if (!strcasecmp(name, "PREPARE")) {
return CMD_PREPARE;
}
if (!strcasecmp(name, "DEALLOCATE")) {
return CMD_DEALLOCATE;
}
if (!strcasecmp(name, "EXECUTE")) {
return CMD_EXECUTE;
}
if (!strcasecmp(name, "REINDEX")) {
return CMD_REINDEX;
}
if (!strcasecmp(name, "COPY")) {
return CMD_UTILITY;
}
if (!strcasecmp(name, "ALL")) {
return CMD_UTILITY;
}
return CMD_UNKNOWN;
}
bool reload_audit_policy()
{
load_policy_accesses(true);
load_policy_privileges(true);
load_audit_policies(true);
/* load filters must be last */
return load_policy_filters(true);
}
bool load_audit_policies(bool reload)
{
if (!reload) {
pg_atomic_add_fetch_u64(&audit_global_version, 1);
}
if (pg_atomic_compare_exchange_u64(&audit_global_version, (uint64*)&audit_local_version, audit_global_version)) {
/* Latest audit policy, changes nothing */
return false;
}
GET_RELATION(GsAuditingPolicyRelationId, AccessShareLock);
TableScanDesc scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
HeapTuple rtup;
Form_gs_auditing_policy rel_data;
gs_policy_set *tmp_policies = new gs_policy_set;
while ((rtup = heap_getnext(scan, ForwardScanDirection))) {
rel_data = (Form_gs_auditing_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_enabled = true;
item.m_modify_date = rel_data->modifydate;
tmp_policies->insert(item);
}
heap_endscan(scan);
heap_close(rel, AccessShareLock);
reset_policy_filters(); /* must reload filters */
if (loaded_auditing_policies != NULL) {
gs_policy_set *pol = loaded_auditing_policies;
loaded_auditing_policies = tmp_policies;
delete pol;
pol = NULL;
} else {
loaded_auditing_policies = tmp_policies;
}
return true;
}
/*
* get audit policy info from cache, Note that u_sess->proc_cxt.MyDatabaseId maybe invalid in some
* senario so that need get the oid from dbname directlly
*/
gs_policy_set *get_audit_policies(const char *dbname)
{
load_audit_policies(true);
return loaded_auditing_policies;
}
bool load_policy_accesses(bool reload)
{
if (!OidIsValid(u_sess->proc_cxt.MyDatabaseId)) {
return false;
}
if (!reload) {
pg_atomic_add_fetch_u64(&access_global_version, 1);
}
if (pg_atomic_compare_exchange_u64(&access_global_version, (uint64*)&access_local_version, access_global_version)) {
/* Latest policy access, changes nothing */
return false;
}
GET_RELATION(GsAuditingPolicyAccessRelationId, AccessShareLock);
TableScanDesc scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
HeapTuple rtup;
Form_gs_auditing_policy_access rel_data;
gs_policy_base_map *tmp_accesses = new gs_policy_base_map;
access_privilege_set *tmp_access = new access_privilege_set;
while ((rtup = heap_getnext(scan, ForwardScanDirection))) {
rel_data = (Form_gs_auditing_policy_access)GETSTRUCT(rtup);
if (rel_data == NULL) {
continue;
}
GsPolicyBase item;
item.m_type = get_access_type(rel_data->accesstype.data);
tmp_access->insert(item.m_type);
item.m_label_name = rel_data->labelname.data;
item.m_modify_date = rel_data->modifydate;
item.m_policy_id = rel_data->policyoid;
(*tmp_accesses)[item.m_policy_id].insert(item);
}
heap_endscan(scan);
heap_close(rel, AccessShareLock);
if (loaded_access != NULL) {
gs_policy_base_map *acc = loaded_access;
loaded_access = tmp_accesses;
delete acc;
acc = NULL;
} else {
loaded_access = tmp_accesses;
}
/* update audited access */
if (loaded_audited_access != NULL) {
access_privilege_set *audit_acc = loaded_audited_access;
loaded_audited_access = tmp_access;
delete audit_acc;
audit_acc = NULL;
} else {
loaded_audited_access = tmp_access;
}
return true;
}
bool check_audited_access(int access)
{
load_policy_accesses(true);
access_privilege_set *tmp = loaded_audited_access;
if (tmp) {
if (tmp->find(access) != tmp->end()) {
return true;
}
/* all */
return tmp->find(CMD_UTILITY) != tmp->end();
}
return false;
}
gs_policy_base_map *get_policy_accesses()
{
(void)load_policy_accesses(true);
return loaded_access;
}
void reset_policy_filters()
{
pg_atomic_exchange_u64(&filter_local_version, 0);
}
bool load_policy_filters(bool reload)
{
if (!OidIsValid(u_sess->proc_cxt.MyDatabaseId)) {
return false;
}
if (!reload) {
pg_atomic_add_fetch_u64(&audit_filter_global_version, 1);
}
if (pg_atomic_compare_exchange_u64(&audit_filter_global_version, (uint64*)&filter_local_version,
audit_filter_global_version)) {
/* Latest audit filter, changes nothing */
return false;
}
GET_RELATION(GsAuditingPolicyFiltersRelationId, AccessShareLock);
TableScanDesc scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
HeapTuple rtup;
Form_gs_auditing_policy_filters rel_data;
gs_policy_filter_map *tmp_filters = new gs_policy_filter_map;
global_roles_in_use *audit_roles_in_use_tmp = new global_roles_in_use;
while ((rtup = heap_getnext(scan, ForwardScanDirection))) {
rel_data = (Form_gs_auditing_policy_filters)GETSTRUCT(rtup);
if (rel_data == NULL) {
continue;
}
bool isNull = true;
Datum logical_operator_datum = heap_getattr(rtup, Anum_gs_auditing_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(audit_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_audit_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_policy_filters != NULL) {
gs_policy_filter_map *cur_filter = loaded_policy_filters;
loaded_policy_filters = tmp_filters;
delete cur_filter;
cur_filter = NULL;
} else {
loaded_policy_filters = tmp_filters;
}
if (audit_roles_in_use != NULL) {
global_roles_in_use *audit_role = audit_roles_in_use;
audit_roles_in_use = audit_roles_in_use_tmp;
delete audit_role;
audit_role = NULL;
} else {
audit_roles_in_use = audit_roles_in_use_tmp;
}
return true;
}
gs_policy_filter_map* get_policy_filters()
{
(void)load_policy_filters(true);
return loaded_policy_filters;
}
bool check_audit_policy_filter(const FilterData *arg, policy_set *policy_ids, const char *dbname)
{
return check_policy_filter(arg, policy_ids, get_audit_policies(dbname), get_policy_filters());
}
static bool verify_object_by_itself(const typed_labels *labels, const PolicyLabelItem *item)
{
typed_labels::const_iterator it;
if ((it = labels->find(item->m_obj_type)) != labels->end()) {
return (it->second->find(*item) != it->second->end());
}
return false;
}
static bool check_label_by_type_and_object(const typed_labels labels, const PolicyLabelItem *item)
{
/* check schema level */
typed_labels::const_iterator it;
if ((it = labels.find(O_SCHEMA)) != labels.end()) {
PolicyLabelItem tmp(item->m_schema, 0, O_SCHEMA);
if (it->second->find(tmp) != it->second->end()) {
return true;
}
}
if (item->m_obj_type == O_COLUMN) {
/* check by table lebel */
if ((it = labels.find(O_TABLE)) != labels.end()) {
PolicyLabelItem tmp(item->m_schema, item->m_object, O_TABLE);
if (it->second->find(tmp) != it->second->end()) {
return true;
}
}
}
return verify_object_by_itself(&labels, item);
}
bool check_privileges(const policy_set *policy_ids, policy_simple_set *policy_result, int privileges_type,
gs_policy_base_map *privileges, const PolicyLabelItem *item)
{
if (privileges == NULL || privileges->empty() || policy_ids == NULL) {
return false;
}
loaded_labels *all_label = get_policy_labels();
/* check policy */
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) {
gs_policy_base_map::const_iterator priv_it = privileges->find(pol_it->m_id);
if (priv_it == privileges->end()) {
continue;
}
const gs_policy_base_set& privileges = *(priv_it.second);
gs_policy_base_set::const_iterator ait = privileges.begin();
gs_policy_base_set::const_iterator aeit = privileges.end();
for (; ait != aeit; ++ait) {
/* check access type */
if (ait->m_type == T_LOGIN) {
if (privileges_type != T_LOGIN_SUCCESS && privileges_type != T_LOGIN_FAILURE) {
continue;
}
} else if (ait->m_type != privileges_type && ait->m_type != T_ALL) {
continue;
}
/* check if label is not any */
if (!strcasecmp(ait->m_label_name.c_str(), "all")) {
policy_result->insert(pol_it->m_id);
continue;
}
/* check label */
if (all_label) {
loaded_labels::const_iterator lit = all_label->find(ait->m_label_name);
if (lit != all_label->end()) {
if (check_label_by_type_and_object((*(lit->second)), item)) {
policy_result->insert(pol_it->m_id);
}
}
}
}
}
return !policy_result->empty();
}
bool check_audit_policy_privileges(const policy_set *policy_ids,
policy_simple_set *policy_result,
int privileges_type,
const PolicyLabelItem *item,
const char *dbname)
{
return check_privileges(policy_ids, policy_result, privileges_type, get_policy_privileges(dbname), item);
}
/*
* Iterate policy labels to match the object labels to generate audit access info
* Note: when there is no label of object access, "all" is the default label
*/
bool check_audit_policy_access(const PolicyLabelItem *item, const PolicyLabelItem *view_item, int access_type,
const policy_set *policy_ids, policy_result *pol_result, gs_policy_base_map *policy_access, int *block_behaviour)
{
if (policy_access == NULL || policy_access->empty() || policy_ids == NULL) {
return false;
}
/*
* load all labels from access object
*/
policy_label_map labels;
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) {
const PolicyPair& pol_item = *pol_it;
gs_policy_base_map::const_iterator acc_it = policy_access->find(pol_item.m_id);
if (acc_it == policy_access->end()) {
continue;
}
const gs_policy_base_set& accesses = *(acc_it.second);
gs_policy_base_set::const_iterator ait = accesses.begin();
gs_policy_base_set::const_iterator aeit = accesses.end();
for (; ait != aeit; ++ait) {
/* check access type */
if (ait->m_type != access_type && ait->m_type != CMD_UTILITY) {
continue;
}
labels[pol_item] = ait->m_label_name.c_str();
}
}
PolicyLabelItem view_object_item(view_item->m_schema, view_item->m_object,
O_COLUMN, view_item->m_column);
/*
* walk through all the access audit label info to match with label of object
*/
bool has_column = false;
if (labels.size()) {
AccessPair object_item("", item->m_obj_type);
{
gs_stl::gs_string value;
item->get_fqdn_value(&value);
object_item.first = value.c_str();
}
loaded_labels *tmp_labels = get_policy_labels();
policy_label_map::const_iterator lit = labels.begin();
policy_label_map::const_iterator elit = labels.end();
for (; lit != elit; ++lit) {
const PolicyPair& pol_item = *(lit.first);
loaded_labels::const_iterator it;
if (tmp_labels != NULL) {
it = tmp_labels->find(lit->second->c_str());
}
if ((tmp_labels == NULL || it == tmp_labels->end()) && *(lit->second) == "all") {
(*pol_result)[pol_item.m_id][object_item];
if (pol_item.m_block_type > 0) {
*block_behaviour = pol_item.m_block_type;
return has_column;
}
} else if (it != tmp_labels->end()) {
typed_labels::const_iterator fit = it->second->begin();
typed_labels::const_iterator feit = it->second->end();
for (; fit != feit; ++fit) {
bool is_columnn = (*(fit->first) == O_COLUMN);
has_column = has_column || is_columnn;
if (*(fit->first) != item->m_obj_type && (*fit->first) != O_SCHEMA) {
continue;
}
const gs_policy_label_set& objects = *(fit->second);
if (is_columnn) {
if (view_object_item.m_object && !view_object_item.empty() &&
objects.find(view_object_item) != objects.end()) {
gs_stl::gs_string value;
view_object_item.get_fqdn_value(&value);
object_item.first = value.c_str();
(*pol_result)[pol_item.m_id][object_item].insert(view_object_item.m_column);
if (pol_item.m_block_type > 0) {
*block_behaviour = pol_item.m_block_type;
return has_column;
}
}
if (objects.find(*item) != objects.end()) {
(*pol_result)[pol_item.m_id][object_item].insert(item->m_column);
if (pol_item.m_block_type > 0) {
*block_behaviour = pol_item.m_block_type;
return has_column;
}
}
} else if (*(fit->first) == O_SCHEMA) {
PolicyLabelItem sch_item;
sch_item.m_schema = item->m_schema;
if (objects.find(sch_item) != objects.end()) {
(*pol_result)[pol_item.m_id][object_item];
}
} else if (objects.find(*item) != objects.end()) {
(*pol_result)[pol_item.m_id][object_item];
if (pol_item.m_block_type > 0) {
*block_behaviour = pol_item.m_block_type;
return has_column;
}
}
}
}
}
}
return has_column;
}
bool check_audited_privilige(int privilige)
{
access_privilege_set *tmp = loaded_audited_privileges;
if (tmp) {
if (tmp->find(privilige) != tmp->end()) {
return true;
}
return tmp->find(T_ALL) != tmp->end();
}
return false;
}
bool load_policy_privileges(bool reload)
{
if (!OidIsValid(u_sess->proc_cxt.MyDatabaseId)) {
return false;
}
if (!reload) {
pg_atomic_add_fetch_u64(&priv_global_version, 1);
}
if (pg_atomic_compare_exchange_u64(&priv_global_version, (uint64*)&priv_local_version, priv_global_version)) {
/* Latest audit priviledges, changes nothing */
return false;
}
GET_RELATION(GsAuditingPolicyPrivilegesRelationId, AccessShareLock);
TableScanDesc scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
HeapTuple rtup;
Form_gs_auditing_policy_privileges rel_data;
gs_policy_base_map *tmp_privileges = new gs_policy_base_map;
access_privilege_set *tmp_audited_priviliges = new access_privilege_set;
while ((rtup = heap_getnext(scan, ForwardScanDirection))) {
rel_data = (Form_gs_auditing_policy_privileges)GETSTRUCT(rtup);
if (rel_data == NULL) {
continue;
}
GsPolicyBase item;
item.m_type = get_privilege_type(rel_data->privilegetype.data);
tmp_audited_priviliges->insert(item.m_type);
item.m_label_name = rel_data->labelname.data;
item.m_modify_date = rel_data->modifydate;
item.m_policy_id = rel_data->policyoid;
(*tmp_privileges)[item.m_policy_id].insert(item);
}
heap_endscan(scan);
heap_close(rel, AccessShareLock);
if (loaded_privileges != NULL) {
gs_policy_base_map *priv = loaded_privileges;
loaded_privileges = tmp_privileges;
delete priv;
priv = NULL;
} else {
loaded_privileges = tmp_privileges;
}
/* update audited privilege */
if (loaded_audited_privileges != NULL) {
access_privilege_set* audit_priv = loaded_audited_privileges;
loaded_audited_privileges = tmp_audited_priviliges;
delete audit_priv;
audit_priv = NULL;
} else {
loaded_audited_privileges = tmp_audited_priviliges;
}
return true;
}
/* get privileges policy */
gs_policy_base_map* get_policy_privileges(const char *dbname)
{
(void)load_policy_privileges(true);
return loaded_privileges;
}
bool is_role_in_use(Oid roleid)
{
reload_audit_policy();
global_roles_in_use *tmp = audit_roles_in_use;
return (tmp != NULL && tmp->find(roleid) != tmp->end());
}
bool check_audit_policy_privileges_for_label(const policy_labels_map *labels_to_drop)
{
GET_RELATION(GsAuditingPolicyPrivilegesRelationId, RowExclusiveLock);
TableScanDesc scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
HeapTuple rtup;
Form_gs_auditing_policy_privileges rel_data;
bool is_found = false;
while ((rtup = heap_getnext(scan, ForwardScanDirection)) && !is_found) {
rel_data = (Form_gs_auditing_policy_privileges)GETSTRUCT(rtup);
if (rel_data == NULL) {
continue;
}
is_found = (labels_to_drop->find(rel_data->privilegetype.data) != labels_to_drop->end());
}
heap_endscan(scan);
heap_close(rel, RowExclusiveLock);
return is_found;
}
bool check_audit_policy_access_for_label(const policy_labels_map *labels_to_drop)
{
GET_RELATION(GsAuditingPolicyAccessRelationId, RowExclusiveLock);
TableScanDesc scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
HeapTuple rtup;
Form_gs_auditing_policy_access rel_data;
bool is_found = false;
while ((rtup = heap_getnext(scan, ForwardScanDirection)) && !is_found) {
rel_data = (Form_gs_auditing_policy_access)GETSTRUCT(rtup);
if (rel_data == NULL) {
continue;
}
is_found = (labels_to_drop->find(rel_data->labelname.data) != labels_to_drop->end());
}
heap_endscan(scan);
heap_close(rel, RowExclusiveLock);
return is_found;
}
bool is_audit_policy_exist(const char *dbname)
{
gs_policy_set *policies = get_audit_policies(dbname);
if (policies == NULL) {
return false;
}
return !policies->empty();
}
void clear_thread_local_auditing()
{
if (loaded_auditing_policies != NULL) {
delete loaded_auditing_policies;
loaded_auditing_policies = NULL;
}
if (loaded_access != NULL) {
delete loaded_access;
loaded_access = NULL;
}
if (loaded_audited_access != NULL) {
delete loaded_audited_access;
loaded_audited_access = NULL;
}
if (loaded_policy_filters != NULL) {
delete loaded_policy_filters;
loaded_policy_filters = NULL;
}
if (audit_roles_in_use != NULL) {
delete audit_roles_in_use;
audit_roles_in_use = NULL;
}
if (loaded_privileges != NULL) {
delete loaded_privileges;
loaded_privileges = NULL;
}
if (loaded_audited_privileges != NULL) {
delete loaded_audited_privileges;
loaded_audited_privileges = NULL;
}
}