summaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
authorRobert Haas2011-01-24 01:44:48 +0000
committerRobert Haas2011-01-24 01:48:27 +0000
commit968bc6fac91d6aaca594488ab85c179b686cbbdd (patch)
tree3cb8fa7ee4101723733e5ed5a06803f9c299c2d7 /contrib
parente5487f65fdbd05716ade642a3ae1c5c6e85b6f22 (diff)
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
Diffstat (limited to 'contrib')
-rw-r--r--contrib/Makefile4
-rw-r--r--contrib/README4
-rw-r--r--contrib/sepgsql/.gitignore1
-rw-r--r--contrib/sepgsql/Makefile25
-rw-r--r--contrib/sepgsql/dml.c353
-rw-r--r--contrib/sepgsql/expected/dml.out182
-rw-r--r--contrib/sepgsql/expected/label.out109
-rw-r--r--contrib/sepgsql/expected/misc.out5
-rw-r--r--contrib/sepgsql/hooks.c446
-rw-r--r--contrib/sepgsql/label.c477
-rw-r--r--contrib/sepgsql/launcher52
-rw-r--r--contrib/sepgsql/proc.c158
-rw-r--r--contrib/sepgsql/relation.c267
-rw-r--r--contrib/sepgsql/schema.c98
-rw-r--r--contrib/sepgsql/selinux.c631
-rw-r--r--contrib/sepgsql/sepgsql-regtest.te59
-rw-r--r--contrib/sepgsql/sepgsql.h288
-rw-r--r--contrib/sepgsql/sepgsql.sql.in36
-rw-r--r--contrib/sepgsql/sql/dml.sql118
-rw-r--r--contrib/sepgsql/sql/label.sql73
-rw-r--r--contrib/sepgsql/sql/misc.sql5
21 files changed, 3391 insertions, 0 deletions
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. <[email protected]>
+sepgsql -
+ External security provider using SELinux
+ by KaiGai Kohei <[email protected]>
+
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