[v9.2] SECURITY LABEL on shared database object

Started by Kohei Kaigaiover 14 years ago1 messages
#1Kohei Kaigai
Kohei.Kaigai@EMEA.NEC.COM
1 attachment(s)

The attached patch enables to assign security labels on shared database object
types (database, tablespace, role).
As pg_description stuff doing, it adds a new pg_shseclabel catalog to store related
labels. Its internal APIs are kept. If and when (Get|Set|Delete)SecurityLabel is
invoked for the shared catalogs, it references the pg_shseclabel instead of the
pg_seclabel.
This patch also contains pg_dump support, actual use cases (contrib/sepgsql),
regression tests and updates of sgml documentation.

Thanks,
--
NEC Europe Ltd, SAP Global Competence Center
KaiGai Kohei <kohei.kaigai@eu.nec.com>

Attachments:

pgsql-shared-security-label.1.patchapplication/octet-stream; name=pgsql-shared-security-label.1.patchDownload
 contrib/sepgsql/Makefile                     |    2 +-
 contrib/sepgsql/database.c                   |  105 +++++++++++++++
 contrib/sepgsql/hooks.c                      |    5 +
 contrib/sepgsql/label.c                      |   23 +++-
 contrib/sepgsql/schema.c                     |   18 ++--
 contrib/sepgsql/sepgsql.h                    |    6 +
 doc/src/sgml/catalogs.sgml                   |   65 ++++++++++
 doc/src/sgml/ref/security_label.sgml         |    3 +
 src/backend/catalog/Makefile                 |    2 +-
 src/backend/catalog/catalog.c                |    5 +
 src/backend/catalog/system_views.sql         |   32 +++++-
 src/backend/commands/seclabel.c              |  176 +++++++++++++++++++++++++-
 src/backend/parser/gram.y                    |    3 +
 src/bin/pg_dump/pg_dumpall.c                 |  108 ++++++++++++----
 src/include/catalog/indexing.h               |    3 +
 src/include/catalog/pg_shseclabel.h          |   41 ++++++
 src/include/catalog/toasting.h               |    3 +
 src/test/regress/expected/rules.out          |    6 +-
 src/test/regress/expected/sanity_check.out   |    3 +-
 src/test/regress/input/security_label.source |   32 +++++-
 20 files changed, 590 insertions(+), 51 deletions(-)

diff --git a/contrib/sepgsql/Makefile b/contrib/sepgsql/Makefile
index bc995dd..6f77239 100644
--- a/contrib/sepgsql/Makefile
+++ b/contrib/sepgsql/Makefile
@@ -2,7 +2,7 @@
 
 MODULE_big = sepgsql
 OBJS = hooks.o selinux.o label.o dml.o \
-	schema.o relation.o proc.o
+	database.o schema.o relation.o proc.o
 DATA_built = sepgsql.sql
 REGRESS = label dml misc
 EXTRA_CLEAN = -r tmp *.pp sepgsql-regtest.if sepgsql-regtest.fc
diff --git a/contrib/sepgsql/database.c b/contrib/sepgsql/database.c
new file mode 100644
index 0000000..efac6a2
--- /dev/null
+++ b/contrib/sepgsql/database.c
@@ -0,0 +1,105 @@
+/* -------------------------------------------------------------------------
+ *
+ * contrib/sepgsql/database.c
+ *
+ * Routines corresponding to database objects
+ *
+ * Copyright (c) 2010-2011, PostgreSQL Global Development Group
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/dependency.h"
+#include "catalog/pg_database.h"
+#include "commands/seclabel.h"
+#include "miscadmin.h"
+#include "utils/lsyscache.h"
+
+#include "sepgsql.h"
+
+/*
+ * sepgsql_database_post_create
+ *
+ * This routine assigns a default security label on a newly defined
+ * database
+ */
+void
+sepgsql_database_post_create(Oid databaseId)
+{
+	char		   *scontext = sepgsql_get_client_label();
+	char		   *tcontext;
+	char		   *ncontext;
+	ObjectAddress	object;
+
+	/*
+	 * Compute a default security label when we create a new database
+	 * object according to the specified template database.
+	 *
+	 * FIXME - In correctly, we'd like to inherit security label of
+	 * the template database object, however, we cannot see which was
+	 * used to as a template. So, we need a facility to deliver an
+	 * opaque datum from the prep-creation hook in the future fixups.
+	 */
+	object.classId = DatabaseRelationId;
+	object.objectId = TemplateDbOid;
+	object.objectSubId = 0;
+	tcontext = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG);
+
+	ncontext = sepgsql_compute_create(scontext, tcontext,
+									  SEPG_CLASS_DB_DATABASE);
+
+	/*
+	 * Assign the default security label on a new database
+	 */
+	object.classId = DatabaseRelationId;
+	object.objectId = databaseId;
+	object.objectSubId = 0;
+
+	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
+
+	pfree(ncontext);
+	pfree(tcontext);
+}
+
+/*
+ * sepgsql_database_relabel
+ *
+ * It checks privileges to relabel the supplied database
+ * by the `seclabel'.
+ */
+void
+sepgsql_database_relabel(Oid databaseId, const char *seclabel)
+{
+	char	   *scontext = sepgsql_get_client_label();
+	char	   *tcontext;
+	char	   *audit_name;
+
+	audit_name = getObjectDescriptionOids(DatabaseRelationId, databaseId);
+
+	/*
+	 * check db_database:{setattr relabelfrom} permission
+	 */
+	tcontext = sepgsql_get_label(DatabaseRelationId, databaseId, 0);
+
+	sepgsql_check_perms(scontext,
+						tcontext,
+						SEPG_CLASS_DB_DATABASE,
+						SEPG_DB_DATABASE__SETATTR |
+						SEPG_DB_DATABASE__RELABELFROM,
+						audit_name,
+						true);
+
+	/*
+	 * check db_database:{relabelto} permission
+	 */
+	sepgsql_check_perms(scontext,
+						seclabel,
+						SEPG_CLASS_DB_DATABASE,
+						SEPG_DB_DATABASE__RELABELTO,
+						audit_name,
+						true);
+
+	pfree(tcontext);
+	pfree(audit_name);
+}
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index 7797ccb..ca0d5f4 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -12,6 +12,7 @@
 
 #include "catalog/objectaccess.h"
 #include "catalog/pg_class.h"
+#include "catalog/pg_database.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_proc.h"
 #include "commands/seclabel.h"
@@ -125,6 +126,10 @@ sepgsql_object_access(ObjectAccessType access,
 		case OAT_POST_CREATE:
 			switch (classId)
 			{
+				case DatabaseRelationId:
+					sepgsql_database_post_create(objectId);
+					break;
+
 				case NamespaceRelationId:
 					sepgsql_schema_post_create(objectId);
 					break;
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c
index 669ee35..c0503f7 100644
--- a/contrib/sepgsql/label.c
+++ b/contrib/sepgsql/label.c
@@ -17,6 +17,7 @@
 #include "catalog/indexing.h"
 #include "catalog/pg_attribute.h"
 #include "catalog/pg_class.h"
+#include "catalog/pg_database.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_proc.h"
 #include "commands/dbcommands.h"
@@ -121,6 +122,9 @@ sepgsql_object_relabel(const ObjectAddress *object, const char *seclabel)
 	 */
 	switch (object->classId)
 	{
+		case DatabaseRelationId:
+			sepgsql_database_relabel(object->objectId, seclabel);
+			break;
 		case NamespaceRelationId:
 			sepgsql_schema_relabel(object->objectId, seclabel);
 			break;
@@ -315,6 +319,7 @@ exec_object_restorecon(struct selabel_handle * sehnd, Oid catalogId)
 							   SnapshotNow, 0, NULL);
 	while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
 	{
+		Form_pg_database datForm;
 		Form_pg_namespace nspForm;
 		Form_pg_class relForm;
 		Form_pg_attribute attForm;
@@ -330,6 +335,19 @@ exec_object_restorecon(struct selabel_handle * sehnd, Oid catalogId)
 		 */
 		switch (catalogId)
 		{
+			case DatabaseRelationId:
+				datForm = (Form_pg_database) GETSTRUCT(tuple);
+
+				objtype = SELABEL_DB_DATABASE;
+
+				objname = quote_object_name(NameStr(datForm->datname),
+											NULL, NULL, NULL);
+
+				object.classId = DatabaseRelationId;
+				object.objectId = HeapTupleGetOid(tuple);
+				object.objectSubId = 0;
+				break;
+
 			case NamespaceRelationId:
 				nspForm = (Form_pg_namespace) GETSTRUCT(tuple);
 
@@ -506,10 +524,7 @@ sepgsql_restorecon(PG_FUNCTION_ARGS)
 			   errmsg("SELinux: failed to initialize labeling handle: %m")));
 	PG_TRY();
 	{
-		/*
-		 * Right now, we have no support labeling on the shared database
-		 * objects, such as database, role, or tablespace.
-		 */
+		exec_object_restorecon(sehnd, DatabaseRelationId);
 		exec_object_restorecon(sehnd, NamespaceRelationId);
 		exec_object_restorecon(sehnd, RelationRelationId);
 		exec_object_restorecon(sehnd, AttributeRelationId);
diff --git a/contrib/sepgsql/schema.c b/contrib/sepgsql/schema.c
index 0de8997..131a4f6 100644
--- a/contrib/sepgsql/schema.c
+++ b/contrib/sepgsql/schema.c
@@ -11,8 +11,10 @@
 #include "postgres.h"
 
 #include "catalog/dependency.h"
+#include "catalog/pg_database.h"
 #include "catalog/pg_namespace.h"
 #include "commands/seclabel.h"
+#include "miscadmin.h"
 #include "utils/lsyscache.h"
 
 #include "sepgsql.h"
@@ -32,16 +34,15 @@ sepgsql_schema_post_create(Oid namespaceId)
 	ObjectAddress object;
 
 	/*
-	 * FIXME: Right now, we assume pg_database object has a fixed security
-	 * label, because pg_seclabel does not support to store label of shared
-	 * database objects.
+	 * Compute a default security label when we create a new schema
+	 * object under the working database.
 	 */
-	tcontext = "system_u:object_r:sepgsql_db_t:s0";
+	object.classId = DatabaseRelationId;
+	object.objectId = MyDatabaseId;
+	object.objectSubId = 0;
+
+	tcontext = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG);
 
-	/*
-	 * Compute a default security label when we create a new schema object
-	 * under the working database.
-	 */
 	ncontext = sepgsql_compute_create(scontext, tcontext,
 									  SEPG_CLASS_DB_SCHEMA);
 
@@ -54,6 +55,7 @@ sepgsql_schema_post_create(Oid namespaceId)
 	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
 
 	pfree(ncontext);
+	pfree(tcontext);
 }
 
 /*
diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h
index 71688ab..5045ddd 100644
--- a/contrib/sepgsql/sepgsql.h
+++ b/contrib/sepgsql/sepgsql.h
@@ -267,6 +267,12 @@ extern Datum sepgsql_restorecon(PG_FUNCTION_ARGS);
 extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort);
 
 /*
+ * database.c
+ */
+extern void sepgsql_database_post_create(Oid databaseId);
+extern void sepgsql_database_relabel(Oid databaseId, const char *seclabel);
+
+/*
  * schema.c
  */
 extern void sepgsql_schema_post_create(Oid namespaceId);
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 7b62818..707fad8 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -239,6 +239,11 @@
      </row>
 
      <row>
+      <entry><link linkend="catalog-pg-shseclabel"><structname>pg_shseclabel</structname></link></entry>
+      <entry>security labels on shared database objects</entry>
+     </row>
+
+     <row>
       <entry><link linkend="catalog-pg-statistic"><structname>pg_statistic</structname></link></entry>
       <entry>planner statistics</entry>
      </row>
@@ -4672,6 +4677,13 @@
    <xref linkend="sql-security-label"> statement.
   </para>
 
+  <para>
+   See also <link linkend="catalog-pg-shseclabel"><structname>pg_shseclabel</structname></link>,
+   which performs a similar function for security labels of database objects that
+   are shared across a database cluster.
+  </para>
+
+
   <table>
    <title><structname>pg_seclabel</structname> Columns</title>
 
@@ -4950,6 +4962,59 @@
 
  </sect1>
 
+ <sect1 id="catalog-pg-shseclabel">
+  <title><structname>pg_shseclabel</structname></title>
+
+  <indexterm zone="catalog-pg-shseclabel">
+   <primary>pg_shseclabel</primary>
+  </indexterm>
+
+  <para>
+   The catalog <structname>pg_shseclabel</structname> stores security
+   labels on shared database objects across a database cluster.
+   See the <xref linkend="sql-security-label"> statement.
+  </para>
+
+  <table>
+   <title><structname>pg_shseclabel</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>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-statistic">
   <title><structname>pg_statistic</structname></title>
diff --git a/doc/src/sgml/ref/security_label.sgml b/doc/src/sgml/ref/security_label.sgml
index 8a01b94..78e3f45 100644
--- a/doc/src/sgml/ref/security_label.sgml
+++ b/doc/src/sgml/ref/security_label.sgml
@@ -26,13 +26,16 @@ 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> [, ...] ) |
+  DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
   DOMAIN <replaceable class="PARAMETER">object_name</replaceable> |
   FOREIGN TABLE <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> |
+  ROLE <replaceable class="PARAMETER">object_name</replaceable> |
   SCHEMA <replaceable class="PARAMETER">object_name</replaceable> |
   SEQUENCE <replaceable class="PARAMETER">object_name</replaceable> |
+  TABLESPACE <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>'
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 3a83461..7e0b7d6 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -39,7 +39,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
 	pg_ts_parser.h pg_ts_template.h pg_extension.h \
 	pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
 	pg_foreign_table.h \
-	pg_default_acl.h pg_seclabel.h pg_collation.h \
+	pg_default_acl.h pg_seclabel.h pg_shseclabel.h pg_collation.h \
 	toasting.h indexing.h \
     )
 
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index cbce007..a8995de 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -34,6 +34,7 @@
 #include "catalog/pg_db_role_setting.h"
 #include "catalog/pg_shdepend.h"
 #include "catalog/pg_shdescription.h"
+#include "catalog/pg_shseclabel.h"
 #include "catalog/pg_tablespace.h"
 #include "catalog/toasting.h"
 #include "miscadmin.h"
@@ -380,6 +381,7 @@ IsSharedRelation(Oid relationId)
 		relationId == PLTemplateRelationId ||
 		relationId == SharedDescriptionRelationId ||
 		relationId == SharedDependRelationId ||
+		relationId == SharedSecLabelRelationId ||
 		relationId == TableSpaceRelationId ||
 		relationId == DbRoleSettingRelationId)
 		return true;
@@ -394,6 +396,7 @@ IsSharedRelation(Oid relationId)
 		relationId == SharedDescriptionObjIndexId ||
 		relationId == SharedDependDependerIndexId ||
 		relationId == SharedDependReferenceIndexId ||
+		relationId == SharedSecLabelObjectIndexId ||
 		relationId == TablespaceOidIndexId ||
 		relationId == TablespaceNameIndexId ||
 		relationId == DbRoleSettingDatidRolidIndexId)
@@ -403,6 +406,8 @@ IsSharedRelation(Oid relationId)
 		relationId == PgDatabaseToastIndex ||
 		relationId == PgShdescriptionToastTable ||
 		relationId == PgShdescriptionToastIndex ||
+		relationId == PgShSecLabelToastTable ||
+		relationId == PgShSecLabelToastIndex ||
 		relationId == PgDbRoleSettingToastTable ||
 		relationId == PgDbRoleSettingToastIndex)
 		return true;
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 325d452..2253ca8 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -283,7 +283,37 @@ FROM
 	pg_seclabel l
 	JOIN pg_namespace nsp ON l.classoid = nsp.tableoid AND l.objoid = nsp.oid
 WHERE
-	l.objsubid = 0;
+	l.objsubid = 0
+UNION ALL
+SELECT
+	l.objoid, l.classoid, 0::int4 AS objsubid,
+	'database'::text AS objtype,
+	NULL::oid AS objnamespace,
+	quote_ident(dat.datname) AS objname,
+	l.provider, l.label
+FROM
+	pg_shseclabel l
+	JOIN pg_database dat ON l.classoid = dat.tableoid AND l.objoid = dat.oid
+UNION ALL
+SELECT
+	l.objoid, l.classoid, 0::int4 AS objsubid,
+	'tablespace'::text AS objtype,
+	NULL::oid AS objnamespace,
+	quote_ident(spc.spcname) AS objname,
+	l.provider, l.label
+FROM
+	pg_shseclabel l
+	JOIN pg_tablespace spc ON l.classoid = spc.tableoid AND l.objoid = spc.oid
+UNION ALL
+SELECT
+	l.objoid, l.classoid, 0::int4 AS objsubid,
+	'role'::text AS objtype,
+	NULL::oid AS objnamespace,
+	quote_ident(rol.rolname) AS objname,
+	l.provider, l.label
+FROM
+	pg_shseclabel l
+	JOIN pg_authid rol ON l.classoid = rol.tableoid AND l.objoid = rol.oid;
 
 CREATE VIEW pg_settings AS
     SELECT * FROM pg_show_all_settings() AS A;
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 7afb713..63d2818 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -16,6 +16,7 @@
 #include "catalog/indexing.h"
 #include "catalog/namespace.h"
 #include "catalog/pg_seclabel.h"
+#include "catalog/pg_shseclabel.h"
 #include "commands/seclabel.h"
 #include "miscadmin.h"
 #include "utils/acl.h"
@@ -134,6 +135,54 @@ ExecSecLabelStmt(SecLabelStmt *stmt)
 }
 
 /*
+ * GetSharedSecurityLabel is a helper function of GetSecurityLabel to
+ * handle shared database objects.
+ */
+static char *
+GetSharedSecurityLabel(const ObjectAddress *object, const char *provider)
+{
+	Relation	pg_shseclabel;
+	ScanKeyData	keys[3];
+	SysScanDesc	scan;
+	HeapTuple	tuple;
+	Datum		datum;
+	bool		isnull;
+	char	   *seclabel = NULL;
+
+	ScanKeyInit(&keys[0],
+				Anum_pg_shseclabel_objoid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(object->objectId));
+	ScanKeyInit(&keys[1],
+				Anum_pg_shseclabel_classoid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(object->classId));
+	ScanKeyInit(&keys[2],
+				Anum_pg_shseclabel_provider,
+				BTEqualStrategyNumber, F_TEXTEQ,
+				CStringGetTextDatum(provider));
+
+	pg_shseclabel = heap_open(SharedSecLabelRelationId, AccessShareLock);
+
+	scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, true,
+							  SnapshotNow, 3, keys);
+
+	tuple = systable_getnext(scan);
+    if (HeapTupleIsValid(tuple))
+	{
+		datum = heap_getattr(tuple, Anum_pg_shseclabel_label,
+							 RelationGetDescr(pg_shseclabel), &isnull);
+		if (!isnull)
+			seclabel = TextDatumGetCString(datum);
+	}
+	systable_endscan(scan);
+
+	heap_close(pg_shseclabel, AccessShareLock);
+
+	return seclabel;
+}
+
+/*
  * GetSecurityLabel returns the security label for a database object for a
  * given provider, or NULL if there is no such label.
  */
@@ -148,7 +197,9 @@ GetSecurityLabel(const ObjectAddress *object, const char *provider)
 	bool		isnull;
 	char	   *seclabel = NULL;
 
-	Assert(!IsSharedRelation(object->classId));
+	/* Special handling for shared database objects */
+	if (IsSharedRelation(object->classId))
+		return GetSharedSecurityLabel(object, provider);
 
 	ScanKeyInit(&keys[0],
 				Anum_pg_seclabel_objoid,
@@ -187,6 +238,84 @@ GetSecurityLabel(const ObjectAddress *object, const char *provider)
 	return seclabel;
 }
 
+/* 
+ * SetSharedSecurityLabel is a helper function of SetSecurityLabel to
+ * handle shared database objects.
+ */
+static void
+SetSharedSecurityLabel(const ObjectAddress *object,
+					   const char *provider, const char *label)
+{
+	Relation	pg_shseclabel;
+	ScanKeyData	keys[4];
+	SysScanDesc	scan;
+	HeapTuple	oldtup;
+	HeapTuple	newtup = NULL;
+	Datum		values[Natts_pg_shseclabel];
+	bool		nulls[Natts_pg_shseclabel];
+	bool		replaces[Natts_pg_shseclabel];
+
+	/* Prepare to form or update a tuple, if necessary. */
+	memset(nulls, false, sizeof(nulls));
+	memset(replaces, false, sizeof(replaces));
+	values[Anum_pg_shseclabel_objoid - 1] = ObjectIdGetDatum(object->objectId);
+	values[Anum_pg_shseclabel_classoid - 1] = ObjectIdGetDatum(object->classId);
+	values[Anum_pg_shseclabel_provider - 1] = CStringGetTextDatum(provider);
+	if (label != NULL)
+		values[Anum_pg_shseclabel_label - 1] = CStringGetTextDatum(label);
+
+	/* Use the index to search for a matching old tuple */
+	ScanKeyInit(&keys[0],
+				Anum_pg_shseclabel_objoid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(object->objectId));
+	ScanKeyInit(&keys[1],
+				Anum_pg_shseclabel_classoid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(object->classId));
+	ScanKeyInit(&keys[2],
+				Anum_pg_shseclabel_provider,
+				BTEqualStrategyNumber, F_TEXTEQ,
+				CStringGetTextDatum(provider));
+
+	pg_shseclabel = heap_open(SharedSecLabelRelationId, RowExclusiveLock);
+
+	scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, true,
+							  SnapshotNow, 3, keys);
+
+	oldtup = systable_getnext(scan);
+	if (HeapTupleIsValid(oldtup))
+	{
+		if (label == NULL)
+			simple_heap_delete(pg_shseclabel, &oldtup->t_self);
+		else
+		{
+			replaces[Anum_pg_shseclabel_label - 1] = true;
+			newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_shseclabel),
+									   values, nulls, replaces);
+			simple_heap_update(pg_shseclabel, &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_shseclabel),
+								 values, nulls);
+		simple_heap_insert(pg_shseclabel, newtup);
+	}
+
+	/* Update indexes, if necessary */
+	if (newtup != NULL)
+	{
+		CatalogUpdateIndexes(pg_shseclabel, newtup);
+		heap_freetuple(newtup);
+	}
+
+	heap_close(pg_shseclabel, RowExclusiveLock);
+}
+
 /*
  * SetSecurityLabel attempts to set the security label for the specified
  * provider on the specified object to the given value.  NULL means that any
@@ -205,8 +334,12 @@ SetSecurityLabel(const ObjectAddress *object,
 	bool		nulls[Natts_pg_seclabel];
 	bool		replaces[Natts_pg_seclabel];
 
-	/* Security labels on shared objects are not supported. */
-	Assert(!IsSharedRelation(object->classId));
+	/* Special handling for security labels on shared objects */
+	if (IsSharedRelation(object->classId))
+	{
+		SetSharedSecurityLabel(object, provider, label);
+		return;
+	}
 
 	/* Prepare to form or update a tuple, if necessary. */
 	memset(nulls, false, sizeof(nulls));
@@ -275,6 +408,38 @@ SetSecurityLabel(const ObjectAddress *object,
 }
 
 /*
+ * DeleteSharedSecurityLabel is a helper function of DeleteSecurityLabel
+ * to handle shared database objects.
+ */
+static void
+DeleteSharedSecurityLabel(const ObjectAddress *object)
+{
+	Relation	pg_shseclabel;
+	ScanKeyData	skey[2];
+	SysScanDesc	scan;
+	HeapTuple	oldtup;
+
+	ScanKeyInit(&skey[0],
+				Anum_pg_shseclabel_objoid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(object->objectId));
+	ScanKeyInit(&skey[1],
+				Anum_pg_shseclabel_classoid,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(object->classId));
+
+	pg_shseclabel = heap_open(SharedSecLabelRelationId, RowExclusiveLock);
+
+	scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, true,
+							  SnapshotNow, 2, skey);
+	while (HeapTupleIsValid(oldtup = systable_getnext(scan)))
+		simple_heap_delete(pg_shseclabel, &oldtup->t_self);
+	systable_endscan(scan);
+
+	heap_close(pg_shseclabel, RowExclusiveLock);
+}
+
+/*
  * DeleteSecurityLabel removes all security labels for an object (and any
  * sub-objects, if applicable).
  */
@@ -287,9 +452,12 @@ DeleteSecurityLabel(const ObjectAddress *object)
 	HeapTuple	oldtup;
 	int			nkeys;
 
-	/* Security labels on shared objects are not supported. */
+	/* Special handling for security labels on shared objects */
 	if (IsSharedRelation(object->classId))
+	{
+		DeleteSharedSecurityLabel(object);
 		return;
+	}
 
 	ScanKeyInit(&skey[0],
 				Anum_pg_seclabel_objoid,
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dd95961..6eac2c2 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -5073,11 +5073,14 @@ opt_provider:	FOR ColId_or_Sconst	{ $$ = $2; }
 
 security_label_type:
 			COLUMN								{ $$ = OBJECT_COLUMN; }
+			| DATABASE							{ $$ = OBJECT_DATABASE; }
 			| FOREIGN TABLE						{ $$ = OBJECT_FOREIGN_TABLE; }
 			| SCHEMA							{ $$ = OBJECT_SCHEMA; }
 			| SEQUENCE							{ $$ = OBJECT_SEQUENCE; }
 			| TABLE								{ $$ = OBJECT_TABLE; }
 			| DOMAIN_P							{ $$ = OBJECT_TYPE; }
+			| ROLE								{ $$ = OBJECT_ROLE; }
+			| TABLESPACE						{ $$ = OBJECT_TABLESPACE; }
 			| TYPE_P							{ $$ = OBJECT_TYPE; }
 			| VIEW								{ $$ = OBJECT_VIEW; }
 		;
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 41a3307..62f6e91 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -52,6 +52,9 @@ static void dumpTimestamp(char *msg);
 static void doShellQuoting(PQExpBuffer buf, const char *str);
 
 static int	runPgDump(const char *dbname);
+static void buildShSecLabels(PGconn *conn, const char *objtype,
+							 uint32 objectId, PQExpBuffer buffer,
+							 const char *target, const char *objname);
 static PGconn *connectDatabase(const char *dbname, const char *pghost, const char *pgport,
 	  const char *pguser, enum trivalue prompt_password, bool fail_on_error);
 static PGresult *executeQuery(PGconn *conn, const char *query);
@@ -718,15 +721,15 @@ dumpRoles(PGconn *conn)
 	for (i = 0; i < PQntuples(res); i++)
 	{
 		const char *rolename;
+		Oid			auth_oid;
 
+		auth_oid = atooid(PQgetvalue(res, i, i_oid));
 		rolename = PQgetvalue(res, i, i_rolname);
 
 		resetPQExpBuffer(buf);
 
 		if (binary_upgrade)
 		{
-			Oid			auth_oid = atooid(PQgetvalue(res, i, i_oid));
-
 			appendPQExpBuffer(buf, "\n-- For binary upgrade, must preserve pg_authid.oid\n");
 			appendPQExpBuffer(buf,
 							  "SELECT binary_upgrade.set_next_pg_authid_oid('%u'::pg_catalog.oid);\n\n",
@@ -796,6 +799,10 @@ dumpRoles(PGconn *conn)
 			appendPQExpBuffer(buf, ";\n");
 		}
 
+		if (!no_security_label && server_version >= 90200)
+			buildShSecLabels(conn, "role", auth_oid,
+							 buf, "ROLE", fmtId(rolename));
+
 		fprintf(OPF, "%s", buf->data);
 
 		if (server_version >= 70300)
@@ -981,7 +988,7 @@ dumpTablespaces(PGconn *conn)
 	 * pg_xxx)
 	 */
 	if (server_version >= 90000)
-		res = executeQuery(conn, "SELECT spcname, "
+		res = executeQuery(conn, "SELECT oid, spcname, "
 						 "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, "
 						   "spclocation, spcacl, "
 						   "array_to_string(spcoptions, ', '),"
@@ -990,7 +997,7 @@ dumpTablespaces(PGconn *conn)
 						   "WHERE spcname !~ '^pg_' "
 						   "ORDER BY 1");
 	else if (server_version >= 80200)
-		res = executeQuery(conn, "SELECT spcname, "
+		res = executeQuery(conn, "SELECT oid, spcname, "
 						 "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, "
 						   "spclocation, spcacl, null, "
 						"pg_catalog.shobj_description(oid, 'pg_tablespace') "
@@ -998,7 +1005,7 @@ dumpTablespaces(PGconn *conn)
 						   "WHERE spcname !~ '^pg_' "
 						   "ORDER BY 1");
 	else
-		res = executeQuery(conn, "SELECT spcname, "
+		res = executeQuery(conn, "SELECT oid, spcname, "
 						 "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, "
 						   "spclocation, spcacl, "
 						   "null, null "
@@ -1012,12 +1019,13 @@ dumpTablespaces(PGconn *conn)
 	for (i = 0; i < PQntuples(res); i++)
 	{
 		PQExpBuffer buf = createPQExpBuffer();
-		char	   *spcname = PQgetvalue(res, i, 0);
-		char	   *spcowner = PQgetvalue(res, i, 1);
-		char	   *spclocation = PQgetvalue(res, i, 2);
-		char	   *spcacl = PQgetvalue(res, i, 3);
-		char	   *spcoptions = PQgetvalue(res, i, 4);
-		char	   *spccomment = PQgetvalue(res, i, 5);
+		uint32		spcoid = atooid(PQgetvalue(res, i, 0));
+		char	   *spcname = PQgetvalue(res, i, 1);
+		char	   *spcowner = PQgetvalue(res, i, 2);
+		char	   *spclocation = PQgetvalue(res, i, 3);
+		char	   *spcacl = PQgetvalue(res, i, 4);
+		char	   *spcoptions = PQgetvalue(res, i, 5);
+		char	   *spccomment = PQgetvalue(res, i, 6);
 		char	   *fspcname;
 
 		/* needed for buildACLCommands() */
@@ -1051,6 +1059,10 @@ dumpTablespaces(PGconn *conn)
 			appendPQExpBuffer(buf, ";\n");
 		}
 
+		if (!no_security_label && server_version >= 90200)
+			buildShSecLabels(conn, "tablespace", spcoid,
+							 buf, "TABLESPACE", fspcname);
+
 		fprintf(OPF, "%s", buf->data);
 
 		free(fspcname);
@@ -1178,7 +1190,7 @@ dumpCreateDB(PGconn *conn)
 	/* Now collect all the information about databases to dump */
 	if (server_version >= 80400)
 		res = executeQuery(conn,
-						   "SELECT datname, "
+						   "SELECT d.oid, datname, "
 						   "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), "
 						   "pg_encoding_to_char(d.encoding), "
 						   "datcollate, datctype, datfrozenxid, "
@@ -1188,7 +1200,7 @@ dumpCreateDB(PGconn *conn)
 						   "WHERE datallowconn ORDER BY 1");
 	else if (server_version >= 80100)
 		res = executeQuery(conn,
-						   "SELECT datname, "
+						   "SELECT d.oid, datname, "
 						   "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), "
 						   "pg_encoding_to_char(d.encoding), "
 		   "null::text AS datcollate, null::text AS datctype, datfrozenxid, "
@@ -1198,7 +1210,7 @@ dumpCreateDB(PGconn *conn)
 						   "WHERE datallowconn ORDER BY 1");
 	else if (server_version >= 80000)
 		res = executeQuery(conn,
-						   "SELECT datname, "
+						   "SELECT d.oid, datname, "
 						   "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), "
 						   "pg_encoding_to_char(d.encoding), "
 		   "null::text AS datcollate, null::text AS datctype, datfrozenxid, "
@@ -1208,7 +1220,7 @@ dumpCreateDB(PGconn *conn)
 						   "WHERE datallowconn ORDER BY 1");
 	else if (server_version >= 70300)
 		res = executeQuery(conn,
-						   "SELECT datname, "
+						   "SELECT d.oid, datname, "
 						   "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), "
 						   "pg_encoding_to_char(d.encoding), "
 		   "null::text AS datcollate, null::text AS datctype, datfrozenxid, "
@@ -1218,7 +1230,7 @@ dumpCreateDB(PGconn *conn)
 						   "WHERE datallowconn ORDER BY 1");
 	else if (server_version >= 70100)
 		res = executeQuery(conn,
-						   "SELECT datname, "
+						   "SELECT d.oid, datname, "
 						   "coalesce("
 					"(select usename from pg_shadow where usesysid=datdba), "
 						   "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), "
@@ -1235,7 +1247,7 @@ dumpCreateDB(PGconn *conn)
 		 * with getting a NULL by not printing any OWNER clause.
 		 */
 		res = executeQuery(conn,
-						   "SELECT datname, "
+						   "SELECT d.oid, datname, "
 					"(select usename from pg_shadow where usesysid=datdba), "
 						   "pg_encoding_to_char(d.encoding), "
 						   "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, "
@@ -1248,16 +1260,17 @@ dumpCreateDB(PGconn *conn)
 
 	for (i = 0; i < PQntuples(res); i++)
 	{
-		char	   *dbname = PQgetvalue(res, i, 0);
-		char	   *dbowner = PQgetvalue(res, i, 1);
-		char	   *dbencoding = PQgetvalue(res, i, 2);
-		char	   *dbcollate = PQgetvalue(res, i, 3);
-		char	   *dbctype = PQgetvalue(res, i, 4);
-		uint32		dbfrozenxid = atooid(PQgetvalue(res, i, 5));
-		char	   *dbistemplate = PQgetvalue(res, i, 6);
-		char	   *dbacl = PQgetvalue(res, i, 7);
-		char	   *dbconnlimit = PQgetvalue(res, i, 8);
-		char	   *dbtablespace = PQgetvalue(res, i, 9);
+		uint32		dboid = atooid(PQgetvalue(res, i, 0));
+		char	   *dbname = PQgetvalue(res, i, 1);
+		char	   *dbowner = PQgetvalue(res, i, 2);
+		char	   *dbencoding = PQgetvalue(res, i, 3);
+		char	   *dbcollate = PQgetvalue(res, i, 4);
+		char	   *dbctype = PQgetvalue(res, i, 5);
+		uint32		dbfrozenxid = atooid(PQgetvalue(res, i, 6));
+		char	   *dbistemplate = PQgetvalue(res, i, 7);
+		char	   *dbacl = PQgetvalue(res, i, 8);
+		char	   *dbconnlimit = PQgetvalue(res, i, 9);
+		char	   *dbtablespace = PQgetvalue(res, i, 10);
 		char	   *fdbname;
 
 		fdbname = strdup(fmtId(dbname));
@@ -1344,6 +1357,10 @@ dumpCreateDB(PGconn *conn)
 			exit(1);
 		}
 
+		if (!no_security_label && server_version >= 90200)
+			buildShSecLabels(conn, "database", dboid,
+							 buf, "DATABASE", fdbname);
+
 		fprintf(OPF, "%s", buf->data);
 
 		if (server_version >= 70300)
@@ -1615,6 +1632,43 @@ runPgDump(const char *dbname)
 	return ret;
 }
 
+/*
+ * buildShSecLabels
+ *
+ * Build SECURITY LABEL command(s) for an shared object
+ *
+ * The caller has to provide object type and identifier to select security
+ * labels from pg_seclabels system view.
+ */
+static void
+buildShSecLabels(PGconn *conn, const char *objtype, uint32 objectId,
+				 PQExpBuffer buffer, const char *target, const char *objname)
+{
+	PQExpBuffer	sql = createPQExpBuffer();
+	PGresult   *res;
+	int			i;
+
+	appendPQExpBuffer(sql,
+					  "SELECT provider, label FROM pg_seclabels "
+					  "WHERE objtype = '%s' AND objoid = %u",
+					  objtype, objectId);
+
+	res = executeQuery(conn, sql->data);
+
+	for (i = 0; i < PQntuples(res); i++)
+    {
+		char   *provider = PQgetvalue(res, i, 0);
+		char   *label = PQgetvalue(res, i, 1);
+
+		appendPQExpBuffer(buffer,
+						  "SECURITY LABEL FOR %s ON %s %s IS ",
+						  fmtId(provider), target, objname);
+		appendStringLiteralConn(buffer, label, conn);
+        appendPQExpBuffer(buffer, ";\n");
+	}
+	PQclear(res);
+	destroyPQExpBuffer(sql);
+}
 
 /*
  * Make a database connection with the given parameters.  An
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 4118e64..9a8e6ff 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -294,6 +294,9 @@ DECLARE_UNIQUE_INDEX(pg_db_role_setting_databaseid_rol_index, 2965, on pg_db_rol
 DECLARE_UNIQUE_INDEX(pg_seclabel_object_index, 3597, on pg_seclabel using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops, provider text_ops));
 #define SecLabelObjectIndexId				3597
 
+DECLARE_UNIQUE_INDEX(pg_shseclabel_object_index, 3593, on pg_shseclabel using btree(objoid oid_ops, classoid oid_ops, provider text_ops));
+#define SharedSecLabelObjectIndexId			3593
+
 DECLARE_UNIQUE_INDEX(pg_extension_oid_index, 3080, on pg_extension using btree(oid oid_ops));
 #define ExtensionOidIndexId 3080
 
diff --git a/src/include/catalog/pg_shseclabel.h b/src/include/catalog/pg_shseclabel.h
new file mode 100644
index 0000000..d1b07da
--- /dev/null
+++ b/src/include/catalog/pg_shseclabel.h
@@ -0,0 +1,41 @@
+/* -------------------------------------------------------------------------
+ *
+ * pg_shseclabel.h
+ *    definition of the system "security label" relation (pg_shseclabel)
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * -------------------------------------------------------------------------
+ */
+#ifndef PG_SHSECLABEL_H
+#define PG_SHSECLABEL_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ *		pg_shseclabel definition. cpp turns this into
+ *		typedef struct FormData_pg_shseclabel
+ * ----------------
+ */
+#define SharedSecLabelRelationId		3592
+
+CATALOG(pg_shseclabel,3592) BKI_SHARED_RELATION BKI_WITHOUT_OIDS
+{
+	Oid		objoid;		/* OID of the shared object itself */
+	Oid		classoid;	/* OID of table containing the shared object */
+	text	provider;	/* name of label provider */
+	text	label;		/* security label of the object */
+} FormData_pg_shseclabel;
+
+/* ----------------
+ *		compiler constants for pg_shseclabel
+ * ----------------
+ */
+#define Natts_pg_shseclabel				4
+#define Anum_pg_shseclabel_objoid		1
+#define Anum_pg_shseclabel_classoid		2
+#define Anum_pg_shseclabel_provider		3
+#define Anum_pg_shseclabel_label		4
+
+#endif	/* PG_SHSECLABEL_H */
diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h
index de3623ac..41cc0a1 100644
--- a/src/include/catalog/toasting.h
+++ b/src/include/catalog/toasting.h
@@ -56,6 +56,9 @@ DECLARE_TOAST(pg_database, 2844, 2845);
 DECLARE_TOAST(pg_shdescription, 2846, 2847);
 #define PgShdescriptionToastTable 2846
 #define PgShdescriptionToastIndex 2847
+DECLARE_TOAST(pg_shseclabel, 3594, 3595);
+#define PgShSecLabelToastTable	3594
+#define PgShSecLabelToastIndex	3595
 DECLARE_TOAST(pg_db_role_setting, 2966, 2967);
 #define PgDbRoleSettingToastTable 2966
 #define PgDbRoleSettingToastIndex 2967
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 20cdc39..63253dc 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -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_available_extension_versions | SELECT e.name, e.version, (x.extname IS NOT NULL) AS installed, e.superuser, e.relocatable, e.schema, e.requires, e.comment FROM (pg_available_extension_versions() e(name, version, superuser, relocatable, schema, requires, comment) LEFT JOIN pg_extension x ON (((e.name = x.extname) AND (e.version = x.extversion))));
  pg_available_extensions         | SELECT e.name, e.default_version, x.extversion AS installed_version, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname)));
@@ -1289,7 +1289,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.rolreplication, 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 WHEN (rel.relkind = 'f'::"char") THEN 'foreign table'::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_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 WHEN (rel.relkind = 'f'::"char") THEN 'foreign table'::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)) UNION ALL SELECT l.objoid, l.classoid, 0 AS objsubid, 'database'::text AS objtype, NULL::oid AS objnamespace, quote_ident((dat.datname)::text) AS objname, l.provider, l.label FROM (pg_shseclabel l JOIN pg_database dat ON (((l.classoid = dat.tableoid) AND (l.objoid = dat.oid))))) UNION ALL SELECT l.objoid, l.classoid, 0 AS objsubid, 'tablespace'::text AS objtype, NULL::oid AS objnamespace, quote_ident((spc.spcname)::text) AS objname, l.provider, l.label FROM (pg_shseclabel l JOIN pg_tablespace spc ON (((l.classoid = spc.tableoid) AND (l.objoid = spc.oid))))) UNION ALL SELECT l.objoid, l.classoid, 0 AS objsubid, 'role'::text AS objtype, NULL::oid AS objnamespace, quote_ident((rol.rolname)::text) AS objname, l.provider, l.label FROM (pg_shseclabel l JOIN pg_authid rol ON (((l.classoid = rol.tableoid) AND (l.objoid = rol.oid))));
  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.rolreplication AS userepl, 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_hostname, 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_hostname, client_port), pg_authid u WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid));
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index ab9e891..d42b0ea 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -120,6 +120,7 @@ SELECT relname, relhasindex
  pg_seclabel             | t
  pg_shdepend             | t
  pg_shdescription        | t
+ pg_shseclabel           | t
  pg_statistic            | t
  pg_tablespace           | t
  pg_trigger              | t
@@ -157,7 +158,7 @@ SELECT relname, relhasindex
  timetz_tbl              | f
  tinterval_tbl           | f
  varchar_tbl             | f
-(146 rows)
+(147 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
index 810a721..e556d23 100644
--- a/src/test/regress/input/security_label.source
+++ b/src/test/regress/input/security_label.source
@@ -12,7 +12,7 @@ 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_user1 WITH CREATEROLE;
 CREATE USER seclabel_user2;
 
 CREATE TABLE seclabel_tbl1 (a int, b text);
@@ -34,6 +34,11 @@ 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
 
+SECURITY LABEL ON ROLE seclabel_user1 IS 'classified';			-- fail
+SECURITY LABEL FOR 'dummy' ON ROLE seclabel_user1 IS 'classified';		-- fail
+SECURITY LABEL ON ROLE seclabel_user1 IS '...invalid label...';		-- fail
+SECURITY LABEL ON ROLE seclabel_user3 IS 'unclassified';			-- fail
+
 -- Load dummy external security provider
 LOAD '@libdir@/dummy_seclabel@DLSUFFIX@';
 
@@ -55,6 +60,26 @@ SET SESSION AUTHORIZATION seclabel_user2;
 SECURITY LABEL ON TABLE seclabel_tbl1 IS 'unclassified';		-- fail
 SECURITY LABEL ON TABLE seclabel_tbl2 IS 'classified';			-- OK
 
+--
+-- Test for shared database object
+--
+SET SESSION AUTHORIZATION seclabel_user1;
+
+SECURITY LABEL ON ROLE seclabel_user1 IS 'classified';			-- OK
+SECURITY LABEL ON ROLE seclabel_user1 IS '...invalid label...';	-- fail
+SECURITY LABEL FOR 'dummy' ON ROLE seclabel_user2 IS 'unclassified';	-- OK
+SECURITY LABEL FOR 'unknown_seclabel' ON ROLE seclabel_user1 IS 'unclassified';	-- fail
+SECURITY LABEL ON ROLE seclabel_user1 IS 'secret';	-- fail (not superuser)
+SECURITY LABEL ON ROLE seclabel_user3 IS 'unclassified';	-- fail (not found)
+
+SET SESSION AUTHORIZATION seclabel_user2;
+SECURITY LABEL ON ROLE seclabel_user2 IS 'unclassified';	-- fail (not privileged)
+
+RESET SESSION AUTHORIZATION;
+
+--
+-- Test for various types of object
+--
 RESET SESSION AUTHORIZATION;
 
 SECURITY LABEL ON TABLE seclabel_tbl1 IS 'top secret';			-- OK
@@ -63,12 +88,17 @@ 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
+SECURITY LABEL ON DATABASE postgres IS 'secret';				-- OK
+SECURITY LABEL ON TABLESPACE pg_default IS 'classified';		-- OK
 
 SELECT objtype, objname, provider, label FROM pg_seclabels
 	ORDER BY objtype, objname;
 
+-- clean up labels
 SECURITY LABEL ON LANGUAGE plpgsql IS NULL;						-- OK
 SECURITY LABEL ON SCHEMA public IS NULL;						-- OK
+SECURITY LABEL ON DATABASE postgres IS NULL;					-- OK
+SECURITY LABEL ON TABLESPACE pg_default IS NULL;				-- OK
 
 -- clean up objects
 DROP FUNCTION seclabel_four();