From 968bc6fac91d6aaca594488ab85c179b686cbbdd Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Sun, 23 Jan 2011 20:44:48 -0500
Subject: [PATCH] sepgsql, an SE-Linux integration for PostgreSQL

This is still pretty rough - among other things, the documentation
needs work, and the messages need a visit from the style police -
but this gets the basic framework in place.

KaiGai Kohei
---
 configure                          | 122 +++++
 configure.in                       |  13 +
 contrib/Makefile                   |   4 +
 contrib/README                     |   4 +
 contrib/sepgsql/.gitignore         |   1 +
 contrib/sepgsql/Makefile           |  25 +
 contrib/sepgsql/dml.c              | 353 +++++++++++++++
 contrib/sepgsql/expected/dml.out   | 182 ++++++++
 contrib/sepgsql/expected/label.out | 109 +++++
 contrib/sepgsql/expected/misc.out  |   5 +
 contrib/sepgsql/hooks.c            | 446 ++++++++++++++++++
 contrib/sepgsql/label.c            | 477 +++++++++++++++++++
 contrib/sepgsql/launcher           |  52 +++
 contrib/sepgsql/proc.c             | 158 +++++++
 contrib/sepgsql/relation.c         | 267 +++++++++++
 contrib/sepgsql/schema.c           |  98 ++++
 contrib/sepgsql/selinux.c          | 631 ++++++++++++++++++++++++++
 contrib/sepgsql/sepgsql-regtest.te |  59 +++
 contrib/sepgsql/sepgsql.h          | 288 ++++++++++++
 contrib/sepgsql/sepgsql.sql.in     |  36 ++
 contrib/sepgsql/sql/dml.sql        | 118 +++++
 contrib/sepgsql/sql/label.sql      |  73 +++
 contrib/sepgsql/sql/misc.sql       |   5 +
 doc/src/sgml/contrib.sgml          |   1 +
 doc/src/sgml/filelist.sgml         |   1 +
 doc/src/sgml/sepgsql.sgml          | 704 +++++++++++++++++++++++++++++
 src/Makefile.global.in             |   1 +
 src/test/regress/pg_regress.c      |   6 +
 src/test/regress/pg_regress.h      |   1 +
 src/test/regress/pg_regress_main.c |   7 +-
 30 files changed, 4246 insertions(+), 1 deletion(-)
 create mode 100644 contrib/sepgsql/.gitignore
 create mode 100644 contrib/sepgsql/Makefile
 create mode 100644 contrib/sepgsql/dml.c
 create mode 100644 contrib/sepgsql/expected/dml.out
 create mode 100644 contrib/sepgsql/expected/label.out
 create mode 100644 contrib/sepgsql/expected/misc.out
 create mode 100644 contrib/sepgsql/hooks.c
 create mode 100644 contrib/sepgsql/label.c
 create mode 100644 contrib/sepgsql/launcher
 create mode 100644 contrib/sepgsql/proc.c
 create mode 100644 contrib/sepgsql/relation.c
 create mode 100644 contrib/sepgsql/schema.c
 create mode 100644 contrib/sepgsql/selinux.c
 create mode 100644 contrib/sepgsql/sepgsql-regtest.te
 create mode 100644 contrib/sepgsql/sepgsql.h
 create mode 100644 contrib/sepgsql/sepgsql.sql.in
 create mode 100644 contrib/sepgsql/sql/dml.sql
 create mode 100644 contrib/sepgsql/sql/label.sql
 create mode 100644 contrib/sepgsql/sql/misc.sql
 create mode 100644 doc/src/sgml/sepgsql.sgml

diff --git a/configure b/configure
index 104ae3072c4..c34816caa51 100755
--- a/configure
+++ b/configure
@@ -715,6 +715,7 @@ with_libxslt
 with_libxml
 XML2_CONFIG
 with_ossp_uuid
+with_selinux
 with_openssl
 with_bonjour
 with_ldap
@@ -837,6 +838,7 @@ with_pam
 with_ldap
 with_bonjour
 with_openssl
+with_selinux
 with_readline
 with_libedit_preferred
 with_ossp_uuid
@@ -848,6 +850,7 @@ with_gnu_ld
 enable_largefile
 enable_float4_byval
 enable_float8_byval
+enable_float8_byval
 '
       ac_precious_vars='build_alias
 host_alias
@@ -858,6 +861,7 @@ LDFLAGS
 LIBS
 CPPFLAGS
 CPP
+CPPFLAGS
 LDFLAGS_EX
 LDFLAGS_SL
 DOCBOOKSTYLE'
@@ -1533,6 +1537,7 @@ Optional Packages:
   --with-ldap             build with LDAP support
   --with-bonjour          build with Bonjour support
   --with-openssl          build with OpenSSL support
+  --with-selinux          build with SELinux support
   --without-readline      do not use GNU Readline nor BSD Libedit for editing
   --with-libedit-preferred
                           prefer BSD Libedit over GNU Readline
@@ -5364,6 +5369,40 @@ fi
 $as_echo "$with_openssl" >&6; }
 
 
+#
+# SELinux
+#
+{ $as_echo "$as_me:$LINENO: checking whether to build with SELinux support" >&5
+$as_echo_n "checking whether to build with SELinux support... " >&6; }
+
+
+
+# Check whether --with-selinux was given.
+if test "${with_selinux+set}" = set; then
+  withval=$with_selinux;
+  case $withval in
+    yes)
+      :
+      ;;
+    no)
+      :
+      ;;
+    *)
+      { { $as_echo "$as_me:$LINENO: error: no argument expected for --with-selinux option" >&5
+$as_echo "$as_me: error: no argument expected for --with-selinux option" >&2;}
+   { (exit 1); exit 1; }; }
+      ;;
+  esac
+
+else
+  with_selinux=no
+
+fi
+
+
+
+{ $as_echo "$as_me:$LINENO: result: $with_selinux" >&5
+$as_echo "$with_selinux" >&6; }
 
 #
 # Readline
@@ -9291,6 +9330,89 @@ fi
 
 fi
 
+# for contrib/sepgsql
+if test "$with_selinux" = yes; then
+
+{ $as_echo "$as_me:$LINENO: checking for getpeercon_raw in -lselinux" >&5
+$as_echo_n "checking for getpeercon_raw in -lselinux... " >&6; }
+if test "${ac_cv_lib_selinux_getpeercon_raw+set}" = set; then
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lselinux  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char getpeercon_raw ();
+int
+main ()
+{
+return getpeercon_raw ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+	 test "$cross_compiling" = yes ||
+	 $as_test_x conftest$ac_exeext
+       }; then
+  ac_cv_lib_selinux_getpeercon_raw=yes
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_cv_lib_selinux_getpeercon_raw=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_getpeercon_raw" >&5
+$as_echo "$ac_cv_lib_selinux_getpeercon_raw" >&6; }
+if test "x$ac_cv_lib_selinux_getpeercon_raw" = x""yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBSELINUX 1
+_ACEOF
+
+  LIBS="-lselinux $LIBS"
+
+else
+  { { $as_echo "$as_me:$LINENO: error: library 'libselinux' is required for SELinux support" >&5
+$as_echo "$as_me: error: library 'libselinux' is required for SELinux support" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+fi
+
 # for contrib/uuid-ossp
 if test "$with_ossp_uuid" = yes ; then
   { $as_echo "$as_me:$LINENO: checking for uuid_export in -lossp-uuid" >&5
diff --git a/configure.in b/configure.in
index 109eb0c0b0c..581a2198b9d 100644
--- a/configure.in
+++ b/configure.in
@@ -676,6 +676,13 @@ PGAC_ARG_BOOL(with, openssl, no, [build with OpenSSL support],
 AC_MSG_RESULT([$with_openssl])
 AC_SUBST(with_openssl)
 
+#
+# SELinux
+#
+AC_MSG_CHECKING([whether to build with SELinux support])
+PGAC_ARG_BOOL(with, selinux, no, [build with SELinux support])
+AC_SUBST(with_selinux)
+AC_MSG_RESULT([$with_selinux])
 
 #
 # Readline
@@ -948,6 +955,12 @@ if test "$with_libxslt" = yes ; then
   AC_CHECK_LIB(xslt, xsltCleanupGlobals, [], [AC_MSG_ERROR([library 'xslt' is required for XSLT support])])
 fi
 
+# for contrib/sepgsql
+if test "$with_selinux" = yes; then
+  AC_CHECK_LIB(selinux, getpeercon_raw, [],
+               [AC_MSG_ERROR([library 'libselinux' is required for SELinux support])])
+fi
+
 # for contrib/uuid-ossp
 if test "$with_ossp_uuid" = yes ; then
   AC_CHECK_LIB(ossp-uuid, uuid_export,
diff --git a/contrib/Makefile b/contrib/Makefile
index 76fa2a65769..2b314501f7c 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -61,6 +61,10 @@ ifeq ($(with_libxml),yes)
 SUBDIRS += xml2
 endif
 
+ifeq ($(with_selinux),yes)
+SUBDIRS += sepgsql
+endif
+
 # Missing:
 #		start-scripts	\ (does not have a makefile)
 
diff --git a/contrib/README b/contrib/README
index fdc5dc11e64..3c4e3242714 100644
--- a/contrib/README
+++ b/contrib/README
@@ -163,6 +163,10 @@ seg -
 	Confidence-interval datatype (GiST indexing example)
 	by Gene Selkov, Jr. <selkovjr@mcs.anl.gov>
 
+sepgsql -
+	External security provider using SELinux
+	by KaiGai Kohei <kaigai@ak.jp.nec.com>
+
 spi -
 	Various trigger functions, examples for using SPI.
 
diff --git a/contrib/sepgsql/.gitignore b/contrib/sepgsql/.gitignore
new file mode 100644
index 00000000000..1e4a297b09b
--- /dev/null
+++ b/contrib/sepgsql/.gitignore
@@ -0,0 +1 @@
+/sepgsql.sql
diff --git a/contrib/sepgsql/Makefile b/contrib/sepgsql/Makefile
new file mode 100644
index 00000000000..37a6dce89d8
--- /dev/null
+++ b/contrib/sepgsql/Makefile
@@ -0,0 +1,25 @@
+# contrib/sepgsql/Makefile
+
+MODULE_big = sepgsql
+OBJS = hooks.o selinux.o label.o dml.o \
+	schema.o relation.o proc.o
+DATA_built = sepgsql.sql sepgsql-regtest.pp
+REGRESS = label dml misc
+EXTRA_CLEAN = -r tmp *.pp sepgsql-regtest.if sepgsql-regtest.fc
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/sepgsql
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
+
+SHLIB_LINK += $(filter -lselinux, $(LIBS))
+REGRESS_OPTS += --launcher $(top_builddir)/contrib/sepgsql/launcher
+
+sepgsql-regtest.pp: sepgsql-regtest.te
+	$(MAKE) -f $(DESTDIR)/usr/share/selinux/devel/Makefile $@
diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c
new file mode 100644
index 00000000000..cfa436d37d9
--- /dev/null
+++ b/contrib/sepgsql/dml.c
@@ -0,0 +1,353 @@
+/* -------------------------------------------------------------------------
+ *
+ * contrib/sepgsql/dml.c
+ *
+ * Routines to handle DML permission checks
+ *
+ * Copyright (c) 2010-2011, PostgreSQL Global Development Group
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/sysattr.h"
+#include "access/tupdesc.h"
+#include "catalog/catalog.h"
+#include "catalog/heap.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_inherits_fn.h"
+#include "commands/seclabel.h"
+#include "commands/tablecmds.h"
+#include "executor/executor.h"
+#include "nodes/bitmapset.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+#include "sepgsql.h"
+
+/*
+ * fixup_whole_row_references
+ *
+ * When user reference a whole of row, it is equivalent to reference to
+ * all the user columns (not system columns). So, we need to fix up the
+ * given bitmapset, if it contains a whole of the row reference.
+ */
+static Bitmapset *
+fixup_whole_row_references(Oid relOid, Bitmapset *columns)
+{
+	Bitmapset  *result;
+	HeapTuple	tuple;
+	AttrNumber	natts;
+	AttrNumber	attno;
+	int			index;
+
+	/* if no whole of row references, do not anything */
+	index = InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber;
+	if (!bms_is_member(index, columns))
+		return columns;
+
+	/* obtain number of attributes */
+	tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "cache lookup failed for relation %u", relOid);
+	natts = ((Form_pg_class) GETSTRUCT(tuple))->relnatts;
+	ReleaseSysCache(tuple);
+
+	/* fix up the given columns */
+	result = bms_copy(columns);
+	result = bms_del_member(result, index);
+
+	for (attno=1; attno <= natts; attno++)
+	{
+		tuple = SearchSysCache2(ATTNUM,
+								ObjectIdGetDatum(relOid),
+								Int16GetDatum(attno));
+		if (!HeapTupleIsValid(tuple))
+			continue;
+
+		if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
+			continue;
+
+		index = attno - FirstLowInvalidHeapAttributeNumber;
+
+		result = bms_add_member(result, index);
+
+		ReleaseSysCache(tuple);
+	}
+	return result;
+}
+
+/*
+ * fixup_inherited_columns
+ *
+ * When user is querying on a table with children, it implicitly accesses
+ * child tables also. So, we also need to check security label of child
+ * tables and columns, but here is no guarantee attribute numbers are
+ * same between the parent ans children.
+ * It returns a bitmapset which contains attribute number of the child
+ * table based on the given bitmapset of the parent.
+ */
+static Bitmapset *
+fixup_inherited_columns(Oid parentId, Oid childId, Bitmapset *columns)
+{
+	AttrNumber	attno;
+	Bitmapset  *tmpset;
+	Bitmapset  *result = NULL;
+	char	   *attname;
+	int			index;
+
+	/*
+	 * obviously, no need to do anything here
+	 */
+	if (parentId == childId)
+		return columns;
+
+	tmpset = bms_copy(columns);
+	while ((index = bms_first_member(tmpset)) > 0)
+	{
+		attno = index + FirstLowInvalidHeapAttributeNumber;
+		/*
+		 * whole-row-reference shall be fixed-up later
+		 */
+		if (attno == InvalidAttrNumber)
+		{
+			result = bms_add_member(result, index);
+			continue;
+		}
+
+		attname = get_attname(parentId, attno);
+		if (!attname)
+			elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+				 attno, parentId);
+		attno = get_attnum(childId, attname);
+		if (attno == InvalidAttrNumber)
+			elog(ERROR, "cache lookup failed for attribute %s of relation %u",
+				 attname, childId);
+
+		index = attno - FirstLowInvalidHeapAttributeNumber;
+		result = bms_add_member(result, index);
+
+		pfree(attname);
+	}
+	bms_free(tmpset);
+
+	return result;
+}
+
+/*
+ * check_relation_privileges
+ *
+ * It actually checks required permissions on a certain relation
+ * and its columns.
+ */
+static bool
+check_relation_privileges(Oid relOid,
+						  Bitmapset *selected,
+						  Bitmapset *modified,
+						  uint32 required,
+						  bool abort)
+{
+	char		relkind = get_rel_relkind(relOid);
+	char	   *scontext = sepgsql_get_client_label();
+	char	   *tcontext;
+	Bitmapset  *columns;
+	int			index;
+	bool		result = true;
+
+	/*
+	 * Hardwired Policies:
+	 * SE-PostgreSQL enforces
+	 * - clients cannot modify system catalogs using DMLs
+	 * - clients cannot reference/modify toast relations using DMLs
+	 */
+	if (sepgsql_getenforce() > 0)
+	{
+		Oid		relnamespace = get_rel_namespace(relOid);
+
+		if (IsSystemNamespace(relnamespace) &&
+			(required & (SEPG_DB_TABLE__UPDATE |
+						 SEPG_DB_TABLE__INSERT |
+						 SEPG_DB_TABLE__DELETE)) != 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("selinux: hardwired security policy violation")));
+
+		if (relkind == RELKIND_TOASTVALUE)
+			ereport(ERROR,
+					(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+					 errmsg("selinux: hardwired security policy violation")));
+	}
+
+	/*
+	 * Check permissions on the relation
+	 */
+	tcontext = sepgsql_get_label(RelationRelationId, relOid, 0);
+	switch (relkind)
+	{
+		case RELKIND_RELATION:
+			result = sepgsql_check_perms(scontext,
+										 tcontext,
+										 SEPG_CLASS_DB_TABLE,
+										 required,
+										 get_rel_name(relOid),
+										 abort);
+			if (!result)
+				return false;
+			break;
+
+		case RELKIND_SEQUENCE:
+			Assert((required & ~SEPG_DB_TABLE__SELECT) == 0);
+
+			if (required & SEPG_DB_TABLE__SELECT)
+				result = sepgsql_check_perms(scontext,
+											 tcontext,
+											 SEPG_CLASS_DB_SEQUENCE,
+											 SEPG_DB_SEQUENCE__GET_VALUE,
+											 get_rel_name(relOid),
+											 abort);
+			return result;
+
+		case RELKIND_VIEW:
+			result = sepgsql_check_perms(scontext,
+										 tcontext,
+										 SEPG_CLASS_DB_VIEW,
+										 SEPG_DB_VIEW__EXPAND,
+										 get_rel_name(relOid),
+										 abort);
+			return result;
+
+		default:
+			/* nothing to be checked */
+			return true;
+	}
+
+	/*
+	 * Check permissions on the columns
+	 */
+	selected = fixup_whole_row_references(relOid, selected);
+	modified = fixup_whole_row_references(relOid, modified);
+	columns = bms_union(selected, modified);
+
+	while ((index = bms_first_member(columns)) >= 0)
+	{
+		AttrNumber	attnum;
+		uint32		column_perms = 0;
+		char		audit_name[NAMEDATALEN * 2 + 10];
+
+		if (bms_is_member(index, selected))
+			column_perms |= SEPG_DB_COLUMN__SELECT;
+		if (bms_is_member(index, modified))
+		{
+			if (required & SEPG_DB_TABLE__UPDATE)
+				column_perms |= SEPG_DB_COLUMN__UPDATE;
+			if (required & SEPG_DB_TABLE__INSERT)
+				column_perms |= SEPG_DB_COLUMN__INSERT;
+		}
+		if (column_perms == 0)
+			continue;
+
+		/* obtain column's permission */
+		attnum = index + FirstLowInvalidHeapAttributeNumber;
+		tcontext = sepgsql_get_label(RelationRelationId, relOid, attnum);
+		snprintf(audit_name, sizeof(audit_name), "%s.%s",
+				 get_rel_name(relOid), get_attname(relOid, attnum));
+
+		result = sepgsql_check_perms(scontext,
+									 tcontext,
+									 SEPG_CLASS_DB_COLUMN,
+									 column_perms,
+									 audit_name,
+									 abort);
+		if (!result)
+			return result;
+	}
+	return true;
+}
+
+/*
+ * sepgsql_dml_privileges
+ *
+ * Entrypoint of the DML permission checks
+ */
+bool
+sepgsql_dml_privileges(List *rangeTabls, bool abort)
+{
+	ListCell   *lr;
+
+	foreach (lr, rangeTabls)
+	{
+		RangeTblEntry  *rte = lfirst(lr);
+		uint32			required = 0;
+		List		   *tableIds;
+		ListCell	   *li;
+
+		/*
+		 * Only regular relations shall be checked
+		 */
+		if (rte->rtekind != RTE_RELATION)
+			continue;
+
+		/*
+		 * Find out required permissions
+		 */
+		if (rte->requiredPerms & ACL_SELECT)
+			required |= SEPG_DB_TABLE__SELECT;
+		if (rte->requiredPerms & ACL_INSERT)
+			required |= SEPG_DB_TABLE__INSERT;
+		if (rte->requiredPerms & ACL_UPDATE)
+		{
+			if (!bms_is_empty(rte->modifiedCols))
+				required |= SEPG_DB_TABLE__UPDATE;
+			else
+				required |= SEPG_DB_TABLE__LOCK;
+		}
+		if (rte->requiredPerms & ACL_DELETE)
+			required |= SEPG_DB_TABLE__DELETE;
+
+		/*
+		 * Skip, if nothing to be checked
+		 */
+		if (required == 0)
+			continue;
+
+		/*
+		 * If this RangeTblEntry is also supposed to reference inherited
+		 * tables, we need to check security label of the child tables.
+		 * So, we expand rte->relid into list of OIDs of inheritance
+		 * hierarchy, then checker routine will be invoked for each
+		 * relations.
+		 */
+		if (!rte->inh)
+			tableIds = list_make1_oid(rte->relid);
+		else
+			tableIds = find_all_inheritors(rte->relid, NoLock, NULL);
+
+		foreach (li, tableIds)
+		{
+			Oid			tableOid = lfirst_oid(li);
+			Bitmapset  *selectedCols;
+			Bitmapset  *modifiedCols;
+
+			/*
+			 * child table has different attribute numbers, so we need
+			 * to fix up them.
+			 */
+			selectedCols = fixup_inherited_columns(rte->relid, tableOid,
+												   rte->selectedCols);
+			modifiedCols = fixup_inherited_columns(rte->relid, tableOid,
+												   rte->modifiedCols);
+
+			/*
+			 * check permissions on individual tables
+			 */
+			if (!check_relation_privileges(tableOid,
+										   selectedCols,
+										   modifiedCols,
+										   required, abort))
+				return false;
+		}
+		list_free(tableIds);
+	}
+	return true;
+}
diff --git a/contrib/sepgsql/expected/dml.out b/contrib/sepgsql/expected/dml.out
new file mode 100644
index 00000000000..5625ebcd9ee
--- /dev/null
+++ b/contrib/sepgsql/expected/dml.out
@@ -0,0 +1,182 @@
+--
+-- Regression Test for DML Permissions
+--
+--
+-- Setup
+--
+CREATE TABLE t1 (a int, b text);
+SECURITY LABEL ON TABLE t1 IS 'system_u:object_r:sepgsql_table_t:s0';
+INSERT INTO t1 VALUES (1, 'aaa'), (2, 'bbb'), (3, 'ccc');
+CREATE TABLE t2 (x int, y text);
+SECURITY LABEL ON TABLE t2 IS 'system_u:object_r:sepgsql_ro_table_t:s0';
+INSERT INTO t2 VALUES (1, 'xxx'), (2, 'yyy'), (3, 'zzz');
+CREATE TABLE t3 (s int, t text);
+SECURITY LABEL ON TABLE t3 IS 'system_u:object_r:sepgsql_fixed_table_t:s0';
+INSERT INTO t3 VALUES (1, 'sss'), (2, 'ttt'), (3, 'uuu');
+CREATE TABLE t4 (m int, n text);
+SECURITY LABEL ON TABLE t4 IS 'system_u:object_r:sepgsql_secret_table_t:s0';
+INSERT INTO t4 VALUES (1, 'mmm'), (2, 'nnn'), (3, 'ooo');
+CREATE TABLE t5 (e text, f text, g text);
+SECURITY LABEL ON TABLE t5 IS 'system_u:object_r:sepgsql_table_t:s0';
+SECURITY LABEL ON COLUMN t5.e IS 'system_u:object_r:sepgsql_table_t:s0';
+SECURITY LABEL ON COLUMN t5.f IS 'system_u:object_r:sepgsql_ro_table_t:s0';
+SECURITY LABEL ON COLUMN t5.g IS 'system_u:object_r:sepgsql_secret_table_t:s0';
+CREATE TABLE customer (cid int primary key, cname text, ccredit text);
+NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "customer_pkey" for table "customer"
+SECURITY LABEL ON COLUMN customer.ccredit IS 'system_u:object_r:sepgsql_secret_table_t:s0';
+INSERT INTO customer VALUES (1, 'Taro',   '1111-2222-3333-4444'),
+                            (2, 'Hanako', '5555-6666-7777-8888');
+CREATE FUNCTION customer_credit(int) RETURNS text
+    AS 'SELECT regexp_replace(ccredit, ''-[0-9]+$'', ''-????'') FROM customer WHERE cid = $1'
+    LANGUAGE sql;
+SECURITY LABEL ON FUNCTION customer_credit(int)
+    IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
+SELECT objtype, objname, label FROM pg_seclabels
+    WHERE provider = 'selinux'
+     AND  objtype in ('table', 'column')
+     AND  objname in ('t1', 't2', 't3', 't4', 't5', 't5.e', 't5.f', 't5.g');
+ objtype | objname |                    label                    
+---------+---------+---------------------------------------------
+ table   | t1      | system_u:object_r:sepgsql_table_t:s0
+ table   | t2      | system_u:object_r:sepgsql_ro_table_t:s0
+ table   | t3      | system_u:object_r:sepgsql_fixed_table_t:s0
+ table   | t4      | system_u:object_r:sepgsql_secret_table_t:s0
+ table   | t5      | system_u:object_r:sepgsql_table_t:s0
+ column  | t5.g    | system_u:object_r:sepgsql_secret_table_t:s0
+ column  | t5.f    | system_u:object_r:sepgsql_ro_table_t:s0
+ column  | t5.e    | system_u:object_r:sepgsql_table_t:s0
+(8 rows)
+
+-- Hardwired Rules
+UPDATE pg_attribute SET attisdropped = true
+    WHERE attrelid = 't5'::regclass AND attname = 'f';	-- failed
+ERROR:  selinux: hardwired security policy violation
+--
+-- Simple DML statements
+--
+SELECT sepgsql_getcon();	-- confirm client privilege
+                   sepgsql_getcon                    
+-----------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
+(1 row)
+
+SELECT * FROM t1;			-- ok
+ a |  b  
+---+-----
+ 1 | aaa
+ 2 | bbb
+ 3 | ccc
+(3 rows)
+
+SELECT * FROM t2;			-- ok
+ x |  y  
+---+-----
+ 1 | xxx
+ 2 | yyy
+ 3 | zzz
+(3 rows)
+
+SELECT * FROM t3;			-- ok
+ s |  t  
+---+-----
+ 1 | sss
+ 2 | ttt
+ 3 | uuu
+(3 rows)
+
+SELECT * FROM t4;			-- failed
+ERROR:  SELinux: security policy violation
+SELECT * FROM t5;			-- failed
+ERROR:  SELinux: security policy violation
+SELECT e,f FROM t5;			-- ok
+ e | f 
+---+---
+(0 rows)
+
+SELECT * FROM customer;			-- failed
+ERROR:  SELinux: security policy violation
+SELECT cid, cname, customer_credit(cid) FROM customer;	-- ok
+ cid | cname  |   customer_credit   
+-----+--------+---------------------
+   1 | Taro   | 1111-2222-3333-????
+   2 | Hanako | 5555-6666-7777-????
+(2 rows)
+
+SELECT count(*) FROM t5;			-- ok
+ count 
+-------
+     0
+(1 row)
+
+SELECT count(*) FROM t5 WHERE g IS NULL;	-- failed
+ERROR:  SELinux: security policy violation
+INSERT INTO t1 VALUES (4, 'abc');		-- ok
+INSERT INTO t2 VALUES (4, 'xyz');		-- failed
+ERROR:  SELinux: security policy violation
+INSERT INTO t3 VALUES (4, 'stu');		-- ok
+INSERT INTO t4 VALUES (4, 'mno');		-- failed
+ERROR:  SELinux: security policy violation
+INSERT INTO t5 VALUES (1,2,3);			-- failed
+ERROR:  SELinux: security policy violation
+INSERT INTO t5 (e,f) VALUES ('abc', 'def');	-- failed
+ERROR:  SELinux: security policy violation
+INSERT INTO t5 (e) VALUES ('abc');		-- ok
+UPDATE t1 SET b = b || '_upd';			-- ok
+UPDATE t2 SET y = y || '_upd';			-- failed
+ERROR:  SELinux: security policy violation
+UPDATE t3 SET t = t || '_upd';			-- failed
+ERROR:  SELinux: security policy violation
+UPDATE t4 SET n = n || '_upd';			-- failed
+ERROR:  SELinux: security policy violation
+UPDATE t5 SET e = 'xyz';			-- ok
+UPDATE t5 SET e = f || '_upd';			-- ok
+UPDATE t5 SET e = g || '_upd';			-- failed
+ERROR:  SELinux: security policy violation
+DELETE FROM t1;					-- ok
+DELETE FROM t2;					-- failed
+ERROR:  SELinux: security policy violation
+DELETE FROM t3;					-- failed
+ERROR:  SELinux: security policy violation
+DELETE FROM t4;					-- failed
+ERROR:  SELinux: security policy violation
+DELETE FROM t5;					-- ok
+DELETE FROM t5 WHERE f IS NULL;			-- ok
+DELETE FROM t5 WHERE g IS NULL;			-- failed
+ERROR:  SELinux: security policy violation
+--
+-- COPY TO/FROM statements
+--
+COPY t1 TO '/dev/null';				-- ok
+COPY t2 TO '/dev/null';				-- ok
+COPY t3 TO '/dev/null';				-- ok
+COPY t4 TO '/dev/null';				-- failed
+ERROR:  SELinux: security policy violation
+COPY t5 TO '/dev/null';				-- failed
+ERROR:  SELinux: security policy violation
+COPY t5(e,f) TO '/dev/null';			-- ok
+COPY t1 FROM '/dev/null';			-- ok
+COPY t2 FROM '/dev/null';			-- failed
+ERROR:  SELinux: security policy violation
+COPY t3 FROM '/dev/null';			-- ok
+COPY t4 FROM '/dev/null';			-- failed
+ERROR:  SELinux: security policy violation
+COPY t5 FROM '/dev/null';			-- failed
+ERROR:  SELinux: security policy violation
+COPY t5 (e,f) FROM '/dev/null';			-- failed
+ERROR:  SELinux: security policy violation
+COPY t5 (e) FROM '/dev/null';			-- ok
+--
+-- Clean up
+--
+SELECT sepgsql_getcon();	-- confirm client privilege
+                    sepgsql_getcon                    
+------------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c255
+(1 row)
+
+DROP TABLE IF EXISTS t1 CASCADE;
+DROP TABLE IF EXISTS t2 CASCADE;
+DROP TABLE IF EXISTS t3 CASCADE;
+DROP TABLE IF EXISTS t4 CASCADE;
+DROP TABLE IF EXISTS t5 CASCADE;
+DROP TABLE IF EXISTS customer CASCADE;
diff --git a/contrib/sepgsql/expected/label.out b/contrib/sepgsql/expected/label.out
new file mode 100644
index 00000000000..0f0615cb306
--- /dev/null
+++ b/contrib/sepgsql/expected/label.out
@@ -0,0 +1,109 @@
+--
+-- Regression Tests for Label Management
+--
+--
+-- Setup
+--
+CREATE TABLE t1 (a int, b text);
+INSERT INTO t1 VALUES (1, 'aaa'), (2, 'bbb'), (3, 'ccc');
+SELECT * INTO t2 FROM t1 WHERE a % 2 = 0;
+CREATE FUNCTION f1 () RETURNS text
+    AS 'SELECT sepgsql_getcon()'
+    LANGUAGE sql;
+CREATE FUNCTION f2 () RETURNS text
+    AS 'SELECT sepgsql_getcon()'
+    LANGUAGE sql;
+SECURITY LABEL ON FUNCTION f2()
+    IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
+CREATE FUNCTION f3 () RETURNS text
+    AS 'BEGIN
+          RAISE EXCEPTION ''an exception from f3()'';
+          RETURN NULL;
+        END;' LANGUAGE plpgsql;
+SECURITY LABEL ON FUNCTION f3()
+    IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
+--
+-- Tests for default labeling behavior
+--
+SELECT sepgsql_getcon();	-- confirm client privilege
+                   sepgsql_getcon                    
+-----------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
+(1 row)
+
+CREATE TABLE t3 (s int, t text);
+INSERT INTO t3 VALUES (1, 'sss'), (2, 'ttt'), (3, 'uuu');
+SELECT objtype, objname, label FROM pg_seclabels
+    WHERE provider = 'selinux'
+     AND  objtype in ('table', 'column')
+     AND  objname in ('t1', 't2', 't3');
+ objtype | objname |                     label                     
+---------+---------+-----------------------------------------------
+ table   | t1      | unconfined_u:object_r:sepgsql_table_t:s0
+ table   | t2      | unconfined_u:object_r:sepgsql_table_t:s0
+ table   | t3      | unconfined_u:object_r:user_sepgsql_table_t:s0
+(3 rows)
+
+--
+-- Tests for SECURITY LABEL
+--
+SELECT sepgsql_getcon();	-- confirm client privilege
+                   sepgsql_getcon                   
+----------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_dba_t:s0
+(1 row)
+
+SECURITY LABEL ON TABLE t1
+    IS 'system_u:object_r:sepgsql_ro_table_t:s0';	-- ok
+SECURITY LABEL ON TABLE t2
+    IS 'invalid seuciryt context';			-- be failed
+ERROR:  invalid security label: "invalid seuciryt context"
+SECURITY LABEL ON COLUMN t2
+    IS 'system_u:object_r:sepgsql_ro_table_t:s0';	-- be failed
+ERROR:  improper relation name (too many dotted names): 
+SECURITY LABEL ON COLUMN t2.b
+    IS 'system_u:object_r:sepgsql_ro_table_t:s0';	-- ok
+--
+-- Tests for Trusted Procedures
+--
+SELECT sepgsql_getcon();	-- confirm client privilege
+                   sepgsql_getcon                    
+-----------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
+(1 row)
+
+SELECT f1();			-- normal procedure
+                         f1                          
+-----------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
+(1 row)
+
+SELECT f2();			-- trusted procedure
+                         f2                          
+-----------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_trusted_proc_t:s0
+(1 row)
+
+SELECT f3();			-- trusted procedure that raises an error
+ERROR:  an exception from f3()
+SELECT sepgsql_getcon();	-- client's label must be restored
+                   sepgsql_getcon                    
+-----------------------------------------------------
+ unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
+(1 row)
+
+--
+-- Clean up
+--
+SELECT sepgsql_getcon();	-- confirm client privilege
+                    sepgsql_getcon                    
+------------------------------------------------------
+ unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c255
+(1 row)
+
+DROP TABLE IF EXISTS t1 CASCADE;
+DROP TABLE IF EXISTS t2 CASCADE;
+DROP TABLE IF EXISTS t3 CASCADE;
+DROP FUNCTION IF EXISTS f1() CASCADE;
+DROP FUNCTION IF EXISTS f2() CASCADE;
+DROP FUNCTION IF EXISTS f3() CASCADE;
diff --git a/contrib/sepgsql/expected/misc.out b/contrib/sepgsql/expected/misc.out
new file mode 100644
index 00000000000..5242333bf49
--- /dev/null
+++ b/contrib/sepgsql/expected/misc.out
@@ -0,0 +1,5 @@
+--
+-- Regression Test for Misc Permission Checks
+--
+LOAD '$libdir/sepgsql';		-- failed
+ERROR:  SELinux: LOAD is not allowed anyway.
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
new file mode 100644
index 00000000000..6b55e484cfd
--- /dev/null
+++ b/contrib/sepgsql/hooks.c
@@ -0,0 +1,446 @@
+/* -------------------------------------------------------------------------
+ *
+ * contrib/sepgsql/hooks.c
+ *
+ * Entrypoints of the hooks in PostgreSQL, and dispatches the callbacks.
+ *
+ * Copyright (c) 2010-2011, PostgreSQL Global Development Group
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/objectaccess.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_proc.h"
+#include "commands/seclabel.h"
+#include "executor/executor.h"
+#include "fmgr.h"
+#include "libpq/auth.h"
+#include "miscadmin.h"
+#include "tcop/utility.h"
+#include "utils/guc.h"
+
+#include "sepgsql.h"
+
+PG_MODULE_MAGIC;
+
+/*
+ * Declarations
+ */
+void _PG_init(void);
+
+/*
+ * Saved hook entries (if stacked)
+ */
+static object_access_hook_type			next_object_access_hook = NULL;
+static ClientAuthentication_hook_type	next_client_auth_hook = NULL;
+static ExecutorCheckPerms_hook_type		next_exec_check_perms_hook = NULL;
+static needs_fmgr_hook_type				next_needs_fmgr_hook = NULL;
+static fmgr_hook_type					next_fmgr_hook = NULL;
+static ProcessUtility_hook_type			next_ProcessUtility_hook = NULL;
+
+/*
+ * GUC: sepgsql.permissive = (on|off)
+ */
+static bool sepgsql_permissive;
+
+bool
+sepgsql_get_permissive(void)
+{
+	return sepgsql_permissive;
+}
+
+/*
+ * GUC: sepgsql.debug_audit = (on|off)
+ */
+static bool sepgsql_debug_audit;
+
+bool
+sepgsql_get_debug_audit(void)
+{
+	return sepgsql_debug_audit;
+}
+
+/*
+ * sepgsql_client_auth
+ *
+ * Entrypoint of the client authentication hook.
+ * It switches the client label according to getpeercon(), and the current
+ * performing mode according to the GUC setting.
+ */
+static void
+sepgsql_client_auth(Port *port, int status)
+{
+	char   *context;
+
+	if (next_client_auth_hook)
+		(*next_client_auth_hook)(port, status);
+
+	/*
+	 * In the case when authentication failed, the supplied socket
+	 * shall be closed soon, so we don't need to do anything here.
+	 */
+	if (status != STATUS_OK)
+		return;
+
+	/*
+	 * Getting security label of the peer process using API of libselinux.
+	 */
+	if (getpeercon_raw(port->sock, &context) < 0)
+		ereport(FATAL,
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("selinux: failed to get the peer label")));
+
+	sepgsql_set_client_label(context);
+
+	/*
+	 * Switch the current performing mode from INTERNAL to either
+	 * DEFAULT or PERMISSIVE.
+	 */
+	if (sepgsql_permissive)
+		sepgsql_set_mode(SEPGSQL_MODE_PERMISSIVE);
+	else
+		sepgsql_set_mode(SEPGSQL_MODE_DEFAULT);
+}
+
+/*
+ * sepgsql_object_access
+ *
+ * Entrypoint of the object_access_hook. This routine performs as
+ * a dispatcher of invocation based on access type and object classes.
+ */
+static void
+sepgsql_object_access(ObjectAccessType access,
+                      Oid classId,
+                      Oid objectId,
+                      int subId)
+{
+	if (next_object_access_hook)
+		(*next_object_access_hook)(access, classId, objectId, subId);
+
+	switch (access)
+	{
+		case OAT_POST_CREATE:
+			switch (classId)
+			{
+				case NamespaceRelationId:
+					sepgsql_schema_post_create(objectId);
+					break;
+
+				case RelationRelationId:
+					if (subId == 0)
+						sepgsql_relation_post_create(objectId);
+					else
+						sepgsql_attribute_post_create(objectId, subId);
+					break;
+
+				case ProcedureRelationId:
+					sepgsql_proc_post_create(objectId);
+					break;
+
+				default:
+					/* Ignore unsupported object classes */
+					break;
+			}
+			break;
+
+		default:
+			elog(ERROR, "unexpected object access type: %d", (int)access);
+			break;
+	}
+}
+
+/*
+ * sepgsql_exec_check_perms
+ *
+ * Entrypoint of DML permissions
+ */
+static bool
+sepgsql_exec_check_perms(List *rangeTabls, bool abort)
+{
+	/*
+	 * If security provider is stacking and one of them replied 'false'
+	 * at least, we don't need to check any more.
+	 */
+	if (next_exec_check_perms_hook &&
+		!(*next_exec_check_perms_hook)(rangeTabls, abort))
+		return false;
+
+	if (!sepgsql_dml_privileges(rangeTabls, abort))
+		return false;
+
+	return true;
+}
+
+/*
+ * sepgsql_needs_fmgr_hook
+ *
+ * It informs the core whether the supplied function is trusted procedure,
+ * or not. If true, sepgsql_fmgr_hook shall be invoked at start, end, and
+ * abort time of function invocation.
+ */
+static bool
+sepgsql_needs_fmgr_hook(Oid functionId)
+{
+	char   *old_label;
+	char   *new_label;
+	char   *function_label;
+
+	if (next_needs_fmgr_hook &&
+		(*next_needs_fmgr_hook)(functionId))
+		return true;
+
+	/*
+	 * SELinux needs the function to be called via security_definer
+	 * wrapper, if this invocation will take a domain-transition.
+	 * We call these functions as trusted-procedure, if the security
+	 * policy has a rule that switches security label of the client
+	 * on execution.
+	 */
+	old_label = sepgsql_get_client_label();
+	new_label = sepgsql_proc_get_domtrans(functionId);
+	if (strcmp(old_label, new_label) != 0)
+	{
+		pfree(new_label);
+		return true;
+	}
+	pfree(new_label);
+
+	/*
+	 * Even if not a trusted-procedure, this function should not be inlined
+	 * unless the client has db_procedure:{execute} permission.
+	 * Please note that it shall be actually failed later because of same
+	 * reason with ACL_EXECUTE.
+	 */
+	function_label = sepgsql_get_label(ProcedureRelationId, functionId, 0);
+	if (sepgsql_check_perms(sepgsql_get_client_label(),
+							function_label,
+							SEPG_CLASS_DB_PROCEDURE,
+							SEPG_DB_PROCEDURE__EXECUTE,
+							NULL, false) != true)
+	{
+		pfree(function_label);
+		return true;
+	}
+	pfree(function_label);
+	return false;
+}
+
+/*
+ * sepgsql_fmgr_hook
+ *
+ * It switches security label of the client on execution of trusted
+ * procedures.
+ */
+static void
+sepgsql_fmgr_hook(FmgrHookEventType event,
+				  FmgrInfo *flinfo, Datum *private)
+{
+	struct {
+		char   *old_label;
+		char   *new_label;
+		Datum	next_private;
+	} *stack;
+
+	switch (event)
+	{
+		case FHET_START:
+			stack = (void *)DatumGetPointer(*private);
+			if (!stack)
+			{
+				MemoryContext	oldcxt;
+
+				oldcxt = MemoryContextSwitchTo(flinfo->fn_mcxt);
+				stack = palloc(sizeof(*stack));
+				stack->old_label = NULL;
+				stack->new_label = sepgsql_proc_get_domtrans(flinfo->fn_oid);
+				stack->next_private = 0;
+
+				MemoryContextSwitchTo(oldcxt);
+
+				*private = PointerGetDatum(stack);
+			}
+			Assert(!stack->old_label);
+			stack->old_label = sepgsql_set_client_label(stack->new_label);
+
+			if (next_fmgr_hook)
+				(*next_fmgr_hook)(event, flinfo, &stack->next_private);
+			break;
+
+		case FHET_END:
+		case FHET_ABORT:
+			stack = (void *)DatumGetPointer(*private);
+
+			if (next_fmgr_hook)
+				(*next_fmgr_hook)(event, flinfo, &stack->next_private);
+
+			sepgsql_set_client_label(stack->old_label);
+			stack->old_label = NULL;
+			break;
+
+		default:
+			elog(ERROR, "unexpected event type: %d", (int)event);
+			break;
+	}
+}
+
+/*
+ * sepgsql_utility_command
+ *
+ * It tries to rough-grained control on utility commands; some of them can
+ * break whole of the things if nefarious user would use.
+ */
+static void
+sepgsql_utility_command(Node *parsetree,
+						const char *queryString,
+						ParamListInfo params,
+						bool isTopLevel,
+						DestReceiver *dest,
+						char *completionTag)
+{
+	if (next_ProcessUtility_hook)
+		(*next_ProcessUtility_hook)(parsetree, queryString, params,
+									isTopLevel, dest, completionTag);
+
+	/*
+	 * Check command tag to avoid nefarious operations
+	 */
+	switch (nodeTag(parsetree))
+	{
+		case T_LoadStmt:
+			/*
+			 * We reject LOAD command across the board on enforcing mode,
+			 * because a binary module can arbitrarily override hooks.
+			 */
+			if (sepgsql_getenforce())
+			{
+				ereport(ERROR,
+						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+						 errmsg("SELinux: LOAD is not allowed anyway.")));
+			}
+			break;
+		default:
+			/*
+			 * Right now we don't check any other utility commands,
+			 * because it needs more detailed information to make
+			 * access control decision here, but we don't want to
+			 * have two parse and analyze routines individually.
+			 */
+			break;
+	}
+
+	/*
+	 * Original implementation
+	 */
+	standard_ProcessUtility(parsetree, queryString, params,
+							isTopLevel, dest, completionTag);
+}
+
+/*
+ * Module load/unload callback
+ */
+void
+_PG_init(void)
+{
+	char   *context;
+
+	/*
+	 * We allow to load the SE-PostgreSQL module on single-user-mode or
+	 * shared_preload_libraries settings only.
+	 */
+	if (IsUnderPostmaster)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("Not allowed to load SE-PostgreSQL now")));
+
+	/*
+	 * Check availability of SELinux on the platform.
+	 * If disabled, we cannot activate any SE-PostgreSQL features,
+	 * and we have to skip rest of initialization.
+	 */
+	if (is_selinux_enabled() < 1)
+	{
+		sepgsql_set_mode(SEPGSQL_MODE_DISABLED);
+		return;
+	}
+
+	/*
+	 * sepgsql.permissive = (on|off)
+	 *
+	 * This variable controls performing mode of SE-PostgreSQL
+	 * on user's session.
+	 */
+	DefineCustomBoolVariable("sepgsql.permissive",
+							 "Turn on/off permissive mode in SE-PostgreSQL",
+							 NULL,
+							 &sepgsql_permissive,
+							 false,
+							 PGC_SIGHUP,
+							 GUC_NOT_IN_SAMPLE,
+							 NULL,
+							 NULL);
+
+	/*
+	 * sepgsql.debug_audit = (on|off)
+	 *
+	 * This variable allows users to turn on/off audit logs on access
+	 * control decisions, independent from auditallow/auditdeny setting
+	 * in the security policy.
+	 * We intend to use this option for debugging purpose.
+	 */
+	DefineCustomBoolVariable("sepgsql.debug_audit",
+							 "Turn on/off debug audit messages",
+							 NULL,
+							 &sepgsql_debug_audit,
+							 false,
+							 PGC_USERSET,
+							 GUC_NOT_IN_SAMPLE,
+							 NULL,
+							 NULL);
+
+	/*
+	 * Set up dummy client label.
+	 *
+	 * XXX - note that PostgreSQL launches background worker process
+	 * like autovacuum without authentication steps. So, we initialize
+	 * sepgsql_mode with SEPGSQL_MODE_INTERNAL, and client_label with
+	 * the security context of server process.
+	 * Later, it also launches background of user session. In this case,
+	 * the process is always hooked on post-authentication, and we can
+	 * initialize the sepgsql_mode and client_label correctly.
+	 */
+	if (getcon_raw(&context) < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("selinux: unable to get security label of server")));
+	sepgsql_set_client_label(context);
+
+	/* Security label provider hook */
+	register_label_provider(SEPGSQL_LABEL_TAG,
+							sepgsql_object_relabel);
+
+	/* Client authentication hook */
+	next_client_auth_hook = ClientAuthentication_hook;
+	ClientAuthentication_hook = sepgsql_client_auth;
+
+	/* Object access hook */
+	next_object_access_hook = object_access_hook;
+	object_access_hook = sepgsql_object_access;
+
+	/* DML permission check */
+	next_exec_check_perms_hook = ExecutorCheckPerms_hook;
+	ExecutorCheckPerms_hook = sepgsql_exec_check_perms;
+
+	/* Trusted procedure hooks */
+	next_needs_fmgr_hook = needs_fmgr_hook;
+	needs_fmgr_hook = sepgsql_needs_fmgr_hook;
+
+	next_fmgr_hook = fmgr_hook;
+	fmgr_hook = sepgsql_fmgr_hook;
+
+	/* ProcessUtility hook */
+	next_ProcessUtility_hook = ProcessUtility_hook;
+	ProcessUtility_hook = sepgsql_utility_command;
+}
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c
new file mode 100644
index 00000000000..bc28adfea55
--- /dev/null
+++ b/contrib/sepgsql/label.c
@@ -0,0 +1,477 @@
+/* -------------------------------------------------------------------------
+ *
+ * contrib/sepgsql/label.c
+ *
+ * Routines to support SELinux labels (security context)
+ *
+ * Copyright (c) 2010-2011, PostgreSQL Global Development Group
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/genam.h"
+#include "catalog/catalog.h"
+#include "catalog/dependency.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_proc.h"
+#include "commands/dbcommands.h"
+#include "commands/seclabel.h"
+#include "libpq/libpq-be.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/tqual.h"
+
+#include "sepgsql.h"
+
+#include <selinux/label.h>
+
+/*
+ * client_label
+ *
+ * security label of the client process
+ */
+static char	   *client_label = NULL;
+
+char *
+sepgsql_get_client_label(void)
+{
+	return client_label;
+}
+
+char *
+sepgsql_set_client_label(char *new_label)
+{
+	char   *old_label = client_label;
+
+	client_label = new_label;
+
+	return old_label;
+}
+
+/*
+ * sepgsql_get_label
+ *
+ * It returns a security context of the specified database object.
+ * If unlabeled or incorrectly labeled, the system "unlabeled" label
+ * shall be returned.
+ */
+char *
+sepgsql_get_label(Oid classId, Oid objectId, int32 subId)
+{
+	ObjectAddress	object;
+	char		   *label;
+
+	object.classId		= classId;
+	object.objectId		= objectId;
+	object.objectSubId	= subId;
+
+	label = GetSecurityLabel(&object, SEPGSQL_LABEL_TAG);
+	if (!label || security_check_context_raw((security_context_t)label))
+	{
+		security_context_t	unlabeled;
+
+		if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_INTERNAL_ERROR),
+					 errmsg("selinux: unable to get initial security label")));
+		PG_TRY();
+		{
+			label = pstrdup(unlabeled);
+		}
+		PG_CATCH();
+		{
+			freecon(unlabeled);
+			PG_RE_THROW();
+		}
+		PG_END_TRY();
+
+		freecon(unlabeled);
+	}
+	return label;
+}
+
+/*
+ * sepgsql_object_relabel
+ *
+ * An entrypoint of SECURITY LABEL statement
+ */
+void
+sepgsql_object_relabel(const ObjectAddress *object, const char *seclabel)
+{
+	/*
+	 * validate format of the supplied security label,
+	 * if it is security context of selinux.
+	 */
+	if (seclabel &&
+		security_check_context_raw((security_context_t) seclabel) < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_NAME),
+				 errmsg("invalid security label: \"%s\"", seclabel)));
+	/*
+	 * Do actual permission checks for each object classes
+	 */
+	switch (object->classId)
+	{
+		case NamespaceRelationId:
+		    sepgsql_schema_relabel(object->objectId, seclabel);
+			break;
+		case RelationRelationId:
+			if (object->objectSubId == 0)
+				sepgsql_relation_relabel(object->objectId,
+										 seclabel);
+			else
+				sepgsql_attribute_relabel(object->objectId,
+										  object->objectSubId,
+										  seclabel);
+			break;
+		case ProcedureRelationId:
+			sepgsql_proc_relabel(object->objectId, seclabel);
+			break;
+
+		default:
+			elog(ERROR, "unsupported object type: %u", object->classId);
+			break;
+	}
+}
+
+/*
+ * TEXT sepgsql_getcon(VOID)
+ *
+ * It returns the security label of the client.
+ */
+PG_FUNCTION_INFO_V1(sepgsql_getcon);
+Datum
+sepgsql_getcon(PG_FUNCTION_ARGS)
+{
+	char   *client_label;
+
+	if (!sepgsql_is_enabled())
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("SELinux: now disabled")));
+
+	client_label = sepgsql_get_client_label();
+
+	PG_RETURN_POINTER(cstring_to_text(client_label));
+}
+
+/*
+ * TEXT sepgsql_mcstrans_in(TEXT)
+ *
+ * It translate the given qualified MLS/MCS range into raw format
+ * when mcstrans daemon is working.
+ */
+PG_FUNCTION_INFO_V1(sepgsql_mcstrans_in);
+Datum
+sepgsql_mcstrans_in(PG_FUNCTION_ARGS)
+{
+	text   *label = PG_GETARG_TEXT_P(0);
+	char   *raw_label;
+	char   *result;
+
+	if (!sepgsql_is_enabled())
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("SELinux: now disabled")));
+
+	if (selinux_trans_to_raw_context(text_to_cstring(label),
+									 &raw_label) < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("SELinux: internal error on mcstrans")));
+
+	PG_TRY();
+	{
+		result = pstrdup(raw_label);
+	}
+	PG_CATCH();
+	{
+		freecon(raw_label);
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
+	freecon(raw_label);
+
+	PG_RETURN_POINTER(cstring_to_text(result));
+}
+
+/*
+ * TEXT sepgsql_mcstrans_out(TEXT)
+ *
+ * It translate the given raw MLS/MCS range into qualified format
+ * when mcstrans daemon is working.
+ */
+PG_FUNCTION_INFO_V1(sepgsql_mcstrans_out);
+Datum
+sepgsql_mcstrans_out(PG_FUNCTION_ARGS)
+{
+	text   *label = PG_GETARG_TEXT_P(0);
+	char   *qual_label;
+	char   *result;
+
+	if (!sepgsql_is_enabled())
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("SELinux: now disabled")));
+
+	if (selinux_raw_to_trans_context(text_to_cstring(label),
+									 &qual_label) < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("SELinux: internal error on mcstrans")));
+
+	PG_TRY();
+	{
+		result = pstrdup(qual_label);
+	}
+	PG_CATCH();
+	{
+		freecon(qual_label);
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
+	freecon(qual_label);
+
+	PG_RETURN_POINTER(cstring_to_text(result));
+}
+
+/*
+ * exec_object_restorecon
+ *
+ * This routine is a helper called by sepgsql_restorecon; it set up
+ * initial security labels of database objects within the supplied
+ * catalog OID.
+ */
+static void
+exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
+{
+	Relation		rel;
+	SysScanDesc		sscan;
+	HeapTuple		tuple;
+	char		   *database_name = get_database_name(MyDatabaseId);
+	char		   *namespace_name;
+	Oid				namespace_id;
+	char		   *relation_name;
+
+	/*
+	 * Open the target catalog. We don't want to allow writable
+	 * accesses by other session during initial labeling.
+	 */
+	rel = heap_open(catalogId, AccessShareLock);
+
+	sscan = systable_beginscan(rel, InvalidOid, false,
+							   SnapshotNow, 0, NULL);
+	while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
+	{
+		Form_pg_namespace	nspForm;
+		Form_pg_class		relForm;
+		Form_pg_attribute	attForm;
+		Form_pg_proc		proForm;
+		char				objname[NAMEDATALEN * 4 + 10];
+		int					objtype = 1234;
+		ObjectAddress		object;
+		security_context_t	context;
+
+		/*
+		 * The way to determine object name depends on object classes.
+		 * So, any branches set up `objtype', `objname' and `object' here.
+		 */
+		switch (catalogId)
+		{
+			case NamespaceRelationId:
+				nspForm = (Form_pg_namespace) GETSTRUCT(tuple);
+
+				objtype = SELABEL_DB_SCHEMA;
+				snprintf(objname, sizeof(objname), "%s.%s",
+						 database_name, NameStr(nspForm->nspname));
+
+				object.classId = NamespaceRelationId;
+				object.objectId = HeapTupleGetOid(tuple);
+				object.objectSubId = 0;
+				break;
+
+			case RelationRelationId:
+				relForm = (Form_pg_class) GETSTRUCT(tuple);
+
+				if (relForm->relkind == RELKIND_RELATION)
+					objtype = SELABEL_DB_TABLE;
+				else if (relForm->relkind == RELKIND_SEQUENCE)
+					objtype = SELABEL_DB_SEQUENCE;
+				else if (relForm->relkind == RELKIND_VIEW)
+					objtype = SELABEL_DB_VIEW;
+				else
+					continue;	/* no need to assign security label */
+
+				namespace_name = get_namespace_name(relForm->relnamespace);
+				snprintf(objname, sizeof(objname), "%s.%s.%s",
+						 database_name, namespace_name,
+						 NameStr(relForm->relname));
+				pfree(namespace_name);
+
+				object.classId = RelationRelationId;
+				object.objectId = HeapTupleGetOid(tuple);
+				object.objectSubId = 0;
+				break;
+
+			case AttributeRelationId:
+				attForm = (Form_pg_attribute) GETSTRUCT(tuple);
+
+				if (get_rel_relkind(attForm->attrelid) != RELKIND_RELATION)
+					continue;	/* no need to assign security label */
+
+				objtype = SELABEL_DB_COLUMN;
+
+				namespace_id = get_rel_namespace(attForm->attrelid);
+				namespace_name = get_namespace_name(namespace_id);
+				relation_name = get_rel_name(attForm->attrelid);
+				snprintf(objname, sizeof(objname), "%s.%s.%s.%s",
+						 database_name, namespace_name,
+						 relation_name, NameStr(attForm->attname));
+				pfree(relation_name);
+				pfree(namespace_name);
+
+				object.classId = RelationRelationId;
+				object.objectId = attForm->attrelid;
+				object.objectSubId = attForm->attnum;
+				break;
+
+			case ProcedureRelationId:
+				proForm = (Form_pg_proc) GETSTRUCT(tuple);
+
+				objtype = SELABEL_DB_PROCEDURE;
+
+				namespace_name = get_namespace_name(proForm->pronamespace);
+				snprintf(objname, sizeof(objname), "%s.%s.%s",
+						 database_name, namespace_name,
+						 NameStr(proForm->proname));
+				pfree(namespace_name);
+
+				object.classId = ProcedureRelationId;
+				object.objectId = HeapTupleGetOid(tuple);
+				object.objectSubId = 0;
+				break;
+
+			default:
+				elog(ERROR, "Bug? %u is not supported to set initial labels",
+					 catalogId);
+				break;
+		}
+
+		if (selabel_lookup_raw(sehnd, &context, objname, objtype) == 0)
+		{
+			PG_TRY();
+			{
+				/*
+				 * Check SELinux permission to relabel the fetched object,
+				 * then do the actual relabeling.
+				 */
+				sepgsql_object_relabel(&object, context);
+
+				SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, context);
+			}
+			PG_CATCH();
+			{
+				freecon(context);
+				PG_RE_THROW();
+			}
+			PG_END_TRY();
+			freecon(context);
+		}
+		else if (errno == ENOENT)
+			ereport(WARNING,
+					(errmsg("no valid initial label on %s (type=%d), skipped",
+							objname, objtype)));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_INTERNAL_ERROR),
+					 errmsg("libselinux: internal error")));
+	}
+	systable_endscan(sscan);
+
+	heap_close(rel, NoLock);
+}
+
+/*
+ * BOOL sepgsql_restorecon(TEXT specfile)
+ *
+ * This function tries to assign initial security labels on all the object
+ * within the current database, according to the system setting.
+ * It is typically invoked by sepgsql-install script just after initdb, to
+ * assign initial security labels.
+ *
+ * If @specfile is not NULL, it uses explicitly specified specfile, instead
+ * of the system default.
+ */
+PG_FUNCTION_INFO_V1(sepgsql_restorecon);
+Datum
+sepgsql_restorecon(PG_FUNCTION_ARGS)
+{
+	struct selabel_handle  *sehnd;
+	struct selinux_opt		seopts;
+
+	/*
+	 * SELinux has to be enabled on the running platform.
+	 */
+	if (!sepgsql_is_enabled())
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("SELinux: now disabled")));
+	/*
+	 * Check DAC permission. Only superuser can set up initial
+	 * security labels, like root-user in filesystems
+	 */
+	if (!superuser())
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("must be superuser to restore initial contexts")));
+
+	/*
+	 * Open selabel_lookup(3) stuff. It provides a set of mapping
+	 * between an initial security label and object class/name due
+	 * to the system setting.
+	 */
+	if (PG_ARGISNULL(0))
+	{
+		seopts.type = SELABEL_OPT_UNUSED;
+		seopts.value = NULL;
+	}
+	else
+	{
+		seopts.type = SELABEL_OPT_PATH;
+		seopts.value = TextDatumGetCString(PG_GETARG_DATUM(0));
+	}
+	sehnd = selabel_open(SELABEL_CTX_DB, &seopts, 1);
+	if (!sehnd)
+		ereport(ERROR,
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("SELinux internal error")));
+	PG_TRY();
+	{
+		/*
+		 * Right now, we have no support labeling on the shared
+		 * database objects, such as database, role, or tablespace.
+		 */
+		exec_object_restorecon(sehnd, NamespaceRelationId);
+		exec_object_restorecon(sehnd, RelationRelationId);
+		exec_object_restorecon(sehnd, AttributeRelationId);
+		exec_object_restorecon(sehnd, ProcedureRelationId);
+	}
+	PG_CATCH();
+	{
+		selabel_close(sehnd);
+		PG_RE_THROW();
+	}
+	PG_END_TRY();	
+
+	selabel_close(sehnd);
+
+	PG_RETURN_BOOL(true);
+}
diff --git a/contrib/sepgsql/launcher b/contrib/sepgsql/launcher
new file mode 100644
index 00000000000..9e5ecdc400b
--- /dev/null
+++ b/contrib/sepgsql/launcher
@@ -0,0 +1,52 @@
+#!/bin/sh
+#
+# A wrapper script to launch psql command in regression test
+#
+# Copyright (c) 2010-2011, PostgreSQL Global Development Group
+#
+# -------------------------------------------------------------------------
+
+if [ $# -lt 1 ]; then
+    echo "usage: `basename $0` <command> [options...]"
+    exit 1
+fi
+
+RUNCON=`which runcon`
+if [ ! -e "$RUNCON" ]; then
+    echo "runcon command is not found"
+    exit 1
+fi
+
+#
+# Read SQL from stdin
+#
+TEMP=`mktemp`
+CONTEXT=""
+
+while IFS='\\n' read LINE
+do
+    if echo "$LINE" | grep -q "^-- @SECURITY-CONTEXT="; then
+        if [ -s "$TEMP" ]; then
+            if [ -n "$CONTEXT" ]; then
+                "$RUNCON" "$CONTEXT" $* < "$TEMP"
+            else
+                $* < $TEMP
+            fi
+            truncate -s0 $TEMP
+        fi
+        CONTEXT=`echo "$LINE" | sed 's/^-- @SECURITY-CONTEXT=//g'`
+        LINE="SELECT sepgsql_getcon();	-- confirm client privilege"
+    fi
+    echo "$LINE" >> $TEMP
+done
+
+if [ -s "$TEMP" ]; then
+    if [ -n "$CONTEXT" ]; then
+        "$RUNCON" "$CONTEXT" $* < "$TEMP"
+    else
+        $* < $TEMP
+    fi
+fi
+
+# cleanup temp file
+rm -f $TEMP
diff --git a/contrib/sepgsql/proc.c b/contrib/sepgsql/proc.c
new file mode 100644
index 00000000000..f1a7b9b7505
--- /dev/null
+++ b/contrib/sepgsql/proc.c
@@ -0,0 +1,158 @@
+/* -------------------------------------------------------------------------
+ *
+ * contrib/sepgsql/proc.c
+ *
+ * Routines corresponding to procedure objects
+ *
+ * Copyright (c) 2010-2011, PostgreSQL Global Development Group
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/sysattr.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_proc.h"
+#include "commands/seclabel.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/tqual.h"
+
+#include "sepgsql.h"
+
+/*
+ * sepgsql_proc_post_create
+ *
+ * This routine assigns a default security label on a newly defined
+ * procedure.
+ */
+void
+sepgsql_proc_post_create(Oid functionId)
+{
+	Relation		rel;
+	ScanKeyData		skey;
+	SysScanDesc		sscan;
+	HeapTuple		tuple;
+	Oid				namespaceId;
+	ObjectAddress	object;
+	char		   *scontext;
+	char		   *tcontext;
+	char		   *ncontext;
+
+	/*
+	 * Fetch namespace of the new procedure. Because pg_proc entry is not
+	 * visible right now, we need to scan the catalog using SnapshotSelf.
+	 */
+	rel = heap_open(ProcedureRelationId, AccessShareLock);
+
+	ScanKeyInit(&skey,
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(functionId));
+
+	sscan = systable_beginscan(rel, ProcedureOidIndexId, true,
+							   SnapshotSelf, 1, &skey);
+
+	tuple = systable_getnext(sscan);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "catalog lookup failed for proc %u", functionId);
+
+	namespaceId = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace;
+
+	systable_endscan(sscan);
+	heap_close(rel, AccessShareLock);
+
+	/*
+	 * Compute a default security label when we create a new procedure
+	 * object under the specified namespace.
+	 */
+	scontext = sepgsql_get_client_label();
+	tcontext = sepgsql_get_label(NamespaceRelationId, namespaceId, 0);
+	ncontext = sepgsql_compute_create(scontext, tcontext,
+									  SEPG_CLASS_DB_PROCEDURE);
+
+	/*
+	 * Assign the default security label on a new procedure
+	 */
+	object.classId = ProcedureRelationId;
+	object.objectId = functionId;
+	object.objectSubId = 0;
+	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
+
+	pfree(tcontext);
+	pfree(ncontext);
+}
+
+/*
+ * sepgsql_proc_relabel
+ *
+ * It checks privileges to relabel the supplied function
+ * by the `seclabel'.
+ */
+void
+sepgsql_proc_relabel(Oid functionId, const char *seclabel)
+{
+	char	   *scontext = sepgsql_get_client_label();
+	char	   *tcontext;
+	char	   *audit_name;
+
+	audit_name = get_func_name(functionId);
+
+	/*
+	 * check db_procedure:{setattr relabelfrom} permission
+	 */
+	tcontext = sepgsql_get_label(ProcedureRelationId, functionId, 0);
+	sepgsql_check_perms(scontext,
+						tcontext,
+						SEPG_CLASS_DB_PROCEDURE,
+						SEPG_DB_PROCEDURE__SETATTR |
+						SEPG_DB_PROCEDURE__RELABELFROM,
+						audit_name,
+						true);
+	pfree(tcontext);
+
+	/*
+	 * check db_procedure:{relabelto} permission
+	 */
+	sepgsql_check_perms(scontext,
+						seclabel,
+						SEPG_CLASS_DB_PROCEDURE,
+						SEPG_DB_PROCEDURE__RELABELTO,
+						audit_name,
+						true);
+	pfree(audit_name);
+}
+
+/*
+ * sepgsql_proc_get_domtrans
+ *
+ * It computes security label of the client that shall be applied when
+ * the current client invokes the supplied function.
+ * This computed label is either same or different from the current one.
+ * If security policy informed the function is a trusted-procedure,
+ * we need to switch security label of the client during execution of
+ * the function.
+ *
+ * Also note that the translated label shall be allocated using palloc().
+ * So, need to switch memory context, if you want to hold the string in
+ * someone except for CurrentMemoryContext.
+ */
+char *
+sepgsql_proc_get_domtrans(Oid functionId)
+{
+	char   *scontext = sepgsql_get_client_label();
+	char   *tcontext;
+	char   *ncontext;
+
+	tcontext = sepgsql_get_label(ProcedureRelationId, functionId, 0);
+
+	ncontext = sepgsql_compute_create(scontext,
+									  tcontext,
+									  SEPG_CLASS_PROCESS);
+	pfree(tcontext);
+
+	return ncontext;
+}
diff --git a/contrib/sepgsql/relation.c b/contrib/sepgsql/relation.c
new file mode 100644
index 00000000000..ceaa6b02357
--- /dev/null
+++ b/contrib/sepgsql/relation.c
@@ -0,0 +1,267 @@
+/* -------------------------------------------------------------------------
+ *
+ * contrib/sepgsql/label.c
+ *
+ * Routines corresponding to relation/attribute objects
+ *
+ * Copyright (c) 2010-2011, PostgreSQL Global Development Group
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/sysattr.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_namespace.h"
+#include "commands/seclabel.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/tqual.h"
+
+#include "sepgsql.h"
+
+/*
+ * sepgsql_attribute_post_create
+ *
+ * This routine assigns a default security label on a newly defined
+ * column, using ALTER TABLE ... ADD COLUMN.
+ * Note that this routine is not invoked in the case of CREATE TABLE,
+ * although it also defines columns in addition to table.
+ */
+void
+sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum)
+{
+	char		   *scontext = sepgsql_get_client_label();
+	char		   *tcontext;
+	char		   *ncontext;
+	ObjectAddress	object;
+
+	/*
+	 * Only attributes within regular relation have individual
+	 * security labels.
+	 */
+	if (get_rel_relkind(relOid) != RELKIND_RELATION)
+		return;
+
+	/*
+	 * Compute a default security label when we create a new procedure
+	 * object under the specified namespace.
+	 */
+	scontext = sepgsql_get_client_label();
+	tcontext = sepgsql_get_label(RelationRelationId, relOid, 0);
+	ncontext = sepgsql_compute_create(scontext, tcontext,
+									  SEPG_CLASS_DB_COLUMN);
+	/*
+	 * Assign the default security label on a new procedure
+	 */
+	object.classId = RelationRelationId;
+	object.objectId = relOid;
+	object.objectSubId = attnum;
+	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
+
+	pfree(tcontext);
+	pfree(ncontext);
+}
+
+/*
+ * sepgsql_attribute_relabel
+ *
+ * It checks privileges to relabel the supplied column
+ * by the `seclabel'.
+ */
+void
+sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
+						  const char *seclabel)
+{
+	char	   *scontext = sepgsql_get_client_label();
+	char	   *tcontext;
+	char		audit_name[NAMEDATALEN * 2 + 10];
+
+	if (get_rel_relkind(relOid) != RELKIND_RELATION)
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("cannot set security label on non-regular columns")));
+
+	snprintf(audit_name, sizeof(audit_name), "%s.%s",
+			 get_rel_name(relOid), get_attname(relOid, attnum));
+
+	/*
+	 * check db_column:{setattr relabelfrom} permission
+	 */
+	tcontext = sepgsql_get_label(RelationRelationId, relOid, attnum);
+	sepgsql_check_perms(scontext,
+						tcontext,
+						SEPG_CLASS_DB_COLUMN,
+						SEPG_DB_COLUMN__SETATTR |
+						SEPG_DB_COLUMN__RELABELFROM,
+						audit_name,
+						true);
+	pfree(tcontext);
+
+	/*
+	 * check db_column:{relabelto} permission
+	 */
+	sepgsql_check_perms(scontext,
+						seclabel,
+						SEPG_CLASS_DB_COLUMN,
+						SEPG_DB_PROCEDURE__RELABELTO,
+						audit_name,
+						true);
+}
+
+/*
+ * sepgsql_relation_post_create
+ *
+ * The post creation hook of relation/attribute
+ */
+void
+sepgsql_relation_post_create(Oid relOid)
+{
+	Relation		rel;
+	ScanKeyData		skey;
+	SysScanDesc		sscan;
+	HeapTuple		tuple;
+	Form_pg_class	classForm;
+	ObjectAddress	object;
+	uint16			tclass;
+	char		   *scontext;	/* subject */
+	char		   *tcontext;	/* schema */
+	char		   *rcontext;	/* relation */
+	char		   *ccontext;	/* column */
+
+	/*
+	 * Fetch catalog record of the new relation. Because pg_class entry is
+	 * not visible right now, we need to scan the catalog using SnapshotSelf.
+	 */
+	rel = heap_open(RelationRelationId, AccessShareLock);
+
+	ScanKeyInit(&skey,
+				ObjectIdAttributeNumber,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(relOid));
+
+	sscan = systable_beginscan(rel, ClassOidIndexId, true,
+							   SnapshotSelf, 1, &skey);
+
+	tuple = systable_getnext(sscan);
+	if (!HeapTupleIsValid(tuple))
+		elog(ERROR, "catalog lookup failed for relation %u", relOid);
+
+	classForm = (Form_pg_class) GETSTRUCT(tuple);
+
+	if (classForm->relkind == RELKIND_RELATION)
+		tclass = SEPG_CLASS_DB_TABLE;
+	else if (classForm->relkind == RELKIND_SEQUENCE)
+		tclass = SEPG_CLASS_DB_SEQUENCE;
+	else if (classForm->relkind == RELKIND_VIEW)
+		tclass = SEPG_CLASS_DB_VIEW;
+	else
+		goto out;	/* No need to assign individual labels */
+
+	/*
+	 * Compute a default security label when we create a new relation
+	 * object under the specified namespace.
+	 */
+	scontext = sepgsql_get_client_label();
+	tcontext = sepgsql_get_label(NamespaceRelationId,
+								 classForm->relnamespace, 0);
+	rcontext = sepgsql_compute_create(scontext, tcontext, tclass);
+
+	/*
+	 * Assign the default security label on the new relation
+	 */
+	object.classId = RelationRelationId;
+	object.objectId = relOid;
+	object.objectSubId = 0;
+	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, rcontext);
+
+	/*
+	 * We also assigns a default security label on columns of the new
+	 * regular tables.
+	 */
+	if (classForm->relkind == RELKIND_RELATION)
+	{
+		AttrNumber	index;
+
+		ccontext = sepgsql_compute_create(scontext, rcontext,
+										  SEPG_CLASS_DB_COLUMN);
+		for (index = FirstLowInvalidHeapAttributeNumber + 1;
+			 index <= classForm->relnatts;
+			 index++)
+		{
+			if (index == InvalidAttrNumber)
+				continue;
+
+			if (index == ObjectIdAttributeNumber && !classForm->relhasoids)
+				continue;
+
+			object.classId = RelationRelationId;
+			object.objectId = relOid;
+			object.objectSubId = index;
+			SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ccontext);
+		}
+		pfree(ccontext);
+	}
+	pfree(rcontext);
+out:
+	systable_endscan(sscan);
+	heap_close(rel, AccessShareLock);
+}
+
+/*
+ * sepgsql_relation_relabel
+ *
+ * It checks privileges to relabel the supplied relation by the `seclabel'.
+ */
+void
+sepgsql_relation_relabel(Oid relOid, const char *seclabel)
+{
+	char	   *scontext = sepgsql_get_client_label();
+	char	   *tcontext;
+	char	   *audit_name;
+	char		relkind;
+	uint16_t	tclass = 0;
+
+	relkind = get_rel_relkind(relOid);
+	if (relkind == RELKIND_RELATION)
+		tclass = SEPG_CLASS_DB_TABLE;
+	else if (relkind == RELKIND_SEQUENCE)
+		tclass = SEPG_CLASS_DB_SEQUENCE;
+	else if (relkind == RELKIND_VIEW)
+		tclass = SEPG_CLASS_DB_VIEW;
+	else
+		ereport(ERROR,
+				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+				 errmsg("cannot set security labels on relations except "
+						"for tables, sequences or views")));
+
+	audit_name = get_rel_name(relOid);
+
+	/*
+	 * check db_xxx:{setattr relabelfrom} permission
+	 */
+	tcontext = sepgsql_get_label(RelationRelationId, relOid, 0);
+
+	sepgsql_check_perms(scontext,
+						tcontext,
+						tclass,
+						SEPG_DB_TABLE__SETATTR |
+						SEPG_DB_TABLE__RELABELFROM,
+						audit_name,
+						true);
+	pfree(tcontext);
+
+	/*
+	 * check db_xxx:{relabelto} permission
+	 */
+	sepgsql_check_perms(scontext,
+						seclabel,
+						tclass,
+						SEPG_DB_TABLE__RELABELTO,
+						audit_name,
+						true);
+}
diff --git a/contrib/sepgsql/schema.c b/contrib/sepgsql/schema.c
new file mode 100644
index 00000000000..df33a027353
--- /dev/null
+++ b/contrib/sepgsql/schema.c
@@ -0,0 +1,98 @@
+/* -------------------------------------------------------------------------
+ *
+ * contrib/sepgsql/schema.c
+ *
+ * Routines corresponding to schema objects
+ *
+ * Copyright (c) 2010-2011, PostgreSQL Global Development Group
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/pg_namespace.h"
+#include "commands/seclabel.h"
+#include "utils/lsyscache.h"
+
+#include "sepgsql.h"
+
+/*
+ * sepgsql_schema_post_create
+ *
+ * This routine assigns a default security label on a newly defined
+ * schema.
+ */
+void
+sepgsql_schema_post_create(Oid namespaceId)
+{
+	char		   *scontext = sepgsql_get_client_label();
+	char		   *tcontext;
+	char		   *ncontext;
+	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.
+	 */
+	tcontext = "system_u:object_r:sepgsql_db_t:s0";
+
+	/*
+	 * 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);
+
+	/*
+	 * Assign the default security label on a new procedure
+	 */
+	object.classId = NamespaceRelationId;
+	object.objectId = namespaceId;
+	object.objectSubId = 0;
+	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
+
+	pfree(ncontext);
+}
+
+/*
+ * sepgsql_schema_relabel
+ *
+ * It checks privileges to relabel the supplied schema
+ * by the `seclabel'.
+ */
+void
+sepgsql_schema_relabel(Oid namespaceId, const char *seclabel)
+{
+	char	   *scontext = sepgsql_get_client_label();
+	char	   *tcontext;
+	char	   *audit_name;
+
+	audit_name = get_namespace_name(namespaceId);
+
+	/*
+	 * check db_schema:{setattr relabelfrom} permission
+	 */
+	tcontext = sepgsql_get_label(NamespaceRelationId, namespaceId, 0);
+
+	sepgsql_check_perms(scontext,
+						tcontext,
+						SEPG_CLASS_DB_SCHEMA,
+						SEPG_DB_SCHEMA__SETATTR |
+						SEPG_DB_SCHEMA__RELABELFROM,
+						audit_name,
+						true);
+
+	/*
+	 * check db_schema:{relabelto} permission
+	 */
+	sepgsql_check_perms(scontext,
+						seclabel,
+						SEPG_CLASS_DB_SCHEMA,
+						SEPG_DB_SCHEMA__RELABELTO,
+						audit_name,
+						true);
+
+	pfree(tcontext);
+	pfree(audit_name);
+}
diff --git a/contrib/sepgsql/selinux.c b/contrib/sepgsql/selinux.c
new file mode 100644
index 00000000000..a67bd567112
--- /dev/null
+++ b/contrib/sepgsql/selinux.c
@@ -0,0 +1,631 @@
+/* -------------------------------------------------------------------------
+ *
+ * contrib/sepgsql/selinux.c
+ *
+ * Interactions between userspace and selinux in kernelspace,
+ * using libselinux api.
+ *
+ * Copyright (c) 2010-2011, PostgreSQL Global Development Group
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "lib/stringinfo.h"
+
+#include "sepgsql.h"
+
+/*
+ * selinux_catalog
+ *
+ * This mapping table enables to translate the name of object classes and
+ * access vectors to/from their own codes.
+ * When we ask SELinux whether the required privileges are allowed or not,
+ * we use security_compute_av(3). It needs us to represent object classes
+ * and access vectors using 'external' codes defined in the security policy.
+ * It is determinded in the runtime, not build time. So, it needs an internal
+ * service to translate object class/access vectors which we want to check
+ * into the code which kernel want to be given.
+ */
+static struct
+{
+	const char		   *class_name;
+	uint16				class_code;
+	struct
+	{
+		const char	   *av_name;
+		uint32			av_code;
+	} av[32];
+} selinux_catalog[] = {
+	{
+		"process",				SEPG_CLASS_PROCESS,
+		{
+			{ "transition",		SEPG_PROCESS__TRANSITION },
+			{ NULL, 0UL }
+		}
+	},
+	{
+		"file",					SEPG_CLASS_FILE,
+		{
+			{ "read",			SEPG_FILE__READ },
+			{ "write",			SEPG_FILE__WRITE },
+			{ "create",			SEPG_FILE__CREATE },
+			{ "getattr",		SEPG_FILE__GETATTR },
+			{ "unlink",			SEPG_FILE__UNLINK },
+			{ "rename",			SEPG_FILE__RENAME },
+			{ "append",			SEPG_FILE__APPEND },
+			{ NULL, 0UL }
+		}
+	},
+	{
+		"dir",					SEPG_CLASS_DIR,
+		{
+			{ "read",			SEPG_DIR__READ },
+			{ "write",			SEPG_DIR__WRITE },
+			{ "create",			SEPG_DIR__CREATE },
+			{ "getattr",		SEPG_DIR__GETATTR },
+			{ "unlink",			SEPG_DIR__UNLINK },
+			{ "rename",			SEPG_DIR__RENAME },
+			{ "search",			SEPG_DIR__SEARCH },
+			{ "add_name",		SEPG_DIR__ADD_NAME },
+			{ "remove_name",	SEPG_DIR__REMOVE_NAME },
+			{ "rmdir",			SEPG_DIR__RMDIR },
+			{ "reparent",		SEPG_DIR__REPARENT },
+			{ NULL, 0UL }
+		}
+	},
+	{
+		"lnk_file",				SEPG_CLASS_LNK_FILE,
+		{
+			{ "read",			SEPG_LNK_FILE__READ },
+			{ "write",			SEPG_LNK_FILE__WRITE },
+			{ "create",			SEPG_LNK_FILE__CREATE },
+			{ "getattr",		SEPG_LNK_FILE__GETATTR },
+			{ "unlink",			SEPG_LNK_FILE__UNLINK },
+			{ "rename",			SEPG_LNK_FILE__RENAME },
+			{ NULL, 0UL }
+		}
+	},
+	{
+		"chr_file",				SEPG_CLASS_CHR_FILE,
+		{
+			{ "read",			SEPG_CHR_FILE__READ },
+			{ "write",			SEPG_CHR_FILE__WRITE },
+			{ "create",			SEPG_CHR_FILE__CREATE },
+			{ "getattr",		SEPG_CHR_FILE__GETATTR },
+			{ "unlink",			SEPG_CHR_FILE__UNLINK },
+			{ "rename",			SEPG_CHR_FILE__RENAME },
+			{ NULL, 0UL }
+		}
+	},
+	{
+		"blk_file",				SEPG_CLASS_BLK_FILE,
+		{
+			{ "read",			SEPG_BLK_FILE__READ },
+			{ "write",			SEPG_BLK_FILE__WRITE },
+			{ "create",			SEPG_BLK_FILE__CREATE },
+			{ "getattr",		SEPG_BLK_FILE__GETATTR },
+			{ "unlink",			SEPG_BLK_FILE__UNLINK },
+			{ "rename",			SEPG_BLK_FILE__RENAME },
+			{ NULL, 0UL }
+		}
+	},
+	{
+		"sock_file",			SEPG_CLASS_SOCK_FILE,
+		{
+			{ "read",			SEPG_SOCK_FILE__READ },
+			{ "write",			SEPG_SOCK_FILE__WRITE },
+			{ "create",			SEPG_SOCK_FILE__CREATE },
+			{ "getattr",		SEPG_SOCK_FILE__GETATTR },
+			{ "unlink",			SEPG_SOCK_FILE__UNLINK },
+			{ "rename",			SEPG_SOCK_FILE__RENAME },
+			{ NULL, 0UL }
+		}
+	},
+	{
+		"fifo_file",			SEPG_CLASS_FIFO_FILE,
+		{
+			{ "read",			SEPG_FIFO_FILE__READ },
+			{ "write",			SEPG_FIFO_FILE__WRITE },
+			{ "create",			SEPG_FIFO_FILE__CREATE },
+			{ "getattr",		SEPG_FIFO_FILE__GETATTR },
+			{ "unlink",			SEPG_FIFO_FILE__UNLINK },
+			{ "rename",			SEPG_FIFO_FILE__RENAME },
+			{ NULL, 0UL }
+		}
+	},
+	{
+		"db_database",			SEPG_CLASS_DB_DATABASE,
+		{
+			{ "create",			SEPG_DB_DATABASE__CREATE },
+			{ "drop",			SEPG_DB_DATABASE__DROP },
+			{ "getattr",		SEPG_DB_DATABASE__GETATTR },
+			{ "setattr",		SEPG_DB_DATABASE__SETATTR },
+			{ "relabelfrom",	SEPG_DB_DATABASE__RELABELFROM },
+			{ "relabelto",		SEPG_DB_DATABASE__RELABELTO },
+			{ "access",			SEPG_DB_DATABASE__ACCESS },
+			{ "load_module",	SEPG_DB_DATABASE__LOAD_MODULE },
+			{ NULL, 0UL },
+		}
+	},
+	{
+		"db_schema",			SEPG_CLASS_DB_SCHEMA,
+		{
+			{ "create",			SEPG_DB_SCHEMA__CREATE },
+			{ "drop",			SEPG_DB_SCHEMA__DROP },
+			{ "getattr",		SEPG_DB_SCHEMA__GETATTR },
+			{ "setattr",		SEPG_DB_SCHEMA__SETATTR },
+			{ "relabelfrom",	SEPG_DB_SCHEMA__RELABELFROM },
+			{ "relabelto",		SEPG_DB_SCHEMA__RELABELTO },
+			{ "search",			SEPG_DB_SCHEMA__SEARCH },
+			{ "add_name",		SEPG_DB_SCHEMA__ADD_NAME },
+			{ "remove_name",	SEPG_DB_SCHEMA__REMOVE_NAME },
+			{ NULL, 0UL },
+		}
+	},
+	{
+		"db_table",				SEPG_CLASS_DB_TABLE,
+		{
+			{ "create",			SEPG_DB_TABLE__CREATE },
+			{ "drop",			SEPG_DB_TABLE__DROP },
+			{ "getattr",		SEPG_DB_TABLE__GETATTR },
+			{ "setattr",		SEPG_DB_TABLE__SETATTR },
+			{ "relabelfrom",	SEPG_DB_TABLE__RELABELFROM },
+			{ "relabelto",		SEPG_DB_TABLE__RELABELTO },
+			{ "select",			SEPG_DB_TABLE__SELECT },
+			{ "update",			SEPG_DB_TABLE__UPDATE },
+			{ "insert",			SEPG_DB_TABLE__INSERT },
+			{ "delete",			SEPG_DB_TABLE__DELETE },
+			{ "lock",			SEPG_DB_TABLE__LOCK },
+			{ NULL, 0UL },
+		}
+	},
+	{
+		"db_sequence",			SEPG_CLASS_DB_SEQUENCE,
+		{
+			{ "create",			SEPG_DB_SEQUENCE__CREATE },
+			{ "drop",			SEPG_DB_SEQUENCE__DROP },
+			{ "getattr",		SEPG_DB_SEQUENCE__GETATTR },
+			{ "setattr",		SEPG_DB_SEQUENCE__SETATTR },
+			{ "relabelfrom",	SEPG_DB_SEQUENCE__RELABELFROM },
+			{ "relabelto",		SEPG_DB_SEQUENCE__RELABELTO },
+			{ "get_value",		SEPG_DB_SEQUENCE__GET_VALUE },
+			{ "next_value",		SEPG_DB_SEQUENCE__NEXT_VALUE },
+			{ "set_value",		SEPG_DB_SEQUENCE__SET_VALUE },
+			{ NULL, 0UL },
+		}
+	},
+	{
+		"db_procedure",			SEPG_CLASS_DB_PROCEDURE,
+		{
+			{ "create",			SEPG_DB_PROCEDURE__CREATE },
+			{ "drop",			SEPG_DB_PROCEDURE__DROP },
+			{ "getattr",		SEPG_DB_PROCEDURE__GETATTR },
+			{ "setattr",		SEPG_DB_PROCEDURE__SETATTR },
+			{ "relabelfrom",	SEPG_DB_PROCEDURE__RELABELFROM },
+			{ "relabelto",		SEPG_DB_PROCEDURE__RELABELTO },
+			{ "execute",		SEPG_DB_PROCEDURE__EXECUTE },
+			{ "entrypoint",		SEPG_DB_PROCEDURE__ENTRYPOINT },
+			{ "install",		SEPG_DB_PROCEDURE__INSTALL },
+			{ NULL, 0UL },
+		}
+	},
+	{
+		"db_column",			SEPG_CLASS_DB_COLUMN,
+		{
+			{ "create",			SEPG_DB_COLUMN__CREATE },
+			{ "drop",			SEPG_DB_COLUMN__DROP },
+			{ "getattr",		SEPG_DB_COLUMN__GETATTR },
+			{ "setattr",		SEPG_DB_COLUMN__SETATTR },
+			{ "relabelfrom",	SEPG_DB_COLUMN__RELABELFROM },
+			{ "relabelto",		SEPG_DB_COLUMN__RELABELTO },
+			{ "select",			SEPG_DB_COLUMN__SELECT },
+			{ "update",			SEPG_DB_COLUMN__UPDATE },
+			{ "insert",			SEPG_DB_COLUMN__INSERT },
+			{ NULL, 0UL },
+		}
+	},
+	{
+		"db_tuple",				SEPG_CLASS_DB_TUPLE,
+		{
+			{ "relabelfrom",	SEPG_DB_TUPLE__RELABELFROM },
+			{ "relabelto",		SEPG_DB_TUPLE__RELABELTO },
+			{ "select",			SEPG_DB_TUPLE__SELECT },
+			{ "update",			SEPG_DB_TUPLE__UPDATE },
+			{ "insert",			SEPG_DB_TUPLE__INSERT },
+			{ "delete",			SEPG_DB_TUPLE__DELETE },
+			{ NULL, 0UL },
+		}
+	},
+	{
+		"db_blob",				SEPG_CLASS_DB_BLOB,
+		{
+			{ "create",			SEPG_DB_BLOB__CREATE },
+			{ "drop",			SEPG_DB_BLOB__DROP },
+			{ "getattr",		SEPG_DB_BLOB__GETATTR },
+			{ "setattr",		SEPG_DB_BLOB__SETATTR },
+			{ "relabelfrom",	SEPG_DB_BLOB__RELABELFROM },
+			{ "relabelto",		SEPG_DB_BLOB__RELABELTO },
+			{ "read",			SEPG_DB_BLOB__READ },
+			{ "write",			SEPG_DB_BLOB__WRITE },
+			{ "import",			SEPG_DB_BLOB__IMPORT },
+			{ "export",			SEPG_DB_BLOB__EXPORT },
+			{ NULL, 0UL },
+		}
+	},
+	{
+		"db_language",			SEPG_CLASS_DB_LANGUAGE,
+		{
+			{ "create",			SEPG_DB_LANGUAGE__CREATE },
+			{ "drop",			SEPG_DB_LANGUAGE__DROP },
+			{ "getattr",		SEPG_DB_LANGUAGE__GETATTR },
+			{ "setattr",		SEPG_DB_LANGUAGE__SETATTR },
+			{ "relabelfrom",	SEPG_DB_LANGUAGE__RELABELFROM },
+			{ "relabelto",		SEPG_DB_LANGUAGE__RELABELTO },
+			{ "implement",		SEPG_DB_LANGUAGE__IMPLEMENT },
+			{ "execute",		SEPG_DB_LANGUAGE__EXECUTE },
+			{ NULL, 0UL },
+		}
+	},
+	{
+		"db_view",				SEPG_CLASS_DB_VIEW,
+		{
+			{ "create",			SEPG_DB_VIEW__CREATE },
+			{ "drop",			SEPG_DB_VIEW__DROP },
+			{ "getattr",		SEPG_DB_VIEW__GETATTR },
+			{ "setattr",		SEPG_DB_VIEW__SETATTR },
+			{ "relabelfrom",	SEPG_DB_VIEW__RELABELFROM },
+			{ "relabelto",		SEPG_DB_VIEW__RELABELTO },
+			{ "expand",			SEPG_DB_VIEW__EXPAND },
+			{ NULL, 0UL },
+		}
+	},
+};
+
+/*
+ * sepgsql_mode
+ *
+ * SEPGSQL_MODE_DISABLED: Disabled on runtime
+ * SEPGSQL_MODE_DEFAULT: Same as system settings
+ * SEPGSQL_MODE_PERMISSIVE: Always permissive mode
+ * SEPGSQL_MODE_INTERNAL: Same as permissive, except for no audit logs
+ */
+static int	sepgsql_mode = SEPGSQL_MODE_INTERNAL;
+
+/*
+ * sepgsql_is_enabled
+ */
+bool
+sepgsql_is_enabled(void)
+{
+	return (sepgsql_mode != SEPGSQL_MODE_DISABLED ? true : false);
+}
+
+/*
+ * sepgsql_get_mode
+ */
+int
+sepgsql_get_mode(void)
+{
+	return sepgsql_mode;
+}
+
+/*
+ * sepgsql_set_mode
+ */
+int
+sepgsql_set_mode(int new_mode)
+{
+	int		old_mode = sepgsql_mode;
+
+	sepgsql_mode = new_mode;
+
+	return old_mode;
+}
+
+/*
+ * sepgsql_getenforce
+ *
+ * It returns whether the current working mode tries to enforce access
+ * control decision, or not. It shall be enforced when sepgsql_mode is
+ * SEPGSQL_MODE_DEFAULT and system is running in enforcing mode.
+ */
+bool
+sepgsql_getenforce(void)
+{
+	if (sepgsql_mode == SEPGSQL_MODE_DEFAULT &&
+		security_getenforce() > 0)
+		return true;
+
+	return false;
+}
+
+/*
+ * sepgsql_audit_log
+ *
+ * It generates a security audit record. In the default, it writes out
+ * audit records into standard PG's logfile. It also allows to set up
+ * external audit log receiver, such as auditd in Linux, using the
+ * sepgsql_audit_hook.
+ *
+ * SELinux can control what should be audited and should not using
+ * "auditdeny" and "auditallow" rules in the security policy. In the
+ * default, all the access violations are audited, and all the access
+ * allowed are not audited. But we can set up the security policy, so
+ * we can have exceptions. So, it is necessary to follow the suggestion
+ * come from the security policy. (av_decision.auditallow and auditdeny)
+ *
+ * Security audit is an important feature, because it enables us to check
+ * what was happen if we have a security incident. In fact, ISO/IEC15408
+ * defines several security functionalities for audit features.
+ */
+void
+sepgsql_audit_log(bool denied,
+				  const char *scontext,
+				  const char *tcontext,
+				  uint16 tclass,
+				  uint32 audited,
+				  const char *audit_name)
+{
+	StringInfoData	buf;
+	const char	   *class_name;
+	const char	   *av_name;
+	int				i;
+
+	/* lookup name of the object class */
+	Assert(tclass < SEPG_CLASS_MAX);
+	class_name = selinux_catalog[tclass].class_name;
+
+	/* lookup name of the permissions */
+	initStringInfo(&buf);
+	appendStringInfo(&buf, "%s {",
+					 (denied ? "denied" : "allowed"));
+	for (i=0; selinux_catalog[tclass].av[i].av_name; i++)
+	{
+		if (audited & (1UL << i))
+		{
+			av_name = selinux_catalog[tclass].av[i].av_name;
+			appendStringInfo(&buf, " %s", av_name);
+		}
+	}
+	appendStringInfo(&buf, " }");
+
+	/*
+	 * Call external audit module, if loaded
+	 */
+	appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s",
+					 scontext, tcontext, class_name);
+	if (audit_name)
+		appendStringInfo(&buf, " name=%s", audit_name);
+
+	ereport(LOG, (errmsg("SELinux: %s", buf.data)));
+}
+
+/*
+ * sepgsql_compute_avd
+ *
+ * It actually asks SELinux what permissions are allowed on a pair of
+ * the security contexts and object class. It also returns what permissions
+ * should be audited on access violation or allowed.
+ * In most cases, subject's security context (scontext) is a client, and
+ * target security context (tcontext) is a database object.
+ *
+ * The access control decision shall be set on the given av_decision.
+ * The av_decision.allowed has a bitmask of SEPG_<class>__<perms>
+ * to suggest a set of allowed actions in this object class.
+ */
+void
+sepgsql_compute_avd(const char *scontext,
+					const char *tcontext,
+					uint16 tclass,
+					struct av_decision *avd)
+{
+	const char		   *tclass_name;
+	security_class_t	tclass_ex;
+	struct av_decision	avd_ex;
+	int					i, deny_unknown = security_deny_unknown();
+
+	/* Get external code of the object class*/
+	Assert(tclass < SEPG_CLASS_MAX);
+	Assert(tclass == selinux_catalog[tclass].class_code);
+
+	tclass_name = selinux_catalog[tclass].class_name;
+	tclass_ex = string_to_security_class(tclass_name);
+
+	if (tclass_ex == 0)
+	{
+		/*
+		 * If the current security policy does not support permissions
+		 * corresponding to database objects, we fill up them with dummy
+		 * data.
+		 * If security_deny_unknown() returns positive value, undefined
+		 * permissions should be denied. Otherwise, allowed
+		 */
+		avd->allowed = (security_deny_unknown() > 0 ? 0 : ~0);
+		avd->auditallow = 0U;
+		avd->auditdeny =  ~0U;
+		avd->flags = 0;
+
+		return;
+	}
+
+	/*
+	 * Ask SELinux what is allowed set of permissions on a pair of the
+	 * security contexts and the given object class.
+	 */
+	if (security_compute_av_flags_raw((security_context_t)scontext,
+									  (security_context_t)tcontext,
+									  tclass_ex, 0, &avd_ex) < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("SELinux could not compute av_decision: "
+						"scontext=%s tcontext=%s tclass=%s",
+						scontext, tcontext, tclass_name)));
+
+	/*
+	 * SELinux returns its access control decision as a set of permissions
+	 * represented in external code which depends on run-time environment.
+	 * So, we need to translate it to the internal representation before
+	 * returning results for the caller.
+	 */
+	memset(avd, 0, sizeof(struct av_decision));
+
+	for (i=0; selinux_catalog[tclass].av[i].av_name; i++)
+	{
+		access_vector_t	av_code_ex;
+		const char	   *av_name = selinux_catalog[tclass].av[i].av_name;
+		uint32			av_code = selinux_catalog[tclass].av[i].av_code;
+
+		av_code_ex = string_to_av_perm(tclass_ex, av_name);
+		if (av_code_ex == 0)
+		{
+			/* fill up undefined permissions */
+			if (!deny_unknown)
+				avd->allowed |= av_code;
+			avd->auditdeny |= av_code;
+
+			continue;
+		}
+
+		if (avd_ex.allowed & av_code_ex)
+			avd->allowed |= av_code;
+		if (avd_ex.auditallow & av_code_ex)
+			avd->auditallow |= av_code;
+		if (avd_ex.auditdeny & av_code_ex)
+			avd->auditdeny |= av_code;
+	}
+
+	return;
+}
+
+/*
+ * sepgsql_compute_create
+ *
+ * It returns a default security context to be assigned on a new database
+ * object. SELinux compute it based on a combination of client, upper object
+ * which owns the new object and object class.
+ *
+ * For example, when a client (staff_u:staff_r:staff_t:s0) tries to create
+ * a new table within a schema (system_u:object_r:sepgsql_schema_t:s0),
+ * SELinux looks-up its security policy. If it has a special rule on the
+ * combination of these security contexts and object class (db_table),
+ * it returns the security context suggested by the special rule.
+ * Otherwise, it returns the security context of schema, as is.
+ *
+ * We expect the caller already applies sanity/validation checks on the
+ * given security context.
+ *
+ * scontext: security context of the subject (mostly, peer process).
+ * tcontext: security context of the the upper database object.
+ * tclass: class code (SEPG_CLASS_*) of the new object in creation
+ */
+char *
+sepgsql_compute_create(const char *scontext,
+					   const char *tcontext,
+					   uint16 tclass)
+{
+	security_context_t	ncontext;
+	security_class_t	tclass_ex;
+	const char		   *tclass_name;
+	char			   *result;
+
+	/* Get external code of the object class*/
+	Assert(tclass < SEPG_CLASS_MAX);
+
+	tclass_name = selinux_catalog[tclass].class_name;
+	tclass_ex = string_to_security_class(tclass_name);
+
+	/*
+	 * Ask SELinux what is the default context for the given object class
+	 * on a pair of security contexts
+	 */
+	if (security_compute_create_raw((security_context_t)scontext,
+									(security_context_t)tcontext,
+									tclass_ex, &ncontext) < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INTERNAL_ERROR),
+				 errmsg("SELinux could not compute a new context: "
+						"scontext=%s tcontext=%s tclass=%s",
+						scontext, tcontext, tclass_name)));
+
+	/*
+	 * libselinux returns malloc()'ed string, so we need to copy it
+	 * on the palloc()'ed region.
+	 */
+	PG_TRY();
+	{
+		result = pstrdup(ncontext);
+	}
+	PG_CATCH();
+	{
+		freecon(ncontext);
+		PG_RE_THROW();
+	}
+	PG_END_TRY();
+	freecon(ncontext);
+
+	return result;
+}
+
+/*
+ * sepgsql_check_perms
+ *
+ * It makes access control decision without userspace caching mechanism.
+ * If SELinux denied the required accesses on the pair of security labels,
+ * it raises an error or returns false.
+ *
+ * scontext: security label of the subject (mostly, peer process)
+ * tcontext: security label of the object being referenced
+ * tclass: class code (SEPG_CLASS_*) of the object being referenced
+ * required: a mask of required permissions (SEPG_<class>__<perm>)
+ * audit_name: a human readable object name for audit logs, or NULL.
+ * abort: true, if caller wants to raise an error on access violation
+ */
+bool
+sepgsql_check_perms(const char *scontext,
+					const char *tcontext,
+					uint16 tclass,
+					uint32 required,
+					const char *audit_name,
+					bool abort)
+{
+	struct av_decision	avd;
+	uint32		denied;
+	uint32		audited;
+	bool		result = true;
+
+	sepgsql_compute_avd(scontext, tcontext, tclass, &avd);
+
+	denied = required & ~avd.allowed;
+
+	if (sepgsql_get_debug_audit())
+		audited = (denied ? denied : required);
+	else
+		audited = (denied ? (denied & avd.auditdeny)
+						  : (required & avd.auditallow));
+
+	if (denied &&
+		sepgsql_getenforce() > 0 &&
+		(avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) == 0)
+		result = false;
+
+	/*
+	 * It records a security audit for the request, if needed.
+	 * But, when SE-PgSQL performs 'internal' mode, it needs to keep silent.
+	 */
+	if (audited && sepgsql_mode != SEPGSQL_MODE_INTERNAL)
+	{
+		sepgsql_audit_log(denied,
+						  scontext,
+						  tcontext,
+						  tclass,
+						  audited,
+						  audit_name);
+	}
+
+	if (!result && abort)
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+				 errmsg("SELinux: security policy violation")));
+	return result;
+}
diff --git a/contrib/sepgsql/sepgsql-regtest.te b/contrib/sepgsql/sepgsql-regtest.te
new file mode 100644
index 00000000000..66666d0c38a
--- /dev/null
+++ b/contrib/sepgsql/sepgsql-regtest.te
@@ -0,0 +1,59 @@
+policy_module(sepgsql-regtest, 1.01)
+
+## <desc>
+## <p>
+## Allow to launch regression test of SE-PostgreSQL
+## Don't switch to TRUE in normal cases
+## </p>
+## </desc>
+gen_tunable(sepgsql_regression_test_mode, false)
+
+#
+# Test domains for database administrators
+#
+role sepgsql_regtest_dba_r;
+userdom_base_user_template(sepgsql_regtest_dba)
+userdom_manage_home_role(sepgsql_regtest_dba_r, sepgsql_regtest_dba_t)
+userdom_write_user_tmp_sockets(sepgsql_regtest_user_t)
+optional_policy(`
+	postgresql_admin(sepgsql_regtest_dba_t, sepgsql_regtest_dba_r)
+	postgresql_stream_connect(sepgsql_regtest_dba_t)
+')
+optional_policy(`
+	unconfined_stream_connect(sepgsql_regtest_dba_t)
+	unconfined_rw_pipes(sepgsql_regtest_dba_t)
+')
+
+#
+# Dummy domain for unpriv users
+#
+role sepgsql_regtest_user_r;
+userdom_base_user_template(sepgsql_regtest_user)
+userdom_manage_home_role(sepgsql_regtest_user_r, sepgsql_regtest_user_t)
+userdom_write_user_tmp_sockets(sepgsql_regtest_user_t)
+optional_policy(`
+	postgresql_role(sepgsql_regtest_user_r, sepgsql_regtest_user_t)
+	postgresql_stream_connect(sepgsql_regtest_user_t)
+')
+optional_policy(`
+	unconfined_stream_connect(sepgsql_regtest_user_t)
+	unconfined_rw_pipes(sepgsql_regtest_user_t)
+')
+
+#
+# Rules to launch psql in the dummy domains
+#
+optional_policy(`
+	gen_require(`
+		role unconfined_r;
+		type unconfined_t;
+		type sepgsql_trusted_proc_t;
+	')
+	tunable_policy(`sepgsql_regression_test_mode',`
+		allow unconfined_t sepgsql_regtest_dba_t : process { transition };
+		allow unconfined_t sepgsql_regtest_user_t : process { transition };
+	')
+	role unconfined_r types sepgsql_regtest_dba_t;
+	role unconfined_r types sepgsql_regtest_user_t;
+	role unconfined_r types sepgsql_trusted_proc_t;
+')
diff --git a/contrib/sepgsql/sepgsql.h b/contrib/sepgsql/sepgsql.h
new file mode 100644
index 00000000000..1a27818fbe8
--- /dev/null
+++ b/contrib/sepgsql/sepgsql.h
@@ -0,0 +1,288 @@
+/* -------------------------------------------------------------------------
+ *
+ * contrib/sepgsql/sepgsql.h
+ *
+ * Definitions corresponding to SE-PostgreSQL
+ *
+ * Copyright (c) 2010-2011, PostgreSQL Global Development Group
+ *
+ * -------------------------------------------------------------------------
+ */
+#ifndef SEPGSQL_H
+#define SEPGSQL_H
+
+#include "catalog/objectaddress.h"
+#include <selinux/selinux.h>
+
+/*
+ * SE-PostgreSQL Label Tag
+ */
+#define SEPGSQL_LABEL_TAG			"selinux"
+
+/*
+ * SE-PostgreSQL performing mode
+ */
+#define SEPGSQL_MODE_DEFAULT		1
+#define SEPGSQL_MODE_PERMISSIVE		2
+#define SEPGSQL_MODE_INTERNAL		3
+#define SEPGSQL_MODE_DISABLED		4
+
+/*
+ * Internally used code of object classes
+ */
+#define SEPG_CLASS_PROCESS			0
+#define SEPG_CLASS_FILE				1
+#define SEPG_CLASS_DIR				2
+#define SEPG_CLASS_LNK_FILE			3
+#define SEPG_CLASS_CHR_FILE			4
+#define SEPG_CLASS_BLK_FILE			5
+#define SEPG_CLASS_SOCK_FILE		6
+#define SEPG_CLASS_FIFO_FILE		7
+#define SEPG_CLASS_DB_DATABASE		8
+#define SEPG_CLASS_DB_SCHEMA		9
+#define SEPG_CLASS_DB_TABLE			10
+#define SEPG_CLASS_DB_SEQUENCE		11
+#define SEPG_CLASS_DB_PROCEDURE		12
+#define SEPG_CLASS_DB_COLUMN		13
+#define SEPG_CLASS_DB_TUPLE			14
+#define SEPG_CLASS_DB_BLOB			15
+#define SEPG_CLASS_DB_LANGUAGE		16
+#define SEPG_CLASS_DB_VIEW			17
+#define SEPG_CLASS_MAX				18
+
+/*
+ * Internally used code of access vectors
+ */
+#define SEPG_PROCESS__TRANSITION			(1<<0)
+
+#define SEPG_FILE__READ						(1<<0)
+#define SEPG_FILE__WRITE					(1<<1)
+#define SEPG_FILE__CREATE					(1<<2)
+#define SEPG_FILE__GETATTR					(1<<3)
+#define SEPG_FILE__UNLINK					(1<<4)
+#define SEPG_FILE__RENAME					(1<<5)
+#define SEPG_FILE__APPEND					(1<<6)
+
+#define SEPG_DIR__READ						(SEPG_FILE__READ)
+#define SEPG_DIR__WRITE						(SEPG_FILE__WRITE)
+#define SEPG_DIR__CREATE					(SEPG_FILE__CREATE)
+#define SEPG_DIR__GETATTR					(SEPG_FILE__GETATTR)
+#define SEPG_DIR__UNLINK					(SEPG_FILE__UNLINK)
+#define SEPG_DIR__RENAME					(SEPG_FILE__RENAME)
+#define SEPG_DIR__SEARCH					(1<<6)
+#define SEPG_DIR__ADD_NAME					(1<<7)
+#define SEPG_DIR__REMOVE_NAME				(1<<8)
+#define SEPG_DIR__RMDIR						(1<<9)
+#define SEPG_DIR__REPARENT					(1<<10)
+
+#define SEPG_LNK_FILE__READ					(SEPG_FILE__READ)
+#define SEPG_LNK_FILE__WRITE				(SEPG_FILE__WRITE)
+#define SEPG_LNK_FILE__CREATE				(SEPG_FILE__CREATE)
+#define SEPG_LNK_FILE__GETATTR				(SEPG_FILE__GETATTR)
+#define SEPG_LNK_FILE__UNLINK				(SEPG_FILE__UNLINK)
+#define SEPG_LNK_FILE__RENAME				(SEPG_FILE__RENAME)
+
+#define SEPG_CHR_FILE__READ					(SEPG_FILE__READ)
+#define SEPG_CHR_FILE__WRITE				(SEPG_FILE__WRITE)
+#define SEPG_CHR_FILE__CREATE				(SEPG_FILE__CREATE)
+#define SEPG_CHR_FILE__GETATTR				(SEPG_FILE__GETATTR)
+#define SEPG_CHR_FILE__UNLINK				(SEPG_FILE__UNLINK)
+#define SEPG_CHR_FILE__RENAME				(SEPG_FILE__RENAME)
+
+#define SEPG_BLK_FILE__READ					(SEPG_FILE__READ)
+#define SEPG_BLK_FILE__WRITE				(SEPG_FILE__WRITE)
+#define SEPG_BLK_FILE__CREATE				(SEPG_FILE__CREATE)
+#define SEPG_BLK_FILE__GETATTR				(SEPG_FILE__GETATTR)
+#define SEPG_BLK_FILE__UNLINK				(SEPG_FILE__UNLINK)
+#define SEPG_BLK_FILE__RENAME				(SEPG_FILE__RENAME)
+
+#define SEPG_SOCK_FILE__READ				(SEPG_FILE__READ)
+#define SEPG_SOCK_FILE__WRITE				(SEPG_FILE__WRITE)
+#define SEPG_SOCK_FILE__CREATE				(SEPG_FILE__CREATE)
+#define SEPG_SOCK_FILE__GETATTR				(SEPG_FILE__GETATTR)
+#define SEPG_SOCK_FILE__UNLINK				(SEPG_FILE__UNLINK)
+#define SEPG_SOCK_FILE__RENAME				(SEPG_FILE__RENAME)
+
+#define SEPG_FIFO_FILE__READ				(SEPG_FILE__READ)
+#define SEPG_FIFO_FILE__WRITE				(SEPG_FILE__WRITE)
+#define SEPG_FIFO_FILE__CREATE				(SEPG_FILE__CREATE)
+#define SEPG_FIFO_FILE__GETATTR				(SEPG_FILE__GETATTR)
+#define SEPG_FIFO_FILE__UNLINK				(SEPG_FILE__UNLINK)
+#define SEPG_FIFO_FILE__RENAME				(SEPG_FILE__RENAME)
+
+#define SEPG_DB_DATABASE__CREATE			(1<<0)
+#define SEPG_DB_DATABASE__DROP				(1<<1)
+#define SEPG_DB_DATABASE__GETATTR			(1<<2)
+#define SEPG_DB_DATABASE__SETATTR			(1<<3)
+#define SEPG_DB_DATABASE__RELABELFROM		(1<<4)
+#define SEPG_DB_DATABASE__RELABELTO			(1<<5)
+#define SEPG_DB_DATABASE__ACCESS			(1<<6)
+#define SEPG_DB_DATABASE__LOAD_MODULE		(1<<7)
+
+#define SEPG_DB_SCHEMA__CREATE				(SEPG_DB_DATABASE__CREATE)
+#define SEPG_DB_SCHEMA__DROP				(SEPG_DB_DATABASE__DROP)
+#define SEPG_DB_SCHEMA__GETATTR				(SEPG_DB_DATABASE__GETATTR)
+#define SEPG_DB_SCHEMA__SETATTR				(SEPG_DB_DATABASE__SETATTR)
+#define SEPG_DB_SCHEMA__RELABELFROM			(SEPG_DB_DATABASE__RELABELFROM)
+#define SEPG_DB_SCHEMA__RELABELTO			(SEPG_DB_DATABASE__RELABELTO)
+#define SEPG_DB_SCHEMA__SEARCH				(1<<6)
+#define SEPG_DB_SCHEMA__ADD_NAME			(1<<7)
+#define SEPG_DB_SCHEMA__REMOVE_NAME			(1<<8)
+
+#define SEPG_DB_TABLE__CREATE				(SEPG_DB_DATABASE__CREATE)
+#define SEPG_DB_TABLE__DROP					(SEPG_DB_DATABASE__DROP)
+#define SEPG_DB_TABLE__GETATTR				(SEPG_DB_DATABASE__GETATTR)
+#define SEPG_DB_TABLE__SETATTR				(SEPG_DB_DATABASE__SETATTR)
+#define SEPG_DB_TABLE__RELABELFROM			(SEPG_DB_DATABASE__RELABELFROM)
+#define SEPG_DB_TABLE__RELABELTO			(SEPG_DB_DATABASE__RELABELTO)
+#define SEPG_DB_TABLE__SELECT				(1<<6)
+#define SEPG_DB_TABLE__UPDATE				(1<<7)
+#define SEPG_DB_TABLE__INSERT				(1<<8)
+#define SEPG_DB_TABLE__DELETE				(1<<9)
+#define SEPG_DB_TABLE__LOCK					(1<<10)
+#define SEPG_DB_TABLE__INDEXON				(1<<11)
+
+#define SEPG_DB_SEQUENCE__CREATE			(SEPG_DB_DATABASE__CREATE)
+#define SEPG_DB_SEQUENCE__DROP				(SEPG_DB_DATABASE__DROP)
+#define SEPG_DB_SEQUENCE__GETATTR			(SEPG_DB_DATABASE__GETATTR)
+#define SEPG_DB_SEQUENCE__SETATTR			(SEPG_DB_DATABASE__SETATTR)
+#define SEPG_DB_SEQUENCE__RELABELFROM		(SEPG_DB_DATABASE__RELABELFROM)
+#define SEPG_DB_SEQUENCE__RELABELTO			(SEPG_DB_DATABASE__RELABELTO)
+#define SEPG_DB_SEQUENCE__GET_VALUE			(1<<6)
+#define SEPG_DB_SEQUENCE__NEXT_VALUE		(1<<7)
+#define SEPG_DB_SEQUENCE__SET_VALUE			(1<<8)
+
+#define SEPG_DB_PROCEDURE__CREATE			(SEPG_DB_DATABASE__CREATE)
+#define SEPG_DB_PROCEDURE__DROP				(SEPG_DB_DATABASE__DROP)
+#define SEPG_DB_PROCEDURE__GETATTR			(SEPG_DB_DATABASE__GETATTR)
+#define SEPG_DB_PROCEDURE__SETATTR			(SEPG_DB_DATABASE__SETATTR)
+#define SEPG_DB_PROCEDURE__RELABELFROM		(SEPG_DB_DATABASE__RELABELFROM)
+#define SEPG_DB_PROCEDURE__RELABELTO		(SEPG_DB_DATABASE__RELABELTO)
+#define SEPG_DB_PROCEDURE__EXECUTE			(1<<6)
+#define SEPG_DB_PROCEDURE__ENTRYPOINT		(1<<7)
+#define SEPG_DB_PROCEDURE__INSTALL			(1<<8)
+
+#define SEPG_DB_COLUMN__CREATE				(SEPG_DB_DATABASE__CREATE)
+#define SEPG_DB_COLUMN__DROP				(SEPG_DB_DATABASE__DROP)
+#define SEPG_DB_COLUMN__GETATTR				(SEPG_DB_DATABASE__GETATTR)
+#define SEPG_DB_COLUMN__SETATTR				(SEPG_DB_DATABASE__SETATTR)
+#define SEPG_DB_COLUMN__RELABELFROM			(SEPG_DB_DATABASE__RELABELFROM)
+#define SEPG_DB_COLUMN__RELABELTO			(SEPG_DB_DATABASE__RELABELTO)
+#define SEPG_DB_COLUMN__SELECT				(1<<6)
+#define SEPG_DB_COLUMN__UPDATE				(1<<7)
+#define SEPG_DB_COLUMN__INSERT				(1<<8)
+
+#define SEPG_DB_TUPLE__RELABELFROM			(SEPG_DB_DATABASE__RELABELFROM)
+#define SEPG_DB_TUPLE__RELABELTO			(SEPG_DB_DATABASE__RELABELTO)
+#define SEPG_DB_TUPLE__SELECT				(SEPG_DB_DATABASE__GETATTR)
+#define SEPG_DB_TUPLE__UPDATE				(SEPG_DB_DATABASE__SETATTR)
+#define SEPG_DB_TUPLE__INSERT				(SEPG_DB_DATABASE__CREATE)
+#define SEPG_DB_TUPLE__DELETE				(SEPG_DB_DATABASE__DROP)
+
+#define SEPG_DB_BLOB__CREATE				(SEPG_DB_DATABASE__CREATE)
+#define SEPG_DB_BLOB__DROP					(SEPG_DB_DATABASE__DROP)
+#define SEPG_DB_BLOB__GETATTR				(SEPG_DB_DATABASE__GETATTR)
+#define SEPG_DB_BLOB__SETATTR				(SEPG_DB_DATABASE__SETATTR)
+#define SEPG_DB_BLOB__RELABELFROM			(SEPG_DB_DATABASE__RELABELFROM)
+#define SEPG_DB_BLOB__RELABELTO				(SEPG_DB_DATABASE__RELABELTO)
+#define SEPG_DB_BLOB__READ					(1<<6)
+#define SEPG_DB_BLOB__WRITE					(1<<7)
+#define SEPG_DB_BLOB__IMPORT				(1<<8)
+#define SEPG_DB_BLOB__EXPORT				(1<<9)
+
+#define SEPG_DB_LANGUAGE__CREATE			(SEPG_DB_DATABASE__CREATE)
+#define SEPG_DB_LANGUAGE__DROP				(SEPG_DB_DATABASE__DROP)
+#define SEPG_DB_LANGUAGE__GETATTR			(SEPG_DB_DATABASE__GETATTR)
+#define SEPG_DB_LANGUAGE__SETATTR			(SEPG_DB_DATABASE__SETATTR)
+#define SEPG_DB_LANGUAGE__RELABELFROM		(SEPG_DB_DATABASE__RELABELFROM)
+#define SEPG_DB_LANGUAGE__RELABELTO			(SEPG_DB_DATABASE__RELABELTO)
+#define SEPG_DB_LANGUAGE__IMPLEMENT			(1<<6)
+#define SEPG_DB_LANGUAGE__EXECUTE			(1<<7)
+
+#define SEPG_DB_VIEW__CREATE				(SEPG_DB_DATABASE__CREATE)
+#define SEPG_DB_VIEW__DROP					(SEPG_DB_DATABASE__DROP)
+#define SEPG_DB_VIEW__GETATTR				(SEPG_DB_DATABASE__GETATTR)
+#define SEPG_DB_VIEW__SETATTR				(SEPG_DB_DATABASE__SETATTR)
+#define SEPG_DB_VIEW__RELABELFROM			(SEPG_DB_DATABASE__RELABELFROM)
+#define SEPG_DB_VIEW__RELABELTO				(SEPG_DB_DATABASE__RELABELTO)
+#define SEPG_DB_VIEW__EXPAND				(1<<6)
+
+/*
+ * hooks.c
+ */
+extern bool sepgsql_get_permissive(void);
+extern bool sepgsql_get_debug_audit(void);
+
+/*
+ * selinux.c
+ */
+extern bool	sepgsql_is_enabled(void);
+extern int	sepgsql_get_mode(void);
+extern int	sepgsql_set_mode(int new_mode);
+extern bool sepgsql_getenforce(void);
+
+extern void sepgsql_audit_log(bool denied,
+							  const char *scontext,
+							  const char *tcontext,
+							  uint16 tclass,
+							  uint32 audited,
+							  const char *audit_name);
+
+extern void sepgsql_compute_avd(const char *scontext,
+								const char *tcontext,
+								uint16 tclass,
+								struct av_decision *avd);
+
+extern char *sepgsql_compute_create(const char *scontext,
+									const char *tcontext,
+									uint16 tclass);
+
+extern bool sepgsql_check_perms(const char *scontext,
+								const char *tcontext,
+								uint16 tclass,
+								uint32 required,
+								const char *audit_name,
+								bool abort);
+/*
+ * label.c
+ */
+extern char *sepgsql_get_client_label(void);
+extern char *sepgsql_set_client_label(char *new_label);
+extern char *sepgsql_get_label(Oid relOid, Oid objOid, int32 subId);
+
+extern void	 sepgsql_object_relabel(const ObjectAddress *object,
+									const char *seclabel);
+
+extern Datum sepgsql_getcon(PG_FUNCTION_ARGS);
+extern Datum sepgsql_mcstrans_in(PG_FUNCTION_ARGS);
+extern Datum sepgsql_mcstrans_out(PG_FUNCTION_ARGS);
+extern Datum sepgsql_restorecon(PG_FUNCTION_ARGS);
+
+/*
+ * dml.c
+ */
+extern bool sepgsql_dml_privileges(List *rangeTabls, bool abort);
+
+/*
+ * schema.c
+ */
+extern void sepgsql_schema_post_create(Oid namespaceId);
+extern void sepgsql_schema_relabel(Oid namespaceId, const char *seclabel);
+
+/*
+ * relation.c
+ */
+extern void sepgsql_attribute_post_create(Oid relOid, AttrNumber attnum);
+extern void sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
+									  const char *seclabel);
+extern void sepgsql_relation_post_create(Oid relOid);
+extern void sepgsql_relation_relabel(Oid relOid, const char *seclabel);
+
+/*
+ * proc.c
+ */
+extern void sepgsql_proc_post_create(Oid functionId);
+extern void sepgsql_proc_relabel(Oid functionId, const char *seclabel);
+extern char *sepgsql_proc_get_domtrans(Oid functionId);
+
+#endif /* SEPGSQL_H */
diff --git a/contrib/sepgsql/sepgsql.sql.in b/contrib/sepgsql/sepgsql.sql.in
new file mode 100644
index 00000000000..45ffe31e6bd
--- /dev/null
+++ b/contrib/sepgsql/sepgsql.sql.in
@@ -0,0 +1,36 @@
+--
+-- contrib/sepgsql/sepgsql.sql
+--
+-- [Step to install]
+--
+-- 1. Run initdb
+--    to set up a new database cluster.
+--
+-- 2. Edit $PGDATA/postgresql.conf
+--    to add 'MODULE_PATHNAME' to shared_preload_libraries.
+--
+--    Example)
+--        shared_preload_libraries = 'MODULE_PATHNAME'
+--
+-- 3. Run this script for each databases
+--    This script installs corresponding functions, and assigns initial
+--    security labels on target database objects.
+--    It can be run both single-user mode and multi-user mode, according
+--    to your preference.
+--
+--    Example)
+--      $ for DBNAME in template0 template1 postgres;     \
+--        do                                              \
+--          postgres --single -F -c exit_on_error=true -D $PGDATA $DBNAME \
+--            < /path/to/script/sepgsql.sql > /dev/null   \
+--        done
+--
+-- 4. Start postmaster,
+--    if you initialized the database in single-user mode.
+--
+LOAD 'MODULE_PATHNAME';
+CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_getcon() RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_getcon' LANGUAGE C;
+CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_mcstrans_in(text) RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_mcstrans_in' LANGUAGE C STRICT;
+CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_mcstrans_out(text) RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_mcstrans_out' LANGUAGE C STRICT;
+CREATE OR REPLACE FUNCTION pg_catalog.sepgsql_restorecon(text) RETURNS bool AS 'MODULE_PATHNAME', 'sepgsql_restorecon' LANGUAGE C;
+SELECT sepgsql_restorecon(NULL);
diff --git a/contrib/sepgsql/sql/dml.sql b/contrib/sepgsql/sql/dml.sql
new file mode 100644
index 00000000000..6fa1eb802e4
--- /dev/null
+++ b/contrib/sepgsql/sql/dml.sql
@@ -0,0 +1,118 @@
+--
+-- Regression Test for DML Permissions
+--
+
+--
+-- Setup
+--
+CREATE TABLE t1 (a int, b text);
+SECURITY LABEL ON TABLE t1 IS 'system_u:object_r:sepgsql_table_t:s0';
+INSERT INTO t1 VALUES (1, 'aaa'), (2, 'bbb'), (3, 'ccc');
+
+CREATE TABLE t2 (x int, y text);
+SECURITY LABEL ON TABLE t2 IS 'system_u:object_r:sepgsql_ro_table_t:s0';
+INSERT INTO t2 VALUES (1, 'xxx'), (2, 'yyy'), (3, 'zzz');
+
+CREATE TABLE t3 (s int, t text);
+SECURITY LABEL ON TABLE t3 IS 'system_u:object_r:sepgsql_fixed_table_t:s0';
+INSERT INTO t3 VALUES (1, 'sss'), (2, 'ttt'), (3, 'uuu');
+
+CREATE TABLE t4 (m int, n text);
+SECURITY LABEL ON TABLE t4 IS 'system_u:object_r:sepgsql_secret_table_t:s0';
+INSERT INTO t4 VALUES (1, 'mmm'), (2, 'nnn'), (3, 'ooo');
+
+CREATE TABLE t5 (e text, f text, g text);
+SECURITY LABEL ON TABLE t5 IS 'system_u:object_r:sepgsql_table_t:s0';
+SECURITY LABEL ON COLUMN t5.e IS 'system_u:object_r:sepgsql_table_t:s0';
+SECURITY LABEL ON COLUMN t5.f IS 'system_u:object_r:sepgsql_ro_table_t:s0';
+SECURITY LABEL ON COLUMN t5.g IS 'system_u:object_r:sepgsql_secret_table_t:s0';
+
+CREATE TABLE customer (cid int primary key, cname text, ccredit text);
+SECURITY LABEL ON COLUMN customer.ccredit IS 'system_u:object_r:sepgsql_secret_table_t:s0';
+INSERT INTO customer VALUES (1, 'Taro',   '1111-2222-3333-4444'),
+                            (2, 'Hanako', '5555-6666-7777-8888');
+CREATE FUNCTION customer_credit(int) RETURNS text
+    AS 'SELECT regexp_replace(ccredit, ''-[0-9]+$'', ''-????'') FROM customer WHERE cid = $1'
+    LANGUAGE sql;
+SECURITY LABEL ON FUNCTION customer_credit(int)
+    IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
+
+SELECT objtype, objname, label FROM pg_seclabels
+    WHERE provider = 'selinux'
+     AND  objtype in ('table', 'column')
+     AND  objname in ('t1', 't2', 't3', 't4', 't5', 't5.e', 't5.f', 't5.g');
+
+-- Hardwired Rules
+UPDATE pg_attribute SET attisdropped = true
+    WHERE attrelid = 't5'::regclass AND attname = 'f';	-- failed
+
+--
+-- Simple DML statements
+--
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
+
+SELECT * FROM t1;			-- ok
+SELECT * FROM t2;			-- ok
+SELECT * FROM t3;			-- ok
+SELECT * FROM t4;			-- failed
+SELECT * FROM t5;			-- failed
+SELECT e,f FROM t5;			-- ok
+
+SELECT * FROM customer;			-- failed
+SELECT cid, cname, customer_credit(cid) FROM customer;	-- ok
+
+SELECT count(*) FROM t5;			-- ok
+SELECT count(*) FROM t5 WHERE g IS NULL;	-- failed
+
+INSERT INTO t1 VALUES (4, 'abc');		-- ok
+INSERT INTO t2 VALUES (4, 'xyz');		-- failed
+INSERT INTO t3 VALUES (4, 'stu');		-- ok
+INSERT INTO t4 VALUES (4, 'mno');		-- failed
+INSERT INTO t5 VALUES (1,2,3);			-- failed
+INSERT INTO t5 (e,f) VALUES ('abc', 'def');	-- failed
+INSERT INTO t5 (e) VALUES ('abc');		-- ok
+
+UPDATE t1 SET b = b || '_upd';			-- ok
+UPDATE t2 SET y = y || '_upd';			-- failed
+UPDATE t3 SET t = t || '_upd';			-- failed
+UPDATE t4 SET n = n || '_upd';			-- failed
+UPDATE t5 SET e = 'xyz';			-- ok
+UPDATE t5 SET e = f || '_upd';			-- ok
+UPDATE t5 SET e = g || '_upd';			-- failed
+
+DELETE FROM t1;					-- ok
+DELETE FROM t2;					-- failed
+DELETE FROM t3;					-- failed
+DELETE FROM t4;					-- failed
+DELETE FROM t5;					-- ok
+DELETE FROM t5 WHERE f IS NULL;			-- ok
+DELETE FROM t5 WHERE g IS NULL;			-- failed
+
+--
+-- COPY TO/FROM statements
+--
+COPY t1 TO '/dev/null';				-- ok
+COPY t2 TO '/dev/null';				-- ok
+COPY t3 TO '/dev/null';				-- ok
+COPY t4 TO '/dev/null';				-- failed
+COPY t5 TO '/dev/null';				-- failed
+COPY t5(e,f) TO '/dev/null';			-- ok
+
+COPY t1 FROM '/dev/null';			-- ok
+COPY t2 FROM '/dev/null';			-- failed
+COPY t3 FROM '/dev/null';			-- ok
+COPY t4 FROM '/dev/null';			-- failed
+COPY t5 FROM '/dev/null';			-- failed
+COPY t5 (e,f) FROM '/dev/null';			-- failed
+COPY t5 (e) FROM '/dev/null';			-- ok
+
+--
+-- Clean up
+--
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c255
+DROP TABLE IF EXISTS t1 CASCADE;
+DROP TABLE IF EXISTS t2 CASCADE;
+DROP TABLE IF EXISTS t3 CASCADE;
+DROP TABLE IF EXISTS t4 CASCADE;
+DROP TABLE IF EXISTS t5 CASCADE;
+DROP TABLE IF EXISTS customer CASCADE;
diff --git a/contrib/sepgsql/sql/label.sql b/contrib/sepgsql/sql/label.sql
new file mode 100644
index 00000000000..31624948782
--- /dev/null
+++ b/contrib/sepgsql/sql/label.sql
@@ -0,0 +1,73 @@
+--
+-- Regression Tests for Label Management
+--
+
+--
+-- Setup
+--
+CREATE TABLE t1 (a int, b text);
+INSERT INTO t1 VALUES (1, 'aaa'), (2, 'bbb'), (3, 'ccc');
+SELECT * INTO t2 FROM t1 WHERE a % 2 = 0;
+
+CREATE FUNCTION f1 () RETURNS text
+    AS 'SELECT sepgsql_getcon()'
+    LANGUAGE sql;
+
+CREATE FUNCTION f2 () RETURNS text
+    AS 'SELECT sepgsql_getcon()'
+    LANGUAGE sql;
+SECURITY LABEL ON FUNCTION f2()
+    IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
+
+CREATE FUNCTION f3 () RETURNS text
+    AS 'BEGIN
+          RAISE EXCEPTION ''an exception from f3()'';
+          RETURN NULL;
+        END;' LANGUAGE plpgsql;
+SECURITY LABEL ON FUNCTION f3()
+    IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
+
+--
+-- Tests for default labeling behavior
+--
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
+CREATE TABLE t3 (s int, t text);
+INSERT INTO t3 VALUES (1, 'sss'), (2, 'ttt'), (3, 'uuu');
+
+SELECT objtype, objname, label FROM pg_seclabels
+    WHERE provider = 'selinux'
+     AND  objtype in ('table', 'column')
+     AND  objname in ('t1', 't2', 't3');
+
+--
+-- Tests for SECURITY LABEL
+--
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_dba_t:s0
+SECURITY LABEL ON TABLE t1
+    IS 'system_u:object_r:sepgsql_ro_table_t:s0';	-- ok
+SECURITY LABEL ON TABLE t2
+    IS 'invalid seuciryt context';			-- be failed
+SECURITY LABEL ON COLUMN t2
+    IS 'system_u:object_r:sepgsql_ro_table_t:s0';	-- be failed
+SECURITY LABEL ON COLUMN t2.b
+    IS 'system_u:object_r:sepgsql_ro_table_t:s0';	-- ok
+
+--
+-- Tests for Trusted Procedures
+--
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:sepgsql_regtest_user_t:s0
+SELECT f1();			-- normal procedure
+SELECT f2();			-- trusted procedure
+SELECT f3();			-- trusted procedure that raises an error
+SELECT sepgsql_getcon();	-- client's label must be restored
+
+--
+-- Clean up
+--
+-- @SECURITY-CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c255
+DROP TABLE IF EXISTS t1 CASCADE;
+DROP TABLE IF EXISTS t2 CASCADE;
+DROP TABLE IF EXISTS t3 CASCADE;
+DROP FUNCTION IF EXISTS f1() CASCADE;
+DROP FUNCTION IF EXISTS f2() CASCADE;
+DROP FUNCTION IF EXISTS f3() CASCADE;
diff --git a/contrib/sepgsql/sql/misc.sql b/contrib/sepgsql/sql/misc.sql
new file mode 100644
index 00000000000..a46d8a6b5c8
--- /dev/null
+++ b/contrib/sepgsql/sql/misc.sql
@@ -0,0 +1,5 @@
+--
+-- Regression Test for Misc Permission Checks
+--
+
+LOAD '$libdir/sepgsql';		-- failed
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index f5bd493543f..75d08d5f695 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -116,6 +116,7 @@ psql -d dbname -f <replaceable>SHAREDIR</>/contrib/<replaceable>module</>.sql
  &pgtrgm;
  &pgupgrade;
  &seg;
+ &sepgsql;
  &contrib-spi;
  &sslinfo;
  &tablefunc;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 9d4cfc927ba..99437ac3789 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -129,6 +129,7 @@
 <!entity pgupgrade       SYSTEM "pgupgrade.sgml">
 <!entity seg             SYSTEM "seg.sgml">
 <!entity contrib-spi     SYSTEM "contrib-spi.sgml">
+<!entity sepgsql         SYSTEM "sepgsql.sgml">
 <!entity sslinfo         SYSTEM "sslinfo.sgml">
 <!entity tablefunc       SYSTEM "tablefunc.sgml">
 <!entity test-parser     SYSTEM "test-parser.sgml">
diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml
new file mode 100644
index 00000000000..91b8614b81a
--- /dev/null
+++ b/doc/src/sgml/sepgsql.sgml
@@ -0,0 +1,704 @@
+<!-- doc/src/sgml/sepgsql.sgml -->
+
+<sect1 id="sepgsql">
+ <title>sepgsql</title>
+
+ <indexterm zone="sepgsql">
+  <primary>sepgsql</primary>
+ </indexterm>
+
+ <para>
+  The <filename>sepgsql</> is a module which performs as an external
+  security provider; to support label based mandatory access control
+  (MAC) base on <productname>SELinux</> policy.
+ </para>
+ <para>
+  This extension won't build at all unless the installation was configured
+  with <literal>--with-selinux</>.
+ </para>
+
+ <sect2 id="sepgsql-overview">
+  <title>Overview</title>
+
+  <para>
+   <productname>PostgreSQL</> provides various kind of hooks. Some of these
+   hooks can be utilized to make access control decision on the supplied
+   users' accesses on database objects.
+   We call plug-in modules making access control decision based on its own
+   security model as an external security provider.
+  </para>
+  <para>
+   This module acquires control on these strategic points, then it asks
+   <productname>SELinux</> to check whether the supplied access shall be
+   allowed, or not. Then, it returns its access control decision.
+   If violated, this module prevents this access with rising an error for
+   example.
+  </para>
+  <para>
+   A series of making decision is done independently from the default
+   database privilege mechanism. Users must be allowed with both of access
+   control models, whenever they try to access something.
+  </para>
+  <para>
+   We can see <productname>SELinux</> as a function which takes two arguments
+   then returns a bool value; allowed or denied. The first argument in this
+   analogy is label of subject which tries to reference a certain obejct.
+   The other one is label of the object being referenced in this operation.
+  </para>
+  <para>
+   Label is a formatted string,
+   like <literal>system_u:object_r:sepgsql_table_t:s0</>.
+   It is not a property depending on characteristics of a certain kind of
+   object, so we can apply common credentials on either database objects
+   or others.
+  </para>
+  <para>
+   <productname>PostgreSQL</> 9.1 or later supports
+   <xref linkend="sql-security-label"> statement that allows to assign
+   a security label on specified database objects, if user wants to change
+   label from the creation default.
+   Also <productname>SELinux</> provides an interface to obtain security
+   label of the peer process that connected to.
+  </para>
+  <para>
+   These facilities enable to integrate <productname>SELinux</> model within
+   access controls to database objects. Because it makes access control
+   decision according to a common centralized security policy (a set of rules),
+   its decision will be always consistent independent from the way to store
+   information assets.
+  </para>
+ </sect2>
+ <sect2 id="sepgsql-installation">
+  <title>Installation</title>
+  <para>
+   The <filename>sepgsql</> module requires the following packages to install.
+   Please check it at first.
+  </para>
+  <variablelist>
+   <varlistentry>
+    <term><productname>Linux kernel</productname></term>
+    <listitem>
+     <para>
+      v2.6.28 or later with built with SELinux enabled
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+    <term><productname>libselinux</productname></term>
+    <listitem>
+     <para>
+      v2.0.80 or later
+     </para>
+     <para>
+      This library provides a set of APIs to communicate with
+      <productname>SELinux</> in kernel.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+    <term><productname>selinux-policy</productname></term>
+    <listitem>
+     <para>
+      v3.9.13 or later
+     </para>
+     <para>
+      The default security policy provides a set of access control rules.
+      Some of distribution may backports necessary rules, even if base
+      policy was older than above version.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+  <para>
+   <productname>SE-PostgreSQL</> needs <productname>SELinux</> being
+   available on the platform. You can check the current setting using
+   <command>sestatus</>.
+<screen>
+$ sestatus
+SELinux status:                 enabled
+SELinuxfs mount:                /selinux
+Current mode:                   enforcing
+Mode from config file:          enforcing
+Policy version:                 24
+Policy from config file:        targeted
+</screen>
+   If disabled or not-installed, you need to set up <productname>SELinux</>
+   prior to all the installation step of <productname>SE-PostgreSQL</>.
+  </para>
+  <para>
+   On the compile time, add <literal>--with-selinux</> option to
+   the <command>configure</> script to check existence of
+   the <productname>libselinux</>, and to set a flag whether
+   we build this contrib module, or not.
+<screen>
+$ ./configure --enable-debug --enable-cassert --with-selinux
+$ make
+$ make install
+</screen>
+  </para>
+  <para>
+   Next to the <command>initdb</>, add <literal>'$libdir/sepgsql'</>
+   to <xref linkend="guc-shared-preload-libraries"> in
+   the <filename>postgresql.conf</>.
+
+   It enables to load <filename>sepgsql</> on the starting up of
+   postmaster process.
+  </para>
+  <para>
+   Then, load the <filename>sepgsql.sql</filename> script for each databases.
+   It installs functions corresponding to security label management, and
+   tries to assign initial labels on the target objects.
+  </para>
+  <para>
+   The following instruction assumes your installation is under the
+   <filename>/usr/local/pgsql</> directory, and the database cluster is in
+   <filename>/usr/local/pgsql/data</>. Substitute your paths appropriately.
+  </para>
+<screen>
+$ initdb -D $PGDATA
+$ vi $PGDATA/postgresql.conf
+$ for DBNAME in template0 template1 postgres; do
+  postgres --single -F -O -c exit_on_error=true -D $PGDATA $DBNAME \
+      < /usr/local/pgsql/share/contrib/sepgsql.sql > /dev/null
+  done
+</screen>
+  <para>
+   If all the installation process was done with no errors, start postmaster
+   process. <productname>SE-PostgreSQL</> shall prevent violated accesses
+   according to the security policy of <productname>SELinux</>.
+  </para>
+ </sect2>
+
+ <sect2 id="sepgsql-regression">
+  <title>Regression Tests</title>
+  <para>
+   The regression test of this module requires a few more configurations
+   on the platform system, in addition to the above installation process.
+   See the following steps.
+  </para>
+  <para>
+   First, install the policy package for regression test.
+   The <filename>sepgsql-regtest.pp</> is a special purpose policy package
+   that provides a set of rules to be allowed during the regression test
+   cases. It shall be installed at <filename>/usr/local/pgsql/share/contrib</>
+   directory in the default setup.
+  </para>
+  <para>
+   You need to install this policy package using <command>semodule</>
+   command which enables to link supplied policy packages and load them
+   into the kernel space. If you could install the pakage correctly,
+   <literal><command>semodule</> -l</> prints sepgsql-regtest as a part
+   of policy packages currently available.
+  </para>
+<screen>
+$ su
+# semodule -u /usr/local/pgsql/share/contrib/sepgsql-regtest.pp
+# semodule -l
+    :
+sepgsql-regtest 1.03
+    :
+</screen>
+  <para>
+   Second, turn on the <literal>sepgsql_regression_test_mode</>.
+   We don't enable all the rules in the <filename>sepgsql-regtest.pp</>
+   in the default, for your system's safety.
+   The <literal>sepgsql_regression_test_mode</literal> parameter is associated
+   with rules to launch regression test.
+   It can be turned on using <command>setsebool</> command.
+  </para>
+<screen>
+$ su
+# setsebool sepgsql_regression_test_mode on
+# getsebool sepgsql_regression_test_mode
+sepgsql_regression_test_mode --> on
+</screen>
+  <para>
+   Last, kick the regression test from the <literal>unconfined_t</> domain.
+  </para>
+  <para>
+   This test policy is designed to kick each test cases from the
+   <literal>unconfined_t</> domain that is a default choice in most of
+   the known <literal>SELinux</> installation base.
+   So, you don't need to set up anything special, as long as you didn't
+   change default configuration of SELinux before.
+  </para>
+  <para>
+   The <command>id</> command tells us the current working domain.
+   Confirm your shell is now performing with <literal>unconfined_t</>
+   domain as follows.
+  </para>
+<screen>
+$ id -Z
+unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
+</screen>
+  <para>
+   If not an expected one, you should revert this configuration.
+   The <xref linkend="sepgsql-resources"> section will give you
+   some useful hints.
+  </para>
+  <para>
+   Then, you will see the all-green result of regression test,
+   if we have no problem here.
+  </para>
+<screen>
+$ make -C contrib/sepgsql/ installcheck
+    :
+../../src/test/regress/pg_regress --inputdir=. --psqldir=/usr/local/pgsql/bin \
+    --dbname=contrib_regression --launcher ../../contrib/sepgsql/launcher     \
+    label dml
+(using postmaster on Unix socket, default port)
+============== dropping database "contrib_regression" ==============
+DROP DATABASE
+============== creating database "contrib_regression" ==============
+CREATE DATABASE
+ALTER DATABASE
+============== running regression test queries        ==============
+test label                    ... ok
+test dml                      ... ok
+test misc                     ... ok
+
+=====================
+ All 3 tests passed.
+=====================
+</screen>
+  <para>
+   If <command>pg_regress</> failed to launch <command>psql</> command,
+   here is a hint to fix up the matter.
+
+   When we try to launch <command>psql</> command with restrictive
+   privileges, the <command>psql</> must eb labeled as <literal>bin_t</>.
+   If not, try to run <command>restorecon</> to fix up security label of
+   the commands as expected.
+  </para>
+<screen>
+$ restorecon -R /usr/local/pgsql/
+</screen>
+ </sect2>
+
+ <sect2 id="sepgsql-parameters">
+  <title>GUC Parameters</title>
+
+  <variablelist>
+   <varlistentry id="guc-sepgsql-permissive" xreflabel="sepgsql.permissive">
+    <term><varname>sepgsql.permissive</> (<type>boolean</type>)</term>
+    <indexterm>
+     <primary><varname>sepgsql.permissive</> configuration parameter</primary>
+    </indexterm>
+    <listitem>
+     <para>
+      This parameter enables to perform <productname>SE-PostgreSQL</>
+      in permissive mode independent from the system setting.
+      The default is off (according to the system setting).
+      This parameter can only be set in the <filename>postgresql.conf</>
+      file or on the server command line.
+     </para>
+     <para>
+      We have two performing mode except for disabled; The one is enforcing
+      mode that checks the security policy on references and actually prevents
+      violated accesses. The other is permissive mode that only checks
+      the security policy, but does not prevents anything except for log
+      generation.
+      This log shall be utilized for debugging of the security policy itself.
+     </para>
+     <para>
+      When this parameter is on, <productname>SE-PostgreSQL</> performs
+      in permissive mode, even if the platform system is working on enforcing
+      mode.
+      We recommend users to keep the default setting, except for the case
+      when we develop security policy by ourself.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry id="guc-sepgsql-debug-audit" xreflabel="sepgsql.debug_audit">
+    <term><varname>sepgsql.debug_audit</> (<type>boolean</>)</>
+    <indexterm>
+     <primary><varname>sepgsql.debug_audit</> configuration parameter</>
+    </indexterm>
+    <listitem>
+     <para>
+      This parameter enables to print audit messages independent from
+      the policy setting.
+      The default is off (according to the security policy setting).
+     </para>
+     <para>
+      The security policy of <productname>SELinux</> also has rules to
+      control what accesses shall be logged, or not.
+      In the default, any access violations are logged, but any allowed
+      accesses are not logged.
+     </para>
+     <para>
+      When this parameter is on, all the possible logs shall be printed
+      independently from the policy settings.
+      We recommend to keep the variable turned off in normal cases to
+      avoid noisy messages.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </sect2>
+
+ <sect2 id="sepgsql-features">
+  <title>Features</title>
+  <sect3>
+   <title>controlled object classes</title>
+   <para>
+    The security model of <productname>SELinux</> describes all the access
+    control rules as a relationship between a subject entity (typically,
+    it is a client of database) and an object entity.
+    And, these entities are identified by a security label.
+   </para>
+   <para>
+    We call a set of these rules as security policy.
+    All the access control decision shall be made according to the security
+    policy, when we ask SELinux whether the required action shall be allowed
+    or not.
+    Thus, we have no way to control accesses on any sort of objects without
+    security labels.
+    (<productname>SELinux</> assumes <literal>unlabeled_t</> is assigned,
+    if no valid security label is assigned on the target object.)
+   </para>
+   <para>
+    This version of <productname>SE-PostgreSQL</> supports to assign
+    a security label on these database object classes: schema, table, column,
+    sequence, view and procedure.
+    Other database object classes are not supported to assign security label
+    on, right now.
+   </para>
+   <para>
+    A security label shall be automatically assigned to the supported
+    database objects on their creation time.
+    This label is called as a default security label; being decided according
+    to the security policy, or a pair of security label of the client and
+    upper object for more correctly.
+   </para>
+   <para>
+    A new database object basically inherits security label of the upper
+    object. A new column inherits security label of its parent table for
+    instance.
+    If and when the security policy has special rules called as
+    type-transition on a pair of the client and upper object, we can assign
+    an individual label as a default. The upper object depends on sort of
+    object classes as follows.
+   </para>
+   <variablelist>
+    <varlistentry>
+     <term>schema</term>
+     <listitem>
+      <para>
+       Its upper object is the current database.
+      </para>
+     </listitem>
+    </varlistentry>
+    <varlistentry>
+     <term>table</term>
+     <listitem>
+      <para>
+       Its upper object is the schema object which owns the new table.
+      </para>
+     </listitem>
+    </varlistentry>
+    <varlistentry>
+     <term>column</term>
+     <listitem>
+      <para>
+       Its upper object is the table object which owns the new column.
+      </para>
+     </listitem>
+    </varlistentry>
+    <varlistentry>
+     <term>sequence</term>
+     <listitem>
+      <para>
+       Its upper object is the schema object which owns the new sequence.
+      </para>
+     </listitem>
+    </varlistentry>
+    <varlistentry>
+     <term>view</term>
+     <listitem>
+      <para>
+       Its upper object is the schema object which owns the new view.
+      </para>
+     </listitem>
+    </varlistentry>
+    <varlistentry>
+     <term>procedure</term>
+     <listitem>
+      <para>
+       Its upper object is the schema object which owns the new procedure.
+      </para>
+     </listitem>
+    </varlistentry>
+   </variablelist>
+  </sect3>
+  <sect3>
+   <title>DML Permissions</title>
+   <para>
+    This section introduces what permissions shall be checked on DML;
+    <literal>SELECT</>, <literal>INSERT</>, <literal>UPDATE</> and
+    <literal>DELETE</>.
+   </para>
+   <para>
+    DML statements are used to reference or modify contents within
+    the specified database objects; such as tables or columns.
+    We basically checks access rights of the client on all the appeared
+    objects in the given statement, and kind of privileges depend on
+    class of object and sort of accesses.
+   </para>
+   <para>
+    For tables, <literal>db_table:select</>, <literal>db_table:insert</>,
+    <literal>db_table:update</> or <literal>db_table:delete</> shall be
+    checked for all the appeared target tables depending on the sort of
+    statement;
+    In addition, <literal>db_table:select</> shall be also checked for
+    all the tables that containin the columns to be referenced in
+    <literal>WHERE</> or <literal>RETURNING</> clause, as a data source
+    of <literal>UPDATE</>, and so on.
+   </para>
+   <para>
+<synopsis>
+UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100;
+</synopsis>
+    In this case, we must have <literal>db_table:select</>, not only
+    <literal>db_table:update</>, because <literal>t1.a</> is referenced
+    within <literal>WHERE</> clause.
+    Also note that column-level permission shall be checked individually.
+   </para>
+   <para>
+    The client must be allowed to reference all the appeared tables and
+    columns, even if they are originated from views then expanded, unlike
+    the default database privileges, because we intend to apply consistent
+    access control rules independent from the route to reference contents
+    of the tables.
+   </para>
+   <para>
+    For columns, <literal>db_column:select</> shall be also checked on
+    not only the columns being read using <literal>SELECT</>, but being
+    referenced in other DML statement.
+   </para>
+   <para>
+    Of course, it also checks <literal>db_column:update</> or
+    <literal>db_column:insert</> on the column being modified by
+    <literal>UPDATE</> or <literal>INSERT</>.
+    Note that we have no definition of column-level delete permission,
+    like as the default database privilege doing.
+   </para>
+   <para>
+<synopsis>
+UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100;
+</synopsis>
+    In this case, it checks <literal>db_column:update</> on
+    the <literal>t1.x</> being updated, <literal>db_column:{select update}</>
+    on the <literal>t1.y</> being updated and referenced,
+    and <literal>db_column:select</> on the <literal>t1.z</> being only
+    referenced in the <literal>WHERE</> clause.
+    Also note that <literal>db_table:{select update}</> shall be checked
+    in the table-level granularity.
+   </para>
+   <para>
+    For sequences, <literal>db_sequence:get_value</> when we reference
+    a sequence object using <literal>SELECT</>, however, note that we
+    cannot check permissions on execution of corresponding functions
+    such as <literal>lastval()</> right now, although they performs same
+    job, because here is no object access hook to acquire controls.
+   </para>
+   <para>
+    For views, <literal>db_view:expand</> shall be checked, then any other
+    corresponding permissions shall be also checked on the objects being
+    expanded from the view, individually.
+    Note that both of permissions have to be allowed.
+   </para>
+   <para>
+    For procedures, <literal>db_procedure:{execute}</> is defined, but not
+    checked in this version.
+   </para>
+   <para>
+    Here is a few more corner cases.
+    The default database privilege system allows database superusers to
+    modify system catalogs using DML commands, and reference or modify
+    toast tables, however, both of the cases shall be denied when
+    <productname>SE-PostgreSQL</> is enabled.
+   </para>
+  </sect3>
+  <sect3>
+   <title>DDL Permissions</title>
+   <para>
+    On <xref linkend="sql-security-label"> command, <literal>setattr</> and
+    <literal>relabelfrom</> shall be checked on the object being relabeled
+    with an old security label, then <literal>relabelto</> on the supplied
+    new security label.
+   </para>
+   <para>
+    In a case when multiple label providers are installed and user tries
+    to set a security label, but is not managed by <productname>SELinux</>,
+    only <literal>setattr</> should be checked here.
+    However, it is not unavailable because of limitation of the hook.
+   </para>
+   <para>
+    As we will describe in <xref linkend="sepgsql-limitations"> section,
+    <productname>SE-PostgreSQL</> does not control any other DDL operations.
+   </para>
+  </sect3>
+  <sect3>
+   <title>Trusted Procedure</title>
+   <para>
+    It is a similar idea to security definer functions or set-uid commands
+    on operating systems. <productname>SELinux</> provides a feature to
+    switch privilege of the client (that is a security label of the client
+    for more correctness) during execution of certain functions; being
+    called as trusted procedures.
+   </para>
+   <para>
+    A trusted function is a function with a special security label being
+    set up as a trusted procedure.
+    So, we need to assign the special security label on the function that
+    we hope it to perform as a trusted procedure, by administrative users.
+    The default security policy also provides this special security label.
+    See the following example.
+   </para>
+<screen>
+postgres=# CREATE TABLE customer (
+               cid     int primary key,
+               cname   text,
+               credit  text
+           );
+CREATE TABLE
+postgres=# SECURITY LABEL ON COLUMN customer.credit
+               IS 'system_u:object_r:sepgsql_secret_table_t:s0';
+SECURITY LABEL
+postgres=# CREATE FUNCTION show_credit(int) RETURNS text
+             AS 'SELECT regexp_replace(credit, ''-[0-9]+$'', ''-xxxx'', ''g'')
+                        FROM customer WHERE cid = $1'
+           LANGUAGE sql;
+CREATE FUNCTION
+postgres=# SECURITY LABEL ON FUNCTION show_credit(int)
+               IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
+SECURITY LABEL
+</screen>
+   <para>
+    Above operations shall be done by administrative users.
+   </para>
+<screen>
+postgres=# SELECT * FROM customer;
+ERROR:  SELinux: security policy violation
+postgres=# SELECT cid, cname, show_credit(cid) FROM customer;
+ cid | cname  |     show_credit
+-----+--------+---------------------
+   1 | taro   | 1111-2222-3333-xxxx
+   2 | hanako | 5555-6666-7777-xxxx
+(2 rows)
+</screen>
+   <para>
+    In this case, a regular user cannot reference <literal>customer.credit</>
+    directly, but a trusted procedure <literal>show_credit</> enables us
+    to print credit number of customers with a bit modification.
+   </para>
+  </sect3>
+  <sect3>
+   <title>Miscellaneous</title>
+   <para>
+    In this version, we reject <xref linkend="sql-load"> command across
+    the board, because the binary module can override security hooks to
+    make access control decision. It means a risk to invalidate all the
+    control by security providers.
+   </para>
+  </sect3>
+ </sect2>
+ <sect2 id="sepgsql-limitations">
+  <title>Limitations</title>
+  <para>
+   This section introduces limitations of <productname>SE-PostgreSQL</>
+   in this version.
+  </para>
+  <variablelist>
+   <varlistentry>
+    <term>Userspace access vector cache</term>
+    <listitem>
+     <para>
+      <productname>SE-PostgreSQL</> tells <productname>SELinux</> its access
+      control decision. It takes system call invocation being heavy, however,
+      we can reduce number of the invocations using caching mechanism; called
+      as access vector cache in <productname>SELinux</>.
+      Because of code size, <productname>SE-PostgreSQL</> does not support
+      this mechanism yet.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+    <term>DDL Permissions</term>
+    <listitem>
+     <para>
+      Now <productname>PostgreSQL</> does not provide a set of hooks on
+      the DDL routines.
+      It means plugin modules cannot acquire control here,
+      so <productname>SE-PostgreSQL</> does not check DDL Permissions
+      right now.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+    <term>Row-level access control</term>
+    <listitem>
+     <para>
+      Now <productname>SE-PostgreSQL</> does not support row-level access
+      control, because a few needed facilities are not supported yet.
+      The one is security labels on users' tables. The other is behavior of
+      optimizer. Also see <xref linkend="rules-privileges"> for more details.
+      We know similar issue on VIEW.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+    <term>Covert channels</term>
+    <listitem>
+     <para>
+      <productname>SE-PostgreSQL</> never tries to hide existence of
+      a certain object, even if user is not allowed to reference.
+      For example, we can infer an existence of invisible object using
+      primary-key confliction, foreign-key violation, and so on, even if
+      we cannot reference contents of these objects.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </sect2>
+ <sect2 id="sepgsql-resources">
+  <title>External Resources</title>
+  <variablelist>
+   <varlistentry>
+    <term><ulink url="http://wiki.postgresql.org/wiki/SEPostgreSQL">SE-PostgreSQL Introduction</ulink></term>
+    <listitem>
+     <para>
+      This wikipage provides a brief-overview, security design, architecture,
+      administration and upcoming feature for more details.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+    <term><ulink url="http://docs.fedoraproject.org/selinux-user-guide/">Fedora SELinux User Guide</ulink></term>
+    <listitem>
+     <para>
+      This document provides wide spectrum of knowledge to administrate
+      SELinux on your systems.
+      It primary focuses on Fedora, but not limited to Fedora.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+    <term><ulink url="http://docs.fedoraproject.org/selinux-faq">Fedora SELinux FAQ</ulink></term>
+    <listitem>
+     <para>
+      This document provides FAQs about SELinux.
+      It primary focuses on Fedora, but not limited to Fedora.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </sect2>
+ <sect2 id="sepgsql-author">
+  <title>Author</title>
+  <para>
+   KaiGai Kohei (<email>kaigai@ak.jp.nec.com</email>)
+  </para>
+ </sect2>
+</sect1>
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index ebeee0c3b2b..d6b7b4769df 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -158,6 +158,7 @@ with_python	= @with_python@
 with_tcl	= @with_tcl@
 with_openssl	= @with_openssl@
 with_ossp_uuid	= @with_ossp_uuid@
+with_selinux	= @with_selinux@
 with_libxml	= @with_libxml@
 with_libxslt	= @with_libxslt@
 with_system_tzdata = @with_system_tzdata@
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 79655cd653c..a3211622b9e 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -84,6 +84,7 @@ bool		debug = false;
 char	   *inputdir = ".";
 char	   *outputdir = ".";
 char	   *psqldir = PGBINDIR;
+char	   *launcher = NULL;
 static _stringlist *loadlanguage = NULL;
 static int	max_connections = 0;
 static char *encoding = NULL;
@@ -1871,6 +1872,7 @@ help(void)
 	printf(_("  --dlpath=DIR              look for dynamic libraries in DIR\n"));
 	printf(_("  --temp-install=DIR        create a temporary installation in DIR\n"));
 	printf(_("  --use-existing            use an existing installation\n"));
+	printf(_("  --launcher=CMD            use CMD as launcher of psql\n"));
 	printf(_("\n"));
 	printf(_("Options for \"temp-install\" mode:\n"));
 	printf(_("  --no-locale               use C locale\n"));
@@ -1922,6 +1924,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
 		{"create-role", required_argument, NULL, 18},
 		{"temp-config", required_argument, NULL, 19},
 		{"use-existing", no_argument, NULL, 20},
+		{"launcher", required_argument, NULL, 21},
 		{NULL, 0, NULL, 0}
 	};
 
@@ -2015,6 +2018,9 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
 			case 20:
 				use_existing = true;
 				break;
+			case 21:
+				launcher = strdup(optarg);
+				break;
 			default:
 				/* getopt_long already emitted a complaint */
 				fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
diff --git a/src/test/regress/pg_regress.h b/src/test/regress/pg_regress.h
index 26069f642e7..606c7a1a70b 100644
--- a/src/test/regress/pg_regress.h
+++ b/src/test/regress/pg_regress.h
@@ -41,6 +41,7 @@ extern _stringlist *dblist;
 extern bool debug;
 extern char *inputdir;
 extern char *outputdir;
+extern char *launcher;
 
 /*
  * This should not be global but every module should be able to read command
diff --git a/src/test/regress/pg_regress_main.c b/src/test/regress/pg_regress_main.c
index 710e55841a7..3e43dd72b8e 100644
--- a/src/test/regress/pg_regress_main.c
+++ b/src/test/regress/pg_regress_main.c
@@ -33,6 +33,7 @@ psql_start_test(const char *testname,
 	char		outfile[MAXPGPATH];
 	char		expectfile[MAXPGPATH];
 	char		psql_cmd[MAXPGPATH * 3];
+	size_t		offset = 0;
 
 	/*
 	 * Look for files in the output dir first, consistent with a vpath search.
@@ -58,7 +59,11 @@ psql_start_test(const char *testname,
 	add_stringlist_item(resultfiles, outfile);
 	add_stringlist_item(expectfiles, expectfile);
 
-	snprintf(psql_cmd, sizeof(psql_cmd),
+	if (launcher)
+		offset += snprintf(psql_cmd + offset, sizeof(psql_cmd) - offset,
+						   "%s ", launcher);
+
+	snprintf(psql_cmd + offset, sizeof(psql_cmd) - offset,
 			 SYSTEMQUOTE "\"%s%spsql\" -X -a -q -d \"%s\" < \"%s\" > \"%s\" 2>&1" SYSTEMQUOTE,
 			 psqldir ? psqldir : "",
 			 psqldir ? "/" : "",