/* -------------------------------------------------------------------------
*
* contrib / sepgsql / relation . c
*
* Routines corresponding to relation / attribute objects
*
* Copyright ( c ) 2010 - 2025 , PostgreSQL Global Development Group
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
#include "postgres.h"
#include "access/genam.h"
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "catalog/dependency.h"
#include "catalog/pg_attribute.h"
#include "catalog/pg_class.h"
#include "catalog/pg_namespace.h"
#include "commands/seclabel.h"
#include "lib/stringinfo.h"
#include "sepgsql.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
static void sepgsql_index_modify(Oid indexOid);
/*
* sepgsql_attribute_post_create
*
* This routine assigns a default security label on a newly defined
* column , using ALTER TABLE . . . ADD COLUMN .
* Note that this routine is not invoked in the case of CREATE TABLE ,
* although it also defines columns in addition to table .
*/
void
sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum)
{
Relation rel;
ScanKeyData skey[2 ];
SysScanDesc sscan;
HeapTuple tuple;
char *scontext;
char *tcontext;
char *ncontext;
ObjectAddress object;
Form_pg_attribute attForm;
StringInfoData audit_name;
char relkind = get_rel_relkind(relOid);
/*
* Only attributes within regular relations or partition relations have
* individual security labels .
*/
if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
return ;
/*
* Compute a default security label of the new column underlying the
* specified relation , and check permission to create it .
*/
rel = table_open(AttributeRelationId, AccessShareLock);
ScanKeyInit(&skey[0 ],
Anum_pg_attribute_attrelid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relOid));
ScanKeyInit(&skey[1 ],
Anum_pg_attribute_attnum,
BTEqualStrategyNumber, F_INT2EQ,
Int16GetDatum(attnum));
sscan = systable_beginscan(rel, AttributeRelidNumIndexId, true ,
SnapshotSelf, 2 , &skey[0 ]);
tuple = systable_getnext(sscan);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "could not find tuple for column %d of relation %u" ,
attnum, relOid);
attForm = (Form_pg_attribute) GETSTRUCT(tuple);
scontext = sepgsql_get_client_label();
tcontext = sepgsql_get_label(RelationRelationId, relOid, 0 );
ncontext = sepgsql_compute_create(scontext, tcontext,
SEPG_CLASS_DB_COLUMN,
NameStr(attForm->attname));
/*
* check db_column : { create } permission
*/
object.classId = RelationRelationId;
object.objectId = relOid;
object.objectSubId = 0 ;
initStringInfo(&audit_name);
appendStringInfo(&audit_name, "%s.%s" ,
getObjectIdentity(&object, false ),
quote_identifier(NameStr(attForm->attname)));
sepgsql_avc_check_perms_label(ncontext,
SEPG_CLASS_DB_COLUMN,
SEPG_DB_COLUMN__CREATE,
audit_name.data,
true );
/*
* Assign the default security label on a new procedure
*/
object.classId = RelationRelationId;
object.objectId = relOid;
object.objectSubId = attnum;
SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
systable_endscan(sscan);
table_close(rel, AccessShareLock);
pfree(tcontext);
pfree(ncontext);
}
/*
* sepgsql_attribute_drop
*
* It checks privileges to drop the supplied column .
*/
void
sepgsql_attribute_drop(Oid relOid, AttrNumber attnum)
{
ObjectAddress object;
char *audit_name;
char relkind = get_rel_relkind(relOid);
if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
return ;
/*
* check db_column : { drop } permission
*/
object.classId = RelationRelationId;
object.objectId = relOid;
object.objectSubId = attnum;
audit_name = getObjectIdentity(&object, false );
sepgsql_avc_check_perms(&object,
SEPG_CLASS_DB_COLUMN,
SEPG_DB_COLUMN__DROP,
audit_name,
true );
pfree(audit_name);
}
/*
* sepgsql_attribute_relabel
*
* It checks privileges to relabel the supplied column
* by the ` seclabel ' .
*/
void
sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
const char *seclabel)
{
ObjectAddress object;
char *audit_name;
char relkind = get_rel_relkind(relOid);
if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot set security label on non-regular columns" )));
object.classId = RelationRelationId;
object.objectId = relOid;
object.objectSubId = attnum;
audit_name = getObjectIdentity(&object, false );
/*
* check db_column : { setattr relabelfrom } permission
*/
sepgsql_avc_check_perms(&object,
SEPG_CLASS_DB_COLUMN,
SEPG_DB_COLUMN__SETATTR |
SEPG_DB_COLUMN__RELABELFROM,
audit_name,
true );
/*
* check db_column : { relabelto } permission
*/
sepgsql_avc_check_perms_label(seclabel,
SEPG_CLASS_DB_COLUMN,
SEPG_DB_PROCEDURE__RELABELTO,
audit_name,
true );
pfree(audit_name);
}
/*
* sepgsql_attribute_setattr
*
* It checks privileges to alter the supplied column .
*/
void
sepgsql_attribute_setattr(Oid relOid, AttrNumber attnum)
{
ObjectAddress object;
char *audit_name;
char relkind = get_rel_relkind(relOid);
if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
return ;
/*
* check db_column : { setattr } permission
*/
object.classId = RelationRelationId;
object.objectId = relOid;
object.objectSubId = attnum;
audit_name = getObjectIdentity(&object, false );
sepgsql_avc_check_perms(&object,
SEPG_CLASS_DB_COLUMN,
SEPG_DB_COLUMN__SETATTR,
audit_name,
true );
pfree(audit_name);
}
/*
* sepgsql_relation_post_create
*
* The post creation hook of relation / attribute
*/
void
sepgsql_relation_post_create(Oid relOid)
{
Relation rel;
ScanKeyData skey;
SysScanDesc sscan;
HeapTuple tuple;
Form_pg_class classForm;
ObjectAddress object;
uint16_t tclass;
char *scontext; /* subject */
char *tcontext; /* schema */
char *rcontext; /* relation */
char *ccontext; /* column */
char *nsp_name;
StringInfoData audit_name;
/*
* Fetch catalog record of the new relation . Because pg_class entry is not
* visible right now , we need to scan the catalog using SnapshotSelf .
*/
rel = table_open(RelationRelationId, AccessShareLock);
ScanKeyInit(&skey,
Anum_pg_class_oid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relOid));
sscan = systable_beginscan(rel, ClassOidIndexId, true ,
SnapshotSelf, 1 , &skey);
tuple = systable_getnext(sscan);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "could not find tuple for relation %u" , relOid);
classForm = (Form_pg_class) GETSTRUCT(tuple);
/* ignore indexes on toast tables */
if (classForm->relkind == RELKIND_INDEX &&
classForm->relnamespace == PG_TOAST_NAMESPACE)
goto out;
/*
* check db_schema : { add_name } permission of the namespace
*/
object.classId = NamespaceRelationId;
object.objectId = classForm->relnamespace;
object.objectSubId = 0 ;
sepgsql_avc_check_perms(&object,
SEPG_CLASS_DB_SCHEMA,
SEPG_DB_SCHEMA__ADD_NAME,
getObjectIdentity(&object, false ),
true );
switch (classForm->relkind)
{
case RELKIND_RELATION:
case RELKIND_PARTITIONED_TABLE:
tclass = SEPG_CLASS_DB_TABLE;
break ;
case RELKIND_SEQUENCE:
tclass = SEPG_CLASS_DB_SEQUENCE;
break ;
case RELKIND_VIEW:
tclass = SEPG_CLASS_DB_VIEW;
break ;
case RELKIND_INDEX:
/* deal with indexes specially; no need for tclass */
sepgsql_index_modify(relOid);
goto out;
default :
/* ignore other relkinds */
goto out;
}
/*
* Compute a default security label when we create a new relation object
* under the specified namespace .
*/
scontext = sepgsql_get_client_label();
tcontext = sepgsql_get_label(NamespaceRelationId,
classForm->relnamespace, 0 );
rcontext = sepgsql_compute_create(scontext, tcontext, tclass,
NameStr(classForm->relname));
/*
* check db_xxx : { create } permission
*/
nsp_name = get_namespace_name(classForm->relnamespace);
initStringInfo(&audit_name);
appendStringInfo(&audit_name, "%s.%s" ,
quote_identifier(nsp_name),
quote_identifier(NameStr(classForm->relname)));
sepgsql_avc_check_perms_label(rcontext,
tclass,
SEPG_DB_DATABASE__CREATE,
audit_name.data,
true );
/*
* Assign the default security label on the new regular or partitioned
* relation .
*/
object.classId = RelationRelationId;
object.objectId = relOid;
object.objectSubId = 0 ;
SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, rcontext);
/*
* We also assign a default security label on columns of a new table .
*/
if (classForm->relkind == RELKIND_RELATION ||
classForm->relkind == RELKIND_PARTITIONED_TABLE)
{
Relation arel;
ScanKeyData akey;
SysScanDesc ascan;
HeapTuple atup;
Form_pg_attribute attForm;
arel = table_open(AttributeRelationId, AccessShareLock);
ScanKeyInit(&akey,
Anum_pg_attribute_attrelid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relOid));
ascan = systable_beginscan(arel, AttributeRelidNumIndexId, true ,
SnapshotSelf, 1 , &akey);
while (HeapTupleIsValid(atup = systable_getnext(ascan)))
{
attForm = (Form_pg_attribute) GETSTRUCT(atup);
resetStringInfo(&audit_name);
appendStringInfo(&audit_name, "%s.%s.%s" ,
quote_identifier(nsp_name),
quote_identifier(NameStr(classForm->relname)),
quote_identifier(NameStr(attForm->attname)));
ccontext = sepgsql_compute_create(scontext,
rcontext,
SEPG_CLASS_DB_COLUMN,
NameStr(attForm->attname));
/*
* check db_column : { create } permission
*/
sepgsql_avc_check_perms_label(ccontext,
SEPG_CLASS_DB_COLUMN,
SEPG_DB_COLUMN__CREATE,
audit_name.data,
true );
object.classId = RelationRelationId;
object.objectId = relOid;
object.objectSubId = attForm->attnum;
SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ccontext);
pfree(ccontext);
}
systable_endscan(ascan);
table_close(arel, AccessShareLock);
}
pfree(rcontext);
out:
systable_endscan(sscan);
table_close(rel, AccessShareLock);
}
/*
* sepgsql_relation_drop
*
* It checks privileges to drop the supplied relation .
*/
void
sepgsql_relation_drop(Oid relOid)
{
ObjectAddress object;
char *audit_name;
uint16_t tclass = 0 ;
char relkind = get_rel_relkind(relOid);
switch (relkind)
{
case RELKIND_RELATION:
case RELKIND_PARTITIONED_TABLE:
tclass = SEPG_CLASS_DB_TABLE;
break ;
case RELKIND_SEQUENCE:
tclass = SEPG_CLASS_DB_SEQUENCE;
break ;
case RELKIND_VIEW:
tclass = SEPG_CLASS_DB_VIEW;
break ;
case RELKIND_INDEX:
/* ignore indexes on toast tables */
if (get_rel_namespace(relOid) == PG_TOAST_NAMESPACE)
return ;
/* other indexes are handled specially below; no need for tclass */
break ;
default :
/* ignore other relkinds */
return ;
}
/*
* check db_schema : { remove_name } permission
*/
object.classId = NamespaceRelationId;
object.objectId = get_rel_namespace(relOid);
object.objectSubId = 0 ;
audit_name = getObjectIdentity(&object, false );
sepgsql_avc_check_perms(&object,
SEPG_CLASS_DB_SCHEMA,
SEPG_DB_SCHEMA__REMOVE_NAME,
audit_name,
true );
pfree(audit_name);
/* deal with indexes specially */
if (relkind == RELKIND_INDEX)
{
sepgsql_index_modify(relOid);
return ;
}
/*
* check db_table / sequence / view : { drop } permission
*/
object.classId = RelationRelationId;
object.objectId = relOid;
object.objectSubId = 0 ;
audit_name = getObjectIdentity(&object, false );
sepgsql_avc_check_perms(&object,
tclass,
SEPG_DB_TABLE__DROP,
audit_name,
true );
pfree(audit_name);
/*
* check db_column : { drop } permission
*/
if (relkind == RELKIND_RELATION || relkind == RELKIND_PARTITIONED_TABLE)
{
Form_pg_attribute attForm;
CatCList *attrList;
HeapTuple atttup;
int i;
attrList = SearchSysCacheList1(ATTNUM, ObjectIdGetDatum(relOid));
for (i = 0 ; i < attrList->n_members; i++)
{
atttup = &attrList->members[i]->tuple;
attForm = (Form_pg_attribute) GETSTRUCT(atttup);
if (attForm->attisdropped)
continue ;
object.classId = RelationRelationId;
object.objectId = relOid;
object.objectSubId = attForm->attnum;
audit_name = getObjectIdentity(&object, false );
sepgsql_avc_check_perms(&object,
SEPG_CLASS_DB_COLUMN,
SEPG_DB_COLUMN__DROP,
audit_name,
true );
pfree(audit_name);
}
ReleaseCatCacheList(attrList);
}
}
/*
* sepgsql_relation_truncate
*
* Check privileges to TRUNCATE the supplied relation .
*/
void
sepgsql_relation_truncate(Oid relOid)
{
ObjectAddress object;
char *audit_name;
uint16_t tclass = 0 ;
char relkind = get_rel_relkind(relOid);
switch (relkind)
{
case RELKIND_RELATION:
case RELKIND_PARTITIONED_TABLE:
tclass = SEPG_CLASS_DB_TABLE;
break ;
default :
/* ignore other relkinds */
return ;
}
/*
* check db_table : { truncate } permission
*/
object.classId = RelationRelationId;
object.objectId = relOid;
object.objectSubId = 0 ;
audit_name = getObjectIdentity(&object, false );
sepgsql_avc_check_perms(&object,
tclass,
SEPG_DB_TABLE__TRUNCATE,
audit_name,
true );
pfree(audit_name);
}
/*
* sepgsql_relation_relabel
*
* It checks privileges to relabel the supplied relation by the ` seclabel ' .
*/
void
sepgsql_relation_relabel(Oid relOid, const char *seclabel)
{
ObjectAddress object;
char *audit_name;
char relkind = get_rel_relkind(relOid);
uint16_t tclass = 0 ;
if (relkind == RELKIND_RELATION || relkind == RELKIND_PARTITIONED_TABLE)
tclass = SEPG_CLASS_DB_TABLE;
else if (relkind == RELKIND_SEQUENCE)
tclass = SEPG_CLASS_DB_SEQUENCE;
else if (relkind == RELKIND_VIEW)
tclass = SEPG_CLASS_DB_VIEW;
else
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot set security labels on relations except "
"for tables, sequences or views" )));
object.classId = RelationRelationId;
object.objectId = relOid;
object.objectSubId = 0 ;
audit_name = getObjectIdentity(&object, false );
/*
* check db_xxx : { setattr relabelfrom } permission
*/
sepgsql_avc_check_perms(&object,
tclass,
SEPG_DB_TABLE__SETATTR |
SEPG_DB_TABLE__RELABELFROM,
audit_name,
true );
/*
* check db_xxx : { relabelto } permission
*/
sepgsql_avc_check_perms_label(seclabel,
tclass,
SEPG_DB_TABLE__RELABELTO,
audit_name,
true );
pfree(audit_name);
}
/*
* sepgsql_relation_setattr
*
* It checks privileges to set attribute of the supplied relation
*/
void
sepgsql_relation_setattr(Oid relOid)
{
Relation rel;
ScanKeyData skey;
SysScanDesc sscan;
HeapTuple oldtup;
HeapTuple newtup;
Form_pg_class oldform;
Form_pg_class newform;
ObjectAddress object;
char *audit_name;
uint16_t tclass;
switch (get_rel_relkind(relOid))
{
case RELKIND_RELATION:
case RELKIND_PARTITIONED_TABLE:
tclass = SEPG_CLASS_DB_TABLE;
break ;
case RELKIND_SEQUENCE:
tclass = SEPG_CLASS_DB_SEQUENCE;
break ;
case RELKIND_VIEW:
tclass = SEPG_CLASS_DB_VIEW;
break ;
case RELKIND_INDEX:
/* deal with indexes specially */
sepgsql_index_modify(relOid);
return ;
default :
/* other relkinds don't need additional work */
return ;
}
/*
* Fetch newer catalog
*/
rel = table_open(RelationRelationId, AccessShareLock);
ScanKeyInit(&skey,
Anum_pg_class_oid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relOid));
sscan = systable_beginscan(rel, ClassOidIndexId, true ,
SnapshotSelf, 1 , &skey);
newtup = systable_getnext(sscan);
if (!HeapTupleIsValid(newtup))
elog(ERROR, "could not find tuple for relation %u" , relOid);
newform = (Form_pg_class) GETSTRUCT(newtup);
/*
* Fetch older catalog
*/
oldtup = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
if (!HeapTupleIsValid(oldtup))
elog(ERROR, "cache lookup failed for relation %u" , relOid);
oldform = (Form_pg_class) GETSTRUCT(oldtup);
/*
* Does this ALTER command takes operation to namespace ?
*/
if (newform->relnamespace != oldform->relnamespace)
{
sepgsql_schema_remove_name(oldform->relnamespace);
sepgsql_schema_add_name(newform->relnamespace);
}
if (strcmp(NameStr(newform->relname), NameStr(oldform->relname)) != 0 )
sepgsql_schema_rename(oldform->relnamespace);
/*
* XXX - In the future version , db_tuple : { use } of system catalog entry
* shall be checked , if tablespace configuration is changed .
*/
/*
* check db_xxx : { setattr } permission
*/
object.classId = RelationRelationId;
object.objectId = relOid;
object.objectSubId = 0 ;
audit_name = getObjectIdentity(&object, false );
sepgsql_avc_check_perms(&object,
tclass,
SEPG_DB_TABLE__SETATTR,
audit_name,
true );
pfree(audit_name);
ReleaseSysCache(oldtup);
systable_endscan(sscan);
table_close(rel, AccessShareLock);
}
/*
* sepgsql_relation_setattr_extra
*
* It checks permission of the relation being referenced by extra attributes ,
* such as pg_index entries . Like core PostgreSQL , sepgsql also does not deal
* with such entries as individual " objects " , thus , modification of these
* entries shall be considered as setting an attribute of the underlying
* relation .
*/
static void
sepgsql_relation_setattr_extra(Relation catalog,
Oid catindex_id,
Oid extra_oid,
AttrNumber anum_relation_id,
AttrNumber anum_extra_id)
{
ScanKeyData skey;
SysScanDesc sscan;
HeapTuple tuple;
Datum datum;
bool isnull;
ScanKeyInit(&skey, anum_extra_id,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(extra_oid));
sscan = systable_beginscan(catalog, catindex_id, true ,
SnapshotSelf, 1 , &skey);
tuple = systable_getnext(sscan);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "could not find tuple for object %u in catalog \" %s\"" ,
extra_oid, RelationGetRelationName(catalog));
datum = heap_getattr(tuple, anum_relation_id,
RelationGetDescr(catalog), &isnull);
Assert(!isnull);
sepgsql_relation_setattr(DatumGetObjectId(datum));
systable_endscan(sscan);
}
/*
* sepgsql_index_modify
* Handle index create , update , drop
*
* Unlike other relation kinds , indexes do not have their own security labels ,
* so instead of doing checks directly , treat them as extra attributes of their
* owning tables ; so check ' setattr ' permissions on the table .
*/
static void
sepgsql_index_modify(Oid indexOid)
{
Relation catalog = table_open(IndexRelationId, AccessShareLock);
/* check db_table:{setattr} permission of the table being indexed */
sepgsql_relation_setattr_extra(catalog,
IndexRelidIndexId,
indexOid,
Anum_pg_index_indrelid,
Anum_pg_index_indexrelid);
table_close(catalog, AccessShareLock);
}
Messung V0.5 in Prozent C=94 H=94 G=93
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland