seclabel-v4.patch
application/octet-stream
Filename: seclabel-v4.patch
Type: application/octet-stream
Part: 0
Message:
Re: security label support, revised
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v4
| File | + | − |
|---|---|---|
| contrib/dummy_seclabel/dummy_seclabel.c | 0 | 0 |
| contrib/dummy_seclabel/Makefile | 0 | 0 |
| doc/src/sgml/catalogs.sgml | 80 | 0 |
| doc/src/sgml/ref/allfiles.sgml | 1 | 0 |
| doc/src/sgml/reference.sgml | 1 | 0 |
| doc/src/sgml/ref/pg_dumpall.sgml | 9 | 0 |
| doc/src/sgml/ref/pg_dump.sgml | 10 | 0 |
| doc/src/sgml/ref/pg_restore.sgml | 10 | 0 |
| doc/src/sgml/ref/security_label.sgml | 194 | 0 |
| src/backend/catalog/dependency.c | 5 | 2 |
| src/backend/catalog/Makefile | 1 | 1 |
| src/backend/commands/Makefile | 1 | 1 |
| src/backend/commands/seclabel.c | 387 | 0 |
| src/backend/nodes/copyfuncs.c | 17 | 0 |
| src/backend/nodes/equalfuncs.c | 15 | 0 |
| src/backend/parser/gram.y | 93 | 3 |
| src/backend/tcop/utility.c | 14 | 0 |
| src/bin/pg_dump/pg_backup_archiver.c | 6 | 0 |
| src/bin/pg_dump/pg_backup.h | 1 | 0 |
| src/bin/pg_dump/pg_dumpall.c | 7 | 0 |
| src/bin/pg_dump/pg_dump.c | 130 | 0 |
| src/bin/pg_dump/pg_restore.c | 6 | 0 |
| src/include/catalog/indexing.h | 3 | 0 |
| src/include/catalog/pg_seclabel.h | 43 | 0 |
| src/include/catalog/toasting.h | 1 | 0 |
| src/include/commands/seclabel.h | 35 | 0 |
| src/include/nodes/nodes.h | 1 | 0 |
| src/include/nodes/parsenodes.h | 14 | 0 |
| src/include/parser/kwlist.h | 1 | 0 |
| src/test/regress/expected/sanity_check.out | 2 | 1 |
diff --git a/contrib/dummy_seclabel/Makefile b/contrib/dummy_seclabel/Makefile
new file mode 100644
index 0000000..316c27f
diff --git a/contrib/dummy_seclabel/dummy_seclabel.c b/contrib/dummy_seclabel/dummy_seclabel.c
new file mode 100644
index 0000000..e46fc45
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index ab11b15..3758b64 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -209,6 +209,11 @@
</row>
<row>
+ <entry><link linkend="catalog-pg-seclabel"><structname>pg_seclabel</structname></link></entry>
+ <entry>security labels on database objects</entry>
+ </row>
+
+ <row>
<entry><link linkend="catalog-pg-shdepend"><structname>pg_shdepend</structname></link></entry>
<entry>dependencies on shared objects</entry>
</row>
@@ -4229,6 +4234,81 @@
</sect1>
+ <sect1 id="catalog-pg-seclabel">
+ <title><structname>pg_seclabel</structname></title>
+
+ <indexterm zone="catalog-pg-seclabel">
+ <primary>pg_seclabel</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_seclabel</structname> stores security
+ labels on database objects. See the
+ <xref linkend="sql-security-label"> statement.
+ </para>
+
+ <table>
+ <title><structname>pg_seclabel</structname> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><structfield>objoid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry>any OID column</entry>
+ <entry>The OID of the object this security label assigned on</entry>
+ </row>
+
+ <row>
+ <entry><structfield>classoid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
+ <entry>The OID of the system catalog this object appears in</entry>
+ </row>
+
+ <row>
+ <entry><structfield>objsubid</structfield></entry>
+ <entry><type>int4</type></entry>
+ <entry></entry>
+ <entry>
+ For a security label on a table column, this is the column number (the
+ <structfield>objoid</> and <structfield>classoid</> refer to
+ the table itself). For all other object types, this column is
+ zero.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>provider</structfield></entry>
+ <entry><type>text</type></entry>
+ <entry></entry>
+ <entry>
+ The label provider associated with this label.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>label</structfield></entry>
+ <entry><type>text</type></entry>
+ <entry></entry>
+ <entry>
+ The security label applied to this object.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
<sect1 id="catalog-pg-shdepend">
<title><structname>pg_shdepend</structname></title>
diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml
index 7b97883..f5d67a2 100644
--- a/doc/src/sgml/ref/allfiles.sgml
+++ b/doc/src/sgml/ref/allfiles.sgml
@@ -132,6 +132,7 @@ Complete list of usable sgml source files in this directory.
<!entity rollbackPrepared system "rollback_prepared.sgml">
<!entity rollbackTo system "rollback_to.sgml">
<!entity savepoint system "savepoint.sgml">
+<!entity securityLabel system "security_label.sgml">
<!entity select system "select.sgml">
<!entity selectInto system "select_into.sgml">
<!entity set system "set.sgml">
diff --git a/doc/src/sgml/ref/pg_dump.sgml b/doc/src/sgml/ref/pg_dump.sgml
index 1b8402e..8242b53 100644
--- a/doc/src/sgml/ref/pg_dump.sgml
+++ b/doc/src/sgml/ref/pg_dump.sgml
@@ -778,6 +778,16 @@ PostgreSQL documentation
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><option>--security-label</option></term>
+ <listitem>
+ <para>
+ With this option, it also outputs security labels of database
+ objects to be dumped, if labeled.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
</refsect1>
diff --git a/doc/src/sgml/ref/pg_dumpall.sgml b/doc/src/sgml/ref/pg_dumpall.sgml
index 14fa109..68dcc35 100644
--- a/doc/src/sgml/ref/pg_dumpall.sgml
+++ b/doc/src/sgml/ref/pg_dumpall.sgml
@@ -493,6 +493,15 @@ PostgreSQL documentation
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--security-label</option></term>
+ <listitem>
+ <para>
+ With this option, it also outputs security labels of database
+ objects to be dumped, if labeled.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</para>
</refsect1>
diff --git a/doc/src/sgml/ref/pg_restore.sgml b/doc/src/sgml/ref/pg_restore.sgml
index 9dc2511..7860696 100644
--- a/doc/src/sgml/ref/pg_restore.sgml
+++ b/doc/src/sgml/ref/pg_restore.sgml
@@ -329,6 +329,16 @@
</varlistentry>
<varlistentry>
+ <term><option>--no-security-label</option></term>
+ <listitem>
+ <para>
+ Do not output commands to restore security labels,
+ even if the archive contains them.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><option>-P <replaceable class="parameter">function-name(argtype [, ...])</replaceable></option></term>
<term><option>--function=<replaceable class="parameter">function-name(argtype [, ...])</replaceable></option></term>
<listitem>
diff --git a/doc/src/sgml/ref/security_label.sgml b/doc/src/sgml/ref/security_label.sgml
new file mode 100644
index 0000000..7fce58b
--- /dev/null
+++ b/doc/src/sgml/ref/security_label.sgml
@@ -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>
diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml
index 052fe0e..463746c 100644
--- a/doc/src/sgml/reference.sgml
+++ b/doc/src/sgml/reference.sgml
@@ -160,6 +160,7 @@
&rollbackPrepared;
&rollbackTo;
&savepoint;
+ &securityLabel;
&select;
&selectInto;
&set;
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index f4a7eb0..6a47f39 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -38,7 +38,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
pg_ts_parser.h pg_ts_template.h \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
- pg_default_acl.h \
+ pg_default_acl.h pg_seclabel.h \
toasting.h indexing.h \
)
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 62598ee..18e07eb 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -57,6 +57,7 @@
#include "commands/defrem.h"
#include "commands/proclang.h"
#include "commands/schemacmds.h"
+#include "commands/seclabel.h"
#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "commands/typecmds.h"
@@ -1004,10 +1005,12 @@ deleteOneObject(const ObjectAddress *object, Relation depRel)
doDeletion(object);
/*
- * Delete any comments associated with this object. (This is a convenient
- * place to do it instead of having every object type know to do it.)
+ * Delete any comments or security labels associated with this object.
+ * (This is a convenient place to do these things, rather than having every
+ * object type know to do it.)
*/
DeleteComments(object->objectId, object->classId, object->objectSubId);
+ DeleteSecurityLabel(object);
/*
* CommandCounterIncrement here to ensure that preceding changes are all
diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile
index 4e9bf43..9d2a732 100644
--- a/src/backend/commands/Makefile
+++ b/src/backend/commands/Makefile
@@ -17,7 +17,7 @@ OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \
dbcommands.o define.o discard.o explain.o foreigncmds.o functioncmds.o \
indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \
portalcmds.o prepare.o proclang.o \
- schemacmds.o sequence.o tablecmds.o tablespace.o trigger.o \
+ schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \
tsearchcmds.o typecmds.o user.o vacuum.o vacuumlazy.o \
variable.o view.o
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
new file mode 100644
index 0000000..417ad88
--- /dev/null
+++ b/src/backend/commands/seclabel.c
@@ -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);
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index deaeb76..e07aa3e 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2607,6 +2607,20 @@ _copyCommentStmt(CommentStmt *from)
return newnode;
}
+static SecLabelStmt *
+_copySecLabelStmt(SecLabelStmt *from)
+{
+ SecLabelStmt *newnode = makeNode(SecLabelStmt);
+
+ COPY_SCALAR_FIELD(objtype);
+ COPY_NODE_FIELD(objname);
+ COPY_NODE_FIELD(objargs);
+ COPY_STRING_FIELD(provider);
+ COPY_STRING_FIELD(label);
+
+ return newnode;
+}
+
static FetchStmt *
_copyFetchStmt(FetchStmt *from)
{
@@ -3958,6 +3972,9 @@ copyObject(void *from)
case T_CommentStmt:
retval = _copyCommentStmt(from);
break;
+ case T_SecLabelStmt:
+ retval = _copySecLabelStmt(from);
+ break;
case T_FetchStmt:
retval = _copyFetchStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 6b6cd99..8d083c8 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1164,6 +1164,18 @@ _equalCommentStmt(CommentStmt *a, CommentStmt *b)
}
static bool
+_equalSecLabelStmt(SecLabelStmt *a, SecLabelStmt *b)
+{
+ COMPARE_SCALAR_FIELD(objtype);
+ COMPARE_NODE_FIELD(objname);
+ COMPARE_NODE_FIELD(objargs);
+ COMPARE_STRING_FIELD(provider);
+ COMPARE_STRING_FIELD(label);
+
+ return true;
+}
+
+static bool
_equalFetchStmt(FetchStmt *a, FetchStmt *b)
{
COMPARE_SCALAR_FIELD(direction);
@@ -2624,6 +2636,9 @@ equal(void *a, void *b)
case T_CommentStmt:
retval = _equalCommentStmt(a, b);
break;
+ case T_SecLabelStmt:
+ retval = _equalSecLabelStmt(a, b);
+ break;
case T_FetchStmt:
retval = _equalFetchStmt(a, b);
break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 22ce353..51fea86 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -204,7 +204,7 @@ static TypeName *TableFuncTypeName(List *columns);
CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt
RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RevokeRoleStmt
RuleActionStmt RuleActionStmtOrEmpty RuleStmt
- SelectStmt TransactionStmt TruncateStmt
+ SecLabelStmt SelectStmt TransactionStmt TruncateStmt
UnlistenStmt UpdateStmt VacuumStmt
VariableResetStmt VariableSetStmt VariableShowStmt
ViewStmt CheckPointStmt CreateConversionStmt
@@ -334,7 +334,7 @@ static TypeName *TableFuncTypeName(List *columns);
%type <boolean> copy_from
%type <ival> opt_column event cursor_options opt_hold opt_set_data
-%type <objtype> reindex_type drop_type comment_type
+%type <objtype> reindex_type drop_type comment_type security_label_type
%type <node> fetch_args limit_clause select_limit_value
offset_clause select_offset_value
@@ -422,6 +422,8 @@ static TypeName *TableFuncTypeName(List *columns);
%type <str> OptTableSpace OptConsTableSpace OptTableSpaceOwner
%type <list> opt_check_option
+%type <str> opt_provider security_label
+
%type <target> xml_attribute_el
%type <list> xml_attribute_list xml_attributes
%type <node> xml_root_version opt_xml_root_standalone
@@ -499,7 +501,7 @@ static TypeName *TableFuncTypeName(List *columns);
KEY
- LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING
+ LABEL LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING
LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP
LOCATION LOCK_P LOGIN_P
@@ -737,6 +739,7 @@ stmt :
| RevokeStmt
| RevokeRoleStmt
| RuleStmt
+ | SecLabelStmt
| SelectStmt
| TransactionStmt
| TruncateStmt
@@ -4327,6 +4330,92 @@ comment_text:
| NULL_P { $$ = NULL; }
;
+
+/*****************************************************************************
+ *
+ * SECURITY LABEL [FOR <provider>] ON <object> IS <label>
+ *
+ * As with COMMENT ON, <object> can refer to various types of database
+ * objects (e.g. TABLE, COLUMN, etc.).
+ *
+ *****************************************************************************/
+
+SecLabelStmt:
+ SECURITY LABEL opt_provider ON security_label_type any_name
+ IS security_label
+ {
+ SecLabelStmt *n = makeNode(SecLabelStmt);
+ n->provider = $3;
+ n->objtype = $5;
+ n->objname = $6;
+ n->objargs = NIL;
+ n->label = $8;
+ $$ = (Node *) n;
+ }
+ | SECURITY LABEL opt_provider ON AGGREGATE func_name aggr_args
+ IS security_label
+ {
+ SecLabelStmt *n = makeNode(SecLabelStmt);
+ n->provider = $3;
+ n->objtype = OBJECT_AGGREGATE;
+ n->objname = $6;
+ n->objargs = $7;
+ n->label = $9;
+ $$ = (Node *) n;
+ }
+ | SECURITY LABEL opt_provider ON FUNCTION func_name func_args
+ IS security_label
+ {
+ SecLabelStmt *n = makeNode(SecLabelStmt);
+ n->provider = $3;
+ n->objtype = OBJECT_FUNCTION;
+ n->objname = $6;
+ n->objargs = extractArgTypes($7);
+ n->label = $9;
+ $$ = (Node *) n;
+ }
+ | SECURITY LABEL opt_provider ON LARGE_P OBJECT_P NumericOnly
+ IS security_label
+ {
+ SecLabelStmt *n = makeNode(SecLabelStmt);
+ n->provider = $3;
+ n->objtype = OBJECT_LARGEOBJECT;
+ n->objname = list_make1($7);
+ n->objargs = NIL;
+ n->label = $9;
+ $$ = (Node *) n;
+ }
+ | SECURITY LABEL opt_provider ON opt_procedural LANGUAGE any_name
+ IS security_label
+ {
+ SecLabelStmt *n = makeNode(SecLabelStmt);
+ n->provider = $3;
+ n->objtype = OBJECT_LANGUAGE;
+ n->objname = $7;
+ n->objargs = NIL;
+ n->label = $9;
+ $$ = (Node *) n;
+ }
+ ;
+
+opt_provider: FOR ColId_or_Sconst { $$ = $2; }
+ | /* empty */ { $$ = NULL; }
+ ;
+
+security_label_type:
+ COLUMN { $$ = OBJECT_COLUMN; }
+ | SCHEMA { $$ = OBJECT_SCHEMA; }
+ | SEQUENCE { $$ = OBJECT_SEQUENCE; }
+ | TABLE { $$ = OBJECT_TABLE; }
+ | DOMAIN_P { $$ = OBJECT_TYPE; }
+ | TYPE_P { $$ = OBJECT_TYPE; }
+ | VIEW { $$ = OBJECT_VIEW; }
+ ;
+
+security_label: Sconst { $$ = $1; }
+ | NULL_P { $$ = NULL; }
+ ;
+
/*****************************************************************************
*
* QUERY:
@@ -10993,6 +11082,7 @@ unreserved_keyword:
| INVOKER
| ISOLATION
| KEY
+ | LABEL
| LANGUAGE
| LARGE_P
| LAST_P
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 1865f84..3636173 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -37,6 +37,7 @@
#include "commands/prepare.h"
#include "commands/proclang.h"
#include "commands/schemacmds.h"
+#include "commands/seclabel.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
@@ -218,6 +219,7 @@ check_xact_readonly(Node *parsetree)
case T_AlterUserMappingStmt:
case T_DropUserMappingStmt:
case T_AlterTableSpaceOptionsStmt:
+ case T_SecLabelStmt:
PreventCommandIfReadOnly(CreateCommandTag(parsetree));
break;
default:
@@ -663,6 +665,10 @@ standard_ProcessUtility(Node *parsetree,
CommentObject((CommentStmt *) parsetree);
break;
+ case T_SecLabelStmt:
+ ExecSecLabelStmt((SecLabelStmt *) parsetree);
+ break;
+
case T_CopyStmt:
{
uint64 processed;
@@ -1592,6 +1598,10 @@ CreateCommandTag(Node *parsetree)
tag = "COMMENT";
break;
+ case T_SecLabelStmt:
+ tag = "SECURITY LABEL";
+ break;
+
case T_CopyStmt:
tag = "COPY";
break;
@@ -2314,6 +2324,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_DDL;
break;
+ case T_SecLabelStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_CopyStmt:
if (((CopyStmt *) parsetree)->is_from)
lev = LOGSTMT_MOD;
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index 6f0277e..8fa9a57 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -103,6 +103,7 @@ typedef struct _restoreOptions
* restore */
int use_setsessauth;/* Use SET SESSION AUTHORIZATION commands
* instead of OWNER TO */
+ int skip_seclabel; /* Skip security label entries */
char *superuser; /* Username to use as superuser */
char *use_role; /* Issue SET ROLE to this */
int dataOnly;
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index a73afec..d1a9c54 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -2275,6 +2275,10 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
if ((!include_acls || ropt->aclsSkip) && _tocEntryIsACL(te))
return 0;
+ /* If it's security labels, maybe ignore it */
+ if (ropt->skip_seclabel && strcmp(te->desc, "SECURITY LABEL") == 0)
+ return 0;
+
/* Ignore DATABASE entry unless we should create it */
if (!ropt->createDB && strcmp(te->desc, "DATABASE") == 0)
return 0;
@@ -2341,6 +2345,8 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
(strcmp(te->desc, "ACL") == 0 &&
strncmp(te->tag, "LARGE OBJECT ", 13) == 0) ||
(strcmp(te->desc, "COMMENT") == 0 &&
+ strncmp(te->tag, "LARGE OBJECT ", 13) == 0) ||
+ (strcmp(te->desc, "SECURITY LABEL") == 0 &&
strncmp(te->tag, "LARGE OBJECT ", 13) == 0))
res = res & REQ_DATA;
else
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index fdf48fe..7bacfb1 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -125,6 +125,7 @@ static int binary_upgrade = 0;
static int disable_dollar_quoting = 0;
static int dump_inserts = 0;
static int column_inserts = 0;
+static int security_label = 0;
static void help(const char *progname);
@@ -183,6 +184,10 @@ static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
const char *tag, const char *nspname, const char *owner,
const char *acls);
+static void dumpSecLabel(Archive *fout, CatalogId objCatId, DumpId objDumpId,
+ const char *type, const char *target, const char *nspname,
+ const char *owner);
+
static void getDependencies(void);
static void getDomainConstraints(TypeInfo *tyinfo);
static void getTableData(TableInfo *tblinfo, int numTables, bool oids);
@@ -300,6 +305,7 @@ main(int argc, char **argv)
{"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
{"role", required_argument, NULL, 3},
{"use-set-session-authorization", no_argument, &use_setsessauth, 1},
+ {"security-label", no_argument, &security_label, 1},
{NULL, 0, NULL, 0}
};
@@ -448,6 +454,8 @@ main(int argc, char **argv)
outputNoTablespaces = 1;
else if (strcmp(optarg, "use-set-session-authorization") == 0)
use_setsessauth = 1;
+ else if (strcmp(optarg, "security-label") == 0)
+ security_label = 1;
else
{
fprintf(stderr,
@@ -643,6 +651,15 @@ main(int argc, char **argv)
do_sql_command(g_conn, "SET quote_all_identifiers = true");
/*
+ * Disables security label support if server version < v9.1.x
+ */
+ if (security_label && g_fout->remoteVersion < 90100)
+ {
+ write_msg(NULL, "Server does not support security labels\n");
+ security_label = 0;
+ }
+
+ /*
* Start serializable transaction to dump consistent data.
*/
do_sql_command(g_conn, "BEGIN");
@@ -839,6 +856,7 @@ help(const char *progname)
printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
printf(_(" --quote-all-identifiers quote all identifiers, even if not keywords\n"));
printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
+ printf(_(" --security-label also dump security labels\n"));
printf(_(" --use-set-session-authorization\n"
" use SET SESSION AUTHORIZATION commands instead of\n"
" ALTER OWNER commands to set ownership\n"));
@@ -10435,6 +10453,111 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
}
/*
+ * dumpSecLabel
+ *
+ * It dumps any security labels associated with the object handed
+ * to this routine. The routine takes a constant character string
+ * for the target part of the security-label command, plus the
+ * namespace and owner of the object (for labeling the ArchiveEntry),
+ * plus catalogId which is lookup key for pg_seclabel, and dumpId
+ * for the object.
+ * If a matching pg_seclabel entry is found, it is dumped.
+ */
+static void
+dumpSecLabel(Archive *fout, CatalogId objCatId, DumpId objDumpId,
+ const char *type, const char *target,
+ const char *nspname, const char *owner)
+{
+ PGresult *res;
+ PQExpBuffer lquery;
+ PQExpBuffer cquery;
+ PQExpBuffer tgbuf;
+ int i_attname;
+ int i_provider;
+ int i_label;
+ int i, ntups;
+
+ /* do nothing, if security label dump is not enabled */
+ if (!security_label)
+ return;
+
+ /* --data-only skips security labels *except* large object */
+ if (dataOnly && strcmp(type, "LARGE OBJECT") != 0)
+ return;
+
+ /*
+ * Fetch security labels associated with the object
+ */
+ lquery = createPQExpBuffer();
+ cquery = createPQExpBuffer();
+ tgbuf = createPQExpBuffer();
+
+ appendPQExpBuffer(lquery,
+ "SELECT a.attname, s.provider, s.label"
+ " FROM pg_catalog.pg_seclabel s"
+ " LEFT OUTER JOIN"
+ " pg_catalog.pg_attribute a"
+ " ON s.objoid = a.attrelid AND"
+ " s.objsubid = a.attnum"
+ " WHERE classoid = %u AND objoid = %u",
+ objCatId.tableoid, objCatId.oid);
+
+ res = PQexec(g_conn, lquery->data);
+ check_sql_result(res, g_conn, lquery->data, PGRES_TUPLES_OK);
+
+ i_attname = PQfnumber(res, "attname");
+ i_provider = PQfnumber(res, "provider");
+ i_label = PQfnumber(res, "label");
+
+ ntups = PQntuples(res);
+
+ for (i = 0; i < ntups; i++)
+ {
+ if (strcmp(type, "TABLE") == 0 &&
+ !PQgetisnull(res, i, i_attname))
+ {
+ appendPQExpBuffer(cquery,
+ "SECURITY LABEL FOR '%s'"
+ " ON COLUMN %s.%s IS '%s';\n",
+ PQgetvalue(res, i, i_provider),
+ target,
+ fmtId(PQgetvalue(res, i, i_attname)),
+ PQgetvalue(res, i, i_label));
+ continue;
+ }
+
+ /*
+ * a.attname can be available only when type = "TABLE",
+ * so we expect it will be always NULL. Elsewhere, we assume
+ * it as a data corruption, so simply ignored.
+ */
+ if (!PQgetisnull(res, i, i_attname))
+ continue;
+
+ appendPQExpBuffer(cquery,
+ "SECURITY LABEL FOR '%s' ON %s %s IS '%s';\n",
+ PQgetvalue(res, i, i_provider),
+ type, target,
+ PQgetvalue(res, i, i_label));
+ }
+ PQclear(res);
+
+ if (cquery->len > 0)
+ {
+ appendPQExpBuffer(tgbuf, "%s %s", type, target);
+ ArchiveEntry(fout, nilCatalogId, createDumpId(),
+ tgbuf->data, nspname, NULL, owner,
+ false, "SECURITY LABEL", SECTION_NONE,
+ cquery->data, "", NULL,
+ &(objDumpId), 1,
+ NULL, NULL);
+ }
+ destroyPQExpBuffer(lquery);
+ destroyPQExpBuffer(cquery);
+ destroyPQExpBuffer(tgbuf);
+}
+
+/*
* dumpTable
* write out to fout the declarations (not data) of a user-defined table
*/
@@ -10458,6 +10581,13 @@ dumpTable(Archive *fout, TableInfo *tbinfo)
tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
tbinfo->relacl);
+ /* Handle the security label here */
+ dumpSecLabel(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId,
+ (tbinfo->relkind == RELKIND_SEQUENCE ? "SEQUENCE" :
+ (tbinfo->relkind == RELKIND_VIEW ? "VIEW" : "TABLE")),
+ namecopy,
+ tbinfo->dobj.namespace->dobj.name,
+ tbinfo->rolname);
/*
* Handle column ACLs, if any. Note: we pull these with a separate
* query rather than trying to fetch them during getTableAttrs, so
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 9294e07..79708be 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -69,6 +69,7 @@ static int disable_triggers = 0;
static int inserts = 0;
static int no_tablespaces = 0;
static int use_setsessauth = 0;
+static int security_label = 0;
static int server_version;
static FILE *OPF;
@@ -133,6 +134,7 @@ main(int argc, char *argv[])
{"quote-all-identifiers", no_argument, "e_all_identifiers, 1},
{"role", required_argument, NULL, 3},
{"use-set-session-authorization", no_argument, &use_setsessauth, 1},
+ {"security-label", no_argument, &security_label, 1},
{NULL, 0, NULL, 0}
};
@@ -286,6 +288,8 @@ main(int argc, char *argv[])
no_tablespaces = 1;
else if (strcmp(optarg, "use-set-session-authorization") == 0)
use_setsessauth = 1;
+ else if (strcmp(optarg, "security-label") == 0)
+ security_label = 1;
else
{
fprintf(stderr,
@@ -371,6 +375,8 @@ main(int argc, char *argv[])
appendPQExpBuffer(pgdumpopts, " --quote-all-identifiers");
if (use_setsessauth)
appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization");
+ if (security_label)
+ appendPQExpBuffer(pgdumpopts, " --security-label");
/*
* If there was a database specified on the command line, use that,
@@ -567,6 +573,7 @@ help(void)
printf(_(" --no-tablespaces do not dump tablespace assignments\n"));
printf(_(" --quote-all-identifiers quote all identifiers, even if not keywords\n"));
printf(_(" --role=ROLENAME do SET ROLE before dump\n"));
+ printf(_(" --security-label also dump security labels\n"));
printf(_(" --use-set-session-authorization\n"
" use SET SESSION AUTHORIZATION commands instead of\n"
" ALTER OWNER commands to set ownership\n"));
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index b9f0e86..1ddba72 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -76,6 +76,7 @@ main(int argc, char **argv)
static int no_data_for_failed_tables = 0;
static int outputNoTablespaces = 0;
static int use_setsessauth = 0;
+ static int skip_seclabel = 0;
struct option cmdopts[] = {
{"clean", 0, NULL, 'c'},
@@ -116,6 +117,7 @@ main(int argc, char **argv)
{"no-tablespaces", no_argument, &outputNoTablespaces, 1},
{"role", required_argument, NULL, 2},
{"use-set-session-authorization", no_argument, &use_setsessauth, 1},
+ {"no-security-label", no_argument, &skip_seclabel, 1},
{NULL, 0, NULL, 0}
};
@@ -262,6 +264,8 @@ main(int argc, char **argv)
outputNoTablespaces = 1;
else if (strcmp(optarg, "use-set-session-authorization") == 0)
use_setsessauth = 1;
+ else if (strcmp(optarg, "no-security-label") == 0)
+ skip_seclabel = 1;
else
{
fprintf(stderr,
@@ -337,6 +341,7 @@ main(int argc, char **argv)
opts->noDataForFailedTables = no_data_for_failed_tables;
opts->noTablespace = outputNoTablespaces;
opts->use_setsessauth = use_setsessauth;
+ opts->skip_seclabel = skip_seclabel;
if (opts->formatName)
{
@@ -442,6 +447,7 @@ usage(const char *progname)
" do not restore data of tables that could not be\n"
" created\n"));
printf(_(" --no-tablespaces do not restore tablespace assignments\n"));
+ printf(_(" --no-security-label do not restore security labels\n"));
printf(_(" --role=ROLENAME do SET ROLE before restore\n"));
printf(_(" --use-set-session-authorization\n"
" use SET SESSION AUTHORIZATION commands instead of\n"
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 38c48b9..9fa11c5 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -281,6 +281,9 @@ DECLARE_UNIQUE_INDEX(pg_default_acl_oid_index, 828, on pg_default_acl using btre
DECLARE_UNIQUE_INDEX(pg_db_role_setting_databaseid_rol_index, 2965, on pg_db_role_setting using btree(setdatabase oid_ops, setrole oid_ops));
#define DbRoleSettingDatidRolidIndexId 2965
+DECLARE_UNIQUE_INDEX(pg_seclabel_object_index, 3038, on pg_seclabel using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops, provider text_ops));
+#define SecLabelObjectIndexId 3038
+
/* last step of initialization script: build the indexes declared above */
BUILD_INDICES
diff --git a/src/include/catalog/pg_seclabel.h b/src/include/catalog/pg_seclabel.h
new file mode 100644
index 0000000..1fd7451
--- /dev/null
+++ b/src/include/catalog/pg_seclabel.h
@@ -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 */
diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h
index 560d837..1e59cd2 100644
--- a/src/include/catalog/toasting.h
+++ b/src/include/catalog/toasting.h
@@ -45,6 +45,7 @@ DECLARE_TOAST(pg_constraint, 2832, 2833);
DECLARE_TOAST(pg_description, 2834, 2835);
DECLARE_TOAST(pg_proc, 2836, 2837);
DECLARE_TOAST(pg_rewrite, 2838, 2839);
+DECLARE_TOAST(pg_seclabel, 3039, 3040);
DECLARE_TOAST(pg_statistic, 2840, 2841);
DECLARE_TOAST(pg_trigger, 2336, 2337);
diff --git a/src/include/commands/seclabel.h b/src/include/commands/seclabel.h
new file mode 100644
index 0000000..4c3854e
--- /dev/null
+++ b/src/include/commands/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 */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 35def5e..0d33a2e 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -347,6 +347,7 @@ typedef enum NodeTag
T_AlterUserMappingStmt,
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
+ T_SecLabelStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b67ab68..5d6498e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1850,6 +1850,20 @@ typedef struct CommentStmt
} CommentStmt;
/* ----------------------
+ * SECURITY LABEL Statement
+ * ----------------------
+ */
+typedef struct SecLabelStmt
+{
+ NodeTag type;
+ ObjectType objtype; /* Object's type */
+ List *objname; /* Qualified name of the object */
+ List *objargs; /* Arguments if needed (eg, for functions) */
+ char *provider; /* Label provider (or NULL) */
+ char *label; /* New security label to be assigned */
+} SecLabelStmt;
+
+/* ----------------------
* Declare Cursor Statement
*
* Note: the "query" field of DeclareCursorStmt is only used in the raw grammar
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 5e3ccd5..028f5ae 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -208,6 +208,7 @@ PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD)
PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD)
+PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD)
PG_KEYWORD("language", LANGUAGE, UNRESERVED_KEYWORD)
PG_KEYWORD("large", LARGE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("last", LAST_P, UNRESERVED_KEYWORD)
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 1d9e110..9596b0b 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -114,6 +114,7 @@ SELECT relname, relhasindex
pg_pltemplate | t
pg_proc | t
pg_rewrite | t
+ pg_seclabel | t
pg_shdepend | t
pg_shdescription | t
pg_statistic | t
@@ -153,7 +154,7 @@ SELECT relname, relhasindex
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
-(142 rows)
+(143 rows)
--
-- another sanity check: every system catalog that has OIDs should have