mirror of https://github.com/postgres/postgres
This is intended as infrastructure to support integration with label-based mandatory access control systems such as SE-Linux. Further changes (mostly hooks) will be needed, but this is a big chunk of it. KaiGai Kohei and Robert Haaspull/1/head
parent
2ce003973d
commit
4d355a8336
@ -0,0 +1,14 @@ |
||||
# contrib/dummy_seclabel/Makefile
|
||||
|
||||
MODULES = dummy_seclabel
|
||||
|
||||
ifdef USE_PGXS |
||||
PG_CONFIG = pg_config
|
||||
PGXS := $(shell $(PG_CONFIG) --pgxs)
|
||||
include $(PGXS) |
||||
else |
||||
subdir = contrib/dummy_seclabel
|
||||
top_builddir = ../..
|
||||
include $(top_builddir)/src/Makefile.global |
||||
include $(top_srcdir)/contrib/contrib-global.mk |
||||
endif |
@ -0,0 +1,49 @@ |
||||
/*
|
||||
* dummy_seclabel.c |
||||
* |
||||
* Dummy security label provider. |
||||
* |
||||
* This module does not provide anything worthwhile from a security |
||||
* perspective, but allows regression testing independent of platform-specific |
||||
* features like SELinux. |
||||
* |
||||
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group |
||||
* Portions Copyright (c) 1994, Regents of the University of California |
||||
*/ |
||||
#include "postgres.h" |
||||
|
||||
#include "commands/seclabel.h" |
||||
#include "miscadmin.h" |
||||
|
||||
PG_MODULE_MAGIC; |
||||
|
||||
/* Entrypoint of the module */ |
||||
void _PG_init(void); |
||||
|
||||
static void |
||||
dummy_object_relabel(const ObjectAddress *object, const char *seclabel) |
||||
{ |
||||
if (seclabel == NULL || |
||||
strcmp(seclabel, "unclassified") == 0 || |
||||
strcmp(seclabel, "classified") == 0) |
||||
return; |
||||
|
||||
if (strcmp(seclabel, "secret") == 0 || |
||||
strcmp(seclabel, "top secret") == 0) |
||||
{ |
||||
if (!superuser()) |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
||||
errmsg("only superuser can set '%s' label", seclabel))); |
||||
return; |
||||
} |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_INVALID_NAME), |
||||
errmsg("'%s' is not a valid security label", seclabel))); |
||||
} |
||||
|
||||
void |
||||
_PG_init(void) |
||||
{ |
||||
register_label_provider("dummy", dummy_object_relabel); |
||||
} |
@ -0,0 +1,194 @@ |
||||
<!-- |
||||
$PostgreSQL$ |
||||
PostgreSQL documentation |
||||
--> |
||||
|
||||
<refentry id="SQL-SECURITY-LABEL"> |
||||
<refmeta> |
||||
<refentrytitle>SECURITY LABEL</refentrytitle> |
||||
<manvolnum>7</manvolnum> |
||||
<refmiscinfo>SQL - Language Statements</refmiscinfo> |
||||
</refmeta> |
||||
|
||||
<refnamediv> |
||||
<refname>SECURITY LABEL</refname> |
||||
<refpurpose>define or change a security label applied to an object</refpurpose> |
||||
</refnamediv> |
||||
|
||||
<indexterm zone="sql-security-label"> |
||||
<primary>SECURITY LABEL</primary> |
||||
</indexterm> |
||||
|
||||
<refsynopsisdiv> |
||||
<synopsis> |
||||
SECURITY LABEL [ FOR <replaceable class="PARAMETER">provider</replaceable> ] ON |
||||
{ |
||||
TABLE <replaceable class="PARAMETER">object_name</replaceable> | |
||||
COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> | |
||||
AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> (<replaceable class="PARAMETER">agg_type</replaceable> [, ...] ) | |
||||
DOMAIN <replaceable class="PARAMETER">object_name</replaceable> | |
||||
FUNCTION <replaceable class="PARAMETER">function_name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) | |
||||
LARGE OBJECT <replaceable class="PARAMETER">large_object_oid</replaceable> | |
||||
[ PROCEDURAL ] LANGUAGE <replaceable class="PARAMETER">object_name</replaceable> | |
||||
SCHEMA <replaceable class="PARAMETER">object_name</replaceable> | |
||||
SEQUENCE <replaceable class="PARAMETER">object_name</replaceable> | |
||||
TYPE <replaceable class="PARAMETER">object_name</replaceable> | |
||||
VIEW <replaceable class="PARAMETER">object_name</replaceable> |
||||
} IS '<replaceable class="PARAMETER">label</replaceable>' |
||||
</synopsis> |
||||
</refsynopsisdiv> |
||||
|
||||
<refsect1> |
||||
<title>Description</title> |
||||
|
||||
<para> |
||||
<command>SECURITY LABEL</command> applies a security label to a database |
||||
object. An arbitrary number of security labels, one per label provider, can |
||||
be associated with a given database object. Label providers are loadable |
||||
modules which register themselves by using the function |
||||
<function>register_label_provider</>. |
||||
</para> |
||||
|
||||
<note> |
||||
<para> |
||||
<function>register_label_provider</> is not an SQL function; it can |
||||
only be called from C code loaded into the backend. |
||||
</para> |
||||
</note> |
||||
|
||||
<para> |
||||
The label provider determines whether a given a label is valid and whether |
||||
it is permissible to assign that label to a given object. The meaning of a |
||||
given label is likewise at the discretion of the label provider. |
||||
<productname>PostgreSQL</> places no restrictions on whether or how a |
||||
label provider must interpret security labels; it merely provides a |
||||
mechanism for storing them. In practice, this facility is intended to allow |
||||
integration with label-based mandatory access control (MAC) systems such as |
||||
<productname>SE-Linux</>. Such systems make all access control decisions |
||||
based on object labels, rather than traditional discretionary access control |
||||
(DAC) concepts such as users and groups. |
||||
</para> |
||||
</refsect1> |
||||
|
||||
<refsect1> |
||||
<title>Parameters</title> |
||||
|
||||
<variablelist> |
||||
<varlistentry> |
||||
<term><replaceable class="parameter">object_name</replaceable></term> |
||||
<term><replaceable class="parameter">table_name.column_name</replaceable></term> |
||||
<term><replaceable class="parameter">agg_name</replaceable></term> |
||||
<term><replaceable class="parameter">function_name</replaceable></term> |
||||
<listitem> |
||||
<para> |
||||
The name of the object to be commented. Names of tables, |
||||
aggregates, domains, functions, sequences, types, and views can |
||||
be schema-qualified. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
|
||||
<varlistentry> |
||||
<term><replaceable class="parameter">provider</replaceable></term> |
||||
<listitem> |
||||
<para> |
||||
The name of the provider with which this label is to be associated. The |
||||
named provider must be loaded and must consent to the proposed labeling |
||||
operation. If exactly one provider is loaded, the provider name may be |
||||
omitted for brevity. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
|
||||
<varlistentry> |
||||
<term><replaceable class="parameter">argmode</replaceable></term> |
||||
|
||||
<listitem> |
||||
<para> |
||||
The mode of a function argument: <literal>IN</>, <literal>OUT</>, |
||||
<literal>INOUT</>, or <literal>VARIADIC</>. |
||||
If omitted, the default is <literal>IN</>. |
||||
Note that <command>COMMENT ON FUNCTION</command> does not actually pay |
||||
any attention to <literal>OUT</> arguments, since only the input |
||||
arguments are needed to determine the function's identity. |
||||
So it is sufficient to list the <literal>IN</>, <literal>INOUT</>, |
||||
and <literal>VARIADIC</> arguments. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
|
||||
<varlistentry> |
||||
<term><replaceable class="parameter">argname</replaceable></term> |
||||
|
||||
<listitem> |
||||
<para> |
||||
The name of a function argument. |
||||
Note that <command>COMMENT ON FUNCTION</command> does not actually pay |
||||
any attention to argument names, since only the argument data |
||||
types are needed to determine the function's identity. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
|
||||
<varlistentry> |
||||
<term><replaceable class="parameter">argtype</replaceable></term> |
||||
|
||||
<listitem> |
||||
<para> |
||||
The data type(s) of the function's arguments (optionally |
||||
schema-qualified), if any. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
|
||||
<varlistentry> |
||||
<term><replaceable class="parameter">large_object_oid</replaceable></term> |
||||
<listitem> |
||||
<para> |
||||
The OID of the large object. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
|
||||
<varlistentry> |
||||
<term><literal>PROCEDURAL</literal></term> |
||||
|
||||
<listitem> |
||||
<para> |
||||
This is a noise word. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
|
||||
<varlistentry> |
||||
<term><replaceable class="parameter">label</replaceable></term> |
||||
<listitem> |
||||
<para> |
||||
The new security label, written as a string literal; or <literal>NULL</> |
||||
to drop the security label. |
||||
</para> |
||||
</listitem> |
||||
</varlistentry> |
||||
</variablelist> |
||||
</refsect1> |
||||
|
||||
<refsect1> |
||||
<title>Examples</title> |
||||
|
||||
<para> |
||||
The following example shows how the security label of a table might |
||||
be changed. |
||||
|
||||
<programlisting> |
||||
SECURITY LABEL FOR selinux ON TABLE mytable IS 'system_u:object_r:sepgsql_table_t:s0'; |
||||
</programlisting> |
||||
</para> |
||||
</refsect1> |
||||
|
||||
<refsect1> |
||||
<title>Compatibility</title> |
||||
<para> |
||||
There is no <command>SECURITY LABEL</command> command in the SQL standard. |
||||
</para> |
||||
</refsect1> |
||||
</refentry> |
@ -0,0 +1,387 @@ |
||||
/* -------------------------------------------------------------------------
|
||||
* |
||||
* seclabel.c |
||||
* routines to support security label feature. |
||||
* |
||||
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group |
||||
* Portions Copyright (c) 1994, Regents of the University of California |
||||
* |
||||
* ------------------------------------------------------------------------- |
||||
*/ |
||||
#include "postgres.h" |
||||
|
||||
#include "access/genam.h" |
||||
#include "access/heapam.h" |
||||
#include "catalog/catalog.h" |
||||
#include "catalog/indexing.h" |
||||
#include "catalog/namespace.h" |
||||
#include "catalog/pg_seclabel.h" |
||||
#include "commands/seclabel.h" |
||||
#include "miscadmin.h" |
||||
#include "utils/acl.h" |
||||
#include "utils/builtins.h" |
||||
#include "utils/fmgroids.h" |
||||
#include "utils/lsyscache.h" |
||||
#include "utils/memutils.h" |
||||
#include "utils/tqual.h" |
||||
|
||||
/*
|
||||
* For most object types, the permissions-checking logic is simple enough |
||||
* that it makes sense to just include it in CommentObject(). However, |
||||
* attributes require a bit more checking. |
||||
*/ |
||||
static void CheckAttributeSecLabel(Relation relation); |
||||
|
||||
typedef struct |
||||
{ |
||||
const char *provider_name; |
||||
check_object_relabel_type hook; |
||||
} LabelProvider; |
||||
|
||||
static List *label_provider_list = NIL; |
||||
|
||||
/*
|
||||
* ExecSecLabelStmt -- |
||||
* |
||||
* Apply a security label to a database object. |
||||
*/ |
||||
void |
||||
ExecSecLabelStmt(SecLabelStmt *stmt) |
||||
{ |
||||
LabelProvider *provider = NULL; |
||||
ObjectAddress address; |
||||
Relation relation; |
||||
ListCell *lc; |
||||
|
||||
/*
|
||||
* Find the named label provider, or if none specified, check whether |
||||
* there's exactly one, and if so use it. |
||||
*/ |
||||
if (stmt->provider == NULL) |
||||
{ |
||||
if (label_provider_list == NIL) |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
||||
errmsg("security label providers have been loaded"))); |
||||
if (lnext(list_head(label_provider_list)) != NULL) |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
||||
errmsg("must specify provider when multiple security label providers have been loaded"))); |
||||
provider = (LabelProvider *) linitial(label_provider_list); |
||||
} |
||||
else |
||||
{ |
||||
foreach (lc, label_provider_list) |
||||
{ |
||||
LabelProvider *lp = lfirst(lc); |
||||
|
||||
if (strcmp(stmt->provider, lp->provider_name) == 0) |
||||
{ |
||||
provider = lp; |
||||
break; |
||||
} |
||||
} |
||||
if (provider == NULL) |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
||||
errmsg("security label provider \"%s\" is not loaded", |
||||
stmt->provider))); |
||||
} |
||||
|
||||
/*
|
||||
* Translate the parser representation which identifies this object |
||||
* into an ObjectAddress. get_object_address() will throw an error if |
||||
* the object does not exist, and will also acquire a lock on the |
||||
* target to guard against concurrent modifications. |
||||
*/ |
||||
address = get_object_address(stmt->objtype, stmt->objname, stmt->objargs, |
||||
&relation, ShareUpdateExclusiveLock); |
||||
|
||||
/* Privilege and integrity checks. */ |
||||
switch (stmt->objtype) |
||||
{ |
||||
case OBJECT_SEQUENCE: |
||||
case OBJECT_TABLE: |
||||
case OBJECT_VIEW: |
||||
if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) |
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, |
||||
RelationGetRelationName(relation)); |
||||
break; |
||||
case OBJECT_COLUMN: |
||||
CheckAttributeSecLabel(relation); |
||||
break; |
||||
case OBJECT_TYPE: |
||||
if (!pg_type_ownercheck(address.objectId, GetUserId())) |
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, |
||||
format_type_be(address.objectId)); |
||||
break; |
||||
case OBJECT_AGGREGATE: |
||||
case OBJECT_FUNCTION: |
||||
if (!pg_proc_ownercheck(address.objectId, GetUserId())) |
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, |
||||
NameListToString(stmt->objname)); |
||||
break; |
||||
case OBJECT_SCHEMA: |
||||
if (!pg_namespace_ownercheck(address.objectId, GetUserId())) |
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, |
||||
strVal(linitial(stmt->objname))); |
||||
break; |
||||
case OBJECT_LANGUAGE: |
||||
if (!superuser()) |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
||||
errmsg("must be superuser to comment on procedural language"))); |
||||
break; |
||||
case OBJECT_LARGEOBJECT: |
||||
if (!pg_largeobject_ownercheck(address.objectId, GetUserId())) |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
||||
errmsg("must be owner of large object %u", |
||||
address.objectId))); |
||||
break; |
||||
default: |
||||
elog(ERROR, "unrecognized object type: %d", |
||||
(int) stmt->objtype); |
||||
} |
||||
|
||||
/* Provider gets control here, may throw ERROR to veto new label. */ |
||||
(*provider->hook)(&address, stmt->label); |
||||
|
||||
/* Apply new label. */ |
||||
SetSecurityLabel(&address, provider->provider_name, stmt->label); |
||||
|
||||
/*
|
||||
* If get_object_address() opened the relation for us, we close it to keep |
||||
* the reference count correct - but we retain any locks acquired by |
||||
* get_object_address() until commit time, to guard against concurrent |
||||
* activity. |
||||
*/ |
||||
if (relation != NULL) |
||||
relation_close(relation, NoLock); |
||||
} |
||||
|
||||
/*
|
||||
* GetSecurityLabel returns the security label for a database object for a |
||||
* given provider, or NULL if there is no such label. |
||||
*/ |
||||
char * |
||||
GetSecurityLabel(const ObjectAddress *object, const char *provider) |
||||
{ |
||||
Relation pg_seclabel; |
||||
ScanKeyData keys[4]; |
||||
SysScanDesc scan; |
||||
HeapTuple tuple; |
||||
Datum datum; |
||||
bool isnull; |
||||
char *seclabel = NULL; |
||||
|
||||
Assert(!IsSharedRelation(object->classId)); |
||||
|
||||
ScanKeyInit(&keys[0], |
||||
Anum_pg_seclabel_objoid, |
||||
BTEqualStrategyNumber, F_OIDEQ, |
||||
ObjectIdGetDatum(object->objectId)); |
||||
ScanKeyInit(&keys[1], |
||||
Anum_pg_seclabel_classoid, |
||||
BTEqualStrategyNumber, F_OIDEQ, |
||||
ObjectIdGetDatum(object->classId)); |
||||
ScanKeyInit(&keys[2], |
||||
Anum_pg_seclabel_objsubid, |
||||
BTEqualStrategyNumber, F_INT4EQ, |
||||
Int32GetDatum(object->objectSubId)); |
||||
ScanKeyInit(&keys[3], |
||||
Anum_pg_seclabel_provider, |
||||
BTEqualStrategyNumber, F_TEXTEQ, |
||||
CStringGetTextDatum(provider)); |
||||
|
||||
pg_seclabel = heap_open(SecLabelRelationId, AccessShareLock); |
||||
|
||||
scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true, |
||||
SnapshotNow, 4, keys); |
||||
|
||||
tuple = systable_getnext(scan); |
||||
if (HeapTupleIsValid(tuple)) |
||||
{ |
||||
datum = heap_getattr(tuple, Anum_pg_seclabel_label, |
||||
RelationGetDescr(pg_seclabel), &isnull); |
||||
if (!isnull) |
||||
seclabel = TextDatumGetCString(datum); |
||||
} |
||||
systable_endscan(scan); |
||||
|
||||
heap_close(pg_seclabel, AccessShareLock); |
||||
|
||||
return seclabel; |
||||
} |
||||
|
||||
/*
|
||||
* SetSecurityLabel attempts to set the security label for the specified |
||||
* provider on the specified object to the given value. NULL means that any |
||||
* any existing label should be deleted. |
||||
*/ |
||||
void |
||||
SetSecurityLabel(const ObjectAddress *object, |
||||
const char *provider, const char *label) |
||||
{ |
||||
Relation pg_seclabel; |
||||
ScanKeyData keys[4]; |
||||
SysScanDesc scan; |
||||
HeapTuple oldtup; |
||||
HeapTuple newtup = NULL; |
||||
Datum values[Natts_pg_seclabel]; |
||||
bool nulls[Natts_pg_seclabel]; |
||||
bool replaces[Natts_pg_seclabel]; |
||||
|
||||
/* Security labels on shared objects are not supported. */ |
||||
Assert(!IsSharedRelation(object->classId)); |
||||
|
||||
/* Prepare to form or update a tuple, if necessary. */ |
||||
memset(nulls, false, sizeof(nulls)); |
||||
memset(replaces, false, sizeof(replaces)); |
||||
values[Anum_pg_seclabel_objoid - 1] = ObjectIdGetDatum(object->objectId); |
||||
values[Anum_pg_seclabel_classoid - 1] = ObjectIdGetDatum(object->classId); |
||||
values[Anum_pg_seclabel_objsubid - 1] = Int32GetDatum(object->objectSubId); |
||||
values[Anum_pg_seclabel_provider - 1] = CStringGetTextDatum(provider); |
||||
if (label != NULL) |
||||
values[Anum_pg_seclabel_label - 1] = CStringGetTextDatum(label); |
||||
|
||||
/* Use the index to search for a matching old tuple */ |
||||
ScanKeyInit(&keys[0], |
||||
Anum_pg_seclabel_objoid, |
||||
BTEqualStrategyNumber, F_OIDEQ, |
||||
ObjectIdGetDatum(object->objectId)); |
||||
ScanKeyInit(&keys[1], |
||||
Anum_pg_seclabel_classoid, |
||||
BTEqualStrategyNumber, F_OIDEQ, |
||||
ObjectIdGetDatum(object->classId)); |
||||
ScanKeyInit(&keys[2], |
||||
Anum_pg_seclabel_objsubid, |
||||
BTEqualStrategyNumber, F_INT4EQ, |
||||
Int32GetDatum(object->objectSubId)); |
||||
ScanKeyInit(&keys[3], |
||||
Anum_pg_seclabel_provider, |
||||
BTEqualStrategyNumber, F_TEXTEQ, |
||||
CStringGetTextDatum(provider)); |
||||
|
||||
pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock); |
||||
|
||||
scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true, |
||||
SnapshotNow, 4, keys); |
||||
|
||||
oldtup = systable_getnext(scan); |
||||
if (HeapTupleIsValid(oldtup)) |
||||
{ |
||||
if (label == NULL) |
||||
simple_heap_delete(pg_seclabel, &oldtup->t_self); |
||||
else |
||||
{ |
||||
replaces[Anum_pg_seclabel_label - 1] = true; |
||||
newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_seclabel), |
||||
values, nulls, replaces); |
||||
simple_heap_update(pg_seclabel, &oldtup->t_self, newtup); |
||||
} |
||||
} |
||||
systable_endscan(scan); |
||||
|
||||
/* If we didn't find an old tuple, insert a new one */ |
||||
if (newtup == NULL && label != NULL) |
||||
{ |
||||
newtup = heap_form_tuple(RelationGetDescr(pg_seclabel), |
||||
values, nulls); |
||||
simple_heap_insert(pg_seclabel, newtup); |
||||
} |
||||
|
||||
/* Update indexes, if necessary */ |
||||
if (newtup != NULL) |
||||
{ |
||||
CatalogUpdateIndexes(pg_seclabel, newtup); |
||||
heap_freetuple(newtup); |
||||
} |
||||
|
||||
heap_close(pg_seclabel, RowExclusiveLock); |
||||
} |
||||
|
||||
/*
|
||||
* DeleteSecurityLabel removes all security labels for an object (and any |
||||
* sub-objects, if applicable). |
||||
*/ |
||||
void |
||||
DeleteSecurityLabel(const ObjectAddress *object) |
||||
{ |
||||
Relation pg_seclabel; |
||||
ScanKeyData skey[3]; |
||||
SysScanDesc scan; |
||||
HeapTuple oldtup; |
||||
int nkeys; |
||||
|
||||
/* Security labels on shared objects are not supported. */ |
||||
if (IsSharedRelation(object->classId)) |
||||
return; |
||||
|
||||
ScanKeyInit(&skey[0], |
||||
Anum_pg_seclabel_objoid, |
||||
BTEqualStrategyNumber, F_OIDEQ, |
||||
ObjectIdGetDatum(object->objectId)); |
||||
ScanKeyInit(&skey[1], |
||||
Anum_pg_seclabel_classoid, |
||||
BTEqualStrategyNumber, F_OIDEQ, |
||||
ObjectIdGetDatum(object->classId)); |
||||
if (object->objectSubId != 0) |
||||
{ |
||||
ScanKeyInit(&skey[2], |
||||
Anum_pg_seclabel_objsubid, |
||||
BTEqualStrategyNumber, F_INT4EQ, |
||||
Int32GetDatum(object->objectSubId)); |
||||
nkeys = 3; |
||||
} |
||||
else |
||||
nkeys = 2; |
||||
|
||||
pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock); |
||||
|
||||
scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true, |
||||
SnapshotNow, nkeys, skey); |
||||
while (HeapTupleIsValid(oldtup = systable_getnext(scan))) |
||||
simple_heap_delete(pg_seclabel, &oldtup->t_self); |
||||
systable_endscan(scan); |
||||
|
||||
heap_close(pg_seclabel, RowExclusiveLock); |
||||
} |
||||
|
||||
/*
|
||||
* Check whether the user is allowed to comment on an attribute of the |
||||
* specified relation. |
||||
*/ |
||||
static void |
||||
CheckAttributeSecLabel(Relation relation) |
||||
{ |
||||
if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) |
||||
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, |
||||
RelationGetRelationName(relation)); |
||||
|
||||
/*
|
||||
* Allow security labels only on columns of tables, views, and composite |
||||
* types (which are the only relkinds for which pg_dump will dump labels). |
||||
*/ |
||||
if (relation->rd_rel->relkind != RELKIND_RELATION && |
||||
relation->rd_rel->relkind != RELKIND_VIEW && |
||||
relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE) |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_WRONG_OBJECT_TYPE), |
||||
errmsg("\"%s\" is not a table, view, or composite type", |
||||
RelationGetRelationName(relation)))); |
||||
} |
||||
|
||||
void |
||||
register_label_provider(const char *provider_name, check_object_relabel_type hook) |
||||
{ |
||||
LabelProvider *provider; |
||||
MemoryContext oldcxt; |
||||
|
||||
oldcxt = MemoryContextSwitchTo(TopMemoryContext); |
||||
provider = palloc(sizeof(LabelProvider)); |
||||
provider->provider_name = pstrdup(provider_name); |
||||
provider->hook = hook; |
||||
label_provider_list = lappend(label_provider_list, provider); |
||||
MemoryContextSwitchTo(oldcxt); |
||||
} |
@ -0,0 +1,43 @@ |
||||
/* -------------------------------------------------------------------------
|
||||
* |
||||
* pg_seclabel.h |
||||
* definition of the system "security label" relation (pg_seclabel) |
||||
* |
||||
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group |
||||
* Portions Copyright (c) 1994, Regents of the University of California |
||||
* |
||||
* ------------------------------------------------------------------------- |
||||
*/ |
||||
#ifndef PG_SECLABEL_H |
||||
#define PG_SECLABEL_H |
||||
|
||||
#include "catalog/genbki.h" |
||||
|
||||
/* ----------------
|
||||
* pg_seclabel definition. cpp turns this into |
||||
* typedef struct FormData_pg_seclabel |
||||
* ---------------- |
||||
*/ |
||||
#define SecLabelRelationId 3037 |
||||
|
||||
CATALOG(pg_seclabel,3037) BKI_WITHOUT_OIDS |
||||
{ |
||||
Oid objoid; /* OID of the object itself */ |
||||
Oid classoid; /* OID of table containing the object */ |
||||
int4 objsubid; /* column number, or 0 if not used */ |
||||
text provider; /* name of label provider */ |
||||
text label; /* security label of the object */ |
||||
} FormData_pg_seclabel; |
||||
|
||||
/* ----------------
|
||||
* compiler constants for pg_seclabel |
||||
* ---------------- |
||||
*/ |
||||
#define Natts_pg_seclabel 5 |
||||
#define Anum_pg_seclabel_objoid 1 |
||||
#define Anum_pg_seclabel_classoid 2 |
||||
#define Anum_pg_seclabel_objsubid 3 |
||||
#define Anum_pg_seclabel_provider 4 |
||||
#define Anum_pg_seclabel_label 5 |
||||
|
||||
#endif /* PG_SECLABEL_H */ |
@ -0,0 +1,35 @@ |
||||
/*
|
||||
* seclabel.h |
||||
* |
||||
* Prototypes for functions in commands/seclabel.c |
||||
* |
||||
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group |
||||
* Portions Copyright (c) 1994, Regents of the University of California |
||||
*/ |
||||
#ifndef SECLABEL_H |
||||
#define SECLABEL_H |
||||
|
||||
#include "catalog/objectaddress.h" |
||||
#include "nodes/primnodes.h" |
||||
#include "nodes/parsenodes.h" |
||||
|
||||
/*
|
||||
* Internal APIs |
||||
*/ |
||||
extern char *GetSecurityLabel(const ObjectAddress *object, |
||||
const char *provider); |
||||
extern void SetSecurityLabel(const ObjectAddress *object, |
||||
const char *provider, const char *label); |
||||
extern void DeleteSecurityLabel(const ObjectAddress *object); |
||||
|
||||
/*
|
||||
* Statement and ESP hook support |
||||
*/ |
||||
extern void ExecSecLabelStmt(SecLabelStmt *stmt); |
||||
|
||||
typedef void (*check_object_relabel_type)(const ObjectAddress *object, |
||||
const char *seclabel); |
||||
extern void register_label_provider(const char *provider, |
||||
check_object_relabel_type hook); |
||||
|
||||
#endif /* SECLABEL_H */ |
@ -0,0 +1,84 @@ |
||||
-- |
||||
-- Test for facilities of security label |
||||
-- |
||||
|
||||
-- initial setups |
||||
SET client_min_messages TO 'warning'; |
||||
|
||||
DROP ROLE IF EXISTS seclabel_user1; |
||||
DROP ROLE IF EXISTS seclabel_user2; |
||||
|
||||
DROP TABLE IF EXISTS seclabel_tbl1; |
||||
DROP TABLE IF EXISTS seclabel_tbl2; |
||||
DROP TABLE IF EXISTS seclabel_tbl3; |
||||
|
||||
CREATE USER seclabel_user1; |
||||
CREATE USER seclabel_user2; |
||||
|
||||
CREATE TABLE seclabel_tbl1 (a int, b text); |
||||
CREATE TABLE seclabel_tbl2 (x int, y text); |
||||
CREATE VIEW seclabel_view1 AS SELECT * FROM seclabel_tbl2; |
||||
CREATE FUNCTION seclabel_four() RETURNS integer AS $$SELECT 4$$ language sql; |
||||
CREATE DOMAIN seclabel_domain AS text; |
||||
|
||||
ALTER TABLE seclabel_tbl1 OWNER TO seclabel_user1; |
||||
ALTER TABLE seclabel_tbl2 OWNER TO seclabel_user2; |
||||
|
||||
RESET client_min_messages; |
||||
|
||||
-- |
||||
-- Test of SECURITY LABEL statement without a plugin |
||||
-- |
||||
SECURITY LABEL ON TABLE seclabel_tbl1 IS 'classified'; -- fail |
||||
SECURITY LABEL FOR 'dummy' ON TABLE seclabel_tbl1 IS 'classified'; -- fail |
||||
SECURITY LABEL ON TABLE seclabel_tbl1 IS '...invalid label...'; -- fail |
||||
SECURITY LABEL ON TABLE seclabel_tbl3 IS 'unclassified'; -- fail |
||||
|
||||
-- Load dummy external security provider |
||||
LOAD '@libdir@/dummy_seclabel@DLSUFFIX@'; |
||||
|
||||
-- |
||||
-- Test of SECURITY LABEL statement with a plugin |
||||
-- |
||||
SET SESSION AUTHORIZATION seclabel_user1; |
||||
|
||||
SECURITY LABEL ON TABLE seclabel_tbl1 IS 'classified'; -- OK |
||||
SECURITY LABEL ON COLUMN seclabel_tbl1.a IS 'unclassified'; -- OK |
||||
SECURITY LABEL ON TABLE seclabel_tbl1 IS '...invalid label...'; -- fail |
||||
SECURITY LABEL FOR 'dummy' ON TABLE seclabel_tbl1 IS 'unclassified'; -- OK |
||||
SECURITY LABEL FOR 'unknown_seclabel' ON TABLE seclabel_tbl1 IS 'classified'; -- fail |
||||
SECURITY LABEL ON TABLE seclabel_tbl2 IS 'unclassified'; -- fail (not owner) |
||||
SECURITY LABEL ON TABLE seclabel_tbl1 IS 'secret'; -- fail (not superuser) |
||||
SECURITY LABEL ON TABLE seclabel_tbl3 IS 'unclassified'; -- fail (not found) |
||||
|
||||
SET SESSION AUTHORIZATION seclabel_user2; |
||||
SECURITY LABEL ON TABLE seclabel_tbl1 IS 'unclassified'; -- fail |
||||
SECURITY LABEL ON TABLE seclabel_tbl2 IS 'classified'; -- OK |
||||
|
||||
RESET SESSION AUTHORIZATION; |
||||
|
||||
SECURITY LABEL ON TABLE seclabel_tbl1 IS 'top secret'; -- OK |
||||
SECURITY LABEL ON VIEW seclabel_view1 IS 'classified'; -- OK |
||||
SECURITY LABEL ON FUNCTION seclabel_four() IS 'classified'; -- OK |
||||
SECURITY LABEL ON DOMAIN seclabel_domain IS 'classified'; -- OK |
||||
SECURITY LABEL ON LANGUAGE plpgsql IS 'unclassified'; -- OK |
||||
SECURITY LABEL ON SCHEMA public IS 'unclassified'; -- OK |
||||
|
||||
SELECT objtype, objname, provider, label FROM pg_seclabels |
||||
ORDER BY objtype, objname; |
||||
|
||||
SECURITY LABEL ON LANGUAGE plpgsql IS NULL; -- OK |
||||
SECURITY LABEL ON SCHEMA public IS NULL; -- OK |
||||
|
||||
-- clean up objects |
||||
DROP FUNCTION seclabel_four(); |
||||
DROP DOMAIN seclabel_domain; |
||||
DROP VIEW seclabel_view1; |
||||
DROP TABLE seclabel_tbl1; |
||||
DROP TABLE seclabel_tbl2; |
||||
DROP USER seclabel_user1; |
||||
DROP USER seclabel_user2; |
||||
|
||||
-- make sure we don't have any leftovers |
||||
SELECT objtype, objname, provider, label FROM pg_seclabels |
||||
ORDER BY objtype, objname; |
@ -0,0 +1,92 @@ |
||||
-- |
||||
-- Test for facilities of security label |
||||
-- |
||||
-- initial setups |
||||
SET client_min_messages TO 'warning'; |
||||
DROP ROLE IF EXISTS seclabel_user1; |
||||
DROP ROLE IF EXISTS seclabel_user2; |
||||
DROP TABLE IF EXISTS seclabel_tbl1; |
||||
DROP TABLE IF EXISTS seclabel_tbl2; |
||||
DROP TABLE IF EXISTS seclabel_tbl3; |
||||
CREATE USER seclabel_user1; |
||||
CREATE USER seclabel_user2; |
||||
CREATE TABLE seclabel_tbl1 (a int, b text); |
||||
CREATE TABLE seclabel_tbl2 (x int, y text); |
||||
CREATE VIEW seclabel_view1 AS SELECT * FROM seclabel_tbl2; |
||||
CREATE FUNCTION seclabel_four() RETURNS integer AS $$SELECT 4$$ language sql; |
||||
CREATE DOMAIN seclabel_domain AS text; |
||||
ALTER TABLE seclabel_tbl1 OWNER TO seclabel_user1; |
||||
ALTER TABLE seclabel_tbl2 OWNER TO seclabel_user2; |
||||
RESET client_min_messages; |
||||
-- |
||||
-- Test of SECURITY LABEL statement without a plugin |
||||
-- |
||||
SECURITY LABEL ON TABLE seclabel_tbl1 IS 'classified'; -- fail |
||||
ERROR: security label providers have been loaded |
||||
SECURITY LABEL FOR 'dummy' ON TABLE seclabel_tbl1 IS 'classified'; -- fail |
||||
ERROR: security label provider "dummy" is not loaded |
||||
SECURITY LABEL ON TABLE seclabel_tbl1 IS '...invalid label...'; -- fail |
||||
ERROR: security label providers have been loaded |
||||
SECURITY LABEL ON TABLE seclabel_tbl3 IS 'unclassified'; -- fail |
||||
ERROR: security label providers have been loaded |
||||
-- Load dummy external security provider |
||||
LOAD '@abs_builddir@/dummy_seclabel@DLSUFFIX@'; |
||||
-- |
||||
-- Test of SECURITY LABEL statement with a plugin |
||||
-- |
||||
SET SESSION AUTHORIZATION seclabel_user1; |
||||
SECURITY LABEL ON TABLE seclabel_tbl1 IS 'classified'; -- OK |
||||
SECURITY LABEL ON COLUMN seclabel_tbl1.a IS 'unclassified'; -- OK |
||||
SECURITY LABEL ON TABLE seclabel_tbl1 IS '...invalid label...'; -- fail |
||||
ERROR: '...invalid label...' is not a valid security label |
||||
SECURITY LABEL FOR 'dummy' ON TABLE seclabel_tbl1 IS 'unclassified'; -- OK |
||||
SECURITY LABEL FOR 'unknown_seclabel' ON TABLE seclabel_tbl1 IS 'classified'; -- fail |
||||
ERROR: security label provider "unknown_seclabel" is not loaded |
||||
SECURITY LABEL ON TABLE seclabel_tbl2 IS 'unclassified'; -- fail (not owner) |
||||
ERROR: must be owner of relation seclabel_tbl2 |
||||
SECURITY LABEL ON TABLE seclabel_tbl1 IS 'secret'; -- fail (not superuser) |
||||
ERROR: only superuser can set 'secret' label |
||||
SECURITY LABEL ON TABLE seclabel_tbl3 IS 'unclassified'; -- fail (not found) |
||||
ERROR: relation "seclabel_tbl3" does not exist |
||||
SET SESSION AUTHORIZATION seclabel_user2; |
||||
SECURITY LABEL ON TABLE seclabel_tbl1 IS 'unclassified'; -- fail |
||||
ERROR: must be owner of relation seclabel_tbl1 |
||||
SECURITY LABEL ON TABLE seclabel_tbl2 IS 'classified'; -- OK |
||||
RESET SESSION AUTHORIZATION; |
||||
SECURITY LABEL ON TABLE seclabel_tbl1 IS 'top secret'; -- OK |
||||
SECURITY LABEL ON VIEW seclabel_view1 IS 'classified'; -- OK |
||||
SECURITY LABEL ON FUNCTION seclabel_four() IS 'classified'; -- OK |
||||
SECURITY LABEL ON DOMAIN seclabel_domain IS 'classified'; -- OK |
||||
SECURITY LABEL ON LANGUAGE plpgsql IS 'unclassified'; -- OK |
||||
SECURITY LABEL ON SCHEMA public IS 'unclassified'; -- OK |
||||
SELECT objtype, objname, provider, label FROM pg_seclabels |
||||
ORDER BY objtype, objname; |
||||
objtype | objname | provider | label |
||||
----------+-----------------+----------+-------------- |
||||
column | seclabel_tbl1.a | dummy | unclassified |
||||
domain | seclabel_domain | dummy | classified |
||||
function | seclabel_four() | dummy | classified |
||||
language | plpgsql | dummy | unclassified |
||||
schema | public | dummy | unclassified |
||||
table | seclabel_tbl1 | dummy | top secret |
||||
table | seclabel_tbl2 | dummy | classified |
||||
view | seclabel_view1 | dummy | classified |
||||
(8 rows) |
||||
|
||||
SECURITY LABEL ON LANGUAGE plpgsql IS NULL; -- OK |
||||
SECURITY LABEL ON SCHEMA public IS NULL; -- OK |
||||
-- clean up objects |
||||
DROP FUNCTION seclabel_four(); |
||||
DROP DOMAIN seclabel_domain; |
||||
DROP VIEW seclabel_view1; |
||||
DROP TABLE seclabel_tbl1; |
||||
DROP TABLE seclabel_tbl2; |
||||
DROP USER seclabel_user1; |
||||
DROP USER seclabel_user2; |
||||
-- make sure we don't have any leftovers |
||||
SELECT objtype, objname, provider, label FROM pg_seclabels |
||||
ORDER BY objtype, objname; |
||||
objtype | objname | provider | label |
||||
---------+---------+----------+------- |
||||
(0 rows) |
||||
|
Loading…
Reference in new issue