sepgsql contrib module
The attached patch is the modular version of SE-PostgreSQL.
Since I reduced the caching mechanism for access control decision,
its code scale became about 2.6KL.
[kaigai@saba sepgsql]$ wc -l *.[ch]
353 dml.c
366 hooks.c
477 label.c
158 proc.c
267 relation.c
98 schema.c
617 selinux.c
287 sepgsql.h
2623 total
In addition, *.sgml file uses about 300 lines.
There is one another issue to be discussed.
We need a special form of regression test. Because SE-PostgreSQL
makes access control decision based on security label of the peer
process, we need to switch psql process during regression test.
(So, I don't include test cases yet.)
We have 'runcon' command to launch a child process with specified
security label as long as the security policy allows. If we could
launch 'psql' by 'runcon' with specified label, we can describe
test-cases on the existing framework on 'make installcheck'.
An idea is to add an option to pg_regress to launch psql command
with a specified wrapper program (like 'runcon').
In this case, each contrib modules kicks with REGRESS_OPTS setting.
One thing to be considered is the security label to be given to
the 'runcon' is flexible for each *.sql files.
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
Attachments:
sepgsql-v9.1-lite.1.patchapplication/octect-stream; name=sepgsql-v9.1-lite.1.patchDownload
configure | 122 ++++++++
configure.in | 13 +
contrib/Makefile | 4 +
contrib/README | 4 +
contrib/sepgsql/Makefile | 19 ++
contrib/sepgsql/dml.c | 353 +++++++++++++++++++++++
contrib/sepgsql/hooks.c | 366 ++++++++++++++++++++++++
contrib/sepgsql/label.c | 477 +++++++++++++++++++++++++++++++
contrib/sepgsql/proc.c | 158 ++++++++++
contrib/sepgsql/relation.c | 267 +++++++++++++++++
contrib/sepgsql/schema.c | 98 +++++++
contrib/sepgsql/selinux.c | 617 ++++++++++++++++++++++++++++++++++++++++
contrib/sepgsql/sepgsql.h | 287 +++++++++++++++++++
contrib/sepgsql/sepgsql.sql.in | 45 +++
doc/src/sgml/contrib.sgml | 1 +
doc/src/sgml/filelist.sgml | 1 +
doc/src/sgml/sepgsql.sgml | 285 ++++++++++++++++++
src/Makefile.global.in | 1 +
src/include/pg_config.h.in | 3 +
19 files changed, 3121 insertions(+), 0 deletions(-)
diff --git a/configure b/configure
index 51d27d8..8738359 100755
--- a/configure
+++ b/configure
@@ -714,6 +714,7 @@ with_libxslt
with_libxml
XML2_CONFIG
with_ossp_uuid
+with_selinux
with_openssl
with_bonjour
with_ldap
@@ -836,6 +837,7 @@ with_pam
with_ldap
with_bonjour
with_openssl
+with_selinux
with_readline
with_libedit_preferred
with_ossp_uuid
@@ -847,6 +849,7 @@ with_gnu_ld
enable_largefile
enable_float4_byval
enable_float8_byval
+enable_float8_byval
'
ac_precious_vars='build_alias
host_alias
@@ -857,6 +860,7 @@ LDFLAGS
LIBS
CPPFLAGS
CPP
+CPPFLAGS
LDFLAGS_EX
LDFLAGS_SL
DOCBOOKSTYLE'
@@ -1532,6 +1536,7 @@ Optional Packages:
--with-ldap build with LDAP support
--with-bonjour build with Bonjour support
--with-openssl build with OpenSSL support
+ --with-selinux build with SELinux support
--without-readline do not use GNU Readline nor BSD Libedit for editing
--with-libedit-preferred
prefer BSD Libedit over GNU Readline
@@ -5363,6 +5368,40 @@ fi
$as_echo "$with_openssl" >&6; }
+#
+# SELinux
+#
+{ $as_echo "$as_me:$LINENO: checking whether to build with SELinux support" >&5
+$as_echo_n "checking whether to build with SELinux support... " >&6; }
+
+
+
+# Check whether --with-selinux was given.
+if test "${with_selinux+set}" = set; then
+ withval=$with_selinux;
+ case $withval in
+ yes)
+ :
+ ;;
+ no)
+ :
+ ;;
+ *)
+ { { $as_echo "$as_me:$LINENO: error: no argument expected for --with-selinux option" >&5
+$as_echo "$as_me: error: no argument expected for --with-selinux option" >&2;}
+ { (exit 1); exit 1; }; }
+ ;;
+ esac
+
+else
+ with_selinux=no
+
+fi
+
+
+
+{ $as_echo "$as_me:$LINENO: result: $with_selinux" >&5
+$as_echo "$with_selinux" >&6; }
#
# Readline
@@ -9290,6 +9329,89 @@ fi
fi
+# for contrib/sepgsql
+if test "$with_selinux" = yes; then
+
+{ $as_echo "$as_me:$LINENO: checking for getpeercon_raw in -lselinux" >&5
+$as_echo_n "checking for getpeercon_raw in -lselinux... " >&6; }
+if test "${ac_cv_lib_selinux_getpeercon_raw+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lselinux $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char getpeercon_raw ();
+int
+main ()
+{
+return getpeercon_raw ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_selinux_getpeercon_raw=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_selinux_getpeercon_raw=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_getpeercon_raw" >&5
+$as_echo "$ac_cv_lib_selinux_getpeercon_raw" >&6; }
+if test "x$ac_cv_lib_selinux_getpeercon_raw" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBSELINUX 1
+_ACEOF
+
+ LIBS="-lselinux $LIBS"
+
+else
+ { { $as_echo "$as_me:$LINENO: error: library 'libselinux' is required for SELinux support" >&5
+$as_echo "$as_me: error: library 'libselinux' is required for SELinux support" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+fi
+
# for contrib/uuid-ossp
if test "$with_ossp_uuid" = yes ; then
{ $as_echo "$as_me:$LINENO: checking for uuid_export in -lossp-uuid" >&5
diff --git a/configure.in b/configure.in
index b999b94..3654ce5 100644
--- a/configure.in
+++ b/configure.in
@@ -676,6 +676,13 @@ PGAC_ARG_BOOL(with, openssl, no, [build with OpenSSL support],
AC_MSG_RESULT([$with_openssl])
AC_SUBST(with_openssl)
+#
+# SELinux
+#
+AC_MSG_CHECKING([whether to build with SELinux support])
+PGAC_ARG_BOOL(with, selinux, no, [build with SELinux support])
+AC_SUBST(with_selinux)
+AC_MSG_RESULT([$with_selinux])
#
# Readline
@@ -948,6 +955,12 @@ if test "$with_libxslt" = yes ; then
AC_CHECK_LIB(xslt, xsltCleanupGlobals, [], [AC_MSG_ERROR([library 'xslt' is required for XSLT support])])
fi
+# for contrib/sepgsql
+if test "$with_selinux" = yes; then
+ AC_CHECK_LIB(selinux, getpeercon_raw, [],
+ [AC_MSG_ERROR([library 'libselinux' is required for SELinux support])])
+fi
+
# for contrib/uuid-ossp
if test "$with_ossp_uuid" = yes ; then
AC_CHECK_LIB(ossp-uuid, uuid_export,
diff --git a/contrib/Makefile b/contrib/Makefile
index 5747bcc..ba2ec82 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -60,6 +60,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 6c5b7d5..016a388 100644
--- a/contrib/README
+++ b/contrib/README
@@ -159,6 +159,10 @@ seg -
Confidence-interval datatype (GiST indexing example)
by Gene Selkov, Jr. <selkovjr@mcs.anl.gov>
+sepgsql -
+ External security provider using SELinux
+ by KaiGai Kohei <kaigai@ak.jp.nec.com>
+
spi -
Various trigger functions, examples for using SPI.
diff --git a/contrib/sepgsql/Makefile b/contrib/sepgsql/Makefile
new file mode 100644
index 0000000..f254a3a
--- /dev/null
+++ b/contrib/sepgsql/Makefile
@@ -0,0 +1,19 @@
+# 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
+
+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))
diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c
new file mode 100644
index 0000000..dff9a2f
--- /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 (security_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
+ */
+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/hooks.c b/contrib/sepgsql/hooks.c
new file mode 100644
index 0000000..2fbf248
--- /dev/null
+++ b/contrib/sepgsql/hooks.c
@@ -0,0 +1,366 @@
+/* -------------------------------------------------------------------------
+ *
+ * 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 "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;
+
+/*
+ * 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 *label_old;
+ char *label_new;
+ bool result = false;
+
+ 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 cause domain-transition.
+ * In general, we call this kind of function as trusted-procedure.
+ */
+ label_old = sepgsql_get_client_label();
+ label_new = sepgsql_proc_get_domtrans(functionId);
+ if (strcmp(label_old, label_new) != 0)
+ result = true;
+ pfree(label_new);
+
+ return result;
+}
+
+/*
+ * 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;
+ }
+}
+
+/*
+ * 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;
+}
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c
new file mode 100644
index 0000000..fbf8c02
--- /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(1));
+ }
+ 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/proc.c b/contrib/sepgsql/proc.c
new file mode 100644
index 0000000..6a9748d
--- /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 0000000..a497dde
--- /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_PROCEDURE,
+ 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 0000000..df33a02
--- /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 0000000..f1592fe
--- /dev/null
+++ b/contrib/sepgsql/selinux.c
@@ -0,0 +1,617 @@
+/* -------------------------------------------------------------------------
+ *
+ * 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 "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,
+ {
+ { "translation", 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 },
+ { "indexon", SEPG_DB_TABLE__INDEXON },
+ { 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 SEPGSQL_MODE_PERMISSIVE,
+ * except for no audit prints
+ */
+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_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 : The security context of subject. In most cases, it is client.
+ * tcontext : The security context of the parent database object..
+ * tclass : One of the object class code (SEPG_CLASS_*) declared in the
+ * header file.
+ */
+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))
+ 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 (client in most cases)
+ * tcontext : security label of the object to be referenced
+ * tclass : one of the SEPG_CLASS_* code
+ * required : a mask of required permissions (SEPG_<class>__<perm>)
+ * audit_name : a human readable name of the target object for audit
+ * logs. NULL is acceptable.
+ * 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 &&
+ security_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.h b/contrib/sepgsql/sepgsql.h
new file mode 100644
index 0000000..d5f5362
--- /dev/null
+++ b/contrib/sepgsql/sepgsql.h
@@ -0,0 +1,287 @@
+/* -------------------------------------------------------------------------
+ *
+ * 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 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 0000000..91d7d5c
--- /dev/null
+++ b/contrib/sepgsql/sepgsql.sql.in
@@ -0,0 +1,45 @@
+--
+-- contrib/sepgsql/sepgsql.sql
+--
+-- [Step to install]
+--
+-- 1. Run initdb
+-- to set up a new database cluster.
+--
+-- (*) You can skip this step, if you try to install SE-PostgreSQL
+-- on an existing database.
+--
+-- 2. Edit $PGDATA/postgresql.conf
+-- to add 'MODULE_PATHNAME' to shared_preload_libraries.
+--
+-- Example)
+-- shared_preload_libraries = 'MODULE_PATHNAME'
+--
+-- (*) If you try to run this script in multi-user mode,
+-- you must restart the postmaster process before step-3.
+--
+-- 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)
+-- $ postgres --single -F -c exit_on_error=true -D $PGDATA DBNAME \
+-- < /path/to/script/sepgsql.sql > /dev/null
+-- Or
+-- $ psql -f /path/to/script/sepgsql.sql DBNAME
+--
+-- (*) Note that the client must have permission to relabel all the
+-- target objects, when we initialize them in multi-user mode.
+-- (*) Don't forget to run it on 'template1', if you want to create
+-- a new database correctly labeled in default.
+--
+-- 4. Start postmaster,
+-- if you initialized the database in single-user mode.
+--
+CREATE OR REPLACE FUNCTION sepgsql_getcon() RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_getcon' LANGUAGE C STRICT;
+CREATE OR REPLACE FUNCTION sepgsql_mcstrans_in(text) RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_mcstrans_in' LANGUAGE C STRICT;
+CREATE OR REPLACE FUNCTION sepgsql_mcstrans_out(text) RETURNS text AS 'MODULE_PATHNAME', 'sepgsql_mcstrans_out' LANGUAGE C STRICT;
+CREATE OR REPLACE FUNCTION sepgsql_restorecon(bool,text) RETURNS bool AS 'MODULE_PATHNAME', 'sepgsql_restorecon' LANGUAGE C STRICT;
+SELECT sepgsql_restorecon(true, NULL);
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index d788473..ab0a99f 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -115,6 +115,7 @@ psql -d dbname -f <replaceable>SHAREDIR</>/contrib/<replaceable>module</>.sql
&pgtrgm;
&pgupgrade;
&seg;
+ &sepgsql;
&contrib-spi;
&sslinfo;
&tablefunc;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index aa2d801..40b10ad 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -128,6 +128,7 @@
<!entity pgupgrade SYSTEM "pgupgrade.sgml">
<!entity seg SYSTEM "seg.sgml">
<!entity contrib-spi SYSTEM "contrib-spi.sgml">
+<!entity sepgsql SYSTEM "sepgsql.sgml">
<!entity sslinfo SYSTEM "sslinfo.sgml">
<!entity tablefunc SYSTEM "tablefunc.sgml">
<!entity test-parser SYSTEM "test-parser.sgml">
diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml
new file mode 100644
index 0000000..048b10f
--- /dev/null
+++ b/doc/src/sgml/sepgsql.sgml
@@ -0,0 +1,285 @@
+<!-- doc/src/sgml/sepgsql.sgml -->
+
+<sect1 id="sepgsql">
+ <title>sepgsql</title>
+
+ <indexterm zone="sepgsql">
+ <primary>sepgsql</primary>
+ </indexterm>
+
+ <para>
+ <filename>sepgsql</> is a module which performs as an external security
+ provider; to support label based mandatory access control (MAC) controled
+ by <productname>SELinux</>.
+ </para>
+
+ <sect2 id="sepgsql-overview">
+ <title>Overview</title>
+
+ <para>
+ <productname>PostgreSQL</> provides various kind of hooks.
+ Some of these hooks can be utilized to make access control decision
+ on the supplied users' accesses on database objects.
+ We call plug-in modules making access control decision based on its
+ own security model as an external security provider.
+ </para>
+ <para>
+ This module (<filename>sepgsql</>) acquires control on these strategic
+ points, then it asks <productname>SELinux</> to check whether the supplied
+ access shall be allowed, or not. Then, it returns its access control
+ decision. If violated, <filename>sepgsql</> module prevents this access.
+
+ A series of making decision is done independently from the default
+ database privilege mechanism. Users must be allowed with both of
+ access control models, whenever they try to access something.
+ </para>
+ <para>
+ We can see <productname>SELinux</> as a function which takes two
+ arguments then returns a bool value; allowed or denied.
+ The first argument in this analogy is label of subject which tries
+ to reference a certain obejct. The other one is label of the object
+ to be referenced in this operation.
+
+ Label is a formatted string, like
+ <literal>system_u:object_r:sepgsql_table_t:s0</> .
+ It is not a property depending on characteristics of a certain kind of
+ object, so we can apply common credentials on either database objects
+ or others.
+ </para>
+ <para>
+ <productname>PostgreSQL</> 9.1 or later supports
+ <xref linkend="sql-security-label"> statement that allows to assign
+ a security label on specified database objects, if user wants to
+ change label from the creation default.
+ Also <productname>SELinux</> provides an interface to obtain security
+ label of the peer process that connected to.
+
+ These facilities enable to integrate <productname>SELinux</> model
+ within access controls to database objects.
+ Because it makes access control decision according to a common
+ centralized security policy (a set of rules), its decision will
+ be always consistent independent from the way to store information
+ assets.
+ </para>
+ </sect2>
+
+ <sect2 id="sepgsql-install">
+ <title>Installation</title>
+ <para>
+ The <filename>sepgsql</filename> module requires the following
+ packages to install. Please check it first.
+
+ <variablelist>
+ <varlistentry>
+ <term><productname>Linux kernel</productname></term>
+ <listitem>
+ <para>
+ v2.6.28 or later with built with SELinux enabled
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><productname>libselinux</productname></term>
+ <listitem>
+ <para>
+ v2.0.80 or later
+ </para>
+ <para>
+ This library provides a set of APIs to communicate with
+ in-kernel SELinux.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><productname>selinux-policy</productname></term>
+ <listitem>
+ <para>
+ v3.6.8 or later
+ </para>
+ <para>
+ The default security policy provides definitions of permissions and
+ a set of basic rules on both of system and databases.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ <para>
+ SE-PostgreSQL needs SELinux being available on the platform.
+ You can check the current setting using <command>sestatus</>.
+<screen>
+$ sestatus
+SELinux status: enabled
+SELinuxfs mount: /selinux
+Current mode: enforcing
+Mode from config file: enforcing
+Policy version: 24
+Policy from config file: targeted
+</screen>
+ If disabled or not-installed, you need to set up SELinux first
+ prior to SE-PostgreSQL installation.
+ </para>
+ <para>
+ On the compile time, add <literal>--with-selinux</literal> option
+ to the <command>configure</command> script to check existence of
+ the <filename>libselinux</filename>, and to set a flag whether
+ we build this contrib module, or not.
+<screen>
+$ ./configure --enable-debug --enable-cassert --with-selinux
+$ make
+$ make install
+</screen>
+ </para>
+ <para>
+ After the compile and installation,
+ you need to set up <filename>sepgsql_contexts</> file under the
+ <filename>/etc/selinux/*/contexts/</> directory, to inform expected
+ initial security label of database objects on initialization.
+
+ A commonly used configuration will be merged as a part of default
+ security policy in the future, however, we need to set up by hand
+ right now.
+<synopsis>
+db_schema *.* system_u:object_r:sepgsql_schema_t:s0
+db_table *.pg_catalog.* system_u:object_r:sepgsql_sysobj_t:s0
+db_column *.pg_catalog.*.* system_u:object_r:sepgsql_sysobj_t:s0
+db_table *.*.* system_u:object_r:sepgsql_table_t:s0
+db_column *.*.*.* system_u:object_r:sepgsql_table_t:s0
+db_sequence *.*.* system_u:object_r:sepgsql_sequence_t:s0
+db_view *.*.* system_u:object_r:sepgsql_view_t:s0
+db_procedure *.*.* system_u:object_r:sepgsql_proc_exec_t:s0
+</synopsis>
+ </para>
+ <para>
+ Next to the <command>initdb</command>, add <literal>'$libdir/sepgsql'</>
+ to <xref linkend="guc-shared-preload-libraries"> in
+ <filename>postgresql.conf</filename>.
+
+ It enables to load <filename>sepgsql</> on the starting up of
+ postmaster process.
+ </para>
+ <para>
+ Then, run the <filename>sepgsql.sql</filename> script for each databases
+ to be labeled. It installs functions corresponding to security label
+ management, and tries to assign initial labels on the target objects.
+<screen>
+$ initdb -D $PGDATA
+$ vi $PGDATA/postgresql.conf
+$ for DBNAME in template1 postgres; do
+ postgres --single -F -O -c exit_on_error=true -D $PGDATA $DBNAME \
+ < /usr/local/pgsql/share/contrib/sepgsql.sql > /dev/null
+ done
+</screen>
+ Don't forget to run this script on the <literal>template1</literal>,
+ if you want to create a new database correctly labeled in default.
+ </para>
+ <para>
+ If all the installation process was done with no errors, start postmaster
+ process. <productname>SE-PostgreSQL</> shall prevent violated accesses
+ according to the security policy of <productname>SELinux</>.
+ </para>
+ </sect2>
+
+ <sect2 id="sepgsql-tests">
+ <title>Run the Test cases</title>
+ <para>
+ We shall put here detail steps to run test cases.
+ </para>
+ </sect2>
+
+ <sect2 id="sepgsql-params">
+ <title>GUC Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><parameter>sepgsql.permissive</parameter></term>
+ <listitem>
+ <para>
+ This parameter controls performing mode of SE-PostgreSQL either
+ system-setting or permissive mode.
+ The default is <literal>false</> that means it follows on system-
+ setting.
+ We can never change this parameter using SQL. So, please edit
+ <filename>$PGDATA/postgresql.conf</> and restart
+ <productname>PostgreSQL</>.
+ </para>
+ <para>
+ Here are two performing modes except for <literal>disabled</> .
+
+ The one is <literal>enforcing</> mode that checks security policy
+ on accesses and actually prevents violated accesses.
+
+ The other is <literal>permissive</> mode that also checks security
+ policy on accesses, but does not prevents anything, except for
+ generating access violation logs.
+
+ We can utilize these logs to find out unexpected lack of permissions
+ and fix up the security policy itself.
+ </para>
+ <para>
+ We recommend users to keep the variable turned off, except for the
+ case when we develop security policy, because it invalidates all the
+ efficient stuff.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>sepgsql.debug_audit</parameter></term>
+ <listitem>
+ <para>
+ This parameter controls audit messages for debugging purpose.
+ The default is <literal>false</literal> that means it follows on
+ security policy setting.
+ </para>
+ <para>
+ The security policy also has rules to controls what accesses shall
+ be logged, and not be logged.
+ If no special rules in the security policy, any access violations
+ are logged, but any allowed accesses are not logged.
+ </para>
+ <para>
+ If this parameter is turned on, all the available logs shall be
+ printed independently from the policy setting.
+ It will help our debugging, but we recommend to keep the variable
+ turned off in operation phase.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect2>
+
+ <sect2 id="sepgsql-resources">
+ <title>External Resources</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><ulink url="http://wiki.postgresql.org/wiki/SEPostgreSQL">SE-PostgreSQL Introduction</ulink></term>
+ <listitem>
+ <para>
+ This wikipage provides a brief-overview, security design, architecture,
+ administration and future-plans for more details.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><ulink url="http://docs.fedoraproject.org/selinux-user-guide/">Fedora SELinux User Guide</ulink></term>
+ <listitem>
+ <para>
+ This document provides wide spectrum of knowledge to administrate
+ SELinux on your systems.
+ It primary focuses on Fedora, but not limited to Fedora.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><ulink url="http://docs.fedoraproject.org/selinux-faq">Fedora SELinux FAQ</ulink></term>
+ <listitem>
+ <para>
+ This document provides FAQs about SELinux.
+ It primary focuses on Fedora, but not limited to Fedora.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect2>
+</sect1>
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 7a61a5a..49336d0 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -158,6 +158,7 @@ with_python = @with_python@
with_tcl = @with_tcl@
with_openssl = @with_openssl@
with_ossp_uuid = @with_ossp_uuid@
+with_selinux = @with_selinux@
with_libxml = @with_libxml@
with_libxslt = @with_libxslt@
with_system_tzdata = @with_system_tzdata@
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index fd169b6..f19de0e 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -284,6 +284,9 @@
/* Define to 1 if you have the <ldap.h> header file. */
#undef HAVE_LDAP_H
+/* Define to 1 if you have the `audit' library (-laudit). */
+#undef HAVE_LIBAUDIT
+
/* Define to 1 if you have the `crypto' library (-lcrypto). */
#undef HAVE_LIBCRYPTO
(2010/12/24 11:53), KaiGai Kohei wrote:
There is one another issue to be discussed.
We need a special form of regression test. Because SE-PostgreSQL
makes access control decision based on security label of the peer
process, we need to switch psql process during regression test.
(So, I don't include test cases yet.)We have 'runcon' command to launch a child process with specified
security label as long as the security policy allows. If we could
launch 'psql' by 'runcon' with specified label, we can describe
test-cases on the existing framework on 'make installcheck'.An idea is to add an option to pg_regress to launch psql command
with a specified wrapper program (like 'runcon').
In this case, each contrib modules kicks with REGRESS_OPTS setting.
One thing to be considered is the security label to be given to
the 'runcon' is flexible for each *.sql files.
The attached patch adds --launcher=COMMAND option to pg_regress.
If a command was specified, pg_regress prepends the specified
command string in front of psql command.
When we use this option, psql command process will launched via
the launcher program. Of course, the launcher has responsibility
to execute psql correctly.)
This example is a case when I run a regression test on cube module.
It tries to launch psql using 'runcon -l s0'.
[kaigai@saba cube]$ make installcheck REGRESS_OPTS="--launcher='runcon -l s0' --dbname=cube_regress"
make -C ../../src/test/regress pg_regress
make[1]: Entering directory `/home/kaigai/repo/pgsql/src/test/regress'
make -C ../../../src/port all
make[2]: Entering directory `/home/kaigai/repo/pgsql/src/port'
make[2]: Nothing to be done for `all'.
make[2]: Leaving directory `/home/kaigai/repo/pgsql/src/port'
make[1]: Leaving directory `/home/kaigai/repo/pgsql/src/test/regress'
../../src/test/regress/pg_regress --inputdir=. --psqldir=/usr/local/pgsql/bin --launcher='runcon -l s0' --dbname=cube_regress cube
(using postmaster on Unix socket, default port)
============== dropping database "cube_regress" ==============
DROP DATABASE
============== creating database "cube_regress" ==============
CREATE DATABASE
ALTER DATABASE
============== running regression test queries ==============
test cube ... ok
=====================
All 1 tests passed.
=====================
During the regression test, I checked security context of the process.
[kaigai@saba ~]$ env LANG=C pstree -Z
systemd(`system_u:system_r:init_t:s0')
:
|-sshd(`unconfined_u:system_r:sshd_t:s0-s0:c0.c1023')
| |-sshd(`unconfined_u:system_r:sshd_t:s0-s0:c0.c1023')
| | `-sshd(`unconfined_u:system_r:sshd_t:s0-s0:c0.c1023')
| | `-bash(`unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023')
| | `-make(`unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023')
| | `-pg_regress(`unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023')
| | `-psql(`unconfined_u:unconfined_r:unconfined_t:s0')
It shows us the launcher program drops privileges of "c0.c1023" on end of
the security label of processes between pg_regress and psql.
How about the idea to implement regression test for SE-PostgreSQL, or
possible other stuff which depends on environment variables.
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
Attachments:
pg_regress-launcher.patchapplication/octect-stream; name=pg_regress-launcher.patchDownload
src/test/regress/pg_regress.c | 6 ++++++
src/test/regress/pg_regress.h | 1 +
src/test/regress/pg_regress_main.c | 7 ++++++-
3 files changed, 13 insertions(+), 1 deletions(-)
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 84aff94..b6af2c8 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -84,6 +84,7 @@ bool debug = false;
char *inputdir = ".";
char *outputdir = ".";
char *psqldir = PGBINDIR;
+char *launcher = NULL;
static _stringlist *loadlanguage = NULL;
static int max_connections = 0;
static char *encoding = NULL;
@@ -1871,6 +1872,7 @@ help(void)
printf(_(" --dlpath=DIR look for dynamic libraries in DIR\n"));
printf(_(" --temp-install=DIR create a temporary installation in DIR\n"));
printf(_(" --use-existing use an existing installation\n"));
+ printf(_(" --launcher=COMMAND use COMMAND as the launcher of psql\n"));
printf(_("\n"));
printf(_("Options for \"temp-install\" mode:\n"));
printf(_(" --no-locale use C locale\n"));
@@ -1922,6 +1924,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
{"create-role", required_argument, NULL, 18},
{"temp-config", required_argument, NULL, 19},
{"use-existing", no_argument, NULL, 20},
+ {"launcher", required_argument, NULL, 21},
{NULL, 0, NULL, 0}
};
@@ -2015,6 +2018,9 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
case 20:
use_existing = true;
break;
+ case 21:
+ launcher = strdup(optarg);
+ break;
default:
/* getopt_long already emitted a complaint */
fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
diff --git a/src/test/regress/pg_regress.h b/src/test/regress/pg_regress.h
index 3f46037..f2844f8 100644
--- a/src/test/regress/pg_regress.h
+++ b/src/test/regress/pg_regress.h
@@ -41,6 +41,7 @@ extern _stringlist *dblist;
extern bool debug;
extern char *inputdir;
extern char *outputdir;
+extern char *launcher;
/*
* This should not be global but every module should be able to read command
diff --git a/src/test/regress/pg_regress_main.c b/src/test/regress/pg_regress_main.c
index c2114b9..afe501b 100644
--- a/src/test/regress/pg_regress_main.c
+++ b/src/test/regress/pg_regress_main.c
@@ -33,6 +33,7 @@ psql_start_test(const char *testname,
char outfile[MAXPGPATH];
char expectfile[MAXPGPATH];
char psql_cmd[MAXPGPATH * 3];
+ size_t offset = 0;
/*
* Look for files in the output dir first, consistent with a vpath search.
@@ -58,7 +59,11 @@ psql_start_test(const char *testname,
add_stringlist_item(resultfiles, outfile);
add_stringlist_item(expectfiles, expectfile);
- snprintf(psql_cmd, sizeof(psql_cmd),
+ if (launcher)
+ offset += snprintf(psql_cmd + offset, sizeof(psql_cmd) - offset,
+ "%s ", launcher);
+
+ snprintf(psql_cmd + offset, sizeof(psql_cmd) - offset,
SYSTEMQUOTE "\"%s%spsql\" -X -a -q -d \"%s\" < \"%s\" > \"%s\" 2>&1" SYSTEMQUOTE,
psqldir ? psqldir : "",
psqldir ? "/" : "",
On Fri, 2010-12-24 at 11:53 +0900, KaiGai Kohei wrote:
The attached patch is the modular version of SE-PostgreSQL.
Looks interesting.
Couple of thoughts...
Docs don't mention row-level security. If we don't have it, I think we
should say that clearly.
I think we need a "Guide to Security Labels" section in the docs. Very
soon, because its hard to know what is being delivered and what is not.
Is the pg_seclabel table secure? Looks like the labels will be available
to read.
How do we tell if sepgsql is installed?
What happens if someone alters the configuration so that the sepgsql
plugin is no longer installed. Does the hidden data become visible?
Thanks
--
Simon Riggs http://www.2ndQuadrant.com/books/
PostgreSQL Development, 24x7 Support, Training and Services
(2010/12/27 17:53), Simon Riggs wrote:
On Fri, 2010-12-24 at 11:53 +0900, KaiGai Kohei wrote:
The attached patch is the modular version of SE-PostgreSQL.
Looks interesting.
Couple of thoughts...
Docs don't mention row-level security. If we don't have it, I think we
should say that clearly.
Indeed, it is a good idea the document mentions what features are not
implemented in this version clearly, not only row-level security, but
DDL permissions and so on. I'd like to revise it soon.
I think we need a "Guide to Security Labels" section in the docs. Very
soon, because its hard to know what is being delivered and what is not.
Does it describe what is security label and the purpose of them?
OK, I'd like to add this section here.
Is the pg_seclabel table secure? Looks like the labels will be available
to read.
If we want to control visibility of each labels, we need row-level
granularity here.
How do we tell if sepgsql is installed?
Check existence of GUC variables of sepgsql.*.
What happens if someone alters the configuration so that the sepgsql
plugin is no longer installed. Does the hidden data become visible?
Yes. If sepgsql plugin is uninstalled, the hidden data become visible.
But no matter. Since only a person who is allowed to edit postgresql.conf
can uninstall it, we cannot uninstall it in run-time.
(An exception is loading a malicious module, but we will be able to
hook this operation in the future version.)
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
On Thu, 2010-12-30 at 09:26 +0900, KaiGai Kohei wrote:
What happens if someone alters the configuration so that the sepgsql
plugin is no longer installed. Does the hidden data become visible?Yes. If sepgsql plugin is uninstalled, the hidden data become visible.
But no matter. Since only a person who is allowed to edit postgresql.conf
can uninstall it, we cannot uninstall it in run-time.
(An exception is loading a malicious module, but we will be able to
hook this operation in the future version.)
IMHO all security labels should be invisible if the provider is not
installed correctly.
That at least prevents us from accidentally de-installing a module and
having top secret data be widely available.
If you have multiple providers configured, you need to be careful not to
allow a provider that incorrectly implements the plugin API, so that
prior plugins are no longer effective.
--
Simon Riggs http://www.2ndQuadrant.com/books/
PostgreSQL Development, 24x7 Support, Training and Services
(2010/12/30 9:34), Simon Riggs wrote:
On Thu, 2010-12-30 at 09:26 +0900, KaiGai Kohei wrote:
What happens if someone alters the configuration so that the sepgsql
plugin is no longer installed. Does the hidden data become visible?Yes. If sepgsql plugin is uninstalled, the hidden data become visible.
But no matter. Since only a person who is allowed to edit postgresql.conf
can uninstall it, we cannot uninstall it in run-time.
(An exception is loading a malicious module, but we will be able to
hook this operation in the future version.)IMHO all security labels should be invisible if the provider is not
installed correctly.
Probably, it needs row-level granularity to control visibility of
each entries of pg_seclabel, because all the provider shares same
system catalog.
So, I don't think this mechanism is feasible right now.
That at least prevents us from accidentally de-installing a module and
having top secret data be widely available.If you have multiple providers configured, you need to be careful not to
allow a provider that incorrectly implements the plugin API, so that
prior plugins are no longer effective.
Yep. It is responsibility of DBA who tries to set up security providers.
DBA has to install only trustable or well-debugged modules (not limited
to security providers) to avoid troubles.
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
The attached patch is the modular version of SE-PostgreSQL (take.2).
Its patch scale grew up to 4KL because of regression test inclusion,
although code size was not changed (2.6KL).
I had to add a small piece into pg_regress to launch psql command
using a launcher program that kicks psql with controlled privilege
set, because SE-PostgreSQL makes access control decision based on
security label of the peer process.
This enhancement allows to implement regression test according to
the framework currently we have, so additional setups to run
regression test got simplified.
I found several bugs during code revising, these were also killed.
How about feasibility to merge this 4KL chunks during the rest of
45 days? I think we should decide this general direction at first.
Simon,
A section of "Guide to Security Labels" is now under describing.
Please wait for a few days to revise documentation a bit more.
Thanks,
$ cat ~/sepgsql-v9.1-lite.2.patch | diffstat
configure | 122 +++++++
configure.in | 13
contrib/Makefile | 4
contrib/README | 4
contrib/sepgsql/Makefile | 25 +
contrib/sepgsql/dml.c | 353 +++++++++++++++++++++
contrib/sepgsql/expected/dml.out | 178 ++++++++++
contrib/sepgsql/expected/label.out | 109 ++++++
contrib/sepgsql/hooks.c | 366 +++++++++++++++++++++
contrib/sepgsql/label.c | 477 ++++++++++++++++++++++++++++
contrib/sepgsql/launcher | 52 +++
contrib/sepgsql/proc.c | 158 +++++++++
contrib/sepgsql/relation.c | 267 +++++++++++++++
contrib/sepgsql/schema.c | 98 +++++
contrib/sepgsql/selinux.c | 618 +++++++++++++++++++++++++++++++++++++
contrib/sepgsql/sepgsql-regtest.te | 59 +++
contrib/sepgsql/sepgsql.h | 287 +++++++++++++++++
contrib/sepgsql/sepgsql.sql.in | 36 ++
contrib/sepgsql/sql/dml.sql | 114 ++++++
contrib/sepgsql/sql/label.sql | 73 ++++
doc/src/sgml/contrib.sgml | 1
doc/src/sgml/filelist.sgml | 1
doc/src/sgml/sepgsql.sgml | 468 ++++++++++++++++++++++++++++
src/Makefile.global.in | 1
src/test/regress/pg_regress.c | 6
src/test/regress/pg_regress.h | 1
src/test/regress/pg_regress_main.c | 7
27 files changed, 3897 insertions(+), 1 deletion(-)
(2010/12/24 11:53), KaiGai Kohei wrote:
The attached patch is the modular version of SE-PostgreSQL.
Since I reduced the caching mechanism for access control decision,
its code scale became about 2.6KL.[kaigai@saba sepgsql]$ wc -l *.[ch]
353 dml.c
366 hooks.c
477 label.c
158 proc.c
267 relation.c
98 schema.c
617 selinux.c
287 sepgsql.h
2623 totalIn addition, *.sgml file uses about 300 lines.
There is one another issue to be discussed.
We need a special form of regression test. Because SE-PostgreSQL
makes access control decision based on security label of the peer
process, we need to switch psql process during regression test.
(So, I don't include test cases yet.)We have 'runcon' command to launch a child process with specified
security label as long as the security policy allows. If we could
launch 'psql' by 'runcon' with specified label, we can describe
test-cases on the existing framework on 'make installcheck'.An idea is to add an option to pg_regress to launch psql command
with a specified wrapper program (like 'runcon').
In this case, each contrib modules kicks with REGRESS_OPTS setting.
One thing to be considered is the security label to be given to
the 'runcon' is flexible for each *.sql files.Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
Attachments:
sepgsql-v9.1-lite.2.patchtext/x-patch; name=sepgsql-v9.1-lite.2.patchDownload
diff --git a/configure b/configure
index 104ae30..c34816c 100755
--- a/configure
+++ b/configure
@@ -715,6 +715,7 @@ with_libxslt
with_libxml
XML2_CONFIG
with_ossp_uuid
+with_selinux
with_openssl
with_bonjour
with_ldap
@@ -837,6 +838,7 @@ with_pam
with_ldap
with_bonjour
with_openssl
+with_selinux
with_readline
with_libedit_preferred
with_ossp_uuid
@@ -848,6 +850,7 @@ with_gnu_ld
enable_largefile
enable_float4_byval
enable_float8_byval
+enable_float8_byval
'
ac_precious_vars='build_alias
host_alias
@@ -858,6 +861,7 @@ LDFLAGS
LIBS
CPPFLAGS
CPP
+CPPFLAGS
LDFLAGS_EX
LDFLAGS_SL
DOCBOOKSTYLE'
@@ -1533,6 +1537,7 @@ Optional Packages:
--with-ldap build with LDAP support
--with-bonjour build with Bonjour support
--with-openssl build with OpenSSL support
+ --with-selinux build with SELinux support
--without-readline do not use GNU Readline nor BSD Libedit for editing
--with-libedit-preferred
prefer BSD Libedit over GNU Readline
@@ -5364,6 +5369,40 @@ fi
$as_echo "$with_openssl" >&6; }
+#
+# SELinux
+#
+{ $as_echo "$as_me:$LINENO: checking whether to build with SELinux support" >&5
+$as_echo_n "checking whether to build with SELinux support... " >&6; }
+
+
+
+# Check whether --with-selinux was given.
+if test "${with_selinux+set}" = set; then
+ withval=$with_selinux;
+ case $withval in
+ yes)
+ :
+ ;;
+ no)
+ :
+ ;;
+ *)
+ { { $as_echo "$as_me:$LINENO: error: no argument expected for --with-selinux option" >&5
+$as_echo "$as_me: error: no argument expected for --with-selinux option" >&2;}
+ { (exit 1); exit 1; }; }
+ ;;
+ esac
+
+else
+ with_selinux=no
+
+fi
+
+
+
+{ $as_echo "$as_me:$LINENO: result: $with_selinux" >&5
+$as_echo "$with_selinux" >&6; }
#
# Readline
@@ -9291,6 +9330,89 @@ fi
fi
+# for contrib/sepgsql
+if test "$with_selinux" = yes; then
+
+{ $as_echo "$as_me:$LINENO: checking for getpeercon_raw in -lselinux" >&5
+$as_echo_n "checking for getpeercon_raw in -lselinux... " >&6; }
+if test "${ac_cv_lib_selinux_getpeercon_raw+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lselinux $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char getpeercon_raw ();
+int
+main ()
+{
+return getpeercon_raw ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_selinux_getpeercon_raw=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_selinux_getpeercon_raw=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_getpeercon_raw" >&5
+$as_echo "$ac_cv_lib_selinux_getpeercon_raw" >&6; }
+if test "x$ac_cv_lib_selinux_getpeercon_raw" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBSELINUX 1
+_ACEOF
+
+ LIBS="-lselinux $LIBS"
+
+else
+ { { $as_echo "$as_me:$LINENO: error: library 'libselinux' is required for SELinux support" >&5
+$as_echo "$as_me: error: library 'libselinux' is required for SELinux support" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+fi
+
# for contrib/uuid-ossp
if test "$with_ossp_uuid" = yes ; then
{ $as_echo "$as_me:$LINENO: checking for uuid_export in -lossp-uuid" >&5
diff --git a/configure.in b/configure.in
index 109eb0c..581a219 100644
--- a/configure.in
+++ b/configure.in
@@ -676,6 +676,13 @@ PGAC_ARG_BOOL(with, openssl, no, [build with OpenSSL support],
AC_MSG_RESULT([$with_openssl])
AC_SUBST(with_openssl)
+#
+# SELinux
+#
+AC_MSG_CHECKING([whether to build with SELinux support])
+PGAC_ARG_BOOL(with, selinux, no, [build with SELinux support])
+AC_SUBST(with_selinux)
+AC_MSG_RESULT([$with_selinux])
#
# Readline
@@ -948,6 +955,12 @@ if test "$with_libxslt" = yes ; then
AC_CHECK_LIB(xslt, xsltCleanupGlobals, [], [AC_MSG_ERROR([library 'xslt' is required for XSLT support])])
fi
+# for contrib/sepgsql
+if test "$with_selinux" = yes; then
+ AC_CHECK_LIB(selinux, getpeercon_raw, [],
+ [AC_MSG_ERROR([library 'libselinux' is required for SELinux support])])
+fi
+
# for contrib/uuid-ossp
if test "$with_ossp_uuid" = yes ; then
AC_CHECK_LIB(ossp-uuid, uuid_export,
diff --git a/contrib/Makefile b/contrib/Makefile
index 5747bcc..ba2ec82 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -60,6 +60,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 6c5b7d5..016a388 100644
--- a/contrib/README
+++ b/contrib/README
@@ -159,6 +159,10 @@ seg -
Confidence-interval datatype (GiST indexing example)
by Gene Selkov, Jr. <selkovjr@mcs.anl.gov>
+sepgsql -
+ External security provider using SELinux
+ by KaiGai Kohei <kaigai@ak.jp.nec.com>
+
spi -
Various trigger functions, examples for using SPI.
diff --git a/contrib/sepgsql/Makefile b/contrib/sepgsql/Makefile
new file mode 100644
index 0000000..d5b71a3
--- /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
+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 0000000..dff9a2f
--- /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 (security_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
+ */
+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 0000000..8fe5c0b
--- /dev/null
+++ b/contrib/sepgsql/expected/dml.out
@@ -0,0 +1,178 @@
+--
+-- 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)
+
+--
+-- 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 0000000..0f0615c
--- /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/hooks.c b/contrib/sepgsql/hooks.c
new file mode 100644
index 0000000..2fbf248
--- /dev/null
+++ b/contrib/sepgsql/hooks.c
@@ -0,0 +1,366 @@
+/* -------------------------------------------------------------------------
+ *
+ * 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 "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;
+
+/*
+ * 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 *label_old;
+ char *label_new;
+ bool result = false;
+
+ 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 cause domain-transition.
+ * In general, we call this kind of function as trusted-procedure.
+ */
+ label_old = sepgsql_get_client_label();
+ label_new = sepgsql_proc_get_domtrans(functionId);
+ if (strcmp(label_old, label_new) != 0)
+ result = true;
+ pfree(label_new);
+
+ return result;
+}
+
+/*
+ * 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;
+ }
+}
+
+/*
+ * 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;
+}
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c
new file mode 100644
index 0000000..bc28adf
--- /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 100755
index 0000000..9e5ecdc
--- /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 0000000..6a9748d
--- /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 0000000..ceaa6b0
--- /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 0000000..df33a02
--- /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 0000000..0f00789
--- /dev/null
+++ b/contrib/sepgsql/selinux.c
@@ -0,0 +1,618 @@
+/* -------------------------------------------------------------------------
+ *
+ * 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,
+ {
+ { "translation", 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 },
+ { "indexon", SEPG_DB_TABLE__INDEXON },
+ { 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 SEPGSQL_MODE_PERMISSIVE,
+ * except for no audit prints
+ */
+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_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 : The security context of subject. In most cases, it is client.
+ * tcontext : The security context of the parent database object..
+ * tclass : One of the object class code (SEPG_CLASS_*) declared in the
+ * header file.
+ */
+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 (client in most cases)
+ * tcontext : security label of the object to be referenced
+ * tclass : one of the SEPG_CLASS_* code
+ * required : a mask of required permissions (SEPG_<class>__<perm>)
+ * audit_name : a human readable name of the target object for audit
+ * logs. NULL is acceptable.
+ * 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 &&
+ security_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 0000000..66666d0
--- /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 0000000..d5f5362
--- /dev/null
+++ b/contrib/sepgsql/sepgsql.h
@@ -0,0 +1,287 @@
+/* -------------------------------------------------------------------------
+ *
+ * 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 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 0000000..45ffe31
--- /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 0000000..5ad1cc7
--- /dev/null
+++ b/contrib/sepgsql/sql/dml.sql
@@ -0,0 +1,114 @@
+--
+-- 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');
+
+--
+-- 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 0000000..3162494
--- /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/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index d788473..ab0a99f 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -115,6 +115,7 @@ psql -d dbname -f <replaceable>SHAREDIR</>/contrib/<replaceable>module</>.sql
&pgtrgm;
&pgupgrade;
&seg;
+ &sepgsql;
&contrib-spi;
&sslinfo;
&tablefunc;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index aa2d801..40b10ad 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -128,6 +128,7 @@
<!entity pgupgrade SYSTEM "pgupgrade.sgml">
<!entity seg SYSTEM "seg.sgml">
<!entity contrib-spi SYSTEM "contrib-spi.sgml">
+<!entity sepgsql SYSTEM "sepgsql.sgml">
<!entity sslinfo SYSTEM "sslinfo.sgml">
<!entity tablefunc SYSTEM "tablefunc.sgml">
<!entity test-parser SYSTEM "test-parser.sgml">
diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml
new file mode 100644
index 0000000..43168e3
--- /dev/null
+++ b/doc/src/sgml/sepgsql.sgml
@@ -0,0 +1,468 @@
+<!-- doc/src/sgml/sepgsql.sgml -->
+
+<sect1 id="sepgsql">
+ <title>sepgsql</title>
+
+ <indexterm zone="sepgsql">
+ <primary>sepgsql</primary>
+ </indexterm>
+
+ <para>
+ <filename>sepgsql</> is a module which performs as an external security
+ provider; to support label based mandatory access control (MAC) controled
+ by <productname>SELinux</>.
+ </para>
+
+ <sect2 id="sepgsql-overview">
+ <title>Overview</title>
+
+ <para>
+ <productname>PostgreSQL</> provides various kind of hooks.
+ Some of these hooks can be utilized to make access control decision
+ on the supplied users' accesses on database objects.
+ We call plug-in modules making access control decision based on its
+ own security model as an external security provider.
+ </para>
+ <para>
+ This module (<filename>sepgsql</>) acquires control on these strategic
+ points, then it asks <productname>SELinux</> to check whether the supplied
+ access shall be allowed, or not. Then, it returns its access control
+ decision. If violated, <filename>sepgsql</> module prevents this access.
+
+ A series of making decision is done independently from the default
+ database privilege mechanism. Users must be allowed with both of
+ access control models, whenever they try to access something.
+ </para>
+ <para>
+ We can see <productname>SELinux</> as a function which takes two
+ arguments then returns a bool value; allowed or denied.
+ The first argument in this analogy is label of subject which tries
+ to reference a certain obejct. The other one is label of the object
+ to be referenced in this operation.
+
+ Label is a formatted string, like
+ <literal>system_u:object_r:sepgsql_table_t:s0</> .
+ It is not a property depending on characteristics of a certain kind of
+ object, so we can apply common credentials on either database objects
+ or others.
+ </para>
+ <para>
+ <productname>PostgreSQL</> 9.1 or later supports
+ <xref linkend="sql-security-label"> statement that allows to assign
+ a security label on specified database objects, if user wants to
+ change label from the creation default.
+ Also <productname>SELinux</> provides an interface to obtain security
+ label of the peer process that connected to.
+
+ These facilities enable to integrate <productname>SELinux</> model
+ within access controls to database objects.
+ Because it makes access control decision according to a common
+ centralized security policy (a set of rules), its decision will
+ be always consistent independent from the way to store information
+ assets.
+ </para>
+ </sect2>
+
+ <sect2 id="sepgsql-install">
+ <title>Installation</title>
+ <para>
+ The <filename>sepgsql</filename> module requires the following
+ packages to install. Please check it first.
+
+ <variablelist>
+ <varlistentry>
+ <term><productname>Linux kernel</productname></term>
+ <listitem>
+ <para>
+ v2.6.28 or later with built with SELinux enabled
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><productname>libselinux</productname></term>
+ <listitem>
+ <para>
+ v2.0.80 or later
+ </para>
+ <para>
+ This library provides a set of APIs to communicate with
+ in-kernel SELinux.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><productname>selinux-policy</productname></term>
+ <listitem>
+ <para>
+ v3.6.8 or later
+ </para>
+ <para>
+ The default security policy provides definitions of permissions and
+ a set of basic rules on both of system and databases.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ <para>
+ SE-PostgreSQL needs SELinux being available on the platform.
+ You can check the current setting using <command>sestatus</>.
+<screen>
+$ sestatus
+SELinux status: enabled
+SELinuxfs mount: /selinux
+Current mode: enforcing
+Mode from config file: enforcing
+Policy version: 24
+Policy from config file: targeted
+</screen>
+ If disabled or not-installed, you need to set up SELinux first
+ prior to SE-PostgreSQL installation.
+ </para>
+ <para>
+ On the compile time, add <literal>--with-selinux</literal> option
+ to the <command>configure</command> script to check existence of
+ the <filename>libselinux</filename>, and to set a flag whether
+ we build this contrib module, or not.
+<screen>
+$ ./configure --enable-debug --enable-cassert --with-selinux
+$ make
+$ make install
+</screen>
+ </para>
+ <para>
+ After the compile and installation,
+ you need to set up <filename>sepgsql_contexts</> file under the
+ <filename>/etc/selinux/*/contexts/</> directory, to inform expected
+ initial security label of database objects on initialization.
+
+ A commonly used configuration shall be merged as a part of default
+ security policy in the future, however, we need to set up by hand
+ right now.
+<synopsis>
+db_schema *.* system_u:object_r:sepgsql_db_t:s0
+db_table *.pg_catalog.* system_u:object_r:sepgsql_sysobj_t:s0
+db_column *.pg_catalog.*.* system_u:object_r:sepgsql_sysobj_t:s0
+db_table *.*.* system_u:object_r:sepgsql_table_t:s0
+db_column *.*.*.* system_u:object_r:sepgsql_table_t:s0
+db_sequence *.*.* system_u:object_r:sepgsql_db_t:s0
+db_view *.*.* system_u:object_r:sepgsql_db_t:s0
+db_procedure *.*.* system_u:object_r:sepgsql_proc_exec_t:s0
+</synopsis>
+ </para>
+ <para>
+ Next to the <command>initdb</command>, add <literal>'$libdir/sepgsql'</>
+ to <xref linkend="guc-shared-preload-libraries"> in
+ <filename>postgresql.conf</filename>.
+
+ It enables to load <filename>sepgsql</> on the starting up of
+ postmaster process.
+ </para>
+ <para>
+ Then, run the <filename>sepgsql.sql</filename> script for each databases.
+ It installs functions corresponding to security label management, and
+ tries to assign initial labels on the target objects.
+<screen>
+$ initdb -D $PGDATA
+$ vi $PGDATA/postgresql.conf
+$ for DBNAME in template0 template1 postgres; do
+ postgres --single -F -O -c exit_on_error=true -D $PGDATA $DBNAME \
+ < /usr/local/pgsql/share/contrib/sepgsql.sql > /dev/null
+ done
+</screen>
+ </para>
+ <para>
+ If all the installation process was done with no errors, start postmaster
+ process. <productname>SE-PostgreSQL</> shall prevent violated accesses
+ according to the security policy of <productname>SELinux</>.
+ </para>
+ </sect2>
+
+ <sect2 id="sepgsql-regression">
+ <title>Regression Tests</title>
+ <para>
+ The regression test of this module requires a few more configurations
+ on the platform system, in addition to the above installation process.
+ See the following steps.
+ </para>
+ <sect3>
+ <title>Install the test policy</title>
+ <para>
+ The <filename>sepgsql-regtest.pp</filename> is a special purpose
+ policy package that provides a set of rules to be allowed during
+ the regression test cases.
+ It shall be installed at <filename>/usr/local/pgsql/share/contrib</>
+ directory in the default setup.
+ </para>
+ <para>
+ You need to install this policy package using <command>semodule</>
+ command which enables to link supplied policy packages and load them
+ into the kernel space.
+ If you could install it correctly, <literal><command>semodule</> -l</>
+ prints <literal>sepgsql-regtest</> as a part of policy packages currently
+ available.
+<screen>
+$ su
+# semodule -u /usr/local/pgsql/share/contrib/sepgsql-regtest.pp
+# semodule -l
+ :
+sepgsql-regtest 1.03
+ :
+</screen>
+ </para>
+ </sect3>
+ <sect3>
+ <title>Turn on the <literal>sepgsql_regression_test_mode</></title>
+ <para>
+ We don't enable all the rules in the <filename>sepgsql-regtest.pp</>
+ in the default, for your system's safety.
+ So, you need to turn on a switch that enables these slept rules.
+ </para>
+ <para>
+ These switches are named as <literal>boolean</>. Some rules are
+ associated with a certain <literal>boolean</>, and these rules can
+ be enabled or disabled depending on state of the <literal>boolean</>
+ on run-time.
+ </para>
+ <para>
+ You need to turn on the <literal>sepgsql_regression_test_mode</>
+ before launching regression test using <command>setsebool</> command.
+<screen>
+$ su
+# setsebool sepgsql_regression_test_mode on
+# getsebool sepgsql_regression_test_mode
+sepgsql_regression_test_mode --> on
+</screen>
+ </para>
+ </sect3>
+ <sect3>
+ <title>Kick regression test from <literal>unconfined_t</> domain</>
+ <para>
+ The <filename>sepgsql-regtest.pp</filename> was designed to kick
+ each test cases from the <literal>unconfined_t</literal>.
+ It is a default choice in most of the known <productname>SELinux</>
+ installation base. So, you don't need to set up anything special,
+ if you didn't modify default configuration of SELinux before.
+ </para>
+ <para>
+ The <command>id</> command tells us the current working domain.
+ Confirm your shell is now performing with <literal>unconfined_t</>
+ domain as follows.
+<screen>
+$ id -Z
+unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
+</screen>
+ If not an expected one, you should revert this configuration.
+ The <xref linkend="sepgsql-resources"> section will give you
+ some useful hints.
+ </para>
+ <para>
+ Then, you can kick regression test.
+ If we have no problem here, all the tests will be passed.
+<screen>
+$ make -C contrib/sepgsql/ installcheck
+ :
+../../src/test/regress/pg_regress --inputdir=. --psqldir=/usr/local/pgsql/bin \
+ --dbname=contrib_regression --launcher ../../contrib/sepgsql/launcher \
+ label dml
+(using postmaster on Unix socket, default port)
+============== dropping database "contrib_regression" ==============
+DROP DATABASE
+============== creating database "contrib_regression" ==============
+CREATE DATABASE
+ALTER DATABASE
+============== running regression test queries ==============
+test label ... ok
+test dml ... ok
+
+=====================
+ All 2 tests passed.
+=====================
+</screen>
+ </para>
+ <para>
+ If <command>pg_regress</> failed to launch <command>psql</> command,
+ here is an additional hint.
+
+ When it tries to launch <command>psql</> command with restrictive
+ privileges, the <command>psql</> must be labeled as <literal>bin_t</>.
+ If not, try the following magic words.
+<screen>
+$ restorecon -R /usr/local/pgsql/
+</screen>
+ </para>
+ </sect3>
+ </sect2>
+
+ <sect2 id="sepgsql-params">
+ <title>GUC Parameters</title>
+
+ <variablelist>
+ <varlistentry>
+ <term><parameter>sepgsql.permissive</parameter></term>
+ <listitem>
+ <para>
+ This parameter controls performing mode of SE-PostgreSQL either
+ system-setting or permissive mode.
+ The default is <literal>false</> that means it follows on system-
+ setting.
+ We can never change this parameter using SQL. So, please edit
+ <filename>$PGDATA/postgresql.conf</> and restart
+ <productname>PostgreSQL</>.
+ </para>
+ <para>
+ Here are two performing modes except for <literal>disabled</> .
+
+ The one is <literal>enforcing</> mode that checks security policy
+ on accesses and actually prevents violated accesses.
+
+ The other is <literal>permissive</> mode that also checks security
+ policy on accesses, but does not prevents anything, except for
+ generating access violation logs.
+
+ We can utilize these logs to find out unexpected lack of permissions
+ and fix up the security policy itself.
+ </para>
+ <para>
+ We recommend users to keep the variable turned off, except for the
+ case when we develop security policy, because it invalidates all the
+ efficient stuff.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><parameter>sepgsql.debug_audit</parameter></term>
+ <listitem>
+ <para>
+ This parameter controls audit messages for debugging purpose.
+ The default is <literal>false</literal> that means it follows on
+ security policy setting.
+ </para>
+ <para>
+ The security policy also has rules to controls what accesses shall
+ be logged, and not be logged.
+ If no special rules in the security policy, any access violations
+ are logged, but any allowed accesses are not logged.
+ </para>
+ <para>
+ If this parameter is turned on, all the available logs shall be
+ printed independently from the policy setting.
+ It will help our debugging, but we recommend to keep the variable
+ turned off in operation phase.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect2>
+
+ <sect2 id="sepgsql-features">
+ <title>Upcoming Features</title>
+ <para>
+ This section introduces upcoming features to be supported in the future
+ version of <productname>SE-PostgreSQL</>, but not supported yet.
+ </para>
+ <sect3>
+ <title>Access vector cache</title>
+ <para>
+ Whenever <productname>SE-PostgreSQL</> makes its access control decision,
+ it tells <productname>SELinux</> in kernel space whether the required
+ access should be allowed or not.
+ It basically takes a system call invocation for each access control
+ decision, however, it also takes unignorable negative effects in
+ performance.
+ </para>
+ <para>
+ In <productname>SELinux</> model, same decision shall be returned for
+ same pair of security context, as long as the current working security
+ policy is not reloaded.
+ So, we can utilize a caching mechanism to reduce number of system call
+ invocations, or performance cost on access controls.
+ </para>
+ <para>
+ Because of code size, this feature is not implemented yet.
+ </para>
+ </sect3>
+ <sect3>
+ <title>DDL Permissions</title>
+ <para>
+ we plan to control DDL operations like existing database privilege
+ mechanism checks <literal>CREATE</> permission or ownership of
+ objects to be modified.
+ </para>
+ <para>
+ Because <productname>SE-PostgreSQL</> applies its access control on
+ the hooks of object accesses that are provided to plugin modules,
+ it cannot check anything on the code where we have no hooks.
+
+ The hooks have limited coverage to implement all the DDL permissions
+ in the 9.1 release, this feature is not implemented yet.
+ </para>
+ <para>
+ We shall provide more widespread hooks for object accesses first,
+ then <productname>SE-PostgreSQL</> also provides DDL Permissions
+ as defined in the policy.
+ </para>
+ </sect3>
+ <sect3>
+ <title>Row-level access control</title>
+ <para>
+ Row-level access control is a catchy and tangible feature that is
+ supported in a few commercial database systems.
+
+ It performs something like <literal>WHERE</> clause that filters
+ out violated tuples on the required accesses.
+
+ We also plan to implement this feature in the future version, however,
+ it requires some facilities to be supported before.
+ </para>
+ <para>
+ Order of evaluation on fetched tuples is a problem.
+ The optimizer may reorder and put user given condition prior to
+ the function of row-level access control, depending on the cost.
+ The <xref linkend="rules-privileges"> describes this problem for
+ more details.
+ </para>
+ <para>
+ In addition, now we don't have a facility to assign a certain security
+ label on tuples within users' table.
+ Unlike system objects, the number of user tuples can be massive,
+ so we need to revise design of security label management for users'
+ table support.
+ </para>
+ </sect3>
+ </sect2>
+
+ <sect2 id="sepgsql-resources">
+ <title>External Resources</title>
+ <variablelist>
+ <varlistentry>
+ <term><ulink url="http://wiki.postgresql.org/wiki/SEPostgreSQL">SE-PostgreSQL Introduction</ulink></term>
+ <listitem>
+ <para>
+ This wikipage provides a brief-overview, security design, architecture,
+ administration and future-plans for more details.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><ulink url="http://docs.fedoraproject.org/selinux-user-guide/">Fedora SELinux User Guide</ulink></term>
+ <listitem>
+ <para>
+ This document provides wide spectrum of knowledge to administrate
+ SELinux on your systems.
+ It primary focuses on Fedora, but not limited to Fedora.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><ulink url="http://docs.fedoraproject.org/selinux-faq">Fedora SELinux FAQ</ulink></term>
+ <listitem>
+ <para>
+ This document provides FAQs about SELinux.
+ It primary focuses on Fedora, but not limited to Fedora.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect2>
+</sect1>
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index ebeee0c..d6b7b47 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -158,6 +158,7 @@ with_python = @with_python@
with_tcl = @with_tcl@
with_openssl = @with_openssl@
with_ossp_uuid = @with_ossp_uuid@
+with_selinux = @with_selinux@
with_libxml = @with_libxml@
with_libxslt = @with_libxslt@
with_system_tzdata = @with_system_tzdata@
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 79655cd..a321162 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -84,6 +84,7 @@ bool debug = false;
char *inputdir = ".";
char *outputdir = ".";
char *psqldir = PGBINDIR;
+char *launcher = NULL;
static _stringlist *loadlanguage = NULL;
static int max_connections = 0;
static char *encoding = NULL;
@@ -1871,6 +1872,7 @@ help(void)
printf(_(" --dlpath=DIR look for dynamic libraries in DIR\n"));
printf(_(" --temp-install=DIR create a temporary installation in DIR\n"));
printf(_(" --use-existing use an existing installation\n"));
+ printf(_(" --launcher=CMD use CMD as launcher of psql\n"));
printf(_("\n"));
printf(_("Options for \"temp-install\" mode:\n"));
printf(_(" --no-locale use C locale\n"));
@@ -1922,6 +1924,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
{"create-role", required_argument, NULL, 18},
{"temp-config", required_argument, NULL, 19},
{"use-existing", no_argument, NULL, 20},
+ {"launcher", required_argument, NULL, 21},
{NULL, 0, NULL, 0}
};
@@ -2015,6 +2018,9 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
case 20:
use_existing = true;
break;
+ case 21:
+ launcher = strdup(optarg);
+ break;
default:
/* getopt_long already emitted a complaint */
fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
diff --git a/src/test/regress/pg_regress.h b/src/test/regress/pg_regress.h
index 26069f6..606c7a1 100644
--- a/src/test/regress/pg_regress.h
+++ b/src/test/regress/pg_regress.h
@@ -41,6 +41,7 @@ extern _stringlist *dblist;
extern bool debug;
extern char *inputdir;
extern char *outputdir;
+extern char *launcher;
/*
* This should not be global but every module should be able to read command
diff --git a/src/test/regress/pg_regress_main.c b/src/test/regress/pg_regress_main.c
index 710e558..3e43dd7 100644
--- a/src/test/regress/pg_regress_main.c
+++ b/src/test/regress/pg_regress_main.c
@@ -33,6 +33,7 @@ psql_start_test(const char *testname,
char outfile[MAXPGPATH];
char expectfile[MAXPGPATH];
char psql_cmd[MAXPGPATH * 3];
+ size_t offset = 0;
/*
* Look for files in the output dir first, consistent with a vpath search.
@@ -58,7 +59,11 @@ psql_start_test(const char *testname,
add_stringlist_item(resultfiles, outfile);
add_stringlist_item(expectfiles, expectfile);
- snprintf(psql_cmd, sizeof(psql_cmd),
+ if (launcher)
+ offset += snprintf(psql_cmd + offset, sizeof(psql_cmd) - offset,
+ "%s ", launcher);
+
+ snprintf(psql_cmd + offset, sizeof(psql_cmd) - offset,
SYSTEMQUOTE "\"%s%spsql\" -X -a -q -d \"%s\" < \"%s\" > \"%s\" 2>&1" SYSTEMQUOTE,
psqldir ? psqldir : "",
psqldir ? "/" : "",
2011/1/5 KaiGai Kohei <kaigai@ak.jp.nec.com>:
The attached patch is the modular version of SE-PostgreSQL (take.2).
I'm reading through the documentation and so far it looks pretty
reasonable. But I have some questions and suggested changes, of
course. :-)
1. Why is sepgsql the right name for this module, as opposed to, say,
selinux? We don't call the cube module cubepgsql, or the hstore
module hstorepgsql. Maybe there's a reason why this case is
different, but I'm not sure.
2. The docs contains some references to /usr/local/pgsql/share.. Does
this really mean "whatever sharedir you established when you ran
configure", i.e. the output of pg_config --sharedir? I hope so.
3. The language for the sepgsql.permissive GUC suggests that it's
PGC_POSTMASTER, but I'd think PGC_SIGHUP ought to be sufficient.
Either way, please copy the appropriate language from some existing
GUC of the same type instead of inventing a new way to say it. I also
have no idea what "because it invalidates all the inefficient stuff"
means.
4. Please remove the upcoming features section of the documentation.
This material is appropriate for a page on the wiki, but shouldn't be
part of the official documentation. Instead, you might want to have a
*short* "Limitations" section.
5. I'm not too sure about this one, but I think it might be good to
elaborate on what we mean by respecting the system SE-Linux policy.
What kinds of objects do we support checks on? What sorts of checks?
What kind of access can we allow/deny?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
(2011/01/06 14:28), Robert Haas wrote:
2011/1/5 KaiGai Kohei<kaigai@ak.jp.nec.com>:
The attached patch is the modular version of SE-PostgreSQL (take.2).
I'm reading through the documentation and so far it looks pretty
reasonable. But I have some questions and suggested changes, of
course. :-)
Thanks for your reviewing in spite of large chunk.
1. Why is sepgsql the right name for this module, as opposed to, say,
selinux? We don't call the cube module cubepgsql, or the hstore
module hstorepgsql. Maybe there's a reason why this case is
different, but I'm not sure.
In some previous cases when SELinux model was ported to other systems,
its project was named as SE-(other system), such as SE-BSD, SE-X, etc...
I named it according to this convention, however, it is indeed uncertain
whether 'sepgsql' follows on the convention in pgsql side.
I don't think it is a strong reason why the module is named as 'sepgsql'
instead of 'selinux'. In advertisement context, we can just call it as
SE-PostgreSQL.
2. The docs contains some references to /usr/local/pgsql/share.. Does
this really mean "whatever sharedir you established when you ran
configure", i.e. the output of pg_config --sharedir? I hope so.
Yes, it means the sharedir being configured.
I found the following description at the installation.sgml.
I should put this kind of mention on the documentation.
| <para>
| These instructions assume that your existing installation is under the
| <filename>/usr/local/pgsql</> directory, and that the data area is in
| <filename>/usr/local/pgsql/data</>. Substitute your paths
| appropriately.
| </para>
3. The language for the sepgsql.permissive GUC suggests that it's
PGC_POSTMASTER, but I'd think PGC_SIGHUP ought to be sufficient.
Either way, please copy the appropriate language from some existing
GUC of the same type instead of inventing a new way to say it. I also
have no idea what "because it invalidates all the inefficient stuff"
means.
OK, I'll try to find up similar description then fix up both of the
code and documentation.
4. Please remove the upcoming features section of the documentation.
This material is appropriate for a page on the wiki, but shouldn't be
part of the official documentation. Instead, you might want to have a
*short* "Limitations" section.
OK, I'll replace an itemization of limitations in this version.
5. I'm not too sure about this one, but I think it might be good to
elaborate on what we mean by respecting the system SE-Linux policy.
What kinds of objects do we support checks on? What sorts of checks?
What kind of access can we allow/deny?
I guess these detailed description makes amount of this chapter
disproportionally increase in the future version.
My preference is wikipage to provide this kind of detailed information.
http://wiki.postgresql.org/wiki/SEPostgreSQL
The contents of above wikipage is now obsoleted, because it assumed
SELinux support as a built-in feature. But it is a good time to fix
up the description.
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
2011/1/6 KaiGai Kohei <kaigai@ak.jp.nec.com>:
1. Why is sepgsql the right name for this module, as opposed to, say,
selinux? We don't call the cube module cubepgsql, or the hstore
module hstorepgsql. Maybe there's a reason why this case is
different, but I'm not sure.In some previous cases when SELinux model was ported to other systems,
its project was named as SE-(other system), such as SE-BSD, SE-X, etc...
I named it according to this convention, however, it is indeed uncertain
whether 'sepgsql' follows on the convention in pgsql side.
OK. If there's an existing convention of calling things
SE-<productname> then let's do the same thing here. As long as it's
documented clearly it shouldn't be a problem.
2. The docs contains some references to /usr/local/pgsql/share.. Does
this really mean "whatever sharedir you established when you ran
configure", i.e. the output of pg_config --sharedir? I hope so.Yes, it means the sharedir being configured.
I found the following description at the installation.sgml.
I should put this kind of mention on the documentation.| <para>
| These instructions assume that your existing installation is under the
| <filename>/usr/local/pgsql</> directory, and that the data area is in
| <filename>/usr/local/pgsql/data</>. Substitute your paths
| appropriately.
| </para>
Why does the user need to know about this at all? Doesn't make
install put everything in the right place?
5. I'm not too sure about this one, but I think it might be good to
elaborate on what we mean by respecting the system SE-Linux policy.
What kinds of objects do we support checks on? What sorts of checks?
What kind of access can we allow/deny?I guess these detailed description makes amount of this chapter
disproportionally increase in the future version.
My preference is wikipage to provide this kind of detailed information.http://wiki.postgresql.org/wiki/SEPostgreSQL
The contents of above wikipage is now obsoleted, because it assumed
SELinux support as a built-in feature. But it is a good time to fix
up the description.
I'd prefer to have it in the actual documentation. I think
SE-PostgreSQL is going to need a lot of documentation. A wiki page
risks getting out of date or having the wrong information for the
version the user has installed. 9.1 may be quite different from 9.2,
for example.
I wouldn't worry about the scale of the patch too much as far as
documentation goes. In reviewing previous patches you've written,
I've often cut large amounts of documentation that I didn't think were
important enough to be worth including (and most of the contents of
the upcoming features section falls into that category). But that
doesn't really take that much time, and it's certainly a lot easier to
remove some extra documentation than it is to write something that's
missing. Most of what you have here right now describes why you might
want to use this feature, rather than what the feature actually does.
If you want to start by updating the wiki page, that's fine, and may
be an easier way for us to collaborate than doing it by exchanging
patches. But ultimately I think it needs to go in the docs.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
2. The docs contains some references to /usr/local/pgsql/share.. Does
this really mean "whatever sharedir you established when you ran
configure", i.e. the output of pg_config --sharedir? I hope so.Yes, it means the sharedir being configured.
I found the following description at the installation.sgml.
I should put this kind of mention on the documentation.|<para>
| These instructions assume that your existing installation is under the
|<filename>/usr/local/pgsql</> directory, and that the data area is in
|<filename>/usr/local/pgsql/data</>. Substitute your paths
| appropriately.
|</para>Why does the user need to know about this at all? Doesn't make
install put everything in the right place?
It seemed to me a convenient writing-style to inform users an installation
path of file. What I like to write is showing users what path stores what
files needed in installation process.
If we use result of the `pg_config --sharedir` here, how about this
writing style? Or, do we have any other ideas?
<screen>
$ su
# SHAREDIR=`pg_config --sharedir`
# semodule -u $SHAREDIR/contrib/sepgsql-regtest.pp
# semodule -l
:
sepgsql-regtest 1.03
:
</screen>
5. I'm not too sure about this one, but I think it might be good to
elaborate on what we mean by respecting the system SE-Linux policy.
What kinds of objects do we support checks on? What sorts of checks?
What kind of access can we allow/deny?I guess these detailed description makes amount of this chapter
disproportionally increase in the future version.
My preference is wikipage to provide this kind of detailed information.http://wiki.postgresql.org/wiki/SEPostgreSQL
The contents of above wikipage is now obsoleted, because it assumed
SELinux support as a built-in feature. But it is a good time to fix
up the description.I'd prefer to have it in the actual documentation. I think
SE-PostgreSQL is going to need a lot of documentation. A wiki page
risks getting out of date or having the wrong information for the
version the user has installed. 9.1 may be quite different from 9.2,
for example.
Indeed, wikipage might not be suitable to document for several different
version. OK, I'll try to add description what you suggested above.
Most of what you have here right now describes why you might
want to use this feature, rather than what the feature actually does.
If you want to start by updating the wiki page, that's fine, and may
be an easier way for us to collaborate than doing it by exchanging
patches. But ultimately I think it needs to go in the docs.
The background of this wikipage is that I was persuading people
this feature being worthful, so the contents tend to philosophical
things rather than actual specifications.
I also think wiki page allows us to brush up the documentation
rather than exchanging patches effectively. I'll set up a wiki page
that contains same contents with *.sgml file to revise documentation
stuff to be included into the *.sgml file finally. How about this idea?
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
2011/1/6 KaiGai Kohei <kaigai@ak.jp.nec.com>:
If we use result of the `pg_config --sharedir` here, how about this
writing style? Or, do we have any other ideas?
I'm not sure - I'll look at your next draft more closely.
The background of this wikipage is that I was persuading people
this feature being worthful, so the contents tend to philosophical
things rather than actual specifications.
Yeah.
I also think wiki page allows us to brush up the documentation
rather than exchanging patches effectively. I'll set up a wiki page
that contains same contents with *.sgml file to revise documentation
stuff to be included into the *.sgml file finally. How about this idea?
Sounds good.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
I also think wiki page allows us to brush up the documentation
rather than exchanging patches effectively. I'll set up a wiki page
that contains same contents with *.sgml file to revise documentation
stuff to be included into the *.sgml file finally. How about this idea?Sounds good.
I set up a wikipage as a source of *.sgml file.
http://wiki.postgresql.org/wiki/SEPostgreSQL_Documentation
We can fix up cosmetic things later, so should we review the
description of the upcoming official documentation?
If we use result of the `pg_config --sharedir` here, how about this
writing style? Or, do we have any other ideas?I'm not sure - I'll look at your next draft more closely.
I added a mention about installation path in the "Installation"
section, as follows:
The following instruction assumes your installation is under
the /usr/local/pgsql directory, and the database cluster is
in /usr/local/pgsql/data. Substitute your paths appropriately.
It seems to me natural rather than using `pg_config --sharedir`
instead. (we might need to care about installation path of the
pg_config in this case.)
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
2011/1/5 KaiGai Kohei <kaigai@ak.jp.nec.com>:
How about feasibility to merge this 4KL chunks during the rest of
45 days? I think we should decide this general direction at first.
I read through this code tonight and it looks pretty straightforward.
I don't see much reason not to accept this more or less as-is. I'm a
bit suspicious of this line:
{ "translation", SEPG_PROCESS__TRANSITION },
I can't help wondering based on the rest of the table whether you
intend to have the same word on that line twice, but you don't. On a
related note, would it make sense to pare down this table to the
entries that are actually used at the moment? And how about adding a
ProcessUtility_hook to trap evil non-DML statements that some
nefarious user might issues?
One other problem is that you need to work on your whitespace a bit.
I believe in a few places you have a mixture of tabs and spaces. More
seriously, pgindent is going to completely mangle things like this:
/*
* 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 SEPGSQL_MODE_PERMISSIVE,
* except for
no audit prints
*/
You have to write it with a line of dashes on the first and last
lines, if you don't want it reformatted as a paragraph. It might be
worth actually running pgindent over contrib/selinux and then check
over the results.
Finally, we need to work on the documentation.
But overall, it looks pretty good, IMHO.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
(2011/01/20 12:10), Robert Haas wrote:
2011/1/5 KaiGai Kohei<kaigai@ak.jp.nec.com>:
How about feasibility to merge this 4KL chunks during the rest of
45 days? I think we should decide this general direction at first.I read through this code tonight and it looks pretty straightforward.
I don't see much reason not to accept this more or less as-is. I'm a
bit suspicious of this line:{ "translation", SEPG_PROCESS__TRANSITION },
I can't help wondering based on the rest of the table whether you
intend to have the same word on that line twice, but you don't. On a
related note, would it make sense to pare down this table to the
entries that are actually used at the moment?
Sorry for giving you a confusion.
It was my typo, so should be fixed as:
{ "transition", SEPG_PROCESS_TRANSITION },
This permission shall be checked when a security label of client being
switched from X to Y, typically on execution of trusted-procedure.
Also, I missed to check on sepgsql_needs_fmgr_hook(). We don't want to
allow inlining the function on lack of permission.
I'll fix them soon.
And how about adding a
ProcessUtility_hook to trap evil non-DML statements that some
nefarious user might issues?
It seems to me reasonable as long as the number of controlled command
are limited. For example, LOAD command may be a candidate being
controlled without exceptions.
However, it will be a tough work, if the plug-in tries to parse and
analyze supplied utility commands by itself.
One other problem is that you need to work on your whitespace a bit.
I believe in a few places you have a mixture of tabs and spaces. More
seriously, pgindent is going to completely mangle things like this:/*
* 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 SEPGSQL_MODE_PERMISSIVE,
* except for
no audit prints
*/You have to write it with a line of dashes on the first and last
lines, if you don't want it reformatted as a paragraph. It might be
worth actually running pgindent over contrib/selinux and then check
over the results.
OK, I'll try to run pgindent to confirm the effect of reformat.
Finally, we need to work on the documentation.
I uploaded my draft here.
http://wiki.postgresql.org/wiki/SEPostgreSQL_Documentation
If reasonable, I'll move them into *.sgml style.
I may want to simplify the step to installation using an installer
script.
But overall, it looks pretty good, IMHO.
Thanks for your reviewing in spite of a large patch.
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
2011/1/19 KaiGai Kohei <kaigai@ak.jp.nec.com>:
And how about adding a
ProcessUtility_hook to trap evil non-DML statements that some
nefarious user might issues?It seems to me reasonable as long as the number of controlled command
are limited. For example, LOAD command may be a candidate being
controlled without exceptions.
However, it will be a tough work, if the plug-in tries to parse and
analyze supplied utility commands by itself.
I think the key is to either accept or reject the command based on
very simple criteria - decide based only on the command type, and
ignore its parameters.
I uploaded my draft here.
http://wiki.postgresql.org/wiki/SEPostgreSQL_DocumentationIf reasonable, I'll move them into *.sgml style.
I have yet to review that, but will try to get to it before too much
more time goes by.
I may want to simplify the step to installation using an installer
script.
OK, but let's get this nailed down as soon as possible. Tempus fugit.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
(2011/01/20 13:01), Robert Haas wrote:
2011/1/19 KaiGai Kohei<kaigai@ak.jp.nec.com>:
And how about adding a
ProcessUtility_hook to trap evil non-DML statements that some
nefarious user might issues?It seems to me reasonable as long as the number of controlled command
are limited. For example, LOAD command may be a candidate being
controlled without exceptions.
However, it will be a tough work, if the plug-in tries to parse and
analyze supplied utility commands by itself.I think the key is to either accept or reject the command based on
very simple criteria - decide based only on the command type, and
ignore its parameters.
I can understand this idea, however, it is hard to implement this
criteria, because SELinux describes all the rules as a relationship
between a client and object using their label, so we cannot know
what actions (typically represented in command tag) are allowed or
denied without resolving their object names.
I uploaded my draft here.
http://wiki.postgresql.org/wiki/SEPostgreSQL_DocumentationIf reasonable, I'll move them into *.sgml style.
I have yet to review that, but will try to get to it before too much
more time goes by.
OK, I try to translate it into *.sgml format.
I may want to simplify the step to installation using an installer
script.OK, but let's get this nailed down as soon as possible. Tempus fugit.
I like to give my higher priority on the ProcessUtility_hook, rather
than installation script.
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
Excerpts from Robert Haas's message of jue ene 20 00:10:55 -0300 2011:
You have to write it with a line of dashes on the first and last
lines, if you don't want it reformatted as a paragraph. It might be
worth actually running pgindent over contrib/selinux and then check
over the results.
Hmm, I don't think pgindent is run regularly on contrib as it is on the
core code.
--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
On Thu, Jan 20, 2011 at 9:59 AM, Alvaro Herrera
<alvherre@commandprompt.com> wrote:
Excerpts from Robert Haas's message of jue ene 20 00:10:55 -0300 2011:
You have to write it with a line of dashes on the first and last
lines, if you don't want it reformatted as a paragraph. It might be
worth actually running pgindent over contrib/selinux and then check
over the results.Hmm, I don't think pgindent is run regularly on contrib as it is on the
core code.
I went back and looked at commit
239d769e7e05e0a5ef3bd6828e93e22ef3962780 and it touches both src and
contrib. But even if we don't always do that, it seems like a good
idea to fix whatever we're committing so that a hypothetical future
pgindent run won't mangle it.
Incidentally, I thought that running pgindent twice in the 9.0 cycle,
once after the end of CF4 and again just before the branch worked
well. Anyone up for doing it that way again this time?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Alvaro Herrera <alvherre@commandprompt.com> writes:
Hmm, I don't think pgindent is run regularly on contrib as it is on the
core code.
News to me.
regards, tom lane
The attached patch is a revised version.
Changeset from the previous revision:
- It fixed up a typo in catalog.
The "process:{transition}" is correct permission name.
- Add checks to avoid inlining function without db_procedure:{execute}
permission. Sorry, process:{transition} shall be checked in other place.
- sepgsql_utility_command() was added as a guest of ProcessUtility_hook,
to control LOAD command, right now.
- Documentation was revised. Mostly, description about permission checks.
- Some mixture of tabs/spaces were fixed.
- Source code comments were revised getting more friendly to pgindent,
as follows:
+/*
+ * 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
+ */
I also tried to run pgindent on the source files. Some of them were revised
well according to the coding rule, but some of them were painful, like:
{
- "db_schema", SEPG_CLASS_DB_SCHEMA,
+ "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 },
- }
+ {
+ "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},}
},
Do we have any workaround to avoid these indenting/formatting?
Or, the reformatted code is better than before?
Thanks,
(2011/01/07 12:02), Robert Haas wrote:
2011/1/6 KaiGai Kohei<kaigai@ak.jp.nec.com>:
If we use result of the `pg_config --sharedir` here, how about this
writing style? Or, do we have any other ideas?I'm not sure - I'll look at your next draft more closely.
The background of this wikipage is that I was persuading people
this feature being worthful, so the contents tend to philosophical
things rather than actual specifications.Yeah.
I also think wiki page allows us to brush up the documentation
rather than exchanging patches effectively. I'll set up a wiki page
that contains same contents with *.sgml file to revise documentation
stuff to be included into the *.sgml file finally. How about this idea?Sounds good.
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
Attachments:
sepgsql-v9.1-lite.3.patchapplication/octect-stream; name=sepgsql-v9.1-lite.3.patchDownload
diff --git a/configure b/configure
index 104ae30..c34816c 100755
--- a/configure
+++ b/configure
@@ -715,6 +715,7 @@ with_libxslt
with_libxml
XML2_CONFIG
with_ossp_uuid
+with_selinux
with_openssl
with_bonjour
with_ldap
@@ -837,6 +838,7 @@ with_pam
with_ldap
with_bonjour
with_openssl
+with_selinux
with_readline
with_libedit_preferred
with_ossp_uuid
@@ -848,6 +850,7 @@ with_gnu_ld
enable_largefile
enable_float4_byval
enable_float8_byval
+enable_float8_byval
'
ac_precious_vars='build_alias
host_alias
@@ -858,6 +861,7 @@ LDFLAGS
LIBS
CPPFLAGS
CPP
+CPPFLAGS
LDFLAGS_EX
LDFLAGS_SL
DOCBOOKSTYLE'
@@ -1533,6 +1537,7 @@ Optional Packages:
--with-ldap build with LDAP support
--with-bonjour build with Bonjour support
--with-openssl build with OpenSSL support
+ --with-selinux build with SELinux support
--without-readline do not use GNU Readline nor BSD Libedit for editing
--with-libedit-preferred
prefer BSD Libedit over GNU Readline
@@ -5364,6 +5369,40 @@ fi
$as_echo "$with_openssl" >&6; }
+#
+# SELinux
+#
+{ $as_echo "$as_me:$LINENO: checking whether to build with SELinux support" >&5
+$as_echo_n "checking whether to build with SELinux support... " >&6; }
+
+
+
+# Check whether --with-selinux was given.
+if test "${with_selinux+set}" = set; then
+ withval=$with_selinux;
+ case $withval in
+ yes)
+ :
+ ;;
+ no)
+ :
+ ;;
+ *)
+ { { $as_echo "$as_me:$LINENO: error: no argument expected for --with-selinux option" >&5
+$as_echo "$as_me: error: no argument expected for --with-selinux option" >&2;}
+ { (exit 1); exit 1; }; }
+ ;;
+ esac
+
+else
+ with_selinux=no
+
+fi
+
+
+
+{ $as_echo "$as_me:$LINENO: result: $with_selinux" >&5
+$as_echo "$with_selinux" >&6; }
#
# Readline
@@ -9291,6 +9330,89 @@ fi
fi
+# for contrib/sepgsql
+if test "$with_selinux" = yes; then
+
+{ $as_echo "$as_me:$LINENO: checking for getpeercon_raw in -lselinux" >&5
+$as_echo_n "checking for getpeercon_raw in -lselinux... " >&6; }
+if test "${ac_cv_lib_selinux_getpeercon_raw+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-lselinux $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h. */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h. */
+
+/* Override any GCC internal prototype to avoid an error.
+ Use char because int might match the return type of a GCC
+ builtin and then its argument prototype would still apply. */
+#ifdef __cplusplus
+extern "C"
+#endif
+char getpeercon_raw ();
+int
+main ()
+{
+return getpeercon_raw ();
+ ;
+ return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\""
+$as_echo "$ac_try_echo") >&5
+ (eval "$ac_link") 2>conftest.er1
+ ac_status=$?
+ grep -v '^ *+' conftest.er1 >conftest.err
+ rm -f conftest.er1
+ cat conftest.err >&5
+ $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } && {
+ test -z "$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ } && test -s conftest$ac_exeext && {
+ test "$cross_compiling" = yes ||
+ $as_test_x conftest$ac_exeext
+ }; then
+ ac_cv_lib_selinux_getpeercon_raw=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_selinux_getpeercon_raw=no
+fi
+
+rm -rf conftest.dSYM
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+ conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_getpeercon_raw" >&5
+$as_echo "$ac_cv_lib_selinux_getpeercon_raw" >&6; }
+if test "x$ac_cv_lib_selinux_getpeercon_raw" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBSELINUX 1
+_ACEOF
+
+ LIBS="-lselinux $LIBS"
+
+else
+ { { $as_echo "$as_me:$LINENO: error: library 'libselinux' is required for SELinux support" >&5
+$as_echo "$as_me: error: library 'libselinux' is required for SELinux support" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+fi
+
# for contrib/uuid-ossp
if test "$with_ossp_uuid" = yes ; then
{ $as_echo "$as_me:$LINENO: checking for uuid_export in -lossp-uuid" >&5
diff --git a/configure.in b/configure.in
index 109eb0c..581a219 100644
--- a/configure.in
+++ b/configure.in
@@ -676,6 +676,13 @@ PGAC_ARG_BOOL(with, openssl, no, [build with OpenSSL support],
AC_MSG_RESULT([$with_openssl])
AC_SUBST(with_openssl)
+#
+# SELinux
+#
+AC_MSG_CHECKING([whether to build with SELinux support])
+PGAC_ARG_BOOL(with, selinux, no, [build with SELinux support])
+AC_SUBST(with_selinux)
+AC_MSG_RESULT([$with_selinux])
#
# Readline
@@ -948,6 +955,12 @@ if test "$with_libxslt" = yes ; then
AC_CHECK_LIB(xslt, xsltCleanupGlobals, [], [AC_MSG_ERROR([library 'xslt' is required for XSLT support])])
fi
+# for contrib/sepgsql
+if test "$with_selinux" = yes; then
+ AC_CHECK_LIB(selinux, getpeercon_raw, [],
+ [AC_MSG_ERROR([library 'libselinux' is required for SELinux support])])
+fi
+
# for contrib/uuid-ossp
if test "$with_ossp_uuid" = yes ; then
AC_CHECK_LIB(ossp-uuid, uuid_export,
diff --git a/contrib/Makefile b/contrib/Makefile
index 5747bcc..ba2ec82 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -60,6 +60,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 6c5b7d5..016a388 100644
--- a/contrib/README
+++ b/contrib/README
@@ -159,6 +159,10 @@ seg -
Confidence-interval datatype (GiST indexing example)
by Gene Selkov, Jr. <selkovjr@mcs.anl.gov>
+sepgsql -
+ External security provider using SELinux
+ by KaiGai Kohei <kaigai@ak.jp.nec.com>
+
spi -
Various trigger functions, examples for using SPI.
diff --git a/contrib/sepgsql/Makefile b/contrib/sepgsql/Makefile
new file mode 100644
index 0000000..37a6dce
--- /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 0000000..cfa436d
--- /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 0000000..5625ebc
--- /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 0000000..0f0615c
--- /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 0000000..5242333
--- /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 0000000..6b55e48
--- /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 0000000..bc28adf
--- /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 100755
index 0000000..9e5ecdc
--- /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 0000000..f1a7b9b
--- /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 0000000..ceaa6b0
--- /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 0000000..df33a02
--- /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 0000000..a67bd56
--- /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 0000000..66666d0
--- /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 0000000..1a27818
--- /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 0000000..45ffe31
--- /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 0000000..6fa1eb8
--- /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 0000000..3162494
--- /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 0000000..a46d8a6
--- /dev/null
+++ b/contrib/sepgsql/sql/misc.sql
@@ -0,0 +1,5 @@
+--
+-- Regression Test for Misc Permission Checks
+--
+
+LOAD '$libdir/sepgsql'; -- failed
diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml
index d788473..ab0a99f 100644
--- a/doc/src/sgml/contrib.sgml
+++ b/doc/src/sgml/contrib.sgml
@@ -115,6 +115,7 @@ psql -d dbname -f <replaceable>SHAREDIR</>/contrib/<replaceable>module</>.sql
&pgtrgm;
&pgupgrade;
&seg;
+ &sepgsql;
&contrib-spi;
&sslinfo;
&tablefunc;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index aa2d801..40b10ad 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -128,6 +128,7 @@
<!entity pgupgrade SYSTEM "pgupgrade.sgml">
<!entity seg SYSTEM "seg.sgml">
<!entity contrib-spi SYSTEM "contrib-spi.sgml">
+<!entity sepgsql SYSTEM "sepgsql.sgml">
<!entity sslinfo SYSTEM "sslinfo.sgml">
<!entity tablefunc SYSTEM "tablefunc.sgml">
<!entity test-parser SYSTEM "test-parser.sgml">
diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml
new file mode 100644
index 0000000..91b8614
--- /dev/null
+++ b/doc/src/sgml/sepgsql.sgml
@@ -0,0 +1,704 @@
+<!-- doc/src/sgml/sepgsql.sgml -->
+
+<sect1 id="sepgsql">
+ <title>sepgsql</title>
+
+ <indexterm zone="sepgsql">
+ <primary>sepgsql</primary>
+ </indexterm>
+
+ <para>
+ The <filename>sepgsql</> is a module which performs as an external
+ security provider; to support label based mandatory access control
+ (MAC) base on <productname>SELinux</> policy.
+ </para>
+ <para>
+ This extension won't build at all unless the installation was configured
+ with <literal>--with-selinux</>.
+ </para>
+
+ <sect2 id="sepgsql-overview">
+ <title>Overview</title>
+
+ <para>
+ <productname>PostgreSQL</> provides various kind of hooks. Some of these
+ hooks can be utilized to make access control decision on the supplied
+ users' accesses on database objects.
+ We call plug-in modules making access control decision based on its own
+ security model as an external security provider.
+ </para>
+ <para>
+ This module acquires control on these strategic points, then it asks
+ <productname>SELinux</> to check whether the supplied access shall be
+ allowed, or not. Then, it returns its access control decision.
+ If violated, this module prevents this access with rising an error for
+ example.
+ </para>
+ <para>
+ A series of making decision is done independently from the default
+ database privilege mechanism. Users must be allowed with both of access
+ control models, whenever they try to access something.
+ </para>
+ <para>
+ We can see <productname>SELinux</> as a function which takes two arguments
+ then returns a bool value; allowed or denied. The first argument in this
+ analogy is label of subject which tries to reference a certain obejct.
+ The other one is label of the object being referenced in this operation.
+ </para>
+ <para>
+ Label is a formatted string,
+ like <literal>system_u:object_r:sepgsql_table_t:s0</>.
+ It is not a property depending on characteristics of a certain kind of
+ object, so we can apply common credentials on either database objects
+ or others.
+ </para>
+ <para>
+ <productname>PostgreSQL</> 9.1 or later supports
+ <xref linkend="sql-security-label"> statement that allows to assign
+ a security label on specified database objects, if user wants to change
+ label from the creation default.
+ Also <productname>SELinux</> provides an interface to obtain security
+ label of the peer process that connected to.
+ </para>
+ <para>
+ These facilities enable to integrate <productname>SELinux</> model within
+ access controls to database objects. Because it makes access control
+ decision according to a common centralized security policy (a set of rules),
+ its decision will be always consistent independent from the way to store
+ information assets.
+ </para>
+ </sect2>
+ <sect2 id="sepgsql-installation">
+ <title>Installation</title>
+ <para>
+ The <filename>sepgsql</> module requires the following packages to install.
+ Please check it at first.
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term><productname>Linux kernel</productname></term>
+ <listitem>
+ <para>
+ v2.6.28 or later with built with SELinux enabled
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><productname>libselinux</productname></term>
+ <listitem>
+ <para>
+ v2.0.80 or later
+ </para>
+ <para>
+ This library provides a set of APIs to communicate with
+ <productname>SELinux</> in kernel.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><productname>selinux-policy</productname></term>
+ <listitem>
+ <para>
+ v3.9.13 or later
+ </para>
+ <para>
+ The default security policy provides a set of access control rules.
+ Some of distribution may backports necessary rules, even if base
+ policy was older than above version.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ <para>
+ <productname>SE-PostgreSQL</> needs <productname>SELinux</> being
+ available on the platform. You can check the current setting using
+ <command>sestatus</>.
+<screen>
+$ sestatus
+SELinux status: enabled
+SELinuxfs mount: /selinux
+Current mode: enforcing
+Mode from config file: enforcing
+Policy version: 24
+Policy from config file: targeted
+</screen>
+ If disabled or not-installed, you need to set up <productname>SELinux</>
+ prior to all the installation step of <productname>SE-PostgreSQL</>.
+ </para>
+ <para>
+ On the compile time, add <literal>--with-selinux</> option to
+ the <command>configure</> script to check existence of
+ the <productname>libselinux</>, and to set a flag whether
+ we build this contrib module, or not.
+<screen>
+$ ./configure --enable-debug --enable-cassert --with-selinux
+$ make
+$ make install
+</screen>
+ </para>
+ <para>
+ Next to the <command>initdb</>, add <literal>'$libdir/sepgsql'</>
+ to <xref linkend="guc-shared-preload-libraries"> in
+ the <filename>postgresql.conf</>.
+
+ It enables to load <filename>sepgsql</> on the starting up of
+ postmaster process.
+ </para>
+ <para>
+ Then, load the <filename>sepgsql.sql</filename> script for each databases.
+ It installs functions corresponding to security label management, and
+ tries to assign initial labels on the target objects.
+ </para>
+ <para>
+ The following instruction assumes your installation is under the
+ <filename>/usr/local/pgsql</> directory, and the database cluster is in
+ <filename>/usr/local/pgsql/data</>. Substitute your paths appropriately.
+ </para>
+<screen>
+$ initdb -D $PGDATA
+$ vi $PGDATA/postgresql.conf
+$ for DBNAME in template0 template1 postgres; do
+ postgres --single -F -O -c exit_on_error=true -D $PGDATA $DBNAME \
+ < /usr/local/pgsql/share/contrib/sepgsql.sql > /dev/null
+ done
+</screen>
+ <para>
+ If all the installation process was done with no errors, start postmaster
+ process. <productname>SE-PostgreSQL</> shall prevent violated accesses
+ according to the security policy of <productname>SELinux</>.
+ </para>
+ </sect2>
+
+ <sect2 id="sepgsql-regression">
+ <title>Regression Tests</title>
+ <para>
+ The regression test of this module requires a few more configurations
+ on the platform system, in addition to the above installation process.
+ See the following steps.
+ </para>
+ <para>
+ First, install the policy package for regression test.
+ The <filename>sepgsql-regtest.pp</> is a special purpose policy package
+ that provides a set of rules to be allowed during the regression test
+ cases. It shall be installed at <filename>/usr/local/pgsql/share/contrib</>
+ directory in the default setup.
+ </para>
+ <para>
+ You need to install this policy package using <command>semodule</>
+ command which enables to link supplied policy packages and load them
+ into the kernel space. If you could install the pakage correctly,
+ <literal><command>semodule</> -l</> prints sepgsql-regtest as a part
+ of policy packages currently available.
+ </para>
+<screen>
+$ su
+# semodule -u /usr/local/pgsql/share/contrib/sepgsql-regtest.pp
+# semodule -l
+ :
+sepgsql-regtest 1.03
+ :
+</screen>
+ <para>
+ Second, turn on the <literal>sepgsql_regression_test_mode</>.
+ We don't enable all the rules in the <filename>sepgsql-regtest.pp</>
+ in the default, for your system's safety.
+ The <literal>sepgsql_regression_test_mode</literal> parameter is associated
+ with rules to launch regression test.
+ It can be turned on using <command>setsebool</> command.
+ </para>
+<screen>
+$ su
+# setsebool sepgsql_regression_test_mode on
+# getsebool sepgsql_regression_test_mode
+sepgsql_regression_test_mode --> on
+</screen>
+ <para>
+ Last, kick the regression test from the <literal>unconfined_t</> domain.
+ </para>
+ <para>
+ This test policy is designed to kick each test cases from the
+ <literal>unconfined_t</> domain that is a default choice in most of
+ the known <literal>SELinux</> installation base.
+ So, you don't need to set up anything special, as long as you didn't
+ change default configuration of SELinux before.
+ </para>
+ <para>
+ The <command>id</> command tells us the current working domain.
+ Confirm your shell is now performing with <literal>unconfined_t</>
+ domain as follows.
+ </para>
+<screen>
+$ id -Z
+unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
+</screen>
+ <para>
+ If not an expected one, you should revert this configuration.
+ The <xref linkend="sepgsql-resources"> section will give you
+ some useful hints.
+ </para>
+ <para>
+ Then, you will see the all-green result of regression test,
+ if we have no problem here.
+ </para>
+<screen>
+$ make -C contrib/sepgsql/ installcheck
+ :
+../../src/test/regress/pg_regress --inputdir=. --psqldir=/usr/local/pgsql/bin \
+ --dbname=contrib_regression --launcher ../../contrib/sepgsql/launcher \
+ label dml
+(using postmaster on Unix socket, default port)
+============== dropping database "contrib_regression" ==============
+DROP DATABASE
+============== creating database "contrib_regression" ==============
+CREATE DATABASE
+ALTER DATABASE
+============== running regression test queries ==============
+test label ... ok
+test dml ... ok
+test misc ... ok
+
+=====================
+ All 3 tests passed.
+=====================
+</screen>
+ <para>
+ If <command>pg_regress</> failed to launch <command>psql</> command,
+ here is a hint to fix up the matter.
+
+ When we try to launch <command>psql</> command with restrictive
+ privileges, the <command>psql</> must eb labeled as <literal>bin_t</>.
+ If not, try to run <command>restorecon</> to fix up security label of
+ the commands as expected.
+ </para>
+<screen>
+$ restorecon -R /usr/local/pgsql/
+</screen>
+ </sect2>
+
+ <sect2 id="sepgsql-parameters">
+ <title>GUC Parameters</title>
+
+ <variablelist>
+ <varlistentry id="guc-sepgsql-permissive" xreflabel="sepgsql.permissive">
+ <term><varname>sepgsql.permissive</> (<type>boolean</type>)</term>
+ <indexterm>
+ <primary><varname>sepgsql.permissive</> configuration parameter</primary>
+ </indexterm>
+ <listitem>
+ <para>
+ This parameter enables to perform <productname>SE-PostgreSQL</>
+ in permissive mode independent from the system setting.
+ The default is off (according to the system setting).
+ This parameter can only be set in the <filename>postgresql.conf</>
+ file or on the server command line.
+ </para>
+ <para>
+ We have two performing mode except for disabled; The one is enforcing
+ mode that checks the security policy on references and actually prevents
+ violated accesses. The other is permissive mode that only checks
+ the security policy, but does not prevents anything except for log
+ generation.
+ This log shall be utilized for debugging of the security policy itself.
+ </para>
+ <para>
+ When this parameter is on, <productname>SE-PostgreSQL</> performs
+ in permissive mode, even if the platform system is working on enforcing
+ mode.
+ We recommend users to keep the default setting, except for the case
+ when we develop security policy by ourself.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry id="guc-sepgsql-debug-audit" xreflabel="sepgsql.debug_audit">
+ <term><varname>sepgsql.debug_audit</> (<type>boolean</>)</>
+ <indexterm>
+ <primary><varname>sepgsql.debug_audit</> configuration parameter</>
+ </indexterm>
+ <listitem>
+ <para>
+ This parameter enables to print audit messages independent from
+ the policy setting.
+ The default is off (according to the security policy setting).
+ </para>
+ <para>
+ The security policy of <productname>SELinux</> also has rules to
+ control what accesses shall be logged, or not.
+ In the default, any access violations are logged, but any allowed
+ accesses are not logged.
+ </para>
+ <para>
+ When this parameter is on, all the possible logs shall be printed
+ independently from the policy settings.
+ We recommend to keep the variable turned off in normal cases to
+ avoid noisy messages.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect2>
+
+ <sect2 id="sepgsql-features">
+ <title>Features</title>
+ <sect3>
+ <title>controlled object classes</title>
+ <para>
+ The security model of <productname>SELinux</> describes all the access
+ control rules as a relationship between a subject entity (typically,
+ it is a client of database) and an object entity.
+ And, these entities are identified by a security label.
+ </para>
+ <para>
+ We call a set of these rules as security policy.
+ All the access control decision shall be made according to the security
+ policy, when we ask SELinux whether the required action shall be allowed
+ or not.
+ Thus, we have no way to control accesses on any sort of objects without
+ security labels.
+ (<productname>SELinux</> assumes <literal>unlabeled_t</> is assigned,
+ if no valid security label is assigned on the target object.)
+ </para>
+ <para>
+ This version of <productname>SE-PostgreSQL</> supports to assign
+ a security label on these database object classes: schema, table, column,
+ sequence, view and procedure.
+ Other database object classes are not supported to assign security label
+ on, right now.
+ </para>
+ <para>
+ A security label shall be automatically assigned to the supported
+ database objects on their creation time.
+ This label is called as a default security label; being decided according
+ to the security policy, or a pair of security label of the client and
+ upper object for more correctly.
+ </para>
+ <para>
+ A new database object basically inherits security label of the upper
+ object. A new column inherits security label of its parent table for
+ instance.
+ If and when the security policy has special rules called as
+ type-transition on a pair of the client and upper object, we can assign
+ an individual label as a default. The upper object depends on sort of
+ object classes as follows.
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>schema</term>
+ <listitem>
+ <para>
+ Its upper object is the current database.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>table</term>
+ <listitem>
+ <para>
+ Its upper object is the schema object which owns the new table.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>column</term>
+ <listitem>
+ <para>
+ Its upper object is the table object which owns the new column.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>sequence</term>
+ <listitem>
+ <para>
+ Its upper object is the schema object which owns the new sequence.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>view</term>
+ <listitem>
+ <para>
+ Its upper object is the schema object which owns the new view.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>procedure</term>
+ <listitem>
+ <para>
+ Its upper object is the schema object which owns the new procedure.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect3>
+ <sect3>
+ <title>DML Permissions</title>
+ <para>
+ This section introduces what permissions shall be checked on DML;
+ <literal>SELECT</>, <literal>INSERT</>, <literal>UPDATE</> and
+ <literal>DELETE</>.
+ </para>
+ <para>
+ DML statements are used to reference or modify contents within
+ the specified database objects; such as tables or columns.
+ We basically checks access rights of the client on all the appeared
+ objects in the given statement, and kind of privileges depend on
+ class of object and sort of accesses.
+ </para>
+ <para>
+ For tables, <literal>db_table:select</>, <literal>db_table:insert</>,
+ <literal>db_table:update</> or <literal>db_table:delete</> shall be
+ checked for all the appeared target tables depending on the sort of
+ statement;
+ In addition, <literal>db_table:select</> shall be also checked for
+ all the tables that containin the columns to be referenced in
+ <literal>WHERE</> or <literal>RETURNING</> clause, as a data source
+ of <literal>UPDATE</>, and so on.
+ </para>
+ <para>
+<synopsis>
+UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100;
+</synopsis>
+ In this case, we must have <literal>db_table:select</>, not only
+ <literal>db_table:update</>, because <literal>t1.a</> is referenced
+ within <literal>WHERE</> clause.
+ Also note that column-level permission shall be checked individually.
+ </para>
+ <para>
+ The client must be allowed to reference all the appeared tables and
+ columns, even if they are originated from views then expanded, unlike
+ the default database privileges, because we intend to apply consistent
+ access control rules independent from the route to reference contents
+ of the tables.
+ </para>
+ <para>
+ For columns, <literal>db_column:select</> shall be also checked on
+ not only the columns being read using <literal>SELECT</>, but being
+ referenced in other DML statement.
+ </para>
+ <para>
+ Of course, it also checks <literal>db_column:update</> or
+ <literal>db_column:insert</> on the column being modified by
+ <literal>UPDATE</> or <literal>INSERT</>.
+ Note that we have no definition of column-level delete permission,
+ like as the default database privilege doing.
+ </para>
+ <para>
+<synopsis>
+UPDATE t1 SET x = 2, y = md5sum(y) WHERE z = 100;
+</synopsis>
+ In this case, it checks <literal>db_column:update</> on
+ the <literal>t1.x</> being updated, <literal>db_column:{select update}</>
+ on the <literal>t1.y</> being updated and referenced,
+ and <literal>db_column:select</> on the <literal>t1.z</> being only
+ referenced in the <literal>WHERE</> clause.
+ Also note that <literal>db_table:{select update}</> shall be checked
+ in the table-level granularity.
+ </para>
+ <para>
+ For sequences, <literal>db_sequence:get_value</> when we reference
+ a sequence object using <literal>SELECT</>, however, note that we
+ cannot check permissions on execution of corresponding functions
+ such as <literal>lastval()</> right now, although they performs same
+ job, because here is no object access hook to acquire controls.
+ </para>
+ <para>
+ For views, <literal>db_view:expand</> shall be checked, then any other
+ corresponding permissions shall be also checked on the objects being
+ expanded from the view, individually.
+ Note that both of permissions have to be allowed.
+ </para>
+ <para>
+ For procedures, <literal>db_procedure:{execute}</> is defined, but not
+ checked in this version.
+ </para>
+ <para>
+ Here is a few more corner cases.
+ The default database privilege system allows database superusers to
+ modify system catalogs using DML commands, and reference or modify
+ toast tables, however, both of the cases shall be denied when
+ <productname>SE-PostgreSQL</> is enabled.
+ </para>
+ </sect3>
+ <sect3>
+ <title>DDL Permissions</title>
+ <para>
+ On <xref linkend="sql-security-label"> command, <literal>setattr</> and
+ <literal>relabelfrom</> shall be checked on the object being relabeled
+ with an old security label, then <literal>relabelto</> on the supplied
+ new security label.
+ </para>
+ <para>
+ In a case when multiple label providers are installed and user tries
+ to set a security label, but is not managed by <productname>SELinux</>,
+ only <literal>setattr</> should be checked here.
+ However, it is not unavailable because of limitation of the hook.
+ </para>
+ <para>
+ As we will describe in <xref linkend="sepgsql-limitations"> section,
+ <productname>SE-PostgreSQL</> does not control any other DDL operations.
+ </para>
+ </sect3>
+ <sect3>
+ <title>Trusted Procedure</title>
+ <para>
+ It is a similar idea to security definer functions or set-uid commands
+ on operating systems. <productname>SELinux</> provides a feature to
+ switch privilege of the client (that is a security label of the client
+ for more correctness) during execution of certain functions; being
+ called as trusted procedures.
+ </para>
+ <para>
+ A trusted function is a function with a special security label being
+ set up as a trusted procedure.
+ So, we need to assign the special security label on the function that
+ we hope it to perform as a trusted procedure, by administrative users.
+ The default security policy also provides this special security label.
+ See the following example.
+ </para>
+<screen>
+postgres=# CREATE TABLE customer (
+ cid int primary key,
+ cname text,
+ credit text
+ );
+CREATE TABLE
+postgres=# SECURITY LABEL ON COLUMN customer.credit
+ IS 'system_u:object_r:sepgsql_secret_table_t:s0';
+SECURITY LABEL
+postgres=# CREATE FUNCTION show_credit(int) RETURNS text
+ AS 'SELECT regexp_replace(credit, ''-[0-9]+$'', ''-xxxx'', ''g'')
+ FROM customer WHERE cid = $1'
+ LANGUAGE sql;
+CREATE FUNCTION
+postgres=# SECURITY LABEL ON FUNCTION show_credit(int)
+ IS 'system_u:object_r:sepgsql_trusted_proc_exec_t:s0';
+SECURITY LABEL
+</screen>
+ <para>
+ Above operations shall be done by administrative users.
+ </para>
+<screen>
+postgres=# SELECT * FROM customer;
+ERROR: SELinux: security policy violation
+postgres=# SELECT cid, cname, show_credit(cid) FROM customer;
+ cid | cname | show_credit
+-----+--------+---------------------
+ 1 | taro | 1111-2222-3333-xxxx
+ 2 | hanako | 5555-6666-7777-xxxx
+(2 rows)
+</screen>
+ <para>
+ In this case, a regular user cannot reference <literal>customer.credit</>
+ directly, but a trusted procedure <literal>show_credit</> enables us
+ to print credit number of customers with a bit modification.
+ </para>
+ </sect3>
+ <sect3>
+ <title>Miscellaneous</title>
+ <para>
+ In this version, we reject <xref linkend="sql-load"> command across
+ the board, because the binary module can override security hooks to
+ make access control decision. It means a risk to invalidate all the
+ control by security providers.
+ </para>
+ </sect3>
+ </sect2>
+ <sect2 id="sepgsql-limitations">
+ <title>Limitations</title>
+ <para>
+ This section introduces limitations of <productname>SE-PostgreSQL</>
+ in this version.
+ </para>
+ <variablelist>
+ <varlistentry>
+ <term>Userspace access vector cache</term>
+ <listitem>
+ <para>
+ <productname>SE-PostgreSQL</> tells <productname>SELinux</> its access
+ control decision. It takes system call invocation being heavy, however,
+ we can reduce number of the invocations using caching mechanism; called
+ as access vector cache in <productname>SELinux</>.
+ Because of code size, <productname>SE-PostgreSQL</> does not support
+ this mechanism yet.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>DDL Permissions</term>
+ <listitem>
+ <para>
+ Now <productname>PostgreSQL</> does not provide a set of hooks on
+ the DDL routines.
+ It means plugin modules cannot acquire control here,
+ so <productname>SE-PostgreSQL</> does not check DDL Permissions
+ right now.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Row-level access control</term>
+ <listitem>
+ <para>
+ Now <productname>SE-PostgreSQL</> does not support row-level access
+ control, because a few needed facilities are not supported yet.
+ The one is security labels on users' tables. The other is behavior of
+ optimizer. Also see <xref linkend="rules-privileges"> for more details.
+ We know similar issue on VIEW.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>Covert channels</term>
+ <listitem>
+ <para>
+ <productname>SE-PostgreSQL</> never tries to hide existence of
+ a certain object, even if user is not allowed to reference.
+ For example, we can infer an existence of invisible object using
+ primary-key confliction, foreign-key violation, and so on, even if
+ we cannot reference contents of these objects.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect2>
+ <sect2 id="sepgsql-resources">
+ <title>External Resources</title>
+ <variablelist>
+ <varlistentry>
+ <term><ulink url="http://wiki.postgresql.org/wiki/SEPostgreSQL">SE-PostgreSQL Introduction</ulink></term>
+ <listitem>
+ <para>
+ This wikipage provides a brief-overview, security design, architecture,
+ administration and upcoming feature for more details.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><ulink url="http://docs.fedoraproject.org/selinux-user-guide/">Fedora SELinux User Guide</ulink></term>
+ <listitem>
+ <para>
+ This document provides wide spectrum of knowledge to administrate
+ SELinux on your systems.
+ It primary focuses on Fedora, but not limited to Fedora.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><ulink url="http://docs.fedoraproject.org/selinux-faq">Fedora SELinux FAQ</ulink></term>
+ <listitem>
+ <para>
+ This document provides FAQs about SELinux.
+ It primary focuses on Fedora, but not limited to Fedora.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </sect2>
+ <sect2 id="sepgsql-author">
+ <title>Author</title>
+ <para>
+ KaiGai Kohei (<email>kaigai@ak.jp.nec.com</email>)
+ </para>
+ </sect2>
+</sect1>
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index ebeee0c..d6b7b47 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -158,6 +158,7 @@ with_python = @with_python@
with_tcl = @with_tcl@
with_openssl = @with_openssl@
with_ossp_uuid = @with_ossp_uuid@
+with_selinux = @with_selinux@
with_libxml = @with_libxml@
with_libxslt = @with_libxslt@
with_system_tzdata = @with_system_tzdata@
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 79655cd..a321162 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -84,6 +84,7 @@ bool debug = false;
char *inputdir = ".";
char *outputdir = ".";
char *psqldir = PGBINDIR;
+char *launcher = NULL;
static _stringlist *loadlanguage = NULL;
static int max_connections = 0;
static char *encoding = NULL;
@@ -1871,6 +1872,7 @@ help(void)
printf(_(" --dlpath=DIR look for dynamic libraries in DIR\n"));
printf(_(" --temp-install=DIR create a temporary installation in DIR\n"));
printf(_(" --use-existing use an existing installation\n"));
+ printf(_(" --launcher=CMD use CMD as launcher of psql\n"));
printf(_("\n"));
printf(_("Options for \"temp-install\" mode:\n"));
printf(_(" --no-locale use C locale\n"));
@@ -1922,6 +1924,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
{"create-role", required_argument, NULL, 18},
{"temp-config", required_argument, NULL, 19},
{"use-existing", no_argument, NULL, 20},
+ {"launcher", required_argument, NULL, 21},
{NULL, 0, NULL, 0}
};
@@ -2015,6 +2018,9 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
case 20:
use_existing = true;
break;
+ case 21:
+ launcher = strdup(optarg);
+ break;
default:
/* getopt_long already emitted a complaint */
fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
diff --git a/src/test/regress/pg_regress.h b/src/test/regress/pg_regress.h
index 26069f6..606c7a1 100644
--- a/src/test/regress/pg_regress.h
+++ b/src/test/regress/pg_regress.h
@@ -41,6 +41,7 @@ extern _stringlist *dblist;
extern bool debug;
extern char *inputdir;
extern char *outputdir;
+extern char *launcher;
/*
* This should not be global but every module should be able to read command
diff --git a/src/test/regress/pg_regress_main.c b/src/test/regress/pg_regress_main.c
index 710e558..3e43dd7 100644
--- a/src/test/regress/pg_regress_main.c
+++ b/src/test/regress/pg_regress_main.c
@@ -33,6 +33,7 @@ psql_start_test(const char *testname,
char outfile[MAXPGPATH];
char expectfile[MAXPGPATH];
char psql_cmd[MAXPGPATH * 3];
+ size_t offset = 0;
/*
* Look for files in the output dir first, consistent with a vpath search.
@@ -58,7 +59,11 @@ psql_start_test(const char *testname,
add_stringlist_item(resultfiles, outfile);
add_stringlist_item(expectfiles, expectfile);
- snprintf(psql_cmd, sizeof(psql_cmd),
+ if (launcher)
+ offset += snprintf(psql_cmd + offset, sizeof(psql_cmd) - offset,
+ "%s ", launcher);
+
+ snprintf(psql_cmd + offset, sizeof(psql_cmd) - offset,
SYSTEMQUOTE "\"%s%spsql\" -X -a -q -d \"%s\" < \"%s\" > \"%s\" 2>&1" SYSTEMQUOTE,
psqldir ? psqldir : "",
psqldir ? "/" : "",
2011/1/21 KaiGai Kohei <kaigai@ak.jp.nec.com>:
- Add checks to avoid inlining function without db_procedure:{execute}
permission. Sorry, process:{transition} shall be checked in other place.
Hrm. What happens if permissions change between plan time and execution time?
For that matter, I wonder what happens with regular function
permissions. If the plan inlines the function and then somebody goes
and changes the permission on the function and makes it SECURITY
DEFINER, what happens?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
For that matter, I wonder what happens with regular function
permissions. If the plan inlines the function and then somebody goes
and changes the permission on the function and makes it SECURITY
DEFINER, what happens?
ALTER FUNCTION is supposed to cause plan invalidation in such a case.
Not sure if GRANT plays nice with that though.
regards, tom lane
On Fri, Jan 21, 2011 at 9:55 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
For that matter, I wonder what happens with regular function
permissions. If the plan inlines the function and then somebody goes
and changes the permission on the function and makes it SECURITY
DEFINER, what happens?ALTER FUNCTION is supposed to cause plan invalidation in such a case.
Not sure if GRANT plays nice with that though.
And in the case of SE-Linux, this could get changed from outside the
database. Not sure how to handle that. I guess we could just never
inline anything, but that might be an overreaction.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
2011/1/22 Robert Haas <robertmhaas@gmail.com>:
On Fri, Jan 21, 2011 at 9:55 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
For that matter, I wonder what happens with regular function
permissions. If the plan inlines the function and then somebody goes
and changes the permission on the function and makes it SECURITY
DEFINER, what happens?ALTER FUNCTION is supposed to cause plan invalidation in such a case.
Not sure if GRANT plays nice with that though.And in the case of SE-Linux, this could get changed from outside the
database. Not sure how to handle that. I guess we could just never
inline anything, but that might be an overreaction.
We can have two standpoints.
The one is that functions are once allowed to execute on the plan time,
so we don't need to check it on execution time, and inlined.
Thus, the function shall be melted.
The other is that permission checks should be done in execution time,
so we never allows to inline functions anyway.
This attitude is more strict, but mostly overreaction.
In my opinion, the later one is more correct standpoint when we put
the highest priority on security.
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
Robert Haas <robertmhaas@gmail.com> writes:
On Fri, Jan 21, 2011 at 9:55 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
ALTER FUNCTION is supposed to cause plan invalidation in such a case.
Not sure if GRANT plays nice with that though.
And in the case of SE-Linux, this could get changed from outside the
database. Not sure how to handle that. I guess we could just never
inline anything, but that might be an overreaction.
I think SELinux is just out of luck in that case. If it didn't refuse
execution permission at the time we checked before inlining (which we
do), it doesn't get to change its mind later.
regards, tom lane
On Fri, Jan 21, 2011 at 10:46 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Fri, Jan 21, 2011 at 9:55 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
ALTER FUNCTION is supposed to cause plan invalidation in such a case.
Not sure if GRANT plays nice with that though.And in the case of SE-Linux, this could get changed from outside the
database. Not sure how to handle that. I guess we could just never
inline anything, but that might be an overreaction.I think SELinux is just out of luck in that case. If it didn't refuse
execution permission at the time we checked before inlining (which we
do), it doesn't get to change its mind later.
Seems reasonable to me, if it works for KaiGai.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
2011/1/22 Robert Haas <robertmhaas@gmail.com>:
On Fri, Jan 21, 2011 at 10:46 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Fri, Jan 21, 2011 at 9:55 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
ALTER FUNCTION is supposed to cause plan invalidation in such a case.
Not sure if GRANT plays nice with that though.And in the case of SE-Linux, this could get changed from outside the
database. Not sure how to handle that. I guess we could just never
inline anything, but that might be an overreaction.I think SELinux is just out of luck in that case. If it didn't refuse
execution permission at the time we checked before inlining (which we
do), it doesn't get to change its mind later.Seems reasonable to me, if it works for KaiGai.
I assume users of SE-PostgreSQL put their first priority on security,
not best-performance. So, I also think it is reasonable to kill a part of
optimization for the strict security checks.
Here is one request for the hook.
needs_fmgr_hook() is called by fmgr_info_cxt_security() and routines
to inline. I need a flag to distinct these cases, because we don't need
to invoke all the functions via fmgr_security_definer(), even if it never
allows to inline.
Thanks,
--
KaiGai Kohei <kaigai@kaigai.gr.jp>
On Fri, Jan 21, 2011 at 11:00 AM, Kohei KaiGai <kaigai@kaigai.gr.jp> wrote:
2011/1/22 Robert Haas <robertmhaas@gmail.com>:
On Fri, Jan 21, 2011 at 10:46 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Fri, Jan 21, 2011 at 9:55 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
ALTER FUNCTION is supposed to cause plan invalidation in such a case.
Not sure if GRANT plays nice with that though.And in the case of SE-Linux, this could get changed from outside the
database. Not sure how to handle that. I guess we could just never
inline anything, but that might be an overreaction.I think SELinux is just out of luck in that case. If it didn't refuse
execution permission at the time we checked before inlining (which we
do), it doesn't get to change its mind later.Seems reasonable to me, if it works for KaiGai.
I assume users of SE-PostgreSQL put their first priority on security,
not best-performance. So, I also think it is reasonable to kill a part of
optimization for the strict security checks.Here is one request for the hook.
needs_fmgr_hook() is called by fmgr_info_cxt_security() and routines
to inline. I need a flag to distinct these cases, because we don't need
to invoke all the functions via fmgr_security_definer(), even if it never
allows to inline.
I don't want to go there, and it's not what Tom was proposing anyway.
The idea is - if the user creates a function which is NOT a trusted
procedure and executes it, and then subsequently changes the system
security policy so that it becomes a trusted procedure, the user will
be responsible for flushing the cached plans before the new value will
take effect. That doesn't require nearly as much de-optimization, and
I don't believe it is a serious issue from a security perspective,
either. (Note that the reverse case, where a trusted procedure is
demoted to a non-trusted procedure, isn't an issue, because we will
have suppressed inlining and the new execution will follow the right
rules, just with reduced performance.)
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
I don't want to go there, and it's not what Tom was proposing anyway.
The idea is - if the user creates a function which is NOT a trusted
procedure and executes it, and then subsequently changes the system
security policy so that it becomes a trusted procedure, the user will
be responsible for flushing the cached plans before the new value will
take effect.
Yeah. Given the rather limited set of things that can be inlined,
I don't think that it's worth the complexity or performance cost to
do differently. Note also that it's pretty easy to force the cache
flush if you are the procedure's owner: any sort of dummy ALTER on
the procedure should do it.
Mind you, I think there probably *is* a case for fixing REVOKE to force
a cache flush on the procedure as well. I just don't want to have to
deal with magic outside-the-database changes.
regards, tom lane
2011/1/21 KaiGai Kohei <kaigai@ak.jp.nec.com>:
Do we have any workaround to avoid these indenting/formatting?
Or, the reformatted code is better than before?
That's pretty horrendous. Tom/Bruce, any ideas?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Sun, Jan 23, 2011 at 03:19, Robert Haas <robertmhaas@gmail.com> wrote:
2011/1/21 KaiGai Kohei <kaigai@ak.jp.nec.com>:
Do we have any workaround to avoid these indenting/formatting?
Or, the reformatted code is better than before?That's pretty horrendous. Tom/Bruce, any ideas?
I saw some similar things earlier, and it turned out to be two
different reasons in two different cases. In one case, it was because
I was using GNU indent, even though I thought I was using the one
that's on our ftp. But it does give a warning in that case, you just
have to actually *read* the warning. In the other case it was really
weird - when my wrapper script (that called pgindent with path
specification and such) executing using dash (the default /bin/sh on
Ubuntu), it did weird things - but when I explicitly executed the
wrapper script with /bin/bash, it worked - even though pgindent itself
is still using /bin/sh.
--
Magnus Hagander
Me: http://www.hagander.net/
Work: http://www.redpill-linpro.com/
Magnus Hagander <magnus@hagander.net> writes:
On Sun, Jan 23, 2011 at 03:19, Robert Haas <robertmhaas@gmail.com> wrote:
That's pretty horrendous. �Tom/Bruce, any ideas?
I saw some similar things earlier, and it turned out to be two
different reasons in two different cases. In one case, it was because
I was using GNU indent, even though I thought I was using the one
that's on our ftp. But it does give a warning in that case, you just
have to actually *read* the warning. In the other case it was really
weird - when my wrapper script (that called pgindent with path
specification and such) executing using dash (the default /bin/sh on
Ubuntu), it did weird things - but when I explicitly executed the
wrapper script with /bin/bash, it worked - even though pgindent itself
is still using /bin/sh.
Hm, but then the inner /bin/sh is really dash no? Maybe the outer
invocation is setting environment variables or something to change the
behavior of the inner invocation. That would be pretty broken, but IME
most bash substitutes are pretty broken.
regards, tom lane
2011/1/21 KaiGai Kohei <kaigai@ak.jp.nec.com>:
The attached patch is a revised version.
I've committed this. Cleanup coming...
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Sun, Jan 23, 2011 at 8:53 PM, Robert Haas <robertmhaas@gmail.com> wrote:
2011/1/21 KaiGai Kohei <kaigai@ak.jp.nec.com>:
The attached patch is a revised version.
I've committed this. Cleanup coming...
Yikes. On further examination, exec_object_restorecon() is pretty
bogus. Surely you need some calls to quote_literal_cstr() in there
someplace. And how about using getObjectDescriptionOids() for the
error message, instead of the entirely bogus construction that's there
now?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Sun, Jan 23, 2011 at 9:56 PM, Robert Haas <robertmhaas@gmail.com> wrote:
On Sun, Jan 23, 2011 at 8:53 PM, Robert Haas <robertmhaas@gmail.com> wrote:
2011/1/21 KaiGai Kohei <kaigai@ak.jp.nec.com>:
The attached patch is a revised version.
I've committed this. Cleanup coming...
Yikes. On further examination, exec_object_restorecon() is pretty
bogus. Surely you need some calls to quote_literal_cstr() in there
someplace. And how about using getObjectDescriptionOids() for the
error message, instead of the entirely bogus construction that's there
now?
Also, shouldn't a bunch of these messages end in ": %m"?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
(2011/01/24 12:49), Robert Haas wrote:
On Sun, Jan 23, 2011 at 9:56 PM, Robert Haas<robertmhaas@gmail.com> wrote:
On Sun, Jan 23, 2011 at 8:53 PM, Robert Haas<robertmhaas@gmail.com> wrote:
2011/1/21 KaiGai Kohei<kaigai@ak.jp.nec.com>:
The attached patch is a revised version.
I've committed this. Cleanup coming...
Yikes. On further examination, exec_object_restorecon() is pretty
bogus. Surely you need some calls to quote_literal_cstr() in there
someplace.
Are you concerning about the object name being supplied to
selabel_lookup_raw() in exec_object_restorecon()?
I also think this quoting you suggested is reasonable.
And how about using getObjectDescriptionOids() for the
error message, instead of the entirely bogus construction that's there
now?
It seems to me a good idea. I'll try to revise corresponding code.
Also, shouldn't a bunch of these messages end in ": %m"?
When these messages are because of unexpected operating system errors,
such as fails in communication with selinux, the "%m" will give us good
suggestions.
I'll submit these fixes within a few days, please wait for a while.
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
(2011/01/26 12:23), KaiGai Kohei wrote:
Yikes. On further examination, exec_object_restorecon() is pretty
bogus. Surely you need some calls to quote_literal_cstr() in there
someplace.Are you concerning about the object name being supplied to
selabel_lookup_raw() in exec_object_restorecon()?
I also think this quoting you suggested is reasonable.
How about the case when the object name only contains alphabet and
numerical characters?
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
2011/1/25 KaiGai Kohei <kaigai@ak.jp.nec.com>:
(2011/01/26 12:23), KaiGai Kohei wrote:
Yikes. On further examination, exec_object_restorecon() is pretty
bogus. Surely you need some calls to quote_literal_cstr() in there
someplace.Are you concerning about the object name being supplied to
selabel_lookup_raw() in exec_object_restorecon()?
I also think this quoting you suggested is reasonable.How about the case when the object name only contains alphabet and
numerical characters?
Oh, quote_literal_cstr() is the wrong function - these are
identifiers, not literals. So we should use quote_identifier().
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
(2011/01/27 0:25), Robert Haas wrote:
2011/1/25 KaiGai Kohei<kaigai@ak.jp.nec.com>:
(2011/01/26 12:23), KaiGai Kohei wrote:
Yikes. On further examination, exec_object_restorecon() is pretty
bogus. Surely you need some calls to quote_literal_cstr() in there
someplace.Are you concerning about the object name being supplied to
selabel_lookup_raw() in exec_object_restorecon()?
I also think this quoting you suggested is reasonable.How about the case when the object name only contains alphabet and
numerical characters?Oh, quote_literal_cstr() is the wrong function - these are
identifiers, not literals. So we should use quote_identifier().
OK, I did with quote_identifier().
The attached patch fixes up several stuffs in sepgsql module.
- The object names being supplied to selabel_lookup_raw() to
lookup initial labels become qualified by quote_identifier(),
if necessary.
- On access violation, sepgsql_check_perms() records audit
logs. It contains object name being referenced.
It became generated using getObjectDescription().
- Also, sepgsql_audit_log() becomes to quote the supplied
object name, because it may contains white-space.
- Error messages become obtaining "%m", when the error was
originated from the libselinux interfaces. It will provides
DBA a hint why interactions with SELinux does not work well.
- Documentation was updated to suggest users to install
libselinux v2.0.93 or later, because it used newer features
than ones provided in v2.0.80.
- Regression Test was updated, because of error message updates.
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
Attachments:
sepgsql-v9.1-fixup.1.patchapplication/octect-stream; name=sepgsql-v9.1-fixup.1.patchDownload
contrib/sepgsql/dml.c | 37 ++++++++++----
contrib/sepgsql/expected/dml.out | 6 +-
contrib/sepgsql/expected/label.out | 2 +-
contrib/sepgsql/expected/misc.out | 2 +-
contrib/sepgsql/hooks.c | 4 +-
contrib/sepgsql/label.c | 89 ++++++++++++++++++++++++++++-------
contrib/sepgsql/proc.c | 3 +-
contrib/sepgsql/relation.c | 20 ++++++--
contrib/sepgsql/schema.c | 3 +-
contrib/sepgsql/selinux.c | 6 +-
doc/src/sgml/sepgsql.sgml | 2 +-
11 files changed, 126 insertions(+), 48 deletions(-)
diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c
index 684b5ee..358a264 100644
--- a/contrib/sepgsql/dml.c
+++ b/contrib/sepgsql/dml.c
@@ -14,6 +14,7 @@
#include "access/tupdesc.h"
#include "catalog/catalog.h"
#include "catalog/heap.h"
+#include "catalog/dependency.h"
#include "catalog/pg_attribute.h"
#include "catalog/pg_class.h"
#include "catalog/pg_inherits_fn.h"
@@ -151,6 +152,7 @@ check_relation_privileges(Oid relOid,
char relkind = get_rel_relkind(relOid);
char *scontext = sepgsql_get_client_label();
char *tcontext;
+ char *audit_name;
Bitmapset *columns;
int index;
bool result = true;
@@ -183,6 +185,7 @@ check_relation_privileges(Oid relOid,
* Check permissions on the relation
*/
tcontext = sepgsql_get_label(RelationRelationId, relOid, 0);
+ audit_name = getObjectDescriptionOids(RelationRelationId, relOid);
switch (relkind)
{
case RELKIND_RELATION:
@@ -190,10 +193,8 @@ check_relation_privileges(Oid relOid,
tcontext,
SEPG_CLASS_DB_TABLE,
required,
- get_rel_name(relOid),
+ audit_name,
abort);
- if (!result)
- return false;
break;
case RELKIND_SEQUENCE:
@@ -204,23 +205,31 @@ check_relation_privileges(Oid relOid,
tcontext,
SEPG_CLASS_DB_SEQUENCE,
SEPG_DB_SEQUENCE__GET_VALUE,
- get_rel_name(relOid),
+ audit_name,
abort);
- return result;
+ break;
case RELKIND_VIEW:
result = sepgsql_check_perms(scontext,
tcontext,
SEPG_CLASS_DB_VIEW,
SEPG_DB_VIEW__EXPAND,
- get_rel_name(relOid),
+ audit_name,
abort);
- return result;
+ break;
default:
/* nothing to be checked */
- return true;
+ break;
}
+ pfree(tcontext);
+ pfree(audit_name);
+
+ /*
+ * Only columns owned by relations shall be checked
+ */
+ if (relkind != RELKIND_RELATION)
+ return true;
/*
* Check permissions on the columns
@@ -233,7 +242,7 @@ check_relation_privileges(Oid relOid,
{
AttrNumber attnum;
uint32 column_perms = 0;
- char audit_name[NAMEDATALEN * 2 + 10];
+ ObjectAddress object;
if (bms_is_member(index, selected))
column_perms |= SEPG_DB_COLUMN__SELECT;
@@ -250,8 +259,11 @@ check_relation_privileges(Oid relOid,
/* 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));
+
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = attnum;
+ audit_name = getObjectDescription(&object);
result = sepgsql_check_perms(scontext,
tcontext,
@@ -259,6 +271,9 @@ check_relation_privileges(Oid relOid,
column_perms,
audit_name,
abort);
+ pfree(tcontext);
+ pfree(audit_name);
+
if (!result)
return result;
}
diff --git a/contrib/sepgsql/expected/dml.out b/contrib/sepgsql/expected/dml.out
index 5625ebc..c1bbbba 100644
--- a/contrib/sepgsql/expected/dml.out
+++ b/contrib/sepgsql/expected/dml.out
@@ -42,15 +42,15 @@ SELECT objtype, objname, label FROM pg_seclabels
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
+ column | t5.f | system_u:object_r:sepgsql_ro_table_t:s0
+ column | t5.g | system_u:object_r:sepgsql_secret_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
+ERROR: SELinux: hardwired security policy violation
--
-- Simple DML statements
--
diff --git a/contrib/sepgsql/expected/label.out b/contrib/sepgsql/expected/label.out
index 0f0615c..89f56f7 100644
--- a/contrib/sepgsql/expected/label.out
+++ b/contrib/sepgsql/expected/label.out
@@ -57,7 +57,7 @@ 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"
+ERROR: SELinux: 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):
diff --git a/contrib/sepgsql/expected/misc.out b/contrib/sepgsql/expected/misc.out
index 5242333..329852c 100644
--- a/contrib/sepgsql/expected/misc.out
+++ b/contrib/sepgsql/expected/misc.out
@@ -2,4 +2,4 @@
-- Regression Test for Misc Permission Checks
--
LOAD '$libdir/sepgsql'; -- failed
-ERROR: SELinux: LOAD is not allowed anyway.
+ERROR: SELinux: LOAD is not permitted
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index bc7ce51..8b87ec3 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -91,7 +91,7 @@ sepgsql_client_auth(Port *port, int status)
if (getpeercon_raw(port->sock, &context) < 0)
ereport(FATAL,
(errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("SELinux: unable to get peer label")));
+ errmsg("SELinux: unable to get peer label : %m")));
sepgsql_set_client_label(context);
@@ -414,7 +414,7 @@ _PG_init(void)
if (getcon_raw(&context) < 0)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("SELinux: failed to get server security label")));
+ errmsg("SELinux: failed to get server security label : %m")));
sepgsql_set_client_label(context);
/* Security label provider hook */
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c
index ad568f8..f0241bf 100644
--- a/contrib/sepgsql/label.c
+++ b/contrib/sepgsql/label.c
@@ -81,7 +81,7 @@ sepgsql_get_label(Oid classId, Oid objectId, int32 subId)
if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("SELinux: failed to get initial security label")));
+ errmsg("SELinux: failed to get initial security label : %m")));
PG_TRY();
{
label = pstrdup(unlabeled);
@@ -184,7 +184,7 @@ sepgsql_mcstrans_in(PG_FUNCTION_ARGS)
&raw_label) < 0)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("SELinux: could not translate security label")));
+ errmsg("SELinux: could not translate security label : %m")));
PG_TRY();
{
@@ -224,7 +224,7 @@ sepgsql_mcstrans_out(PG_FUNCTION_ARGS)
&qual_label) < 0)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("SELinux: could not translate security label")));
+ errmsg("SELinux: could not translate security label : %m")));
PG_TRY();
{
@@ -242,6 +242,51 @@ sepgsql_mcstrans_out(PG_FUNCTION_ARGS)
}
/*
+ * quote_object_names
+ *
+ * It tries to quote the supplied identifiers
+ */
+static char *
+quote_object_name(const char *src1, const char *src2,
+ const char *src3, const char *src4)
+{
+ StringInfoData result;
+ const char *temp;
+
+ initStringInfo(&result);
+
+ if (src1)
+ {
+ temp = quote_identifier(src1);
+ appendStringInfo(&result, "%s", temp);
+ if (src1 != temp)
+ pfree((void *)temp);
+ }
+ if (src2)
+ {
+ temp = quote_identifier(src2);
+ appendStringInfo(&result, ".%s", temp);
+ if (src2 != temp)
+ pfree((void *)temp);
+ }
+ if (src3)
+ {
+ temp = quote_identifier(src3);
+ appendStringInfo(&result, ".%s", temp);
+ if (src3 != temp)
+ pfree((void *)temp);
+ }
+ if (src4)
+ {
+ temp = quote_identifier(src4);
+ appendStringInfo(&result, ".%s", temp);
+ if (src4 != temp)
+ pfree((void *)temp);
+ }
+ return result.data;
+}
+
+/*
* exec_object_restorecon
*
* This routine is a helper called by sepgsql_restorecon; it set up
@@ -273,7 +318,7 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
Form_pg_class relForm;
Form_pg_attribute attForm;
Form_pg_proc proForm;
- char objname[NAMEDATALEN * 4 + 10];
+ char *objname;
int objtype = 1234;
ObjectAddress object;
security_context_t context;
@@ -288,8 +333,10 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
nspForm = (Form_pg_namespace) GETSTRUCT(tuple);
objtype = SELABEL_DB_SCHEMA;
- snprintf(objname, sizeof(objname), "%s.%s",
- database_name, NameStr(nspForm->nspname));
+
+ objname = quote_object_name(database_name,
+ NameStr(nspForm->nspname),
+ NULL, NULL);
object.classId = NamespaceRelationId;
object.objectId = HeapTupleGetOid(tuple);
@@ -309,9 +356,10 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
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));
+ objname = quote_object_name(database_name,
+ namespace_name,
+ NameStr(relForm->relname),
+ NULL);
pfree(namespace_name);
object.classId = RelationRelationId;
@@ -330,11 +378,12 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
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);
+ objname = quote_object_name(database_name,
+ namespace_name,
+ relation_name,
+ NameStr(attForm->attname));
pfree(namespace_name);
+ pfree(relation_name);
object.classId = RelationRelationId;
object.objectId = attForm->attrelid;
@@ -347,9 +396,10 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
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));
+ objname = quote_object_name(database_name,
+ namespace_name,
+ NameStr(proForm->proname),
+ NULL);
pfree(namespace_name);
object.classId = ProcedureRelationId;
@@ -359,6 +409,7 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
default:
elog(ERROR, "unexpected catalog id: %u", catalogId);
+ objname = NULL; /* for compiler quiet */
break;
}
@@ -389,7 +440,9 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
else
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("SELinux: could not determine initial security label for %s (type=%d)", objname, objtype)));
+ errmsg("SELinux: could not determine initial security label for %s (type=%d) : %m", objname, objtype)));
+
+ pfree(objname);
}
systable_endscan(sscan);
@@ -449,7 +502,7 @@ sepgsql_restorecon(PG_FUNCTION_ARGS)
if (!sehnd)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("SELinux: failed to initialize labeling handle")));
+ errmsg("SELinux: failed to initialize labeling handle : %m")));
PG_TRY();
{
/*
diff --git a/contrib/sepgsql/launcher b/contrib/sepgsql/launcher
old mode 100644
new mode 100755
diff --git a/contrib/sepgsql/proc.c b/contrib/sepgsql/proc.c
index f1a7b9b..5a0c494 100644
--- a/contrib/sepgsql/proc.c
+++ b/contrib/sepgsql/proc.c
@@ -13,6 +13,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/sysattr.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
@@ -99,7 +100,7 @@ sepgsql_proc_relabel(Oid functionId, const char *seclabel)
char *tcontext;
char *audit_name;
- audit_name = get_func_name(functionId);
+ audit_name = getObjectDescriptionOids(ProcedureRelationId, functionId);
/*
* check db_procedure:{setattr relabelfrom} permission
diff --git a/contrib/sepgsql/relation.c b/contrib/sepgsql/relation.c
index ceaa6b0..ed5e3ad 100644
--- a/contrib/sepgsql/relation.c
+++ b/contrib/sepgsql/relation.c
@@ -14,6 +14,7 @@
#include "access/heapam.h"
#include "access/sysattr.h"
#include "catalog/indexing.h"
+#include "catalog/dependency.h"
#include "catalog/pg_attribute.h"
#include "catalog/pg_class.h"
#include "catalog/pg_namespace.h"
@@ -79,15 +80,18 @@ sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
{
char *scontext = sepgsql_get_client_label();
char *tcontext;
- char audit_name[NAMEDATALEN * 2 + 10];
+ char *audit_name;
+ ObjectAddress object;
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));
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = attnum;
+ audit_name = getObjectDescription(&object);
/*
* check db_column:{setattr relabelfrom} permission
@@ -100,7 +104,6 @@ sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
SEPG_DB_COLUMN__RELABELFROM,
audit_name,
true);
- pfree(tcontext);
/*
* check db_column:{relabelto} permission
@@ -111,6 +114,9 @@ sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
SEPG_DB_PROCEDURE__RELABELTO,
audit_name,
true);
+
+ pfree(tcontext);
+ pfree(audit_name);
}
/*
@@ -239,7 +245,7 @@ sepgsql_relation_relabel(Oid relOid, const char *seclabel)
errmsg("cannot set security labels on relations except "
"for tables, sequences or views")));
- audit_name = get_rel_name(relOid);
+ audit_name = getObjectDescriptionOids(RelationRelationId, relOid);
/*
* check db_xxx:{setattr relabelfrom} permission
@@ -253,7 +259,6 @@ sepgsql_relation_relabel(Oid relOid, const char *seclabel)
SEPG_DB_TABLE__RELABELFROM,
audit_name,
true);
- pfree(tcontext);
/*
* check db_xxx:{relabelto} permission
@@ -264,4 +269,7 @@ sepgsql_relation_relabel(Oid relOid, const char *seclabel)
SEPG_DB_TABLE__RELABELTO,
audit_name,
true);
+
+ pfree(tcontext);
+ pfree(audit_name);
}
diff --git a/contrib/sepgsql/schema.c b/contrib/sepgsql/schema.c
index df33a02..8538d18 100644
--- a/contrib/sepgsql/schema.c
+++ b/contrib/sepgsql/schema.c
@@ -10,6 +10,7 @@
*/
#include "postgres.h"
+#include "catalog/dependency.h"
#include "catalog/pg_namespace.h"
#include "commands/seclabel.h"
#include "utils/lsyscache.h"
@@ -68,7 +69,7 @@ sepgsql_schema_relabel(Oid namespaceId, const char *seclabel)
char *tcontext;
char *audit_name;
- audit_name = get_namespace_name(namespaceId);
+ audit_name = getObjectDescriptionOids(NamespaceRelationId, namespaceId);
/*
* check db_schema:{setattr relabelfrom} permission
diff --git a/contrib/sepgsql/selinux.c b/contrib/sepgsql/selinux.c
index a67bd56..93fe943 100644
--- a/contrib/sepgsql/selinux.c
+++ b/contrib/sepgsql/selinux.c
@@ -396,7 +396,7 @@ sepgsql_audit_log(bool denied,
appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s",
scontext, tcontext, class_name);
if (audit_name)
- appendStringInfo(&buf, " name=%s", audit_name);
+ appendStringInfo(&buf, " name=\"%s\"", audit_name);
ereport(LOG, (errmsg("SELinux: %s", buf.data)));
}
@@ -459,7 +459,7 @@ sepgsql_compute_avd(const char *scontext,
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("SELinux could not compute av_decision: "
- "scontext=%s tcontext=%s tclass=%s",
+ "scontext=%s tcontext=%s tclass=%s : %m",
scontext, tcontext, tclass_name)));
/*
@@ -545,7 +545,7 @@ sepgsql_compute_create(const char *scontext,
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("SELinux could not compute a new context: "
- "scontext=%s tcontext=%s tclass=%s",
+ "scontext=%s tcontext=%s tclass=%s : %m",
scontext, tcontext, tclass_name)));
/*
diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml
index 7b56506..805dfde 100644
--- a/doc/src/sgml/sepgsql.sgml
+++ b/doc/src/sgml/sepgsql.sgml
@@ -75,7 +75,7 @@
<term><productname>libselinux</productname></term>
<listitem>
<para>
- v2.0.80 or later
+ v2.0.93 or later
</para>
<para>
This library provides a set of APIs to communicate with
2011/1/27 KaiGai Kohei <kaigai@ak.jp.nec.com>:
- Error messages become obtaining "%m", when the error was
originated from the libselinux interfaces. It will provides
DBA a hint why interactions with SELinux does not work well.
No space before the ": %m", please.
Also this word looks like a typo: seuciryt
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
(2011/01/27 22:26), Robert Haas wrote:
2011/1/27 KaiGai Kohei<kaigai@ak.jp.nec.com>:
- Error messages become obtaining "%m", when the error was
originated from the libselinux interfaces. It will provides
DBA a hint why interactions with SELinux does not work well.No space before the ": %m", please.
Also this word looks like a typo: seuciryt
The attached patch eliminated spaces before ": %m", and
fixed up the typo.
Thanks,
--
KaiGai Kohei <kaigai@ak.jp.nec.com>
Attachments:
sepgsql-v9.1-fixup.2.patchapplication/octect-stream; name=sepgsql-v9.1-fixup.2.patchDownload
contrib/sepgsql/dml.c | 37 ++++++++++----
contrib/sepgsql/expected/dml.out | 6 +-
contrib/sepgsql/expected/label.out | 4 +-
contrib/sepgsql/expected/misc.out | 2 +-
contrib/sepgsql/hooks.c | 4 +-
contrib/sepgsql/label.c | 89 ++++++++++++++++++++++++++++-------
contrib/sepgsql/proc.c | 3 +-
contrib/sepgsql/relation.c | 20 ++++++--
contrib/sepgsql/schema.c | 3 +-
contrib/sepgsql/selinux.c | 6 +-
contrib/sepgsql/sql/label.sql | 2 +-
doc/src/sgml/sepgsql.sgml | 2 +-
12 files changed, 128 insertions(+), 50 deletions(-)
diff --git a/contrib/sepgsql/dml.c b/contrib/sepgsql/dml.c
index 684b5ee..358a264 100644
--- a/contrib/sepgsql/dml.c
+++ b/contrib/sepgsql/dml.c
@@ -14,6 +14,7 @@
#include "access/tupdesc.h"
#include "catalog/catalog.h"
#include "catalog/heap.h"
+#include "catalog/dependency.h"
#include "catalog/pg_attribute.h"
#include "catalog/pg_class.h"
#include "catalog/pg_inherits_fn.h"
@@ -151,6 +152,7 @@ check_relation_privileges(Oid relOid,
char relkind = get_rel_relkind(relOid);
char *scontext = sepgsql_get_client_label();
char *tcontext;
+ char *audit_name;
Bitmapset *columns;
int index;
bool result = true;
@@ -183,6 +185,7 @@ check_relation_privileges(Oid relOid,
* Check permissions on the relation
*/
tcontext = sepgsql_get_label(RelationRelationId, relOid, 0);
+ audit_name = getObjectDescriptionOids(RelationRelationId, relOid);
switch (relkind)
{
case RELKIND_RELATION:
@@ -190,10 +193,8 @@ check_relation_privileges(Oid relOid,
tcontext,
SEPG_CLASS_DB_TABLE,
required,
- get_rel_name(relOid),
+ audit_name,
abort);
- if (!result)
- return false;
break;
case RELKIND_SEQUENCE:
@@ -204,23 +205,31 @@ check_relation_privileges(Oid relOid,
tcontext,
SEPG_CLASS_DB_SEQUENCE,
SEPG_DB_SEQUENCE__GET_VALUE,
- get_rel_name(relOid),
+ audit_name,
abort);
- return result;
+ break;
case RELKIND_VIEW:
result = sepgsql_check_perms(scontext,
tcontext,
SEPG_CLASS_DB_VIEW,
SEPG_DB_VIEW__EXPAND,
- get_rel_name(relOid),
+ audit_name,
abort);
- return result;
+ break;
default:
/* nothing to be checked */
- return true;
+ break;
}
+ pfree(tcontext);
+ pfree(audit_name);
+
+ /*
+ * Only columns owned by relations shall be checked
+ */
+ if (relkind != RELKIND_RELATION)
+ return true;
/*
* Check permissions on the columns
@@ -233,7 +242,7 @@ check_relation_privileges(Oid relOid,
{
AttrNumber attnum;
uint32 column_perms = 0;
- char audit_name[NAMEDATALEN * 2 + 10];
+ ObjectAddress object;
if (bms_is_member(index, selected))
column_perms |= SEPG_DB_COLUMN__SELECT;
@@ -250,8 +259,11 @@ check_relation_privileges(Oid relOid,
/* 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));
+
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = attnum;
+ audit_name = getObjectDescription(&object);
result = sepgsql_check_perms(scontext,
tcontext,
@@ -259,6 +271,9 @@ check_relation_privileges(Oid relOid,
column_perms,
audit_name,
abort);
+ pfree(tcontext);
+ pfree(audit_name);
+
if (!result)
return result;
}
diff --git a/contrib/sepgsql/expected/dml.out b/contrib/sepgsql/expected/dml.out
index 5625ebc..c1bbbba 100644
--- a/contrib/sepgsql/expected/dml.out
+++ b/contrib/sepgsql/expected/dml.out
@@ -42,15 +42,15 @@ SELECT objtype, objname, label FROM pg_seclabels
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
+ column | t5.f | system_u:object_r:sepgsql_ro_table_t:s0
+ column | t5.g | system_u:object_r:sepgsql_secret_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
+ERROR: SELinux: hardwired security policy violation
--
-- Simple DML statements
--
diff --git a/contrib/sepgsql/expected/label.out b/contrib/sepgsql/expected/label.out
index 0f0615c..daf8d08 100644
--- a/contrib/sepgsql/expected/label.out
+++ b/contrib/sepgsql/expected/label.out
@@ -56,8 +56,8 @@ SELECT sepgsql_getcon(); -- confirm client privilege
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"
+ IS 'invalid security context'; -- be failed
+ERROR: SELinux: invalid security label: "invalid security 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):
diff --git a/contrib/sepgsql/expected/misc.out b/contrib/sepgsql/expected/misc.out
index 5242333..329852c 100644
--- a/contrib/sepgsql/expected/misc.out
+++ b/contrib/sepgsql/expected/misc.out
@@ -2,4 +2,4 @@
-- Regression Test for Misc Permission Checks
--
LOAD '$libdir/sepgsql'; -- failed
-ERROR: SELinux: LOAD is not allowed anyway.
+ERROR: SELinux: LOAD is not permitted
diff --git a/contrib/sepgsql/hooks.c b/contrib/sepgsql/hooks.c
index bc7ce51..83a505e 100644
--- a/contrib/sepgsql/hooks.c
+++ b/contrib/sepgsql/hooks.c
@@ -91,7 +91,7 @@ sepgsql_client_auth(Port *port, int status)
if (getpeercon_raw(port->sock, &context) < 0)
ereport(FATAL,
(errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("SELinux: unable to get peer label")));
+ errmsg("SELinux: unable to get peer label: %m")));
sepgsql_set_client_label(context);
@@ -414,7 +414,7 @@ _PG_init(void)
if (getcon_raw(&context) < 0)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("SELinux: failed to get server security label")));
+ errmsg("SELinux: failed to get server security label: %m")));
sepgsql_set_client_label(context);
/* Security label provider hook */
diff --git a/contrib/sepgsql/label.c b/contrib/sepgsql/label.c
index ad568f8..828512a 100644
--- a/contrib/sepgsql/label.c
+++ b/contrib/sepgsql/label.c
@@ -81,7 +81,7 @@ sepgsql_get_label(Oid classId, Oid objectId, int32 subId)
if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("SELinux: failed to get initial security label")));
+ errmsg("SELinux: failed to get initial security label: %m")));
PG_TRY();
{
label = pstrdup(unlabeled);
@@ -184,7 +184,7 @@ sepgsql_mcstrans_in(PG_FUNCTION_ARGS)
&raw_label) < 0)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("SELinux: could not translate security label")));
+ errmsg("SELinux: could not translate security label: %m")));
PG_TRY();
{
@@ -224,7 +224,7 @@ sepgsql_mcstrans_out(PG_FUNCTION_ARGS)
&qual_label) < 0)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("SELinux: could not translate security label")));
+ errmsg("SELinux: could not translate security label: %m")));
PG_TRY();
{
@@ -242,6 +242,51 @@ sepgsql_mcstrans_out(PG_FUNCTION_ARGS)
}
/*
+ * quote_object_names
+ *
+ * It tries to quote the supplied identifiers
+ */
+static char *
+quote_object_name(const char *src1, const char *src2,
+ const char *src3, const char *src4)
+{
+ StringInfoData result;
+ const char *temp;
+
+ initStringInfo(&result);
+
+ if (src1)
+ {
+ temp = quote_identifier(src1);
+ appendStringInfo(&result, "%s", temp);
+ if (src1 != temp)
+ pfree((void *)temp);
+ }
+ if (src2)
+ {
+ temp = quote_identifier(src2);
+ appendStringInfo(&result, ".%s", temp);
+ if (src2 != temp)
+ pfree((void *)temp);
+ }
+ if (src3)
+ {
+ temp = quote_identifier(src3);
+ appendStringInfo(&result, ".%s", temp);
+ if (src3 != temp)
+ pfree((void *)temp);
+ }
+ if (src4)
+ {
+ temp = quote_identifier(src4);
+ appendStringInfo(&result, ".%s", temp);
+ if (src4 != temp)
+ pfree((void *)temp);
+ }
+ return result.data;
+}
+
+/*
* exec_object_restorecon
*
* This routine is a helper called by sepgsql_restorecon; it set up
@@ -273,7 +318,7 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
Form_pg_class relForm;
Form_pg_attribute attForm;
Form_pg_proc proForm;
- char objname[NAMEDATALEN * 4 + 10];
+ char *objname;
int objtype = 1234;
ObjectAddress object;
security_context_t context;
@@ -288,8 +333,10 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
nspForm = (Form_pg_namespace) GETSTRUCT(tuple);
objtype = SELABEL_DB_SCHEMA;
- snprintf(objname, sizeof(objname), "%s.%s",
- database_name, NameStr(nspForm->nspname));
+
+ objname = quote_object_name(database_name,
+ NameStr(nspForm->nspname),
+ NULL, NULL);
object.classId = NamespaceRelationId;
object.objectId = HeapTupleGetOid(tuple);
@@ -309,9 +356,10 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
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));
+ objname = quote_object_name(database_name,
+ namespace_name,
+ NameStr(relForm->relname),
+ NULL);
pfree(namespace_name);
object.classId = RelationRelationId;
@@ -330,11 +378,12 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
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);
+ objname = quote_object_name(database_name,
+ namespace_name,
+ relation_name,
+ NameStr(attForm->attname));
pfree(namespace_name);
+ pfree(relation_name);
object.classId = RelationRelationId;
object.objectId = attForm->attrelid;
@@ -347,9 +396,10 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
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));
+ objname = quote_object_name(database_name,
+ namespace_name,
+ NameStr(proForm->proname),
+ NULL);
pfree(namespace_name);
object.classId = ProcedureRelationId;
@@ -359,6 +409,7 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
default:
elog(ERROR, "unexpected catalog id: %u", catalogId);
+ objname = NULL; /* for compiler quiet */
break;
}
@@ -389,7 +440,9 @@ exec_object_restorecon(struct selabel_handle *sehnd, Oid catalogId)
else
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("SELinux: could not determine initial security label for %s (type=%d)", objname, objtype)));
+ errmsg("SELinux: could not determine initial security label for %s (type=%d): %m", objname, objtype)));
+
+ pfree(objname);
}
systable_endscan(sscan);
@@ -449,7 +502,7 @@ sepgsql_restorecon(PG_FUNCTION_ARGS)
if (!sehnd)
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("SELinux: failed to initialize labeling handle")));
+ errmsg("SELinux: failed to initialize labeling handle: %m")));
PG_TRY();
{
/*
diff --git a/contrib/sepgsql/launcher b/contrib/sepgsql/launcher
old mode 100644
new mode 100755
diff --git a/contrib/sepgsql/proc.c b/contrib/sepgsql/proc.c
index f1a7b9b..5a0c494 100644
--- a/contrib/sepgsql/proc.c
+++ b/contrib/sepgsql/proc.c
@@ -13,6 +13,7 @@
#include "access/genam.h"
#include "access/heapam.h"
#include "access/sysattr.h"
+#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
@@ -99,7 +100,7 @@ sepgsql_proc_relabel(Oid functionId, const char *seclabel)
char *tcontext;
char *audit_name;
- audit_name = get_func_name(functionId);
+ audit_name = getObjectDescriptionOids(ProcedureRelationId, functionId);
/*
* check db_procedure:{setattr relabelfrom} permission
diff --git a/contrib/sepgsql/relation.c b/contrib/sepgsql/relation.c
index ceaa6b0..ed5e3ad 100644
--- a/contrib/sepgsql/relation.c
+++ b/contrib/sepgsql/relation.c
@@ -14,6 +14,7 @@
#include "access/heapam.h"
#include "access/sysattr.h"
#include "catalog/indexing.h"
+#include "catalog/dependency.h"
#include "catalog/pg_attribute.h"
#include "catalog/pg_class.h"
#include "catalog/pg_namespace.h"
@@ -79,15 +80,18 @@ sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
{
char *scontext = sepgsql_get_client_label();
char *tcontext;
- char audit_name[NAMEDATALEN * 2 + 10];
+ char *audit_name;
+ ObjectAddress object;
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));
+ object.classId = RelationRelationId;
+ object.objectId = relOid;
+ object.objectSubId = attnum;
+ audit_name = getObjectDescription(&object);
/*
* check db_column:{setattr relabelfrom} permission
@@ -100,7 +104,6 @@ sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
SEPG_DB_COLUMN__RELABELFROM,
audit_name,
true);
- pfree(tcontext);
/*
* check db_column:{relabelto} permission
@@ -111,6 +114,9 @@ sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum,
SEPG_DB_PROCEDURE__RELABELTO,
audit_name,
true);
+
+ pfree(tcontext);
+ pfree(audit_name);
}
/*
@@ -239,7 +245,7 @@ sepgsql_relation_relabel(Oid relOid, const char *seclabel)
errmsg("cannot set security labels on relations except "
"for tables, sequences or views")));
- audit_name = get_rel_name(relOid);
+ audit_name = getObjectDescriptionOids(RelationRelationId, relOid);
/*
* check db_xxx:{setattr relabelfrom} permission
@@ -253,7 +259,6 @@ sepgsql_relation_relabel(Oid relOid, const char *seclabel)
SEPG_DB_TABLE__RELABELFROM,
audit_name,
true);
- pfree(tcontext);
/*
* check db_xxx:{relabelto} permission
@@ -264,4 +269,7 @@ sepgsql_relation_relabel(Oid relOid, const char *seclabel)
SEPG_DB_TABLE__RELABELTO,
audit_name,
true);
+
+ pfree(tcontext);
+ pfree(audit_name);
}
diff --git a/contrib/sepgsql/schema.c b/contrib/sepgsql/schema.c
index df33a02..8538d18 100644
--- a/contrib/sepgsql/schema.c
+++ b/contrib/sepgsql/schema.c
@@ -10,6 +10,7 @@
*/
#include "postgres.h"
+#include "catalog/dependency.h"
#include "catalog/pg_namespace.h"
#include "commands/seclabel.h"
#include "utils/lsyscache.h"
@@ -68,7 +69,7 @@ sepgsql_schema_relabel(Oid namespaceId, const char *seclabel)
char *tcontext;
char *audit_name;
- audit_name = get_namespace_name(namespaceId);
+ audit_name = getObjectDescriptionOids(NamespaceRelationId, namespaceId);
/*
* check db_schema:{setattr relabelfrom} permission
diff --git a/contrib/sepgsql/selinux.c b/contrib/sepgsql/selinux.c
index a67bd56..03ba25c 100644
--- a/contrib/sepgsql/selinux.c
+++ b/contrib/sepgsql/selinux.c
@@ -396,7 +396,7 @@ sepgsql_audit_log(bool denied,
appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s",
scontext, tcontext, class_name);
if (audit_name)
- appendStringInfo(&buf, " name=%s", audit_name);
+ appendStringInfo(&buf, " name=\"%s\"", audit_name);
ereport(LOG, (errmsg("SELinux: %s", buf.data)));
}
@@ -459,7 +459,7 @@ sepgsql_compute_avd(const char *scontext,
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("SELinux could not compute av_decision: "
- "scontext=%s tcontext=%s tclass=%s",
+ "scontext=%s tcontext=%s tclass=%s: %m",
scontext, tcontext, tclass_name)));
/*
@@ -545,7 +545,7 @@ sepgsql_compute_create(const char *scontext,
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("SELinux could not compute a new context: "
- "scontext=%s tcontext=%s tclass=%s",
+ "scontext=%s tcontext=%s tclass=%s: %m",
scontext, tcontext, tclass_name)));
/*
diff --git a/contrib/sepgsql/sql/label.sql b/contrib/sepgsql/sql/label.sql
index 3162494..1100fcb 100644
--- a/contrib/sepgsql/sql/label.sql
+++ b/contrib/sepgsql/sql/label.sql
@@ -46,7 +46,7 @@ SELECT objtype, objname, label FROM pg_seclabels
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
+ IS 'invalid security 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
diff --git a/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml
index 7b56506..805dfde 100644
--- a/doc/src/sgml/sepgsql.sgml
+++ b/doc/src/sgml/sepgsql.sgml
@@ -75,7 +75,7 @@
<term><productname>libselinux</productname></term>
<listitem>
<para>
- v2.0.80 or later
+ v2.0.93 or later
</para>
<para>
This library provides a set of APIs to communicate with
2011/1/27 KaiGai Kohei <kaigai@ak.jp.nec.com>:
(2011/01/27 22:26), Robert Haas wrote:
2011/1/27 KaiGai Kohei<kaigai@ak.jp.nec.com>:
- Error messages become obtaining "%m", when the error was
originated from the libselinux interfaces. It will provides
DBA a hint why interactions with SELinux does not work well.No space before the ": %m", please.
Also this word looks like a typo: seuciryt
The attached patch eliminated spaces before ": %m", and
fixed up the typo.
Committed.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Wed, Feb 2, 2011 at 11:43 PM, Robert Haas <robertmhaas@gmail.com> wrote:
Committed.
I did some more polishing of the documentation as well.
It would be good to have some buildfarm coverage of this code. Can we
find anyone brave enough to set up a buildfarm critter using
--with-selinux?
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Sorry for the late responding, because of my relocation.
It would be good to have some buildfarm coverage of this code. Can we
find anyone brave enough to set up a buildfarm critter using
--with-selinux?
Although I don't have an account on the buildfarm, I'll set up an environment
for daily build with --with-selinux.
Because it needs kernel with selinux, libselinux and security policy, I think
it is good idea to set up a virtual machine for the purpose.
Thanks,
Show quoted text
-----Original Message-----
From: Robert Haas [mailto:robertmhaas@gmail.com]
Sent: 03 February 2011 05:27
To: KaiGai Kohei
Cc: KaiGai Kohei; PgHacker
Subject: Re: [HACKERS] sepgsql contrib moduleOn Wed, Feb 2, 2011 at 11:43 PM, Robert Haas <robertmhaas@gmail.com> wrote:
Committed.
I did some more polishing of the documentation as well.
It would be good to have some buildfarm coverage of this code. Can we
find anyone brave enough to set up a buildfarm critter using
--with-selinux?--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
KaiGai,
* Kohei Kaigai (Kohei.Kaigai@EU.NEC.COM) wrote:
It would be good to have some buildfarm coverage of this code. Can we
find anyone brave enough to set up a buildfarm critter using
--with-selinux?Although I don't have an account on the buildfarm, I'll set up an environment
for daily build with --with-selinux.
Because it needs kernel with selinux, libselinux and security policy, I think
it is good idea to set up a virtual machine for the purpose.
We really need to get a buildfarm which is building with this. To that
end, would you mind providing directions so someone else could set up a
buildfarm member to test it..?
Thanks,
Stephen
We really need to get a buildfarm which is building with this. To that
end, would you mind providing directions so someone else could set up a
buildfarm member to test it..?
It seems to me not difficult to describe a direction to build, install and
run regression test.
Do we have any Fedora 14 environment in the buildfarm?
It is the most suitable distribution to set up sepgsql module, because the
default installation already has selinux configurations.
Thanks,
Show quoted text
-----Original Message-----
From: Stephen Frost [mailto:sfrost@snowman.net]
Sent: 14 February 2011 16:29
To: Kohei Kaigai
Cc: Robert Haas; KaiGai Kohei; PgHacker
Subject: Re: [HACKERS] sepgsql contrib moduleKaiGai,
* Kohei Kaigai (Kohei.Kaigai@EU.NEC.COM) wrote:
It would be good to have some buildfarm coverage of this code. Can
we find anyone brave enough to set up a buildfarm critter using
--with-selinux?Although I don't have an account on the buildfarm, I'll set up an
environment for daily build with --with-selinux.
Because it needs kernel with selinux, libselinux and security policy,
I think it is good idea to set up a virtual machine for the purpose.We really need to get a buildfarm which is building with this. To that
end, would you mind providing directions so someone else could set up a
buildfarm member to test it..?Thanks,
Stephen
Excerpts from Kohei Kaigai's message of lun feb 14 13:47:58 -0300 2011:
We really need to get a buildfarm which is building with this. To that
end, would you mind providing directions so someone else could set up a
buildfarm member to test it..?It seems to me not difficult to describe a direction to build, install and
run regression test.
Do we have any Fedora 14 environment in the buildfarm?
It is the most suitable distribution to set up sepgsql module, because the
default installation already has selinux configurations.
It would be good to fix the Makefile problem that prevents the code to
build elsewhere. There's another thread about a hardcoded path to a
system Makefile in contrib/sepgsql that causes that module to FTBFS. Is
there a way to fix that? I had a look some days ago and it didn't seem
like there was a way to query the system for the proper path to use.
--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
On 02/14/2011 11:47 AM, Kohei Kaigai wrote:
We really need to get a buildfarm which is building with this. To that
end, would you mind providing directions so someone else could set up a
buildfarm member to test it..?It seems to me not difficult to describe a direction to build, install and
run regression test.
Do we have any Fedora 14 environment in the buildfarm?
It is the most suitable distribution to set up sepgsql module, because the
default installation already has selinux configurations.
Thew makefile still has this bogosity:
sepgsql-regtest.pp: sepgsql-regtest.te
$(MAKE) -f $(DESTDIR)/usr/share/selinux/devel/Makefile $@
We need to fix that up before we even think of trying to get buildfarm
coverage. The presence and location of this makefile should be
determined at configure time, ISTM.
cheers
andrew
Andrew Dunstan <andrew@dunslane.net> writes:
Thew makefile still has this bogosity:
sepgsql-regtest.pp: sepgsql-regtest.te
$(MAKE) -f $(DESTDIR)/usr/share/selinux/devel/Makefile $@
We need to fix that up before we even think of trying to get buildfarm
coverage. The presence and location of this makefile should be
determined at configure time, ISTM.
I'd suggest just getting rid of the $(DESTDIR), which is flat out wrong,
and leaving it as-is otherwise. The portability level of this code is
somewhere at the bad-joke level anyway; if you're trying to get it to
run anywhere but a recent Fedora/RHEL system, I really doubt that path
is the first or biggest problem you'll hit.
regards, tom lane
On 02/14/2011 04:21 PM, Tom Lane wrote:
Andrew Dunstan<andrew@dunslane.net> writes:
Thew makefile still has this bogosity:
sepgsql-regtest.pp: sepgsql-regtest.te
$(MAKE) -f $(DESTDIR)/usr/share/selinux/devel/Makefile $@
We need to fix that up before we even think of trying to get buildfarm
coverage. The presence and location of this makefile should be
determined at configure time, ISTM.I'd suggest just getting rid of the $(DESTDIR), which is flat out wrong,
and leaving it as-is otherwise. The portability level of this code is
somewhere at the bad-joke level anyway; if you're trying to get it to
run anywhere but a recent Fedora/RHEL system, I really doubt that path
is the first or biggest problem you'll hit.
Yeah. The next thing I hit was this:
[andrew@aurelia sepgsql]$ make -f /usr/share/selinux/devel/Makefile
sepgsql-regtest.pp
cat: /selinux/mls: No such file or directory
make: *** No rule to make target `sepgsql-regtest.pp'. Stop.
[andrew@aurelia sepgsql]$
That's not very nice.
cheers
andrew
Andrew Dunstan <andrew@dunslane.net> writes:
Yeah. The next thing I hit was this:
[andrew@aurelia sepgsql]$ make -f /usr/share/selinux/devel/Makefile
sepgsql-regtest.pp
cat: /selinux/mls: No such file or directory
make: *** No rule to make target `sepgsql-regtest.pp'. Stop.
[andrew@aurelia sepgsql]$
Hmph. A build with --with-selinux goes through for me on a
pretty-vanilla Fedora 13 installation (at least the build and install
steps, I dunno how to test it).
It looks to me like /selinux/mls is some weird phony-filesystem file,
because "cat" prints one character (a "1") while "ls" claims the file is
of zero length. So it's probably something consed up by the kernel,
like /proc/. Do you have selinux enabled on your machine?
(BTW, testing what seems to be a kernel-configuration-reporting flag at
build time strikes me as pretty awful design.)
regards, tom lane
On 02/14/2011 08:36 PM, Tom Lane wrote:
Andrew Dunstan<andrew@dunslane.net> writes:
Yeah. The next thing I hit was this:
[andrew@aurelia sepgsql]$ make -f /usr/share/selinux/devel/Makefile
sepgsql-regtest.pp
cat: /selinux/mls: No such file or directory
make: *** No rule to make target `sepgsql-regtest.pp'. Stop.
[andrew@aurelia sepgsql]$Hmph. A build with --with-selinux goes through for me on a
pretty-vanilla Fedora 13 installation (at least the build and install
steps, I dunno how to test it).It looks to me like /selinux/mls is some weird phony-filesystem file,
because "cat" prints one character (a "1") while "ls" claims the file is
of zero length. So it's probably something consed up by the kernel,
like /proc/. Do you have selinux enabled on your machine?
Np, but that really shouldn't be a build requirement, ISTM, even if it
is a test requirement.
(BTW, testing what seems to be a kernel-configuration-reporting flag at
build time strikes me as pretty awful design.)
Yeah, I agree.
cheers
andrew
Andrew Dunstan <andrew@dunslane.net> writes:
On 02/14/2011 08:36 PM, Tom Lane wrote:
It looks to me like /selinux/mls is some weird phony-filesystem file,
because "cat" prints one character (a "1") while "ls" claims the file is
of zero length. So it's probably something consed up by the kernel,
like /proc/. Do you have selinux enabled on your machine?
Np, but that really shouldn't be a build requirement, ISTM, even if it
is a test requirement.
[ A few reboots later... ] Yeah, I've confirmed that /selinux/mls isn't
there at all when SELinux is disabled. When it is there, it reflects
the setting of SELINUXTYPE ("targeted" or "mls"). So that explains what
/usr/share/selinux/devel/Makefile is doing, but it doesn't make it a
good idea.
(BTW, testing what seems to be a kernel-configuration-reporting flag at
build time strikes me as pretty awful design.)
Yeah, I agree.
Yup, this is just broken by design.
It looks like /usr/share/selinux/devel/Makefile basically just sets NAME
and TYPE and then calls /usr/share/selinux/devel/include/Makefile, so we
could avoid the dependence on the build machine's current state if we
did that for ourselves. Of course that just begs the question of what
we should set these variables *to*. Since the file being built is only
used for regression testing, it wouldn't be unreasonable to pick some
values, but it's not clear to me whether things would go blooey if the
eventual test machine had different settings.
On the whole, I don't think that sepgsql-regtest.pp should be built or
installed at all during the build phase. It ought to be generated
during regression test startup, instead.
regards, tom lane
On Mon, Feb 14, 2011 at 9:55 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
On the whole, I don't think that sepgsql-regtest.pp should be built or
installed at all during the build phase. It ought to be generated
during regression test startup, instead.
You have to manually install and enable it before you can run the
regression tests. This is documented.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 02/15/2011 10:34 AM, Robert Haas wrote:
On Mon, Feb 14, 2011 at 9:55 PM, Tom Lane<tgl@sss.pgh.pa.us> wrote:
On the whole, I don't think that sepgsql-regtest.pp should be built or
installed at all during the build phase. It ought to be generated
during regression test startup, instead.You have to manually install and enable it before you can run the
regression tests. This is documented.
That's not the point. Right now you can't even run just a postgresql
build on a machine that doesn't have selinux enabled, let alone run the
regression tests. And if we construct the regression gadget at
postgresql build time, who is to say it has the right settings for the
run time environment, given that the build is apparently dependent on a
runtime kernel setting?
cheers
andrew
On Tue, Feb 15, 2011 at 10:50 AM, Andrew Dunstan <andrew@dunslane.net> wrote:
On 02/15/2011 10:34 AM, Robert Haas wrote:
On Mon, Feb 14, 2011 at 9:55 PM, Tom Lane<tgl@sss.pgh.pa.us> wrote:
On the whole, I don't think that sepgsql-regtest.pp should be built or
installed at all during the build phase. It ought to be generated
during regression test startup, instead.You have to manually install and enable it before you can run the
regression tests. This is documented.That's not the point. Right now you can't even run just a postgresql build
on a machine that doesn't have selinux enabled, let alone run the regression
tests. And if we construct the regression gadget at postgresql build time,
who is to say it has the right settings for the run time environment, given
that the build is apparently dependent on a runtime kernel setting?
Those are good points. My point was just that you can't actually
build that file at the time you RUN the regression tests, because you
have to build it first, then install it, then run the regression
tests. It could be a separate target, like 'make policy', but I don't
think it works to make it part of 'make installcheck'.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
Those are good points. My point was just that you can't actually
build that file at the time you RUN the regression tests, because you
have to build it first, then install it, then run the regression
tests. It could be a separate target, like 'make policy', but I don't
think it works to make it part of 'make installcheck'.
So? Once you admit that you can do that, it's a matter of a couple more
lines to make the installcheck target depend on the policy target iff
selinux was enabled.
regards, tom lane
On Tue, Feb 15, 2011 at 11:01 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
Those are good points. My point was just that you can't actually
build that file at the time you RUN the regression tests, because you
have to build it first, then install it, then run the regression
tests. It could be a separate target, like 'make policy', but I don't
think it works to make it part of 'make installcheck'.So? Once you admit that you can do that, it's a matter of a couple more
lines to make the installcheck target depend on the policy target iff
selinux was enabled.
Sure, you could do that, but I don't see what problem it would fix.
You'd still have to build and manually install the policy before you
could run make installcheck. And once you've done that, you don't
need to rebuild it every future time you run make installcheck.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Robert Haas <robertmhaas@gmail.com> writes:
On Tue, Feb 15, 2011 at 11:01 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
Those are good points. �My point was just that you can't actually
build that file at the time you RUN the regression tests, because you
have to build it first, then install it, then run the regression
tests. �It could be a separate target, like 'make policy', but I don't
think it works to make it part of 'make installcheck'.
So? �Once you admit that you can do that, it's a matter of a couple more
lines to make the installcheck target depend on the policy target iff
selinux was enabled.
Sure, you could do that, but I don't see what problem it would fix.
You'd still have to build and manually install the policy before you
could run make installcheck. And once you've done that, you don't
need to rebuild it every future time you run make installcheck.
Oh, I see: you're pointing out the root-only "semodule" step that has to
be done in between there. Good point. But the current arrangement is
still a mistake: the required contents of sepgsql-regtest.pp depend on
the configuration of the test system, which can't be known at build
time.
So what we should do is offer a "make policy" target and alter the test
instructions to say you should do that and then run semodule. Or maybe
just put the whole "make -f /usr/share/selinux/devel/Makefile" dance
into the instructions --- it doesn't look to me like our makefile
infrastructure really has anything useful to add to that.
regards, tom lane
On Tue, Feb 15, 2011 at 11:41 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Tue, Feb 15, 2011 at 11:01 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
Those are good points. My point was just that you can't actually
build that file at the time you RUN the regression tests, because you
have to build it first, then install it, then run the regression
tests. It could be a separate target, like 'make policy', but I don't
think it works to make it part of 'make installcheck'.So? Once you admit that you can do that, it's a matter of a couple more
lines to make the installcheck target depend on the policy target iff
selinux was enabled.Sure, you could do that, but I don't see what problem it would fix.
You'd still have to build and manually install the policy before you
could run make installcheck. And once you've done that, you don't
need to rebuild it every future time you run make installcheck.Oh, I see: you're pointing out the root-only "semodule" step that has to
be done in between there. Good point. But the current arrangement is
still a mistake: the required contents of sepgsql-regtest.pp depend on
the configuration of the test system, which can't be known at build
time.So what we should do is offer a "make policy" target and alter the test
instructions to say you should do that and then run semodule. Or maybe
just put the whole "make -f /usr/share/selinux/devel/Makefile" dance
into the instructions --- it doesn't look to me like our makefile
infrastructure really has anything useful to add to that.
Yeah, agreed.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
-----Original Message-----
From: Robert Haas [mailto:robertmhaas@gmail.com]
Sent: 15 February 2011 16:52
To: Tom Lane
Cc: Andrew Dunstan; Kohei Kaigai; Stephen Frost; KaiGai Kohei; PgHacker
Subject: Re: [HACKERS] sepgsql contrib moduleOn Tue, Feb 15, 2011 at 11:41 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Tue, Feb 15, 2011 at 11:01 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
Those are good points. My point was just that you can't actually
build that file at the time you RUN the regression tests, because you
have to build it first, then install it, then run the regression
tests. It could be a separate target, like 'make policy', but I don't
think it works to make it part of 'make installcheck'.So? Once you admit that you can do that, it's a matter of a couple
more
lines to make the installcheck target depend on the policy target iff
selinux was enabled.Sure, you could do that, but I don't see what problem it would fix.
You'd still have to build and manually install the policy before you
could run make installcheck. And once you've done that, you don't
need to rebuild it every future time you run make installcheck.Oh, I see: you're pointing out the root-only "semodule" step that has
to
be done in between there. Good point. But the current arrangement is
still a mistake: the required contents of sepgsql-regtest.pp depend on
the configuration of the test system, which can't be known at build
time.So what we should do is offer a "make policy" target and alter the test
instructions to say you should do that and then run semodule. Or maybe
just put the whole "make -f /usr/share/selinux/devel/Makefile" dance
into the instructions --- it doesn't look to me like our makefile
infrastructure really has anything useful to add to that.Yeah, agreed.
I also agree with this direction. The policy type depends on individual installations,
it is not easy to assume on build time.
Please wait for a small patch to remove this rule from Makefile and update documentation.
As a side note, we can have a build option that does not require selinux enabled.
The reason why Makefile of selinux tries to /selinux/mls is that we don't specify
MLS=1 or MLS=0 explicitly.
IIRC, the specfile of RHEL/Fedora gives all the Makefile parameters explicitly, thus,
selinux does not need to be enabled on the build server.
However, it is not a solution in this case. It is not easy to estimate the required
policy type and existence of MLS support on build time.
Thanks,
--
NEC Europe Ltd, Global Competence Center
KaiGai Kohei <kohei.kaigai@eu.nec.com>
The attached patch removes rules to build a policy package for regression
test and modifies documentation part to introduce steps to run the test.
Thanks,
--
NEC Europe Ltd, Global Competence Center
KaiGai Kohei <kohei.kaigai@eu.nec.com>
Show quoted text
-----Original Message-----
From: Kohei Kaigai
Sent: 15 February 2011 18:27
To: 'Robert Haas'; Tom Lane
Cc: Andrew Dunstan; Stephen Frost; KaiGai Kohei; PgHacker
Subject: RE: [HACKERS] sepgsql contrib module-----Original Message-----
From: Robert Haas [mailto:robertmhaas@gmail.com]
Sent: 15 February 2011 16:52
To: Tom Lane
Cc: Andrew Dunstan; Kohei Kaigai; Stephen Frost; KaiGai Kohei; PgHacker
Subject: Re: [HACKERS] sepgsql contrib moduleOn Tue, Feb 15, 2011 at 11:41 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
On Tue, Feb 15, 2011 at 11:01 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Robert Haas <robertmhaas@gmail.com> writes:
Those are good points. My point was just that you can't actually
build that file at the time you RUN the regression tests, becauseyou
have to build it first, then install it, then run the regression
tests. It could be a separate target, like 'make policy', but Idon't
think it works to make it part of 'make installcheck'.
So? Once you admit that you can do that, it's a matter of a couple
more
lines to make the installcheck target depend on the policy target
iff
selinux was enabled.
Sure, you could do that, but I don't see what problem it would fix.
You'd still have to build and manually install the policy before you
could run make installcheck. And once you've done that, you don't
need to rebuild it every future time you run make installcheck.Oh, I see: you're pointing out the root-only "semodule" step that has
to
be done in between there. Good point. But the current arrangement
is
still a mistake: the required contents of sepgsql-regtest.pp depend
on
the configuration of the test system, which can't be known at build
time.So what we should do is offer a "make policy" target and alter the test
instructions to say you should do that and then run semodule. Or maybe
just put the whole "make -f /usr/share/selinux/devel/Makefile" dance
into the instructions --- it doesn't look to me like our makefile
infrastructure really has anything useful to add to that.Yeah, agreed.
I also agree with this direction. The policy type depends on individual
installations,
it is not easy to assume on build time.
Please wait for a small patch to remove this rule from Makefile and update
documentation.As a side note, we can have a build option that does not require selinux
enabled.
The reason why Makefile of selinux tries to /selinux/mls is that we don't
specify
MLS=1 or MLS=0 explicitly.
IIRC, the specfile of RHEL/Fedora gives all the Makefile parameters
explicitly, thus,
selinux does not need to be enabled on the build server.
However, it is not a solution in this case. It is not easy to estimate the
required
policy type and existence of MLS support on build time.Thanks,
--
NEC Europe Ltd, Global Competence Center
KaiGai Kohei <kohei.kaigai@eu.nec.com>
Attachments:
sepgsql-policy.1.patchapplication/octet-stream; name=sepgsql-policy.1.patchDownload
diff --git a/contrib/sepgsql/Makefile b/contrib/sepgsql/Makefile
index 37a6dce..bc995dd 100644
--- a/contrib/sepgsql/Makefile
+++ b/contrib/sepgsql/Makefile
@@ -3,7 +3,7 @@
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
+DATA_built = sepgsql.sql
REGRESS = label dml misc
EXTRA_CLEAN = -r tmp *.pp sepgsql-regtest.if sepgsql-regtest.fc
@@ -20,6 +20,3 @@ 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/doc/src/sgml/sepgsql.sgml b/doc/src/sgml/sepgsql.sgml
index 9ef2bf9..463924f 100644
--- a/doc/src/sgml/sepgsql.sgml
+++ b/doc/src/sgml/sepgsql.sgml
@@ -124,10 +124,14 @@ $ for DBNAME in template0 template1 postgres; do
</para>
<para>
- First, install the policy package for the regression test.
+ First, build and install the policy package for the regression test.
The <filename>sepgsql-regtest.pp</> is a special purpose policy package
which provides a set of rules to be allowed during the regression tests.
- You need to install this policy package using the <command>semodule</>
+ You need to build this policy package from the policy source file
+ (<filename>sepgsql-regtest.te</>), using the makefile for additional
+ policy package. Please adjust its path in the below example depending
+ on your installation.
+ Then, you can install this policy package using the <command>semodule</>
command, which links supplied policy packages and loads them
into the kernel space. If this packages is correctly installed,
<literal><command>semodule</> -l</> should list sepgsql-regtest as an
@@ -135,8 +139,9 @@ $ for DBNAME in template0 template1 postgres; do
</para>
<screen>
+$ make -C ./contrib/sepgsql -f /usr/share/selinux/devel/Makefile
$ su
-# semodule -u /usr/local/pgsql/share/contrib/sepgsql-regtest.pp
+# semodule -u ./contrib/sepgsql/sepgsql-regtest.pp
# semodule -l
:
sepgsql-regtest 1.03
On Thu, Feb 17, 2011 at 3:56 AM, Kohei Kaigai <Kohei.Kaigai@eu.nec.com> wrote:
The attached patch removes rules to build a policy package for regression
test and modifies documentation part to introduce steps to run the test.
Committed. Incidentally, on my Ubuntu system:
$ find /usr/share/selinux -name '*ake*'
/usr/share/selinux/default/include/Makefile
/usr/share/selinux/ubuntu/include/Makefile
/usr/share/selinux/mls/include/Makefile
Not sure which of these would be the right one to use.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Sorry so much!
I thought I replied to the question already, but not yet.
$ find /usr/share/selinux -name '*ake*'
/usr/share/selinux/default/include/Makefile
/usr/share/selinux/ubuntu/include/Makefile
/usr/share/selinux/mls/include/MakefileNot sure which of these would be the right one to use.
The 4th level entry shall be replaced by policy type.
So, if "ubuntu" policy type is available on the system, the Makefile
we shall use is /usr/share/selinux/ubuntu/include/Makefile .
^^^^^^
We can confirm the current available policy type from /etc/selinux/config
or using sestatus command.
[kaigai@vmlinux tmp]$ sestatus
SELinux status: enabled
SELinuxfs mount: /selinux
Current mode: enforcing
Mode from config file: enforcing
Policy version: 24
Policy from config file: targeted
^^^^^^^^ It is the policy type.
In this case, the current available policy type is "targeted".
BTW, it seems to me the base version of selinux-policy-* package in Ubuntu
is forked from an older snapshot (20091117), so it does not have enough rules
to run SE-PostgreSQL.
Right now, Fedora 13/14 is the easiest way.
Thanks,
--
NEC Europe Ltd, Global Competence Center
KaiGai Kohei <kohei.kaigai@eu.nec.com>
Show quoted text
-----Original Message-----
From: Robert Haas [mailto:robertmhaas@gmail.com]
Sent: 17. Februar 2011 11:42
To: Kohei Kaigai
Cc: Tom Lane; Andrew Dunstan; Stephen Frost; KaiGai Kohei; PgHacker
Subject: Re: [HACKERS] sepgsql contrib moduleOn Thu, Feb 17, 2011 at 3:56 AM, Kohei Kaigai <Kohei.Kaigai@eu.nec.com>
wrote:The attached patch removes rules to build a policy package for regression
test and modifies documentation part to introduce steps to run the test.Committed. Incidentally, on my Ubuntu system:
$ find /usr/share/selinux -name '*ake*'
/usr/share/selinux/default/include/Makefile
/usr/share/selinux/ubuntu/include/Makefile
/usr/share/selinux/mls/include/MakefileNot sure which of these would be the right one to use.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL CompanyClick
https://www.mailcontrol.com/sr/1JPOTPNZc+vTndxI!oX7UnkyRQ0MRq91W9aRlCO
56S1wi0rtpLI1rpvj957f8eUOrAhhBS0z5yrieLvRJKIvyA== to report this email
as spam.
On Thu, Mar 3, 2011 at 5:38 AM, Kohei Kaigai <Kohei.Kaigai@eu.nec.com> wrote:
BTW, it seems to me the base version of selinux-policy-* package in Ubuntu
is forked from an older snapshot (20091117), so it does not have enough rules
to run SE-PostgreSQL.Right now, Fedora 13/14 is the easiest way.
Yeah. I think that pretty much sucks, because really we would like
this to work wherever SE-Linux works. But I suppose in time it will
fix itself.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company