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