OSDN Git Service

Add a SECURITY LABEL command.
authorRobert Haas <rhaas@postgresql.org>
Tue, 28 Sep 2010 00:55:27 +0000 (20:55 -0400)
committerRobert Haas <rhaas@postgresql.org>
Tue, 28 Sep 2010 00:55:27 +0000 (20:55 -0400)
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 Haas

42 files changed:
contrib/Makefile
contrib/dummy_seclabel/Makefile [new file with mode: 0644]
contrib/dummy_seclabel/dummy_seclabel.c [new file with mode: 0644]
doc/src/sgml/catalogs.sgml
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/pg_dump.sgml
doc/src/sgml/ref/pg_dumpall.sgml
doc/src/sgml/ref/pg_restore.sgml
doc/src/sgml/ref/security_label.sgml [new file with mode: 0644]
doc/src/sgml/reference.sgml
src/backend/catalog/Makefile
src/backend/catalog/dependency.c
src/backend/catalog/system_views.sql
src/backend/commands/Makefile
src/backend/commands/seclabel.c [new file with mode: 0644]
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/backend/tcop/utility.c
src/bin/pg_dump/pg_backup.h
src/bin/pg_dump/pg_backup_archiver.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dumpall.c
src/bin/pg_dump/pg_restore.c
src/bin/psql/tab-complete.c
src/include/catalog/catversion.h
src/include/catalog/indexing.h
src/include/catalog/pg_seclabel.h [new file with mode: 0644]
src/include/catalog/toasting.h
src/include/commands/seclabel.h [new file with mode: 0644]
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/parser/kwlist.h
src/test/regress/GNUmakefile
src/test/regress/expected/.gitignore
src/test/regress/expected/rules.out
src/test/regress/expected/sanity_check.out
src/test/regress/input/security_label.source [new file with mode: 0644]
src/test/regress/output/security_label.source [new file with mode: 0644]
src/test/regress/parallel_schedule
src/test/regress/serial_schedule
src/test/regress/sql/.gitignore

index c1d3317..b777325 100644 (file)
@@ -15,6 +15,7 @@ SUBDIRS = \
                dblink          \
                dict_int        \
                dict_xsyn       \
+               dummy_seclabel  \
                earthdistance   \
                fuzzystrmatch   \
                hstore          \
diff --git a/contrib/dummy_seclabel/Makefile b/contrib/dummy_seclabel/Makefile
new file mode 100644 (file)
index 0000000..105400f
--- /dev/null
@@ -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
diff --git a/contrib/dummy_seclabel/dummy_seclabel.c b/contrib/dummy_seclabel/dummy_seclabel.c
new file mode 100644 (file)
index 0000000..8bd50a3
--- /dev/null
@@ -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);
+}
index ab11b15..8e4081c 100644 (file)
      </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>
  </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 pertains to</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>
 
      </row>
 
      <row>
+      <entry><link linkend="view-pg-seclabels"><structname>pg_seclabels</structname></link></entry>
+      <entry>security labels</entry>
+     </row>
+
+     <row>
       <entry><link linkend="view-pg-settings"><structname>pg_settings</structname></link></entry>
       <entry>parameter settings</entry>
      </row>
 
  </sect1>
 
+ <sect1 id="view-pg-seclabels">
+  <title><structname>pg_seclabels</structname></title>
+
+  <indexterm zone="view-pg-seclabels">
+   <primary>pg_seclabels</primary>
+  </indexterm>
+
+  <para>
+   The view <structname>pg_seclabels</structname> provides information about
+   security labels.  It as an easier-to-query version of the
+   <link linkend="catalog-pg-seclabel"><structname>pg_seclabel</></> catalog.
+  </para>
+
+  <table>
+   <title><structname>pg_seclabels</> 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 pertains to</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>objtype</structfield></entry>
+      <entry><type>text</type></entry>
+      <entry></entry>
+      <entry>
+         The type of object to which this label applies, as text.
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>objnamespace</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry>
+      <entry>
+       The OID of the namespace for this object, if applicable;
+       otherwise NULL.
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>objname</structfield></entry>
+      <entry><type>text</type></entry>
+      <entry></entry>
+      <entry>
+       The name of the object to which this label applies, as text.
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>provider</structfield></entry>
+      <entry><type>text</type></entry>
+      <entry><literal><link linkend="catalog-pg-seclabel"><structname>pg_seclabel</structname></link>.provider</literal></entry>
+      <entry>The label provider associated with this label.</entry>
+     </row>
+     <row>
+      <entry><structfield>label</structfield></entry>
+      <entry><type>text</type></entry>
+      <entry><literal><link linkend="catalog-pg-seclabel"><structname>pg_seclabel</structname></link>.label</literal></entry>
+      <entry>The security label applied to this object.</entry>
+     </row>
+    </tbody>
+   </tgroup>
+  </table>
+ </sect1>
+
  <sect1 id="view-pg-settings">
   <title><structname>pg_settings</structname></title>
 
index 7b97883..f5d67a2 100644 (file)
@@ -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">
index 1b8402e..8242b53 100644 (file)
@@ -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>
index 14fa109..68dcc35 100644 (file)
@@ -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>
index 9dc2511..7860696 100644 (file)
      </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 (file)
index 0000000..7fce58b
--- /dev/null
@@ -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>
index 052fe0e..463746c 100644 (file)
    &rollbackPrepared;
    &rollbackTo;
    &savepoint;
+   &securityLabel;
    &select;
    &selectInto;
    &set;
index f4a7eb0..6a47f39 100644 (file)
@@ -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 \
     )
 
index 62598ee..18e07eb 100644 (file)
@@ -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
index 651ffc6..09574c3 100644 (file)
@@ -160,6 +160,114 @@ CREATE VIEW pg_prepared_xacts AS
 CREATE VIEW pg_prepared_statements AS
     SELECT * FROM pg_prepared_statement() AS P;
 
+CREATE VIEW pg_seclabels AS
+SELECT
+       l.objoid, l.classoid, l.objsubid,
+       CASE WHEN rel.relkind = 'r' THEN 'table'::text
+            WHEN rel.relkind = 'v' THEN 'view'::text
+            WHEN rel.relkind = 'S' THEN 'sequence'::text END AS objtype,
+       rel.relnamespace AS objnamespace,
+       CASE WHEN pg_table_is_visible(rel.oid)
+            THEN quote_ident(rel.relname)
+            ELSE quote_ident(nsp.nspname) || '.' || quote_ident(rel.relname)
+            END AS objname,
+       l.provider, l.label
+FROM
+       pg_seclabel l
+       JOIN pg_class rel ON l.classoid = rel.tableoid AND l.objoid = rel.oid
+       JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid
+WHERE
+       l.objsubid = 0
+UNION ALL
+SELECT
+       l.objoid, l.classoid, l.objsubid,
+       'column'::text AS objtype,
+       rel.relnamespace AS objnamespace,
+       CASE WHEN pg_table_is_visible(rel.oid)
+            THEN quote_ident(rel.relname)
+            ELSE quote_ident(nsp.nspname) || '.' || quote_ident(rel.relname)
+            END || '.' || att.attname AS objname,
+       l.provider, l.label
+FROM
+       pg_seclabel l
+       JOIN pg_class rel ON l.classoid = rel.tableoid AND l.objoid = rel.oid
+       JOIN pg_attribute att
+            ON rel.oid = att.attrelid AND l.objsubid = att.attnum
+       JOIN pg_namespace nsp ON rel.relnamespace = nsp.oid
+WHERE
+       l.objsubid != 0
+UNION ALL
+SELECT
+       l.objoid, l.classoid, l.objsubid,
+       CASE WHEN pro.proisagg = true THEN 'aggregate'::text
+            WHEN pro.proisagg = false THEN 'function'::text
+       END AS objtype,
+       pro.pronamespace AS objnamespace,
+       CASE WHEN pg_function_is_visible(pro.oid)
+            THEN quote_ident(pro.proname)
+            ELSE quote_ident(nsp.nspname) || '.' || quote_ident(pro.proname)
+       END || '(' || pg_catalog.pg_get_function_arguments(pro.oid) || ')' AS objname,
+       l.provider, l.label
+FROM
+       pg_seclabel l
+       JOIN pg_proc pro ON l.classoid = pro.tableoid AND l.objoid = pro.oid
+       JOIN pg_namespace nsp ON pro.pronamespace = nsp.oid
+WHERE
+       l.objsubid = 0
+UNION ALL
+SELECT
+       l.objoid, l.classoid, l.objsubid,
+       CASE WHEN typ.typtype = 'd' THEN 'domain'::text
+       ELSE 'type'::text END AS objtype,
+       typ.typnamespace AS objnamespace,
+       CASE WHEN pg_type_is_visible(typ.oid)
+       THEN quote_ident(typ.typname)
+       ELSE quote_ident(nsp.nspname) || '.' || quote_ident(typ.typname)
+       END AS objname,
+       l.provider, l.label
+FROM
+       pg_seclabel l
+       JOIN pg_type typ ON l.classoid = typ.tableoid AND l.objoid = typ.oid
+       JOIN pg_namespace nsp ON typ.typnamespace = nsp.oid
+WHERE
+       l.objsubid = 0
+UNION ALL
+SELECT
+       l.objoid, l.classoid, l.objsubid,
+       'large object'::text AS objtype,
+       NULL::oid AS objnamespace,
+       l.objoid::text AS objname,
+       l.provider, l.label
+FROM
+       pg_seclabel l
+       JOIN pg_largeobject_metadata lom ON l.objoid = lom.oid
+WHERE
+       l.classoid = 'pg_catalog.pg_largeobject'::regclass AND l.objsubid = 0
+UNION ALL
+SELECT
+       l.objoid, l.classoid, l.objsubid,
+       'language'::text AS objtype,
+       NULL::oid AS objnamespace,
+       quote_ident(lan.lanname) AS objname,
+       l.provider, l.label
+FROM
+       pg_seclabel l
+       JOIN pg_language lan ON l.classoid = lan.tableoid AND l.objoid = lan.oid
+WHERE
+       l.objsubid = 0
+UNION ALL
+SELECT
+       l.objoid, l.classoid, l.objsubid,
+       'schema'::text AS objtype,
+       nsp.oid AS objnamespace,
+       quote_ident(nsp.nspname) AS objname,
+       l.provider, l.label
+FROM
+       pg_seclabel l
+       JOIN pg_namespace nsp ON l.classoid = nsp.tableoid AND l.objoid = nsp.oid
+WHERE
+       l.objsubid = 0;
+
 CREATE VIEW pg_settings AS 
     SELECT * FROM pg_show_all_settings() AS A; 
 
index 4e9bf43..9d2a732 100644 (file)
@@ -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 (file)
index 0000000..417ad88
--- /dev/null
@@ -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);
+}
index deaeb76..e07aa3e 100644 (file)
@@ -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;
index 6b6cd99..8d083c8 100644 (file)
@@ -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;
index 40bd7a3..4054cb1 100644 (file)
@@ -205,7 +205,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
                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
@@ -335,7 +335,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 %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
@@ -423,6 +423,8 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 %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
@@ -500,7 +502,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
 
        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
 
@@ -739,6 +741,7 @@ stmt :
                        | RevokeStmt
                        | RevokeRoleStmt
                        | RuleStmt
+                       | SecLabelStmt
                        | SelectStmt
                        | TransactionStmt
                        | TruncateStmt
@@ -4368,6 +4371,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:
@@ -11049,6 +11138,7 @@ unreserved_keyword:
                        | INVOKER
                        | ISOLATION
                        | KEY
+                       | LABEL
                        | LANGUAGE
                        | LARGE_P
                        | LAST_P
index f69559f..75cb354 100644 (file)
@@ -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;
@@ -2318,6 +2328,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;
index 6f0277e..8fa9a57 100644 (file)
@@ -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;
index a73afec..d1a9c54 100644 (file)
@@ -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
index 2e1486f..ff7d97a 100644 (file)
@@ -70,6 +70,14 @@ typedef struct
        int                     objsubid;               /* subobject (table column #) */
 } CommentItem;
 
+typedef struct
+{
+       const char *provider;           /* label provider of this security label */
+       const char *label;                      /* security label for an object */
+       Oid                     classoid;               /* object class (catalog OID) */
+       Oid                     objoid;                 /* object OID */
+       int                     objsubid;               /* subobject (table column #) */
+} SecLabelItem;
 
 /* global decls */
 bool           g_verbose;                      /* User wants verbose narration of our
@@ -125,6 +133,7 @@ static int  binary_upgrade = 0;
 static int     disable_dollar_quoting = 0;
 static int     dump_inserts = 0;
 static int     column_inserts = 0;
+static int     no_security_label = 0;
 
 
 static void help(const char *progname);
@@ -141,6 +150,12 @@ static void dumpComment(Archive *fout, const char *target,
 static int findComments(Archive *fout, Oid classoid, Oid objoid,
                         CommentItem **items);
 static int     collectComments(Archive *fout, CommentItem **items);
+static void    dumpSecLabel(Archive *fout, const char *target,
+                                                const char *namespace, const char *owner,
+                                                CatalogId catalogId, int subid, DumpId dumpId);
+static int     findSecLabels(Archive *fout, Oid classoid, Oid objoid,
+                                                 SecLabelItem **items);
+static int     collectSecLabels(Archive *fout, SecLabelItem **items);
 static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
 static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
 static void dumpType(Archive *fout, TypeInfo *tyinfo);
@@ -300,6 +315,7 @@ main(int argc, char **argv)
                {"quote-all-identifiers", no_argument, &quote_all_identifiers, 1},
                {"role", required_argument, NULL, 3},
                {"use-set-session-authorization", no_argument, &use_setsessauth, 1},
+               {"no-security-label", no_argument, &no_security_label, 1},
 
                {NULL, 0, NULL, 0}
        };
@@ -448,6 +464,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)
+                                       no_security_label = 1;
                                else
                                {
                                        fprintf(stderr,
@@ -643,6 +661,12 @@ 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 (!no_security_label && g_fout->remoteVersion < 90100)
+               no_security_label = 1;
+
+       /*
         * Start serializable transaction to dump consistent data.
         */
        do_sql_command(g_conn, "BEGIN");
@@ -839,6 +863,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(_("  --no-security-label         do not dump security label assignments\n"));
        printf(_("  --use-set-session-authorization\n"
                         "                              use SET SESSION AUTHORIZATION commands instead of\n"
        "                              ALTER OWNER commands to set ownership\n"));
@@ -2058,6 +2083,11 @@ dumpBlob(Archive *AH, BlobInfo *binfo)
                                NULL, binfo->rolname,
                                binfo->dobj.catId, 0, binfo->dobj.dumpId);
 
+       /* Dump security label if any */
+       dumpSecLabel(AH, cquery->data,
+                                NULL, binfo->rolname,
+                                binfo->dobj.catId, 0, binfo->dobj.dumpId);
+
        /* Dump ACL if any */
        if (binfo->blobacl)
                dumpACL(AH, binfo->dobj.catId, binfo->dobj.dumpId, "LARGE OBJECT",
@@ -6569,12 +6599,15 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo)
                                 nspinfo->dobj.dependencies, nspinfo->dobj.nDeps,
                                 NULL, NULL);
 
-       /* Dump Schema Comments */
+       /* Dump Schema Comments and Security Labels */
        resetPQExpBuffer(q);
        appendPQExpBuffer(q, "SCHEMA %s", qnspname);
        dumpComment(fout, q->data,
                                NULL, nspinfo->rolname,
                                nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
+       dumpSecLabel(fout, q->data,
+                                NULL, nspinfo->rolname,
+                                nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
 
        dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA",
                        qnspname, NULL, nspinfo->dobj.name, NULL,
@@ -6699,13 +6732,16 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
                                 tyinfo->dobj.dependencies, tyinfo->dobj.nDeps,
                                 NULL, NULL);
 
-       /* Dump Type Comments */
+       /* Dump Type Comments and Security Labels */
        resetPQExpBuffer(q);
 
        appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name));
        dumpComment(fout, q->data,
                                tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
                                tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+       dumpSecLabel(fout, q->data,
+                                tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+                                tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
 
        PQclear(res);
        destroyPQExpBuffer(q);
@@ -7075,13 +7111,16 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
                                 tyinfo->dobj.dependencies, tyinfo->dobj.nDeps,
                                 NULL, NULL);
 
-       /* Dump Type Comments */
+       /* Dump Type Comments and Security Labels */
        resetPQExpBuffer(q);
 
        appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name));
        dumpComment(fout, q->data,
                                tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
                                tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+       dumpSecLabel(fout, q->data,
+                                tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+                                tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
 
        PQclear(res);
        destroyPQExpBuffer(q);
@@ -7199,13 +7238,16 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
                                 tyinfo->dobj.dependencies, tyinfo->dobj.nDeps,
                                 NULL, NULL);
 
-       /* Dump Domain Comments */
+       /* Dump Domain Comments and Security Labels */
        resetPQExpBuffer(q);
 
        appendPQExpBuffer(q, "DOMAIN %s", fmtId(tyinfo->dobj.name));
        dumpComment(fout, q->data,
                                tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
                                tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+       dumpSecLabel(fout, q->data,
+                                tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+                                tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
 
        destroyPQExpBuffer(q);
        destroyPQExpBuffer(delq);
@@ -7299,13 +7341,16 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
                                 NULL, NULL);
 
 
-       /* Dump Type Comments */
+       /* Dump Type Comments and Security Labels */
        resetPQExpBuffer(q);
 
        appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name));
        dumpComment(fout, q->data,
                                tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
                                tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+       dumpSecLabel(fout, q->data,
+                                tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
+                                tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
 
        PQclear(res);
        destroyPQExpBuffer(q);
@@ -7623,12 +7668,15 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang)
                                 plang->dobj.dependencies, plang->dobj.nDeps,
                                 NULL, NULL);
 
-       /* Dump Proc Lang Comments */
+       /* Dump Proc Lang Comments and Security Labels */
        resetPQExpBuffer(defqry);
        appendPQExpBuffer(defqry, "LANGUAGE %s", qlanname);
        dumpComment(fout, defqry->data,
                                NULL, "",
                                plang->dobj.catId, 0, plang->dobj.dumpId);
+       dumpSecLabel(fout, defqry->data,
+                                NULL, "",
+                                plang->dobj.catId, 0, plang->dobj.dumpId);
 
        if (plang->lanpltrusted)
                dumpACL(fout, plang->dobj.catId, plang->dobj.dumpId, "LANGUAGE",
@@ -8184,12 +8232,15 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
                                 finfo->dobj.dependencies, finfo->dobj.nDeps,
                                 NULL, NULL);
 
-       /* Dump Function Comments */
+       /* Dump Function Comments and Security Labels */
        resetPQExpBuffer(q);
        appendPQExpBuffer(q, "FUNCTION %s", funcsig);
        dumpComment(fout, q->data,
                                finfo->dobj.namespace->dobj.name, finfo->rolname,
                                finfo->dobj.catId, 0, finfo->dobj.dumpId);
+       dumpSecLabel(fout, q->data,
+                                finfo->dobj.namespace->dobj.name, finfo->rolname,
+                                finfo->dobj.catId, 0, finfo->dobj.dumpId);
 
        dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION",
                        funcsig, NULL, funcsig_tag,
@@ -9687,6 +9738,9 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
        dumpComment(fout, q->data,
                        agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname,
                                agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
+       dumpSecLabel(fout, q->data,
+                       agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname,
+                                agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
 
        /*
         * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
@@ -10435,6 +10489,300 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
 }
 
 /*
+ * dumpSecLabel 
+ *
+ * This routine is used to dump 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 catalog ID and subid which are the lookup key for pg_seclabel,
+ * plus the dump ID for the object (for setting a dependency).
+ * If a matching pg_seclabel entry is found, it is dumped.
+ *
+ * Note: although this routine takes a dumpId for dependency purposes,
+ * that purpose is just to mark the dependency in the emitted dump file
+ * for possible future use by pg_restore.  We do NOT use it for determining
+ * ordering of the label in the dump file, because this routine is called
+ * after dependency sorting occurs.  This routine should be called just after
+ * calling ArchiveEntry() for the specified object.
+ */
+static void
+dumpSecLabel(Archive *fout, const char *target,
+                        const char *namespace, const char *owner,
+                        CatalogId catalogId, int subid, DumpId dumpId)
+{
+       SecLabelItem   *labels;
+       int                             nlabels;
+       int                             i;
+       PQExpBuffer             query;
+
+       /* do nothing, if --no-security-label is supplied */
+       if (no_security_label)
+               return;
+
+       /* Comments are schema not data ... except blob comments are data */
+       if (strncmp(target, "LARGE OBJECT ", 13) != 0)
+       {
+               if (dataOnly)
+                       return;
+       }
+       else
+       {
+               if (schemaOnly)
+                       return;
+       }
+
+       /* Search for security labels associated with catalogId, using table */
+       nlabels = findSecLabels(fout, catalogId.tableoid, catalogId.oid, &labels);
+
+       query = createPQExpBuffer();
+
+       for (i = 0; i < nlabels; i++)
+       {
+               /*
+                * Ignore label entries for which the subid doesn't match.
+                */
+               if (labels[i].objsubid != subid)
+                       continue;
+
+               appendPQExpBuffer(query,
+                                                 "SECURITY LABEL FOR %s ON %s IS ",
+                                                 fmtId(labels[i].provider), target);
+               appendStringLiteralAH(query, labels[i].label, fout);
+               appendPQExpBuffer(query, ";\n");
+       }
+
+       if (query->len > 0)
+       {
+               ArchiveEntry(fout, nilCatalogId, createDumpId(),
+                                        target, namespace, NULL, owner,
+                                        false, "SECURITY LABEL", SECTION_NONE,
+                                        query->data, "", NULL,
+                                        &(dumpId), 1,
+                                        NULL, NULL);
+       }
+       destroyPQExpBuffer(query);
+}
+
+/*
+ * dumpTableSecLabel
+ *
+ * As above, but dump security label for both the specified table (or view)
+ * and its columns.
+ */
+static void
+dumpTableSecLabel(Archive *fout, TableInfo *tbinfo, const char *reltypename)
+{
+       SecLabelItem   *labels;
+       int                             nlabels;
+       int                             i;
+       PQExpBuffer             query;
+    PQExpBuffer                target;
+
+       /* do nothing, if --no-security-label is supplied */
+       if (no_security_label)
+               return;
+
+       /* SecLabel are SCHEMA not data */
+       if (dataOnly)
+               return;
+
+       /* Search for comments associated with relation, using table */
+       nlabels = findSecLabels(fout,
+                                                       tbinfo->dobj.catId.tableoid,
+                                                       tbinfo->dobj.catId.oid,
+                                                       &labels);
+
+       /* If comments exist, build SECURITY LABEL statements */
+       if (nlabels <= 0)
+               return;
+
+       query = createPQExpBuffer();
+       target = createPQExpBuffer();
+
+       for (i = 0; i < nlabels; i++)
+       {
+               const char *colname;
+               const char *provider    = labels[i].provider;
+               const char *label               = labels[i].label;
+               int                     objsubid        = labels[i].objsubid;
+
+               resetPQExpBuffer(target);
+               if (objsubid == 0)
+               {
+                       appendPQExpBuffer(target, "%s %s", reltypename,
+                                                         fmtId(tbinfo->dobj.name));
+               }
+               else
+               {
+                       colname = getAttrName(objsubid, tbinfo);
+                       appendPQExpBuffer(target, "COLUMN %s.%s",
+                                                         fmtId(tbinfo->dobj.name),
+                                                         fmtId(colname));
+               }
+               appendPQExpBuffer(query, "SECURITY LABEL FOR %s ON %s IS ",
+                                                 fmtId(provider), target->data);
+               appendStringLiteralAH(query, label, fout);
+               appendPQExpBuffer(query, ";\n");
+       }
+       if (query->len > 0)
+       {
+               resetPQExpBuffer(target);
+               appendPQExpBuffer(target, "%s %s", reltypename,
+                                                 fmtId(tbinfo->dobj.name));
+               ArchiveEntry(fout, nilCatalogId, createDumpId(),
+                                        target->data,
+                                        tbinfo->dobj.namespace->dobj.name,
+                                        NULL, tbinfo->rolname,
+                                        false, "SECURITY LABEL", SECTION_NONE,
+                                        query->data, "", NULL,
+                                        &(tbinfo->dobj.dumpId), 1,
+                                        NULL, NULL);
+       }
+       destroyPQExpBuffer(query);
+       destroyPQExpBuffer(target);
+}
+
+/*
+ * findSecLabels
+ *
+ * Find the security label(s), if any, associated with the given object.
+ * All the objsubid values associated with the given classoid/objoid are
+ * found with one search.
+ */
+static int
+findSecLabels(Archive *fout, Oid classoid, Oid objoid, SecLabelItem **items)
+{
+       /* static storage for table of security labels */
+       static SecLabelItem *labels = NULL;
+       static int                       nlabels = -1;
+
+       SecLabelItem            *middle = NULL;
+       SecLabelItem            *low;
+       SecLabelItem            *high;
+       int                                      nmatch;
+
+       /* Get security labels if we didn't already */
+       if (nlabels < 0)
+               nlabels = collectSecLabels(fout, &labels);
+
+       /*
+        * Do binary search to find some item matching the object.
+        */
+       low = &labels[0];
+       high = &labels[nlabels - 1];
+       while (low <= high)
+       {
+               middle = low + (high - low) / 2;
+
+               if (classoid < middle->classoid)
+                       high = middle - 1;
+               else if (classoid > middle->classoid)
+                       low = middle + 1;
+               else if (objoid < middle->objoid)
+                       high = middle - 1;
+               else if (objoid > middle->objoid)
+                       low = middle + 1;
+               else
+                       break;                  /* found a match */
+       }
+
+       if (low > high)                 /* no matches */
+       {
+               *items = NULL;
+               return 0;
+       }
+
+       /*
+        * Now determine how many items match the object.  The search loop
+        * invariant still holds: only items between low and high inclusive could
+        * match.
+        */
+       nmatch = 1;
+       while (middle > low)
+       {
+               if (classoid != middle[-1].classoid ||
+                       objoid != middle[-1].objoid)
+                       break;
+               middle--;
+               nmatch++;
+       }
+
+       *items = middle;
+
+       middle += nmatch;
+       while (middle <= high)
+       {
+               if (classoid != middle->classoid ||
+                       objoid != middle->objoid)
+                       break;
+               middle++;
+               nmatch++;
+       }
+
+       return nmatch;
+}
+
+/*
+ * collectSecLabels
+ *
+ * Construct a table of all security labels available for database objects.
+ * It's much faster to pull them all at once.
+ *
+ * The table is sorted by classoid/objid/objsubid for speed in lookup.
+ */
+static int
+collectSecLabels(Archive *fout, SecLabelItem **items)
+{
+       PGresult           *res;
+       PQExpBuffer             query;
+       int                             i_label;
+       int                             i_provider;
+       int                             i_classoid;
+       int                             i_objoid;
+       int                             i_objsubid;
+       int                             ntups;
+       int                             i;
+       SecLabelItem   *labels;
+
+       query = createPQExpBuffer();
+
+       appendPQExpBuffer(query,
+                                         "SELECT label, provider, classoid, objoid, objsubid "
+                                         "FROM pg_catalog.pg_seclabel "
+                                         "ORDER BY classoid, objoid, objsubid");
+
+       res = PQexec(g_conn, query->data);
+       check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+       /* Construct lookup table containing OIDs in numeric form */
+       i_label         = PQfnumber(res, "label");
+       i_provider      = PQfnumber(res, "provider");
+       i_classoid      = PQfnumber(res, "classoid");
+       i_objoid        = PQfnumber(res, "objoid");
+       i_objsubid      = PQfnumber(res, "objsubid");
+
+       ntups = PQntuples(res);
+
+       labels = (SecLabelItem *) malloc(ntups * sizeof(SecLabelItem));
+
+       for (i = 0; i < ntups; i++)
+       {
+               labels[i].label         = PQgetvalue(res, i, i_label);
+               labels[i].provider      = PQgetvalue(res, i, i_provider);
+               labels[i].classoid = atooid(PQgetvalue(res, i, i_classoid));
+               labels[i].objoid = atooid(PQgetvalue(res, i, i_objoid));
+               labels[i].objsubid = atoi(PQgetvalue(res, i, i_objsubid));
+       }
+
+       /* Do NOT free the PGresult since we are keeping pointers into it */
+    destroyPQExpBuffer(query);
+
+       *items = labels;
+       return ntups;
+}
+
+/*
  * dumpTable
  *       write out to fout the declarations (not data) of a user-defined table
  */
@@ -10950,6 +11298,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
        /* Dump Table Comments */
        dumpTableComment(fout, tbinfo, reltypename);
 
+       /* Dump Table Security Labels */
+       dumpTableSecLabel(fout, tbinfo, reltypename);
+
        /* Dump comments on inlined table constraints */
        for (j = 0; j < tbinfo->ncheck; j++)
        {
@@ -11663,12 +12014,15 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
                        }
                }
 
-               /* Dump Sequence Comments */
+               /* Dump Sequence Comments and Security Labels */
                resetPQExpBuffer(query);
                appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo->dobj.name));
                dumpComment(fout, query->data,
                                        tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
                                        tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
+               dumpSecLabel(fout, query->data,
+                                        tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
+                                        tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
        }
 
        if (!schemaOnly)
index 9294e07..bf91d72 100644 (file)
@@ -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     no_security_label = 0;
 static int     server_version;
 
 static FILE *OPF;
@@ -133,6 +134,7 @@ main(int argc, char *argv[])
                {"quote-all-identifiers", no_argument, &quote_all_identifiers, 1},
                {"role", required_argument, NULL, 3},
                {"use-set-session-authorization", no_argument, &use_setsessauth, 1},
+               {"no-security-label", no_argument, &no_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, "no-security-label") == 0)
+                                       no_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 (no_security_label)
+               appendPQExpBuffer(pgdumpopts, " --no-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(_("  --no-security-label         do not dump security label assignments\n"));
        printf(_("  --use-set-session-authorization\n"
                         "                              use SET SESSION AUTHORIZATION commands instead of\n"
        "                              ALTER OWNER commands to set ownership\n"));
index b9f0e86..1ddba72 100644 (file)
@@ -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"
index 04c8184..51010e1 100644 (file)
@@ -631,8 +631,9 @@ psql_completion(char *text, int start, int end)
                "DELETE FROM", "DISCARD", "DO", "DROP", "END", "EXECUTE", "EXPLAIN", "FETCH",
                "GRANT", "INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", "PREPARE",
                "REASSIGN", "REINDEX", "RELEASE", "RESET", "REVOKE", "ROLLBACK",
-               "SAVEPOINT", "SELECT", "SET", "SHOW", "START", "TABLE", "TRUNCATE", "UNLISTEN",
-               "UPDATE", "VACUUM", "VALUES", "WITH", NULL
+               "SAVEPOINT", "SECURITY LABEL", "SELECT", "SET", "SHOW", "START",
+               "TABLE", "TRUNCATE", "UNLISTEN", "UPDATE", "VACUUM", "VALUES", "WITH",
+               NULL
        };
 
        static const char *const backslash_commands[] = {
@@ -2193,6 +2194,40 @@ psql_completion(char *text, int start, int end)
                        COMPLETE_WITH_QUERY(Query_for_list_of_databases);
        }
 
+/* SECURITY LABEL */
+       else if (pg_strcasecmp(prev_wd, "SECURITY") == 0)
+               COMPLETE_WITH_CONST("LABEL");
+       else if (pg_strcasecmp(prev2_wd, "SECURITY") == 0 &&
+                        pg_strcasecmp(prev_wd, "LABEL") == 0)
+       {
+               static const char *const list_SECURITY_LABEL_preposition[] =
+                       {"ON", "FOR"};
+               COMPLETE_WITH_LIST(list_SECURITY_LABEL_preposition);
+       }
+       else if (pg_strcasecmp(prev4_wd, "SECURITY") == 0 &&
+                        pg_strcasecmp(prev3_wd, "LABEL") == 0 &&
+                        pg_strcasecmp(prev2_wd, "FOR") == 0)
+               COMPLETE_WITH_CONST("ON");
+       else if ((pg_strcasecmp(prev3_wd, "SECURITY") == 0 &&
+                         pg_strcasecmp(prev2_wd, "LABEL") == 0 &&
+                         pg_strcasecmp(prev_wd, "ON") == 0) ||
+                (pg_strcasecmp(prev5_wd, "SECURITY") == 0 &&
+                         pg_strcasecmp(prev4_wd, "LABEL") == 0 &&
+                         pg_strcasecmp(prev3_wd, "FOR") == 0 &&
+                         pg_strcasecmp(prev_wd, "ON") == 0))
+       {
+               static const char *const list_SECURITY_LABEL[] =
+               {"LANGUAGE", "SCHEMA", "SEQUENCE", "TABLE", "TYPE", "VIEW", "COLUMN",
+                "AGGREGATE", "FUNCTION", "DOMAIN", "LARGE OBJECT",
+               NULL};
+
+               COMPLETE_WITH_LIST(list_SECURITY_LABEL);
+       }
+       else if (pg_strcasecmp(prev5_wd, "SECURITY") == 0 &&
+                        pg_strcasecmp(prev4_wd, "LABEL") == 0 &&
+                        pg_strcasecmp(prev3_wd, "ON") == 0)
+               COMPLETE_WITH_CONST("IS");
+
 /* SELECT */
        /* naah . . . */
 
index f88730e..74f1e24 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201009021
+#define CATALOG_VERSION_NO     201009271
 
 #endif
index 38c48b9..9fa11c5 100644 (file)
@@ -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 (file)
index 0000000..1fd7451
--- /dev/null
@@ -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 */
index 560d837..1e59cd2 100644 (file)
@@ -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 (file)
index 0000000..4c3854e
--- /dev/null
@@ -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 */
index 35def5e..0d33a2e 100644 (file)
@@ -347,6 +347,7 @@ typedef enum NodeTag
        T_AlterUserMappingStmt,
        T_DropUserMappingStmt,
        T_AlterTableSpaceOptionsStmt,
+       T_SecLabelStmt,
 
        /*
         * TAGS FOR PARSE TREE NODES (parsenodes.h)
index 68de95e..b2f0fef 100644 (file)
@@ -1851,6 +1851,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
index 01005d8..d3ea04b 100644 (file)
@@ -209,6 +209,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)
index 3683475..b2b5b16 100644 (file)
@@ -107,9 +107,9 @@ installdirs-tests: installdirs
        $(MKDIR_P)  $(patsubst $(srcdir)/%/,'$(DESTDIR)$(pkglibdir)/regress/%',$(sort $(dir $(regress_data_files))))
 
 
-# Get some extra C modules from contrib/spi...
+# Get some extra C modules from contrib/spi and contrib/dummy_seclabel...
 
-all: refint$(DLSUFFIX) autoinc$(DLSUFFIX)
+all: refint$(DLSUFFIX) autoinc$(DLSUFFIX) dummy_seclabel$(DLSUFFIX)
 
 refint$(DLSUFFIX): $(top_builddir)/contrib/spi/refint$(DLSUFFIX)
        cp $< $@
@@ -117,12 +117,17 @@ refint$(DLSUFFIX): $(top_builddir)/contrib/spi/refint$(DLSUFFIX)
 autoinc$(DLSUFFIX): $(top_builddir)/contrib/spi/autoinc$(DLSUFFIX)
        cp $< $@
 
+dummy_seclabel$(DLSUFFIX): $(top_builddir)/contrib/dummy_seclabel/dummy_seclabel$(DLSUFFIX)
+       cp $< $@
+
 $(top_builddir)/contrib/spi/refint$(DLSUFFIX): $(top_srcdir)/contrib/spi/refint.c
        $(MAKE) -C $(top_builddir)/contrib/spi refint$(DLSUFFIX)
 
 $(top_builddir)/contrib/spi/autoinc$(DLSUFFIX): $(top_srcdir)/contrib/spi/autoinc.c
        $(MAKE) -C $(top_builddir)/contrib/spi autoinc$(DLSUFFIX)
 
+$(top_builddir)/contrib/dummy_seclabel/dummy_seclabel$(DLSUFFIX): $(top_builddir)/contrib/dummy_seclabel/dummy_seclabel.c
+       $(MAKE) -C $(top_builddir)/contrib/dummy_seclabel dummy_seclabel$(DLSUFFIX)
 
 # Tablespace setup
 
@@ -171,7 +176,8 @@ bigcheck: all
 
 clean distclean maintainer-clean: clean-lib
 # things built by `all' target
-       rm -f $(OBJS) refint$(DLSUFFIX) autoinc$(DLSUFFIX) pg_regress_main.o pg_regress.o pg_regress$(X)
+       rm -f $(OBJS) refint$(DLSUFFIX) autoinc$(DLSUFFIX) dummy_seclabel$(DLSUFFIX)
+       rm -f pg_regress_main.o pg_regress.o pg_regress$(X)
 # things created by various check targets
        rm -f $(output_files) $(input_files)
        rm -rf testtablespace
index e14ce3a..93c56c8 100644 (file)
@@ -5,4 +5,5 @@
 /largeobject.out
 /largeobject_1.out
 /misc.out
+/security_label.out
 /tablespace.out
index ee3bd3b..c7a796a 100644 (file)
@@ -1276,8 +1276,8 @@ drop table cchild;
 -- Check that ruleutils are working
 --
 SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schema' ORDER BY viewname;
-          viewname           |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            definition                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+          viewname           |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                definition                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  iexit                       | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath);
  pg_cursors                  | SELECT c.name, c.statement, c.is_holdable, c.is_binary, c.is_scrollable, c.creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time);
  pg_group                    | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin);
@@ -1287,6 +1287,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
  pg_prepared_xacts           | SELECT p.transaction, p.gid, p.prepared, u.rolname AS owner, d.datname AS database FROM ((pg_prepared_xact() p(transaction, gid, prepared, ownerid, dbid) LEFT JOIN pg_authid u ON ((p.ownerid = u.oid))) LEFT JOIN pg_database d ON ((p.dbid = d.oid)));
  pg_roles                    | SELECT pg_authid.rolname, pg_authid.rolsuper, pg_authid.rolinherit, pg_authid.rolcreaterole, pg_authid.rolcreatedb, pg_authid.rolcatupdate, pg_authid.rolcanlogin, pg_authid.rolconnlimit, '********'::text AS rolpassword, pg_authid.rolvaliduntil, s.setconfig AS rolconfig, pg_authid.oid FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid))));
  pg_rules                    | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (r.rulename <> '_RETURN'::name);
+ pg_seclabels                | (((((SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (rel.relkind = 'r'::"char") THEN 'table'::text WHEN (rel.relkind = 'v'::"char") THEN 'view'::text WHEN (rel.relkind = 'S'::"char") THEN 'sequence'::text ELSE NULL::text END AS objtype, rel.relnamespace AS objnamespace, CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid = 0) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'column'::text AS objtype, rel.relnamespace AS objnamespace, ((CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END || '.'::text) || (att.attname)::text) AS objname, l.provider, l.label FROM (((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_attribute att ON (((rel.oid = att.attrelid) AND (l.objsubid = att.attnum)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid <> 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (pro.proisagg = true) THEN 'aggregate'::text WHEN (pro.proisagg = false) THEN 'function'::text ELSE NULL::text END AS objtype, pro.pronamespace AS objnamespace, (((CASE WHEN pg_function_is_visible(pro.oid) THEN quote_ident((pro.proname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((pro.proname)::text)) END || '('::text) || pg_get_function_arguments(pro.oid)) || ')'::text) AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_proc pro ON (((l.classoid = pro.tableoid) AND (l.objoid = pro.oid)))) JOIN pg_namespace nsp ON ((pro.pronamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (typ.typtype = 'd'::"char") THEN 'domain'::text ELSE 'type'::text END AS objtype, typ.typnamespace AS objnamespace, CASE WHEN pg_type_is_visible(typ.oid) THEN quote_ident((typ.typname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((typ.typname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_type typ ON (((l.classoid = typ.tableoid) AND (l.objoid = typ.oid)))) JOIN pg_namespace nsp ON ((typ.typnamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'large object'::text AS objtype, NULL::oid AS objnamespace, (l.objoid)::text AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_largeobject_metadata lom ON ((l.objoid = lom.oid))) WHERE ((l.classoid = ('pg_largeobject'::regclass)::oid) AND (l.objsubid = 0))) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'language'::text AS objtype, NULL::oid AS objnamespace, quote_ident((lan.lanname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_language lan ON (((l.classoid = lan.tableoid) AND (l.objoid = lan.oid)))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'schema'::text AS objtype, nsp.oid AS objnamespace, quote_ident((nsp.nspname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_namespace nsp ON (((l.classoid = nsp.tableoid) AND (l.objoid = nsp.oid)))) WHERE (l.objsubid = 0);
  pg_settings                 | SELECT a.name, a.setting, a.unit, a.category, a.short_desc, a.extra_desc, a.context, a.vartype, a.source, a.min_val, a.max_val, a.enumvals, a.boot_val, a.reset_val, a.sourcefile, a.sourceline FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline);
  pg_shadow                   | SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, pg_authid.rolcreatedb AS usecreatedb, pg_authid.rolsuper AS usesuper, pg_authid.rolcatupdate AS usecatupd, pg_authid.rolpassword AS passwd, (pg_authid.rolvaliduntil)::abstime AS valuntil, s.setconfig AS useconfig FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin;
  pg_stat_activity            | SELECT s.datid, d.datname, s.procpid, s.usesysid, u.rolname AS usename, s.application_name, s.client_addr, s.client_port, s.backend_start, s.xact_start, s.query_start, s.waiting, s.current_query FROM pg_database d, pg_stat_get_activity(NULL::integer) s(datid, procpid, usesysid, application_name, current_query, waiting, xact_start, query_start, backend_start, client_addr, client_port), pg_authid u WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
@@ -1333,7 +1334,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
  shoelace_obsolete           | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color))));
  street                      | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath);
  toyemp                      | SELECT emp.name, emp.age, emp.location, (12 * emp.salary) AS annualsal FROM emp;
-(55 rows)
+(56 rows)
 
 SELECT tablename, rulename, definition FROM pg_rules 
        ORDER BY tablename, rulename;
index 1d9e110..9596b0b 100644 (file)
@@ -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
diff --git a/src/test/regress/input/security_label.source b/src/test/regress/input/security_label.source
new file mode 100644 (file)
index 0000000..810a721
--- /dev/null
@@ -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;
diff --git a/src/test/regress/output/security_label.source b/src/test/regress/output/security_label.source
new file mode 100644 (file)
index 0000000..da8f49d
--- /dev/null
@@ -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)
+
index a05bfeb..3b99e86 100644 (file)
@@ -76,7 +76,7 @@ ignore: random
 # ----------
 test: select_into select_distinct select_distinct_on select_implicit select_having subselect union case join aggregates transactions random portals arrays btree_index hash_index update namespace prepared_xacts delete
 
-test: privileges
+test: privileges security_label
 test: misc
 # rules cannot run concurrently with any test that creates a view
 test: rules
index e2f8351..b348f0e 100644 (file)
@@ -88,6 +88,7 @@ test: delete
 test: namespace
 test: prepared_xacts
 test: privileges
+test: security_label
 test: misc
 test: rules
 test: select_views
index 0b7c2cf..46c8112 100644 (file)
@@ -4,4 +4,5 @@
 /create_function_2.sql
 /largeobject.sql
 /misc.sql
+/security_label.sql
 /tablespace.sql