openGauss-server/contrib/security_plugin/gs_policy_labels.cpp

304 lines
9.9 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_policy_labels.cpp
* operation functions related to labes, such as update, scan, check elements.
*
* IDENTIFICATION
* contrib/security_plugin/gs_policy_labels.cpp
*
* -------------------------------------------------------------------------
*/
#include "access/heapam.h"
#include <ctype.h>
#include "commands/dbcommands.h"
#include "catalog/namespace.h"
#include "catalog/indexing.h"
#include "gs_policy_object_types.h"
#include "gs_policy_labels.h"
#include "storage/lock/lock.h"
#include "storage/spin.h"
#include "utils/atomic.h"
#include "utils/builtins.h"
#include "utils/relcache.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/lsyscache.h"
static THR_LOCAL loaded_labels *all_labels = NULL;
static pg_atomic_uint64 label_global_version = 1;
static THR_LOCAL pg_atomic_uint64 label_local_version = 0;
/*
* update_label_value
* update related label row while data resource is altered
*
* If update target is view/table/function/schema will not update, they recorded using oid. So no need update.
* For column type, update gs_policy_label when table column altered.
*/
bool update_label_value(const gs_stl::gs_string object_name, const gs_stl::gs_string new_object_name,
int object_type)
{
bool updated = false;
bool nulls[Natts_gs_policy_label] = {false};
bool replaces[Natts_gs_policy_label] = {false};
Datum values[Natts_gs_policy_label] = {0};
HeapTuple rtup = NULL;
Relation rel = NULL;
TableScanDesc scan = NULL;
/* schema and table are recorded in Oid, so they are no need to update when renamed */
if (object_type != O_COLUMN) {
return updated;
}
rel = heap_open(GsPolicyLabelRelationId, RowExclusiveLock);
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
if (scan != NULL) {
while ((rtup = heap_getnext(scan, ForwardScanDirection))) {
Form_gs_policy_label rel_data = (Form_gs_policy_label)GETSTRUCT(rtup);
if (rel_data == NULL) {
continue;
}
if (!strcasecmp(rel_data->relcolumn.data, object_name.c_str())) {
replaces[Anum_gs_policy_label_relcolumn - 1] = true;
values[Anum_gs_policy_label_relcolumn - 1] =
DirectFunctionCall1(namein, CStringGetDatum(new_object_name.c_str()));
}
HeapTuple new_tuple = heap_modify_tuple(rtup, RelationGetDescr(rel), values, nulls, replaces);
simple_heap_update(rel, &new_tuple->t_self, new_tuple);
CatalogUpdateIndexes(rel, new_tuple);
updated = true;
}
heap_endscan(scan);
}
heap_close(rel, RowExclusiveLock);
return updated;
}
/*
* scan_policy_labels
* scan existing labels from gs_policy_label to tmp_labels
*/
bool scan_policy_labels(loaded_labels *tmp_labels)
{
Assert(tmp_labels != NULL);
tmp_labels->clear();
TableScanDesc scan = NULL;
HeapTuple rtup = NULL;
Form_gs_policy_label rel_data = NULL;
bool label_type_found = false;
Relation rel = NULL;
rel = heap_open(GsPolicyLabelRelationId, AccessShareLock);
scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
/* scan each row from gs_policy_label into tmp_labels */
while ((rtup = heap_getnext(scan, ForwardScanDirection))) {
rel_data = (Form_gs_policy_label)GETSTRUCT(rtup);
if (rel_data == NULL) {
continue;
}
PolicyLabelItem item(rel_data->fqdnnamespace,
rel_data->fqdnid,
get_privilege_object_type(rel_data->fqdntype.data),
rel_data->relcolumn.data);
const char *label_name = rel_data->labelname.data;
if (item.m_obj_type == O_UNKNOWN) {
if (strlen(label_name) > 0) {
(*tmp_labels)[label_name];
}
continue; /* empty label */
}
(*tmp_labels)[label_name][item.m_obj_type].insert(item);
}
heap_endscan(scan);
heap_close(rel, AccessShareLock);
return label_type_found;
}
/*
* get_label_tables
* return sets of existing labels from given labelname
*/
bool get_label_tables(const char *label_name, policy_default_str_uset *label_tables)
{
loaded_labels *tmp = get_policy_labels();
if (tmp == NULL) {
return false;
}
loaded_labels::const_iterator it = tmp->find(label_name);
if (it == tmp->end()) {
return false;
}
typed_labels::const_iterator fit = it->second->find(O_TABLE);
if (fit != it->second->end()) {
gs_policy_label_set::const_iterator tit = fit->second->begin();
gs_policy_label_set::const_iterator teit = fit->second->end();
for (; tit != teit; ++tit) {
gs_stl::gs_string value;
tit->get_fqdn_value(&value);
label_tables->insert(value.c_str());
}
}
return true;
}
bool is_label_exist(const char *name)
{
if (!strcasecmp(name, "all")) { /* any label */
return true;
}
loaded_labels *tmp = get_policy_labels();
if (tmp == NULL) {
return false;
}
return (tmp->find(name) != tmp->end());
}
loaded_labels *get_policy_labels()
{
load_policy_labels(true);
return all_labels;
}
/*
* is_labels_has_object
* this function is for other features (masking, auditing etc.)
* check given fqdn name is bound to labels, and this label is belong to policies
* CheckLabelBoundPolicy is the check function provided by other features that label is of policies
* labels means used labels in other features.
*/
bool is_labels_has_object(const GsPolicyFQDN *fqdn, const policy_default_str_set *labels,
bool (*CheckLabelBoundPolicy)(bool, const gs_stl::gs_string))
{
static const PrivObject check_obj_types[] = {
O_VIEW,
O_TABLE,
O_COLUMN,
O_UNKNOWN
};
PolicyLabelItem object(0, fqdn->m_value_object, O_UNKNOWN);
for (uint8 i = 0; check_obj_types[i] != O_UNKNOWN; ++i) {
object.m_obj_type = check_obj_types[i];
if (check_obj_types[i] == O_COLUMN) {
int rc = snprintf_s(object.m_column, sizeof(object.m_column),
fqdn->m_value_object_attrib.size(), "%s", fqdn->m_value_object_attrib.c_str());
securec_check_ss(rc, "\0", "\0");
}
if (check_label_has_object(&object, CheckLabelBoundPolicy, false, labels)) {
return true;
}
}
return false;
}
bool load_policy_labels(bool reload)
{
if (!OidIsValid(u_sess->proc_cxt.MyDatabaseId)) {
return false;
}
if (!reload) {
pg_atomic_add_fetch_u64(&label_global_version, 1);
}
if (pg_atomic_compare_exchange_u64(&label_global_version, (uint64*)&label_local_version, label_global_version)) {
/* Latest label, changes nothing */
return false;
}
/* scan to load all labels */
if (all_labels == NULL) {
all_labels = new loaded_labels;
}
scan_policy_labels(all_labels);
return true;
}
/*
* check_label_has_object
* check object is bound to labels and used in other policies.
* param 'labels' means labels used in other features.
* CheckLabelBoundPolicy is the check function provided by other features that label is of policies
*/
bool check_label_has_object(const PolicyLabelItem *object,
bool (*CheckLabelBoundPolicy)(bool, const gs_stl::gs_string),
bool column_type_is_changed,
const policy_default_str_set *labels)
{
/* Ignore checking when upgrade */
if (u_sess->attr.attr_common.IsInplaceUpgrade) {
return false;
}
Assert(CheckLabelBoundPolicy != NULL);
loaded_labels *all_labels = get_policy_labels();
if (all_labels == NULL) {
return false;
}
loaded_labels::const_iterator it = all_labels->begin();
loaded_labels::const_iterator eit = all_labels->end();
for (; it != eit; ++it) {
/* for each item of loaded existing labels, and match labels */
if (labels != NULL && labels->find(*(it->first)) == labels->end()) {
continue;
}
/* find wether object(label row) is in matched label */
typed_labels::const_iterator fit = it->second->find(object->m_obj_type);
if (fit != it->second->end()) {
if (fit->second->find(*object) != fit->second->end()) {
/* check label is bound to Policy */
if (CheckLabelBoundPolicy(column_type_is_changed, *(it->first))) {
return true;
}
} else if (object->m_obj_type == O_COLUMN) { /* verify table of column */
gs_policy_label_set::const_iterator tit, teit;
tit = fit->second->begin();
teit = fit->second->end();
for (; tit != teit; ++tit) {
if (tit->m_schema == object->m_schema && tit->m_object == object->m_object) {
return true;
}
}
}
}
}
return false;
}
void reset_policy_labels()
{
pg_atomic_exchange_u64(&label_local_version, 0);
}
void clear_thread_local_label()
{
if (all_labels != NULL) {
delete all_labels;
all_labels = NULL;
}
}