sepostgresql/sepostgresql-9.0-fullset.patch
2010-05-24 12:52:13 +00:00

19565 lines
577 KiB
Diff

diff --git a/configure b/configure
index b91de59..b44a6be 100755
--- a/configure
+++ b/configure
@@ -707,6 +707,7 @@ LDFLAGS_SL
ELF_SYS
EGREP
GREP
+enable_selinux
with_zlib
with_system_tzdata
with_libxslt
@@ -842,6 +843,7 @@ with_libxml
with_libxslt
with_system_tzdata
with_zlib
+enable_selinux
with_gnu_ld
enable_largefile
enable_float4_byval
@@ -1498,6 +1500,7 @@ Optional Features:
--enable-depend turn on automatic dependency tracking
--enable-cassert enable assertion checks (for debugging)
--disable-thread-safety disable thread-safety in client libraries
+ --enable-selinux build with SELinux support
--disable-largefile omit support for large files
--disable-float4-byval disable float4 passed by value
--disable-float8-byval disable float8 passed by value
@@ -5608,6 +5611,201 @@ fi
#
+# SELinux support
+#
+
+
+# Check whether --enable-selinux was given.
+if test "${enable_selinux+set}" = set; then
+ enableval=$enable_selinux;
+ case $enableval in
+ yes)
+ :
+ ;;
+ no)
+ :
+ ;;
+ *)
+ { { $as_echo "$as_me:$LINENO: error: no argument expected for --enable-selinux option" >&5
+$as_echo "$as_me: error: no argument expected for --enable-selinux option" >&2;}
+ { (exit 1); exit 1; }; }
+ ;;
+ esac
+
+else
+ enable_selinux=no
+
+fi
+
+
+if test "$enable_selinux" = yes; then
+
+{ $as_echo "$as_me:$LINENO: checking for avc_open in -lselinux" >&5
+$as_echo_n "checking for avc_open in -lselinux... " >&6; }
+if test "${ac_cv_lib_selinux_avc_open+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 avc_open ();
+int
+main ()
+{
+return avc_open ();
+ ;
+ 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_avc_open=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_selinux_avc_open=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_avc_open" >&5
+$as_echo "$ac_cv_lib_selinux_avc_open" >&6; }
+if test "x$ac_cv_lib_selinux_avc_open" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBSELINUX 1
+_ACEOF
+
+ LIBS="-lselinux $LIBS"
+
+else
+ { { $as_echo "$as_me:$LINENO: error: \"SELinux support requires libselinux.\"" >&5
+$as_echo "$as_me: error: \"SELinux support requires libselinux.\"" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+
+{ $as_echo "$as_me:$LINENO: checking for audit_open in -laudit" >&5
+$as_echo_n "checking for audit_open in -laudit... " >&6; }
+if test "${ac_cv_lib_audit_audit_open+set}" = set; then
+ $as_echo_n "(cached) " >&6
+else
+ ac_check_lib_save_LIBS=$LIBS
+LIBS="-laudit $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 audit_open ();
+int
+main ()
+{
+return audit_open ();
+ ;
+ 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_audit_audit_open=yes
+else
+ $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_cv_lib_audit_audit_open=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_audit_audit_open" >&5
+$as_echo "$ac_cv_lib_audit_audit_open" >&6; }
+if test "x$ac_cv_lib_audit_audit_open" = x""yes; then
+ cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBAUDIT 1
+_ACEOF
+
+ LIBS="-laudit $LIBS"
+
+else
+ { { $as_echo "$as_me:$LINENO: error: \"SELinux support requires libaudit.\"" >&5
+$as_echo "$as_me: error: \"SELinux support requires libaudit.\"" >&2;}
+ { (exit 1); exit 1; }; }
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SELINUX 1
+_ACEOF
+
+
+fi
+
+#
# Elf
#
diff --git a/configure.in b/configure.in
index b2fa682..5797ff5 100644
--- a/configure.in
+++ b/configure.in
@@ -755,6 +755,19 @@ PGAC_ARG_BOOL(with, zlib, yes,
AC_SUBST(with_zlib)
#
+# SELinux support
+#
+PGAC_ARG_BOOL(enable, selinux, no, [build with SELinux support])
+if test "$enable_selinux" = yes; then
+ AC_CHECK_LIB(selinux, avc_open,,
+ AC_MSG_ERROR("SELinux support requires libselinux."))
+ AC_CHECK_LIB(audit, audit_open,,
+ AC_MSG_ERROR("SELinux support requires libaudit."))
+ AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1, [SE-PostgreSQL feature is enabled])
+ AC_SUBST(enable_selinux)
+fi
+
+#
# Elf
#
diff --git a/contrib/adminpack/adminpack.c b/contrib/adminpack/adminpack.c
index 13e04b2..c613826 100644
--- a/contrib/adminpack/adminpack.c
+++ b/contrib/adminpack/adminpack.c
@@ -22,6 +22,7 @@
#include "funcapi.h"
#include "miscadmin.h"
#include "postmaster/syslogger.h"
+#include "sepgsql/hooks.h"
#include "storage/fd.h"
#include "utils/builtins.h"
#include "utils/datetime.h"
@@ -142,10 +143,16 @@ pg_file_write(PG_FUNCTION_ARGS)
(ERRCODE_DUPLICATE_FILE,
errmsg("file \"%s\" exists", filename)));
+ sepgsql_file_write(filename, true);
+
f = fopen(filename, "wb");
}
else
+ {
+ sepgsql_file_append(filename);
+
f = fopen(filename, "ab");
+ }
if (!f)
ereport(ERROR,
@@ -217,6 +224,7 @@ pg_file_rename(PG_FUNCTION_ARGS)
if (fn3)
{
+ sepgsql_file_rename(fn2, fn3);
if (rename(fn2, fn3) != 0)
{
ereport(ERROR,
@@ -224,6 +232,7 @@ pg_file_rename(PG_FUNCTION_ARGS)
errmsg("could not rename \"%s\" to \"%s\": %m",
fn2, fn3)));
}
+ sepgsql_file_rename(fn1, fn2);
if (rename(fn1, fn2) != 0)
{
ereport(WARNING,
@@ -247,11 +256,15 @@ pg_file_rename(PG_FUNCTION_ARGS)
}
}
}
- else if (rename(fn1, fn2) != 0)
+ else
{
- ereport(ERROR,
- (errcode_for_file_access(),
- errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2)));
+ sepgsql_file_rename(fn1, fn2);
+ if (rename(fn1, fn2) != 0)
+ {
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not rename \"%s\" to \"%s\": %m", fn1, fn2)));
+ }
}
PG_RETURN_BOOL(true);
@@ -277,6 +290,8 @@ pg_file_unlink(PG_FUNCTION_ARGS)
errmsg("file \"%s\" is not accessible: %m", filename)));
}
+ sepgsql_file_unlink(filename);
+
if (unlink(filename) < 0)
{
ereport(WARNING,
@@ -312,11 +327,14 @@ pg_logdir_ls(PG_FUNCTION_ARGS)
TupleDesc tupdesc;
funcctx = SRF_FIRSTCALL_INIT();
+
+ sepgsql_dir_list(Log_directory);
+
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
fctx = palloc(sizeof(directory_fctx));
- tupdesc = CreateTemplateTupleDesc(2, false);
+ tupdesc = CreateTemplateTupleDesc(2, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "starttime",
TIMESTAMPOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "filename",
diff --git a/contrib/sepgtest/sepgtest b/contrib/sepgtest/sepgtest
new file mode 100755
index 0000000..30e7cb9
--- /dev/null
+++ b/contrib/sepgtest/sepgtest
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+SEPGTEST=`basename $0`
+PSQL=`which psql`
+DIFF=`which diff`
+RUNCON=`which runcon`
+
+DBUSER=""
+DBHOST=""
+DBPORT=""
+DBNAME="testdb"
+
+usage()
+{
+ echo "${SEPGTEST}: [-u <username>] [-h <host>] [-p <port>] [-d <dbname>]"
+ exit 1
+}
+
+# parse options
+CMD_OPTS=`getopt -n "${SEPGTEST}" "u:h:p:d:" $*` || usage
+eval set -- $CMD_OPTS
+while [ -n "$1" ];
+do
+ case $1 in
+ -u)
+ DBUSER="-U $2"; shift 2;;
+ -h)
+ DBHOST="-h $2"; shift 2;;
+ -p)
+ DBPORT="-p $2"; shift 2;;
+ -d)
+ DBNAME="$2"; shift 2;;
+ --)
+ shift; break;;
+ *)
+ usage; shift 2;;
+ esac
+done
+
+PSQL_OPTS="${PSQL} ${DBUSER} ${DBHOST} ${DBPORT} ${DBNAME}"
+
+# check environment
+
+
+
diff --git a/contrib/sepgtest/sepgtest.in b/contrib/sepgtest/sepgtest.in
new file mode 100755
index 0000000..30e7cb9
--- /dev/null
+++ b/contrib/sepgtest/sepgtest.in
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+SEPGTEST=`basename $0`
+PSQL=`which psql`
+DIFF=`which diff`
+RUNCON=`which runcon`
+
+DBUSER=""
+DBHOST=""
+DBPORT=""
+DBNAME="testdb"
+
+usage()
+{
+ echo "${SEPGTEST}: [-u <username>] [-h <host>] [-p <port>] [-d <dbname>]"
+ exit 1
+}
+
+# parse options
+CMD_OPTS=`getopt -n "${SEPGTEST}" "u:h:p:d:" $*` || usage
+eval set -- $CMD_OPTS
+while [ -n "$1" ];
+do
+ case $1 in
+ -u)
+ DBUSER="-U $2"; shift 2;;
+ -h)
+ DBHOST="-h $2"; shift 2;;
+ -p)
+ DBPORT="-p $2"; shift 2;;
+ -d)
+ DBNAME="$2"; shift 2;;
+ --)
+ shift; break;;
+ *)
+ usage; shift 2;;
+ esac
+done
+
+PSQL_OPTS="${PSQL} ${DBUSER} ${DBHOST} ${DBPORT} ${DBNAME}"
+
+# check environment
+
+
+
diff --git a/git-update-branches.sh b/git-update-branches.sh
new file mode 100755
index 0000000..2f25fde
--- /dev/null
+++ b/git-update-branches.sh
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+GIT_BIN=`which git`
+GIT_DIR=`(cd \`dirname $0\`; pwd)`
+UPSTREAM_URL="git://git.postgresql.org/git/postgresql.git"
+UPSTREAM_BRANCH="master"
+LOCAL_BRANCHES="master v9.0/sepgsql"
+RPMSOURCE=`rpm -E '%{_sourcedir}'`
+#================================================================
+
+cd ${GIT_DIR}
+
+CURRENT_BRANCH=`${GIT_BIN} branch -l | grep '^*' | awk '{print $2}'`
+
+for tree in ${LOCAL_BRANCHES}
+do
+ ${GIT_BIN} branch -lr | grep -q "origin/${tree}" || exit 1
+ ${GIT_BIN} branch -l | grep -q "${tree}" || \
+ ${GIT_BIN} checkout --track "origin/${tree}" || exit 1
+done
+
+# ======== create patches ========
+if [ "$1" = "--patch" ]; then
+ LEFT_TREE=""
+ RIGHT_TREE=""
+ NUM=0
+
+ BASE_VERSION=`grep AC_INIT ${GIT_DIR}/configure.in \
+ | head -1 \
+ | sed -e 's/,/ /g' -e 's/\[//g' -e 's/\]//g' \
+ | awk '{print $2}'`
+ BASE_MAJOR=`echo $BASE_VERSION | sed 's/\.[0-9]\+$//g'`
+
+ for tree in ${LOCAL_BRANCHES}
+ do
+ LEFT_TREE=$RIGHT_TREE
+ RIGHT_TREE=$tree
+
+ if [ -n "$LEFT_TREE" -a -n "$RIGHT_TREE" ]; then
+ tag=`basename ${RIGHT_TREE}`
+ ${GIT_BIN} diff $LEFT_TREE $RIGHT_TREE \
+ > $RPMSOURCE/pgsql-${NUM}-${BASE_MAJOR}-${tag}.patch
+ echo "${NUM}) $RPMSOURCE/pgsql-${NUM}-${BASE_MAJOR}-${tag}.patch"
+ fi
+ NUM=`printf "%02d" \`expr $NUM + 1\``
+ done
+
+ exit 0
+fi
+
+# ======== sync remote git ========
+
+if echo "$1" | grep -q '^--sync-upstream=\?'; then
+ UPSTREAM_TAG=`echo "$1" | sed 's/^--sync-upstream=\?//g'`
+ if [ -n "${UPSTREAM_TAG}" ]; then
+ UPSTREAM_TAG=HEAD
+ fi
+
+ LOCAL_MASTER=`echo "${LOCAL_BRANCHES}" | awk '{print $1}'`
+
+ ${GIT_BIN} checkout ${LOCAL_MASTER} || exit 1
+ ${GIT_BIN} pull ${UPSTREAM_URL} ${UPSTREAM_BRANCH} ${UPSTREAM_TAG}
+ if [ $? -ne 0 ]; then
+ ${GIT_BIN} reset HEAD
+ ${GIT_BIN} checkout .
+ ${GIT_BIN} clean -fd
+ ${GIT_BIN} pull --no-commit ${UPSTREAM_URL} ${UPSTREAM_BRANCH} ${UPSTREAM_TAG}
+ exit 2
+ fi
+fi
+
+LEFT_TREE=""
+for RIGHT_TREE in ${LOCAL_BRANCHES}
+do
+ if [ -n "${LEFT_TREE}" -a -n "${RIGHT_TREE}" ]; then
+ ${GIT_BIN} checkout ${RIGHT_TREE} || exit 1
+ ${GIT_BIN} pull ${GIT_DIR} ${LEFT_TREE} || exit 1
+ if [ $? -ne 0 ]; then
+ ${GIT_BIN} reset HEAD
+ ${GIT_BIN} checkout .
+ ${GIT_BIN} clean -fd
+ ${GIT_BIN} pull --no-commit ${GIT_DIR} ${LEFT_TREE}
+ exit 2
+ fi
+ fi
+ LEFT_TREE=${RIGHT_TREE}
+done
+
+${GIT_BIN} checkout ${CURRENT_BRANCH}
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
index 1c38ac2..0e3bed5 100644
--- a/src/Makefile.global.in
+++ b/src/Makefile.global.in
@@ -164,6 +164,7 @@ enable_nls = @enable_nls@
enable_debug = @enable_debug@
enable_dtrace = @enable_dtrace@
enable_coverage = @enable_coverage@
+enable_selinux = @enable_selinux@
enable_thread_safety = @enable_thread_safety@
python_includespec = @python_includespec@
diff --git a/src/backend/Makefile b/src/backend/Makefile
index 218544e..0b1dd31 100644
--- a/src/backend/Makefile
+++ b/src/backend/Makefile
@@ -16,7 +16,7 @@ include $(top_builddir)/src/Makefile.global
SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \
main nodes optimizer port postmaster regex replication rewrite \
- storage tcop tsearch utils $(top_builddir)/src/timezone
+ sepgsql storage tcop tsearch utils $(top_builddir)/src/timezone
include $(srcdir)/common.mk
@@ -40,6 +40,11 @@ LIBS := $(filter-out -lpgport, $(LIBS)) $(LDAP_LIBS_BE)
# The backend doesn't need everything that's in LIBS, however
LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS))
+# SELinux needs libselinux and libaudit
+ifeq ($(enable_selinux), yes)
+LIBS := $(filter-out -lselinux -laudit, $(LIBS)) -lselinux -laudit
+endif
+
##########################################################################
all: submake-libpgport submake-schemapg postgres $(POSTGRES_IMP)
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 6ec73f0..db0023f 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -60,6 +60,7 @@
#include "access/heapam.h"
#include "access/sysattr.h"
#include "access/tuptoaster.h"
+#include "catalog/pg_seclabel.h"
#include "executor/tuptable.h"
@@ -559,6 +560,9 @@ heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
case TableOidAttributeNumber:
result = ObjectIdGetDatum(tup->t_tableOid);
break;
+ case SecurityLabelAttributeNumber:
+ result = seclabelSysattOutput(tup->t_tableOid, tup);
+ break;
default:
elog(ERROR, "invalid attnum: %d", attnum);
result = 0; /* keep compiler quiet */
@@ -682,6 +686,8 @@ heap_form_tuple(TupleDesc tupleDescriptor,
if (tupleDescriptor->tdhasoid)
len += sizeof(Oid);
+ if (tupleDescriptor->tdhassecid)
+ len += sizeof(Oid)+1;
hoff = len = MAXALIGN(len); /* align user data safely */
@@ -713,6 +719,8 @@ heap_form_tuple(TupleDesc tupleDescriptor,
if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */
td->t_infomask = HEAP_HASOID;
+ if (tupleDescriptor->tdhassecid)
+ td->t_infomask |= HEAP_HASSECID;
heap_fill_tuple(tupleDescriptor,
values,
@@ -824,6 +832,8 @@ heap_modify_tuple(HeapTuple tuple,
newTuple->t_tableOid = tuple->t_tableOid;
if (tupleDesc->tdhasoid)
HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple));
+ if (tupleDesc->tdhassecid)
+ HeapTupleSetSecid(newTuple, HeapTupleGetSecid(tuple));
return newTuple;
}
@@ -1434,6 +1444,8 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor,
if (tupleDescriptor->tdhasoid)
len += sizeof(Oid);
+ if (tupleDescriptor->tdhassecid)
+ len += sizeof(Oid);
hoff = len = MAXALIGN(len); /* align user data safely */
@@ -1455,6 +1467,8 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor,
if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */
tuple->t_infomask = HEAP_HASOID;
+ if (tupleDescriptor->tdhassecid)
+ tuple->t_infomask |= HEAP_HASSECID;
heap_fill_tuple(tupleDescriptor,
values,
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 9a8611f..325dbde 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -34,7 +34,7 @@
* caller can overwrite this if needed.
*/
TupleDesc
-CreateTemplateTupleDesc(int natts, bool hasoid)
+CreateTemplateTupleDesc(int natts, bool hasoid, bool hassecid)
{
TupleDesc desc;
char *stg;
@@ -88,6 +88,7 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
desc->tdtypeid = RECORDOID;
desc->tdtypmod = -1;
desc->tdhasoid = hasoid;
+ desc->tdhassecid = hassecid;
desc->tdrefcount = -1; /* assume not reference-counted */
return desc;
@@ -105,7 +106,8 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
* caller can overwrite this if needed.
*/
TupleDesc
-CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
+CreateTupleDesc(int natts, bool hasoid, bool hassecid,
+ Form_pg_attribute *attrs)
{
TupleDesc desc;
@@ -121,6 +123,7 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
desc->tdtypeid = RECORDOID;
desc->tdtypmod = -1;
desc->tdhasoid = hasoid;
+ desc->tdhassecid = hassecid;
desc->tdrefcount = -1; /* assume not reference-counted */
return desc;
@@ -139,7 +142,8 @@ CreateTupleDescCopy(TupleDesc tupdesc)
TupleDesc desc;
int i;
- desc = CreateTemplateTupleDesc(tupdesc->natts, tupdesc->tdhasoid);
+ desc = CreateTemplateTupleDesc(tupdesc->natts,
+ tupdesc->tdhasoid, tupdesc->tdhassecid);
for (i = 0; i < desc->natts; i++)
{
@@ -150,6 +154,7 @@ CreateTupleDescCopy(TupleDesc tupdesc)
desc->tdtypeid = tupdesc->tdtypeid;
desc->tdtypmod = tupdesc->tdtypmod;
+ desc->tdhassecid = tupdesc->tdhassecid;
return desc;
}
@@ -166,7 +171,8 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
TupleConstr *constr = tupdesc->constr;
int i;
- desc = CreateTemplateTupleDesc(tupdesc->natts, tupdesc->tdhasoid);
+ desc = CreateTemplateTupleDesc(tupdesc->natts,
+ tupdesc->tdhasoid, tupdesc->tdhassecid);
for (i = 0; i < desc->natts; i++)
{
@@ -314,6 +320,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (tupdesc1->tdhasoid != tupdesc2->tdhasoid)
return false;
+ if (tupdesc1->tdhassecid != tupdesc2->tdhassecid)
+ return false;
for (i = 0; i < tupdesc1->natts; i++)
{
@@ -519,7 +527,7 @@ BuildDescForRelation(List *schema)
* allocate a new tuple descriptor
*/
natts = list_length(schema);
- desc = CreateTemplateTupleDesc(natts, false);
+ desc = CreateTemplateTupleDesc(natts, false, false);
has_not_null = false;
attnum = 0;
@@ -604,7 +612,7 @@ BuildDescFromLists(List *names, List *types, List *typmods)
/*
* allocate a new tuple descriptor
*/
- desc = CreateTemplateTupleDesc(natts, false);
+ desc = CreateTemplateTupleDesc(natts, false, false);
attnum = 0;
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index f01ed1e..4e8dfcb 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -33,7 +33,7 @@ initGinState(GinState *state, Relation index)
for (i = 0; i < index->rd_att->natts; i++)
{
- state->tupdesc[i] = CreateTemplateTupleDesc(2, false);
+ state->tupdesc[i] = CreateTemplateTupleDesc(2, false, false);
TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL,
INT2OID, -1, 0);
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 759fc35..2fc282d 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -52,6 +52,7 @@
#include "access/xlogutils.h"
#include "catalog/catalog.h"
#include "catalog/namespace.h"
+#include "catalog/pg_seclabel.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "storage/bufmgr.h"
@@ -1862,6 +1863,22 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
Assert(!(tup->t_data->t_infomask & HEAP_HASOID));
}
+ /*
+ * If this tuple has a capability to store its security id, but it has
+ * not been assigned yet, the default security id should be assigned.
+ * Note that this step does not apply any permission checks. All the
+ * caller of heap_insert() is trusted.
+ */
+ if (relation->rd_rel->relhassecids)
+ {
+ if (!OidIsValid(HeapTupleGetSecid(tup)))
+ HeapTupleSetSecid(tup, seclabelGetNewSecid(relation, tup));
+ }
+ else
+ {
+ Assert(!HeapTupleHasSecid(tup));
+ }
+
tup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
tup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK);
tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
@@ -2560,6 +2577,20 @@ l2:
Assert(!(newtup->t_data->t_infomask & HEAP_HASOID));
}
+ /*
+ * Preserve security-id, if not changed
+ */
+ if (relation->rd_rel->relhassecids)
+ {
+ if (!OidIsValid(HeapTupleGetSecid(newtup)))
+ HeapTupleSetSecid(newtup, HeapTupleGetSecid(&oldtup));
+ }
+ else
+ {
+ /* check there is not space for a security-id */
+ Assert(!HeapTupleHasSecid(newtup));
+ }
+
newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
newtup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK);
newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED);
@@ -3501,6 +3532,10 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
memcpy((char *) htup + htup->t_hoff,
(char *) tuple->t_data + tuple->t_data->t_hoff,
newlen);
+ if (HeapTupleHeaderHasOid(htup))
+ HeapTupleHeaderSetOid(htup, HeapTupleGetOid(tuple));
+ if (HeapTupleHeaderHasSecid(htup))
+ HeapTupleHeaderSetSecid(htup, HeapTupleGetSecid(tuple));
MarkBufferDirty(buffer);
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 2af81df..18341f4 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -591,6 +591,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
hoff += BITMAPLEN(numAttrs);
if (newtup->t_data->t_infomask & HEAP_HASOID)
hoff += sizeof(Oid);
+ if (HeapTupleHasSecid(newtup))
+ hoff += sizeof(Oid);
hoff = MAXALIGN(hoff);
Assert(hoff == newtup->t_data->t_hoff);
/* now convert to a limit on the tuple data size */
@@ -868,6 +870,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
new_len += BITMAPLEN(numAttrs);
if (olddata->t_infomask & HEAP_HASOID)
new_len += sizeof(Oid);
+ if (HeapTupleHeaderHasSecid(olddata))
+ new_len += sizeof(Oid);
new_len = MAXALIGN(new_len);
Assert(new_len == olddata->t_hoff);
new_data_len = heap_compute_data_size(tupleDesc,
@@ -1019,6 +1023,8 @@ toast_flatten_tuple_attribute(Datum value,
new_len += BITMAPLEN(numAttrs);
if (olddata->t_infomask & HEAP_HASOID)
new_len += sizeof(Oid);
+ if (HeapTupleHeaderHasSecid(olddata))
+ new_len += sizeof(Oid);
new_len = MAXALIGN(new_len);
Assert(new_len == olddata->t_hoff);
new_data_len = heap_compute_data_size(tupleDesc,
diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index d432c9d..28e7bde 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -605,7 +605,7 @@ pg_prepared_xact(PG_FUNCTION_ARGS)
/* build tupdesc for result tuples */
/* this had better match pg_prepared_xacts view in system_views.sql */
- tupdesc = CreateTemplateTupleDesc(5, false);
+ tupdesc = CreateTemplateTupleDesc(5, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "transaction",
XIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "gid",
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index b88cff2..d6636f7 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -36,6 +36,7 @@
#include "libpq/be-fsstubs.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "sepgsql/hooks.h"
#include "storage/bufmgr.h"
#include "storage/fd.h"
#include "storage/lmgr.h"
@@ -140,6 +141,8 @@ typedef struct TransactionStateData
int maxChildXids; /* allocated size of childXids[] */
Oid prevUser; /* previous CurrentUserId setting */
int prevSecContext; /* previous SecurityRestrictionContext */
+ char *prevSecLabel; /* previous security label of client */
+ int prevRowlvMode; /* previous row-level access control mode */
bool prevXactReadOnly; /* entry-time xact r/o state */
bool startedInRecovery; /* did we start in recovery? */
struct TransactionStateData *parent; /* back link to parent */
@@ -169,6 +172,8 @@ static TransactionStateData TopTransactionStateData = {
0, /* allocated size of childXids[] */
InvalidOid, /* previous CurrentUserId setting */
0, /* previous SecurityRestrictionContext */
+ NULL, /* previous security label of the client */
+ 0, /* previous row-level access control mode */
false, /* entry-time xact r/o state */
false, /* startedInRecovery */
NULL /* link to parent state block */
@@ -1658,6 +1663,10 @@ StartTransaction(void)
/* SecurityRestrictionContext should never be set outside a transaction */
Assert(s->prevSecContext == 0);
+ /* Save status of SELinux */
+ s->prevSecLabel = sepgsql_get_client_label();
+ s->prevRowlvMode = sepgsql_rowlv_get_mode();
+
/*
* initialize other subsystems for new transaction
*/
@@ -2162,6 +2171,10 @@ AbortTransaction(void)
*/
SetUserIdAndSecContext(s->prevUser, s->prevSecContext);
+ /* Reset SELinux status */
+ sepgsql_set_client_label(s->prevSecLabel);
+ sepgsql_rowlv_set_mode(s->prevRowlvMode);
+
/*
* do abort processing
*/
@@ -4006,6 +4019,10 @@ AbortSubTransaction(void)
*/
SetUserIdAndSecContext(s->prevUser, s->prevSecContext);
+ /* Reset SELinux status */
+ sepgsql_set_client_label(s->prevSecLabel);
+ sepgsql_rowlv_set_mode(s->prevRowlvMode);
+
/*
* We can skip all this stuff if the subxact failed before creating a
* ResourceOwner...
@@ -4145,6 +4162,8 @@ PushTransaction(void)
s->state = TRANS_DEFAULT;
s->blockState = TBLOCK_SUBBEGIN;
GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext);
+ s->prevSecLabel = sepgsql_get_client_label();
+ s->prevRowlvMode = sepgsql_rowlv_get_mode();
s->prevXactReadOnly = XactReadOnly;
CurrentTransactionState = s;
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 0b05e25..4f783e5 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -8765,7 +8765,7 @@ pg_xlogfile_name_offset(PG_FUNCTION_ARGS)
* Construct a tuple descriptor for the result row. This must match this
* function's pg_proc entry!
*/
- resultTupleDesc = CreateTemplateTupleDesc(2, false);
+ resultTupleDesc = CreateTemplateTupleDesc(2, false, false);
TupleDescInitEntry(resultTupleDesc, (AttrNumber) 1, "file_name",
TEXTOID, -1, 0);
TupleDescInitEntry(resultTupleDesc, (AttrNumber) 2, "file_offset",
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 387d43e..aecb449 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -32,6 +32,7 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_class.h"
#include "catalog/pg_namespace.h"
+#include "catalog/pg_seclabel.h"
#include "catalog/pg_tablespace.h"
#include "catalog/toasting.h"
#include "commands/defrem.h"
@@ -187,10 +188,11 @@ Boot_CreateStmt:
TupleDesc tupdesc;
bool shared_relation;
bool mapped_relation;
+ bool hassecid = seclabelCatalogHasSysAttr($3);
do_start();
- tupdesc = CreateTupleDesc(numattr, !($6), attrtypes);
+ tupdesc = CreateTupleDesc(numattr, !($6), hassecid, attrtypes);
shared_relation = $5;
@@ -245,7 +247,8 @@ Boot_CreateStmt:
ONCOMMIT_NOOP,
(Datum) 0,
false,
- true);
+ true,
+ NULL);
elog(DEBUG4, "relation created with oid %u", id);
}
do_end();
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 455eac1..0590156 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -33,6 +33,7 @@
#include "postmaster/bgwriter.h"
#include "postmaster/walwriter.h"
#include "replication/walreceiver.h"
+#include "sepgsql/hooks.h"
#include "storage/bufmgr.h"
#include "storage/ipc.h"
#include "storage/proc.h"
@@ -319,6 +320,9 @@ AuxiliaryProcessMain(int argc, char *argv[])
case WalReceiverProcess:
statmsg = "wal receiver process";
break;
+ case SecurityWorkerProcess:
+ statmsg = "security worker process";
+ break;
default:
statmsg = "??? process";
break;
@@ -428,6 +432,10 @@ AuxiliaryProcessMain(int argc, char *argv[])
WalReceiverMain();
proc_exit(1); /* should never return */
+ case SecurityWorkerProcess:
+ sepgsql_worker_main();
+ proc_exit(1); /* should never return */
+
default:
elog(PANIC, "unrecognized process type: %d", auxType);
proc_exit(1);
@@ -482,6 +490,11 @@ BootstrapModeMain(void)
boot_yyparse();
/*
+ * Initial security labeling
+ */
+ sepgsql_post_bootstraping();
+
+ /*
* We should now know about all mapped relations, so it's okay to write
* out the initial relation mapping files.
*/
@@ -779,6 +792,7 @@ InsertOneTuple(Oid objectid)
tupDesc = CreateTupleDesc(numattr,
RelationGetForm(boot_reldesc)->relhasoids,
+ RelationGetForm(boot_reldesc)->relhassecids,
attrtypes);
tuple = heap_form_tuple(tupDesc, values, Nulls);
if (objectid != (Oid) 0)
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index dafae3f..c2aea7a 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -13,7 +13,7 @@ include $(top_builddir)/src/Makefile.global
OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o pg_enum.o \
pg_inherits.o pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o \
- pg_db_role_setting.o pg_shdepend.o pg_type.o storage.o toasting.o
+ pg_db_role_setting.o pg_seclabel.o pg_shdepend.o pg_type.o storage.o toasting.o
BKIFILES = postgres.bki postgres.description postgres.shdescription
@@ -34,7 +34,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \
pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \
pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
- pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
+ pg_seclabel.h pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
pg_ts_parser.h pg_ts_template.h \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
pg_default_acl.h \
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 2561d83..002a66c 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -38,6 +38,7 @@
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
+#include "catalog/pg_seclabel.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_type.h"
#include "catalog/pg_ts_config.h"
@@ -46,6 +47,7 @@
#include "foreign/foreign.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
@@ -1499,6 +1501,10 @@ expand_all_col_privileges(Oid table_oid, Form_pg_class classForm,
if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids)
continue;
+ /* Skip security label column, if it doesn't exist */
+ if (curr_att == SecurityLabelAttributeNumber && !classForm->relhassecids)
+ continue;
+
/* Views don't have any system columns at all */
if (classForm->relkind == RELKIND_VIEW && curr_att < 0)
continue;
@@ -1608,6 +1614,8 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname,
relOid, grantorId, ACL_KIND_COLUMN,
relname, attnum,
NameStr(pg_attribute_tuple->attname));
+ /* SELinux checks */
+ sepgsql_attribute_grant(relOid, attnum);
/*
* Generate new ACL.
@@ -1869,6 +1877,8 @@ ExecGrant_Relation(InternalGrant *istmt)
? ACL_KIND_SEQUENCE : ACL_KIND_CLASS,
NameStr(pg_class_tuple->relname),
0, NULL);
+ /* SELinux checks */
+ sepgsql_relation_grant(relOid);
/*
* Generate new ACL.
@@ -2063,6 +2073,8 @@ ExecGrant_Database(InternalGrant *istmt)
datId, grantorId, ACL_KIND_DATABASE,
NameStr(pg_database_tuple->datname),
0, NULL);
+ /* SELinux checks */
+ sepgsql_database_grant(datId);
/*
* Generate new ACL.
@@ -2188,6 +2200,8 @@ ExecGrant_Fdw(InternalGrant *istmt)
fdwid, grantorId, ACL_KIND_FDW,
NameStr(pg_fdw_tuple->fdwname),
0, NULL);
+ /* SELinux checks */
+ sepgsql_fdw_grant(fdwid);
/*
* Generate new ACL.
@@ -2313,6 +2327,8 @@ ExecGrant_ForeignServer(InternalGrant *istmt)
srvid, grantorId, ACL_KIND_FOREIGN_SERVER,
NameStr(pg_server_tuple->srvname),
0, NULL);
+ /* SELinux checks */
+ sepgsql_fserver_grant(srvid);
/*
* Generate new ACL.
@@ -2831,6 +2847,8 @@ ExecGrant_Namespace(InternalGrant *istmt)
nspid, grantorId, ACL_KIND_NAMESPACE,
NameStr(pg_namespace_tuple->nspname),
0, NULL);
+ /* SELinux checks */
+ sepgsql_schema_grant(nspid);
/*
* Generate new ACL.
@@ -2955,6 +2973,8 @@ ExecGrant_Tablespace(InternalGrant *istmt)
tblId, grantorId, ACL_KIND_TABLESPACE,
NameStr(pg_tablespace_tuple->spcname),
0, NULL);
+ /* SELinux checks */
+ sepgsql_tablespace_grant(tblId);
/*
* Generate new ACL.
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index 3edfc23..b451e59 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -32,6 +32,7 @@
#include "catalog/pg_namespace.h"
#include "catalog/pg_pltemplate.h"
#include "catalog/pg_db_role_setting.h"
+#include "catalog/pg_seclabel.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
#include "catalog/pg_tablespace.h"
@@ -306,6 +307,7 @@ IsSharedRelation(Oid relationId)
relationId == AuthMemRelationId ||
relationId == DatabaseRelationId ||
relationId == PLTemplateRelationId ||
+ relationId == SecLabelRelationId ||
relationId == SharedDescriptionRelationId ||
relationId == SharedDependRelationId ||
relationId == TableSpaceRelationId ||
@@ -319,6 +321,8 @@ IsSharedRelation(Oid relationId)
relationId == DatabaseNameIndexId ||
relationId == DatabaseOidIndexId ||
relationId == PLTemplateNameIndexId ||
+ relationId == SecLabelSecidIndexId ||
+ relationId == SecLabelLabelIndexId ||
relationId == SharedDescriptionObjIndexId ||
relationId == SharedDependDependerIndexId ||
relationId == SharedDependReferenceIndexId ||
@@ -329,6 +333,8 @@ IsSharedRelation(Oid relationId)
/* These are their toast tables and toast indexes (see toasting.h) */
if (relationId == PgDatabaseToastTable ||
relationId == PgDatabaseToastIndex ||
+ relationId == PgSecLabelToastTable ||
+ relationId == PgSecLabelToastIndex ||
relationId == PgShdescriptionToastTable ||
relationId == PgShdescriptionToastIndex ||
relationId == PgDbRoleSettingToastTable ||
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index 31aabda..c9fc09e 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -218,7 +218,8 @@ foreach my $catname ( @{ $catalogs->{names} } )
{cmin => 'cid'},
{xmax => 'xid'},
{cmax => 'cid'},
- {tableoid => 'oid'}
+ {tableoid => 'oid'},
+ {security_label => 'text'}
);
foreach my $attr (@SYS_ATTRS)
{
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index d848ef0..deaf2fb 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -43,6 +43,7 @@
#include "catalog/pg_constraint.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_namespace.h"
+#include "catalog/pg_seclabel.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_type.h"
@@ -82,14 +83,16 @@ static void AddNewRelationTuple(Relation pg_class_desc,
Oid relowner,
char relkind,
Datum relacl,
- Datum reloptions);
+ Datum reloptions,
+ Oid *secLabels);
static Oid AddNewRelationType(const char *typeName,
Oid typeNamespace,
Oid new_rel_oid,
char new_rel_kind,
Oid ownerid,
Oid new_row_type,
- Oid new_array_type);
+ Oid new_array_type,
+ Oid securityId);
static void RelationRemoveInheritance(Oid relid);
static void StoreRelCheck(Relation rel, char *ccname, Node *expr,
bool is_local, int inhcount);
@@ -173,7 +176,16 @@ static FormData_pg_attribute a7 = {
true, 'p', 'i', true, false, false, true, 0
};
-static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7};
+/*
+ * Security Label system column
+ */
+static FormData_pg_attribute a8 = {
+ 0, {"security_label"}, TEXTOID, 0, -1,
+ SecurityLabelAttributeNumber, 0, -1, -1,
+ false, 'x', 'i', true, false, false, true, 0
+};
+
+static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8};
/*
* This function returns a Form_pg_attribute pointer for a system attribute.
@@ -181,12 +193,14 @@ static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7};
* happen if there's a problem upstream.
*/
Form_pg_attribute
-SystemAttributeDefinition(AttrNumber attno, bool relhasoids)
+SystemAttributeDefinition(AttrNumber attno, bool relhasoids, bool relhassecids)
{
if (attno >= 0 || attno < -(int) lengthof(SysAtt))
elog(ERROR, "invalid system attribute number %d", attno);
if (attno == ObjectIdAttributeNumber && !relhasoids)
elog(ERROR, "invalid system attribute number %d", attno);
+ if (attno == SecurityLabelAttributeNumber && !relhassecids)
+ elog(ERROR, "invalid system attribute number %d", attno);
return SysAtt[-attno - 1];
}
@@ -195,7 +209,7 @@ SystemAttributeDefinition(AttrNumber attno, bool relhasoids)
* pointer for a prototype definition. If not, return NULL.
*/
Form_pg_attribute
-SystemAttributeByName(const char *attname, bool relhasoids)
+SystemAttributeByName(const char *attname, bool relhasoids, bool relhassecids)
{
int j;
@@ -203,16 +217,29 @@ SystemAttributeByName(const char *attname, bool relhasoids)
{
Form_pg_attribute att = SysAtt[j];
- if (relhasoids || att->attnum != ObjectIdAttributeNumber)
- {
- if (strcmp(NameStr(att->attname), attname) == 0)
- return att;
- }
+ if (!relhasoids && att->attnum == ObjectIdAttributeNumber)
+ continue;
+ if (!relhassecids && att->attnum == SecurityLabelAttributeNumber)
+ continue;
+
+ if (strcmp(NameStr(att->attname), attname) == 0)
+ return att;
}
return NULL;
}
+/*
+ * If the given attribute is writable system attribute, it returns true.
+ */
+bool
+SystemAttributeWritable(AttrNumber attnum, bool hasoids, bool hassecids)
+{
+ if (hassecids && attnum == SecurityLabelAttributeNumber)
+ return true;
+
+ return false;
+}
/* ----------------------------------------------------------------
* XXX END OF UGLY HARD CODED BADNESS XXX
@@ -391,7 +418,8 @@ CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind,
for (i = 0; i < natts; i++)
{
if (SystemAttributeByName(NameStr(tupdesc->attrs[i]->attname),
- tupdesc->tdhasoid) != NULL)
+ tupdesc->tdhasoid,
+ tupdesc->tdhassecid) != NULL)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
errmsg("column name \"%s\" conflicts with a system column name",
@@ -509,7 +537,8 @@ CheckAttributeType(const char *attname, Oid atttypid,
void
InsertPgAttributeTuple(Relation pg_attribute_rel,
Form_pg_attribute new_attribute,
- CatalogIndexState indstate)
+ CatalogIndexState indstate,
+ Oid securityId)
{
Datum values[Natts_pg_attribute];
bool nulls[Natts_pg_attribute];
@@ -542,6 +571,8 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
nulls[Anum_pg_attribute_attoptions - 1] = true;
tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls);
+ if (HeapTupleHasSecid(tup))
+ HeapTupleSetSecid(tup, securityId);
/* finally insert the new tuple, update the indexes, and clean up */
simple_heap_insert(pg_attribute_rel, tup);
@@ -566,13 +597,15 @@ AddNewAttributeTuples(Oid new_rel_oid,
TupleDesc tupdesc,
char relkind,
bool oidislocal,
- int oidinhcount)
+ int oidinhcount,
+ Oid *secLabels)
{
Form_pg_attribute attr;
int i;
Relation rel;
CatalogIndexState indstate;
int natts = tupdesc->natts;
+ Oid secid;
ObjectAddress myself,
referenced;
@@ -596,7 +629,10 @@ AddNewAttributeTuples(Oid new_rel_oid,
attr->attstattarget = -1;
attr->attcacheoff = -1;
- InsertPgAttributeTuple(rel, attr, indstate);
+ secid = (!secLabels ? InvalidOid :
+ secLabels[i - FirstLowInvalidHeapAttributeNumber]);
+
+ InsertPgAttributeTuple(rel, attr, indstate, secid);
/* Add dependency info */
myself.classId = RelationRelationId;
@@ -624,6 +660,11 @@ AddNewAttributeTuples(Oid new_rel_oid,
SysAtt[i]->attnum == ObjectIdAttributeNumber)
continue;
+ /* skip security label where appropriate */
+ if (!tupdesc->tdhassecid &&
+ SysAtt[i]->attnum == SecurityLabelAttributeNumber)
+ continue;
+
memcpy(&attStruct, (char *) SysAtt[i], sizeof(FormData_pg_attribute));
/* Fill in the correct relation OID in the copied tuple */
@@ -636,7 +677,10 @@ AddNewAttributeTuples(Oid new_rel_oid,
attStruct.attinhcount = oidinhcount;
}
- InsertPgAttributeTuple(rel, &attStruct, indstate);
+ secid = (!secLabels ? InvalidOid
+ : secLabels[SysAtt[i]->attnum - FirstLowInvalidHeapAttributeNumber]);
+
+ InsertPgAttributeTuple(rel, &attStruct, indstate, secid);
}
}
@@ -666,7 +710,8 @@ InsertPgClassTuple(Relation pg_class_desc,
Relation new_rel_desc,
Oid new_rel_oid,
Datum relacl,
- Datum reloptions)
+ Datum reloptions,
+ Oid securityId)
{
Form_pg_class rd_rel = new_rel_desc->rd_rel;
Datum values[Natts_pg_class];
@@ -696,6 +741,7 @@ InsertPgClassTuple(Relation pg_class_desc,
values[Anum_pg_class_relnatts - 1] = Int16GetDatum(rd_rel->relnatts);
values[Anum_pg_class_relchecks - 1] = Int16GetDatum(rd_rel->relchecks);
values[Anum_pg_class_relhasoids - 1] = BoolGetDatum(rd_rel->relhasoids);
+ values[Anum_pg_class_relhassecids - 1] = BoolGetDatum(rd_rel->relhassecids);
values[Anum_pg_class_relhaspkey - 1] = BoolGetDatum(rd_rel->relhaspkey);
values[Anum_pg_class_relhasexclusion - 1] = BoolGetDatum(rd_rel->relhasexclusion);
values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules);
@@ -719,6 +765,9 @@ InsertPgClassTuple(Relation pg_class_desc,
*/
HeapTupleSetOid(tup, new_rel_oid);
+ if (HeapTupleHasSecid(tup))
+ HeapTupleSetSecid(tup, securityId);
+
/* finally insert the new tuple, update the indexes, and clean up */
simple_heap_insert(pg_class_desc, tup);
@@ -743,9 +792,11 @@ AddNewRelationTuple(Relation pg_class_desc,
Oid relowner,
char relkind,
Datum relacl,
- Datum reloptions)
+ Datum reloptions,
+ Oid *secLabels)
{
Form_pg_class new_rel_reltup;
+ Oid secid;
/*
* first we update some of the information in our uncataloged relation's
@@ -803,9 +854,11 @@ AddNewRelationTuple(Relation pg_class_desc,
new_rel_desc->rd_att->tdtypeid = new_type_oid;
+ secid = (!secLabels ? InvalidOid : secLabels[0]);
+
/* Now build and insert the tuple */
InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid,
- relacl, reloptions);
+ relacl, reloptions, secid);
}
@@ -822,7 +875,8 @@ AddNewRelationType(const char *typeName,
char new_rel_kind,
Oid ownerid,
Oid new_row_type,
- Oid new_array_type)
+ Oid new_array_type,
+ Oid securityId)
{
return
TypeCreate(new_row_type, /* optional predetermined OID */
@@ -854,7 +908,8 @@ AddNewRelationType(const char *typeName,
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
- false); /* Type NOT NULL */
+ false, /* Type NOT NULL */
+ securityId); /* security-id of the type */
}
/* --------------------------------
@@ -903,7 +958,8 @@ heap_create_with_catalog(const char *relname,
OnCommitAction oncommit,
Datum reloptions,
bool use_user_acl,
- bool allow_system_table_mods)
+ bool allow_system_table_mods,
+ Oid *secLabels)
{
Relation pg_class_desc;
Relation new_rel_desc;
@@ -911,6 +967,7 @@ heap_create_with_catalog(const char *relname,
Oid old_type_oid;
Oid new_type_oid;
Oid new_array_oid = InvalidOid;
+ Oid type_secid = InvalidOid;
pg_class_desc = heap_open(RelationRelationId, RowExclusiveLock);
@@ -1030,6 +1087,11 @@ heap_create_with_catalog(const char *relname,
relkind == RELKIND_COMPOSITE_TYPE))
new_array_oid = AssignTypeArrayOid();
+ /* security context of the relation type */
+ if (secLabels)
+ type_secid = seclabelMoveSecid(TypeRelationId,
+ RelationRelationId, secLabels[0]);
+
/*
* Since defining a relation also defines a complex type, we add a new
* system type corresponding to the new relation. The OID of the type can
@@ -1046,7 +1108,8 @@ heap_create_with_catalog(const char *relname,
relkind,
ownerid,
reltypeid,
- new_array_oid);
+ new_array_oid,
+ type_secid);
/*
* Now make the array type if wanted.
@@ -1086,7 +1149,8 @@ heap_create_with_catalog(const char *relname,
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
- false); /* Type NOT NULL */
+ false, /* Type NOT NULL */
+ type_secid); /* security-id of the type */
pfree(relarrayname);
}
@@ -1106,13 +1170,14 @@ heap_create_with_catalog(const char *relname,
ownerid,
relkind,
PointerGetDatum(relacl),
- reloptions);
+ reloptions,
+ secLabels);
/*
* now add tuples to pg_attribute for the attributes in our new relation.
*/
AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind,
- oidislocal, oidinhcount);
+ oidislocal, oidinhcount, secLabels);
/*
* Make a dependency link to force the relation to be deleted if its
@@ -1590,6 +1655,11 @@ heap_drop_with_catalog(Oid relid)
* delete relation tuple
*/
DeleteRelationTuple(relid);
+
+ /*
+ * delete orphan pg_seclabel entries
+ */
+ seclabelOnDropTable(relid);
}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 69946fe..23fb19b 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -39,6 +39,7 @@
#include "catalog/pg_constraint.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opclass.h"
+#include "catalog/pg_seclabel.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -90,7 +91,8 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation,
Oid *classObjectId);
static void InitializeAttributeOids(Relation indexRelation,
int numatts, Oid indexoid);
-static void AppendAttributeTuples(Relation indexRelation, int numatts);
+static void AppendAttributeTuples(Relation indexRelation,
+ int numatts, Oid securityId);
static void UpdateIndexRelation(Oid indexoid, Oid heapoid,
IndexInfo *indexInfo,
Oid *classOids,
@@ -155,7 +157,7 @@ ConstructTupleDescriptor(Relation heapRelation,
/*
* allocate the new tuple descriptor
*/
- indexTupDesc = CreateTemplateTupleDesc(numatts, false);
+ indexTupDesc = CreateTemplateTupleDesc(numatts, false, false);
/*
* For simple index columns, we copy the pg_attribute row from the parent
@@ -182,7 +184,8 @@ ConstructTupleDescriptor(Relation heapRelation,
* here we are indexing on a system attribute (-1...-n)
*/
from = SystemAttributeDefinition(atnum,
- heapRelation->rd_rel->relhasoids);
+ heapRelation->rd_rel->relhasoids,
+ heapRelation->rd_rel->relhassecids);
}
else
{
@@ -339,13 +342,16 @@ InitializeAttributeOids(Relation indexRelation,
* ----------------------------------------------------------------
*/
static void
-AppendAttributeTuples(Relation indexRelation, int numatts)
+AppendAttributeTuples(Relation indexRelation, int numatts, Oid securityId)
{
Relation pg_attribute;
CatalogIndexState indstate;
TupleDesc indexTupDesc;
int i;
+ /* copy security id */
+ securityId = seclabelMoveSecid(AttributeRelationId,
+ RelationRelationId, securityId);
/*
* open the attribute relation and its indexes
*/
@@ -367,7 +373,8 @@ AppendAttributeTuples(Relation indexRelation, int numatts)
Assert(indexTupDesc->attrs[i]->attnum == i + 1);
Assert(indexTupDesc->attrs[i]->attcacheoff == -1);
- InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], indstate);
+ InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i],
+ indstate, securityId);
}
CatalogCloseIndexes(indstate);
@@ -545,6 +552,7 @@ index_create(Oid heapRelationId,
bool mapped_relation;
bool is_exclusion;
Oid namespaceId;
+ Oid securityId;
int i;
is_exclusion = (indexInfo->ii_ExclusionOps != NULL);
@@ -682,15 +690,21 @@ index_create(Oid heapRelationId,
indexRelation->rd_rel->relam = accessMethodObjectId;
indexRelation->rd_rel->relkind = RELKIND_INDEX;
indexRelation->rd_rel->relhasoids = false;
+ indexRelation->rd_rel->relhassecids = false;
indexRelation->rd_rel->relhasexclusion = is_exclusion;
/*
+ * Index always has same security id of the relation to be indexed on.
+ */
+ securityId = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(heapRelationId));
+
+ /*
* store index's pg_class entry
*/
InsertPgClassTuple(pg_class, indexRelation,
RelationGetRelid(indexRelation),
(Datum) 0,
- reloptions);
+ reloptions, securityId);
/* done with pg_class */
heap_close(pg_class, RowExclusiveLock);
@@ -706,7 +720,7 @@ index_create(Oid heapRelationId,
/*
* append ATTRIBUTE tuples for the index
*/
- AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs);
+ AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs, securityId);
/* ----------------
* update pg_index
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 5581346..cf1910a 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -40,6 +40,7 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_func.h"
+#include "sepgsql/hooks.h"
#include "storage/backendid.h"
#include "storage/ipc.h"
#include "utils/acl.h"
@@ -2311,6 +2312,9 @@ LookupExplicitNamespace(const char *nspname)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
nspname);
+ /* SELinux checks */
+ sepgsql_schema_search(namespaceId, true);
+
return namespaceId;
}
@@ -2903,7 +2907,8 @@ recomputeNamespacePath(void)
if (OidIsValid(namespaceId) &&
!list_member_oid(oidlist, namespaceId) &&
pg_namespace_aclcheck(namespaceId, roleid,
- ACL_USAGE) == ACLCHECK_OK)
+ ACL_USAGE) == ACLCHECK_OK &&
+ sepgsql_schema_search(namespaceId, false))
oidlist = lappend_oid(oidlist, namespaceId);
}
}
@@ -2930,7 +2935,8 @@ recomputeNamespacePath(void)
if (OidIsValid(namespaceId) &&
!list_member_oid(oidlist, namespaceId) &&
pg_namespace_aclcheck(namespaceId, roleid,
- ACL_USAGE) == ACLCHECK_OK)
+ ACL_USAGE) == ACLCHECK_OK &&
+ sepgsql_schema_search(namespaceId, false))
oidlist = lappend_oid(oidlist, namespaceId);
}
}
@@ -2996,9 +3002,12 @@ InitTempTableNamespace(void)
char namespaceName[NAMEDATALEN];
Oid namespaceId;
Oid toastspaceId;
+ Oid secid;
Assert(!OidIsValid(myTempNamespace));
+ snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId);
+
/*
* First, do permission check to see if we are authorized to make temp
* tables. We use a nonstandard error message here since "databasename:
@@ -3016,6 +3025,9 @@ InitTempTableNamespace(void)
errmsg("permission denied to create temporary tables in database \"%s\"",
get_database_name(MyDatabaseId))));
+ /* SELinux checks */
+ secid = sepgsql_schema_create(namespaceName, true);
+
/*
* Do not allow a Hot Standby slave session to make temp tables. Aside
* from problems with modifying the system catalogs, there is a naming
@@ -3031,8 +3043,6 @@ InitTempTableNamespace(void)
(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
errmsg("cannot create temporary tables during recovery")));
- snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId);
-
namespaceId = GetSysCacheOid1(NAMESPACENAME,
CStringGetDatum(namespaceName));
if (!OidIsValid(namespaceId))
@@ -3045,7 +3055,9 @@ InitTempTableNamespace(void)
* temp tables. This works because the places that access the temp
* namespace for my own backend skip permissions checks on it.
*/
- namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID);
+ namespaceId = NamespaceCreate(namespaceName,
+ BOOTSTRAP_SUPERUSERID,
+ secid);
/* Advance command counter to make namespace visible */
CommandCounterIncrement();
}
@@ -3070,7 +3082,9 @@ InitTempTableNamespace(void)
CStringGetDatum(namespaceName));
if (!OidIsValid(toastspaceId))
{
- toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID);
+ toastspaceId = NamespaceCreate(namespaceName,
+ BOOTSTRAP_SUPERUSERID,
+ secid);
/* Advance command counter to make namespace visible */
CommandCounterIncrement();
}
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 582d894..d7b6bd1 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -27,6 +27,7 @@
#include "parser/parse_coerce.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
@@ -67,6 +68,7 @@ AggregateCreate(const char *aggName,
Oid *fnArgs;
int nargs_transfn;
Oid procOid;
+ Oid procSecid;
TupleDesc tupDesc;
int i;
ObjectAddress myself,
@@ -161,6 +163,10 @@ AggregateCreate(const char *aggName,
}
Assert(OidIsValid(finaltype));
+ /* SELinux checks */
+ procSecid = sepgsql_aggregate_create(aggName, aggNamespace,
+ transfn, finalfn);
+
/*
* If finaltype (i.e. aggregate return type) is polymorphic, inputs must
* be polymorphic also, else parser will fail to deduce result type.
@@ -229,7 +235,8 @@ AggregateCreate(const char *aggName,
NIL, /* parameterDefaults */
PointerGetDatum(NULL), /* proconfig */
1, /* procost */
- 0); /* prorows */
+ 0, /* prorows */
+ procSecid); /* security-id */
/*
* Okay to create the pg_aggregate entry.
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 99085c9..b4c0b3a 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -40,7 +40,7 @@ Oid
ConversionCreate(const char *conname, Oid connamespace,
Oid conowner,
int32 conforencoding, int32 contoencoding,
- Oid conproc, bool def)
+ Oid conproc, bool def, Oid securityId)
{
int i;
Relation rel;
@@ -104,6 +104,8 @@ ConversionCreate(const char *conname, Oid connamespace,
tup = heap_form_tuple(tupDesc, values, nulls);
+ HeapTupleSetSecid(tup, securityId);
+
/* insert a new tuple */
oid = simple_heap_insert(rel, tup);
Assert(OidIsValid(oid));
diff --git a/src/backend/catalog/pg_largeobject.c b/src/backend/catalog/pg_largeobject.c
index e3f18bf..572ce01 100644
--- a/src/backend/catalog/pg_largeobject.c
+++ b/src/backend/catalog/pg_largeobject.c
@@ -21,10 +21,13 @@
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_authid.h"
+#include "catalog/pg_description.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
+#include "catalog/pg_seclabel.h"
#include "catalog/toasting.h"
#include "miscadmin.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/bytea.h"
#include "utils/fmgroids.h"
@@ -40,7 +43,7 @@
* will appear to exist with size 0.
*/
Oid
-LargeObjectCreate(Oid loid)
+LargeObjectCreate(Oid loid, Oid securityId)
{
Relation pg_lo_meta;
HeapTuple ntup;
@@ -66,6 +69,8 @@ LargeObjectCreate(Oid loid)
if (OidIsValid(loid))
HeapTupleSetOid(ntup, loid);
+ HeapTupleSetSecid(ntup, securityId);
+
loid_new = simple_heap_insert(pg_lo_meta, ntup);
Assert(!OidIsValid(loid) || loid == loid_new);
@@ -245,6 +250,64 @@ LargeObjectAlterOwner(Oid loid, Oid newOwnerId)
}
/*
+ * LargeObjectAlterSecLabel
+ *
+ * Implementation of ALTER LARGE OBJECT xxx SECURITY LABEL
+ */
+void
+LargeObjectAlterSecLabel(Oid loid, char *new_label)
+{
+ Relation pg_lo_meta;
+ ScanKeyData skey;
+ SysScanDesc scan;
+ HeapTuple oldtup;
+ HeapTuple newtup;
+ Oid securityId;
+
+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
+ RowExclusiveLock);
+
+ ScanKeyInit(&skey,
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(loid));
+
+ scan = systable_beginscan(pg_lo_meta,
+ LargeObjectMetadataOidIndexId, true,
+ SnapshotNow, 1, &skey);
+
+ oldtup = systable_getnext(scan);
+ if (!HeapTupleIsValid(oldtup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("large object %u does not exist", loid)));
+
+ /* Must be owner of the large object */
+ if (!pg_largeobject_ownercheck(loid, GetUserId()))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be owner of large object %u", loid)));
+
+ /* SELinux checks */
+ securityId = sepgsql_largeobject_relabel(loid, new_label);
+
+ /* update the tuple */
+ newtup = heap_copytuple(oldtup);
+
+ HeapTupleSetSecid(newtup, securityId);
+
+ simple_heap_update(pg_lo_meta, &newtup->t_self, newtup);
+
+ CatalogUpdateIndexes(pg_lo_meta, newtup);
+
+ heap_freetuple(newtup);
+
+ systable_endscan(scan);
+
+ heap_close(pg_lo_meta, RowExclusiveLock);
+}
+
+/*
* LargeObjectExists
*
* We don't use the system cache to for large object metadata, for fear of
diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c
index 22111a3..cba7274 100644
--- a/src/backend/catalog/pg_namespace.c
+++ b/src/backend/catalog/pg_namespace.c
@@ -28,7 +28,7 @@
* ---------------
*/
Oid
-NamespaceCreate(const char *nspName, Oid ownerId)
+NamespaceCreate(const char *nspName, Oid ownerId, Oid secid)
{
Relation nspdesc;
HeapTuple tup;
@@ -64,6 +64,8 @@ NamespaceCreate(const char *nspName, Oid ownerId)
tupDesc = nspdesc->rd_att;
tup = heap_form_tuple(tupDesc, values, nulls);
+ if (HeapTupleHasSecid(tup))
+ HeapTupleSetSecid(tup, secid);
nspoid = simple_heap_insert(nspdesc, tup);
Assert(OidIsValid(nspoid));
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 2362268..f6df55e 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -28,6 +28,7 @@
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "parser/parse_oper.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
@@ -204,6 +205,7 @@ OperatorShellMake(const char *operatorName,
{
Relation pg_operator_desc;
Oid operatorObjectId;
+ Oid securityId;
int i;
HeapTuple tup;
Datum values[Natts_pg_operator];
@@ -220,6 +222,12 @@ OperatorShellMake(const char *operatorName,
errmsg("\"%s\" is not a valid operator name",
operatorName)));
+ /* SELinux checks */
+ securityId = sepgsql_operator_create(operatorName, InvalidOid,
+ operatorNamespace,
+ InvalidOid, InvalidOid, InvalidOid,
+ InvalidOid, InvalidOid);
+
/*
* initialize our *nulls and *values arrays
*/
@@ -261,6 +269,8 @@ OperatorShellMake(const char *operatorName,
*/
tup = heap_form_tuple(tupDesc, values, nulls);
+ HeapTupleSetSecid(tup, securityId);
+
/*
* insert our "shell" operator tuple
*/
@@ -340,6 +350,7 @@ OperatorCreate(const char *operatorName,
bool replaces[Natts_pg_operator];
Datum values[Natts_pg_operator];
Oid operatorObjectId;
+ Oid securityId;
bool operatorAlreadyDefined;
Oid operResultType;
Oid commutatorId,
@@ -476,6 +487,12 @@ OperatorCreate(const char *operatorName,
else
negatorId = InvalidOid;
+ /* SELinux checks */
+ securityId = sepgsql_operator_create(operatorName, operatorObjectId,
+ operatorNamespace,
+ procedureId, restrictionId, joinId,
+ commutatorId, negatorId);
+
/*
* set up values in the operator tuple
*/
@@ -516,6 +533,8 @@ OperatorCreate(const char *operatorName,
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for operator %u",
operatorObjectId);
+ if (securityId != HeapTupleHasSecid(tup))
+ elog(ERROR, "Bug? security-id was mismatched.");
tup = heap_modify_tuple(tup,
RelationGetDescr(pg_operator_desc),
@@ -530,6 +549,8 @@ OperatorCreate(const char *operatorName,
tupDesc = pg_operator_desc->rd_att;
tup = heap_form_tuple(tupDesc, values, nulls);
+ HeapTupleSetSecid(tup, securityId);
+
operatorObjectId = simple_heap_insert(pg_operator_desc, tup);
}
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index d76e415..f4c74a3 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -84,7 +84,8 @@ ProcedureCreate(const char *procedureName,
List *parameterDefaults,
Datum proconfig,
float4 procost,
- float4 prorows)
+ float4 prorows,
+ Oid prosecid)
{
Oid retval;
int parameterCount;
@@ -364,6 +365,8 @@ ProcedureCreate(const char *procedureName,
if (!pg_proc_ownercheck(HeapTupleGetOid(oldtup), proowner))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
procedureName);
+ if (prosecid != HeapTupleGetSecid(oldtup))
+ elog(ERROR, "Bug? security-id was tried to be changed.");
/*
* Not okay to change the return type of the existing proc, since
@@ -548,6 +551,9 @@ ProcedureCreate(const char *procedureName,
nulls[Anum_pg_proc_proacl - 1] = true;
tup = heap_form_tuple(tupDesc, values, nulls);
+
+ HeapTupleSetSecid(tup, prosecid);
+
simple_heap_insert(rel, tup);
is_update = false;
}
diff --git a/src/backend/catalog/pg_seclabel.c b/src/backend/catalog/pg_seclabel.c
new file mode 100644
index 0000000..4816635
--- /dev/null
+++ b/src/backend/catalog/pg_seclabel.c
@@ -0,0 +1,646 @@
+/*
+ * pg_seclabel.c
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/sysattr.h"
+#include "access/tupdesc.h"
+#include "catalog/catalog.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_aggregate.h"
+#include "catalog/pg_amop.h"
+#include "catalog/pg_amproc.h"
+#include "catalog/pg_attrdef.h"
+#include "catalog/pg_auth_members.h"
+#include "catalog/pg_constraint.h"
+#include "catalog/pg_db_role_setting.h"
+#include "catalog/pg_depend.h"
+#include "catalog/pg_description.h"
+#include "catalog/pg_enum.h"
+#include "catalog/pg_index.h"
+#include "catalog/pg_inherits.h"
+#include "catalog/pg_largeobject.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_rewrite.h"
+#include "catalog/pg_seclabel.h"
+#include "catalog/pg_shdepend.h"
+#include "catalog/pg_shdescription.h"
+#include "catalog/pg_trigger.h"
+#include "catalog/pg_ts_config_map.h"
+#include "executor/spi.h"
+#include "miscadmin.h"
+#include "sepgsql/sepgsql.h"
+#include "sepgsql/hooks.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+#include "utils/tqual.h"
+
+bool ignore_security_label_input;
+
+void
+seclabelOnCreateDatabase(Oid src_datid, Oid dst_datid)
+{
+ Relation rel;
+ ScanKeyData keys[1];
+ SysScanDesc scan;
+ HeapTuple oldtup, newtup;
+ Datum values[Natts_pg_seclabel];
+ bool nulls[Natts_pg_seclabel];
+ bool replaces[Natts_pg_seclabel];
+
+ /* Scan all entries with pg_seclabel.datid = src_datid */
+ ScanKeyInit(&keys[0],
+ Anum_pg_seclabel_datid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(src_datid));
+
+ rel = heap_open(SecLabelRelationId, RowExclusiveLock);
+
+ scan = systable_beginscan(rel, SecLabelSecidIndexId, true,
+ SnapshotNow, 1, keys);
+
+ /* corresponding entries will be inserted with new datid */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+ memset(replaces, false, sizeof(replaces));
+
+ values[Anum_pg_seclabel_datid - 1] = ObjectIdGetDatum(dst_datid);
+ replaces[Anum_pg_seclabel_datid - 1] = true;
+ while (HeapTupleIsValid(oldtup = systable_getnext(scan)))
+ {
+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
+ values, nulls, replaces);
+ simple_heap_insert(rel, newtup);
+
+ CatalogUpdateIndexes(rel, newtup);
+
+ heap_freetuple(newtup);
+ }
+ systable_endscan(scan);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+void
+seclabelOnDropDatabase(Oid datid)
+{
+ Relation rel;
+ ScanKeyData keys[1];
+ SysScanDesc scan;
+ HeapTuple tuple;
+
+ /* Scan all entries with pg_seclabel.datid = datid */
+ ScanKeyInit(&keys[0],
+ Anum_pg_seclabel_datid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(datid));
+
+ rel = heap_open(SecLabelRelationId, RowExclusiveLock);
+
+ scan = systable_beginscan(rel, SecLabelSecidIndexId, true,
+ SnapshotNow, 1, keys);
+
+ while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+ {
+ simple_heap_delete(rel, &tuple->t_self);
+ }
+
+ systable_endscan(scan);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+void
+seclabelOnDropTable(Oid relid)
+{
+ Relation rel;
+ SysScanDesc scan;
+ ScanKeyData key[2];
+ HeapTuple tuple;
+ Oid database_oid;
+
+ database_oid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId);
+ ScanKeyInit(&key[0],
+ Anum_pg_seclabel_datid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(database_oid));
+ ScanKeyInit(&key[1],
+ Anum_pg_seclabel_relid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(relid));
+
+ rel = heap_open(SecLabelRelationId, RowExclusiveLock);
+ scan = systable_beginscan(rel, SecLabelLabelIndexId, true,
+ SnapshotNow, 2, key);
+
+ while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+ simple_heap_delete(rel, &tuple->t_self);
+
+ systable_endscan(scan);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+/*
+ * XXX - it should be replaced by BKI scripts
+ */
+bool
+seclabelCatalogHasSysAttr(Oid relOid)
+{
+ switch (relOid)
+ {
+ case AggregateRelationId:
+ /* pg_aggregate is property of pg_proc */
+ case AccessMethodOperatorRelationId:
+ /* pg_amop is property of pg_opfamily */
+ case AccessMethodProcedureRelationId:
+ /* pg_amproc is property of pg_opfamily */
+ case AttrDefaultRelationId:
+ /* pg_attrdef is property of pg_attribute */
+ case AuthMemRelationId:
+ /* pg_auth_members is property of pg_auth */
+ case ConstraintRelationId:
+ /*
+ * CHECK constraint is property of pg_class
+ * DOMAIN constraint is property of pg_type
+ * Global assertion is property of pg_database
+ */
+ case DbRoleSettingRelationId:
+ /* pg_db_role_setting is property of pg_auth or pg_database */
+ case DependRelationId:
+ /* property of the depending object */
+ case DescriptionRelationId:
+ /* property of the object commented on */
+ case EnumRelationId:
+ /* pg_enum is property of pg_type */
+ case IndexRelationId:
+ /* pg_index is property of pg_class with RELKIND_INDEX */
+ case InheritsRelationId:
+ /* pg_inherits is property of the child relation */
+ case LargeObjectRelationId:
+ /* pg_largeobject is data chunk of pg_largeobject_metadata */
+ case RewriteRelationId:
+ /* pg_rewrite is property of pg_class */
+ case SecLabelRelationId:
+ /* No security attribute has no security label */
+ case SharedDependRelationId:
+ /* property of the depending shared object */
+ case SharedDescriptionRelationId:
+ /* property of the shared object commented on */
+ case TriggerRelationId:
+ /* pg_trigger is property of pg_class */
+ case TSConfigMapRelationId:
+ /* pg_ts_config_map is property of pg_ts_config */
+ return false;
+
+ default:
+ return true;
+ }
+}
+
+Oid *
+seclabelMakeRelationDefaults(TupleDesc tupdesc, List *supOids)
+{
+ ListCell *l;
+ Oid *secLabels;
+ Oid securityId;
+ int index, attno, nitems;
+
+ nitems = tupdesc->natts - FirstLowInvalidHeapAttributeNumber;
+ secLabels = palloc0(sizeof(Oid) * nitems);
+
+ foreach (l, supOids)
+ {
+ Oid relOid = lfirst_oid(l);
+
+ securityId = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid));
+
+ if (!OidIsValid(secLabels[0]))
+ secLabels[0] = securityId;
+ else if (!seclabelCompareSecid(RelationRelationId, secLabels[0],
+ RelationRelationId, securityId))
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot inherit relations with different label")));
+
+ for (index = 1; index < nitems; index++)
+ {
+ attno = index + FirstLowInvalidHeapAttributeNumber;
+
+ if (attno < 0)
+ securityId = GetSysCacheSecid2(ATTNUM,
+ ObjectIdGetDatum(relOid),
+ Int16GetDatum(attno));
+ else
+ {
+ const char *attname = NameStr(tupdesc->attrs[attno]->attname);
+ securityId = GetSysCacheSecid2(ATTNAME,
+ ObjectIdGetDatum(relOid),
+ PointerGetDatum(attname));
+ }
+
+ if (!OidIsValid(securityId))
+ continue;
+
+ if (!OidIsValid(secLabels[index]))
+ secLabels[index] = securityId;
+ else if (!seclabelCompareSecid(AttributeRelationId, secLabels[index],
+ AttributeRelationId, securityId))
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot inherit attribute with different label")));
+ }
+ }
+ return secLabels;
+}
+
+Oid *
+seclabelMakeToastDefaults(TupleDesc tupdesc, Oid relOid)
+{
+ Oid *secLabels;
+ Oid securityId;
+ int index, nitems;
+
+ nitems = tupdesc->natts + 1 - FirstLowInvalidHeapAttributeNumber;
+ secLabels = palloc0(sizeof(Oid) * nitems);
+
+ securityId = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid));
+ secLabels[0] = securityId;
+
+ securityId = seclabelMoveSecid(AttributeRelationId,
+ RelationRelationId,
+ securityId);
+ for (index = 1; index < nitems; index++)
+ secLabels[index] = securityId;
+
+ return secLabels;
+}
+
+Oid
+seclabelGetNewSecid(Relation rel, HeapTuple tuple)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled() &&
+ !IsBootstrapProcessingMode())
+ {
+ return sepgsql_get_default_secid(rel, tuple);
+ }
+#endif
+ return InvalidOid;
+}
+
+static Oid
+inputSecurityLabel(Oid relid, const char *seclabel)
+{
+ LOCKMODE lockmode = AccessShareLock;
+ Relation rel;
+ ScanKeyData skey[3];
+ SysScanDesc scan;
+ HeapTuple tuple;
+ Oid datid;
+ Oid secid;
+ Datum values[Natts_pg_seclabel];
+ bool nulls[Natts_pg_seclabel];
+
+ datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId);
+
+retry:
+ /*
+ * Lookup pg_seclabel first, then insert a new entry if not found.
+ * An exclusive lock is not necessary for the first read-only path,
+ * and we assume most of trials are read-only.
+ */
+ rel = heap_open(SecLabelRelationId, lockmode);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_seclabel_datid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(datid));
+ ScanKeyInit(&skey[1],
+ Anum_pg_seclabel_relid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(relid));
+ ScanKeyInit(&skey[2],
+ Anum_pg_seclabel_label,
+ BTEqualStrategyNumber, F_TEXTEQ,
+ CStringGetTextDatum(seclabel));
+
+ scan = systable_beginscan(rel, SecLabelLabelIndexId, true,
+ SnapshotToast, 3, skey);
+
+ tuple = systable_getnext(scan);
+ if (HeapTupleIsValid(tuple))
+ {
+ secid = ((Form_pg_seclabel) GETSTRUCT(tuple))->secid;
+ systable_endscan(scan);
+ heap_close(rel, lockmode);
+ return secid;
+ }
+
+ /*
+ * If not exist, try to insert a new entry.
+ */
+ if (lockmode == AccessShareLock)
+ {
+ systable_endscan(scan);
+ heap_close(rel, lockmode);
+ lockmode = RowExclusiveLock;
+ goto retry;
+ }
+
+ memset(nulls, false, sizeof(nulls));
+ secid = GetNewOidWithIndex(rel, SecLabelSecidIndexId,
+ Anum_pg_seclabel_secid);
+ values[Anum_pg_seclabel_secid - 1] = ObjectIdGetDatum(secid);
+ values[Anum_pg_seclabel_datid - 1] = ObjectIdGetDatum(datid);
+ values[Anum_pg_seclabel_relid - 1] = ObjectIdGetDatum(relid);
+ values[Anum_pg_seclabel_label - 1] = CStringGetTextDatum(seclabel);
+
+ tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
+
+ simple_heap_insert(rel, tuple);
+
+ CatalogUpdateIndexes(rel, tuple);
+
+ systable_endscan(scan);
+
+ heap_close(rel, lockmode);
+
+ return secid;
+}
+
+static char *
+outputSecurityLabel(Oid relid, Oid secid)
+{
+ Relation rel;
+ ScanKeyData skey[3];
+ SysScanDesc scan;
+ HeapTuple tuple;
+ Oid datid;
+ char *result = NULL;
+
+ datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId);
+
+ /*
+ * Lookup pg_seclabel for the given datid/relid/secid
+ */
+ rel = heap_open(SecLabelRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_seclabel_secid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(secid));
+ ScanKeyInit(&skey[1],
+ Anum_pg_seclabel_datid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(datid));
+ ScanKeyInit(&skey[2],
+ Anum_pg_seclabel_relid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(relid));
+
+ scan = systable_beginscan(rel, SecLabelSecidIndexId, true,
+ SnapshotToast, 3, skey);
+ tuple = systable_getnext(scan);
+ if (HeapTupleIsValid(tuple))
+ {
+ Datum datum;
+ bool isnull;
+
+ datum = heap_getattr(tuple,
+ Anum_pg_seclabel_label,
+ RelationGetDescr(rel), &isnull);
+ if (!isnull)
+ result = TextDatumGetCString(datum);
+ }
+ systable_endscan(scan);
+
+ heap_close(rel, AccessShareLock);
+
+ return result;
+}
+
+Oid
+seclabelRawInput(Oid relid, char *seclabel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ seclabel = sepgsql_rawlabel_in(seclabel);
+#endif
+ return inputSecurityLabel(relid, seclabel);
+}
+
+char *
+seclabelRawOutput(Oid relid, Oid secid)
+{
+ char *seclabel = outputSecurityLabel(relid, secid);
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ seclabel = sepgsql_rawlabel_out(seclabel);
+#endif
+ return seclabel;
+}
+
+Oid
+seclabelTransInput(Oid relid, char *seclabel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ seclabel = sepgsql_mcstrans_in(seclabel);
+#endif
+ return seclabelRawInput(relid, seclabel);
+}
+
+char *
+seclabelTransOutput(Oid relid, Oid secid)
+{
+ char *seclabel = seclabelRawOutput(relid, secid);
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ seclabel = sepgsql_mcstrans_out(seclabel);
+#endif
+ return seclabel;
+}
+
+Oid
+seclabelMoveSecid(Oid dst_relid, Oid src_relid, Oid secid)
+{
+ char *label = seclabelRawOutput(src_relid, secid);
+
+ if (!label)
+ return InvalidOid;
+
+ secid = seclabelRawInput(dst_relid, label);
+
+ pfree(label);
+
+ return secid;
+}
+
+bool
+seclabelCompareSecid(Oid relid1, Oid secid1, Oid relid2, Oid secid2)
+{
+ char *label1 = seclabelRawOutput(relid1, secid1);
+ char *label2 = seclabelRawOutput(relid2, secid2);
+ bool retval = false;
+
+ if (label1 && label2 && strcmp(label1, label2) == 0)
+ retval = true;
+ else if (!label1 && !label2)
+ retval = false;
+
+ if (label1)
+ pfree(label1);
+ if (label2)
+ pfree(label2);
+
+ return retval;
+}
+
+Datum
+seclabelSysattOutput(Oid relid, HeapTuple tuple)
+{
+ Oid secid;
+ char *seclabel;
+
+ secid = HeapTupleGetSecid(tuple);
+
+ seclabel = seclabelTransOutput(relid, secid);
+ if (!seclabel)
+ seclabel = "unlabeled";
+
+ return CStringGetTextDatum(seclabel);
+}
+
+/*
+ * seclabelRelationReclaim
+ *
+ * It reclaims security labels already referenced to.
+ * It has to be called under the VACUUM FULL context that means the relation
+ * to be reclaimed is already locked exclusively.
+ */
+void
+seclabelRelationReclaim(Oid relOid)
+{
+ StringInfoData query;
+ const char *nspname_reclaimed;
+ const char *relname_reclaimed;
+ const char *nspname_pg_seclabel;
+ const char *relname_pg_seclabel;
+ const char *attname_secid;
+ const char *attname_datid;
+ const char *attname_relid;
+ const char *attname_label;
+ const char *nspname_to_secid;
+ const char *proname_to_secid;
+ Oid databaseId;
+ Oid namespaceId;
+ int index;
+ int save_sepgsql_mode;
+
+ if (SPI_connect() != SPI_OK_CONNECT)
+ elog(ERROR, "SPI_connect() failed");
+
+ /*
+ * DELETE orphan entries
+ */
+ databaseId = (IsSharedRelation(relOid) ? InvalidOid : MyDatabaseId);
+
+ namespaceId = get_rel_namespace(relOid);
+ nspname_reclaimed = get_namespace_name(namespaceId);
+ relname_reclaimed = get_rel_name(relOid);
+
+ namespaceId = get_rel_namespace(SecLabelRelationId);
+ nspname_pg_seclabel = get_namespace_name(namespaceId);
+ relname_pg_seclabel = get_rel_name(SecLabelRelationId);
+
+ attname_secid = get_attname(SecLabelRelationId, Anum_pg_seclabel_secid);
+ attname_datid = get_attname(SecLabelRelationId, Anum_pg_seclabel_datid);
+ attname_relid = get_attname(SecLabelRelationId, Anum_pg_seclabel_relid);
+ attname_label = get_attname(SecLabelRelationId, Anum_pg_seclabel_label);
+
+ namespaceId = get_func_namespace(F_SECLABEL_TO_SECID);
+ nspname_to_secid = get_namespace_name(namespaceId);
+ proname_to_secid = get_func_name(F_SECLABEL_TO_SECID);
+
+ initStringInfo(&query);
+ appendStringInfo(&query,
+ "DELETE FROM %s.%s "
+ "WHERE %s = %u AND %s = %u AND %s NOT IN "
+ "(SELECT %s.%s(%s) FROM ONLY %s.%s) "
+ "RETURNING %s,%s",
+ quote_identifier(nspname_pg_seclabel),
+ quote_identifier(relname_pg_seclabel),
+ quote_identifier(attname_datid),
+ databaseId,
+ quote_identifier(attname_relid),
+ relOid,
+ quote_identifier(attname_secid),
+ quote_identifier(nspname_to_secid),
+ quote_identifier(proname_to_secid),
+ quote_identifier(relname_reclaimed),
+ quote_identifier(nspname_reclaimed),
+ quote_identifier(relname_reclaimed),
+ quote_identifier(attname_secid),
+ quote_identifier(attname_label));
+ /*
+ * Run the query
+ */
+ elog(DEBUG1, "query: %s", query.data);
+
+ save_sepgsql_mode = sepostgresql_mode;
+
+ PG_TRY();
+ {
+ sepostgresql_mode = SEPGSQL_MODE_INTERNAL;
+
+ if (SPI_execute(query.data, false, 0) != SPI_OK_DELETE_RETURNING)
+ elog(ERROR, "Failed to run: %s", query.data);
+ }
+ PG_CATCH();
+ {
+ sepostgresql_mode = save_sepgsql_mode;
+
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ sepostgresql_mode = save_sepgsql_mode;
+
+ for (index = 0; index < SPI_processed; index++)
+ {
+ HeapTuple tuple = SPI_tuptable->vals[index];
+ char *label;
+ Oid secid;
+ Datum datum;
+ bool isnull;
+
+ datum = heap_getattr(tuple, 1, SPI_tuptable->tupdesc, &isnull);
+ secid = (isnull ? InvalidOid : DatumGetObjectId(datum));
+
+ datum = heap_getattr(tuple, 2, SPI_tuptable->tupdesc, &isnull);
+ label = (isnull ? NULL : TextDatumGetCString(datum));
+
+ elog(DEBUG1, "seclabel: \"%s\"was reclaimed (secid=%u)",
+ label, secid);
+ }
+
+ if (SPI_finish() != SPI_OK_FINISH)
+ elog(ERROR, "SPI_finish() failed");
+}
+
+Datum
+seclabel_to_secid(PG_FUNCTION_ARGS)
+{
+ HeapTupleHeader htup = PG_GETARG_HEAPTUPLEHEADER(0);
+
+ PG_RETURN_OID(HeapTupleHeaderGetSecid(htup));
+}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 76f9e06..0b4dcc3 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -25,6 +25,7 @@
#include "commands/typecmds.h"
#include "miscadmin.h"
#include "parser/scansup.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@@ -57,10 +58,17 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
Datum values[Natts_pg_type];
bool nulls[Natts_pg_type];
Oid typoid;
+ Oid secid;
NameData name;
Assert(PointerIsValid(typeName));
+ /* SELinux checks */
+ secid = sepgsql_type_create(typeName, InvalidOid,
+ typeNamespace, TYPTYPE_PSEUDO,
+ F_SHELL_IN, F_SHELL_OUT,
+ InvalidOid, InvalidOid,
+ InvalidOid, InvalidOid, InvalidOid);
/*
* open pg_type
*/
@@ -126,6 +134,8 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
binary_upgrade_next_pg_type_oid = InvalidOid;
}
+ HeapTupleSetSecid(tup, secid);
+
/*
* insert the tuple in the relation and get the tuple's oid.
*/
@@ -204,7 +214,8 @@ TypeCreate(Oid newTypeOid,
char storage,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
- bool typeNotNull)
+ bool typeNotNull,
+ Oid securityId)
{
Relation pg_type_desc;
Oid typeObjectId;
@@ -389,6 +400,8 @@ TypeCreate(Oid newTypeOid,
*/
if (((Form_pg_type) GETSTRUCT(tup))->typowner != ownerId)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, typeName);
+ if (HeapTupleGetSecid(tup) != securityId)
+ elog(ERROR, "Bug? security-id was mismatched");
/* trouble if caller wanted to force the OID */
if (OidIsValid(newTypeOid))
@@ -425,6 +438,8 @@ TypeCreate(Oid newTypeOid,
}
/* else allow system to assign oid */
+ HeapTupleSetSecid(tup, securityId);
+
typeObjectId = simple_heap_insert(pg_type_desc, tup);
}
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 435dfdd..5d86354 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -24,6 +24,7 @@
#include "catalog/namespace.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
+#include "catalog/pg_seclabel.h"
#include "catalog/pg_type.h"
#include "catalog/toasting.h"
#include "miscadmin.h"
@@ -127,6 +128,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
int16 coloptions[2];
ObjectAddress baseobject,
toastobject;
+ Oid *secLabels;
/*
* Toast table is shared if and only if its parent is.
@@ -168,7 +170,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
"pg_toast_%u_index", relOid);
/* this is pretty painful... need a tuple descriptor */
- tupdesc = CreateTemplateTupleDesc(3, false);
+ tupdesc = CreateTemplateTupleDesc(3, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1,
"chunk_id",
OIDOID,
@@ -206,6 +208,11 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
binary_upgrade_next_pg_type_toast_oid = InvalidOid;
}
+ /*
+ * Toast inherits security-id from the heap relation
+ */
+ secLabels = seclabelMakeToastDefaults(tupdesc, relOid);
+
toast_relid = heap_create_with_catalog(toast_relname,
namespaceid,
rel->rd_rel->reltablespace,
@@ -223,7 +230,8 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
ONCOMMIT_NOOP,
reloptions,
false,
- true);
+ true,
+ secLabels);
/* make the toast relation visible, else index creation will fail */
CommandCounterIncrement();
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index 4964fb3..390a1c1 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -32,6 +32,7 @@
#include "miscadmin.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
@@ -307,6 +308,9 @@ RenameAggregate(List *name, List *args, const char *newname)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
+ /* SELinux checks */
+ sepgsql_proc_alter_rename(procOid, newname);
+
/* rename */
namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname);
simple_heap_update(rel, &tup->t_self, tup);
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 17e1e77..c1c8bfd 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -289,3 +289,64 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
(int) stmt->objectType);
}
}
+
+/*
+ * ExecAlterSecLabelStmt
+ *
+ * Execute ALTER xxx SECURITY LABEL TO statement
+ */
+void
+ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt)
+{
+ const char *name;
+ char *seclabel;
+
+ Assert(IsA(stmt->secLabel, String));
+ seclabel = strVal(stmt->secLabel);
+
+ switch (stmt->objectType)
+ {
+ case OBJECT_DATABASE:
+ name = strVal(linitial(stmt->object));
+ AlterDatabaseSecLabel(name, seclabel);
+ break;
+
+ case OBJECT_SCHEMA:
+ name = strVal(linitial(stmt->object));
+ AlterSchemaSecLabel(name, seclabel);
+ break;
+
+ case OBJECT_TABLE:
+ case OBJECT_SEQUENCE:
+ case OBJECT_VIEW:
+ case OBJECT_COLUMN:
+ AlterRelationSecLabel(stmt->relation, stmt->addname,
+ stmt->objectType, seclabel);
+ break;
+
+ case OBJECT_AGGREGATE:
+ AlterFunctionSecLabel(stmt->object, stmt->objarg, true, seclabel);
+ break;
+
+ case OBJECT_FUNCTION:
+ AlterFunctionSecLabel(stmt->object, stmt->objarg, false, seclabel);
+ break;
+
+ case OBJECT_LARGEOBJECT:
+ LargeObjectAlterSecLabel(intVal(linitial(stmt->object)), seclabel);
+ break;
+
+ case OBJECT_TYPE:
+ case OBJECT_DOMAIN:
+ AlterTypeSecLabel(stmt->object, seclabel);
+ break;
+
+ case OBJECT_TABLESPACE:
+ AlterTableSpaceSecLabel(strVal(linitial(stmt->object)), seclabel);
+ break;
+
+ default:
+ elog(ERROR, "unrecognized AlterSecLabelStmt type: %d",
+ (int) stmt->objectType);
+ }
+}
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index ccb4599..394740a 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -30,12 +30,14 @@
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_namespace.h"
+#include "catalog/pg_seclabel.h"
#include "catalog/toasting.h"
#include "commands/cluster.h"
#include "commands/tablecmds.h"
#include "commands/trigger.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
+#include "sepgsql/hooks.h"
#include "storage/bufmgr.h"
#include "storage/procarray.h"
#include "storage/smgr.h"
@@ -116,6 +118,9 @@ cluster(ClusterStmt *stmt, bool isTopLevel)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
RelationGetRelationName(rel));
+ /* SELinux checks */
+ sepgsql_relation_cluster(tableOid, true);
+
/*
* Reject clustering a remote temp table ... their local buffer
* manager is not going to cope.
@@ -290,7 +295,8 @@ cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose,
Form_pg_index indexForm;
/* Check that the user still owns the relation */
- if (!pg_class_ownercheck(tableOid, GetUserId()))
+ if (!pg_class_ownercheck(tableOid, GetUserId()) ||
+ !sepgsql_relation_cluster(tableOid, false))
{
relation_close(OldHeap, AccessExclusiveLock);
return;
@@ -632,6 +638,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
HeapTuple tuple;
Datum reloptions;
bool isNull;
+ Oid *secLabels;
OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock);
OldHeapDesc = RelationGetDescr(OldHeap);
@@ -657,6 +664,11 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
reloptions = (Datum) 0;
/*
+ * The new heap copies all the security-id from the original
+ */
+ secLabels = seclabelMakeRelationDefaults(tupdesc,
+ list_make1_oid(OIDOldHeap));
+ /*
* Create the new heap, using a temporary name in the same namespace as
* the existing table. NOTE: there is some risk of collision with user
* relnames. Working around this seems more trouble than it's worth; in
@@ -687,7 +699,8 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
ONCOMMIT_NOOP,
reloptions,
false,
- true);
+ true,
+ secLabels);
ReleaseSysCache(tuple);
@@ -977,6 +990,9 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex,
/* Preserve OID, if any */
if (NewHeap->rd_rel->relhasoids)
HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple));
+ /* Preserve security-id, if any */
+ if (NewHeap->rd_rel->relhassecids)
+ HeapTupleSetSecid(copiedTuple, HeapTupleGetSecid(tuple));
/* The heap rewrite module does the rest */
rewrite_heap_tuple(rwstate, tuple, copiedTuple);
@@ -1464,7 +1480,8 @@ get_tables_to_cluster(MemoryContext cluster_context)
{
index = (Form_pg_index) GETSTRUCT(indexTuple);
- if (!pg_class_ownercheck(index->indrelid, GetUserId()))
+ if (!pg_class_ownercheck(index->indrelid, GetUserId()) ||
+ !sepgsql_relation_cluster(index->indrelid, false))
continue;
/*
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index 64792f2..6d71642 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -49,6 +49,7 @@
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "parser/parse_type.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@@ -548,6 +549,9 @@ CommentRelation(int objtype, List *relname, char *comment)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
RelationGetRelationName(relation));
+ /* SELinux checks */
+ sepgsql_relation_comment(RelationGetRelid(relation));
+
/* Next, verify that the relation type matches the intent */
switch (objtype)
@@ -651,6 +655,9 @@ CommentAttribute(List *qualname, char *comment)
errmsg("column \"%s\" of relation \"%s\" does not exist",
attrname, RelationGetRelationName(relation))));
+ /* SELinux checks */
+ sepgsql_attribute_comment(RelationGetRelid(relation), attnum);
+
/* Create the comment using the relation's oid */
CreateComments(RelationGetRelid(relation), RelationRelationId,
(int32) attnum, comment);
@@ -704,6 +711,9 @@ CommentDatabase(List *qualname, char *comment)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
database);
+ /* SELinux checks */
+ sepgsql_database_comment(oid);
+
/* Call CreateSharedComments() to create/drop the comments */
CreateSharedComments(oid, DatabaseRelationId, comment);
}
@@ -742,6 +752,9 @@ CommentTablespace(List *qualname, char *comment)
if (!pg_tablespace_ownercheck(oid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, tablespace);
+ /* SELinux checks */
+ sepgsql_tablespace_comment(oid);
+
/* Call CreateSharedComments() to create/drop the comments */
CreateSharedComments(oid, TableSpaceRelationId, comment);
}
@@ -774,6 +787,9 @@ CommentRole(List *qualname, char *comment)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be member of role \"%s\" to comment upon it", role)));
+ /* SELinux checks */
+ sepgsql_role_comment(oid);
+
/* Call CreateSharedComments() to create/drop the comments */
CreateSharedComments(oid, AuthIdRelationId, comment);
}
@@ -810,6 +826,9 @@ CommentNamespace(List *qualname, char *comment)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
namespace);
+ /* SELinux checks */
+ sepgsql_schema_comment(oid);
+
/* Call CreateComments() to create/drop the comments */
CreateComments(oid, NamespaceRelationId, 0, comment);
}
@@ -919,6 +938,9 @@ CommentRule(List *qualname, char *comment)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
get_rel_name(reloid));
+ /* SELinux checks */
+ sepgsql_rule_comment(reloid, rulename);
+
/* Call CreateComments() to create/drop the comments */
CreateComments(ruleoid, RewriteRelationId, 0, comment);
@@ -953,6 +975,9 @@ CommentType(List *typename, char *comment)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
format_type_be(oid));
+ /* SELinux checks */
+ sepgsql_type_comment(oid);
+
/* Call CreateComments() to create/drop the comments */
CreateComments(oid, TypeRelationId, 0, comment);
}
@@ -977,6 +1002,9 @@ CommentAggregate(List *aggregate, List *arguments, char *comment)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(aggregate));
+ /* SELinux checks */
+ sepgsql_proc_comment(oid);
+
/* Call CreateComments() to create/drop the comments */
CreateComments(oid, ProcedureRelationId, 0, comment);
}
@@ -1005,6 +1033,9 @@ CommentProc(List *function, List *arguments, char *comment)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(function));
+ /* SELinux checks */
+ sepgsql_proc_comment(oid);
+
/* Call CreateComments() to create/drop the comments */
CreateComments(oid, ProcedureRelationId, 0, comment);
}
@@ -1036,6 +1067,9 @@ CommentOperator(List *opername, List *arguments, char *comment)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
NameListToString(opername));
+ /* SELinux checks */
+ sepgsql_operator_comment(oid);
+
/* Call CreateComments() to create/drop the comments */
CreateComments(oid, OperatorRelationId, 0, comment);
}
@@ -1080,6 +1114,9 @@ CommentTrigger(List *qualname, char *comment)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
RelationGetRelationName(relation));
+ /* SELinux checks */
+ sepgsql_trigger_comment(RelationGetRelid(relation), trigname);
+
/*
* Fetch the trigger tuple from pg_trigger. There can be only one because
* of the unique index.
@@ -1153,6 +1190,9 @@ CommentConstraint(List *qualname, char *comment)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
RelationGetRelationName(relation));
+ /* SELinux checks */
+ sepgsql_constraint_comment(RelationGetRelid(relation), conName);
+
conOid = GetConstraintByName(RelationGetRelid(relation), conName);
/* Call CreateComments() to create/drop the comments */
@@ -1188,6 +1228,9 @@ CommentConversion(List *qualname, char *comment)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
NameListToString(qualname));
+ /* SELinux checks */
+ sepgsql_conversion_comment(conversionOid);
+
/* Call CreateComments() to create/drop the comments */
CreateComments(conversionOid, ConversionRelationId, 0, comment);
}
@@ -1304,6 +1347,9 @@ CommentOpClass(List *qualname, List *arguments, char *comment)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
NameListToString(qualname));
+ /* SELinux checks */
+ sepgsql_opclass_comment(opcID);
+
ReleaseSysCache(tuple);
/* Call CreateComments() to create/drop the comments */
@@ -1385,6 +1431,9 @@ CommentOpFamily(List *qualname, List *arguments, char *comment)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
NameListToString(qualname));
+ /* SELinux checks */
+ sepgsql_opfamily_comment(opfID);
+
ReleaseSysCache(tuple);
/* Call CreateComments() to create/drop the comments */
@@ -1505,6 +1554,9 @@ CommentCast(List *qualname, List *arguments, char *comment)
format_type_be(sourcetypeid),
format_type_be(targettypeid))));
+ /* SELinux checks */
+ sepgsql_cast_comment(sourcetypeid, targettypeid);
+
ReleaseSysCache(tuple);
/* Call CreateComments() to create/drop the comments */
@@ -1522,6 +1574,8 @@ CommentTSParser(List *qualname, char *comment)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to comment on text search parser")));
+ /* SELinux checks */
+ sepgsql_ts_parser_comment(prsId);
CreateComments(prsId, TSParserRelationId, 0, comment);
}
@@ -1536,6 +1590,8 @@ CommentTSDictionary(List *qualname, char *comment)
if (!pg_ts_dict_ownercheck(dictId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
NameListToString(qualname));
+ /* SELinux checks */
+ sepgsql_ts_dict_comment(dictId);
CreateComments(dictId, TSDictionaryRelationId, 0, comment);
}
@@ -1551,6 +1607,8 @@ CommentTSTemplate(List *qualname, char *comment)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to comment on text search template")));
+ /* SELinux checks */
+ sepgsql_ts_template_comment(tmplId);
CreateComments(tmplId, TSTemplateRelationId, 0, comment);
}
@@ -1565,6 +1623,8 @@ CommentTSConfiguration(List *qualname, char *comment)
if (!pg_ts_config_ownercheck(cfgId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION,
NameListToString(qualname));
+ /* SELinux checks */
+ sepgsql_ts_config_comment(cfgId);
CreateComments(cfgId, TSConfigRelationId, 0, comment);
}
diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c
index 57ddab0..0c10a64 100644
--- a/src/backend/commands/conversioncmds.c
+++ b/src/backend/commands/conversioncmds.c
@@ -24,6 +24,7 @@
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
@@ -45,6 +46,7 @@ CreateConversionCommand(CreateConversionStmt *stmt)
int from_encoding;
int to_encoding;
Oid funcoid;
+ Oid secid;
const char *from_encoding_name = stmt->for_encoding_name;
const char *to_encoding_name = stmt->to_encoding_name;
List *func_name = stmt->func_name;
@@ -96,6 +98,10 @@ CreateConversionCommand(CreateConversionStmt *stmt)
aclcheck_error(aclresult, ACL_KIND_PROC,
NameListToString(func_name));
+ /* SELinux checks */
+ secid = sepgsql_conversion_create(conversion_name,
+ namespaceId, funcoid);
+
/*
* Check that the conversion function is suitable for the requested source
* and target encodings. We do that by calling the function with an empty
@@ -114,7 +120,7 @@ CreateConversionCommand(CreateConversionStmt *stmt)
* name)
*/
ConversionCreate(conversion_name, namespaceId, GetUserId(),
- from_encoding, to_encoding, funcoid, stmt->def);
+ from_encoding, to_encoding, funcoid, stmt->def, secid);
}
/*
@@ -174,6 +180,9 @@ DropConversionsCommand(DropStmt *drop)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
NameStr(con->conname));
+ /* SELinux checks */
+ sepgsql_conversion_drop(conversionOid, false);
+
object.classId = ConversionRelationId;
object.objectId = conversionOid;
object.objectSubId = 0;
@@ -235,6 +244,9 @@ RenameConversion(List *name, const char *newname)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
+ /* SELinux checks */
+ sepgsql_conversion_alter_rename(conversionOid, newname);
+
/* rename */
namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname);
simple_heap_update(rel, &tup->t_self, tup);
@@ -329,6 +341,8 @@ AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(convForm->connamespace));
}
+ /* SELinux checks */
+ sepgsql_conversion_alter(HeapTupleGetOid(tup));
/*
* Modify the owner --- okay to scribble on tup because it's a copy
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 9d46e47..270eb74 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -22,7 +22,10 @@
#include "access/heapam.h"
#include "access/xact.h"
+#include "access/sysattr.h"
+#include "catalog/heap.h"
#include "catalog/namespace.h"
+#include "catalog/pg_seclabel.h"
#include "catalog/pg_type.h"
#include "commands/copy.h"
#include "commands/defrem.h"
@@ -35,6 +38,7 @@
#include "optimizer/planner.h"
#include "parser/parse_relation.h"
#include "rewrite/rewriteHandler.h"
+#include "sepgsql/hooks.h"
#include "storage/fd.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
@@ -161,6 +165,12 @@ typedef struct CopyStateData
char *raw_buf;
int raw_buf_index; /* next byte to process */
int raw_buf_len; /* total # of bytes stored */
+
+ /*
+ * Dump/Restore support for security_label
+ */
+ FmgrInfo seclabel_out_function;
+ bool seclabel_force_quot;
} CopyStateData;
typedef CopyStateData *CopyState;
@@ -244,7 +254,7 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0";
/* non-export function prototypes */
static void DoCopyTo(CopyState cstate);
static void CopyTo(CopyState cstate);
-static void CopyOneRowTo(CopyState cstate, Oid tupleOid,
+static void CopyOneRowTo(CopyState cstate, HeapTuple tuple,
Datum *values, bool *nulls);
static void CopyFrom(CopyState cstate);
static bool CopyReadLine(CopyState cstate);
@@ -986,8 +996,21 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
errhint("Anyone can COPY to stdout or from stdin. "
"psql's \\copy command also works for anyone.")));
+ /* SELinux checks on file COPY */
+ if (stmt->filename)
+ {
+ if (is_from)
+ sepgsql_file_read(stmt->filename);
+ else
+ sepgsql_file_write(stmt->filename, true);
+ }
+
if (stmt->relation)
{
+ Bitmapset *columnsSet = NULL;
+ List *attnums;
+ ListCell *cur;
+
Assert(!stmt->query);
cstate->queryDesc = NULL;
@@ -998,16 +1021,20 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
tupDesc = RelationGetDescr(cstate->rel);
/* Check relation permissions. */
+ attnums = CopyGetAttnums(tupDesc, cstate->rel, attnamelist);
+ foreach(cur, attnums)
+ {
+ int index = lfirst_int(cur)
+ - FirstLowInvalidHeapAttributeNumber;
+ columnsSet = bms_add_member(columnsSet, index);
+ }
+
relPerms = pg_class_aclmask(RelationGetRelid(cstate->rel), GetUserId(),
required_access, ACLMASK_ALL);
remainingPerms = required_access & ~relPerms;
if (remainingPerms != 0)
{
/* We don't have table permissions, check per-column permissions */
- List *attnums;
- ListCell *cur;
-
- attnums = CopyGetAttnums(tupDesc, cstate->rel, attnamelist);
foreach(cur, attnums)
{
int attnum = lfirst_int(cur);
@@ -1021,6 +1048,13 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
}
}
+ if (is_from)
+ sepgsql_relation_perms(RelationGetRelid(cstate->rel),
+ ACL_INSERT, NULL, columnsSet, true);
+ else
+ sepgsql_relation_perms(RelationGetRelid(cstate->rel),
+ ACL_SELECT, columnsSet, NULL, true);
+
/* check read-only transaction */
if (XactReadOnly && is_from && !cstate->rel->rd_islocaltemp)
PreventCommandIfReadOnly("COPY FROM");
@@ -1130,11 +1164,24 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
int attnum = lfirst_int(cur);
if (!list_member_int(cstate->attnumlist, attnum))
+ {
+ Form_pg_attribute attForm;
+
+ if (attnum > 0)
+ attForm = tupDesc->attrs[attnum - 1];
+ else
+ attForm = SystemAttributeDefinition(attnum, true, true);
+
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
errmsg("FORCE QUOTE column \"%s\" not referenced by COPY",
- NameStr(tupDesc->attrs[attnum - 1]->attname))));
- cstate->force_quote_flags[attnum - 1] = true;
+ NameStr(attForm->attname))));
+ }
+
+ if (attnum == SecurityLabelAttributeNumber)
+ cstate->seclabel_force_quot = true;
+ else
+ cstate->force_quote_flags[attnum - 1] = true;
}
}
@@ -1152,10 +1199,24 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
int attnum = lfirst_int(cur);
if (!list_member_int(cstate->attnumlist, attnum))
+ {
+ Form_pg_attribute attForm;
+
+ if (attnum > 0)
+ attForm = tupDesc->attrs[attnum - 1];
+ else
+ attForm = SystemAttributeDefinition(attnum,
+ tupDesc->tdhasoid,
+ tupDesc->tdhassecid);
+
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY",
- NameStr(tupDesc->attrs[attnum - 1]->attname))));
+ NameStr(attForm->attname))));
+ }
+ /* ignore system columns, if specified */
+ if (attnum <= 0)
+ continue;
cstate->force_notnull_flags[attnum - 1] = true;
}
}
@@ -1347,16 +1408,29 @@ CopyTo(CopyState cstate)
int attnum = lfirst_int(cur);
Oid out_func_oid;
bool isvarlena;
+ FmgrInfo *out_fmgr;
+ Form_pg_attribute attForm;
+
+ if (attnum == SecurityLabelAttributeNumber)
+ {
+ attForm = SystemAttributeDefinition(attnum, true, true);
+ out_fmgr = &cstate->seclabel_out_function;
+ }
+ else
+ {
+ attForm = attr[attnum - 1];
+ out_fmgr = &cstate->out_functions[attnum - 1];
+ }
if (cstate->binary)
- getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid,
+ getTypeBinaryOutputInfo(attForm->atttypid,
&out_func_oid,
&isvarlena);
else
- getTypeOutputInfo(attr[attnum - 1]->atttypid,
+ getTypeOutputInfo(attForm->atttypid,
&out_func_oid,
&isvarlena);
- fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]);
+ fmgr_info(out_func_oid, out_fmgr);
}
/*
@@ -1411,7 +1485,17 @@ CopyTo(CopyState cstate)
CopySendChar(cstate, cstate->delim[0]);
hdr_delim = true;
- colname = NameStr(attr[attnum - 1]->attname);
+ if (SystemAttributeWritable(attnum,
+ tupDesc->tdhasoid,
+ tupDesc->tdhassecid))
+ {
+ Form_pg_attribute attForm
+ = SystemAttributeDefinition(attnum, true, true);
+
+ colname = NameStr(attForm->attname);
+ }
+ else
+ colname = NameStr(attr[attnum - 1]->attname);
CopyAttributeOutCSV(cstate, colname, false,
list_length(cstate->attnumlist) == 1);
@@ -1437,11 +1521,15 @@ CopyTo(CopyState cstate)
{
CHECK_FOR_INTERRUPTS();
+ /* Row-level access control policy */
+ if (!sepgsql_rowlv_copyto(cstate->rel, tuple))
+ continue;
+
/* Deconstruct the tuple ... faster than repeated heap_getattr */
heap_deform_tuple(tuple, tupDesc, values, nulls);
/* Format and send the data */
- CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls);
+ CopyOneRowTo(cstate, tuple, values, nulls);
}
heap_endscan(scandesc);
@@ -1467,13 +1555,17 @@ CopyTo(CopyState cstate)
* Emit one row during CopyTo().
*/
static void
-CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls)
+CopyOneRowTo(CopyState cstate, HeapTuple tuple, Datum *values, bool *nulls)
{
bool need_delim = false;
FmgrInfo *out_functions = cstate->out_functions;
MemoryContext oldcontext;
ListCell *cur;
char *string;
+ Oid tupleOid = InvalidOid;
+
+ if (HeapTupleIsValid(tuple))
+ tupleOid = HeapTupleGetOid(tuple);
MemoryContextReset(cstate->rowcontext);
oldcontext = MemoryContextSwitchTo(cstate->rowcontext);
@@ -1506,8 +1598,10 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls)
foreach(cur, cstate->attnumlist)
{
int attnum = lfirst_int(cur);
- Datum value = values[attnum - 1];
- bool isnull = nulls[attnum - 1];
+ Datum value;
+ bool isnull;
+ bool force_quot;
+ FmgrInfo *out_fmgr;
if (!cstate->binary)
{
@@ -1516,6 +1610,21 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls)
need_delim = true;
}
+ if (attnum == SecurityLabelAttributeNumber)
+ {
+ value = seclabelSysattOutput(RelationGetRelid(cstate->rel), tuple);
+ isnull = false;
+ force_quot = cstate->seclabel_force_quot;
+ out_fmgr = &cstate->seclabel_out_function;
+ }
+ else
+ {
+ value = values[attnum - 1];
+ isnull = nulls[attnum - 1];
+ force_quot = cstate->force_quote_flags[attnum - 1];
+ out_fmgr = &out_functions[attnum - 1];
+ }
+
if (isnull)
{
if (!cstate->binary)
@@ -1527,11 +1636,9 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls)
{
if (!cstate->binary)
{
- string = OutputFunctionCall(&out_functions[attnum - 1],
- value);
+ string = OutputFunctionCall(out_fmgr, value);
if (cstate->csv_mode)
- CopyAttributeOutCSV(cstate, string,
- cstate->force_quote_flags[attnum - 1],
+ CopyAttributeOutCSV(cstate, string, force_quot,
list_length(cstate->attnumlist) == 1);
else
CopyAttributeOutText(cstate, string);
@@ -1540,8 +1647,7 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls)
{
bytea *outputbytes;
- outputbytes = SendFunctionCall(&out_functions[attnum - 1],
- value);
+ outputbytes = SendFunctionCall(out_fmgr, value);
CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ);
CopySendData(cstate, VARDATA(outputbytes),
VARSIZE(outputbytes) - VARHDRSZ);
@@ -1675,8 +1781,10 @@ CopyFrom(CopyState cstate)
num_defaults;
FmgrInfo *in_functions;
FmgrInfo oid_in_function;
+ FmgrInfo seclabel_in_function;
Oid *typioparams;
Oid oid_typioparam;
+ Oid seclabel_typioparam;
int attnum;
int i;
Oid in_func_oid;
@@ -1919,6 +2027,19 @@ CopyFrom(CopyState cstate)
fmgr_info(in_func_oid, &oid_in_function);
}
+ if (list_member_int(cstate->attnumlist, SecurityLabelAttributeNumber))
+ {
+ if (!cstate->binary)
+ getTypeInputInfo(TEXTOID, &in_func_oid, &seclabel_typioparam);
+ else
+ getTypeBinaryInputInfo(TEXTOID, &in_func_oid, &seclabel_typioparam);
+
+ fmgr_info(in_func_oid, &seclabel_in_function);
+ }
+
+
+
+
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
nulls = (bool *) palloc(num_phys_attrs * sizeof(bool));
@@ -1953,6 +2074,7 @@ CopyFrom(CopyState cstate)
{
bool skip_tuple;
Oid loaded_oid = InvalidOid;
+ Oid loaded_secid = InvalidOid;
CHECK_FOR_INTERRUPTS();
@@ -2024,14 +2146,20 @@ CopyFrom(CopyState cstate)
/* Loop to read the user attributes on the line. */
foreach(cur, cstate->attnumlist)
{
+ Form_pg_attribute attForm;
int attnum = lfirst_int(cur);
int m = attnum - 1;
+ if (attnum == SecurityLabelAttributeNumber)
+ attForm = SystemAttributeDefinition(attnum, true, true);
+ else
+ attForm = attr[m];
+
if (fieldno >= fldct)
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
errmsg("missing data for column \"%s\"",
- NameStr(attr[m]->attname))));
+ NameStr(attForm->attname))));
string = field_strings[fieldno++];
if (cstate->csv_mode && string == NULL &&
@@ -2041,14 +2169,38 @@ CopyFrom(CopyState cstate)
string = cstate->null_print;
}
- cstate->cur_attname = NameStr(attr[m]->attname);
+ cstate->cur_attname = NameStr(attForm->attname);
cstate->cur_attval = string;
- values[m] = InputFunctionCall(&in_functions[m],
- string,
- typioparams[m],
- attr[m]->atttypmod);
- if (string != NULL)
- nulls[m] = false;
+
+ if (attnum == SecurityLabelAttributeNumber)
+ {
+ if (string && !ignore_security_label_input)
+ {
+ Datum datum = InputFunctionCall(&seclabel_in_function,
+ string,
+ seclabel_typioparam,
+ attForm->atttypmod);
+ loaded_secid
+ = seclabelTransInput(RelationGetRelid(cstate->rel),
+ TextDatumGetCString(datum));
+ }
+ }
+ else
+ {
+ if (cstate->csv_mode && string == NULL &&
+ cstate->force_notnull_flags[m])
+ {
+ /* Go ahead and read the NULL string */
+ string = cstate->null_print;
+ }
+
+ values[m] = InputFunctionCall(&in_functions[m],
+ string,
+ typioparams[m],
+ attForm->atttypmod);
+ if (string != NULL)
+ nulls[m] = false;
+ }
cstate->cur_attname = NULL;
cstate->cur_attval = NULL;
}
@@ -2094,17 +2246,38 @@ CopyFrom(CopyState cstate)
i = 0;
foreach(cur, cstate->attnumlist)
{
+ Form_pg_attribute attForm;
int attnum = lfirst_int(cur);
int m = attnum - 1;
- cstate->cur_attname = NameStr(attr[m]->attname);
+ if (attnum == SecurityLabelAttributeNumber)
+ attForm = SystemAttributeDefinition(attnum, true, true);
+ else
+ attForm = attr[m];
+
+ cstate->cur_attname = NameStr(attForm->attname);
i++;
- values[m] = CopyReadBinaryAttribute(cstate,
- i,
- &in_functions[m],
- typioparams[m],
- attr[m]->atttypmod,
- &nulls[m]);
+
+ if (attnum == SecurityLabelAttributeNumber)
+ {
+ Datum datum = CopyReadBinaryAttribute(cstate, i,
+ &seclabel_in_function,
+ seclabel_typioparam,
+ attForm->atttypmod,
+ &isnull);
+ if (!isnull && !ignore_security_label_input)
+ loaded_secid
+ = seclabelTransInput(RelationGetRelid(cstate->rel),
+ TextDatumGetCString(datum));
+ }
+ else
+ {
+ values[m] = CopyReadBinaryAttribute(cstate, i,
+ &in_functions[m],
+ typioparams[m],
+ attr[m]->atttypmod,
+ &nulls[m]);
+ }
cstate->cur_attname = NULL;
}
}
@@ -2125,6 +2298,8 @@ CopyFrom(CopyState cstate)
if (cstate->oids && file_has_oids)
HeapTupleSetOid(tuple, loaded_oid);
+ if (HeapTupleHasSecid(tuple))
+ HeapTupleSetSecid(tuple, loaded_secid);
/* Triggers and stuff need to be invoked in query context. */
MemoryContextSwitchTo(oldcontext);
@@ -2149,6 +2324,9 @@ CopyFrom(CopyState cstate)
}
if (!skip_tuple)
+ sepgsql_tuple_insert(cstate->rel, tuple);
+
+ if (!skip_tuple)
{
List *recheckIndexes = NIL;
@@ -3435,6 +3613,17 @@ CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist)
}
if (attnum == InvalidAttrNumber)
{
+ Form_pg_attribute attForm;
+ bool hasoid = tupDesc->tdhasoid;
+ bool hassecid = tupDesc->tdhassecid;
+
+ attForm = SystemAttributeByName(name, hasoid, hassecid);
+ if (attForm &&
+ SystemAttributeWritable(attForm->attnum, hasoid, hassecid))
+ attnum = attForm->attnum;
+ }
+ if (attnum == InvalidAttrNumber)
+ {
if (rel != NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
@@ -3482,7 +3671,7 @@ copy_dest_receive(TupleTableSlot *slot, DestReceiver *self)
slot_getallattrs(slot);
/* And send the data */
- CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull);
+ CopyOneRowTo(cstate, slot->tts_tuple, slot->tts_values, slot->tts_isnull);
}
/*
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index e7dac22..16360db 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -35,6 +35,8 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_database.h"
#include "catalog/pg_db_role_setting.h"
+#include "catalog/pg_seclabel.h"
+#include "catalog/pg_shdescription.h"
#include "catalog/pg_tablespace.h"
#include "commands/comment.h"
#include "commands/dbcommands.h"
@@ -43,6 +45,7 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "postmaster/bgwriter.h"
+#include "sepgsql/hooks.h"
#include "storage/bufmgr.h"
#include "storage/fd.h"
#include "storage/lmgr.h"
@@ -111,6 +114,7 @@ createdb(const CreatedbStmt *stmt)
Datum new_record[Natts_pg_database];
bool new_record_nulls[Natts_pg_database];
Oid dboid;
+ Oid dbsecid;
Oid datdba;
ListCell *option;
DefElem *dtablespacename = NULL;
@@ -486,6 +490,9 @@ createdb(const CreatedbStmt *stmt)
/* Note there is no additional permission check in this path */
}
+ /* SELinux permission checks */
+ dbsecid = sepgsql_database_create(dbname, src_dboid);
+
/*
* Check for db name conflict. This is just to give a more friendly error
* message than "unique index violation". There's a race condition but
@@ -560,6 +567,8 @@ createdb(const CreatedbStmt *stmt)
new_record, new_record_nulls);
HeapTupleSetOid(tuple, dboid);
+ if (HeapTupleHasSecid(tuple))
+ HeapTupleSetSecid(tuple, dbsecid);
simple_heap_insert(pg_database_rel, tuple);
@@ -576,6 +585,9 @@ createdb(const CreatedbStmt *stmt)
/* Create pg_shdepend entries for objects within database */
copyTemplateDependencies(src_dboid, dboid);
+ /* Create pg_seclabel entries for objects within database */
+ seclabelOnCreateDatabase(src_dboid, dboid);
+
/*
* Force a checkpoint before starting the copy. This will force dirty
* buffers out to disk, to ensure source database is up-to-date on disk
@@ -777,6 +789,9 @@ dropdb(const char *dbname, bool missing_ok)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
dbname);
+ /* SELinux checks */
+ sepgsql_database_drop(db_id, false);
+
/*
* Disallow dropping a DB that is marked istemplate. This is just to
* prevent people from accidentally dropping template0 or template1; they
@@ -833,6 +848,11 @@ dropdb(const char *dbname, bool missing_ok)
dropDatabaseDependencies(db_id);
/*
+ * Remove pg_seclabel entries for the database
+ */
+ seclabelOnDropDatabase(db_id);
+
+ /*
* Drop pages for this database that are in the shared buffer cache. This
* is important to ensure that no remaining backend tries to write out a
* dirty buffer to the dead database later...
@@ -915,6 +935,9 @@ RenameDatabase(const char *oldname, const char *newname)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to rename database")));
+ /* SELinux checks */
+ sepgsql_database_alter(db_id);
+
/*
* Make sure the new name doesn't exist. See notes for same error in
* CREATE DATABASE.
@@ -1053,6 +1076,9 @@ movedb(const char *dbname, const char *tblspcname)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("pg_global cannot be used as default tablespace")));
+ /* SELinux checks */
+ sepgsql_database_alter(db_id);
+
/*
* No-op if same tablespace
*/
@@ -1369,6 +1395,9 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
stmt->dbname);
+ /* SELinux checks */
+ sepgsql_database_alter(HeapTupleGetOid(tuple));
+
/*
* Build an updated tuple, perusing the information just obtained
*/
@@ -1419,6 +1448,9 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
stmt->dbname);
+ /* SELinux checks */
+ sepgsql_database_alter(datid);
+
AlterSetting(datid, InvalidOid, stmt->setstmt);
UnlockSharedObject(DatabaseRelationId, datid, 0, AccessShareLock);
@@ -1494,6 +1526,9 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to change owner of database")));
+ /* SELinux checks */
+ sepgsql_database_alter(HeapTupleGetOid(tuple));
+
memset(repl_null, false, sizeof(repl_null));
memset(repl_repl, false, sizeof(repl_repl));
@@ -1533,6 +1568,58 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
heap_close(rel, NoLock);
}
+/*
+ * ALTER DATABASE SECURITY LABEL TO
+ */
+void
+AlterDatabaseSecLabel(const char *dbname, char *new_label)
+{
+ Relation rel;
+ HeapTuple oldtup;
+ HeapTuple newtup;
+ ScanKeyData skey;
+ SysScanDesc scan;
+ Oid databaseId;
+ Oid securityId;
+
+ /* Fetch the old tuple */
+ rel = heap_open(DatabaseRelationId, RowExclusiveLock);
+ ScanKeyInit(&skey,
+ Anum_pg_database_datname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ NameGetDatum(dbname));
+ scan = systable_beginscan(rel, DatabaseNameIndexId, true,
+ SnapshotNow, 1, &skey);
+ oldtup = systable_getnext(scan);
+ if (!HeapTupleIsValid(oldtup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
+ errmsg("database \"%s\" does not exist", dbname)));
+
+ newtup = heap_copytuple(oldtup);
+
+ systable_endscan(scan);
+
+ databaseId = HeapTupleGetOid(newtup);
+
+ /* DAC permission checks */
+ if (!pg_database_ownercheck(databaseId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname);
+
+ /* SELinux checks */
+ securityId = sepgsql_database_relabel(databaseId, new_label);
+
+ /* Update it */
+ HeapTupleSetSecid(newtup, securityId);
+
+ simple_heap_update(rel, &newtup->t_self, newtup);
+
+ CatalogUpdateIndexes(rel, newtup);
+
+ heap_freetuple(newtup);
+
+ heap_close(rel, RowExclusiveLock);
+}
/*
* Helper functions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 0bda24a..334b123 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -257,7 +257,7 @@ ExplainResultDesc(ExplainStmt *stmt)
}
/* Need a tuple descriptor representing a single TEXT or XML column */
- tupdesc = CreateTemplateTupleDesc(1, false);
+ tupdesc = CreateTemplateTupleDesc(1, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
xml ? XMLOID : TEXTOID, -1, 0);
return tupdesc;
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index 14356a2..d1f255e 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -27,6 +27,7 @@
#include "foreign/foreign.h"
#include "miscadmin.h"
#include "parser/parse_func.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
@@ -230,6 +231,9 @@ AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
fdwId = HeapTupleGetOid(tup);
form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup);
+ /* SELinux checks */
+ sepgsql_fdw_alter(fdwId, InvalidOid);
+
if (form->fdwowner != newOwnerId)
{
form->fdwowner = newOwnerId;
@@ -294,6 +298,8 @@ AlterForeignServerOwner(const char *name, Oid newOwnerId)
aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
}
}
+ /* SELinux checks */
+ sepgsql_fserver_alter(srvId);
form->srvowner = newOwnerId;
@@ -339,6 +345,7 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
Oid fdwvalidator;
Datum fdwoptions;
Oid ownerId;
+ Oid securityId;
/* Must be super user */
if (!superuser())
@@ -391,8 +398,13 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
else
nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
+ /* SELinux checks */
+ securityId = sepgsql_fdw_create(stmt->fdwname, fdwvalidator);
+
tuple = heap_form_tuple(rel->rd_att, values, nulls);
+ HeapTupleSetSecid(tuple, securityId);
+
fdwId = simple_heap_insert(rel, tuple);
CatalogUpdateIndexes(rel, tuple);
@@ -511,6 +523,8 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
}
+ /* SELinux checks */
+ sepgsql_fdw_alter(fdwId, fdwvalidator);
/* Everything looks good - update the tuple */
@@ -559,6 +573,8 @@ RemoveForeignDataWrapper(DropFdwStmt *stmt)
stmt->fdwname)));
return;
}
+ /* SELinux checks */
+ sepgsql_fdw_drop(fdwId, false);
/*
* Do the deletion
@@ -608,6 +624,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
HeapTuple tuple;
Oid srvId;
Oid ownerId;
+ Oid securityId;
AclResult aclresult;
ObjectAddress myself;
ObjectAddress referenced;
@@ -635,6 +652,9 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
+ /* SELinux checks */
+ securityId = sepgsql_fserver_create(stmt->servername, fdw->fdwid);
+
/*
* Insert tuple into pg_foreign_server.
*/
@@ -678,6 +698,8 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
tuple = heap_form_tuple(rel->rd_att, values, nulls);
+ HeapTupleSetSecid(tuple, securityId);
+
srvId = simple_heap_insert(rel, tuple);
CatalogUpdateIndexes(rel, tuple);
@@ -732,6 +754,9 @@ AlterForeignServer(AlterForeignServerStmt *stmt)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
stmt->servername);
+ /* SELinux checks */
+ sepgsql_fserver_alter(srvId);
+
memset(repl_val, 0, sizeof(repl_val));
memset(repl_null, false, sizeof(repl_null));
memset(repl_repl, false, sizeof(repl_repl));
@@ -823,6 +848,8 @@ RemoveForeignServer(DropForeignServerStmt *stmt)
if (!pg_foreign_server_ownercheck(srvId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
stmt->servername);
+ /* SELinux checks */
+ sepgsql_fserver_drop(srvId, false);
object.classId = ForeignServerRelationId;
object.objectId = srvId;
@@ -896,6 +923,7 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
HeapTuple tuple;
Oid useId;
Oid umId;
+ Oid securityId;
ObjectAddress myself;
ObjectAddress referenced;
ForeignServer *srv;
@@ -908,6 +936,9 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
+ /* SELinux checks */
+ securityId = sepgsql_user_mapping_create(useId, srv->serverid);
+
/*
* Check that the user mapping is unique within server.
*/
@@ -947,6 +978,8 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
tuple = heap_form_tuple(rel->rd_att, values, nulls);
+ HeapTupleSetSecid(tuple, securityId);
+
umId = simple_heap_insert(rel, tuple);
CatalogUpdateIndexes(rel, tuple);
@@ -1000,6 +1033,9 @@ AlterUserMapping(AlterUserMappingStmt *stmt)
user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername);
+ /* SELinux checks */
+ sepgsql_user_mapping_alter(umId);
+
tp = SearchSysCacheCopy1(USERMAPPINGOID, ObjectIdGetDatum(umId));
if (!HeapTupleIsValid(tp))
@@ -1114,6 +1150,9 @@ RemoveUserMapping(DropUserMappingStmt *stmt)
user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername);
+ /* SELinux checks */
+ sepgsql_user_mapping_drop(umId, false);
+
/*
* Do the deletion
*/
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 9a584ed..f914280 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -43,6 +43,7 @@
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_proc_fn.h"
+#include "catalog/pg_seclabel.h"
#include "catalog/pg_type.h"
#include "catalog/pg_type_fn.h"
#include "commands/defrem.h"
@@ -53,6 +54,7 @@
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@@ -788,6 +790,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
ArrayType *parameterNames;
List *parameterDefaults;
Oid requiredResultType;
+ Oid replacedFunc = InvalidOid;
+ Oid prosecid;
bool isWindowFunc,
isStrict,
security;
@@ -933,6 +937,18 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
errmsg("ROWS is not applicable when function does not return a set")));
/*
+ * SELinux checks
+ */
+ if (stmt->replace)
+ replacedFunc = GetSysCacheOid3(PROCNAMEARGSNSP,
+ PointerGetDatum(funcname),
+ PointerGetDatum(parameterTypes),
+ ObjectIdGetDatum(namespaceId));
+
+ prosecid = sepgsql_proc_create(funcname, replacedFunc,
+ namespaceId, languageOid);
+
+ /*
* And now that we have all the parameters, and know we're permitted to do
* so, go ahead and create the function.
*/
@@ -957,7 +973,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
parameterDefaults,
PointerGetDatum(proconfig),
procost,
- prorows);
+ prorows,
+ prosecid);
}
@@ -999,6 +1016,9 @@ RemoveFunction(RemoveFuncStmt *stmt)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(functionName));
+ /* SELinux checks */
+ sepgsql_proc_drop(funcOid, false);
+
if (((Form_pg_proc) GETSTRUCT(tup))->proisagg)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -1135,6 +1155,9 @@ RenameFunction(List *name, List *argtypes, const char *newname)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
+ /* SELinux checks */
+ sepgsql_proc_alter_rename(procOid, newname);
+
/* rename */
namestrcpy(&(procForm->proname), newname);
simple_heap_update(rel, &tup->t_self, tup);
@@ -1239,6 +1262,8 @@ AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(procForm->pronamespace));
}
+ /* SELinux checks */
+ sepgsql_proc_alter(procOid);
memset(repl_null, false, sizeof(repl_null));
memset(repl_repl, false, sizeof(repl_repl));
@@ -1277,6 +1302,49 @@ AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
}
/*
+ * ALTER FUNCTION f(...) SECURITY LABEL TO
+ */
+void
+AlterFunctionSecLabel(List *name, List *argtypes, bool isagg, char *new_label)
+{
+ Relation rel;
+ HeapTuple tuple;
+ Oid procOid;
+ Oid securityId;
+
+ /* open pg_proc system catalog */
+ rel = heap_open(ProcedureRelationId, RowExclusiveLock);
+
+ /* get function OID */
+ if (isagg)
+ procOid = LookupAggNameTypeNames(name, argtypes, false);
+ else
+ procOid = LookupFuncNameTypeNames(name, argtypes, false);
+
+ tuple = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for function %u", procOid);
+
+ /* Must be owner */
+ if (!pg_proc_ownercheck(procOid, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+ get_func_name(procOid));
+
+ /* SELinux checks */
+ securityId = sepgsql_proc_relabel(procOid, new_label);
+
+ /* Update it */
+ HeapTupleSetSecid(tuple, securityId);
+
+ simple_heap_update(rel, &tuple->t_self, tuple);
+ CatalogUpdateIndexes(rel, tuple);
+
+ heap_freetuple(tuple);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+/*
* Implements the ALTER FUNCTION utility command (except for the
* RENAME and OWNER clauses, which are handled as part of the generic
* ALTER framework).
@@ -1313,6 +1381,9 @@ AlterFunction(AlterFunctionStmt *stmt)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(stmt->func->funcname));
+ /* SELinux checks */
+ sepgsql_proc_alter(funcOid);
+
if (procForm->proisagg)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -1659,6 +1730,9 @@ CreateCast(CreateCastStmt *stmt)
errmsg("array data types are not binary-compatible")));
}
+ /* SELinux checks */
+ sepgsql_cast_create(sourcetypeid, targettypeid, castmethod, funcid);
+
/*
* Allow source and target types to be same only for length coercion
* functions. We assume a multi-arg function does length coercion.
@@ -1795,6 +1869,9 @@ DropCast(DropCastStmt *stmt)
format_type_be(sourcetypeid),
format_type_be(targettypeid))));
+ /* SELinux checks */
+ sepgsql_cast_drop(sourcetypeid, targettypeid, false);
+
/*
* Do the deletion
*/
@@ -1873,6 +1950,9 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
/* get schema OID and check its permissions */
nspOid = LookupCreationNamespace(newschema);
+ /* SELinux checks */
+ sepgsql_proc_alter_schema(procOid, nspOid);
+
if (oldNspOid == nspOid)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_FUNCTION),
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 94657b8..7cb3634 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -39,6 +39,7 @@
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "parser/parsetree.h"
+#include "sepgsql/hooks.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
#include "storage/procarray.h"
@@ -242,6 +243,10 @@ DefineIndex(RangeVar *heapRelation,
get_tablespace_name(tablespaceId));
}
+ /* SELinux checks */
+ if (check_rights)
+ sepgsql_index_create(relationId, namespaceId);
+
/*
* Force shared indexes into the pg_global tablespace. This is a bit of a
* hack but seems simpler than marking them in the BKI commands. On the
@@ -363,7 +368,9 @@ DefineIndex(RangeVar *heapRelation,
errmsg("primary keys cannot be expressions")));
/* System attributes are never null, so no problem */
- if (SystemAttributeByName(key->name, rel->rd_rel->relhasoids))
+ if (SystemAttributeByName(key->name,
+ rel->rd_rel->relhasoids,
+ rel->rd_rel->relhassecids))
continue;
atttuple = SearchSysCacheAttName(relationId, key->name);
@@ -1572,6 +1579,9 @@ ReindexIndex(RangeVar *indexRelation)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
indexRelation->relname);
+ /* SELinux checks */
+ sepgsql_index_reindex(indOid);
+
ReleaseSysCache(tuple);
reindex_index(indOid, false);
@@ -1604,6 +1614,9 @@ ReindexTable(RangeVar *relation)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
relation->relname);
+ /* SELinux checks */
+ sepgsql_relation_reindex(heapOid);
+
ReleaseSysCache(tuple);
if (!reindex_relation(heapOid, true, false))
@@ -1642,6 +1655,9 @@ ReindexDatabase(const char *databaseName, bool do_system, bool do_user)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
databaseName);
+ /* SELinux checks */
+ sepgsql_database_reindex(MyDatabaseId);
+
/*
* Create a memory context that will survive forced transaction commits we
* do below. Since it is a child of PortalContext, it will go away
diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c
index 283947a..657ff95 100644
--- a/src/backend/commands/lockcmds.c
+++ b/src/backend/commands/lockcmds.c
@@ -20,6 +20,7 @@
#include "commands/lockcmds.h"
#include "miscadmin.h"
#include "parser/parse_clause.h"
+#include "sepgsql/hooks.h"
#include "storage/lmgr.h"
#include "utils/acl.h"
#include "utils/lsyscache.h"
@@ -149,6 +150,9 @@ LockTableRecurse(Oid reloid, RangeVar *rv,
errmsg("\"%s\" is not a table",
RelationGetRelationName(rel))));
+ /* SELinux checks */
+ sepgsql_relation_lock(rel);
+
/*
* If requested, recurse to children. We use find_inheritance_children
* not find_all_inheritors to avoid taking locks far in advance of
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index ac0270f..25b84fd 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -35,6 +35,7 @@
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "parser/parse_type.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@@ -164,7 +165,8 @@ OpClassCacheLookup(Oid amID, List *opclassname)
* Caller must have done permissions checks etc. already.
*/
static Oid
-CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
+CreateOpFamily(char *amname, char *opfname,
+ Oid namespaceoid, Oid amoid, Oid securityId)
{
Oid opfamilyoid;
Relation rel;
@@ -204,6 +206,8 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
tup = heap_form_tuple(rel->rd_att, values, nulls);
+ HeapTupleSetSecid(tup, securityId);
+
opfamilyoid = simple_heap_insert(rel, tup);
CatalogUpdateIndexes(rel, tup);
@@ -369,11 +373,16 @@ DefineOpClass(CreateOpClassStmt *stmt)
}
else
{
+ Oid securityId;
+
+ /* SELinux checks */
+ securityId = sepgsql_opfamily_create(opcname, namespaceoid, amoid);
+
/*
* Create it ... again no need for more permissions ...
*/
opfamilyoid = CreateOpFamily(stmt->amname, opcname,
- namespaceoid, amoid);
+ namespaceoid, amoid, securityId);
}
}
@@ -505,6 +514,12 @@ DefineOpClass(CreateOpClassStmt *stmt)
stmt->amname)));
}
+ /* SELinux checks */
+ sepgsql_opfamily_alter(opfamilyoid, false, amoid,
+ operators, procedures);
+ sepgsql_opclass_create(opcname, namespaceoid,
+ typeoid, opfamilyoid, storageoid);
+
rel = heap_open(OperatorClassRelationId, RowExclusiveLock);
/*
@@ -650,6 +665,7 @@ DefineOpFamily(CreateOpFamilyStmt *stmt)
NameData opfName;
ObjectAddress myself,
referenced;
+ Oid securityId;
/* Convert list of names to a name and namespace */
namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname,
@@ -701,6 +717,9 @@ DefineOpFamily(CreateOpFamilyStmt *stmt)
errmsg("operator family \"%s\" for access method \"%s\" already exists",
opfname, stmt->amname)));
+ /* SELinux checks */
+ securityId = sepgsql_opfamily_create(opfname, namespaceoid, amoid);
+
/*
* Okay, let's create the pg_opfamily entry.
*/
@@ -715,6 +734,8 @@ DefineOpFamily(CreateOpFamilyStmt *stmt)
tup = heap_form_tuple(rel->rd_att, values, nulls);
+ HeapTupleSetSecid(tup, securityId);
+
opfamilyoid = simple_heap_insert(rel, tup);
CatalogUpdateIndexes(rel, tup);
@@ -925,6 +946,9 @@ AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
break;
}
}
+ /* SELinux checks */
+ sepgsql_opfamily_alter(opfamilyoid, false, amoid,
+ operators, procedures);
/*
* Add tuples to pg_amop and pg_amproc tying in the operators and
@@ -1002,6 +1026,9 @@ AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid,
}
}
+ /* SELinux checks */
+ sepgsql_opfamily_alter(opfamilyoid, true, amoid, operators, procedures);
+
/*
* Remove tuples from pg_amop and pg_amproc.
*/
@@ -1522,6 +1549,9 @@ RemoveOpClass(RemoveOpClassStmt *stmt)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS,
NameListToString(stmt->opclassname));
+ /* SELinux checks */
+ sepgsql_opclass_drop(opcID, false);
+
ReleaseSysCache(tuple);
/*
@@ -1583,6 +1613,9 @@ RemoveOpFamily(RemoveOpFamilyStmt *stmt)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY,
NameListToString(stmt->opfamilyname));
+ /* SELinux checks */
+ sepgsql_opfamily_drop(opfID, false);
+
ReleaseSysCache(tuple);
/*
@@ -1781,6 +1814,9 @@ RenameOpClass(List *name, const char *access_method, const char *newname)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
+ /* SELinux checks */
+ sepgsql_opclass_alter_rename(opcOid, newname);
+
/* rename */
namestrcpy(&(((Form_pg_opclass) GETSTRUCT(tup))->opcname), newname);
simple_heap_update(rel, &tup->t_self, tup);
@@ -1875,6 +1911,9 @@ RenameOpFamily(List *name, const char *access_method, const char *newname)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
+ /* SELinux checks */
+ sepgsql_opfamily_alter_rename(opfOid, newname);
+
/* rename */
namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname);
simple_heap_update(rel, &tup->t_self, tup);
@@ -1990,6 +2029,8 @@ AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
}
+ /* SELinux checks */
+ sepgsql_opclass_alter(HeapTupleGetOid(tup));
/*
* Modify the owner --- okay to scribble on tup because it's a copy
@@ -2112,7 +2153,8 @@ AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
}
-
+ /* SELinux checks */
+ sepgsql_opfamily_alter_owner(HeapTupleGetOid(tup), newOwnerId);
/*
* Modify the owner --- okay to scribble on tup because it's a copy
*/
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index 9c07cf3..2e19448 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -45,6 +45,7 @@
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "parser/parse_type.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
@@ -319,6 +320,9 @@ RemoveOperator(RemoveFuncStmt *stmt)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER,
NameListToString(operatorName));
+ /* SELinux checks */
+ sepgsql_operator_drop(operOid, false);
+
ReleaseSysCache(tup);
/*
@@ -426,6 +430,8 @@ AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(oprForm->oprnamespace));
}
+ /* SELinux checks */
+ sepgsql_operator_alter(operOid);
/*
* Modify the owner --- okay to scribble on tup because it's a copy
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index e765382..a54dd3f 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -759,7 +759,7 @@ pg_prepared_statement(PG_FUNCTION_ARGS)
* build tupdesc for result tuples. This must match the definition of the
* pg_prepared_statements view in system_views.sql
*/
- tupdesc = CreateTemplateTupleDesc(5, false);
+ tupdesc = CreateTemplateTupleDesc(5, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index 633a093..a3375b2 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -29,6 +29,7 @@
#include "miscadmin.h"
#include "parser/parse_func.h"
#include "parser/parser.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@@ -145,7 +146,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
NIL,
PointerGetDatum(NULL),
1,
- 0);
+ 0,
+ InvalidOid);
}
/*
@@ -180,7 +182,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
NIL,
PointerGetDatum(NULL),
1,
- 0);
+ 0,
+ InvalidOid);
}
}
else
@@ -218,7 +221,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
NIL,
PointerGetDatum(NULL),
1,
- 0);
+ 0,
+ InvalidOid);
}
}
else
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index b0a9a22..57951fe 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -21,10 +21,12 @@
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_namespace.h"
+#include "catalog/pg_seclabel.h"
#include "commands/dbcommands.h"
#include "commands/schemacmds.h"
#include "miscadmin.h"
#include "parser/parse_utilcmd.h"
+#include "sepgsql/hooks.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
@@ -49,6 +51,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
Oid owner_uid;
Oid saved_uid;
int save_sec_context;
+ Oid secid;
AclResult aclresult;
GetUserIdAndSecContext(&saved_uid, &save_sec_context);
@@ -75,6 +78,9 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
check_is_member_of_role(saved_uid, owner_uid);
+ /* SELinux checks */
+ secid = sepgsql_schema_create(schemaName, false);
+
/* Additional check to protect reserved schema names */
if (!allowSystemTableMods && IsReservedName(schemaName))
ereport(ERROR,
@@ -95,7 +101,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
/* Create the schema's namespace */
- namespaceId = NamespaceCreate(schemaName, owner_uid);
+ namespaceId = NamespaceCreate(schemaName, owner_uid, secid);
/* Advance cmd counter to make the namespace visible */
CommandCounterIncrement();
@@ -204,6 +210,9 @@ RemoveSchemas(DropStmt *drop)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE,
namespaceName);
+ /* SELinux checks */
+ sepgsql_schema_drop(namespaceId, false);
+
object.classId = NamespaceRelationId;
object.objectId = namespaceId;
object.objectSubId = 0;
@@ -288,6 +297,9 @@ RenameSchema(const char *oldname, const char *newname)
errmsg("unacceptable schema name \"%s\"", newname),
errdetail("The prefix \"pg_\" is reserved for system schemas.")));
+ /* SELinux checks */
+ sepgsql_schema_alter(HeapTupleGetOid(tup));
+
/* rename */
namestrcpy(&(((Form_pg_namespace) GETSTRUCT(tup))->nspname), newname);
simple_heap_update(rel, &tup->t_self, tup);
@@ -389,6 +401,9 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
aclcheck_error(aclresult, ACL_KIND_DATABASE,
get_database_name(MyDatabaseId));
+ /* SELinux checks */
+ sepgsql_schema_alter(HeapTupleGetOid(tup));
+
memset(repl_null, false, sizeof(repl_null));
memset(repl_repl, false, sizeof(repl_repl));
@@ -423,3 +438,43 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
}
}
+
+/*
+ * ALTER SCHEMA SECURITY LABEL TO
+ */
+void
+AlterSchemaSecLabel(const char *name, char *new_label)
+{
+ Relation rel;
+ HeapTuple tuple;
+ Oid namespaceId;
+ Oid securityId;
+
+ /* open pg_namespace relation */
+ rel = heap_open(NamespaceRelationId, RowExclusiveLock);
+ tuple = SearchSysCacheCopy1(NAMESPACENAME,
+ CStringGetDatum(name));
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_SCHEMA),
+ errmsg("schema \"%s\" does not exist", name)));
+ namespaceId = HeapTupleGetOid(tuple);
+
+ /* DAC permission check */
+ if (!pg_namespace_ownercheck(namespaceId, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, name);
+
+ /* SELinux checks */
+ securityId = sepgsql_schema_relabel(namespaceId, new_label);
+
+ /* Update it */
+ HeapTupleSetSecid(tuple, securityId);
+
+ simple_heap_update(rel, &tuple->t_self, tuple);
+
+ CatalogUpdateIndexes(rel, tuple);
+
+ heap_freetuple(tuple);
+
+ heap_close(rel, RowExclusiveLock);
+}
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index f52e1d8..ecff05d 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -26,6 +26,7 @@
#include "commands/tablecmds.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
+#include "sepgsql/hooks.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
@@ -331,6 +332,9 @@ AlterSequence(AlterSeqStmt *stmt)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
stmt->sequence->relname);
+ /* SELinux checks */
+ sepgsql_relation_alter(relid);
+
/* do the work */
AlterSequenceInternal(relid, stmt->options);
}
@@ -469,6 +473,9 @@ nextval_internal(Oid relid)
errmsg("permission denied for sequence %s",
RelationGetRelationName(seqrel))));
+ /* SELinux checks */
+ sepgsql_sequence_next_value(elm->relid);
+
/* read-only transactions may only modify temp sequences */
if (!seqrel->rd_islocaltemp)
PreventCommandIfReadOnly("nextval()");
@@ -668,6 +675,9 @@ currval_oid(PG_FUNCTION_ARGS)
errmsg("permission denied for sequence %s",
RelationGetRelationName(seqrel))));
+ /* SELinux checks */
+ sepgsql_sequence_get_value(elm->relid);
+
if (!elm->last_valid)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
@@ -710,6 +720,9 @@ lastval(PG_FUNCTION_ARGS)
errmsg("permission denied for sequence %s",
RelationGetRelationName(seqrel))));
+ /* SELinux checks */
+ sepgsql_sequence_get_value(last_used_seq->relid);
+
result = last_used_seq->last;
relation_close(seqrel, NoLock);
@@ -746,6 +759,9 @@ do_setval(Oid relid, int64 next, bool iscalled)
errmsg("permission denied for sequence %s",
RelationGetRelationName(seqrel))));
+ /* SELinux checks */
+ sepgsql_sequence_set_value(elm->relid);
+
/* read-only transactions may only modify temp sequences */
if (!seqrel->rd_islocaltemp)
PreventCommandIfReadOnly("setval()");
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 9b5ce65..34ffbe2 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -32,6 +32,7 @@
#include "catalog/pg_inherits_fn.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
+#include "catalog/pg_seclabel.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
@@ -62,12 +63,14 @@
#include "parser/parser.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteHandler.h"
+#include "sepgsql/hooks.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
+#include "utils/guc.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
@@ -223,7 +226,7 @@ static const struct dropmsgstrings dropmsgstringarray[] = {
static void truncate_check_rel(Relation rel);
static List *MergeAttributes(List *schema, List *supers, bool istemp,
- List **supOids, List **supconstr, int *supOidCount);
+ List **supOids, List **supconstr, int *supOidCount, int *supSecidCount);
static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
static bool change_varattnos_walker(Node *node, const AttrNumber *newattno);
static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
@@ -271,10 +274,12 @@ static void ATOneLevelRecursion(List **wqueue, Relation rel,
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
AlterTableCmd *cmd);
static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
- ColumnDef *colDef, bool isOid);
+ ColumnDef *colDef, bool isOid, bool isSecid);
static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse,
AlterTableCmd *cmd);
+static void ATPrepAddSecLabel(List **wqueue, Relation rel, bool recurse,
+ AlterTableCmd *cmd);
static void ATExecDropNotNull(Relation rel, const char *colName);
static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
const char *colName);
@@ -355,6 +360,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
List *old_constraints;
bool localHasOids;
int parentOidCount;
+ int parentSecidCount;
List *rawDefaults;
List *cookedDefaults;
Datum reloptions;
@@ -362,6 +368,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
AttrNumber attnum;
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Oid ofTypeId;
+ Oid *secLabels;
/*
* Truncate relname to appropriate length (probably a waste of time, as
@@ -461,7 +468,8 @@ DefineRelation(CreateStmt *stmt, char relkind)
*/
schema = MergeAttributes(schema, stmt->inhRelations,
stmt->relation->istemp,
- &inheritOids, &old_constraints, &parentOidCount);
+ &inheritOids, &old_constraints,
+ &parentOidCount, &parentSecidCount);
/*
* Create a tuple descriptor from the relation schema. Note that this
@@ -473,6 +481,16 @@ DefineRelation(CreateStmt *stmt, char relkind)
localHasOids = interpretOidsOption(stmt->options);
descriptor->tdhasoid = (localHasOids || parentOidCount > 0);
+ if ((relkind == RELKIND_RELATION && default_with_secids) || parentSecidCount > 0)
+ descriptor->tdhassecid = true;
+
+ /* SELinux permission checks */
+ secLabels = sepgsql_relation_create(relname,
+ relkind,
+ descriptor,
+ namespaceId,
+ inheritOids,
+ false);
/*
* Find columns with default values and prepare for insertion of the
* defaults. Pre-cooked (that is, inherited) defaults go into a list of
@@ -546,7 +564,8 @@ DefineRelation(CreateStmt *stmt, char relkind)
stmt->oncommit,
reloptions,
true,
- allowSystemTableMods);
+ allowSystemTableMods,
+ secLabels);
StoreCatalogInheritance(relationId, inheritOids);
@@ -755,6 +774,9 @@ RemoveRelations(DropStmt *drop)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
rel->relname);
+ /* SELinux checks */
+ sepgsql_relation_drop(relOid, false);
+
if (!allowSystemTableMods && IsSystemClass(classform))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
@@ -918,6 +940,9 @@ ExecuteTruncate(TruncateStmt *stmt)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
RelationGetRelationName(seq_rel));
+ /* SELinux checks */
+ sepgsql_relation_alter(seq_relid);
+
seq_relids = lappend_oid(seq_relids, seq_relid);
relation_close(seq_rel, NoLock);
@@ -1086,6 +1111,8 @@ truncate_check_rel(Relation rel)
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_CLASS,
RelationGetRelationName(rel));
+ /* SELinux checks */
+ sepgsql_relation_truncate(rel);
if (!allowSystemTableMods && IsSystemRelation(rel))
ereport(ERROR,
@@ -1146,6 +1173,7 @@ storage_name(char c)
* 'supconstr' receives a list of constraints belonging to the parents,
* updated as necessary to be valid for the child.
* 'supOidCount' is set to the number of parents that have OID columns.
+ * 'supSecidCount' is set to the number of parents that have SID columns.
*
* Return value:
* Completed schema list.
@@ -1191,13 +1219,15 @@ storage_name(char c)
*/
static List *
MergeAttributes(List *schema, List *supers, bool istemp,
- List **supOids, List **supconstr, int *supOidCount)
+ List **supOids, List **supconstr,
+ int *supOidCount, int *supSecidCount)
{
ListCell *entry;
List *inhSchema = NIL;
List *parentOids = NIL;
List *constraints = NIL;
int parentsWithOids = 0;
+ int parentsWithSecids = 0;
bool have_bogus_defaults = false;
int child_attno;
static Node bogus_marker = {0}; /* marks conflicting defaults */
@@ -1325,6 +1355,8 @@ MergeAttributes(List *schema, List *supers, bool istemp,
if (relation->rd_rel->relhasoids)
parentsWithOids++;
+ if (relation->rd_rel->relhassecids)
+ parentsWithSecids++;
tupleDesc = RelationGetDescr(relation);
constr = tupleDesc->constr;
@@ -1626,6 +1658,7 @@ MergeAttributes(List *schema, List *supers, bool istemp,
*supOids = parentOids;
*supconstr = constraints;
*supOidCount = parentsWithOids;
+ *supSecidCount = parentsWithSecids;
return schema;
}
@@ -1985,6 +2018,9 @@ renameatt(Oid myrelid,
errmsg("permission denied: \"%s\" is a system catalog",
RelationGetRelationName(targetrelation))));
+ /* SELinux checks */
+ sepgsql_attribute_alter(myrelid, oldattname);
+
/*
* if the 'recurse' flag is set then we are supposed to rename this
* attribute in all classes that inherit from 'relname' (as well as in
@@ -2136,6 +2172,9 @@ RenameRelation(Oid myrelid, const char *newrelname, ObjectType reltype)
errmsg("\"%s\" is not a view",
RelationGetRelationName(targetrelation))));
+ /* SELinux checks */
+ sepgsql_relation_alter_rename(myrelid, newrelname);
+
/*
* Don't allow ALTER TABLE on composite types. We want people to use ALTER
* TYPE for that.
@@ -2566,6 +2605,27 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
}
pass = AT_PASS_DROP;
break;
+ case AT_AddSecLabel:
+ ATSimplePermissions(rel, false);
+ /* Performs own recursion */
+ if (!rel->rd_rel->relhassecids || recursing)
+ ATPrepAddSecLabel(wqueue, rel, recurse, cmd);
+ pass = AT_PASS_ADD_COL;
+ break;
+ case AT_DropSecLabel:
+ ATSimplePermissions(rel, false);
+ /* Performs own recursion */
+ if (rel->rd_rel->relhassecids)
+ {
+ AlterTableCmd *dropCmd = makeNode(AlterTableCmd);
+
+ dropCmd->subtype = AT_DropColumn;
+ dropCmd->name = pstrdup("security_label");
+ dropCmd->behavior = cmd->behavior;
+ ATPrepCmd(wqueue, rel, dropCmd, recurse, false);
+ }
+ pass = AT_PASS_DROP;
+ break;
case AT_SetTableSpace: /* SET TABLESPACE */
ATSimplePermissionsRelationOrIndex(rel);
/* This command never recurses */
@@ -2690,7 +2750,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
case AT_AddColumn: /* ADD COLUMN */
case AT_AddColumnToView: /* add column via CREATE OR REPLACE
* VIEW */
- ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false);
+ ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false, false);
break;
case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
ATExecColumnDefault(rel, cmd->name, cmd->def);
@@ -2762,17 +2822,22 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
case AT_AddOids: /* SET WITH OIDS */
/* Use the ADD COLUMN code, unless prep decided to do nothing */
if (cmd->def != NULL)
- ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, true);
+ ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, true, false);
+ break;
+ case AT_AddSecLabel: /* SET WITH SECURITY LABEL */
+ /* Use the ADD COLUMN code, unless prep decided to do nothing */
+ if (cmd->def != NULL)
+ ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false, true);
break;
case AT_DropOids: /* SET WITHOUT OIDS */
-
+ case AT_DropSecLabel: /* SET WITHOUT SECURITY LABEL */
/*
* Nothing to do here; we'll have generated a DropColumn
* subcommand to do the real work
*/
break;
case AT_SetTableSpace: /* SET TABLESPACE */
-
+ sepgsql_relation_alter(RelationGetRelid(rel));
/*
* Nothing to do here; Phase 3 does the work
*/
@@ -3126,6 +3191,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
MemoryContext oldCxt;
List *dropped_attrs = NIL;
ListCell *lc;
+ Oid tupSecidInherit = InvalidOid;
econtext = GetPerTupleExprContext(estate);
@@ -3156,6 +3222,19 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
}
/*
+ * If this routine is called due to ALTER TABLE SET WITH SECURITY LABEL,
+ * security label of the relation shall be assigned tuples.
+ */
+ if (!oldTupDesc->tdhassecid && newTupDesc->tdhassecid)
+ {
+ Oid relSecid = GetSysCacheSecid1(RELOID,
+ ObjectIdGetDatum(tab->relid));
+
+ tupSecidInherit = seclabelMoveSecid(RelationGetRelid(oldrel),
+ RelationRelationId, relSecid);
+ }
+
+ /*
* Scan through the rows, generating a new row if needed and then
* checking all the constraints.
*/
@@ -3172,11 +3251,16 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
if (newrel)
{
Oid tupOid = InvalidOid;
+ Oid tupSecid = InvalidOid;
/* Extract data from old tuple */
heap_deform_tuple(tuple, oldTupDesc, values, isnull);
if (oldTupDesc->tdhasoid)
tupOid = HeapTupleGetOid(tuple);
+ if (oldTupDesc->tdhassecid)
+ tupSecid = HeapTupleGetSecid(tuple);
+ else
+ tupSecid = tupSecidInherit;
/* Set dropped attributes to null in new tuple */
foreach(lc, dropped_attrs)
@@ -3208,6 +3292,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
/* Preserve OID, if any */
if (newTupDesc->tdhasoid)
HeapTupleSetOid(tuple, tupOid);
+ /* Preserve security-id, if any */
+ if (newTupDesc->tdhassecid)
+ HeapTupleSetSecid(tuple, tupSecid);
}
/* Now check any constraints on the possibly-changed tuple */
@@ -3601,7 +3688,7 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
static void
ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
- ColumnDef *colDef, bool isOid)
+ ColumnDef *colDef, bool isOid, bool isSecid)
{
Oid myrelid = RelationGetRelid(rel);
Relation pgclass,
@@ -3615,12 +3702,16 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
int32 typmod;
Form_pg_type tform;
Expr *defval;
+ Oid securityId;
if (rel->rd_rel->reloftype)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot add column to typed table")));
+ /* SELinux permission check */
+ securityId = sepgsql_attribute_create(myrelid, colDef->colname);
+
attrdesc = heap_open(AttributeRelationId, RowExclusiveLock);
/*
@@ -3648,6 +3739,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
errmsg("child table \"%s\" has different type for column \"%s\"",
RelationGetRelationName(rel), colDef->colname)));
+ if (!seclabelCompareSecid(AttributeRelationId, securityId,
+ AttributeRelationId, HeapTupleGetSecid(tuple)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("child table \"%s\" has different label for column \"%s\"",
+ RelationGetRelationName(rel), colDef->colname)));
+
/* If it's OID, child column must actually be OID */
if (isOid && childatt->attnum != ObjectIdAttributeNumber)
ereport(ERROR,
@@ -3655,6 +3753,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
errmsg("child table \"%s\" has a conflicting \"%s\" column",
RelationGetRelationName(rel), colDef->colname)));
+ /* If it's SecID, child column must actually be SecID */
+ if (isSecid && childatt->attnum != SecurityLabelAttributeNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("child table \"%s\" has a conflicting \"%s\" column",
+ RelationGetRelationName(rel), colDef->colname)));
+
/* Bump the existing child att's inhcount */
childatt->attinhcount++;
simple_heap_update(attrdesc, &tuple->t_self, tuple);
@@ -3694,6 +3799,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
/* Determine the new attribute's number */
if (isOid)
newattnum = ObjectIdAttributeNumber;
+ else if (isSecid)
+ newattnum = SecurityLabelAttributeNumber;
else
{
newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
@@ -3733,7 +3840,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
ReleaseSysCache(typeTuple);
- InsertPgAttributeTuple(attrdesc, &attribute, NULL);
+ InsertPgAttributeTuple(attrdesc, &attribute, NULL, securityId);
heap_close(attrdesc, RowExclusiveLock);
@@ -3742,6 +3849,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
*/
if (isOid)
((Form_pg_class) GETSTRUCT(reltup))->relhasoids = true;
+ else if (isSecid)
+ ((Form_pg_class) GETSTRUCT(reltup))->relhassecids = true;
else
((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
@@ -3853,7 +3962,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
* If we are adding an OID column, we have to tell Phase 3 to rewrite the
* table to fix that.
*/
- if (isOid)
+ if (isOid || isSecid)
tab->new_changeoids = true;
/*
@@ -3906,6 +4015,31 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd)
}
/*
+ * ALTER TABLE SET WITH SECURITY LABEL
+ *
+ * Basically this is an ADD COLUMN for the special SecLabel column.
+ * We have to cons up a ColumnDef node because the ADD COLUMN code needs one.
+ */
+static void
+ATPrepAddSecLabel(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd)
+{
+ /* If we're recursing to a child table, the ColumnDef is already set up */
+ if (cmd->def == NULL)
+ {
+ ColumnDef *cdef = makeNode(ColumnDef);
+
+ cdef->colname = pstrdup("security_label");
+ cdef->typeName = makeTypeNameFromOid(TEXTOID, -1);
+ cdef->inhcount = 0;
+ cdef->is_local = true;
+ cdef->is_not_null = true;
+ cdef->storage = 0;
+ cmd->def = (Node *) cdef;
+ }
+ ATPrepAddColumn(wqueue, rel, recurse, cmd);
+}
+
+/*
* ALTER TABLE ALTER COLUMN DROP NOT NULL
*/
static void
@@ -3917,6 +4051,9 @@ ATExecDropNotNull(Relation rel, const char *colName)
List *indexoidlist;
ListCell *indexoidscan;
+ /* SELinux checks */
+ sepgsql_attribute_alter(RelationGetRelid(rel), colName);
+
/*
* lookup the attribute
*/
@@ -4007,6 +4144,9 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
AttrNumber attnum;
Relation attr_rel;
+ /* SELinux checks */
+ sepgsql_attribute_alter(RelationGetRelid(rel), colName);
+
/*
* lookup the attribute
*/
@@ -4057,6 +4197,9 @@ ATExecColumnDefault(Relation rel, const char *colName,
{
AttrNumber attnum;
+ /* SELinux checks */
+ sepgsql_attribute_alter(RelationGetRelid(rel), colName);
+
/*
* get the number of the attribute
*/
@@ -4131,6 +4274,9 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue)
HeapTuple tuple;
Form_pg_attribute attrtuple;
+ /* SELinux checks */
+ sepgsql_attribute_alter(RelationGetRelid(rel), colName);
+
Assert(IsA(newValue, Integer));
newtarget = intVal(newValue);
@@ -4197,6 +4343,9 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options,
bool repl_null[Natts_pg_attribute];
bool repl_repl[Natts_pg_attribute];
+ /* SELinux checks */
+ sepgsql_attribute_alter(RelationGetRelid(rel), colName);
+
attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
@@ -4256,6 +4405,9 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue)
HeapTuple tuple;
Form_pg_attribute attrtuple;
+ /* SELinux checks */
+ sepgsql_attribute_alter(RelationGetRelid(rel), colName);
+
Assert(IsA(newValue, String));
storagemode = strVal(newValue);
@@ -4346,6 +4498,9 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
if (recursing)
ATSimplePermissions(rel, false);
+ /* SELinux checks */
+ sepgsql_attribute_drop(RelationGetRelid(rel), colName, false);
+
/*
* get the number of the attribute
*/
@@ -4371,8 +4526,10 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
attnum = targetatt->attnum;
- /* Can't drop a system attribute, except OID */
- if (attnum <= 0 && attnum != ObjectIdAttributeNumber)
+ /* Can't drop a system attribute, except OID/SecID */
+ if (attnum <= 0 &&
+ attnum != ObjectIdAttributeNumber &&
+ attnum != SecurityLabelAttributeNumber)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot drop system column \"%s\"",
@@ -4488,7 +4645,8 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
* If we dropped the OID column, must adjust pg_class.relhasoids and tell
* Phase 3 to physically get rid of the column.
*/
- if (attnum == ObjectIdAttributeNumber)
+ if (attnum == ObjectIdAttributeNumber ||
+ attnum == SecurityLabelAttributeNumber)
{
Relation class_rel;
Form_pg_class tuple_class;
@@ -4503,7 +4661,11 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
RelationGetRelid(rel));
tuple_class = (Form_pg_class) GETSTRUCT(tuple);
- tuple_class->relhasoids = false;
+ if (attnum == ObjectIdAttributeNumber)
+ tuple_class->relhasoids = false;
+ if (attnum == SecurityLabelAttributeNumber)
+ tuple_class->relhassecids = false;
+
simple_heap_update(class_rel, &tuple->t_self, tuple);
/* Keep the catalog indexes up to date */
@@ -4650,6 +4812,9 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
if (recursing)
ATSimplePermissions(rel, false);
+ /* SELinux checks */
+ sepgsql_relation_alter(RelationGetRelid(rel));
+
/*
* Call AddRelationNewConstraints to do the work, making sure it works on
* a copy of the Constraint so transformExpr can't modify the original. It
@@ -4847,6 +5012,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
checkFkeyPermissions(pkrel, pkattnum, numpks);
checkFkeyPermissions(rel, fkattnum, numfks);
+ /* SELinux checks */
+ sepgsql_relation_alter(RelationGetRelid(rel));
+
/*
* Look up the equality operators to use in the constraint.
*
@@ -5592,6 +5760,9 @@ ATExecDropConstraint(Relation rel, const char *constrName,
if (recursing)
ATSimplePermissions(rel, false);
+ /* SELinux checks */
+ sepgsql_relation_alter(RelationGetRelid(rel));
+
conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
/*
@@ -5924,6 +6095,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
SysScanDesc scan;
HeapTuple depTup;
+ /* SELinux checks */
+ sepgsql_attribute_alter(RelationGetRelid(rel), colName);
+
attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
/* Look up the target column */
@@ -6537,6 +6711,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
}
+ /* SELinux checks */
+ sepgsql_relation_alter(relationOid);
}
memset(repl_null, false, sizeof(repl_null));
@@ -6702,6 +6878,9 @@ ATExecClusterOn(Relation rel, const char *indexName)
{
Oid indexOid;
+ /* SELinux checks */
+ sepgsql_relation_alter(RelationGetRelid(rel));
+
indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
if (!OidIsValid(indexOid))
@@ -6726,6 +6905,9 @@ ATExecClusterOn(Relation rel, const char *indexName)
static void
ATExecDropCluster(Relation rel)
{
+ /* SELinux checks */
+ sepgsql_relation_alter(RelationGetRelid(rel));
+
mark_index_clustered(rel, InvalidOid);
}
@@ -6776,6 +6958,9 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset)
bool repl_repl[Natts_pg_class];
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
+ /* SELinux checks */
+ sepgsql_relation_alter(RelationGetRelid(rel));
+
if (defList == NIL)
return; /* nothing to do */
@@ -7090,6 +7275,9 @@ static void
ATExecEnableDisableTrigger(Relation rel, char *trigname,
char fires_when, bool skip_system)
{
+ /* SELinux checks */
+ sepgsql_relation_alter(RelationGetRelid(rel));
+
EnableDisableTrigger(rel, trigname, fires_when, skip_system);
}
@@ -7102,6 +7290,9 @@ static void
ATExecEnableDisableRule(Relation rel, char *trigname,
char fires_when)
{
+ /* SELinux checks */
+ sepgsql_relation_alter(RelationGetRelid(rel));
+
EnableDisableRule(rel, trigname, fires_when);
}
@@ -7135,6 +7326,10 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent)
*/
ATSimplePermissions(parent_rel, false);
+ /* SELinux checks */
+ sepgsql_relation_alter_inherit(RelationGetRelid(child_rel),
+ RelationGetRelid(parent_rel));
+
/* Permanent rels cannot inherit from temporary ones */
if (parent_rel->rd_istemp && !child_rel->rd_istemp)
ereport(ERROR,
@@ -7487,6 +7682,9 @@ ATExecDropInherit(Relation rel, RangeVar *parent)
List *connames;
bool found = false;
+ /* SELinux checks */
+ sepgsql_relation_alter(RelationGetRelid(rel));
+
/*
* AccessShareLock on the parent is probably enough, seeing that DROP
* TABLE doesn't lock parent tables at all. We need some lock since we'll
@@ -7784,6 +7982,9 @@ AlterTableNamespace(RangeVar *relation, const char *newschema,
/* get schema OID and check its permissions */
nspOid = LookupCreationNamespace(newschema);
+ /* SELinux checks */
+ sepgsql_relation_alter_schema(RelationGetRelid(rel), nspOid);
+
if (oldNspOid == nspOid)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_TABLE),
@@ -7981,6 +8182,282 @@ AlterSeqNamespaces(Relation classRel, Relation rel,
relation_close(depRel, AccessShareLock);
}
+/*
+ * ALTER TABLE/SEQUENCE/VIEW SECURITY LABEL TO
+ */
+void
+AlterRelationSecLabelInternal(Oid relOid, Oid securityId, int expected_parents)
+{
+ Form_pg_class classForm;
+ Relation targetRel;
+ Relation inhRel;
+ Relation classRel;
+ SysScanDesc scan;
+ ScanKeyData skey;
+ HeapTuple tuple;
+ List *indexList;
+ ListCell *cell;
+ int inhcount = 0;
+
+ /*
+ * Grab an exclusive lock on the target table
+ */
+ targetRel = relation_open(relOid, AccessExclusiveLock);
+
+ /*
+ * Check num of inheritors
+ */
+ inhRel = heap_open(InheritsRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey,
+ Anum_pg_inherits_inhrelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(relOid));
+
+ scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId,
+ true, SnapshotNow, 1, &skey);
+ while (HeapTupleIsValid(systable_getnext(scan)))
+ inhcount++;
+
+ systable_endscan(scan);
+
+ heap_close(inhRel, AccessShareLock);
+
+ if (inhcount != expected_parents)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("cannot relabel inherited relation")));
+
+ /*
+ * Update pg_class relation
+ */
+ classRel = heap_open(RelationRelationId, RowExclusiveLock);
+
+ tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", relOid);
+
+ classForm = (Form_pg_class) GETSTRUCT(tuple);
+
+ HeapTupleSetSecid(tuple, securityId);
+
+ simple_heap_update(classRel, &tuple->t_self, tuple);
+
+ CatalogUpdateIndexes(classRel, tuple);
+
+ heap_close(classRel, RowExclusiveLock);
+
+ /*
+ * Also update TOAST and INDEX
+ */
+ if (OidIsValid(classForm->reltoastrelid))
+ AlterRelationSecLabelInternal(classForm->reltoastrelid,
+ securityId, 0);
+
+ indexList = RelationGetIndexList(targetRel);
+ foreach (cell, indexList)
+ AlterRelationSecLabelInternal(lfirst_oid(cell), securityId, 0);
+
+ /*
+ * Also update pg_attribute, if not RELKIND_RELATION
+ */
+ if (classForm->relkind != RELKIND_RELATION)
+ {
+ Relation attRel;
+ HeapTuple oldtup, newtup;
+ Oid attsecid;
+
+ /* move security-id to pg_attribtue catalog */
+ attsecid = seclabelMoveSecid(AttributeRelationId,
+ RelationRelationId,
+ securityId);
+
+ attRel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+ ScanKeyInit(&skey,
+ Anum_pg_attribute_attrelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationGetRelid(targetRel)));
+
+ scan = systable_beginscan(attRel, AttributeRelidNumIndexId, true,
+ SnapshotNow, 1, &skey);
+ while (HeapTupleIsValid(oldtup = systable_getnext(scan)))
+ {
+ Form_pg_attribute attForm
+ = (Form_pg_attribute) GETSTRUCT(oldtup);
+
+ if (attForm->attinhcount > 0)
+ elog(ERROR, "Bug? attinhcount is %d at %s of %s",
+ attForm->attinhcount, NameStr(attForm->attname),
+ RelationGetRelationName(targetRel));
+
+ newtup = heap_copytuple(oldtup);
+
+ HeapTupleSetSecid(newtup, attsecid);
+
+ simple_heap_update(attRel, &newtup->t_self, newtup);
+
+ CatalogUpdateIndexes(attRel, newtup);
+ }
+ systable_endscan(scan);
+
+ heap_close(attRel, RowExclusiveLock);
+ }
+
+ heap_close(targetRel, NoLock); /* close rel but keep lock */
+}
+
+void
+AlterAttributeSecLabelInternal(Oid relOid, const char *attname,
+ Oid securityId, int expected_parents)
+{
+ Form_pg_attribute attForm;
+ Relation targetRel;
+ Relation attRel;
+ HeapTuple tuple;
+
+ /*
+ * Grab an exclusive lock on the target table, which we will NOT
+ * release until end of transaction.
+ */
+ targetRel = heap_open(relOid, AccessExclusiveLock);
+
+ attRel = heap_open(AttributeRelationId, RowExclusiveLock);
+
+ tuple = SearchSysCacheCopyAttName(relOid, attname);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" does not exist", attname)));
+
+ attForm = (Form_pg_attribute) GETSTRUCT(tuple);
+#if 0
+ /*
+ * XXX - here is no active reason why we forbid to relabel
+ * system columns.
+ */
+ if (attForm->attnum <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot relabel system column \"%s\"", attname)));
+#endif
+ if (attForm->attinhcount > expected_parents)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("cannot relabel inherited column \"%s\"", attname)));
+
+ /* update pg_attribute */
+ HeapTupleSetSecid(tuple, securityId);
+
+ simple_heap_update(attRel, &tuple->t_self, tuple);
+
+ CatalogUpdateIndexes(attRel, tuple);
+
+ heap_close(attRel, RowExclusiveLock);
+
+ heap_close(targetRel, NoLock); /* close rel but keep lock */
+}
+
+void
+AlterRelationSecLabel(RangeVar *relation, const char *attname,
+ ObjectType objtype, char *new_label)
+{
+ Oid relOid = RangeVarGetRelid(relation, false);
+ Oid securityId;
+ char relkind;
+ List *child_oids, *child_numparents;
+ ListCell *lo, *li;
+
+ /*
+ * Sanity checks for relation types
+ */
+ relkind = get_rel_relkind(relOid);
+ switch (objtype)
+ {
+ case OBJECT_TABLE:
+ Assert(attname == NULL);
+ if (relkind != RELKIND_RELATION &&
+ relkind != RELKIND_SEQUENCE &&
+ relkind != RELKIND_VIEW)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a table, sequence or view",
+ get_rel_name(relOid))));
+ break;
+
+ case OBJECT_SEQUENCE:
+ Assert(attname == NULL);
+ if (relkind != RELKIND_SEQUENCE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a sequence",
+ get_rel_name(relOid))));
+ break;
+
+ case OBJECT_VIEW:
+ Assert(attname == NULL);
+ if (relkind != RELKIND_VIEW)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a view",
+ get_rel_name(relOid))));
+ break;
+
+ case OBJECT_COLUMN:
+ Assert(attname != NULL);
+ if (relkind != RELKIND_RELATION)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a table",
+ get_rel_name(relOid))));
+ break;
+
+ default:
+ elog(ERROR, "Bug? unexpected object type %d", objtype);
+ break;
+ }
+
+ /*
+ * Recursive calls to child relations including myself
+ */
+ child_oids = find_all_inheritors(relOid,
+ AccessExclusiveLock,
+ &child_numparents);
+ forboth (lo, child_oids, li, child_numparents)
+ {
+ Oid childOid = lfirst_oid(lo);
+ int numParents = lfirst_int(li);
+ Oid relnsp = get_rel_namespace(childOid);
+
+ /* Permission checks */
+ if (!pg_class_ownercheck(childOid, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ get_rel_name(childOid));
+
+ if (!allowSystemTableMods &&
+ (IsSystemNamespace(relnsp) || IsToastNamespace(relnsp)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied: \"%s\" is a system catalog",
+ get_rel_name(childOid))));
+
+ /* SELinux checks */
+ if (objtype != OBJECT_COLUMN)
+ securityId = sepgsql_relation_relabel(childOid,
+ new_label);
+ else
+ securityId = sepgsql_attribute_relabel(childOid, attname,
+ new_label);
+
+ /* Do work */
+ if (objtype != OBJECT_COLUMN)
+ AlterRelationSecLabelInternal(childOid,
+ securityId, numParents);
+ else
+ AlterAttributeSecLabelInternal(childOid, attname,
+ securityId, numParents);
+ }
+}
/*
* This code supports
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 862cd2d..8ea4118 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -65,6 +65,7 @@
#include "commands/tablespace.h"
#include "miscadmin.h"
#include "postmaster/bgwriter.h"
+#include "sepgsql/hooks.h"
#include "storage/fd.h"
#include "storage/procarray.h"
#include "storage/standby.h"
@@ -235,6 +236,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
Oid tablespaceoid;
char *location;
Oid ownerId;
+ Oid securityId;
/* Must be super user */
if (!superuser())
@@ -244,6 +246,9 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
stmt->tablespacename),
errhint("Must be superuser to create a tablespace.")));
+ /* SELinux check */
+ securityId = sepgsql_tablespace_create(stmt->tablespacename);
+
/* However, the eventual owner of the tablespace need not be */
if (stmt->owner)
ownerId = get_roleid_checked(stmt->owner);
@@ -324,6 +329,8 @@ CreateTableSpace(CreateTableSpaceStmt *stmt)
tuple = heap_form_tuple(rel->rd_att, values, nulls);
+ HeapTupleSetSecid(tuple, securityId);
+
tablespaceoid = simple_heap_insert(rel, tuple);
CatalogUpdateIndexes(rel, tuple);
@@ -429,6 +436,9 @@ DropTableSpace(DropTableSpaceStmt *stmt)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE,
tablespacename);
+ /* SELinux checks */
+ sepgsql_tablespace_drop(tablespaceoid, false);
+
/* Disallow drop of the standard tablespaces, even by superuser */
if (tablespaceoid == GLOBALTABLESPACE_OID ||
tablespaceoid == DEFAULTTABLESPACE_OID)
@@ -787,6 +797,9 @@ RenameTableSpace(const char *oldname, const char *newname)
if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId()))
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, oldname);
+ /* SELinux checks */
+ sepgsql_tablespace_alter(HeapTupleGetOid(newtuple));
+
/* Validate new name */
if (!allowSystemTableMods && IsReservedName(newname))
ereport(ERROR,
@@ -868,6 +881,9 @@ AlterTableSpaceOwner(const char *name, Oid newOwnerId)
/* Must be able to become new owner */
check_is_member_of_role(GetUserId(), newOwnerId);
+ /* SELinux checks */
+ sepgsql_tablespace_alter(HeapTupleGetOid(tup));
+
/*
* Normally we would also check for create permissions here, but there
* are none for tablespaces so we follow what rename tablespace does
@@ -985,6 +1001,54 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt)
}
/*
+ * ALTER TABLESPACE xxx SECURITY LABEL TO ...
+ */
+void
+AlterTableSpaceSecLabel(const char *tspaceName, char *newLabel)
+{
+ Relation rel;
+ ScanKeyData skey;
+ HeapScanDesc scan;
+ HeapTuple oldtup;
+ HeapTuple newtup;
+ Oid securityId;
+
+ rel = heap_open(TableSpaceRelationId, RowExclusiveLock);
+
+ /* scan pg_tablespace catalog */
+ ScanKeyInit(&skey,
+ Anum_pg_tablespace_spcname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(tspaceName));
+ scan = heap_beginscan(rel, SnapshotNow, 1, &skey);
+ oldtup = heap_getnext(scan, ForwardScanDirection);
+ if (!HeapTupleIsValid(oldtup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("tablespace \"%s\" does not exist", tspaceName)));
+
+ /* Must be owner */
+ if (!pg_tablespace_ownercheck(HeapTupleGetOid(oldtup), GetUserId()))
+ aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, tspaceName);
+
+ /* SELinux checks */
+ securityId = sepgsql_tablespace_relabel(HeapTupleGetSecid(oldtup), newLabel);
+
+ /* update it */
+ newtup = heap_copytuple(oldtup);
+
+ HeapTupleSetSecid(newtup, securityId);
+
+ simple_heap_update(rel, &newtup->t_self, newtup);
+
+ CatalogUpdateIndexes(rel, newtup);
+
+ heap_endscan(scan);
+
+ heap_close(rel, RowExclusiveLock);
+}
+
+/*
* Routines for handling the GUC variable 'default_tablespace'.
*/
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 2cbc192..2ea9e6e 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -40,6 +40,7 @@
#include "parser/parsetree.h"
#include "pgstat.h"
#include "rewrite/rewriteManip.h"
+#include "sepgsql/hooks.h"
#include "storage/bufmgr.h"
#include "tcop/utility.h"
#include "utils/acl.h"
@@ -330,6 +331,10 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
NameListToString(stmt->funcname))));
}
+ /* SELinux checks */
+ sepgsql_trigger_create(RelationGetRelid(rel), stmt->trigname,
+ constrrelid, funcoid);
+
/*
* If the command is a user-entered CREATE CONSTRAINT TRIGGER command that
* references one of the built-in RI_FKey trigger functions, assume it is
@@ -1007,6 +1012,9 @@ DropTrigger(Oid relid, const char *trigname, DropBehavior behavior,
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
get_rel_name(relid));
+ /* SELinux checks */
+ sepgsql_trigger_drop(relid, trigname, false);
+
object.classId = TriggerRelationId;
object.objectId = HeapTupleGetOid(tup);
object.objectSubId = 0;
@@ -1116,6 +1124,9 @@ renametrig(Oid relid,
SysScanDesc tgscan;
ScanKeyData key[2];
+ /* SELinux checks */
+ sepgsql_trigger_alter(relid, oldname);
+
/*
* Grab an exclusive lock on the target table, which we will NOT release
* until end of transaction.
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index ba3de63..19bebb4 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -35,6 +35,7 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_func.h"
+#include "sepgsql/hooks.h"
#include "tsearch/ts_cache.h"
#include "tsearch/ts_public.h"
#include "tsearch/ts_utils.h"
@@ -171,6 +172,7 @@ DefineTSParser(List *names, List *parameters)
NameData pname;
Oid prsOid;
Oid namespaceoid;
+ Oid securityId;
if (!superuser())
ereport(ERROR,
@@ -250,6 +252,14 @@ DefineTSParser(List *names, List *parameters)
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("text search parser lextypes method is required")));
+ /* SELinux checks */
+ securityId = sepgsql_ts_parser_create(prsname, namespaceoid,
+ DatumGetObjectId(values[Anum_pg_ts_parser_prsstart - 1]),
+ DatumGetObjectId(values[Anum_pg_ts_parser_prstoken - 1]),
+ DatumGetObjectId(values[Anum_pg_ts_parser_prsend - 1]),
+ DatumGetObjectId(values[Anum_pg_ts_parser_prsheadline - 1]),
+ DatumGetObjectId(values[Anum_pg_ts_parser_prslextype - 1]));
+
/*
* Looks good, insert
*/
@@ -257,6 +267,8 @@ DefineTSParser(List *names, List *parameters)
tup = heap_form_tuple(prsRel->rd_att, values, nulls);
+ HeapTupleSetSecid(tup, securityId);
+
prsOid = simple_heap_insert(prsRel, tup);
CatalogUpdateIndexes(prsRel, tup);
@@ -314,6 +326,8 @@ RemoveTSParsers(DropStmt *drop)
}
continue;
}
+ /* SELinux checks */
+ sepgsql_ts_parser_drop(prsOid, false);
object.classId = TSParserRelationId;
object.objectId = prsOid;
@@ -366,10 +380,13 @@ RenameTSParser(List *oldname, const char *newname)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to rename text search parsers")));
- rel = heap_open(TSParserRelationId, RowExclusiveLock);
-
prsId = TSParserGetPrsid(oldname, false);
+ /* SELinux checks */
+ sepgsql_ts_parser_alter_rename(prsId, newname);
+
+ rel = heap_open(TSParserRelationId, RowExclusiveLock);
+
tup = SearchSysCacheCopy1(TSPARSEROID, ObjectIdGetDatum(prsId));
if (!HeapTupleIsValid(tup)) /* should not happen */
@@ -496,6 +513,7 @@ DefineTSDictionary(List *names, List *parameters)
List *dictoptions = NIL;
Oid dictOid;
Oid namespaceoid;
+ Oid securityId;
AclResult aclresult;
char *dictname;
@@ -508,6 +526,9 @@ DefineTSDictionary(List *names, List *parameters)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceoid));
+ /* SELinux checks */
+ securityId = sepgsql_ts_dict_create(dictname, namespaceoid);
+
/*
* loop over the definition list and extract the information we need.
*/
@@ -557,6 +578,8 @@ DefineTSDictionary(List *names, List *parameters)
tup = heap_form_tuple(dictRel->rd_att, values, nulls);
+ HeapTupleSetSecid(tup, securityId);
+
dictOid = simple_heap_insert(dictRel, tup);
CatalogUpdateIndexes(dictRel, tup);
@@ -610,6 +633,8 @@ RenameTSDictionary(List *oldname, const char *newname)
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
+ /* SELinux checks */
+ sepgsql_ts_dict_alter_rename(dictId, newname);
namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname);
simple_heap_update(rel, &tup->t_self, tup);
@@ -674,6 +699,8 @@ RemoveTSDictionaries(DropStmt *drop)
!pg_namespace_ownercheck(namespaceId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
NameListToString(names));
+ /* SELinux checks */
+ sepgsql_ts_dict_drop(dictOid, false);
object.classId = TSDictionaryRelationId;
object.objectId = dictOid;
@@ -745,6 +772,8 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt)
if (!pg_ts_dict_ownercheck(dictId, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
NameListToString(stmt->dictname));
+ /* SELinux checks */
+ sepgsql_ts_dict_alter(dictId);
/* deserialize the existing set of options */
opt = SysCacheGetAttr(TSDICTOID, tup,
@@ -871,6 +900,8 @@ AlterTSDictionaryOwner(List *name, Oid newOwnerId)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
}
+ /* SELinux checks */
+ sepgsql_ts_dict_alter(dictId);
form->dictowner = newOwnerId;
@@ -982,6 +1013,7 @@ DefineTSTemplate(List *names, List *parameters)
int i;
Oid dictOid;
Oid namespaceoid;
+ Oid securityId;
char *tmplname;
if (!superuser())
@@ -1036,6 +1068,10 @@ DefineTSTemplate(List *names, List *parameters)
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("text search template lexize method is required")));
+ /* SELinux checks */
+ securityId = sepgsql_ts_template_create(tmplname, namespaceoid,
+ DatumGetObjectId(values[Anum_pg_ts_template_tmplinit - 1]),
+ DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1]));
/*
* Looks good, insert
*/
@@ -1044,6 +1080,8 @@ DefineTSTemplate(List *names, List *parameters)
tup = heap_form_tuple(tmplRel->rd_att, values, nulls);
+ HeapTupleSetSecid(tup, securityId);
+
dictOid = simple_heap_insert(tmplRel, tup);
CatalogUpdateIndexes(tmplRel, tup);
@@ -1075,6 +1113,9 @@ RenameTSTemplate(List *oldname, const char *newname)
tmplId = TSTemplateGetTmplid(oldname, false);
+ /* SELinux checks */
+ sepgsql_ts_template_alter_rename(tmplId, newname);
+
tup = SearchSysCacheCopy1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId));
if (!HeapTupleIsValid(tup)) /* should not happen */
@@ -1145,6 +1186,8 @@ RemoveTSTemplates(DropStmt *drop)
}
continue;
}
+ /* SELinux checks */
+ sepgsql_ts_template_drop(tmplOid, false);
object.classId = TSTemplateRelationId;
object.objectId = tmplOid;
@@ -1305,6 +1348,7 @@ DefineTSConfiguration(List *names, List *parameters)
bool nulls[Natts_pg_ts_config];
AclResult aclresult;
Oid namespaceoid;
+ Oid securityId;
char *cfgname;
NameData cname;
Oid sourceOid = InvalidOid;
@@ -1321,6 +1365,9 @@ DefineTSConfiguration(List *names, List *parameters)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceoid));
+ /* SELinux checks */
+ securityId = sepgsql_ts_config_create(cfgname, namespaceoid);
+
/*
* loop over the definition list and extract the information we need.
*/
@@ -1388,6 +1435,8 @@ DefineTSConfiguration(List *names, List *parameters)
tup = heap_form_tuple(cfgRel->rd_att, values, nulls);
+ HeapTupleSetSecid(tup, securityId);
+
cfgOid = simple_heap_insert(cfgRel, tup);
CatalogUpdateIndexes(cfgRel, tup);
@@ -1489,6 +1538,9 @@ RenameTSConfiguration(List *oldname, const char *newname)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
+ /* SELinux checks */
+ sepgsql_ts_config_alter_rename(cfgId, newname);
+
namestrcpy(&(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname), newname);
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
@@ -1549,6 +1601,9 @@ RemoveTSConfigurations(DropStmt *drop)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION,
NameListToString(names));
+ /* SELinux checks */
+ sepgsql_ts_config_drop(cfgOid, false);
+
object.classId = TSConfigRelationId;
object.objectId = cfgOid;
object.objectSubId = 0;
@@ -1656,6 +1711,8 @@ AlterTSConfigurationOwner(List *name, Oid newOwnerId)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
}
+ /* SELinux checks */
+ sepgsql_ts_config_alter(cfgId);
form->cfgowner = newOwnerId;
@@ -1693,6 +1750,9 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION,
NameListToString(stmt->cfgname));
+ /* SELinux checks */
+ sepgsql_ts_config_alter(HeapTupleGetOid(tup));
+
relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock);
/* Add or drop mappings */
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 1e14dca..f7429f0 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -56,6 +56,7 @@
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@@ -143,6 +144,8 @@ DefineType(List *names, List *parameters)
char *array_type;
Oid array_oid;
Oid typoid;
+ Oid type_replaced;
+ Oid securityId;
Oid resulttype;
ListCell *pl;
@@ -520,6 +523,15 @@ DefineType(List *names, List *parameters)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(analyzeName));
#endif
+ /* SELinux checks */
+ type_replaced = GetSysCacheOid2(TYPENAMENSP,
+ CStringGetDatum(typeName),
+ ObjectIdGetDatum(typeNamespace));
+ securityId = sepgsql_type_create(typeName, type_replaced,
+ typeNamespace, TYPTYPE_BASE,
+ inputOid, outputOid,
+ receiveOid, sendOid,
+ typmodinOid, typmodoutOid, analyzeOid);
array_oid = AssignTypeArrayOid();
@@ -562,7 +574,8 @@ DefineType(List *names, List *parameters)
storage, /* TOAST strategy */
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
- false); /* Type NOT NULL */
+ false, /* Type NOT NULL */
+ securityId); /* security-id of the type */
/*
* Create the array type that goes with it.
@@ -601,7 +614,8 @@ DefineType(List *names, List *parameters)
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
- false); /* Type NOT NULL */
+ false, /* Type NOT NULL */
+ securityId); /* security-id of the type */
pfree(array_type);
}
@@ -668,6 +682,9 @@ RemoveTypes(DropStmt *drop)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
format_type_be(typeoid));
+ /* SELinux checks */
+ sepgsql_type_drop(typeoid, false);
+
if (drop->removeType == OBJECT_DOMAIN)
{
/* Check that this is actually a domain */
@@ -766,6 +783,7 @@ DefineDomain(CreateDomainStmt *stmt)
Oid basetypeoid;
Oid domainoid;
Oid old_type_oid;
+ Oid securityId;
Form_pg_type baseType;
int32 basetypeMod;
@@ -1011,6 +1029,13 @@ DefineDomain(CreateDomainStmt *stmt)
}
}
+ /* SELinux checks */
+ securityId = sepgsql_type_create(domainName, InvalidOid,
+ domainNamespace, TYPTYPE_DOMAIN,
+ inputProcedure, outputProcedure,
+ receiveProcedure, sendProcedure,
+ InvalidOid, InvalidOid, analyzeProcedure);
+
/*
* Have TypeCreate do all the real work.
*/
@@ -1044,7 +1069,8 @@ DefineDomain(CreateDomainStmt *stmt)
storage, /* TOAST strategy */
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
- typNotNull); /* Type NOT NULL */
+ typNotNull, /* Type NOT NULL */
+ securityId); /* security-id of the type */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
@@ -1094,6 +1120,7 @@ DefineEnum(CreateEnumStmt *stmt)
AclResult aclresult;
Oid old_type_oid;
Oid enumArrayOid;
+ Oid securityId;
/* Convert list of names to a name and namespace */
enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1120,6 +1147,13 @@ DefineEnum(CreateEnumStmt *stmt)
errmsg("type \"%s\" already exists", enumName)));
}
+ /* SELinux checks */
+ securityId = sepgsql_type_create(enumName, old_type_oid,
+ enumNamespace, TYPTYPE_ENUM,
+ F_ENUM_IN, F_ENUM_OUT,
+ F_ENUM_RECV, F_ENUM_SEND,
+ InvalidOid, InvalidOid, InvalidOid);
+
enumArrayOid = AssignTypeArrayOid();
/* Create the pg_type entry */
@@ -1153,7 +1187,8 @@ DefineEnum(CreateEnumStmt *stmt)
'p', /* TOAST strategy always plain */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
- false); /* Type NOT NULL */
+ false, /* Type NOT NULL */
+ securityId); /* security-id of the type */
/* Enter the enum's values into pg_enum */
EnumValuesCreate(enumTypeOid, stmt->vals, InvalidOid);
@@ -1192,7 +1227,8 @@ DefineEnum(CreateEnumStmt *stmt)
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
- false); /* Type NOT NULL */
+ false, /* Type NOT NULL */
+ securityId); /* security-id of the type */
pfree(enumArrayName);
}
@@ -1585,6 +1621,8 @@ AlterDomainDefault(List *names, Node *defaultRaw)
/* Check it's a domain and check user has permission for ALTER DOMAIN */
checkDomainOwner(tup, typename);
+ sepgsql_type_alter(domainoid);
+
/* Setup new tuple */
MemSet(new_record, (Datum) 0, sizeof(new_record));
MemSet(new_record_nulls, false, sizeof(new_record_nulls));
@@ -1711,6 +1749,8 @@ AlterDomainNotNull(List *names, bool notNull)
/* Check it's a domain and check user has permission for ALTER DOMAIN */
checkDomainOwner(tup, typename);
+ sepgsql_type_alter(domainoid);
+
/* Is the domain already set to the desired constraint? */
if (typTup->typnotnull == notNull)
{
@@ -1810,6 +1850,8 @@ AlterDomainDropConstraint(List *names, const char *constrName,
/* Check it's a domain and check user has permission for ALTER DOMAIN */
checkDomainOwner(tup, typename);
+ sepgsql_type_alter(domainoid);
+
/* Grab an appropriate lock on the pg_constraint relation */
conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
@@ -1884,6 +1926,8 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
/* Check it's a domain and check user has permission for ALTER DOMAIN */
checkDomainOwner(tup, typename);
+ sepgsql_type_alter(domainoid);
+
if (!IsA(newConstraint, Constraint))
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(newConstraint));
@@ -2507,6 +2551,9 @@ RenameType(List *names, const char *newTypeName)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
format_type_be(typeOid));
+ /* SELinux checks */
+ sepgsql_type_alter_rename(typeOid, newTypeName);
+
/*
* If it's a composite type, we need to check that it really is a
* free-standing composite type, and not a table's rowtype. We want people
@@ -2627,6 +2674,8 @@ AlterTypeOwner(List *names, Oid newOwnerId)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(typTup->typnamespace));
}
+ /* SELinux checks */
+ sepgsql_type_alter(HeapTupleGetOid(tup));
/*
* If it's a composite type, invoke ATExecChangeOwner so that we fix
@@ -2731,6 +2780,9 @@ AlterTypeNamespace(List *names, const char *newschema)
/* get schema OID and check its permissions */
nspOid = LookupCreationNamespace(newschema);
+ /* SELinux checks */
+ sepgsql_type_alter_schema(typeOid, nspOid);
+
/* don't allow direct alteration of array types */
elemOid = get_element_type(typeOid);
if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid)
@@ -2882,3 +2934,100 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
if (OidIsValid(arrayOid))
AlterTypeNamespaceInternal(arrayOid, nspOid, true, true);
}
+
+/*
+ * ALTER TYPE xxx SECURITY LABEL TO ...
+ */
+void
+AlterTypeSecLabelInternal(Oid typeOid, Oid securityId)
+{
+ Relation typeRel;
+ Form_pg_type typeForm;
+ HeapTuple tuple;
+
+ typeRel = heap_open(TypeRelationId, RowExclusiveLock);
+
+ tuple = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for type %u", typeOid);
+ typeForm = (Form_pg_type) GETSTRUCT(tuple);
+
+ /* update it */
+ HeapTupleSetSecid(tuple, securityId);
+
+ simple_heap_update(typeRel, &tuple->t_self, tuple);
+
+ CatalogUpdateIndexes(typeRel, tuple);
+
+ /* if it is a composit type, update pg_class too */
+ if (OidIsValid(typeForm->typrelid))
+ {
+ Oid classSecId = seclabelMoveSecid(RelationRelationId,
+ TypeRelationId,
+ securityId);
+ AlterRelationSecLabelInternal(typeForm->typrelid, classSecId, 0);
+ }
+
+ /* if it has an array type, update that too */
+ if (OidIsValid(typeForm->typarray))
+ AlterTypeSecLabelInternal(typeForm->typarray, securityId);
+
+ heap_freetuple(tuple);
+
+ heap_close(typeRel, RowExclusiveLock);
+}
+
+void
+AlterTypeSecLabel(List *names, char *new_label)
+{
+ Form_pg_type typeForm;
+ HeapTuple typtup;
+ TypeName *typename;
+ Oid typeOid;
+ Oid securityId;
+
+ /* resolve type name */
+ typename = makeTypeNameFromNameList(names);
+ typtup = LookupTypeName(NULL, typename, NULL);
+ if (!HeapTupleIsValid(typtup))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type \"%s\" does not exist",
+ TypeNameToString(typename))));
+
+ typeForm = (Form_pg_type) GETSTRUCT(typtup);
+ typeOid = HeapTupleGetOid(typtup);
+
+ /*
+ * If it's a composite type, we need to check that it really is a
+ * free-standing composite type, and not a table's rowtype. We want people
+ * to use ALTER TABLE not ALTER TYPE for that case.
+ */
+ if (typeForm->typtype == TYPTYPE_COMPOSITE &&
+ get_rel_relkind(typeForm->typrelid) != RELKIND_COMPOSITE_TYPE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("%s is a table's row type",
+ format_type_be(typeOid)),
+ errhint("Use ALTER TABLE instead.")));
+
+ /* don't allow direct alteration of array types, either */
+ if (OidIsValid(typeForm->typelem) &&
+ get_array_type(typeForm->typelem) == typeOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot alter array type %s",
+ format_type_be(typeOid))));
+
+ /* DAC permission checks */
+ if (!pg_type_ownercheck(typeOid, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
+ format_type_be(typeOid));
+
+ /* SELinux checks */
+ securityId = sepgsql_type_relabel(typeOid, new_label);
+
+ AlterTypeSecLabelInternal(typeOid, securityId);
+
+ ReleaseSysCache(typtup);
+}
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 2f0788e..8de31ae 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -21,11 +21,13 @@
#include "catalog/pg_authid.h"
#include "catalog/pg_database.h"
#include "catalog/pg_db_role_setting.h"
+#include "catalog/pg_seclabel.h"
#include "commands/comment.h"
#include "commands/dbcommands.h"
#include "commands/user.h"
#include "libpq/md5.h"
#include "miscadmin.h"
+#include "sepgsql/hooks.h"
#include "storage/lmgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
@@ -111,6 +113,7 @@ CreateRole(CreateRoleStmt *stmt)
DefElem *drolemembers = NULL;
DefElem *dadminmembers = NULL;
DefElem *dvalidUntil = NULL;
+ Oid securityId;
/* The defaults can vary depending on the original statement type */
switch (stmt->stmt_type)
@@ -279,6 +282,9 @@ CreateRole(CreateRoleStmt *stmt)
errmsg("permission denied to create role")));
}
+ /* SELinux checks */
+ securityId = sepgsql_role_create(stmt->role);
+
if (strcmp(stmt->role, "public") == 0 ||
strcmp(stmt->role, "none") == 0)
ereport(ERROR,
@@ -365,6 +371,8 @@ CreateRole(CreateRoleStmt *stmt)
tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls);
+ HeapTupleSetSecid(tuple, securityId);
+
/*
* Insert new record in the pg_authid table
*/
@@ -606,6 +614,8 @@ AlterRole(AlterRoleStmt *stmt)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied")));
}
+ /* SELinux checks */
+ sepgsql_role_alter(roleid);
/* Convert validuntil to internal form */
if (validUntil)
@@ -791,6 +801,8 @@ AlterRoleSet(AlterRoleSetStmt *stmt)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied")));
}
+ /* SELinux checks */
+ sepgsql_role_alter(HeapTupleGetOid(roletuple));
/* look up and lock the database, if specified */
if (stmt->database != NULL)
@@ -886,6 +898,9 @@ DropRole(DropRoleStmt *stmt)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to drop superusers")));
+ /* SELinux checks */
+ sepgsql_role_drop(roleid, false);
+
/*
* Lock the role, so nobody can add dependencies to her while we drop
* her. We keep the lock until the end of transaction.
@@ -1282,6 +1297,9 @@ AddRoleMems(const char *rolename, Oid roleid,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to set grantor")));
+ /* SELinux checks */
+ sepgsql_role_grant(roleid, true, memberIds);
+
pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
@@ -1412,6 +1430,8 @@ DelRoleMems(const char *rolename, Oid roleid,
errmsg("must have admin option on role \"%s\"",
rolename)));
}
+ /* SELinux checks */
+ sepgsql_role_grant(roleid, false, memberIds);
pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock);
pg_authmem_dsc = RelationGetDescr(pg_authmem_rel);
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 49a206e..4646c41 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -980,6 +980,12 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
relation_close(onerel, NoLock);
/*
+ * VACUUM FULL also reclaim orphan security labels, if exist
+ */
+ if (vacstmt->options & VACOPT_FULL)
+ seclabelRelationReclaim(relid);
+
+ /*
* Complete the transaction and free all temporary memory used.
*/
PopActiveSnapshot();
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index d7a06bc..5480c83 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -28,6 +28,7 @@
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rewriteSupport.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
@@ -167,6 +168,9 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
RelationGetRelationName(rel));
+ /* SELinux checks */
+ sepgsql_view_replace(viewOid);
+
/* Also check it's not in use already */
CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW");
diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c
index 5e555ad..963096d 100644
--- a/src/backend/executor/execJunk.c
+++ b/src/backend/executor/execJunk.c
@@ -58,7 +58,8 @@
* An optional resultSlot can be passed as well.
*/
JunkFilter *
-ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot)
+ExecInitJunkFilter(List *targetList, bool hasoid, bool hassecid,
+ TupleTableSlot *slot)
{
JunkFilter *junkfilter;
TupleDesc cleanTupType;
@@ -70,7 +71,7 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot)
/*
* Compute the tuple descriptor for the cleaned tuple.
*/
- cleanTupType = ExecCleanTypeFromTL(targetList, hasoid);
+ cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, hassecid);
/*
* Use the given slot, or make a new slot if we weren't given one.
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index d299310..febb02e 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -38,6 +38,7 @@
#include "access/xact.h"
#include "catalog/heap.h"
#include "catalog/namespace.h"
+#include "catalog/pg_seclabel.h"
#include "catalog/toasting.h"
#include "commands/tablespace.h"
#include "commands/trigger.h"
@@ -47,11 +48,13 @@
#include "optimizer/clauses.h"
#include "parser/parse_clause.h"
#include "parser/parsetree.h"
+#include "sepgsql/hooks.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/smgr.h"
#include "tcop/utility.h"
#include "utils/acl.h"
+#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
@@ -414,7 +417,16 @@ ExecCheckRTPerms(List *rangeTable)
foreach(l, rangeTable)
{
- ExecCheckRTEPerms((RangeTblEntry *) lfirst(l));
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
+
+ ExecCheckRTEPerms(rte);
+
+ if (rte->rtekind == RTE_RELATION)
+ sepgsql_relation_perms(rte->relid,
+ rte->requiredPerms,
+ rte->selectedCols,
+ rte->modifiedCols,
+ true);
}
}
@@ -829,6 +841,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
j = ExecInitJunkFilter(planstate->plan->targetlist,
tupType->tdhasoid,
+ tupType->tdhassecid,
ExecInitExtraTupleSlot(estate));
estate->es_junkFilter = j;
@@ -1065,6 +1078,37 @@ ExecContextForcesOids(PlanState *planstate, bool *hasoids)
return false;
}
+/*
+ * ExecContextForcesSecids
+ *
+ * It is same with ExecContextForcesOids, except for it checks space
+ * for security id of the tuples.
+ */
+bool
+ExecContextForcesSecids(PlanState *planstate, bool *hassecid)
+{
+ ResultRelInfo *ri = planstate->state->es_result_relation_info;
+
+ if (ri != NULL)
+ {
+ Relation rel = ri->ri_RelationDesc;
+
+ if (rel != NULL)
+ {
+ *hassecid = RelationGetForm(rel)->relhassecids;
+ return true;
+ }
+ }
+
+ if (planstate->state->es_select_into)
+ {
+ *hassecid = default_with_secids;
+ return true;
+ }
+
+ return false;
+}
+
/* ----------------------------------------------------------------
* ExecEndPlan
*
@@ -2073,6 +2117,7 @@ OpenIntoRel(QueryDesc *queryDesc)
Oid intoRelationId;
TupleDesc tupdesc;
DR_intorel *myState;
+ Oid *secLabels;
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
Assert(into);
@@ -2144,6 +2189,14 @@ OpenIntoRel(QueryDesc *queryDesc)
get_tablespace_name(tablespaceId));
}
+ /* SELinux checks */
+ secLabels = sepgsql_relation_create(intoName,
+ RELKIND_RELATION,
+ queryDesc->tupDesc,
+ namespaceId,
+ NIL,
+ true);
+
/* Parse and validate any reloptions */
reloptions = transformRelOptions((Datum) 0,
into->options,
@@ -2174,7 +2227,8 @@ OpenIntoRel(QueryDesc *queryDesc)
into->onCommit,
reloptions,
true,
- allowSystemTableMods);
+ allowSystemTableMods,
+ secLabels);
FreeTupleDesc(tupdesc);
@@ -2298,6 +2352,11 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self)
*/
if (myState->rel->rd_rel->relhasoids)
HeapTupleSetOid(tuple, InvalidOid);
+ if (myState->rel->rd_rel->relhassecids)
+ HeapTupleSetSecid(tuple, InvalidOid);
+
+ /* SELinux checks */
+ sepgsql_tuple_insert(myState->rel, tuple);
heap_insert(myState->rel,
tuple,
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 005e15e..6e93932 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -49,6 +49,7 @@
#include "optimizer/planner.h"
#include "parser/parse_coerce.h"
#include "pgstat.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
@@ -1177,6 +1178,9 @@ init_fcache(Oid foid, FuncExprState *fcache,
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid));
+ /* SELinux checks */
+ sepgsql_proc_execute(foid);
+
/*
* Safety check on nargs. Under normal circumstances this should never
* fail, as parser should check sooner. But possibly it might fail if
@@ -1221,7 +1225,7 @@ init_fcache(Oid foid, FuncExprState *fcache,
else if (functypclass == TYPEFUNC_SCALAR)
{
/* Base data type, i.e. scalar */
- tupdesc = CreateTemplateTupleDesc(1, false);
+ tupdesc = CreateTemplateTupleDesc(1, false, false);
TupleDescInitEntry(tupdesc,
(AttrNumber) 1,
NULL,
@@ -2111,7 +2115,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
/*
* Scalar type, so make a single-column descriptor
*/
- tupdesc = CreateTemplateTupleDesc(1, false);
+ tupdesc = CreateTemplateTupleDesc(1, false, false);
TupleDescInitEntry(tupdesc,
(AttrNumber) 1,
"column",
@@ -4108,6 +4112,9 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
aclcheck_error(aclresult, ACL_KIND_PROC,
get_func_name(acoerce->elemfuncid));
+ /* SELinux checks */
+ sepgsql_proc_execute(acoerce->elemfuncid);
+
/* Set up the primary fmgr lookup information */
fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc),
econtext->ecxt_per_query_memory);
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 53fe195..68f790f 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -20,6 +20,7 @@
#include "executor/executor.h"
#include "miscadmin.h"
+#include "parser/parsetree.h"
#include "utils/memutils.h"
@@ -114,6 +115,9 @@ ExecScan(ScanState *node,
ProjectionInfo *projInfo;
ExprDoneCond isDone;
TupleTableSlot *resultSlot;
+ Scan *scan = (Scan *)node->ps.plan;
+ EState *estate = node->ps.state;
+ RangeTblEntry *rte = rt_fetch(scan->scanrelid, estate->es_range_table);
/*
* Fetch data from node
@@ -125,7 +129,7 @@ ExecScan(ScanState *node,
* If we have neither a qual to check nor a projection to do, just skip
* all the overhead and return the raw scan tuple.
*/
- if (!qual && !projInfo)
+ if (!qual && !projInfo && !rte->rowlvPerms)
return ExecScanFetch(node, accessMtd, recheckMtd);
/*
@@ -189,9 +193,20 @@ ExecScan(ScanState *node,
* when the qual is nil ... saves only a few cycles, but they add up
* ...
*/
- if (!qual || ExecQual(qual, econtext, false))
+ if (sepgsql_rowlv_quals(node->ss_currentRelation,
+ rte->rowlvPerms, slot, false) &&
+ (!qual || ExecQual(qual, econtext, false)))
{
/*
+ * NOTE: When FK checks, the Row-level features needs to raise
+ * an error after evaluation of all the given quals, to avoid
+ * incorrect error reports about unrelated tuples.
+ * We assume FK does not use malicious functions in the quals.
+ */
+ sepgsql_rowlv_quals(node->ss_currentRelation,
+ rte->rowlvPerms, slot, true);
+
+ /*
* Found a satisfactory scan tuple.
*/
if (projInfo)
@@ -258,6 +273,7 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc
int numattrs = tupdesc->natts;
int attrno;
bool hasoid;
+ bool hassecid;
ListCell *tlist_item = list_head(tlist);
/* Check the tlist attributes */
@@ -307,6 +323,9 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc
if (ExecContextForcesOids(ps, &hasoid) &&
hasoid != tupdesc->tdhasoid)
return false;
+ if (ExecContextForcesSecids(ps, &hassecid) &&
+ hassecid != tupdesc->tdhassecid)
+ return false;
return true;
}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 1caf976..64f6468 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -98,7 +98,7 @@
static TupleDesc ExecTypeFromTLInternal(List *targetList,
- bool hasoid, bool skipjunk);
+ bool hasoid, bool hassecid, bool skipjunk);
/* ----------------------------------------------------------------
@@ -899,9 +899,9 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
* ----------------------------------------------------------------
*/
TupleDesc
-ExecTypeFromTL(List *targetList, bool hasoid)
+ExecTypeFromTL(List *targetList, bool hasoid, bool hassecid)
{
- return ExecTypeFromTLInternal(targetList, hasoid, false);
+ return ExecTypeFromTLInternal(targetList, hasoid, hassecid, false);
}
/* ----------------------------------------------------------------
@@ -911,13 +911,14 @@ ExecTypeFromTL(List *targetList, bool hasoid)
* ----------------------------------------------------------------
*/
TupleDesc
-ExecCleanTypeFromTL(List *targetList, bool hasoid)
+ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hassecid)
{
- return ExecTypeFromTLInternal(targetList, hasoid, true);
+ return ExecTypeFromTLInternal(targetList, hasoid, hassecid, true);
}
static TupleDesc
-ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk)
+ExecTypeFromTLInternal(List *targetList,
+ bool hasoid, bool hassecid, bool skipjunk)
{
TupleDesc typeInfo;
ListCell *l;
@@ -928,7 +929,7 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk)
len = ExecCleanTargetListLength(targetList);
else
len = ExecTargetListLength(targetList);
- typeInfo = CreateTemplateTupleDesc(len, hasoid);
+ typeInfo = CreateTemplateTupleDesc(len, hasoid, hassecid);
foreach(l, targetList)
{
@@ -960,7 +961,7 @@ ExecTypeFromExprList(List *exprList)
int cur_resno = 1;
char fldname[NAMEDATALEN];
- typeInfo = CreateTemplateTupleDesc(list_length(exprList), false);
+ typeInfo = CreateTemplateTupleDesc(list_length(exprList), false, false);
foreach(l, exprList)
{
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 98e4a64..14f9404 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -444,6 +444,7 @@ void
ExecAssignResultTypeFromTL(PlanState *planstate)
{
bool hasoid;
+ bool hassecid;
TupleDesc tupDesc;
if (ExecContextForcesOids(planstate, &hasoid))
@@ -456,12 +457,15 @@ ExecAssignResultTypeFromTL(PlanState *planstate)
hasoid = false;
}
+ if (!ExecContextForcesSecids(planstate, &hassecid))
+ hassecid = false;
+
/*
* ExecTypeFromTL needs the parse-time representation of the tlist, not a
* list of ExprStates. This is good because some plan nodes don't bother
* to set up planstate->targetlist ...
*/
- tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid);
+ tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid, hassecid);
ExecAssignResultType(planstate, tupDesc);
}
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index d552698..ed1913f 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -1153,7 +1153,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
/* Set up junk filter if needed */
if (junkFilter)
- *junkFilter = ExecInitJunkFilter(tlist, false, NULL);
+ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL);
}
else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID)
{
@@ -1192,7 +1192,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
}
/* Set up junk filter if needed */
if (junkFilter)
- *junkFilter = ExecInitJunkFilter(tlist, false, NULL);
+ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL);
return false; /* NOT returning whole tuple */
}
}
@@ -1205,7 +1205,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
* what the caller expects will happen at runtime.
*/
if (junkFilter)
- *junkFilter = ExecInitJunkFilter(tlist, false, NULL);
+ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL);
return true;
}
Assert(tupdesc);
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index ddd91fc..cfb685e 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -89,6 +89,7 @@
#include "optimizer/tlist.h"
#include "parser/parse_agg.h"
#include "parser/parse_coerce.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
@@ -1641,6 +1642,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
get_func_name(finalfn_oid));
}
}
+ /* SELinux checks */
+ sepgsql_aggregate_execute(aggref->aggfnoid);
/* resolve actual type of transition state, if polymorphic */
aggtranstype = aggform->aggtranstype;
@@ -1722,7 +1725,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
* Get a tupledesc corresponding to the inputs (including sort
* expressions) of the agg.
*/
- peraggstate->evaldesc = ExecTypeFromTL(aggref->args, false);
+ peraggstate->evaldesc = ExecTypeFromTL(aggref->args, false, false);
/* Create slot we're going to do argument evaluation in */
peraggstate->evalslot = ExecInitExtraTupleSlot(estate);
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index 66e6b74..a31907c 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -178,7 +178,7 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
/* Base data type, i.e. scalar */
char *attname = strVal(linitial(node->funccolnames));
- tupdesc = CreateTemplateTupleDesc(1, false);
+ tupdesc = CreateTemplateTupleDesc(1, false, false);
TupleDescInitEntry(tupdesc,
(AttrNumber) 1,
attname,
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index ca6b0f8..b5c79d0 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -98,6 +98,7 @@
#include "executor/execdefs.h"
#include "executor/nodeMergejoin.h"
#include "miscadmin.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
@@ -216,6 +217,9 @@ MJExamineQuals(List *mergeclauses,
aclcheck_error(aclresult, ACL_KIND_PROC,
get_func_name(cmpproc));
+ /* SELinux permissions */
+ sepgsql_proc_execute(cmpproc);
+
/* Set up the fmgr lookup information */
fmgr_info(cmpproc, &(clause->cmpfinfo));
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 7856b66..eee2739 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -38,11 +38,13 @@
#include "postgres.h"
#include "access/xact.h"
+#include "catalog/pg_seclabel.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "executor/nodeModifyTable.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
+#include "sepgsql/hooks.h"
#include "storage/bufmgr.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
@@ -160,7 +162,8 @@ ExecProcessReturning(ProjectionInfo *projectReturning,
static TupleTableSlot *
ExecInsert(TupleTableSlot *slot,
TupleTableSlot *planSlot,
- EState *estate)
+ EState *estate,
+ Oid securityId)
{
HeapTuple tuple;
ResultRelInfo *resultRelInfo;
@@ -194,6 +197,8 @@ ExecInsert(TupleTableSlot *slot,
*/
if (resultRelationDesc->rd_rel->relhasoids)
HeapTupleSetOid(tuple, InvalidOid);
+ if (resultRelationDesc->rd_rel->relhassecids)
+ HeapTupleSetSecid(tuple, securityId);
/* BEFORE ROW INSERT Triggers */
if (resultRelInfo->ri_TrigDesc &&
@@ -224,6 +229,8 @@ ExecInsert(TupleTableSlot *slot,
tuple = newtuple;
}
}
+ /* SELinux checks */
+ sepgsql_tuple_insert(resultRelationDesc, tuple);
/*
* Check the constraints of the tuple
@@ -421,7 +428,8 @@ ExecUpdate(ItemPointer tupleid,
TupleTableSlot *slot,
TupleTableSlot *planSlot,
EPQState *epqstate,
- EState *estate)
+ EState *estate,
+ Oid securityId)
{
HeapTuple tuple;
ResultRelInfo *resultRelInfo;
@@ -449,6 +457,13 @@ ExecUpdate(ItemPointer tupleid,
resultRelInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelInfo->ri_RelationDesc;
+ /*
+ * If the result relation has writable system attributes,
+ * we store user given value (or InvalidOid) on the tuple.
+ */
+ if (resultRelationDesc->rd_rel->relhassecids)
+ HeapTupleSetSecid(tuple, securityId);
+
/* BEFORE ROW UPDATE Triggers */
if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0)
@@ -479,6 +494,8 @@ ExecUpdate(ItemPointer tupleid,
tuple = newtuple;
}
}
+ /* SELinux checks */
+ sepgsql_tuple_update(resultRelationDesc, tupleid, tuple);
/*
* Check the constraints of the tuple
@@ -635,6 +652,42 @@ fireASTriggers(ModifyTableState *node)
}
}
+/*
+ * FetchWritableSecLabel
+ *
+ * It moves user given security label into slot->
+ */
+static Oid
+FetchWritableSecLabel(Relation relation,
+ JunkFilter *junkfilter,
+ TupleTableSlot *slot)
+{
+ Oid securityId = InvalidOid;
+ AttrNumber attno;
+ Datum datum;
+ bool isnull;
+ char *label;
+
+ /*
+ * If no explicit label was given, set a default label later
+ */
+ attno = ExecFindJunkAttribute(junkfilter, "security_label");
+ if (attno != InvalidAttrNumber && !ignore_security_label_input)
+ {
+ datum = ExecGetJunkAttribute(slot, attno, &isnull);
+ if (isnull)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("Unable to set NULL on \"security_label\"")));
+
+ label = TextDatumGetCString(datum);
+
+ securityId = seclabelTransInput(RelationGetRelid(relation), label);
+
+ pfree(label);
+ }
+ return securityId;
+}
/* ----------------------------------------------------------------
* ExecModifyTable
@@ -682,6 +735,8 @@ ExecModifyTable(ModifyTableState *node)
*/
for (;;)
{
+ Oid securityId = InvalidOid;
+
planSlot = ExecProcNode(subplanstate);
if (TupIsNull(planSlot))
@@ -705,6 +760,14 @@ ExecModifyTable(ModifyTableState *node)
if (junkfilter != NULL)
{
+ Relation targetRel
+ = estate->es_result_relation_info->ri_RelationDesc;
+
+ /*
+ * extract writable system column
+ */
+ securityId = FetchWritableSecLabel(targetRel, junkfilter, slot);
+
/*
* extract the 'ctid' junk attribute.
*/
@@ -734,11 +797,11 @@ ExecModifyTable(ModifyTableState *node)
switch (operation)
{
case CMD_INSERT:
- slot = ExecInsert(slot, planSlot, estate);
+ slot = ExecInsert(slot, planSlot, estate, securityId);
break;
case CMD_UPDATE:
slot = ExecUpdate(tupleid, slot, planSlot,
- &node->mt_epqstate, estate);
+ &node->mt_epqstate, estate, securityId);
break;
case CMD_DELETE:
slot = ExecDelete(tupleid, planSlot,
@@ -850,7 +913,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
* RETURNING list. We assume the rest will look the same.
*/
tupDesc = ExecTypeFromTL((List *) linitial(node->returningLists),
- false);
+ false, false);
/* Set up a slot for the output of the RETURNING projection(s) */
ExecInitResultTupleSlot(estate, &mtstate->ps);
@@ -884,7 +947,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
* We still must construct a dummy result tuple type, because InitPlan
* expects one (maybe should change that?).
*/
- tupDesc = ExecTypeFromTL(NIL, false);
+ tupDesc = ExecTypeFromTL(NIL, false, false);
ExecInitResultTupleSlot(estate, &mtstate->ps);
ExecAssignResultType(&mtstate->ps, tupDesc);
@@ -976,6 +1039,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
j = ExecInitJunkFilter(subplan->targetlist,
resultRelInfo->ri_RelationDesc->rd_att->tdhasoid,
+ resultRelInfo->ri_RelationDesc->rd_att->tdhassecid,
ExecInitExtraTupleSlot(estate));
if (operation == CMD_UPDATE || operation == CMD_DELETE)
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 9f1ff16..bef704b 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -859,7 +859,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
* (hack alert!). The righthand expressions will be evaluated in our
* own innerecontext.
*/
- tupDesc = ExecTypeFromTL(leftptlist, false);
+ tupDesc = ExecTypeFromTL(leftptlist, false, false);
slot = ExecInitExtraTupleSlot(estate);
ExecSetSlotDescriptor(slot, tupDesc);
sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
@@ -867,7 +867,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
slot,
NULL);
- tupDesc = ExecTypeFromTL(rightptlist, false);
+ tupDesc = ExecTypeFromTL(rightptlist, false, false);
slot = ExecInitExtraTupleSlot(estate);
ExecSetSlotDescriptor(slot, tupDesc);
sstate->projRight = ExecBuildProjectionInfo(righttlist,
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index 712bab1..e69f3c7 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -43,6 +43,7 @@
#include "optimizer/clauses.h"
#include "parser/parse_agg.h"
#include "parser/parse_coerce.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/datum.h"
@@ -1554,6 +1555,12 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
aclcheck_error(aclresult, ACL_KIND_PROC,
get_func_name(wfunc->winfnoid));
+ /* SELinux checks */
+ if (wfunc->winagg)
+ sepgsql_aggregate_execute(wfunc->winfnoid);
+ else
+ sepgsql_proc_execute(wfunc->winfnoid);
+
/* Fill in the perfuncstate data */
perfuncstate->wfuncstate = wfuncstate;
perfuncstate->wfunc = wfunc;
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 7f0b5e4..2e2c63b 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -767,6 +767,8 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
mtuple->t_tableOid = tuple->t_tableOid;
if (rel->rd_att->tdhasoid)
HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple));
+ if (rel->rd_att->tdhassecid)
+ HeapTupleSetSecid(mtuple, HeapTupleGetSecid(tuple));
}
else
{
@@ -795,7 +797,8 @@ SPI_fnumber(TupleDesc tupdesc, const char *fname)
return res + 1;
}
- sysatt = SystemAttributeByName(fname, true /* "oid" will be accepted */ );
+ /* "oid" and "security_label" will be accepted */
+ sysatt = SystemAttributeByName(fname, true, true);
if (sysatt != NULL)
return sysatt->attnum;
@@ -820,7 +823,7 @@ SPI_fname(TupleDesc tupdesc, int fnumber)
if (fnumber > 0)
att = tupdesc->attrs[fnumber - 1];
else
- att = SystemAttributeDefinition(fnumber, true);
+ att = SystemAttributeDefinition(fnumber, true, true);
return pstrdup(NameStr(att->attname));
}
@@ -852,7 +855,7 @@ SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber)
if (fnumber > 0)
typoid = tupdesc->attrs[fnumber - 1]->atttypid;
else
- typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
+ typoid = (SystemAttributeDefinition(fnumber, true, true))->atttypid;
getTypeOutputInfo(typoid, &foutoid, &typisvarlena);
@@ -909,7 +912,7 @@ SPI_gettype(TupleDesc tupdesc, int fnumber)
if (fnumber > 0)
typoid = tupdesc->attrs[fnumber - 1]->atttypid;
else
- typoid = (SystemAttributeDefinition(fnumber, true))->atttypid;
+ typoid = (SystemAttributeDefinition(fnumber, true, true))->atttypid;
typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typoid));
@@ -939,7 +942,7 @@ SPI_gettypeid(TupleDesc tupdesc, int fnumber)
if (fnumber > 0)
return tupdesc->attrs[fnumber - 1]->atttypid;
else
- return (SystemAttributeDefinition(fnumber, true))->atttypid;
+ return (SystemAttributeDefinition(fnumber, true, true))->atttypid;
}
char *
diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c
index f4f50f8..4ab5698 100644
--- a/src/backend/libpq/be-fsstubs.c
+++ b/src/backend/libpq/be-fsstubs.c
@@ -46,6 +46,7 @@
#include "libpq/be-fsstubs.h"
#include "libpq/libpq-fs.h"
#include "miscadmin.h"
+#include "sepgsql/hooks.h"
#include "storage/fd.h"
#include "storage/large_object.h"
#include "utils/acl.h"
@@ -172,6 +173,8 @@ lo_read(int fd, char *buf, int len)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied for large object %u",
cookies[fd]->id)));
+ /* SELinux checks */
+ sepgsql_largeobject_read(cookies[fd]->id, cookies[fd]->snapshot);
status = inv_read(cookies[fd], buf, len);
@@ -204,6 +207,8 @@ lo_write(int fd, const char *buf, int len)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied for large object %u",
cookies[fd]->id)));
+ /* SELinux checks */
+ sepgsql_largeobject_write(cookies[fd]->id, cookies[fd]->snapshot);
status = inv_write(cookies[fd], buf, len);
@@ -233,6 +238,7 @@ Datum
lo_creat(PG_FUNCTION_ARGS)
{
Oid lobjId;
+ Oid securityId;
/*
* We don't actually need to store into fscxt, but create it anyway to
@@ -240,7 +246,10 @@ lo_creat(PG_FUNCTION_ARGS)
*/
CreateFSContext();
- lobjId = inv_create(InvalidOid);
+ /* SELinux checks */
+ securityId = sepgsql_largeobject_create(InvalidOid);
+
+ lobjId = inv_create(InvalidOid, securityId);
PG_RETURN_OID(lobjId);
}
@@ -249,6 +258,7 @@ Datum
lo_create(PG_FUNCTION_ARGS)
{
Oid lobjId = PG_GETARG_OID(0);
+ Oid securityId;
/*
* We don't actually need to store into fscxt, but create it anyway to
@@ -256,7 +266,10 @@ lo_create(PG_FUNCTION_ARGS)
*/
CreateFSContext();
- lobjId = inv_create(lobjId);
+ /* SELinux checks */
+ securityId = sepgsql_largeobject_create(lobjId);
+
+ lobjId = inv_create(lobjId, securityId);
PG_RETURN_OID(lobjId);
}
@@ -286,6 +299,9 @@ lo_unlink(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be owner of large object %u", lobjId)));
+ /* SELinux checks */
+ sepgsql_largeobject_drop(lobjId, false);
+
/*
* If there are any open LO FDs referencing that ID, close 'em.
*/
@@ -381,9 +397,10 @@ lo_import_internal(text *filename, Oid lobjOid)
int nbytes,
tmp;
char buf[BUFSIZE];
- char fnamebuf[MAXPGPATH];
+ char *fnamebuf = text_to_cstring(filename);
LargeObjectDesc *lobj;
Oid oid;
+ Oid securityId;
#ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
if (!superuser())
@@ -392,13 +409,14 @@ lo_import_internal(text *filename, Oid lobjOid)
errmsg("must be superuser to use server-side lo_import()"),
errhint("Anyone can use the client-side lo_import() provided by libpq.")));
#endif
-
CreateFSContext();
+ /* SELinux checks */
+ securityId = sepgsql_largeobject_import(lobjOid, fnamebuf);
+
/*
* open the file to be read in
*/
- text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, 0666);
if (fd < 0)
ereport(ERROR,
@@ -409,7 +427,7 @@ lo_import_internal(text *filename, Oid lobjOid)
/*
* create an inversion object
*/
- oid = inv_create(lobjOid);
+ oid = inv_create(lobjOid, securityId);
/*
* read in from the filesystem and write to the inversion object
@@ -447,7 +465,7 @@ lo_export(PG_FUNCTION_ARGS)
int nbytes,
tmp;
char buf[BUFSIZE];
- char fnamebuf[MAXPGPATH];
+ char *fnamebuf = text_to_cstring(filename);
LargeObjectDesc *lobj;
mode_t oumask;
@@ -458,7 +476,6 @@ lo_export(PG_FUNCTION_ARGS)
errmsg("must be superuser to use server-side lo_export()"),
errhint("Anyone can use the client-side lo_export() provided by libpq.")));
#endif
-
CreateFSContext();
/*
@@ -466,6 +483,9 @@ lo_export(PG_FUNCTION_ARGS)
*/
lobj = inv_open(lobjId, INV_READ, fscxt);
+ /* SELinux checks */
+ sepgsql_largeobject_export(lobj->id, lobj->snapshot, fnamebuf);
+
/*
* open the file to be written to
*
@@ -473,7 +493,6 @@ lo_export(PG_FUNCTION_ARGS)
* 022. This code used to drop it all the way to 0, but creating
* world-writable export files doesn't seem wise.
*/
- text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
oumask = umask((mode_t) 0022);
fd = PathNameOpenFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666);
umask(oumask);
@@ -528,6 +547,9 @@ lo_truncate(PG_FUNCTION_ARGS)
errmsg("permission denied for large object %u",
cookies[fd]->id)));
+ /* SELinux checks */
+ sepgsql_largeobject_write(cookies[fd]->id, cookies[fd]->snapshot);
+
inv_truncate(cookies[fd], len);
PG_RETURN_INT32(0);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index e770e89..9642a66 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1823,6 +1823,7 @@ _copyRangeTblEntry(RangeTblEntry *from)
COPY_SCALAR_FIELD(checkAsUser);
COPY_BITMAPSET_FIELD(selectedCols);
COPY_BITMAPSET_FIELD(modifiedCols);
+ COPY_SCALAR_FIELD(rowlvPerms);
return newnode;
}
@@ -2755,6 +2756,21 @@ _copyAlterOwnerStmt(AlterOwnerStmt *from)
return newnode;
}
+static AlterSecLabelStmt *
+_copyAlterSecLabelStmt(AlterSecLabelStmt *from)
+{
+ AlterSecLabelStmt *newnode = makeNode(AlterSecLabelStmt);
+
+ COPY_SCALAR_FIELD(objectType);
+ COPY_NODE_FIELD(relation);
+ COPY_NODE_FIELD(object);
+ COPY_NODE_FIELD(objarg);
+ COPY_STRING_FIELD(addname);
+ COPY_NODE_FIELD(secLabel);
+
+ return newnode;
+}
+
static RuleStmt *
_copyRuleStmt(RuleStmt *from)
{
@@ -3968,6 +3984,9 @@ copyObject(void *from)
case T_AlterOwnerStmt:
retval = _copyAlterOwnerStmt(from);
break;
+ case T_AlterSecLabelStmt:
+ retval = _copyAlterSecLabelStmt(from);
+ break;
case T_RuleStmt:
retval = _copyRuleStmt(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 5d83727..fd4071d 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1308,6 +1308,19 @@ _equalAlterOwnerStmt(AlterOwnerStmt *a, AlterOwnerStmt *b)
}
static bool
+_equalAlterSecLabelStmt(AlterSecLabelStmt *a, AlterSecLabelStmt *b)
+{
+ COMPARE_SCALAR_FIELD(objectType);
+ COMPARE_NODE_FIELD(relation);
+ COMPARE_NODE_FIELD(object);
+ COMPARE_NODE_FIELD(objarg);
+ COMPARE_STRING_FIELD(addname);
+ COMPARE_NODE_FIELD(secLabel);
+
+ return true;
+}
+
+static bool
_equalRuleStmt(RuleStmt *a, RuleStmt *b)
{
COMPARE_NODE_FIELD(relation);
@@ -2186,6 +2199,7 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
COMPARE_SCALAR_FIELD(checkAsUser);
COMPARE_BITMAPSET_FIELD(selectedCols);
COMPARE_BITMAPSET_FIELD(modifiedCols);
+ COMPARE_SCALAR_FIELD(rowlvPerms);
return true;
}
@@ -2657,6 +2671,9 @@ equal(void *a, void *b)
case T_AlterOwnerStmt:
retval = _equalAlterOwnerStmt(a, b);
break;
+ case T_AlterSecLabelStmt:
+ retval = _equalAlterSecLabelStmt(a, b);
+ break;
case T_RuleStmt:
retval = _equalRuleStmt(a, b);
break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e7dae4b..4e8b350 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2135,6 +2135,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
WRITE_OID_FIELD(checkAsUser);
WRITE_BITMAPSET_FIELD(selectedCols);
WRITE_BITMAPSET_FIELD(modifiedCols);
+ WRITE_UINT_FIELD(rowlvPerms);
}
static void
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index bc6e2a6..792b9ff 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1171,6 +1171,7 @@ _readRangeTblEntry(void)
READ_OID_FIELD(checkAsUser);
READ_BITMAPSET_FIELD(selectedCols);
READ_BITMAPSET_FIELD(modifiedCols);
+ READ_UINT_FIELD(rowlvPerms);
READ_DONE();
}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 3950ab4..18e5d80 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -39,6 +39,7 @@
#include "parser/parse_expr.h"
#include "parser/parse_oper.h"
#include "parser/parsetree.h"
+#include "sepgsql/hooks.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
@@ -371,6 +372,16 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
}
/*
+ * We set up rowlvPerms before expanding inherited tables.
+ */
+ foreach(l, parse->rtable)
+ {
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
+
+ rte->rowlvPerms = sepgsql_rowlv_permissions(rte);
+ }
+
+ /*
* Preprocess RowMark information. We need to do this after subquery
* pullup (so that all non-inherited RTEs are present) and before
* inheritance expansion (so that the info is available for
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index e525ba6..6474fbc 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -38,6 +38,7 @@
#include "parser/parse_coerce.h"
#include "parser/parse_func.h"
#include "rewrite/rewriteManip.h"
+#include "sepgsql/hooks.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
#include "utils/builtins.h"
@@ -3712,6 +3713,10 @@ inline_function(Oid funcid, Oid result_type, List *args,
if (pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK)
return NULL;
+ /* SELinux checks */
+ if (!sepgsql_proc_be_inlined(func_tuple))
+ return NULL;
+
/*
* Make a temporary memory context, so that we don't leak all the stuff
* that parsing might create.
@@ -4164,7 +4169,8 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
funcform->provolatile == PROVOLATILE_VOLATILE ||
funcform->prosecdef ||
!funcform->proretset ||
- !heap_attisnull(func_tuple, Anum_pg_proc_proconfig))
+ !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) ||
+ !sepgsql_proc_be_inlined(func_tuple))
{
ReleaseSysCache(func_tuple);
return NULL;
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 6b99a10..4eb9d24 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -25,6 +25,7 @@
#include "postgres.h"
#include "access/sysattr.h"
+#include "catalog/heap.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
@@ -40,6 +41,7 @@
#include "parser/parse_target.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+#include "utils/guc.h"
#include "utils/rel.h"
@@ -651,7 +653,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
tle = makeTargetEntry(expr,
attr_num,
col->name,
- false);
+ attr_num < 0 ? true : false);
qry->targetList = lappend(qry->targetList, tle);
rte->modifiedCols = bms_add_member(rte->modifiedCols,
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b793c4d..866e0ba 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -183,8 +183,8 @@ static TypeName *TableFuncTypeName(List *columns);
%type <node> stmt schema_stmt
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
AlterForeignServerStmt AlterGroupStmt
- AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
- AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
+ AlterObjectSchemaStmt AlterOwnerStmt AlterSecLabelStmt AlterSeqStmt
+ AlterTableStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt
AlterRoleStmt AlterRoleSetStmt
AlterDefaultPrivilegesStmt DefACLAction
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
@@ -422,6 +422,8 @@ static TypeName *TableFuncTypeName(List *columns);
%type <str> OptTableSpace OptConsTableSpace OptTableSpaceOwner
%type <list> opt_check_option
+%type <value> SecLabelItem
+
%type <target> xml_attribute_el
%type <list> xml_attribute_list xml_attributes
%type <node> xml_root_version opt_xml_root_standalone
@@ -498,7 +500,7 @@ static TypeName *TableFuncTypeName(List *columns);
KEY
- LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING
+ LABEL LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING
LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP
LOCATION LOCK_P LOGIN_P
@@ -654,6 +656,7 @@ stmt :
| AlterGroupStmt
| AlterObjectSchemaStmt
| AlterOwnerStmt
+ | AlterSecLabelStmt
| AlterSeqStmt
| AlterTableStmt
| AlterRoleSetStmt
@@ -1758,6 +1761,20 @@ alter_table_cmd:
n->subtype = AT_DropOids;
$$ = (Node *)n;
}
+ /* ALTER TABLE <name> SET WITH SECURITY LABEL */
+ | SET WITH SECURITY LABEL
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_AddSecLabel;
+ $$ = (Node *)n;
+ }
+ /* ALTER TABLE <name> SET WITHOUT SECURITY LABEL */
+ | SET WITHOUT SECURITY LABEL
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_DropSecLabel;
+ $$ = (Node *)n;
+ }
/* ALTER TABLE <name> CLUSTER ON <indexname> */
| CLUSTER ON name
{
@@ -6022,6 +6039,102 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId
}
;
+/*****************************************************************************
+ *
+ * ALTER THING name SECURITY LABEL TO new_label
+ *
+ *****************************************************************************/
+
+AlterSecLabelStmt: ALTER DATABASE database_name SecLabelItem
+ {
+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt);
+ n->objectType = OBJECT_DATABASE;
+ n->object = list_make1(makeString($3));
+ n->secLabel = $4;
+ $$ = (Node *)n;
+ }
+ | ALTER SCHEMA name SecLabelItem
+ {
+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt);
+ n->objectType = OBJECT_SCHEMA;
+ n->object = list_make1(makeString($3));
+ n->secLabel = $4;
+ $$ = (Node *)n;
+ }
+ | ALTER TABLE relation_expr SecLabelItem
+ {
+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt);
+ n->objectType = OBJECT_TABLE;
+ n->relation = $3;
+ n->secLabel = $4;
+ $$ = (Node *)n;
+ }
+ | ALTER TABLE relation_expr ALTER opt_column ColId SecLabelItem
+ {
+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt);
+ n->objectType = OBJECT_COLUMN;
+ n->relation = $3;
+ n->addname = $6;
+ n->secLabel = $7;
+ $$ = (Node *)n;
+ }
+ | ALTER SEQUENCE qualified_name SecLabelItem
+ {
+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt);
+ n->objectType = OBJECT_SEQUENCE;
+ n->relation = $3;
+ n->secLabel = $4;
+ $$ = (Node *)n;
+ }
+ | ALTER VIEW qualified_name SecLabelItem
+ {
+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt);
+ n->objectType = OBJECT_VIEW;
+ n->relation = $3;
+ n->secLabel = $4;
+ $$ = (Node *)n;
+ }
+ | ALTER FUNCTION function_with_argtypes SecLabelItem
+ {
+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt);
+ n->objectType = OBJECT_FUNCTION;
+ n->object = $3->funcname;
+ n->objarg = $3->funcargs;
+ n->secLabel = $4;
+ $$ = (Node *)n;
+ }
+ | ALTER AGGREGATE func_name aggr_args SecLabelItem
+ {
+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt);
+ n->objectType = OBJECT_AGGREGATE;
+ n->object = $3;
+ n->objarg = $4;
+ n->secLabel = $5;
+ $$ = (Node *)n;
+ }
+ | ALTER LARGE_P OBJECT_P Iconst SecLabelItem
+ {
+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt);
+ n->objectType = OBJECT_LARGEOBJECT;
+ n->object = list_make1(makeInteger($4));
+ n->secLabel = $5;
+ $$ = (Node *)n;
+ }
+ | ALTER TYPE_P any_name SecLabelItem
+ {
+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt);
+ n->objectType = OBJECT_TYPE;
+ n->object = $3;
+ n->secLabel = $4;
+ $$ = (Node *)n;
+ }
+ ;
+
+SecLabelItem: SECURITY LABEL TO Sconst
+ {
+ $$ = makeString($4);
+ }
+ ;
/*****************************************************************************
*
@@ -10921,6 +11034,7 @@ unreserved_keyword:
| INVOKER
| ISOLATION
| KEY
+ | LABEL
| LANGUAGE
| LARGE_P
| LAST_P
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 38c7e91..fbc4e9f 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -2302,8 +2302,8 @@ specialAttNum(const char *attname)
{
Form_pg_attribute sysatt;
- sysatt = SystemAttributeByName(attname,
- true /* "oid" will be accepted */ );
+ /* "oid" and "security_label" will be accepted */
+ sysatt = SystemAttributeByName(attname, true, true);
if (sysatt != NULL)
return sysatt->attnum;
return InvalidAttrNumber;
@@ -2324,7 +2324,9 @@ attnumAttName(Relation rd, int attid)
{
Form_pg_attribute sysatt;
- sysatt = SystemAttributeDefinition(attid, rd->rd_rel->relhasoids);
+ sysatt = SystemAttributeDefinition(attid,
+ rd->rd_rel->relhasoids,
+ rd->rd_rel->relhassecids);
return &sysatt->attname;
}
if (attid > rd->rd_att->natts)
@@ -2346,7 +2348,9 @@ attnumTypeId(Relation rd, int attid)
{
Form_pg_attribute sysatt;
- sysatt = SystemAttributeDefinition(attid, rd->rd_rel->relhasoids);
+ sysatt = SystemAttributeDefinition(attid,
+ rd->rd_rel->relhasoids,
+ rd->rd_rel->relhassecids);
return sysatt->atttypid;
}
if (attid > rd->rd_att->natts)
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index e542dc0..8191f94 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -14,6 +14,7 @@
*/
#include "postgres.h"
+#include "catalog/heap.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "funcapi.h"
@@ -365,16 +366,34 @@ transformAssignedExpr(ParseState *pstate,
Oid attrtype; /* type of target column */
int32 attrtypmod;
Relation rd = pstate->p_target_relation;
+ bool relhasoids = RelationGetForm(rd)->relhasoids;
+ bool relhassecids = RelationGetForm(rd)->relhassecids;
Assert(rd != NULL);
- if (attrno <= 0)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot assign to system column \"%s\"",
- colname),
- parser_errposition(pstate, location)));
- attrtype = attnumTypeId(rd, attrno);
- attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;
+ if (attrno > 0)
+ {
+ attrtype = attnumTypeId(rd, attrno);
+ attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;
+ }
+ else
+ {
+ Form_pg_attribute attr;
+
+ attr = SystemAttributeDefinition(attrno, relhasoids, relhassecids);
+ if (attr && SystemAttributeWritable(attrno, relhasoids, relhassecids))
+ {
+ attrtype = attr->atttypid;
+ attrtypmod = attr->atttypmod;
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot assign to system column \"%s\"", colname),
+ parser_errposition(pstate, location)));
+ return NULL;
+ }
+ }
/*
* If the expression is a DEFAULT placeholder, insert the attribute's
@@ -503,6 +522,10 @@ updateTargetListEntry(ParseState *pstate,
List *indirection,
int location)
{
+ Relation rel = pstate->p_target_relation;
+ bool relhasoids = RelationGetForm(rel)->relhasoids;
+ bool relhassecids = RelationGetForm(rel)->relhassecids;
+
/* Fix up expression as needed */
tle->expr = transformAssignedExpr(pstate,
tle->expr,
@@ -519,6 +542,9 @@ updateTargetListEntry(ParseState *pstate,
*/
tle->resno = (AttrNumber) attrno;
tle->resname = colname;
+
+ if (SystemAttributeWritable(attrno, relhasoids, relhassecids))
+ tle->resjunk = true;
}
@@ -793,6 +819,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
Bitmapset *wholecols = NULL;
Bitmapset *partialcols = NULL;
ListCell *tl;
+ uint32 system_attrs = 0UL;
foreach(tl, cols)
{
@@ -801,14 +828,42 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
int attrno;
/* Lookup column name, ereport on failure */
- attrno = attnameAttNum(pstate->p_target_relation, name, false);
+ attrno = attnameAttNum(pstate->p_target_relation, name, true);
if (attrno == InvalidAttrNumber)
+ {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" of relation \"%s\" does not exist",
name,
RelationGetRelationName(pstate->p_target_relation)),
parser_errposition(pstate, col->location)));
+ }
+ else if (attrno < 0)
+ {
+ Relation rel = pstate->p_target_relation;
+ bool relhasoids = RelationGetForm(rel)->relhasoids;
+ bool relhassecids = RelationGetForm(rel)->relhassecids;
+
+ if (SystemAttributeWritable(attrno, relhasoids, relhassecids))
+ {
+ uint32 mask = (1<<(-attrno));
+
+ if ((system_attrs & mask) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_COLUMN),
+ errmsg("column \"%s\" specified more than once",
+ name),
+ parser_errposition(pstate, col->location)));
+ system_attrs |= mask;
+ *attrnos = lappend_int(*attrnos, attrno);
+ continue;
+ }
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ errmsg("column \"%s\" of relation \"%s\" is system column",
+ name, RelationGetRelationName(rel)),
+ parser_errposition(pstate, col->location)));
+ }
/*
* Check for duplicates, but only of whole columns --- we allow
@@ -1263,7 +1318,7 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
expandRTE(rte, var->varno, 0, var->location, false,
&names, &vars);
- tupleDesc = CreateTemplateTupleDesc(list_length(vars), false);
+ tupleDesc = CreateTemplateTupleDesc(list_length(vars), false, false);
i = 1;
forboth(lname, names, lvar, vars)
{
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 90d5c76..b78f9ba 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -53,8 +53,10 @@
#include "parser/parse_utilcmd.h"
#include "parser/parser.h"
#include "rewrite/rewriteManip.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/relcache.h"
#include "utils/syscache.h"
@@ -70,6 +72,7 @@ typedef struct
List *inhRelations; /* relations to inherit from */
bool isalter; /* true if altering existing table */
bool hasoids; /* does relation have an OID column? */
+ bool hassecids; /* does relation have an security label? */
List *columns; /* ColumnDef items */
List *ckconstraints; /* CHECK constraints */
List *fkconstraints; /* FOREIGN KEY constraints */
@@ -185,6 +188,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
cxt.alist = NIL;
cxt.pkey = NULL;
cxt.hasoids = interpretOidsOption(stmt->options);
+ cxt.hassecids = default_with_secids;
Assert(!stmt->ofTypename || !stmt->inhRelations); /* grammar enforces */
@@ -587,6 +591,9 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
aclcheck_error(aclresult, ACL_KIND_CLASS,
RelationGetRelationName(relation));
+ /* SELinux checks */
+ sepgsql_relation_getattr(RelationGetRelid(relation));
+
tupleDesc = RelationGetDescr(relation);
constr = tupleDesc->constr;
@@ -1368,7 +1375,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
if (constraint->contype == CONSTR_PRIMARY)
column->is_not_null = TRUE;
}
- else if (SystemAttributeByName(key, cxt->hasoids) != NULL)
+ else if (SystemAttributeByName(key, cxt->hasoids, cxt->hassecids) != NULL)
{
/*
* column will be a system column in the new table, so accept it.
@@ -1945,6 +1952,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
cxt.inhRelations = NIL;
cxt.isalter = true;
cxt.hasoids = false; /* need not be right */
+ cxt.hassecids = false; /* need not be right */
cxt.columns = NIL;
cxt.ckconstraints = NIL;
cxt.fkconstraints = NIL;
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index aa8f2da..604a72c 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -109,6 +109,7 @@
#include "postmaster/postmaster.h"
#include "postmaster/syslogger.h"
#include "replication/walsender.h"
+#include "sepgsql/hooks.h"
#include "storage/fd.h"
#include "storage/ipc.h"
#include "storage/pg_shmem.h"
@@ -212,7 +213,8 @@ static pid_t StartupPID = 0,
AutoVacPID = 0,
PgArchPID = 0,
PgStatPID = 0,
- SysLoggerPID = 0;
+ SysLoggerPID = 0,
+ SecWorkerPID = 0;
/* Startup/shutdown state */
#define NoShutdown 0
@@ -467,6 +469,7 @@ static void ShmemBackendArrayRemove(Backend *bn);
#define StartBackgroundWriter() StartChildProcess(BgWriterProcess)
#define StartWalWriter() StartChildProcess(WalWriterProcess)
#define StartWalReceiver() StartChildProcess(WalReceiverProcess)
+#define StartSecurityWorker() StartChildProcess(SecurityWorkerProcess)
/* Macros to check exit status of a child process */
#define EXIT_STATUS_0(st) ((st) == 0)
@@ -1480,6 +1483,11 @@ ServerLoop(void)
if (PgStatPID == 0 && pmState == PM_RUN)
PgStatPID = pgstat_start();
+ /* If we have lost security worker, try to start a new one */
+ if (SecWorkerPID == 0 && pmState == PM_RUN &&
+ sepgsql_worker_needed())
+ SecWorkerPID = StartSecurityWorker();
+
/* If we need to signal the autovacuum launcher, do so now */
if (avlauncher_needs_signal)
{
@@ -2120,6 +2128,8 @@ SIGHUP_handler(SIGNAL_ARGS)
signal_child(SysLoggerPID, SIGHUP);
if (PgStatPID != 0)
signal_child(PgStatPID, SIGHUP);
+ if (SecWorkerPID != 0)
+ signal_child(SecWorkerPID, SIGHUP);
/* Reload authentication config files too */
if (!load_hba())
@@ -2180,6 +2190,10 @@ pmdie(SIGNAL_ARGS)
/* and the walwriter too */
if (WalWriterPID != 0)
signal_child(WalWriterPID, SIGTERM);
+ /* and the security worker too */
+ if (SecWorkerPID != 0)
+ signal_child(SecWorkerPID, SIGTERM);
+
/*
* If we're in recovery, we can't kill the startup process
* right away, because at present doing so does not release
@@ -2241,6 +2255,9 @@ pmdie(SIGNAL_ARGS)
/* and the walwriter too */
if (WalWriterPID != 0)
signal_child(WalWriterPID, SIGTERM);
+ /* and the security worker too */
+ if (SecWorkerPID != 0)
+ signal_child(SecWorkerPID, SIGTERM);
pmState = PM_WAIT_BACKENDS;
}
@@ -2276,6 +2293,8 @@ pmdie(SIGNAL_ARGS)
signal_child(PgArchPID, SIGQUIT);
if (PgStatPID != 0)
signal_child(PgStatPID, SIGQUIT);
+ if (SecWorkerPID != 0)
+ signal_child(SecWorkerPID, SIGQUIT);
ExitPostmaster(0);
break;
}
@@ -2547,6 +2566,16 @@ reaper(SIGNAL_ARGS)
continue;
}
+ /* Was it the security worker process? */
+ if (pid == SecWorkerPID)
+ {
+ SecWorkerPID = 0;
+ if (!EXIT_STATUS_0(exitstatus))
+ LogChildExit(LOG, _("security worker process"),
+ pid, exitstatus);
+ continue;
+ }
+
/*
* Else do standard backend child cleanup.
*/
@@ -2750,6 +2779,18 @@ HandleChildCrash(int pid, int exitstatus, const char *procname)
signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT));
}
+ /* Take care of the security worker process too */
+ if (pid == SecWorkerPID)
+ SecWorkerPID = 0;
+ else if (SecWorkerPID != 0 && !FatalError)
+ {
+ ereport(DEBUG2,
+ (errmsg_internal("sending %s to process %d",
+ (SendStop ? "SIGSTOP" : "SIGQUIT"),
+ (int) SecWorkerPID)));
+ signal_child(SecWorkerPID, (SendStop ? SIGSTOP : SIGQUIT));
+ }
+
/*
* Force a power-cycle of the pgarch process too. (This isn't absolutely
* necessary, but it seems like a good idea for robustness, and it
@@ -2906,7 +2947,8 @@ PostmasterStateMachine(void)
WalReceiverPID == 0 &&
(BgWriterPID == 0 || !FatalError) &&
WalWriterPID == 0 &&
- AutoVacPID == 0)
+ AutoVacPID == 0 &&
+ SecWorkerPID == 0)
{
if (FatalError)
{
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 68be146..6ba1d2f 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -27,6 +27,7 @@
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rewriteSupport.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/inval.h"
@@ -265,6 +266,9 @@ DefineQueryRewrite(char *rulename,
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
RelationGetRelationName(event_relation));
+ /* SELinux checks */
+ sepgsql_rule_create(event_relid, rulename);
+
/*
* No rule actions that modify OLD or NEW
*/
diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c
index c1c5ce9..0f6a446 100644
--- a/src/backend/rewrite/rewriteRemove.c
+++ b/src/backend/rewrite/rewriteRemove.c
@@ -22,6 +22,7 @@
#include "catalog/pg_rewrite.h"
#include "miscadmin.h"
#include "rewrite/rewriteRemove.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
@@ -77,6 +78,9 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior,
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
get_rel_name(eventRelationOid));
+ /* SELinux checks */
+ sepgsql_rule_drop(eventRelationOid, ruleName, false);
+
/*
* Do the deletion
*/
diff --git a/src/backend/sepgsql/Makefile b/src/backend/sepgsql/Makefile
new file mode 100644
index 0000000..c8fb740
--- /dev/null
+++ b/src/backend/sepgsql/Makefile
@@ -0,0 +1,21 @@
+#
+# Makefile for security subsystem
+#
+
+subdir = src/backend/sepgsql
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+ifeq ($(enable_selinux), yes)
+OBJS = selinux.o avc.o label.o
+else
+OBJS = dummy.o
+endif
+
+OBJS += rowlv.o
+
+OBJS += misc.o database.o schema.o relation.o attribute.o proc.o \
+ type.o tablespace.o operator.o role.o blob.o conversion.o \
+ tsearch.o fdw.o file.o
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/sepgsql/attribute.c b/src/backend/sepgsql/attribute.c
new file mode 100644
index 0000000..3b9c916
--- /dev/null
+++ b/src/backend/sepgsql/attribute.c
@@ -0,0 +1,260 @@
+/*
+ * attribute.c
+ *
+ * SELinux hooks related to attribute
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/sysattr.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_seclabel.h"
+#include "sepgsql/hooks.h"
+#include "sepgsql/sepgsql.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+#include "utils/tqual.h"
+
+#ifdef HAVE_SELINUX
+bool
+sepgsql_attribute_common(Oid relOid, AttrNumber attno,
+ uint32 required, bool abort)
+{
+ Form_pg_attribute attForm;
+ HeapTuple tuple;
+ sepgsql_sid_t tsid;
+ char auname[NAMEDATALEN * 2 + 10];
+ bool retval;
+
+ Assert(get_rel_relkind(relOid) == RELKIND_RELATION);
+
+ tuple = SearchSysCache2(ATTNUM,
+ ObjectIdGetDatum(relOid),
+ Int16GetDatum(attno));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
+ attno, relOid);
+ attForm = (Form_pg_attribute) GETSTRUCT(tuple);
+
+ tsid.relid = AttributeRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ snprintf(auname, sizeof(auname), "%s.%s",
+ get_rel_name(relOid), NameStr(attForm->attname));
+
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_COLUMN,
+ required,
+ auname,
+ abort);
+ ReleaseSysCache(tuple);
+
+ return retval;
+}
+#endif
+
+Oid
+sepgsql_attribute_create(Oid relOid, const char *attName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+ char auname[NAMEDATALEN * 2 + 10];
+
+ if (get_rel_relkind(relOid) != RELKIND_RELATION)
+ {
+ sepgsql_relation_common(relOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ nsid.relid = RelationRelationId;
+ nsid.secid = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid));
+
+ nsid = sepgsql_move_secid(AttributeRelationId, nsid);
+
+ return nsid.secid;
+ }
+
+ nsid = sepgsql_get_default_column_secid(relOid);
+ snprintf(auname, sizeof(auname), "%s.%s",
+ get_rel_name(relOid), attName);
+
+ /* db_column:{create} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_COLUMN,
+ SEPG_DB_COLUMN__CREATE,
+ auname,
+ true);
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_attribute_alter(Oid relOid, const char *attName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ AttrNumber attnum = get_attnum(relOid, attName);
+
+ if (attnum == InvalidAttrNumber)
+ return; /* to be failed later */
+
+ if (get_rel_relkind(relOid) == RELKIND_RELATION)
+ sepgsql_attribute_common(relOid, attnum,
+ SEPG_DB_COLUMN__SETATTR, true);
+ else
+ sepgsql_relation_common(relOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ }
+#endif
+}
+
+Oid
+sepgsql_attribute_relabel(Oid relOid, const char *attName, char *new_label)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ AttrNumber attnum = get_attnum(relOid, attName);
+ sepgsql_sid_t nsid;
+ char auname[NAMEDATALEN * 2 + 10];
+
+ Assert(get_rel_relkind(relOid) == RELKIND_RELATION);
+ if (attnum == InvalidAttrNumber)
+ return InvalidOid; /* to be failed later */
+
+ nsid.relid = AttributeRelationId;
+ nsid.secid = seclabelTransInput(nsid.relid, new_label);
+
+ snprintf(auname, sizeof(auname), "%s.%s",
+ get_rel_name(relOid), get_attname(relOid, attnum));
+
+ /* db_column:{setattr relabelfrom} */
+ sepgsql_attribute_common(relOid, attnum,
+ SEPG_DB_COLUMN__SETATTR |
+ SEPG_DB_COLUMN__RELABELFROM,
+ true);
+
+ /* db_column:{relabelto} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_COLUMN,
+ SEPG_DB_COLUMN__RELABELTO,
+ auname,
+ true);
+ return nsid.secid;
+ }
+#endif
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("SE-PostgreSQL is not available")));
+ return InvalidOid;
+}
+
+void
+sepgsql_attribute_drop(Oid relOid, const char *attName, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ AttrNumber attnum = get_attnum(relOid, attName);
+
+ /*
+ * If not found, the caller raises an error with an appropriate
+ * error message.
+ */
+ if (attnum == InvalidAttrNumber)
+ return;
+
+ if (get_rel_relkind(relOid) == RELKIND_RELATION)
+ {
+ sepgsql_attribute_common(relOid, attnum,
+ SEPG_DB_COLUMN__DROP, true);
+ /*
+ * ALTER TABLE SET WITHOUT SECURITY LABEL is equivalent to
+ * relabel all the tuples within the target relation.
+ * In this case, we need to check {relabelfrom relabelto}
+ */
+ if (!cascade &&
+ attnum == SecurityLabelAttributeNumber)
+ {
+ Relation rel;
+ HeapScanDesc scan;
+ HeapTuple tuple;
+ sepgsql_sid_t tsid;
+
+ rel = heap_open(relOid, AccessShareLock);
+
+ scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+
+ while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
+ {
+ /* db_tuple:{update relabelfrom} */
+ tsid.relid = relOid;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__UPDATE |
+ SEPG_DB_TUPLE__RELABELFROM,
+ NULL,
+ true);
+ }
+ heap_endscan(scan);
+
+ heap_close(rel, AccessShareLock);
+
+ /* db_tuple:{relabelto} */
+ tsid.relid = RelationRelationId;
+ tsid.secid = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid));
+
+ sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__RELABELTO,
+ NULL,
+ true);
+ }
+ }
+ else if (!cascade)
+ sepgsql_relation_common(relOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_attribute_grant(Oid relOid, AttrNumber attnum)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ if (get_rel_relkind(relOid) == RELKIND_RELATION)
+ sepgsql_attribute_common(relOid, attnum,
+ SEPG_DB_COLUMN__SETATTR, true);
+ else
+ sepgsql_relation_common(relOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_attribute_comment(Oid relOid, AttrNumber attnum)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ if (get_rel_relkind(relOid) == RELKIND_RELATION)
+ sepgsql_attribute_common(relOid, attnum,
+ SEPG_DB_COLUMN__SETATTR, true);
+ else
+ sepgsql_relation_common(relOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ }
+#endif
+}
diff --git a/src/backend/sepgsql/avc.c b/src/backend/sepgsql/avc.c
new file mode 100644
index 0000000..5f58974
--- /dev/null
+++ b/src/backend/sepgsql/avc.c
@@ -0,0 +1,503 @@
+/*
+ * avc.c
+ * userspace access vector cache
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "access/hash.h"
+#include "access/xact.h"
+#include "catalog/pg_seclabel.h"
+#include "libpq/libpq-be.h"
+#include "libpq/pqsignal.h"
+#include "miscadmin.h"
+#include "sepgsql/hooks.h"
+#include "sepgsql/sepgsql.h"
+#include "storage/shmem.h"
+#include "storage/lock.h"
+#include "utils/memutils.h"
+
+#include <selinux/selinux.h>
+#include <selinux/avc.h>
+#include <unistd.h>
+
+/* ------------------------------------------------------------
+ *
+ * Userspace Access Vector Cache
+ *
+ * ------------------------------------------------------------
+ */
+static MemoryContext AvcMemCtx = NULL;
+
+#define AVC_HASH_NUM_SLOTS 256
+#define AVC_HASH_NUM_NODES 180
+
+#define avc_hash_key(trelid, tsecid, tclass, nrelid) \
+ (hash_uint32((trelid) ^ (tsecid) ^ ((tclass) << 3) ^ (nrelid)))
+
+typedef struct _avc_datum
+{
+ uint32 hash_key;
+
+ uint16 tclass;
+ sepgsql_sid_t tsid;
+ sepgsql_sid_t nsid;
+ char *tcontext;
+ char *ncontext;
+
+ uint32 allowed;
+ uint32 auditallow;
+ uint32 auditdeny;
+ bool permissive;
+
+ bool hot_cache;
+} avc_datum;
+
+typedef struct _avc_page
+{
+ struct _avc_page *next;
+
+ List *slot[AVC_HASH_NUM_SLOTS];
+
+ uint32 avc_count;
+ uint32 lru_hint;
+
+ char scontext[1];
+} avc_page;
+
+static avc_page *current_page = NULL;
+
+static int avc_version = -1;
+
+/*
+ * selinux_state
+ *
+ * It is deployed on the shared memory region, to show the system
+ * state of SELinux and its security policy.
+ */
+struct
+{
+ int version;
+
+ bool enforcing;
+} *selinux_state = NULL;
+
+Size
+sepgsql_shmem_size(void)
+{
+ return sizeof(*selinux_state);
+}
+
+static void
+sepgsql_shmem_init(void)
+{
+ bool found;
+
+ selinux_state = ShmemInitStruct("SELinux system state",
+ sepgsql_shmem_size(), &found);
+ if (!found)
+ {
+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE);
+
+ selinux_state->version = 0;
+ selinux_state->enforcing = (security_getenforce() > 0);
+
+ LWLockRelease(SepgsqlAvcLock);
+ }
+}
+
+void
+sepgsql_avc_switch(const char *scontext)
+{
+ avc_page *new_page;
+ int i, length;
+
+ if (current_page)
+ {
+ new_page = current_page;
+ do {
+ if (strcmp(new_page->scontext, scontext) == 0)
+ {
+ current_page = new_page;
+ return;
+ }
+ new_page = new_page->next;
+ } while (new_page != current_page);
+ }
+
+ /*
+ * Not found, so create a new avc_page
+ */
+ length = sizeof(avc_page) + strlen(scontext);
+ new_page = MemoryContextAllocZero(AvcMemCtx, length);
+
+ strcpy(new_page->scontext, scontext);
+ for (i = 0; i < AVC_HASH_NUM_SLOTS; i++)
+ new_page->slot[i] = NIL;
+
+ if (!current_page)
+ new_page->next = new_page;
+ else
+ {
+ new_page->next = current_page->next;
+ current_page->next = new_page;
+ }
+ current_page = new_page;
+}
+
+
+static void
+sepgsql_avc_reset(void)
+{
+ Assert(AvcMemCtx != NULL);
+
+ MemoryContextReset(AvcMemCtx);
+
+ current_page = NULL;
+
+ sepgsql_avc_switch(sepgsql_get_client_label());
+}
+
+static bool
+sepgsql_avc_is_valid(void)
+{
+ bool result = true;
+
+ LWLockAcquire(SepgsqlAvcLock, LW_SHARED);
+ if (avc_version != selinux_state->version)
+ {
+ sepgsql_avc_reset();
+
+ /* copy current version to local variable */
+ avc_version = selinux_state->version;
+
+ result = false;
+ }
+ LWLockRelease(SepgsqlAvcLock);
+
+ return result;
+}
+
+
+static void
+sepgsql_avc_reclaim(avc_page *page)
+{
+ ListCell *l;
+
+ while (page->avc_count > AVC_HASH_NUM_NODES - 10)
+ {
+ foreach (l, page->slot[page->lru_hint])
+ {
+ avc_datum *cache = lfirst(l);
+
+ if (cache->hot_cache)
+ cache->hot_cache = false;
+ {
+ list_delete_ptr(page->slot[page->lru_hint], cache);
+ pfree(cache);
+ page->avc_count--;
+ }
+ }
+ page->lru_hint = (page->lru_hint + 1) % AVC_HASH_NUM_SLOTS;
+ }
+}
+
+static avc_datum *
+sepgsql_avc_make_entry(avc_page *page,
+ sepgsql_sid_t tsid, uint16 tclass, Oid nrelid)
+{
+ struct av_decision avd;
+ MemoryContext oldctx;
+ char *scontext;
+ char *tcontext;
+ char *ncontext;
+ avc_datum *cache;
+ uint32 hash_key, index;
+
+ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass, nrelid);
+ index = hash_key % AVC_HASH_NUM_SLOTS;
+
+ oldctx = MemoryContextSwitchTo(AvcMemCtx);
+
+ scontext = page->scontext;
+ tcontext = seclabelRawOutput(tsid.relid, tsid.secid);
+ ncontext = sepgsql_compute_create(scontext, tcontext, tclass);
+
+ sepgsql_compute_avd(scontext, tcontext, tclass, &avd);
+
+ cache = palloc0(sizeof(avc_datum));
+
+ cache->hash_key = hash_key;
+
+ cache->tclass = tclass;
+
+ cache->hot_cache = true;
+ cache->tcontext = tcontext;
+ cache->ncontext = ncontext;
+
+ cache->tsid.relid = tsid.relid;
+ cache->tsid.secid = tsid.secid;
+
+ if (OidIsValid(nrelid))
+ {
+ cache->nsid.relid = nrelid;
+ cache->nsid.secid = seclabelRawInput(nrelid, ncontext);
+ }
+
+ cache->allowed = avd.allowed;
+ cache->auditallow = avd.auditallow;
+ cache->auditdeny = avd.auditdeny;
+ if (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE)
+ cache->permissive = true;
+
+ if (page->avc_count > AVC_HASH_NUM_NODES)
+ sepgsql_avc_reclaim(page);
+
+ page->slot[index] = lcons(cache, page->slot[index]);
+ page->avc_count++;
+
+ MemoryContextSwitchTo(oldctx);
+
+ return cache;
+}
+
+static avc_datum *
+sepgsql_avc_lookup(avc_page *page,
+ sepgsql_sid_t tsid, uint16 tclass, Oid nrelid)
+{
+ avc_datum *cache = NULL;
+ uint32 hash_key, index;
+ ListCell *l;
+
+ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass, nrelid);
+ index = hash_key % AVC_HASH_NUM_SLOTS;
+
+ foreach (l, page->slot[index])
+ {
+ cache = lfirst(l);
+ if (cache->hash_key == hash_key &&
+ cache->tclass == tclass &&
+ cache->tsid.relid == tsid.relid &&
+ cache->tsid.secid == tsid.secid &&
+ cache->nsid.relid == nrelid)
+ {
+ cache->hot_cache = true;
+ return cache;
+ }
+ }
+ return NULL;
+}
+
+bool
+sepgsql_client_perms(sepgsql_sid_t tsid,
+ uint16 tclass, uint32 required,
+ const char *audit_name, bool abort)
+{
+ avc_datum *cache;
+ uint32 denied, audited;
+ bool result = true;
+
+ do {
+ cache = sepgsql_avc_lookup(current_page,
+ tsid, tclass, InvalidOid);
+ if (!cache)
+ cache = sepgsql_avc_make_entry(current_page,
+ tsid, tclass, InvalidOid);
+ } while (!sepgsql_avc_is_valid());
+
+ denied = required & ~cache->allowed;
+ if (sepgsql_debug_audit && tclass != SEPG_CLASS_DB_TUPLE)
+ audited = (denied ? (denied & ~0) : (required & ~0));
+ else
+ audited = (denied ? (denied & cache->auditdeny)
+ : (required & cache->auditallow));
+
+ if (audited)
+ {
+ sepgsql_audit_log(!!denied,
+ current_page->scontext,
+ seclabelRawOutput(tsid.relid, tsid.secid),
+ tclass, audited, audit_name);
+ }
+
+ if (denied)
+ {
+ if (!sepgsql_get_enforce() || cache->permissive)
+ cache->allowed |= required; /* prevent flood of audit log */
+ else
+ {
+ if (abort)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("SELinux: security policy violation")));
+ result = false;
+ }
+ }
+
+ return result;
+}
+
+sepgsql_sid_t
+sepgsql_client_create_secid(sepgsql_sid_t tsid, uint16 tclass, Oid nrelid)
+{
+ avc_datum *cache;
+
+ do {
+ cache = sepgsql_avc_lookup(current_page, tsid, tclass, nrelid);
+
+ if (!cache)
+ cache = sepgsql_avc_make_entry(current_page,
+ tsid, tclass, nrelid);
+ } while (!sepgsql_avc_is_valid());
+
+ return cache->nsid;
+}
+
+char *
+sepgsql_client_create_label(sepgsql_sid_t tsid, uint16 tclass)
+{
+ avc_datum *cache;
+
+ do {
+ cache = sepgsql_avc_lookup(current_page, tsid, tclass, InvalidOid);
+
+ if (!cache)
+ cache = sepgsql_avc_make_entry(current_page,
+ tsid, tclass, InvalidOid);
+ } while (!sepgsql_avc_is_valid());
+
+ return cache->ncontext;
+}
+
+static void
+sepgsql_avc_xact_callback(XactEvent event, void *arg)
+{
+ if (event == XACT_EVENT_ABORT)
+ sepgsql_avc_reset();
+}
+
+static void
+sepgsql_avc_sub_xact_callback(SubXactEvent event, SubTransactionId mySubid,
+ SubTransactionId parentSubid, void *arg)
+{
+ if (event == SUBXACT_EVENT_ABORT_SUB)
+ sepgsql_avc_reset();
+}
+
+void
+sepgsql_avc_init(void)
+{
+ sepgsql_shmem_init();
+
+ AvcMemCtx = AllocSetContextCreate(TopMemoryContext,
+ "Userspace AVC",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ /*
+ * userspace avc should be invalidate when the current transaction
+ * is aborted on errors, because sid to be created shall be rollbacked.
+ */
+ RegisterXactCallback(sepgsql_avc_xact_callback, NULL);
+ RegisterSubXactCallback(sepgsql_avc_sub_xact_callback, NULL);
+}
+
+/* ------------------------------------------------------------
+ *
+ * SELinux state monitor process
+ *
+ * ------------------------------------------------------------
+ */
+static int
+sepgsql_cb_log(int type, const char *fmt, ...)
+{
+ char *c, buffer[1024];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buffer, sizeof(buffer), fmt, ap);
+ va_end(ap);
+
+ c = strrchr(buffer, '\n');
+ if (c)
+ *c = '\0';
+
+ ereport(LOG,(errmsg("%s", buffer)));
+
+ return 0;
+}
+
+static int
+sepgsql_cb_setenforce(int enforce)
+{
+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE);
+ selinux_state->enforcing = (enforce ? true : false);
+ selinux_state->version++;
+ LWLockRelease(SepgsqlAvcLock);
+
+ return 0;
+}
+
+static int
+sepgsql_cb_policyload(int seqno)
+{
+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE);
+ selinux_state->version++;
+ LWLockRelease(SepgsqlAvcLock);
+
+ return 0;
+}
+
+void
+sepgsql_avc_worker_main(void)
+{
+ union selinux_callback cb;
+
+ Assert(sepgsql_is_enabled());
+
+#ifdef HAVE_SETSID
+ if (setsid() < 0)
+ elog(FATAL, "setsid() failed: %m");
+#endif
+
+ /*
+ * setup the signal handler
+ */
+ pqinitmask();
+ pqsignal(SIGHUP, SIG_IGN);
+ pqsignal(SIGINT, SIG_IGN);
+ pqsignal(SIGTERM, exit);
+ pqsignal(SIGQUIT, exit);
+ pqsignal(SIGUSR1, SIG_IGN);
+ pqsignal(SIGUSR2, SIG_IGN);
+ pqsignal(SIGCHLD, SIG_DFL);
+ PG_SETMASK(&UnBlockSig);
+
+ /*
+ * map shared memory segment
+ */
+ sepgsql_shmem_init();
+
+ ereport(LOG, (errmsg("SELinux: netlink receiver (pid=%u)", getpid())));
+
+ /*
+ * setup callback functions from avc_netlink_loop()
+ */
+ cb.func_log = sepgsql_cb_log;
+ selinux_set_callback(SELINUX_CB_LOG, cb);
+ cb.func_setenforce = sepgsql_cb_setenforce;
+ selinux_set_callback(SELINUX_CB_SETENFORCE, cb);
+ cb.func_policyload = sepgsql_cb_policyload;
+ selinux_set_callback(SELINUX_CB_POLICYLOAD, cb);
+
+ /*
+ * open netlink socket and wait for messages
+ */
+ avc_netlink_open(1);
+
+ avc_netlink_loop();
+
+ exit(0);
+}
diff --git a/src/backend/sepgsql/blob.c b/src/backend/sepgsql/blob.c
new file mode 100644
index 0000000..e60e9cc
--- /dev/null
+++ b/src/backend/sepgsql/blob.c
@@ -0,0 +1,245 @@
+/*
+ * blob.c
+ *
+ * SELinux hooks related to large objects
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/sysattr.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_largeobject.h"
+#include "catalog/pg_largeobject_metadata.h"
+#include "catalog/pg_seclabel.h"
+#include "miscadmin.h"
+#include "sepgsql/hooks.h"
+#include "sepgsql/sepgsql.h"
+#include "utils/fmgroids.h"
+#include "utils/tqual.h"
+
+#ifdef HAVE_SELINUX
+bool
+sepgsql_largeobject_common(Oid loid, Snapshot snapshot,
+ uint32 required, bool abort)
+{
+ Relation pg_lo_meta;
+ ScanKeyData skey;
+ SysScanDesc scan;
+ HeapTuple tuple;
+ sepgsql_sid_t tsid;
+ char auname[NAMEDATALEN];
+ bool retval;
+
+ snprintf(auname, sizeof(auname), "blob_%u", loid);
+
+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId,
+ AccessShareLock);
+
+ ScanKeyInit(&skey,
+ ObjectIdAttributeNumber,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(loid));
+
+ scan = systable_beginscan(pg_lo_meta,
+ LargeObjectMetadataOidIndexId, true,
+ snapshot, 1, &skey);
+
+ tuple = systable_getnext(scan);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "large object %u does not exist", loid);
+
+ tsid.relid = LargeObjectMetadataRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_BLOB,
+ required,
+ auname,
+ abort);
+ systable_endscan(scan);
+
+ heap_close(pg_lo_meta, AccessShareLock);
+
+ return retval;
+}
+#endif
+
+Oid
+sepgsql_largeobject_create(Oid loid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+ char auname[NAMEDATALEN];
+
+ nsid = sepgsql_get_default_blob_secid(MyDatabaseId);
+ snprintf(auname, sizeof(auname), "blob_%u", loid);
+
+ /* db_blob:{create} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_BLOB,
+ SEPG_DB_BLOB__CREATE,
+ auname,
+ true);
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_largeobject_alter(Oid loid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_largeobject_common(loid, SnapshotNow,
+ SEPG_DB_BLOB__SETATTR, true);
+ }
+#endif
+}
+
+Oid
+sepgsql_largeobject_relabel(Oid loid, char *newLabel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+ char auname[NAMEDATALEN];
+
+ nsid.relid = LargeObjectMetadataRelationId;
+ nsid.secid = seclabelTransInput(nsid.relid, newLabel);
+
+ snprintf(auname, sizeof(auname), "blob_%u", loid);
+
+ /* db_blob:{setattr relabelfrom} */
+ sepgsql_largeobject_common(loid,
+ SnapshotNow,
+ SEPG_DB_BLOB__SETATTR |
+ SEPG_DB_BLOB__RELABELFROM,
+ true);
+ /* db_blob:{relabelto} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_BLOB,
+ SEPG_DB_BLOB__RELABELTO,
+ auname,
+ true);
+ return nsid.secid;
+ }
+#endif
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("SE-PostgreSQL is not available")));
+ return InvalidOid;
+}
+
+void
+sepgsql_largeobject_drop(Oid loid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_largeobject_common(loid, SnapshotNow,
+ SEPG_DB_BLOB__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_largeobject_read(Oid loid, Snapshot snapshot)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_largeobject_common(loid, snapshot,
+ SEPG_DB_BLOB__READ, true);
+ }
+#endif
+}
+
+void
+sepgsql_largeobject_write(Oid loid, Snapshot snapshot)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_largeobject_common(loid, snapshot,
+ SEPG_DB_BLOB__WRITE, true);
+ }
+#endif
+}
+
+Oid
+sepgsql_largeobject_import(Oid loid, const char *filename)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+ char auname[NAMEDATALEN];
+
+ nsid = sepgsql_get_default_blob_secid(MyDatabaseId);
+ snprintf(auname, sizeof(auname), "blob_%u", loid);
+
+ /* db_blob:{create} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_BLOB,
+ SEPG_DB_BLOB__CREATE |
+ SEPG_DB_BLOB__WRITE |
+ SEPG_DB_BLOB__IMPORT,
+ auname,
+ true);
+ /* db_file:{read} */
+ // XXX - todo: add file read checks
+
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_largeobject_export(Oid loid, Snapshot snapshot, const char *filename)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_blob:{read export} */
+ sepgsql_largeobject_common(loid, snapshot,
+ SEPG_DB_BLOB__READ |
+ SEPG_DB_BLOB__EXPORT, true);
+ /* file:{write} */
+ // TODO: add security checks
+ }
+#endif
+}
+
+void
+sepgsql_largeobject_grant(Oid loid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_largeobject_common(loid, SnapshotNow,
+ SEPG_DB_BLOB__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_largeobject_comment(Oid loid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_largeobject_common(loid, SnapshotNow,
+ SEPG_DB_BLOB__SETATTR, true);
+ }
+#endif
+}
diff --git a/src/backend/sepgsql/conversion.c b/src/backend/sepgsql/conversion.c
new file mode 100644
index 0000000..6a60284
--- /dev/null
+++ b/src/backend/sepgsql/conversion.c
@@ -0,0 +1,147 @@
+/*
+ * conversion.c
+ *
+ * SELinux hooks related to conversion
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "catalog/pg_conversion.h"
+#include "sepgsql/hooks.h"
+#include "sepgsql/sepgsql.h"
+#include "utils/syscache.h"
+
+#ifdef HAVE_SELINUX
+bool
+sepgsql_conversion_common(Oid convOid, uint32 required, bool abort)
+{
+ sepgsql_sid_t tsid;
+ HeapTuple tuple;
+ const char *auname;
+ bool retval;
+
+ tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(convOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for conversion %u", convOid);
+
+ tsid.relid = ConversionRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ auname = NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname);
+
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ required,
+ auname,
+ abort);
+ ReleaseSysCache(tuple);
+
+ return retval;
+}
+
+static Oid
+get_conversion_namespace(Oid convOid)
+{
+ HeapTuple tuple;
+ Oid namespaceId = InvalidOid;
+
+ tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(convOid));
+ if (HeapTupleIsValid(tuple))
+ {
+ namespaceId = ((Form_pg_conversion) GETSTRUCT(tuple))->connamespace;
+
+ ReleaseSysCache(tuple);
+ }
+ return namespaceId;
+}
+#endif
+
+Oid
+sepgsql_conversion_create(const char *convName,
+ Oid namespaceId, Oid conversionFunc)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+
+ nsid = sepgsql_get_default_tuple_secid(ConversionRelationId);
+
+ /* db_schema:{add_name} */
+ sepgsql_schema_common(namespaceId,
+ SEPG_DB_SCHEMA__ADD_NAME, true);
+ /* db_procedure:{install} */
+ sepgsql_proc_common(conversionFunc,
+ SEPG_DB_PROCEDURE__INSTALL, true);
+ /* db_tuple:{insert} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__INSERT,
+ convName,
+ true);
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_conversion_alter(Oid convOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_conversion_alter_rename(Oid convOid, const char *newName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_conversion_namespace(convOid);
+
+ /* db_schema:{add_name remove_name} */
+ sepgsql_schema_common(namespaceId,
+ SEPG_DB_SCHEMA__ADD_NAME |
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_tuple:{update} */
+ sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_conversion_drop(Oid convOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_conversion_namespace(convOid);
+
+ /* db_schema:{remove_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_tuple:{delete} */
+ sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__DELETE, true);
+ }
+#endif
+}
+
+void
+sepgsql_conversion_comment(Oid convOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
diff --git a/src/backend/sepgsql/database.c b/src/backend/sepgsql/database.c
new file mode 100644
index 0000000..bee9d36
--- /dev/null
+++ b/src/backend/sepgsql/database.c
@@ -0,0 +1,201 @@
+/*
+ * database.c
+ *
+ * SELinux hooks related to database
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "catalog/pg_database.h"
+#include "catalog/pg_seclabel.h"
+#include "commands/dbcommands.h"
+#include "miscadmin.h"
+#include "sepgsql/hooks.h"
+#include "sepgsql/sepgsql.h"
+#include "utils/syscache.h"
+
+#ifdef HAVE_SELINUX
+bool
+sepgsql_database_common(Oid datOid, uint32 required, bool abort)
+{
+ HeapTuple tuple;
+ sepgsql_sid_t tsid;
+ const char *auname;
+ bool retval;
+
+ tuple = SearchSysCache1(DATABASEOID,
+ ObjectIdGetDatum(datOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for database %u", datOid);
+
+ tsid.relid = DatabaseRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ auname = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname);
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_DATABASE,
+ required,
+ auname,
+ abort);
+ ReleaseSysCache(tuple);
+
+ return retval;
+}
+#endif
+
+Oid
+sepgsql_database_create(const char *datName, Oid templateOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+
+ /* compute a default security context */
+ nsid = sepgsql_get_default_database_secid(templateOid);
+
+ /* db_database:{create} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_DATABASE,
+ SEPG_DB_DATABASE__CREATE,
+ datName, true);
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_database_alter(Oid databaseOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_database_common(databaseOid,
+ SEPG_DB_DATABASE__SETATTR,
+ true);
+ }
+#endif
+}
+
+Oid
+sepgsql_database_relabel(Oid databaseOid, char *new_label)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+ char *auname;
+
+ nsid.relid = DatabaseRelationId;
+ nsid.secid = seclabelTransInput(nsid.relid, new_label);
+
+ auname = get_database_name(databaseOid);
+
+ /* db_database:{setattr relabelfrom} */
+ sepgsql_database_common(databaseOid,
+ SEPG_DB_DATABASE__SETATTR |
+ SEPG_DB_DATABASE__RELABELFROM,
+ true);
+
+ /* db_database:{relabelto} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_DATABASE,
+ SEPG_DB_DATABASE__RELABELTO,
+ auname,
+ true);
+ pfree(auname);
+
+ return nsid.secid;
+ }
+#endif
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("SE-PostgreSQL is not available")));
+ return InvalidOid;
+}
+
+void
+sepgsql_database_drop(Oid databaseOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_database_common(databaseOid,
+ SEPG_DB_DATABASE__DROP,
+ true);
+ }
+#endif
+}
+
+void
+sepgsql_database_grant(Oid databaseOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_database_common(databaseOid,
+ SEPG_DB_DATABASE__SETATTR,
+ true);
+ }
+#endif
+}
+
+void
+sepgsql_database_comment(Oid databaseOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_database_common(databaseOid,
+ SEPG_DB_DATABASE__SETATTR,
+ true);
+ }
+#endif
+}
+
+void
+sepgsql_database_connect(Oid databaseOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ if (!sepgsql_database_common(databaseOid,
+ SEPG_DB_DATABASE__ACCESS,
+ false))
+ ereport(FATAL,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("permission denied for database \"%s\"",
+ get_database_name(databaseOid))));
+ }
+#endif
+}
+
+void
+sepgsql_database_reindex(Oid databaseOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ // TODO: check db_table:{indexon} for each
+
+
+
+ }
+#endif
+}
+
+void
+sepgsql_database_getattr(Oid databaseOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_database_common(databaseOid,
+ SEPG_DB_DATABASE__GETATTR,
+ true);
+ }
+#endif
+}
diff --git a/src/backend/sepgsql/fdw.c b/src/backend/sepgsql/fdw.c
new file mode 100644
index 0000000..be4a34b
--- /dev/null
+++ b/src/backend/sepgsql/fdw.c
@@ -0,0 +1,296 @@
+/*
+ * fdw.c
+ *
+ * SELinux hooks related to foreign data wrapper
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "catalog/pg_foreign_data_wrapper.h"
+#include "catalog/pg_foreign_server.h"
+#include "catalog/pg_user_mapping.h"
+#include "miscadmin.h"
+#include "sepgsql/hooks.h"
+#include "sepgsql/sepgsql.h"
+#include "utils/syscache.h"
+
+#ifdef HAVE_SELINUX
+bool
+sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort)
+{
+ Form_pg_foreign_data_wrapper fdwForm;
+ HeapTuple tuple;
+ sepgsql_sid_t tsid;
+ bool retval;
+
+ tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for foreign-data-wrapper %u", fdwOid);
+ fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple);
+
+ tsid.relid = ForeignDataWrapperRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ required,
+ NameStr(fdwForm->fdwname),
+ abort);
+ ReleaseSysCache(tuple);
+
+ return retval;
+}
+
+bool
+sepgsql_fserver_common(Oid fservOid, uint32 required, bool abort)
+{
+ HeapTuple tuple;
+ sepgsql_sid_t tsid;
+ const char *auname;
+ bool retval;
+
+ tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(fservOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for foreign-server %u", fservOid);
+
+ tsid.relid = ForeignServerRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ auname = NameStr(((Form_pg_foreign_server) GETSTRUCT(tuple))->srvname);
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ required,
+ auname,
+ abort);
+ ReleaseSysCache(tuple);
+
+ return retval;
+}
+
+static char *
+get_fserver_name(Oid fservOid)
+{
+ Form_pg_foreign_server servForm;
+ HeapTuple tuple;
+ char *srvname = NULL;
+
+ tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(fservOid));
+ if (HeapTupleIsValid(tuple))
+ {
+ servForm = (Form_pg_foreign_server) GETSTRUCT(tuple);
+
+ srvname = pstrdup(NameStr(servForm->srvname));
+
+ ReleaseSysCache(tuple);
+ }
+ return srvname;
+}
+
+bool
+sepgsql_user_mapping_common(Oid umapOid, uint32 required, bool abort)
+{
+ HeapTuple tuple;
+ sepgsql_sid_t tsid;
+ Oid umuser;
+ Oid umserver;
+ char auname[NAMEDATALEN * 2 + 10];
+ bool retval;
+
+ tuple = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(umapOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for user mapping %u", umapOid);
+
+ tsid.relid = UserMappingRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ umuser = ((Form_pg_user_mapping) GETSTRUCT(tuple))->umuser;
+ umserver = ((Form_pg_user_mapping) GETSTRUCT(tuple))->umserver;
+
+ snprintf(auname, sizeof(auname), "%s@%s",
+ OidIsValid(umuser) ? GetUserNameFromId(umuser) : "public",
+ get_fserver_name(umserver));
+
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ required,
+ auname,
+ abort);
+ ReleaseSysCache(tuple);
+
+ return retval;
+}
+#endif
+
+Oid
+sepgsql_fdw_create(const char *fdwName, Oid validatorFunc)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid
+ = sepgsql_get_default_tuple_secid(ForeignDataWrapperRelationId);
+
+ /* db_procedure:{install} */
+ if (OidIsValid(validatorFunc))
+ sepgsql_proc_common(validatorFunc,
+ SEPG_DB_PROCEDURE__INSTALL, true);
+
+ /* db_tuple:{insert} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__INSERT,
+ fdwName,
+ true);
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_fdw_alter(Oid fdwOid, Oid newValidator)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_procedure:{install} */
+ if (OidIsValid(newValidator))
+ sepgsql_proc_common(newValidator,
+ SEPG_DB_PROCEDURE__INSTALL, true);
+ /* db_tuple:{update} */
+ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_fdw_drop(Oid fdwOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{delete} */
+ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__DELETE, true);
+ }
+#endif
+}
+
+void
+sepgsql_fdw_grant(Oid fdwOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+Oid
+sepgsql_fserver_create(const char *fservName, Oid fdwOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid
+ = sepgsql_get_default_tuple_secid(ForeignServerRelationId);
+
+ /* db_tuple:{insert} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__INSERT,
+ fservName,
+ true);
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_fserver_alter(Oid fservOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_fserver_common(fservOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_fserver_drop(Oid fservOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_fserver_common(fservOid, SEPG_DB_TUPLE__DELETE, true);
+ }
+#endif
+}
+
+void
+sepgsql_fserver_grant(Oid fservOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_fserver_common(fservOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+Oid
+sepgsql_user_mapping_create(Oid umuserId, Oid fservOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ char auname[NAMEDATALEN * 2 + 10];
+ sepgsql_sid_t nsid
+ = sepgsql_get_default_tuple_secid(UserMappingRelationId);
+
+ snprintf(auname, sizeof(auname), "%s@%s",
+ OidIsValid(umuserId) ? GetUserNameFromId(umuserId) : "public",
+ get_fserver_name(fservOid));
+
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__INSERT,
+ auname,
+ true);
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_user_mapping_alter(Oid umapOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_user_mapping_common(umapOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_user_mapping_drop(Oid umapOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{delete} */
+ sepgsql_user_mapping_common(umapOid, SEPG_DB_TUPLE__DELETE, true);
+ }
+#endif
+}
diff --git a/src/backend/sepgsql/file.c b/src/backend/sepgsql/file.c
new file mode 100644
index 0000000..9a9e8a3
--- /dev/null
+++ b/src/backend/sepgsql/file.c
@@ -0,0 +1,287 @@
+/*
+ * file.c
+ *
+ * SELinux hooks related to filesystem
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "sepgsql/hooks.h"
+#include "sepgsql/sepgsql.h"
+
+#include <libgen.h>
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#ifdef HAVE_SELINUX
+static uint16
+sepgsql_file_class(const char *filename, bool abort)
+{
+ struct stat stbuf;
+ uint16 tclass;
+
+ if (stat(filename, &stbuf) < 0)
+ {
+ if (abort)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not stat file \"%s\": %m", filename)));
+ return 0;
+ }
+
+ /* decide object class to be applied */
+ if (S_ISDIR(stbuf.st_mode))
+ tclass = SEPG_CLASS_DIR;
+ else if (S_ISCHR(stbuf.st_mode))
+ tclass = SEPG_CLASS_CHR_FILE;
+ else if (S_ISBLK(stbuf.st_mode))
+ tclass = SEPG_CLASS_BLK_FILE;
+ else if (S_ISFIFO(stbuf.st_mode))
+ tclass = SEPG_CLASS_FIFO_FILE;
+ else if (S_ISLNK(stbuf.st_mode))
+ tclass = SEPG_CLASS_LNK_FILE;
+ else if (S_ISSOCK(stbuf.st_mode))
+ tclass = SEPG_CLASS_SOCK_FILE;
+ else
+ tclass = SEPG_CLASS_FILE;
+
+ return tclass;
+}
+
+static bool
+sepgsql_file_common(const char *filename, uint16 tclass, uint32 required, bool abort)
+{
+ struct stat stbuf;
+ char *scontext = sepgsql_get_client_label();
+ char *tcontext;
+ char *temp;
+ bool retval;
+
+ if (stat(filename, &stbuf) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not stat file \"%s\"", filename)));
+
+ if (getfilecon_raw(filename, &temp) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not get security context \"%s\"", filename)));
+
+ PG_TRY();
+ {
+ tcontext = pstrdup(temp);
+ }
+ PG_CATCH();
+ {
+ freecon(temp);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(temp);
+
+ retval = sepgsql_compute_perms(scontext, tcontext,
+ tclass, required,
+ filename, abort);
+ pfree(tcontext);
+
+ return retval;
+}
+#endif
+
+void
+sepgsql_file_getattr(const char *filename)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ uint16 tclass = sepgsql_file_class(filename, true);
+
+ sepgsql_file_common(filename, tclass,
+ SEPG_FILE__GETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_file_read(const char *filename)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ uint16 tclass = sepgsql_file_class(filename, true);
+
+ sepgsql_file_common(filename, tclass,
+ SEPG_FILE__READ, true);
+ }
+#endif
+}
+
+void
+sepgsql_file_write(const char *filename, bool may_create)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ uint16 tclass = sepgsql_file_class(filename, !may_create);
+ char *dir_name;
+ char *tcontext;
+ char *ncontext;
+ char *temp;
+
+ /* write an existing file */
+ if (tclass != 0)
+ {
+ sepgsql_file_common(filename, tclass,
+ SEPG_FILE__WRITE, true);
+ return;
+ }
+
+ /* create a new file, and write it */
+ dir_name = dirname(pstrdup(filename));
+
+ if (sepgsql_file_class(dir_name, true) != SEPG_CLASS_DIR)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a directory", dir_name)));
+
+ if (getfilecon(dir_name, &temp) < 0)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not get security context \"%s\"", dir_name)));
+ PG_TRY();
+ {
+ tcontext = pstrdup(temp);
+ }
+ PG_CATCH();
+ {
+ freecon(temp);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(temp);
+
+ /* dir:{add_name} */
+ sepgsql_file_common(dir_name, SEPG_CLASS_DIR,
+ SEPG_DIR__ADD_NAME, true);
+
+ /* file:{create write} */
+ ncontext = sepgsql_compute_create(sepgsql_get_client_label(),
+ tcontext, SEPG_CLASS_FILE);
+ sepgsql_compute_perms(sepgsql_get_client_label(),
+ ncontext,
+ SEPG_CLASS_FILE,
+ SEPG_FILE__CREATE |
+ SEPG_FILE__WRITE,
+ filename, true);
+ }
+#endif
+}
+
+void
+sepgsql_file_append(const char *filename)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ if (sepgsql_file_class(filename, true) != SEPG_CLASS_FILE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a regular file", filename)));
+
+ sepgsql_file_common(filename, SEPG_CLASS_FILE,
+ SEPG_FILE__APPEND, true);
+ }
+#endif
+}
+
+void
+sepgsql_file_unlink(const char *filename)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ uint16 tclass = sepgsql_file_class(filename, true);
+
+ sepgsql_file_common(filename, tclass, SEPG_FILE__UNLINK, true);
+ }
+#endif
+}
+
+void
+sepgsql_file_rename(const char *oldpath, const char *newpath)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ char *olddir = dirname(pstrdup(oldpath));
+ char *newdir = dirname(pstrdup(newpath));
+ uint16 tclass;
+ uint32 required;
+
+ /* file:{setattr} on oldpath */
+ tclass = sepgsql_file_class(oldpath, true);
+ sepgsql_file_common(oldpath, tclass,
+ SEPG_FILE__RENAME, true);
+
+ if (sepgsql_file_class(olddir, true) != SEPG_CLASS_DIR)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a directory", olddir)));
+
+ required = SEPG_DIR__SEARCH | SEPG_DIR__REMOVE_NAME;
+ if (strcmp(olddir, newdir) == 0)
+ {
+ /* dir:{search remove_name add_name} */
+ required |= SEPG_DIR__ADD_NAME;
+ sepgsql_file_common(olddir, SEPG_CLASS_DIR, required, true);
+ }
+ else
+ {
+ /* dir:{search remove_name (reparent)} */
+ if (tclass == SEPG_CLASS_DIR)
+ required |= SEPG_DIR__REPARENT;
+ sepgsql_file_common(olddir, SEPG_CLASS_DIR, required, true);
+
+ if (sepgsql_file_class(newdir, true) != SEPG_CLASS_DIR)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not a directory", newdir)));
+
+ /* dir:{add_name} */
+ sepgsql_file_common(newdir, SEPG_CLASS_DIR,
+ SEPG_DIR__ADD_NAME, true);
+ }
+
+ /* file:{unlink} or dir:{DIR__RMDIR}, if necessary */
+ tclass = sepgsql_file_class(newpath, false);
+ if (tclass == SEPG_CLASS_DIR)
+ sepgsql_file_common(newpath, tclass, SEPG_DIR__RMDIR, true);
+ else if (tclass > 0)
+ sepgsql_file_common(newpath, tclass, SEPG_FILE__UNLINK, true);
+ }
+#endif
+}
+
+void
+sepgsql_dir_list(const char *dirname)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ uint16 tclass = sepgsql_file_class(dirname, true);
+
+ if (tclass != SEPG_CLASS_DIR)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is not directory", dirname)));
+
+ sepgsql_file_common(dirname, tclass, SEPG_DIR__READ, true);
+ }
+#endif
+}
diff --git a/src/backend/sepgsql/label.c b/src/backend/sepgsql/label.c
new file mode 100644
index 0000000..7ab355c
--- /dev/null
+++ b/src/backend/sepgsql/label.c
@@ -0,0 +1,656 @@
+/*
+ * label.c
+ * SE-PostgreSQL security label management
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "access/relscan.h"
+#include "access/xact.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_database.h"
+#include "catalog/pg_largeobject_metadata.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_seclabel.h"
+#include "catalog/pg_type.h"
+#include "commands/dbcommands.h"
+#include "miscadmin.h"
+#include "sepgsql/sepgsql.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
+#include "utils/tqual.h"
+#include <fnmatch.h> // for workaround hack
+#include <selinux/selinux.h>
+
+/*
+ * guc parameter to turn on/off mcstrans
+ */
+bool sepgsql_mcstrans;
+
+sepgsql_sid_t
+sepgsql_move_secid(Oid dst_relid, sepgsql_sid_t ssid)
+{
+ char *label;
+ sepgsql_sid_t dsid = { .relid = dst_relid,
+ .secid = InvalidOid };
+
+ label = seclabelRawOutput(ssid.relid, ssid.secid);
+ if (label)
+ {
+ dsid.secid = seclabelRawInput(dsid.relid, label);
+
+ pfree(label);
+ }
+ return dsid;
+}
+
+static sepgsql_sid_t
+get_default_secid_with_database(Oid relOid, Oid databaseOid, uint16 tclass)
+{
+ HeapTuple tuple;
+ sepgsql_sid_t datsid;
+
+ tuple = SearchSysCache1(DATABASEOID,
+ ObjectIdGetDatum(databaseOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for database: %u", databaseOid);
+
+ datsid.relid = DatabaseRelationId;
+ datsid.secid = HeapTupleGetSecid(tuple);
+
+ ReleaseSysCache(tuple);
+
+ return sepgsql_client_create_secid(datsid, tclass, relOid);
+}
+
+static sepgsql_sid_t
+get_default_secid_with_schema(Oid relOid, Oid namespaceOid, uint16 tclass)
+{
+ HeapTuple tuple;
+ sepgsql_sid_t nspsid;
+
+ tuple = SearchSysCache1(NAMESPACEOID,
+ ObjectIdGetDatum(namespaceOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for namespace: %u", namespaceOid);
+
+ nspsid.relid = NamespaceRelationId;
+ nspsid.secid = HeapTupleGetSecid(tuple);
+
+ ReleaseSysCache(tuple);
+
+ return sepgsql_client_create_secid(nspsid, tclass, relOid);
+}
+
+static sepgsql_sid_t
+get_default_secid_with_table(Oid relOid, Oid tableOid, uint16 tclass)
+{
+ HeapTuple tuple;
+ sepgsql_sid_t tblsid;
+
+ tuple = SearchSysCache1(RELOID,
+ ObjectIdGetDatum(tableOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation: %u", tableOid);
+
+ tblsid.relid = RelationRelationId;
+ tblsid.secid = HeapTupleGetSecid(tuple);
+
+ ReleaseSysCache(tuple);
+
+ return sepgsql_client_create_secid(tblsid, tclass, relOid);
+}
+
+sepgsql_sid_t
+sepgsql_get_default_database_secid(Oid templateOid)
+{
+ return get_default_secid_with_database(DatabaseRelationId,
+ templateOid,
+ SEPG_CLASS_DB_DATABASE);
+}
+
+sepgsql_sid_t
+sepgsql_get_default_schema_secid(Oid databaseOid)
+{
+ return get_default_secid_with_database(NamespaceRelationId,
+ databaseOid,
+ SEPG_CLASS_DB_SCHEMA);
+}
+
+sepgsql_sid_t
+sepgsql_get_default_table_secid(Oid namespaceOid)
+{
+ return get_default_secid_with_schema(RelationRelationId,
+ namespaceOid,
+ SEPG_CLASS_DB_TABLE);
+}
+
+sepgsql_sid_t
+sepgsql_get_default_sequence_secid(Oid namespaceOid)
+{
+ return get_default_secid_with_schema(RelationRelationId,
+ namespaceOid,
+ SEPG_CLASS_DB_SEQUENCE);
+}
+
+sepgsql_sid_t
+sepgsql_get_default_view_secid(Oid namespaceOid)
+{
+ return get_default_secid_with_schema(RelationRelationId,
+ namespaceOid,
+ SEPG_CLASS_DB_VIEW);
+}
+
+sepgsql_sid_t
+sepgsql_get_default_proc_secid(Oid namespaceOid)
+{
+ return get_default_secid_with_schema(ProcedureRelationId,
+ namespaceOid,
+ SEPG_CLASS_DB_PROCEDURE);
+}
+
+sepgsql_sid_t
+sepgsql_get_default_column_secid(Oid tableOid)
+{
+ return get_default_secid_with_table(AttributeRelationId,
+ tableOid,
+ SEPG_CLASS_DB_COLUMN);
+}
+
+sepgsql_sid_t
+sepgsql_get_default_tuple_secid(Oid tableOid)
+{
+ return get_default_secid_with_table(tableOid,
+ tableOid,
+ SEPG_CLASS_DB_TUPLE);
+}
+
+sepgsql_sid_t
+sepgsql_get_default_blob_secid(Oid databaseOid)
+{
+ return get_default_secid_with_database(LargeObjectMetadataRelationId,
+ databaseOid,
+ SEPG_CLASS_DB_BLOB);
+}
+
+Oid
+sepgsql_get_default_secid(Relation rel, HeapTuple tuple)
+{
+ Oid namespaceId;
+ sepgsql_sid_t nsid
+ = { .relid = RelationGetRelid(rel), .secid = InvalidOid };
+
+ switch (RelationGetRelid(rel))
+ {
+ case DatabaseRelationId:
+ case RelationRelationId:
+ case AttributeRelationId:
+ elog(WARNING, "Bug? a new tuple without security id on \"%s\"",
+ RelationGetRelationName(rel));
+ break;
+
+ case NamespaceRelationId:
+ nsid = sepgsql_get_default_schema_secid(MyDatabaseId);
+ break;
+
+ case ProcedureRelationId:
+ namespaceId = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace;
+ nsid = sepgsql_get_default_proc_secid(namespaceId);
+ break;
+
+ case LargeObjectMetadataRelationId:
+ nsid = sepgsql_get_default_blob_secid(MyDatabaseId);
+ break;
+
+ default:
+ nsid = sepgsql_get_default_tuple_secid(RelationGetRelid(rel));
+ break;
+ }
+ return nsid.secid;
+}
+
+/*
+ * a workaround implementation until libselinux/refpolicy don't
+ * support db_schema or other object classes.
+ */
+static struct {
+ uint16 tclass;
+ char *pattern;
+ char *context;
+} initial_label_catalog[] = {
+ {SEPG_CLASS_DB_DATABASE, "*",
+ "system_u:object_r:sepgsql_db_t:s0"},
+ {SEPG_CLASS_DB_SCHEMA, "*.*",
+ "system_u:object_r:sepgsql_db_t:s0"},
+ {SEPG_CLASS_DB_TABLE, "*.pg_catalog.*",
+ "system_u:object_r:sepgsql_sysobj_t:s0"},
+ {SEPG_CLASS_DB_TABLE, "*.*.*",
+ "system_u:object_r:sepgsql_table_t:s0"},
+ {SEPG_CLASS_DB_VIEW, "*.*.*",
+ "system_u:object_r:sepgsql_db_t:s0"},
+ {SEPG_CLASS_DB_SEQUENCE, "*.*.*",
+ "system_u:object_r:sepgsql_db_t:s0"},
+ {SEPG_CLASS_DB_PROCEDURE, "*.pg_catalog.*",
+ "system_u:object_r:sepgsql_proc_exec_t:s0"},
+ {SEPG_CLASS_DB_PROCEDURE, "*.*.*",
+ "system_u:object_r:sepgsql_user_proc_exec_t:s0"},
+ {SEPG_CLASS_DB_COLUMN, "*.pg_catalog.*.*",
+ "system_u:object_r:sepgsql_sysobj_t:s0"},
+ {SEPG_CLASS_DB_COLUMN, "*.*.*.*",
+ "system_u:object_r:sepgsql_table_t:s0"},
+ {SEPG_CLASS_DB_TUPLE, "*.pg_catalog.*",
+ "system_u:object_r:sepgsql_sysobj_t:s0"},
+ {SEPG_CLASS_DB_TUPLE, "*.*.*",
+ "system_u:object_r:sepgsql_table_t:s0"},
+ {SEPG_CLASS_DB_BLOB, "*.*",
+ "system_u:object_r:sepgsql_blob_t:s0"},
+ {0, NULL, NULL},
+};
+
+static char *
+lookup_init_catalog(uint16 tclass, const char *name)
+{
+ int i;
+
+ for (i = 0; initial_label_catalog[i].pattern; i++)
+ {
+ if (initial_label_catalog[i].tclass == tclass &&
+ fnmatch(initial_label_catalog[i].pattern, name, 0) == 0)
+ return initial_label_catalog[i].context;
+ }
+ elog(ERROR, "no valid initial security context for %s (tclass=%d)",
+ name, tclass);
+ return NULL; /* for compiler quiet */
+}
+
+static char *
+lookup_init_tuple_label(Oid relOid, HeapTuple tuple)
+{
+ Oid relNsp = get_rel_namespace(relOid);
+ char namebuf[NAMEDATALEN * 3 + 10];
+
+ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s",
+ get_database_name(MyDatabaseId),
+ get_namespace_name(relNsp),
+ get_rel_name(relOid));
+
+ return lookup_init_catalog(SEPG_CLASS_DB_TUPLE, namebuf);
+}
+
+static char *
+lookup_init_database_label(HeapTuple tuple)
+{
+ Form_pg_database datForm = (Form_pg_database) GETSTRUCT(tuple);
+
+ return lookup_init_catalog(SEPG_CLASS_DB_DATABASE,
+ NameStr(datForm->datname));
+}
+
+static char *
+lookup_init_schema_label(HeapTuple tuple)
+{
+ Form_pg_namespace nspForm = (Form_pg_namespace) GETSTRUCT(tuple);
+ char namebuf[NAMEDATALEN * 2 + 10];
+
+ snprintf(namebuf, sizeof(namebuf), "%s.%s",
+ get_database_name(MyDatabaseId),
+ NameStr(nspForm->nspname));
+
+ return lookup_init_catalog(SEPG_CLASS_DB_SCHEMA, namebuf);
+}
+
+static char *
+lookup_init_relation_label(HeapTuple tuple)
+{
+ Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
+ const char *relName = NameStr(classForm->relname);
+ Oid relNsp = classForm->relnamespace;
+ char *seclabel;
+ char namebuf[NAMEDATALEN * 3 + 10];
+
+ switch (classForm->relkind)
+ {
+ case RELKIND_RELATION:
+ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s",
+ get_database_name(MyDatabaseId),
+ get_namespace_name(relNsp), relName);
+ seclabel = lookup_init_catalog(SEPG_CLASS_DB_TABLE, namebuf);
+ break;
+
+ case RELKIND_SEQUENCE:
+ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s",
+ get_database_name(MyDatabaseId),
+ get_namespace_name(relNsp), relName);
+ seclabel = lookup_init_catalog(SEPG_CLASS_DB_SEQUENCE, namebuf);
+ break;
+
+ case RELKIND_VIEW:
+ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s",
+ get_database_name(MyDatabaseId),
+ get_namespace_name(relNsp), relName);
+ seclabel = lookup_init_catalog(SEPG_CLASS_DB_VIEW, namebuf);
+ break;
+
+ case RELKIND_INDEX: {
+ HeapTuple tbltup;
+ HeapTuple indtup;
+ Oid tblOid;
+ Oid indOid = HeapTupleGetOid(tuple);
+
+ indtup = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indOid));
+ if (!HeapTupleIsValid(indtup))
+ elog(ERROR, "cache lookup failed for index %u", indOid);
+
+ tblOid = ((Form_pg_index) GETSTRUCT(indtup))->indrelid;
+ tbltup = SearchSysCache1(RELOID, ObjectIdGetDatum(tblOid));
+ if (!HeapTupleIsValid(tbltup))
+ elog(ERROR, "cache lookup failed for relation %u", tblOid);
+
+ seclabel = lookup_init_relation_label(tbltup);
+
+ ReleaseSysCache(tbltup);
+ ReleaseSysCache(indtup);
+
+ break;
+ }
+ case RELKIND_TOASTVALUE: {
+ HeapTuple tbltup;
+ Oid tblOid;
+
+ /*
+ * XXX - we assume all the toast relation's name is
+ * "pg_toast_%u", and the "%u" shall be replaced by OID
+ * of the relation which owns the toast relation
+ */
+ tblOid = strtoul(relName + 9, NULL, 10);
+
+ tbltup = SearchSysCache1(RELOID,
+ ObjectIdGetDatum(tblOid));
+ if (!HeapTupleIsValid(tbltup))
+ elog(ERROR, "cache lookup failed for relation %u", tblOid);
+
+ seclabel = lookup_init_relation_label(tbltup);
+
+ ReleaseSysCache(tbltup);
+
+ break;
+ }
+ case RELKIND_COMPOSITE_TYPE: {
+ Oid typOid = classForm->reltype;
+ HeapTuple typtup;
+
+ typtup = SearchSysCache1(TYPEOID,
+ ObjectIdGetDatum(typOid));
+ if (!HeapTupleIsValid(typtup))
+ elog(ERROR, "cache lookup failed for type %u", typOid);
+
+ seclabel = lookup_init_tuple_label(TypeRelationId, typtup);
+
+ ReleaseSysCache(typtup);
+
+ break;
+ }
+ default:
+ elog(ERROR, "unexpected relkind %c of \"%s\"",
+ classForm->relkind, relName);
+ seclabel = NULL; /* compiler quiet */
+ break;
+ }
+ return seclabel;
+}
+
+static char *
+lookup_init_attribute_label(HeapTuple tuple)
+{
+ Form_pg_attribute attForm = (Form_pg_attribute) GETSTRUCT(tuple);
+ Oid tblOid = attForm->attrelid;
+ char *seclabel;
+ char namebuf[NAMEDATALEN * 4 + 10];
+
+ if (get_rel_relkind(tblOid) == RELKIND_RELATION)
+ {
+ Oid tblNsp = get_rel_namespace(tblOid);
+
+ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s.%s",
+ get_database_name(MyDatabaseId),
+ get_namespace_name(tblNsp),
+ get_rel_name(tblOid),
+ NameStr(attForm->attname));
+
+ seclabel = lookup_init_catalog(SEPG_CLASS_DB_COLUMN, namebuf);
+ }
+ else
+ {
+ HeapTuple tbltup;
+
+ tbltup = SearchSysCache1(RELOID, ObjectIdGetDatum(tblOid));
+ if (!HeapTupleIsValid(tbltup))
+ elog(ERROR, "cache lookup failed for relation %u", tblOid);
+
+ seclabel = lookup_init_relation_label(tbltup);
+
+ ReleaseSysCache(tbltup);
+ }
+ return seclabel;
+}
+
+static char *
+lookup_init_procedure_label(HeapTuple tuple)
+{
+ Form_pg_proc proForm = (Form_pg_proc) GETSTRUCT(tuple);
+ Oid proNsp = proForm->pronamespace;
+ char namebuf[NAMEDATALEN * 3 + 10];
+
+ snprintf(namebuf, sizeof(namebuf), "%s.%s.%s",
+ get_database_name(MyDatabaseId),
+ get_namespace_name(proNsp),
+ NameStr(proForm->proname));
+
+ return lookup_init_catalog(SEPG_CLASS_DB_PROCEDURE, namebuf);
+}
+
+static char *
+lookup_init_largeobject_label(HeapTuple tuple)
+{
+ char namebuf[NAMEDATALEN + 20];
+
+ snprintf(namebuf, sizeof(namebuf), "%s.%u",
+ get_database_name(MyDatabaseId),
+ HeapTupleGetOid(tuple));
+
+ return lookup_init_catalog(SEPG_CLASS_DB_BLOB, namebuf);
+}
+
+void
+sepgsql_initial_labeling(void)
+{
+ Relation classRel;
+ SysScanDesc classScan;
+ ScanKeyData classSkey;
+ HeapTuple classTup;
+ Relation rel;
+ HeapScanDesc scan;
+ HeapTuple oldtup;
+ HeapTuple newtup;
+
+ Assert(IsBootstrapProcessingMode());
+
+ StartTransactionCommand();
+
+ classRel = heap_open(RelationRelationId, AccessShareLock);
+
+ ScanKeyInit(&classSkey,
+ Anum_pg_class_relhassecids,
+ BTEqualStrategyNumber, F_BOOLEQ,
+ BoolGetDatum(true));
+
+ classScan = systable_beginscan(classRel, InvalidOid, false,
+ SnapshotNow, 1, &classSkey);
+
+ while (HeapTupleIsValid(classTup = systable_getnext(classScan)))
+ {
+ Oid relOid = HeapTupleGetOid(classTup);
+
+ Assert(((Form_pg_class) GETSTRUCT(classTup))->relhassecids);
+
+ rel = heap_open(relOid, RowExclusiveLock);
+
+ scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+
+ while (HeapTupleIsValid(oldtup = heap_getnext(scan, ForwardScanDirection)))
+ {
+ char *label;
+ Oid secid;
+
+ switch (relOid)
+ {
+ case DatabaseRelationId:
+ label = lookup_init_database_label(oldtup);
+ break;
+
+ case NamespaceRelationId:
+ label = lookup_init_schema_label(oldtup);
+ break;
+
+ case RelationRelationId:
+ label = lookup_init_relation_label(oldtup);
+ break;
+
+ case AttributeRelationId:
+ label = lookup_init_attribute_label(oldtup);
+ break;
+
+ case ProcedureRelationId:
+ label = lookup_init_procedure_label(oldtup);
+ break;
+
+ case LargeObjectMetadataRelationId:
+ label = lookup_init_largeobject_label(oldtup);
+ break;
+
+ default:
+ label = lookup_init_tuple_label(relOid, oldtup);
+ break;
+ }
+ /*
+ * inplace-updating
+ */
+ newtup = heap_copytuple(oldtup);
+
+ secid = seclabelTransInput(relOid, label);
+
+ HeapTupleSetSecid(newtup, secid);
+
+ heap_inplace_update(rel, newtup);
+
+ heap_freetuple(newtup);
+ }
+ heap_endscan(scan);
+
+ heap_close(rel, RowExclusiveLock);
+ }
+ systable_endscan(classScan);
+
+ heap_close(classRel, AccessShareLock);
+
+ CommitTransactionCommand();
+}
+
+char *
+sepgsql_mcstrans_in(char *trans_label)
+{
+ security_context_t raw_label;
+ security_context_t result;
+
+ if (!sepgsql_mcstrans)
+ return trans_label;
+
+ if (selinux_trans_to_raw_context(trans_label, &raw_label) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("SELinux: unable to translate \"%s\"", trans_label)));
+ PG_TRY();
+ {
+ result = pstrdup(raw_label);
+ }
+ PG_CATCH();
+ {
+ freecon(raw_label);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(raw_label);
+
+ return result;
+}
+
+char *
+sepgsql_mcstrans_out(char *raw_label)
+{
+ security_context_t trans_label;
+ security_context_t result;
+
+ if (!sepgsql_mcstrans)
+ return raw_label;
+
+ if (selinux_raw_to_trans_context(raw_label, &trans_label) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("SELinux: unable to translate \"%s\"", raw_label)));
+ PG_TRY();
+ {
+ result = pstrdup(trans_label);
+ }
+ PG_CATCH();
+ {
+ freecon(trans_label);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(trans_label);
+
+ return result;
+}
+
+char *
+sepgsql_rawlabel_in(char *label)
+{
+ if (!label || security_check_context_raw(label) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_NAME),
+ errmsg("invalid security context \"%s\"", label)));
+ return label;
+}
+
+char *
+sepgsql_rawlabel_out(char *label)
+{
+ if (!label || security_check_context_raw(label) < 0)
+ {
+ security_context_t unlabeled_label;
+
+ if (security_get_initial_context_raw("unlabeled",
+ &unlabeled_label) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("unable to get unlabeled security context")));
+ PG_TRY();
+ {
+ label = pstrdup(unlabeled_label);
+ }
+ PG_CATCH();
+ {
+ freecon(unlabeled_label);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(unlabeled_label);
+ }
+ return label;
+}
diff --git a/src/backend/sepgsql/misc.c b/src/backend/sepgsql/misc.c
new file mode 100644
index 0000000..377a72b
--- /dev/null
+++ b/src/backend/sepgsql/misc.c
@@ -0,0 +1,147 @@
+/*
+ * misc.c
+ *
+ * SELinux hooks related to misc features
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "libpq/libpq-be.h"
+#include "miscadmin.h"
+#include "sepgsql/hooks.h"
+#include "sepgsql/sepgsql.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/guc.h"
+
+#include <selinux/selinux.h>
+
+/*
+ * sepgsql_client_label
+ *
+ * security context of the peer process
+ */
+static char *sepgsql_client_label = NULL;
+
+char *
+sepgsql_get_client_label(void)
+{
+ return sepgsql_client_label;
+}
+
+char *
+sepgsql_set_client_label(char *new_label)
+{
+ char *old_label = sepgsql_client_label;
+
+ sepgsql_client_label = new_label;
+
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /*
+ * NOTE: When an error happened prior to initialization of
+ * SE-PostgreSQL, NULL maight be delivered. In this case,
+ * we don't need to reset avc.
+ */
+ if (new_label != NULL)
+ sepgsql_avc_switch(new_label);
+ }
+#endif
+ return old_label;
+}
+
+void
+sepgsql_post_bootstraping(void)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ sepgsql_initial_labeling();
+#endif
+}
+
+void
+sepgsql_initialize(void)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ security_context_t context;
+
+ /* init userspace avc */
+ sepgsql_avc_init();
+
+ /* init privilege of the client */
+ if (!MyProcPort)
+ {
+ /*
+ * SE-PgSQL does not prevent anything in single-user mode.
+ */
+ sepostgresql_mode = SEPGSQL_MODE_INTERNAL;
+
+ if (getprevcon_raw(&context) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("SELinux: could not get server context")));
+ }
+ else
+ {
+ if (getpeercon_raw(MyProcPort->sock, &context) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INTERNAL_ERROR),
+ errmsg("SELinux: could not get client context")));
+ }
+ sepgsql_set_client_label(context);
+
+ return;
+ }
+#endif
+ if (default_with_secids)
+ {
+ default_with_secids = false;
+ elog(LOG, "guc: default_with_secid was turned off "
+ "because no label based access control is availabel now");
+ }
+}
+
+bool
+sepgsql_worker_needed(void)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ return true;
+#endif
+ return false;
+}
+
+void
+sepgsql_worker_main(void)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_avc_worker_main();
+ return;
+ }
+#endif
+ elog(FATAL, "Bug? try to launch worker process without security provider");
+}
+
+Datum
+sepgsql_getcon(PG_FUNCTION_ARGS)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ char *label = sepgsql_get_client_label();
+
+ PG_RETURN_TEXT_P(cstring_to_text(label));
+ }
+#endif
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("SE-PostgreSQL is not available")));
+ PG_RETURN_NULL();
+}
diff --git a/src/backend/sepgsql/operator.c b/src/backend/sepgsql/operator.c
new file mode 100644
index 0000000..7960525
--- /dev/null
+++ b/src/backend/sepgsql/operator.c
@@ -0,0 +1,454 @@
+/*
+ * operator.c
+ *
+ * SELinux hooks related to operators
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "catalog/pg_operator.h"
+#include "catalog/pg_opclass.h"
+#include "catalog/pg_opfamily.h"
+#include "catalog/pg_seclabel.h"
+#include "sepgsql/hooks.h"
+#include "sepgsql/sepgsql.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+#ifdef HAVE_SELINUX
+bool
+sepgsql_operator_common(Oid operOid, uint32 required, bool abort)
+{
+ sepgsql_sid_t tsid;
+ char *auname;
+ bool retval;
+
+ tsid.relid = OperatorRelationId;
+ tsid.secid = GetSysCacheSecid1(OPEROID, ObjectIdGetDatum(operOid));
+
+ auname = format_operator(operOid);
+
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ required,
+ auname,
+ abort);
+ pfree(auname);
+
+ return retval;
+}
+
+bool
+sepgsql_opclass_common(Oid opcOid, uint32 required, bool abort)
+{
+ sepgsql_sid_t tsid;
+ HeapTuple tuple;
+ const char *auname;
+ bool retval;
+
+ tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for operator class %u", opcOid);
+
+ tsid.relid = OperatorClassRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ auname = NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname);
+
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ required,
+ auname,
+ abort);
+ ReleaseSysCache(tuple);
+
+ return retval;
+}
+
+bool
+sepgsql_opfamily_common(Oid opfOid, uint32 required, bool abort)
+{
+ sepgsql_sid_t tsid;
+ HeapTuple tuple;
+ const char *auname;
+ bool retval;
+
+ tuple = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for operator family %u", opfOid);
+
+ tsid.relid = OperatorFamilyRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ auname = NameStr(((Form_pg_opfamily) GETSTRUCT(tuple))->opfname);
+
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ required,
+ auname,
+ abort);
+ ReleaseSysCache(tuple);
+
+ return retval;
+}
+
+static Oid
+get_operator_namespace(Oid operOid)
+{
+ HeapTuple tuple;
+ Oid namespaceId;
+
+ tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
+ if (HeapTupleIsValid(tuple))
+ {
+ namespaceId = ((Form_pg_operator) GETSTRUCT(tuple))->oprnamespace;
+
+ ReleaseSysCache(tuple);
+ }
+ return namespaceId;
+}
+
+static Oid
+get_opclass_namespace(Oid opcOid)
+{
+ HeapTuple tuple;
+ Oid namespaceId = InvalidOid;
+
+ tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcOid));
+ if (HeapTupleIsValid(tuple))
+ {
+ namespaceId = ((Form_pg_opclass) GETSTRUCT(tuple))->opcnamespace;
+
+ ReleaseSysCache(tuple);
+ }
+ return namespaceId;
+}
+
+static Oid
+get_opfamily_namespace(Oid opfOid)
+{
+ HeapTuple tuple;
+ Oid namespaceId = InvalidOid;
+
+ tuple = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfOid));
+ if (HeapTupleIsValid(tuple))
+ {
+ namespaceId = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfnamespace;
+
+ ReleaseSysCache(tuple);
+ }
+ return namespaceId;
+}
+
+#endif
+
+Oid
+sepgsql_operator_create(const char *operName, Oid replaced, Oid namespaceId,
+ Oid codeFunc, Oid restrictFunc, Oid joinFunc,
+ Oid commutatorOp, Oid negatorOp)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+ uint32 required;
+
+ /* db_schema:{add_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true);
+
+ /* db_tuple:{insert} */
+ if (OidIsValid(replaced))
+ {
+ nsid.relid = OperatorRelationId;
+ nsid.secid = GetSysCacheSecid1(OPEROID,
+ ObjectIdGetDatum(replaced));
+ required = SEPG_DB_TUPLE__UPDATE;
+ }
+ else
+ {
+ nsid = sepgsql_get_default_tuple_secid(OperatorRelationId);
+ required = SEPG_DB_TUPLE__INSERT;
+ }
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_TUPLE,
+ required,
+ operName,
+ true);
+
+ /* db_procedure:{install} */
+ if (OidIsValid(codeFunc))
+ sepgsql_proc_common(codeFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+ if (OidIsValid(restrictFunc))
+ sepgsql_proc_common(restrictFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+ if (OidIsValid(joinFunc))
+ sepgsql_proc_common(joinFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+
+ /*
+ * XXX - we should check anything on he commutatorOp/negatorOp
+ */
+
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_operator_alter(Oid operOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_operator_common(operOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+Oid
+sepgsql_operator_relabel(Oid operOid, char *newLabel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+
+ nsid.relid = OperatorRelationId;
+ nsid.secid = seclabelTransInput(OperatorRelationId, newLabel);
+
+ /* db_tuple:{update relabelfrom} */
+ sepgsql_operator_common(operOid,
+ SEPG_DB_TUPLE__UPDATE |
+ SEPG_DB_TUPLE__RELABELFROM,
+ true);
+
+ /* db_tuple:{relabelto} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__RELABELTO,
+ format_operator(operOid),
+ true);
+ return nsid.secid;
+ }
+#endif
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("SE-PostgreSQL is not available")));
+ return InvalidOid;
+}
+
+void
+sepgsql_operator_drop(Oid operOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_operator_namespace(operOid);
+
+ /* db_namespace:{remove_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_tuple:{delete} */
+ sepgsql_operator_common(operOid, SEPG_DB_TUPLE__DELETE, true);
+ }
+#endif
+}
+
+void
+sepgsql_operator_comment(Oid operOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_operator_common(operOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+Oid
+sepgsql_opclass_create(const char *opcName, Oid namespaceId,
+ Oid typeOid, Oid opfamilyOid, Oid storageOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+
+ /* db_schema:{add_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_TUPLE__UPDATE, true);
+
+ /* db_tuple:{insert} */
+ nsid = sepgsql_get_default_tuple_secid(OperatorRelationId);
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__INSERT,
+ opcName,
+ true);
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_opclass_alter(Oid opcOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_opclass_alter_rename(Oid opcOid, const char *newName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_opclass_namespace(opcOid);
+
+ /* db_schema:{add_name remove_name} */
+ sepgsql_schema_common(namespaceId,
+ SEPG_DB_SCHEMA__ADD_NAME |
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_tuple:{update} */
+ sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_opclass_drop(Oid opcOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_opclass_namespace(opcOid);
+
+ /* db_schema:{remove_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_tuple:{drop} */
+ sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__DELETE, true);
+ }
+#endif
+}
+
+void
+sepgsql_opclass_comment(Oid opcOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+Oid
+sepgsql_opfamily_create(const char *opfName, Oid namespaceId, Oid amOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+
+ /* db_schema:{add_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true);
+
+ /* db_tuple:{insert} */
+ nsid = sepgsql_get_default_tuple_secid(OperatorFamilyRelationId);
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__INSERT,
+ opfName,
+ true);
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_opfamily_alter(Oid opfOid, bool isDrop, Oid amOid,
+ List *operators, List *procedures)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true);
+
+ /* XXX - to do we should install checks? */
+ }
+#endif
+}
+
+void
+sepgsql_opfamily_alter_rename(Oid opfOid, const char *newName)
+{
+
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_opfamily_namespace(opfOid);
+
+ /* db_schema:{add_name remove_name} */
+ sepgsql_schema_common(namespaceId,
+ SEPG_DB_SCHEMA__ADD_NAME |
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_tuple:{update} */
+ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_opfamily_alter_owner(Oid opfOid, Oid newOwner)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_opfamily_drop(Oid opfOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_opfamily_namespace(opfOid);
+
+ /* db_schema:{remove_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_tuple:{delete} */
+ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__DELETE, true);
+ }
+#endif
+}
+
+void
+sepgsql_opfamily_comment(Oid opfOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
diff --git a/src/backend/sepgsql/proc.c b/src/backend/sepgsql/proc.c
new file mode 100644
index 0000000..1149571
--- /dev/null
+++ b/src/backend/sepgsql/proc.c
@@ -0,0 +1,366 @@
+/*
+ * proc.c
+ *
+ * SELinux hooks related to procedures
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "catalog/pg_aggregate.h"
+#include "catalog/pg_language.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_seclabel.h"
+#include "sepgsql/hooks.h"
+#include "sepgsql/sepgsql.h"
+#include "utils/builtins.h"
+#include "utils/syscache.h"
+#include "utils/lsyscache.h"
+
+#ifdef HAVE_SELINUX
+bool
+sepgsql_proc_common(Oid procOid, uint32 required, bool abort)
+{
+ sepgsql_sid_t tsid;
+ char *auname;
+ bool retval;
+
+ tsid.relid = ProcedureRelationId;
+ tsid.secid = GetSysCacheSecid1(PROCOID,
+ ObjectIdGetDatum(procOid));
+
+ auname = format_procedure(procOid);
+
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_PROCEDURE,
+ required,
+ auname,
+ abort);
+ pfree(auname);
+
+ return retval;
+}
+#endif
+
+Oid
+sepgsql_proc_create(const char *proName, Oid replaced,
+ Oid namespaceId, Oid languageId)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ HeapTuple tuple;
+ sepgsql_sid_t nsid;
+ char *scontext;
+ char *tcontext;
+ uint32 required;
+
+ /* db_schema:{add_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true);
+
+ /* db_procedure:{create or setattr} */
+ if (OidIsValid(replaced))
+ {
+ nsid.relid = ProcedureRelationId;
+ nsid.secid = GetSysCacheSecid1(PROCOID,
+ ObjectIdGetDatum(replaced));
+ required = SEPG_DB_PROCEDURE__SETATTR;
+ }
+ else
+ {
+ nsid = sepgsql_get_default_proc_secid(namespaceId);
+ required = SEPG_DB_PROCEDURE__CREATE;
+ }
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_PROCEDURE,
+ required,
+ proName,
+ true);
+
+ /* db_language:{implemente} */
+ tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(languageId));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for language %u", languageId);
+
+ scontext = seclabelRawOutput(LanguageRelationId,
+ HeapTupleGetSecid(tuple));
+ tcontext = seclabelRawOutput(nsid.relid, nsid.secid);
+
+ sepgsql_compute_perms(scontext, tcontext,
+ SEPG_CLASS_DB_LANGUAGE,
+ SEPG_DB_LANGUAGE__IMPLEMENTE,
+ proName, true);
+
+ ReleaseSysCache(tuple);
+
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_proc_alter(Oid procOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_proc_alter_rename(Oid procOid, const char *newName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_func_namespace(procOid);
+
+ /* db_schema:{remove_name add_name} */
+ sepgsql_schema_common(namespaceId,
+ SEPG_DB_SCHEMA__REMOVE_NAME |
+ SEPG_DB_SCHEMA__ADD_NAME, true);
+
+ /* db_procedure:{setattr} */
+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_proc_alter_schema(Oid procOid, Oid newSchema)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_func_namespace(procOid);
+
+ /* db_schema:{remove_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_schema:{add_name} */
+ sepgsql_schema_common(newSchema, SEPG_DB_SCHEMA__ADD_NAME, true);
+
+ /* db_procedure:{setattr} */
+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true);
+ }
+#endif
+}
+
+Oid
+sepgsql_proc_relabel(Oid procOid, char *new_label)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t tsid;
+
+ tsid.relid = ProcedureRelationId;
+ tsid.secid = seclabelTransInput(tsid.relid, new_label);
+
+ /* db_procedure:{setattr relabelfrom} */
+ sepgsql_proc_common(procOid,
+ SEPG_DB_PROCEDURE__SETATTR |
+ SEPG_DB_PROCEDURE__RELABELFROM, true);
+
+ /* db_procedure:{relabelto} */
+ sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_PROCEDURE,
+ SEPG_DB_PROCEDURE__RELABELTO,
+ format_procedure(procOid),
+ true);
+ return tsid.secid;
+ }
+#endif
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("SE-PostgreSQL is not available")));
+ return InvalidOid;
+}
+
+void
+sepgsql_proc_drop(Oid procOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_func_namespace(procOid);
+
+ /* db_schema:{remove_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_procedure:{proc} */
+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__DROP, true);
+ }
+#endif
+}
+
+void
+sepgsql_proc_grant(Oid procOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_proc_comment(Oid procOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_proc_execute(Oid procOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__EXECUTE, true);
+ }
+#endif
+}
+
+bool
+sepgsql_proc_be_inlined(HeapTuple protup)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t tsid;
+ char *old_label;
+ char *new_label;
+ const char *auname
+ = NameStr(((Form_pg_proc) GETSTRUCT(protup))->proname);
+
+ tsid.relid = ProcedureRelationId;
+ tsid.secid = HeapTupleGetSecid(protup);
+
+ if (!sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_PROCEDURE,
+ SEPG_DB_PROCEDURE__EXECUTE,
+ auname, false))
+ return false;
+
+ old_label = sepgsql_get_client_label();
+ new_label = sepgsql_client_create_label(tsid, SEPG_CLASS_PROCESS);
+ if (strcmp(old_label, new_label) != 0)
+ return false;
+
+ return true;
+ }
+#endif
+ return true;
+}
+
+char *
+sepgsql_proc_domtrans(HeapTuple protup, MemoryContext mcxt)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t tsid;
+ char *old_label = sepgsql_get_client_label();
+ char *new_label;
+ char *auname
+ = NameStr(((Form_pg_proc) GETSTRUCT(protup))->proname);
+
+ tsid.relid = ProcedureRelationId;
+ tsid.secid = HeapTupleGetSecid(protup);
+
+ new_label = sepgsql_client_create_label(tsid, SEPG_CLASS_PROCESS);
+
+ if (strcmp(old_label, new_label) == 0)
+ return NULL;
+
+ /* db_procedure:{entrypoint} */
+ sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_PROCEDURE,
+ SEPG_DB_PROCEDURE__ENTRYPOINT,
+ auname,
+ true);
+
+ /* db_process:{transition} */
+ sepgsql_compute_perms(old_label,
+ new_label,
+ SEPG_CLASS_PROCESS,
+ SEPG_PROCESS__TRANSITION,
+ NULL,
+ true);
+
+ return MemoryContextStrdup(mcxt, new_label);
+ }
+#endif
+ return NULL;
+}
+
+Oid
+sepgsql_aggregate_create(const char *aggName, Oid namespaceId,
+ Oid transFunc, Oid finalFunc)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid securityId;
+
+ /* check normal creation permission */
+ securityId = sepgsql_proc_create(aggName, InvalidOid,
+ namespaceId, INTERNALlanguageId);
+
+ /* db_procedure:{install} */
+ if (OidIsValid(transFunc))
+ sepgsql_proc_common(transFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+
+ /* db_procedure:{install} */
+ if (OidIsValid(finalFunc))
+ sepgsql_proc_common(finalFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+
+ return securityId;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_aggregate_execute(Oid aggOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Form_pg_aggregate aggForm;
+ HeapTuple tuple;
+
+ tuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for aggregate %u", aggOid);
+
+ aggForm = (Form_pg_aggregate) GETSTRUCT(tuple);
+
+ /* pg_proc:{execute} */
+ sepgsql_proc_common(aggOid, SEPG_DB_PROCEDURE__EXECUTE, true);
+
+ /* pg_proc:{execute} */
+ if (OidIsValid(aggForm->aggtransfn))
+ sepgsql_proc_common(aggForm->aggtransfn,
+ SEPG_DB_PROCEDURE__EXECUTE, true);
+
+ /* pg_proc:{execute} */
+ if (OidIsValid(aggForm->aggfinalfn))
+ sepgsql_proc_common(aggForm->aggfinalfn,
+ SEPG_DB_PROCEDURE__EXECUTE, true);
+
+ ReleaseSysCache(tuple);
+ }
+#endif
+}
diff --git a/src/backend/sepgsql/relation.c b/src/backend/sepgsql/relation.c
new file mode 100644
index 0000000..2604749
--- /dev/null
+++ b/src/backend/sepgsql/relation.c
@@ -0,0 +1,819 @@
+/*
+ * relation.c
+ *
+ * SELinux hooks related to relation
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "access/sysattr.h"
+#include "catalog/catalog.h"
+#include "catalog/heap.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_seclabel.h"
+#include "catalog/pg_type.h"
+#include "sepgsql/hooks.h"
+#include "sepgsql/sepgsql.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+#ifdef HAVE_SELINUX
+bool
+sepgsql_relation_common(Oid relOid, uint32 required, bool abort)
+{
+ Form_pg_class classForm;
+ HeapTuple tuple;
+ sepgsql_sid_t tsid;
+ uint16 tclass;
+ bool retval;
+
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", relOid);
+ classForm = (Form_pg_class) GETSTRUCT(tuple);
+
+ tsid.relid = RelationRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ switch (classForm->relkind)
+ {
+ case RELKIND_SEQUENCE:
+ tclass = SEPG_CLASS_DB_SEQUENCE;
+ break;
+
+ case RELKIND_VIEW:
+ tclass = SEPG_CLASS_DB_VIEW;
+ break;
+
+ case RELKIND_COMPOSITE_TYPE:
+ tclass = SEPG_CLASS_DB_TUPLE;
+ break;
+
+ default:
+ tclass = SEPG_CLASS_DB_TABLE;
+ break;
+ }
+
+ retval = sepgsql_client_perms(tsid,
+ tclass,
+ required,
+ NameStr(classForm->relname),
+ abort);
+ ReleaseSysCache(tuple);
+
+ return retval;
+}
+
+static Bitmapset *
+fixup_whole_row_reference(Oid relOid, int natts, Bitmapset *columns)
+{
+ Bitmapset *result;
+ AttrNumber attno;
+
+ attno = InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber;
+ if (!bms_is_member(attno, columns))
+ return columns; /* no need to fixup */
+
+ result = bms_copy(columns);
+ result = bms_del_member(result, attno);
+
+ for (attno=1; attno <= natts; attno++)
+ {
+ Form_pg_attribute attForm;
+ HeapTuple atttup;
+
+ atttup = SearchSysCache2(ATTNUM,
+ ObjectIdGetDatum(relOid),
+ Int16GetDatum(attno));
+ if (!HeapTupleIsValid(atttup))
+ continue;
+
+ attForm = (Form_pg_attribute) GETSTRUCT(atttup);
+ if (!attForm->attisdropped)
+ {
+ int cindex = attno - FirstLowInvalidHeapAttributeNumber;
+ result = bms_add_member(result, cindex);
+ }
+ ReleaseSysCache(atttup);
+ }
+
+ return result;
+}
+#endif
+
+bool
+sepgsql_relation_perms(Oid relOid, AclMode aclmask,
+ Bitmapset *selectedCols,
+ Bitmapset *modifiedCols, bool abort)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Form_pg_class relForm;
+ HeapTuple tuple;
+ sepgsql_sid_t rsid;
+ Bitmapset *selColsEx;
+ Bitmapset *modColsEx;
+ Bitmapset *columns;
+ AttrNumber nattrs;
+ AttrNumber attno;
+ const char *auname;
+ char relkind;
+ uint16 tclass = 0;
+ uint32 required = 0;
+ bool rc = true;
+
+ /*
+ * Hardwired policy:
+ * SE-PostgreSQL enforces clients cannot modify system catalogs
+ * and access toast values using DML statements in enforcing mode.
+ * Note that it performs in permissive mode during initdb phase.
+ */
+ if (sepgsql_get_enforce())
+ {
+ if (IsSystemNamespace(get_rel_namespace(relOid)) &&
+ (aclmask & (ACL_UPDATE | ACL_INSERT | ACL_DELETE)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("not allowed to modify system catalog \"%s\"",
+ get_rel_name(relOid))));
+
+ if (get_rel_relkind(relOid) == RELKIND_TOASTVALUE)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("not allowed to access toast values \"%s\"",
+ get_rel_name(relOid))));
+ }
+
+ /*
+ * check relation's permissions
+ */
+ tuple = SearchSysCache1(RELOID,
+ ObjectIdGetDatum(relOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", relOid);
+
+ rsid.relid = RelationRelationId;
+ rsid.secid = HeapTupleGetSecid(tuple);
+
+ relForm = (Form_pg_class) GETSTRUCT(tuple);
+ auname = NameStr(relForm->relname);
+ relkind = relForm->relkind;
+ nattrs = relForm->relnatts;
+
+ switch (relkind)
+ {
+ case RELKIND_RELATION:
+ tclass = SEPG_CLASS_DB_TABLE;
+
+ if (aclmask & ACL_SELECT)
+ required |= SEPG_DB_TABLE__SELECT;
+ if (aclmask & ACL_INSERT)
+ required |= SEPG_DB_TABLE__INSERT;
+ if (aclmask & ACL_UPDATE)
+ required |= (!modifiedCols
+ ? SEPG_DB_TABLE__LOCK
+ : SEPG_DB_TABLE__UPDATE);
+ if (aclmask & ACL_DELETE)
+ required |= SEPG_DB_TABLE__DELETE;
+ break;
+
+ case RELKIND_SEQUENCE:
+ tclass = SEPG_CLASS_DB_SEQUENCE;
+ if (aclmask & ACL_SELECT)
+ required |= SEPG_DB_SEQUENCE__GET_VALUE;
+ break;
+
+ case RELKIND_VIEW:
+ tclass = SEPG_CLASS_DB_VIEW;
+ if (aclmask != 0)
+ required |= SEPG_DB_VIEW__EXPAND;
+ break;
+
+ default:
+ elog(ERROR, "Bug? unexpected relkind %c", relkind);
+ return false;
+ }
+
+ if (required != 0)
+ rc = sepgsql_client_perms(rsid, tclass, required, auname, abort);
+
+ ReleaseSysCache(tuple);
+
+ if (!rc || relkind != RELKIND_RELATION)
+ return rc;
+
+ /*
+ * Check column's permissions
+ */
+ selColsEx = fixup_whole_row_reference(relOid, nattrs,
+ selectedCols);
+ modColsEx = fixup_whole_row_reference(relOid, nattrs,
+ modifiedCols);
+ columns = bms_union(selColsEx, modColsEx);
+
+ while ((attno = bms_first_member(columns)) >= 0)
+ {
+ required = 0;
+
+ if (bms_is_member(attno, selColsEx))
+ required |= SEPG_DB_COLUMN__SELECT;
+ if (bms_is_member(attno, modColsEx))
+ {
+ if (aclmask & ACL_UPDATE)
+ required |= SEPG_DB_COLUMN__UPDATE;
+ if (aclmask & ACL_INSERT)
+ required |= SEPG_DB_COLUMN__INSERT;
+ }
+ if (required == 0)
+ continue;
+
+ attno += FirstLowInvalidHeapAttributeNumber;
+ rc = sepgsql_attribute_common(relOid, attno, required, abort);
+ if (!rc)
+ break;
+ }
+
+ if (selColsEx != selectedCols)
+ bms_free(selColsEx);
+ if (modColsEx != modifiedCols)
+ bms_free(modColsEx);
+ bms_free(columns);
+
+ return rc;
+ }
+#endif
+ return true;
+}
+
+Oid *
+sepgsql_relation_create(const char *relName,
+ char relkind,
+ TupleDesc tupDesc,
+ Oid namespaceId,
+ List *supOids,
+ bool createAs)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t tsid;
+ sepgsql_sid_t csid;
+ Oid *secLabels;
+ uint16 tclass;
+ uint32 perms;
+ AttrNumber index, attno, nitems;
+
+ /*
+ * The secLabeld array stores security identifiers to be
+ * assigned on the new table and columns.
+ *
+ * secLabels[0] is security-id of the relation.
+ * secLabels[attnum - FirstLowInvalidHeapAttributeNumber]
+ * is security-id of the columns.
+ */
+ secLabels = seclabelMakeRelationDefaults(tupDesc, supOids);
+ nitems = tupDesc->natts - FirstLowInvalidHeapAttributeNumber;
+
+ switch (relkind)
+ {
+ case RELKIND_RELATION:
+ if (!OidIsValid(secLabels[0]))
+ {
+ tsid = sepgsql_get_default_table_secid(namespaceId);
+ secLabels[0] = tsid.secid;
+ }
+ tclass = SEPG_CLASS_DB_TABLE;
+ perms = SEPG_DB_TABLE__CREATE;
+ if (createAs)
+ perms |= SEPG_DB_TABLE__INSERT;
+ break;
+
+ case RELKIND_SEQUENCE:
+ if (!OidIsValid(secLabels[0]))
+ {
+ tsid = sepgsql_get_default_sequence_secid(namespaceId);
+ secLabels[0] = tsid.secid;
+ }
+ tclass = SEPG_CLASS_DB_SEQUENCE;
+ perms = SEPG_DB_SEQUENCE__CREATE;
+ break;
+
+ case RELKIND_VIEW:
+ if (!OidIsValid(secLabels[0]))
+ {
+ tsid = sepgsql_get_default_view_secid(namespaceId);
+ secLabels[0] = tsid.secid;
+ }
+ tclass = SEPG_CLASS_DB_VIEW;
+ perms = SEPG_DB_VIEW__CREATE;
+ break;
+
+ case RELKIND_COMPOSITE_TYPE:
+ if (!OidIsValid(secLabels[0]))
+ {
+ tsid = sepgsql_get_default_tuple_secid(TypeRelationId);
+ secLabels[0] = seclabelMoveSecid(RelationRelationId,
+ TypeRelationId, tsid.secid);
+ }
+ tclass = SEPG_CLASS_DB_TUPLE;
+ perms = SEPG_DB_TUPLE__INSERT;
+ break;
+
+ default:
+ elog(ERROR, "Bug? unexpected relkind %c", relkind);
+ return NULL;
+ }
+ /* db_schema:{add_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true);
+
+ /* db_table:{create} or others */
+ tsid.relid = RelationRelationId;
+ tsid.secid = secLabels[0];
+ sepgsql_client_perms(tsid, tclass, perms, relName, true);
+
+ /* no individual security-id except for RELKIND_RELATION */
+ if (relkind != RELKIND_RELATION)
+ {
+ Oid securityId = seclabelMoveSecid(AttributeRelationId,
+ RelationRelationId, secLabels[0]);
+
+ for (index = 1; index < nitems; index++)
+ secLabels[index] = securityId;
+
+ return secLabels;
+ }
+
+ /*
+ * security context of the columns
+ */
+ for (index = 1; index < nitems; index++)
+ {
+ Form_pg_attribute attForm;
+ char auname[NAMEDATALEN * 2 + 10];
+
+ attno = index + FirstLowInvalidHeapAttributeNumber;
+
+ /* skip unnecessary system columns */
+ if ((attno == ObjectIdAttributeNumber && !tupDesc->tdhasoid) ||
+ (attno == SecurityLabelAttributeNumber && !tupDesc->tdhassecid))
+ continue;
+
+ if (!OidIsValid(secLabels[index]))
+ {
+ csid = sepgsql_client_create_secid(tsid,
+ SEPG_CLASS_DB_COLUMN,
+ AttributeRelationId);
+ secLabels[index] = csid.secid;
+ }
+
+ if (attno < 0)
+ attForm = SystemAttributeDefinition(attno,
+ tupDesc->tdhasoid,
+ tupDesc->tdhassecid);
+ else
+ attForm = tupDesc->attrs[attno];
+
+ /* db_column:{create (insert)} permission */
+ csid.relid = AttributeRelationId;
+ csid.secid = secLabels[index];
+
+ perms = SEPG_DB_COLUMN__CREATE;
+ if (createAs && attno >= 0)
+ perms |= SEPG_DB_COLUMN__INSERT;
+
+ snprintf(auname, sizeof(auname), "%s.%s",
+ relName, NameStr(attForm->attname));
+
+ sepgsql_client_perms(csid,
+ SEPG_CLASS_DB_COLUMN,
+ perms,
+ auname,
+ true);
+ }
+ return secLabels;
+ }
+#endif
+ return NULL;
+}
+
+void
+sepgsql_relation_alter(Oid relationOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_relation_alter_schema(Oid relationOid, Oid newSchema)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_relation_alter_rename(Oid relationOid, const char *newName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_relation_alter_inherit(Oid childOid, Oid parentOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_relation_common(childOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ }
+#endif
+}
+
+Oid
+sepgsql_relation_relabel(Oid relationOid, char *new_label)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+ char *auname;
+ char relkind;
+ uint16 tclass = 0;
+
+ nsid.relid = RelationRelationId;
+ nsid.secid = seclabelTransInput(nsid.relid, new_label);
+
+ auname = get_rel_name(relationOid);
+ relkind = get_rel_relkind(relationOid);
+
+ switch (relkind)
+ {
+ case RELKIND_RELATION:
+ tclass = SEPG_CLASS_DB_TABLE;
+ break;
+
+ case RELKIND_SEQUENCE:
+ tclass = SEPG_CLASS_DB_SEQUENCE;
+ break;
+
+ case RELKIND_VIEW:
+ tclass = SEPG_CLASS_DB_VIEW;
+ break;
+
+ case RELKIND_COMPOSITE_TYPE:
+ tclass = SEPG_CLASS_DB_TUPLE;
+ break;
+
+ default:
+ elog(ERROR, "unexpected relkind %c", relkind);
+ break;;
+ }
+ /* db_xxx:{setattr relabelfrom} */
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__SETATTR |
+ SEPG_DB_TABLE__RELABELFROM, true);
+
+ /* db_xxx:{relabelto} */
+ sepgsql_client_perms(nsid,
+ tclass,
+ SEPG_DB_TABLE__RELABELTO,
+ auname, true);
+ pfree(auname);
+
+ return nsid.secid;
+ }
+#endif
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("SE-PostgreSQL is not available")));
+ return InvalidOid;
+}
+
+void
+sepgsql_relation_drop(Oid relationOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__DROP, true);
+ }
+#endif
+}
+
+void
+sepgsql_relation_getattr(Oid relationOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__GETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_relation_grant(Oid relationOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_relation_comment(Oid relationOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ }
+#endif
+}
+
+bool
+sepgsql_relation_cluster(Oid relationOid, bool abort)
+{
+#ifdef HAVE_SELINUX
+ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION);
+
+ if (sepgsql_is_enabled())
+ {
+ bool retval =
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__INDEXON, true);
+ return retval;
+ }
+#endif
+ return true;
+}
+
+void
+sepgsql_relation_truncate(Relation rel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION);
+
+ /* db_table:{delete} */
+ sepgsql_relation_common(RelationGetRelid(rel),
+ SEPG_DB_TABLE__DELETE, true);
+ /* db_tuple:{delete} */
+ }
+#endif
+}
+
+void
+sepgsql_relation_lock(Relation rel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION);
+
+ /* db_table:{lock} */
+ sepgsql_relation_common(RelationGetRelid(rel),
+ SEPG_DB_TABLE__LOCK, true);
+ }
+#endif
+}
+
+void
+sepgsql_relation_reindex(Oid relationOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION);
+
+ /* db_table:{indexon} */
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__INDEXON, true);
+ }
+#endif
+}
+
+void
+sepgsql_view_replace(Oid viewOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Assert(get_rel_relkind(viewOid) == RELKIND_VIEW);
+ /* db_view:{setattr} */
+ sepgsql_relation_common(viewOid,
+ SEPG_DB_VIEW__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_index_create(Oid relationOid, Oid namespaceOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_schema:{add_name} */
+ sepgsql_schema_common(namespaceOid,
+ SEPG_DB_SCHEMA__ADD_NAME, true);
+ /* db_table:{setattr indexon} */
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__SETATTR |
+ SEPG_DB_TABLE__INDEXON, true);
+ }
+#endif
+}
+
+void
+sepgsql_index_reindex(Oid indexOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ HeapTuple tuple;
+ Oid relationOid;
+
+ tuple = SearchSysCache1(INDEXRELID, indexOid);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for index %u", indexOid);
+ relationOid = ((Form_pg_index) GETSTRUCT(tuple))->indrelid;
+ ReleaseSysCache(tuple);
+
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__INDEXON, true);
+ }
+#endif
+}
+
+void
+sepgsql_sequence_get_value(Oid sequenceOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Assert(get_rel_relkind(sequenceOid) == RELKIND_SEQUENCE);
+ sepgsql_relation_common(sequenceOid,
+ SEPG_DB_SEQUENCE__GET_VALUE, true);
+ }
+#endif
+}
+
+void
+sepgsql_sequence_next_value(Oid sequenceOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Assert(get_rel_relkind(sequenceOid) == RELKIND_SEQUENCE);
+ sepgsql_relation_common(sequenceOid,
+ SEPG_DB_SEQUENCE__NEXT_VALUE, true);
+ }
+#endif
+}
+
+void
+sepgsql_sequence_set_value(Oid sequenceOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Assert(get_rel_relkind(sequenceOid) == RELKIND_SEQUENCE);
+ sepgsql_relation_common(sequenceOid,
+ SEPG_DB_SEQUENCE__SET_VALUE, true);
+ }
+#endif
+}
+
+void
+sepgsql_rule_create(Oid relationOid, const char *ruleName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_rule_drop(Oid relationOid, const char *ruleName, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled() && !cascade)
+ {
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_rule_comment(Oid relationOid, const char *ruleName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_trigger_create(Oid relationOid, const char *triggerName,
+ Oid constrrelid, Oid funcOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION);
+
+ /* db_table:{setattr} */
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ /* db_procedure:{install} */
+ // sepgsql_procedure_common...
+ }
+#endif
+}
+
+void
+sepgsql_trigger_alter(Oid relationOid, const char *triggerName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION);
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_trigger_drop(Oid relationOid, const char *triggerName, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled() && !cascade)
+ {
+ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION);
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_trigger_comment(Oid relationOid, const char *triggerName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION);
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ }
+#endif
+}
+
+void
+sepgsql_constraint_comment(Oid relationOid, const char *constName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Assert(get_rel_relkind(relationOid) == RELKIND_RELATION);
+ sepgsql_relation_common(relationOid,
+ SEPG_DB_TABLE__SETATTR, true);
+ }
+#endif
+}
diff --git a/src/backend/sepgsql/role.c b/src/backend/sepgsql/role.c
new file mode 100644
index 0000000..3c4891c
--- /dev/null
+++ b/src/backend/sepgsql/role.c
@@ -0,0 +1,142 @@
+/*
+ * role.c
+ *
+ * SELinux hooks related to roles
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "catalog/pg_authid.h"
+#include "catalog/pg_seclabel.h"
+#include "miscadmin.h"
+#include "sepgsql/hooks.h"
+#include "sepgsql/sepgsql.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+#ifdef HAVE_SELINUX
+bool
+sepgsql_role_common(Oid roleOid, uint32 required, bool abort)
+{
+ sepgsql_sid_t tsid;
+ HeapTuple tuple;
+ const char *auname;
+ bool retval;
+
+ tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for role %u", roleOid);
+
+ tsid.relid = AuthIdRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ auname = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname);
+
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ required,
+ auname,
+ abort);
+ ReleaseSysCache(tuple);
+
+ return retval;
+}
+#endif
+
+Oid
+sepgsql_role_create(const char *roleName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+
+ nsid = sepgsql_get_default_tuple_secid(AuthIdRelationId);
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__INSERT,
+ roleName,
+ true);
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_role_alter(Oid roleOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_role_common(roleOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+Oid
+sepgsql_role_relabel(Oid roleOid, char *newLabel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t tsid;
+
+ tsid.relid = AuthIdRelationId;
+ tsid.secid = seclabelTransInput(tsid.relid, newLabel);
+
+ /* db_tuple:{update relabelfrom} */
+ sepgsql_role_common(roleOid,
+ SEPG_DB_TUPLE__UPDATE |
+ SEPG_DB_TUPLE__RELABELFROM,
+ true);
+
+ /* db_tuple:{relabelto} */
+ sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__RELABELTO,
+ GetUserNameFromId(roleOid),
+ true);
+ return tsid.secid;
+ }
+#endif
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("SE-PostgreSQL is not available")));
+ return InvalidOid;
+}
+
+void
+sepgsql_role_drop(Oid roleOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_role_common(roleOid, SEPG_DB_TUPLE__DELETE, true);
+ }
+#endif
+}
+
+void
+sepgsql_role_grant(Oid roleOid, bool is_grant, List *memberIds)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_role_common(roleOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_role_comment(Oid roleOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_role_common(roleOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
diff --git a/src/backend/sepgsql/rowlv.c b/src/backend/sepgsql/rowlv.c
new file mode 100644
index 0000000..1078777
--- /dev/null
+++ b/src/backend/sepgsql/rowlv.c
@@ -0,0 +1,346 @@
+/*
+ * rowlv.c
+ *
+ * Row-level access control facilities
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/sysattr.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_database.h"
+#include "catalog/pg_language.h"
+#include "catalog/pg_largeobject_metadata.h"
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_seclabel.h"
+#include "catalog/pg_type.h"
+#include "nodes/makefuncs.h"
+#include "parser/parsetree.h"
+#include "sepgsql/hooks.h"
+#include "sepgsql/sepgsql.h"
+#include "storage/bufmgr.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+#include "utils/tqual.h"
+
+static int sepgsql_rowlv_mode = SEPGSQL_ROWLV_FILTER;
+
+int
+sepgsql_rowlv_get_mode(void)
+{
+ return sepgsql_rowlv_mode;
+}
+
+int
+sepgsql_rowlv_set_mode(int new_mode)
+{
+ int old_mode = sepgsql_rowlv_mode;
+
+ Assert(new_mode == SEPGSQL_ROWLV_FILTER ||
+ new_mode == SEPGSQL_ROWLV_ABORT ||
+ new_mode == SEPGSQL_ROWLV_BYPASS);
+
+ sepgsql_rowlv_mode = new_mode;
+
+ return old_mode;
+}
+
+uint32
+sepgsql_rowlv_permissions(RangeTblEntry *rte)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid relOid = rte->relid;
+ HeapTuple reltup;
+ bool relhassecids;
+ uint32 required = 0;
+
+ if (!OidIsValid(relOid))
+ return 0;
+
+ if (get_rel_relkind(relOid) != RELKIND_RELATION)
+ return 0;
+
+ reltup = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
+ if (!HeapTupleIsValid(reltup))
+ elog(ERROR, "cache lookup failed for relation %u", relOid);
+ relhassecids = ((Form_pg_class) GETSTRUCT(reltup))->relhassecids;
+ ReleaseSysCache(reltup);
+
+ if (!relhassecids)
+ return 0;
+
+ if (rte->requiredPerms & ACL_SELECT)
+ required |= SEPG_DB_TUPLE__SELECT;
+
+ if (rte->requiredPerms & ACL_UPDATE &&
+ !bms_is_empty(rte->modifiedCols))
+ required |= SEPG_DB_TUPLE__UPDATE;
+
+ if (rte->requiredPerms & ACL_DELETE)
+ required |= SEPG_DB_TUPLE__DELETE;
+
+ return required;
+ }
+#endif
+ return 0;
+}
+
+#ifdef HAVE_SELINUX
+static bool
+sepgsql_tuple_perms(Relation rel, HeapTuple tuple, uint32 required, bool abort)
+{
+ Form_pg_class relForm;
+ Form_pg_attribute attForm;
+ Form_pg_type typForm;
+ sepgsql_sid_t tsid;
+ uint16 tclass;
+ bool result;
+
+ /* object class? */
+ switch (RelationGetRelid(rel))
+ {
+ case DatabaseRelationId:
+ tclass = SEPG_CLASS_DB_DATABASE;
+ break;
+
+ case NamespaceRelationId:
+ tclass = SEPG_CLASS_DB_SCHEMA;
+ break;
+
+ case RelationRelationId:
+ relForm = (Form_pg_class) GETSTRUCT(tuple);
+ switch (relForm->relkind)
+ {
+ case RELKIND_RELATION:
+ tclass = SEPG_CLASS_DB_TABLE;
+ break;
+ case RELKIND_SEQUENCE:
+ tclass = SEPG_CLASS_DB_SEQUENCE;
+ break;
+ case RELKIND_VIEW:
+ tclass = SEPG_CLASS_DB_VIEW;
+ break;
+ case RELKIND_COMPOSITE_TYPE:
+ tclass = SEPG_CLASS_DB_TUPLE;
+ break;
+ default: /* index, toast */
+ tclass = SEPG_CLASS_DB_TABLE;
+ break;
+ }
+ break;
+
+ case AttributeRelationId:
+ attForm = (Form_pg_attribute) GETSTRUCT(tuple);
+ switch (get_rel_relkind(attForm->attrelid))
+ {
+ case RELKIND_RELATION:
+ tclass = SEPG_CLASS_DB_COLUMN;
+ break;
+ case RELKIND_SEQUENCE:
+ tclass = SEPG_CLASS_DB_SEQUENCE;
+ break;
+ case RELKIND_VIEW:
+ tclass = SEPG_CLASS_DB_VIEW;
+ break;
+ case RELKIND_COMPOSITE_TYPE:
+ tclass = SEPG_CLASS_DB_TUPLE;
+ break;
+ default: /* index, toast */
+ tclass = SEPG_CLASS_DB_TABLE;
+ break;
+ }
+ break;
+
+ case ProcedureRelationId:
+ tclass = SEPG_CLASS_DB_PROCEDURE;
+ break;
+
+ case LanguageRelationId:
+ tclass = SEPG_CLASS_DB_LANGUAGE;
+ break;
+
+ case LargeObjectMetadataRelationId:
+ tclass = SEPG_CLASS_DB_BLOB;
+ break;
+
+ case TypeRelationId:
+ typForm = (Form_pg_type) GETSTRUCT(tuple);
+ if (!OidIsValid(typForm->typrelid))
+ tclass = SEPG_CLASS_DB_TUPLE;
+ else
+ {
+ switch (get_rel_relkind(typForm->typrelid))
+ {
+ case RELKIND_RELATION:
+ tclass = SEPG_CLASS_DB_TABLE;
+ break;
+ case RELKIND_SEQUENCE:
+ tclass = SEPG_CLASS_DB_SEQUENCE;
+ break;
+ case RELKIND_VIEW:
+ tclass = SEPG_CLASS_DB_VIEW;
+ break;
+ case RELKIND_COMPOSITE_TYPE:
+ tclass = SEPG_CLASS_DB_TUPLE;
+ break;
+ default: /* index, toast */
+ tclass = SEPG_CLASS_DB_TABLE;
+ break;
+ }
+ }
+ break;
+
+ default:
+ tclass = SEPG_CLASS_DB_TUPLE;
+ break;
+ }
+
+ /* do permission check */
+ tsid.relid = RelationGetRelid(rel);
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ result = sepgsql_client_perms(tsid, tclass, required, NULL, abort);
+
+ if (!result)
+ elog(NOTICE, "denied: oid=%u tsid(relid=%u, secid=%u) label=%s tclass=%d required=%u", HeapTupleGetOid(tuple), tsid.relid, tsid.secid, seclabelRawOutput(tsid.relid, tsid.secid), tclass, required);
+
+ return result;
+}
+#endif
+
+bool
+sepgsql_rowlv_quals(Relation rel, uint32 required,
+ TupleTableSlot *slot, bool abort)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ HeapTuple tuple;
+
+ /* no permission to be checked */
+ if (!required)
+ return true;
+
+ /* mismatch invocation for the current rowlv mode */
+ if ((!abort && sepgsql_rowlv_mode != SEPGSQL_ROWLV_FILTER) ||
+ (abort && sepgsql_rowlv_mode != SEPGSQL_ROWLV_ABORT))
+ return true;
+
+ Assert(OidIsValid(RelationGetRelid(rel)));
+
+ tuple = ExecMaterializeSlot(slot);
+
+ return sepgsql_tuple_perms(rel, tuple, required, abort);
+ }
+#endif
+ return true;
+}
+
+bool
+sepgsql_rowlv_copyto(Relation rel, HeapTuple tuple)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ return sepgsql_tuple_perms(rel, tuple, SEPG_DB_TUPLE__SELECT, false);
+ }
+#endif
+ return true;
+}
+
+void
+sepgsql_tuple_insert(Relation rel, HeapTuple tuple)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+ Oid relOid = RelationGetRelid(rel);
+
+ if (!RelationGetForm(rel)->relhassecids)
+ {
+ nsid.relid = RelationRelationId;
+ nsid.secid = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid));
+ }
+ else if (OidIsValid(HeapTupleGetSecid(tuple)))
+ {
+ nsid.relid = relOid;
+ nsid.secid = HeapTupleGetSecid(tuple);
+ }
+ else
+ {
+ nsid = sepgsql_get_default_tuple_secid(relOid);
+ HeapTupleSetSecid(tuple, nsid.secid);
+ }
+ /* db_tuple:{insert} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__INSERT,
+ NULL,
+ true);
+ }
+#endif
+}
+
+void
+sepgsql_tuple_update(Relation rel, ItemPointer otid, HeapTuple newtup)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t tsid;
+ HeapTupleData oldtup;
+ Buffer oldbuf;
+ Oid newSecId = HeapTupleGetSecid(newtup);
+ Oid oldSecId;
+
+ /*
+ * heap_update() preserves security id of the original tuple,
+ * if no explicit security label was given, so we don't need
+ * to check anything.
+ * At this point, db_tuple:{update} is already checked.
+ */
+ if (!OidIsValid(newSecId))
+ return;
+
+ /*
+ * User gave an explicit security label
+ */
+ ItemPointerCopy(otid, &oldtup.t_self);
+ if (!heap_fetch(rel, SnapshotAny, &oldtup, &oldbuf, false, NULL))
+ elog(ERROR, "failed to fetch old version of the tuple");
+
+ tsid.relid = RelationGetRelid(rel);
+ oldSecId = HeapTupleGetSecid(&oldtup);
+
+ if (!seclabelCompareSecid(tsid.relid, oldSecId,
+ tsid.relid, newSecId))
+ {
+ /* db_tuple:{relabelfrom} */
+ tsid.secid = oldSecId;
+ sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__RELABELFROM,
+ NULL,
+ true);
+
+ /* db_tuple:{relabelto} */
+ tsid.secid = newSecId;
+ sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__RELABELTO,
+ NULL,
+ true);
+ }
+ ReleaseBuffer(oldbuf);
+ }
+#endif
+}
diff --git a/src/backend/sepgsql/schema.c b/src/backend/sepgsql/schema.c
new file mode 100644
index 0000000..24878f9
--- /dev/null
+++ b/src/backend/sepgsql/schema.c
@@ -0,0 +1,173 @@
+/*
+ * schema.c
+ *
+ * SELinux hooks related to schema
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "catalog/pg_namespace.h"
+#include "catalog/pg_seclabel.h"
+#include "miscadmin.h"
+#include "sepgsql/hooks.h"
+#include "sepgsql/sepgsql.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+#ifdef HAVE_SELINUX
+bool
+sepgsql_schema_common(Oid namespaceOid, uint32 required, bool abort)
+{
+ HeapTuple tuple;
+ sepgsql_sid_t tsid;
+ const char *auname;
+ bool retval;
+
+ tuple = SearchSysCache1(NAMESPACEOID,
+ ObjectIdGetDatum(namespaceOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for namespace %u", namespaceOid);
+
+ tsid.relid = NamespaceRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ auname = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname);
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_SCHEMA,
+ required,
+ auname,
+ abort);
+ ReleaseSysCache(tuple);
+
+ return retval;
+}
+#endif
+
+Oid
+sepgsql_schema_create(const char *nspName, bool is_temp)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+
+ /* compute default security context */
+ nsid = sepgsql_get_default_schema_secid(MyDatabaseId);
+
+ /* db_schema:{create} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_DB_SCHEMA__CREATE,
+ nspName, true);
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_schema_alter(Oid namespaceOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_schema_common(namespaceOid,
+ SEPG_DB_SCHEMA__SETATTR,
+ true);
+ }
+#endif
+}
+
+Oid
+sepgsql_schema_relabel(Oid namespaceOid, char *new_label)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+ char *auname;
+
+ nsid.relid = NamespaceRelationId;
+ nsid.secid = seclabelTransInput(nsid.relid, new_label);
+
+ auname = get_namespace_name(namespaceOid);
+
+ /* db_schema:{setattr relabelfrom} */
+ sepgsql_schema_common(namespaceOid,
+ SEPG_DB_SCHEMA__SETATTR |
+ SEPG_DB_SCHEMA__RELABELFROM,
+ true);
+
+ /* db_schema:{relabelto} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_DB_SCHEMA__RELABELTO,
+ auname,
+ true);
+ pfree(auname);
+
+ return nsid.secid;
+ }
+#endif
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("SE-PostgreSQL is not available")));
+ return InvalidOid;
+}
+
+void
+sepgsql_schema_drop(Oid namespaceOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_schema_common(namespaceOid,
+ SEPG_DB_SCHEMA__DROP,
+ true);
+ }
+#endif
+}
+
+void
+sepgsql_schema_grant(Oid namespaceOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_schema_common(namespaceOid,
+ SEPG_DB_SCHEMA__SETATTR,
+ true);
+ }
+#endif
+}
+
+bool
+sepgsql_schema_search(Oid namespaceOid, bool abort)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ bool retval =
+ sepgsql_schema_common(namespaceOid,
+ SEPG_DB_SCHEMA__SEARCH,
+ abort);
+ return retval;
+ }
+#endif
+ return true;
+}
+
+void
+sepgsql_schema_comment(Oid namespaceOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_schema_common(namespaceOid,
+ SEPG_DB_SCHEMA__SETATTR,
+ true);
+ }
+#endif
+}
diff --git a/src/backend/sepgsql/selinux.c b/src/backend/sepgsql/selinux.c
new file mode 100644
index 0000000..5e40cf9
--- /dev/null
+++ b/src/backend/sepgsql/selinux.c
@@ -0,0 +1,690 @@
+/*
+ * src/backend/security/sepgsql/selinux.c
+ * Routines to communicate with SELinux.
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "catalog/pg_seclabel.h"
+#include "libpq/libpq.h"
+#include "miscadmin.h"
+#include "sepgsql/sepgsql.h"
+#include "utils/guc.h"
+#include "utils/memutils.h"
+
+#include <selinux/selinux.h>
+
+/*
+ * selinux_catalog
+ *
+ * This static translation lookup table enables to associate a certain
+ * object class/permission name with its internal code, such as
+ * SEPG_CLASS_DB_SCHEMA.
+ *
+ * SELinux requires applications to represent object class and a set of
+ * permissions in code, instead of its name, when we ask SELinux's decision.
+ *
+ * See the definition of security_compute_av(3) API in libselinux.
+ * We need to gives a code of object class, and interpret what permissions
+ * are allowed on the object class from av_decision structure.
+ * Actual values of the code depend on the security policy. In other words,
+ * we cannot know what number is assigned on a certain object class and
+ * permissions.
+ * The string_to_security_class(3) and string_to_av_perm(3) APIs takes
+ * arguments with the name of object class/permission, and returns the
+ * code for the given object class/permissions.
+ * For example, we can know what code is assigned on the "db_table" class
+ * using these functions as follows:
+ *
+ * uint16 tclass_ex = string_to_security_class("db_table");
+ *
+ * On the other hand, we use an alternative code internally to simplify
+ * the implementation, such as SEPG_CLASS_* for object class.
+ * The following selinux_catalog is used to translate the 'internal'
+ * code and the 'external' code.
+ *
+ * It allows to lookup name of the object class or permission corresponding
+ * to a certain 'internal' code. Then, we can give the name to SELinux's
+ * API to obtain 'external' code which can be used to ask in-kernel SELinux.
+ */
+static struct
+{
+ const char *class_name;
+ uint16 class_code;
+ struct
+ {
+ const char *perm_name;
+ uint32 perm_code;
+ } perms[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_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 },
+ }
+ },
+ {
+ "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__IMPLEMENTE },
+ { "execute", SEPG_DB_LANGUAGE__EXECUTE },
+ { NULL, 0UL },
+ }
+ },
+};
+
+/*
+ * GUC option: sepostgresql = [default|enforcing|permissive|disabled]
+ *
+ * SEPGSQL_MODE_DEFAULT : It follows system setting
+ * SEPGSQL_MODE_ENFORCING : Use enforcing mode always
+ * SEPGSQL_MODE_PERMISSIVE : Use permissive mode always
+ * SEPGSQL_MODE_INTERNAL : Internally used mode. Same as permissive mode
+ * except for silence in audit logs
+ * SEPGSQL_MODE_DISABLED : It always disables SE-PgSQL configuration
+ */
+int sepostgresql_mode;
+
+/*
+ * sepgsql_is_enabled
+ *
+ * If it returns true, SE-PgSQL is enabled. Otherwise, it is disabled.
+ */
+bool
+sepgsql_is_enabled(void)
+{
+ static int enabled = -1;
+
+ /*
+ * If sepostgresql = disabled, it always returns FALSE
+ * independently from the system status.
+ */
+ if (sepostgresql_mode == SEPGSQL_MODE_DISABLED)
+ return false;
+
+ /*
+ * SE-PgSQL needs SELinux is enabled on the operating system.
+ * If it is disabled, SE-PgSQL has to be also disabled, even if
+ * 'enforcing' or 'permissive' are specified.
+ */
+ if (enabled < 0)
+ enabled = is_selinux_enabled();
+
+ return enabled > 0 ? true : false;
+}
+
+/*
+ * sepgsql_get_enforce
+ *
+ * It returns true, if SE-PgSQL performs in enforcing mode.
+ *
+ * In enforcing mode, SE-PgSQL performs as expected. It checks permissions
+ * on the required action, and it prevents them if violated.
+ * In permissive mode, SE-PgSQL also checks permissions, but it does not
+ * prevent anything, even if violated. It generates audit logs for access
+ * violations, so we can use this mode to debug security policy itself.
+ */
+bool
+sepgsql_get_enforce(void)
+{
+ if (sepostgresql_mode == SEPGSQL_MODE_DEFAULT)
+ {
+ if (security_getenforce() == 1)
+ return true;
+ }
+ else if (sepostgresql_mode == SEPGSQL_MODE_ENFORCING)
+ return true;
+
+ return false;
+}
+
+/*
+ * sepgsql_show_mode
+ *
+ * It returns the current performing mode ('selinux_support')
+ * in human readable form.
+ */
+const char *
+sepgsql_show_mode(void)
+{
+ if (!sepgsql_is_enabled())
+ return "disabled";
+
+ if (!sepgsql_get_enforce())
+ return "permissive";
+
+ return "enforcing";
+}
+
+/*
+ * GUC parameter to turn on/off debuging audit generation
+ */
+bool sepgsql_debug_audit;
+
+/*
+ * 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, char *scontext, char *tcontext,
+ uint16 tclass, uint32 audited, const char *audit_name)
+{
+ StringInfoData buf;
+ const char *tclass_name;
+ const char *perm_name;
+ int level = LOG;
+ int i;
+
+ /*
+ * translation of security contexts to human readable format,
+ * if sepgsql_mcstrans is turned on.
+ */
+ scontext = sepgsql_mcstrans_out(scontext);
+ tcontext = sepgsql_mcstrans_out(tcontext);
+
+ /* lookup name of the object class */
+ tclass_name = selinux_catalog[tclass].class_name;
+
+ /* lookup name of the permissions */
+ initStringInfo(&buf);
+ appendStringInfo(&buf, "{");
+
+ for (i=0; selinux_catalog[tclass].perms[i].perm_name; i++)
+ {
+ if (audited & (1UL << i))
+ {
+ perm_name = selinux_catalog[tclass].perms[i].perm_name;
+ appendStringInfo(&buf, " %s", perm_name);
+ }
+ }
+ appendStringInfo(&buf, " }");
+
+ /*
+ * Call external audit module, if loaded
+ */
+ appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s",
+ scontext, tcontext, tclass_name);
+ if (audit_name)
+ appendStringInfo(&buf, " name=%s", audit_name);
+
+ if (sepgsql_debug_audit)
+ level = client_min_messages;
+
+ ereport(level,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("SELinux: %s %s",
+ (denied ? "denied" : "allowed"), 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(char *scontext, 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);
+
+ 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 = (deny_unknown > 0 ? 0 : ~0U);
+ 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(scontext, 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].perms[i].perm_name; i++)
+ {
+ access_vector_t perm_code_ex;
+ const char *perm_name = selinux_catalog[tclass].perms[i].perm_name;
+ uint32 perm_code = selinux_catalog[tclass].perms[i].perm_code;
+
+ perm_code_ex = string_to_av_perm(tclass_ex, perm_name);
+ if (perm_code_ex == 0)
+ {
+ /* fill up undefined permissions */
+ if (!deny_unknown)
+ avd->allowed |= perm_code;
+ avd->auditdeny |= perm_code;
+
+ continue;
+ }
+
+ if (avd_ex.allowed & perm_code_ex)
+ avd->allowed |= perm_code;
+ if (avd_ex.auditallow & perm_code_ex)
+ avd->auditallow |= perm_code;
+ if (avd_ex.auditdeny & perm_code_ex)
+ avd->auditdeny |= perm_code;
+ }
+
+ return;
+}
+
+/*
+ * sepgsql_compute_perms
+ *
+ * It makes access control decision communicating with SELinux.
+ * If SELinux does not allow required permissions on a pair of the security
+ * contexts, it raises an error or returns false.
+ *
+ * scontext : The security context of subject. In most cases, it is client.
+ * tcontext : The security context of target database object.
+ * tclass : One of the object class code (SEPG_CLASS_*) declared in the
+ * header file.
+ * required : A bitmap of the required permissions (SEPG_<class>__<perm>)
+ * declared in the header file.
+ * audit_name : A human readable name of the database object for auditing.
+ * abort : True, if caller want to raise an error on access violation.
+ */
+bool
+sepgsql_compute_perms(char *scontext, char *tcontext,
+ uint16 tclass, uint32 required,
+ const char *audit_name, bool abort)
+{
+ struct av_decision avd;
+ uint32 denied;
+ uint32 audited;
+
+ sepgsql_compute_avd(scontext, tcontext, tclass, &avd);
+
+ /*
+ * It logs a security audit record for the given request, if necessary.
+ * When SE-PgSQL performs 'internal' mode, it needs to keep silent.
+ */
+ denied = required & ~avd.allowed;
+ if (sepgsql_debug_audit && tclass != SEPG_CLASS_DB_TUPLE)
+ audited = (denied ? (denied & ~0) : (required & ~0));
+ else
+ audited = (denied ? (denied & avd.auditdeny)
+ : (required & avd.auditallow));
+
+ if (audited && sepostgresql_mode != SEPGSQL_MODE_INTERNAL)
+ {
+ sepgsql_audit_log(!!denied, scontext, tcontext,
+ tclass, audited, audit_name);
+ }
+
+ /*
+ * If here is no policy violations, or SE-PgSQL performs in permissive
+ * mode, or the client process peforms in permissive domain, it returns
+ * normally with 'true'.
+ */
+ if (!denied ||
+ !sepgsql_get_enforce() ||
+ (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) != 0)
+ return true;
+
+ /*
+ * Otherwise, it raises an error or returns 'false', depending on the
+ * caller's indication by 'abort'.
+ */
+ if (abort)
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("SELinux: security policy violation")));
+
+ return false;
+}
+
+/*
+ * 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(char *scontext, 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(scontext, 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;
+}
diff --git a/src/backend/sepgsql/tablespace.c b/src/backend/sepgsql/tablespace.c
new file mode 100644
index 0000000..1d3a35e
--- /dev/null
+++ b/src/backend/sepgsql/tablespace.c
@@ -0,0 +1,157 @@
+/*
+ * tablespace.c
+ *
+ * SELinux hooks related to tablespaces
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "catalog/pg_tablespace.h"
+#include "catalog/pg_seclabel.h"
+#include "commands/tablespace.h"
+#include "sepgsql/hooks.h"
+#include "sepgsql/sepgsql.h"
+#include "utils/syscache.h"
+#include "utils/lsyscache.h"
+
+#ifdef HAVE_SELINUX
+bool
+sepgsql_tablespace_common(Oid tablespaceOid, uint32 required, bool abort)
+{
+ HeapTuple tuple;
+ sepgsql_sid_t tsid;
+ bool retval;
+ const char *auname;
+
+ tuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(tablespaceOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for tablespace %u", tablespaceOid);
+
+ tsid.relid = TableSpaceRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ auname = NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname);
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ required,
+ auname,
+ abort);
+ ReleaseSysCache(tuple);
+
+ return retval;
+}
+
+#endif
+
+Oid
+sepgsql_tablespace_create(const char *tablespaceName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+
+ nsid = sepgsql_get_default_tuple_secid(TableSpaceRelationId);
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__INSERT,
+ tablespaceName,
+ true);
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_tablespace_alter(Oid tablespaceOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_tablespace_common(tablespaceOid,
+ SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+Oid
+sepgsql_tablespace_relabel(Oid tablespaceOid, char *newLabel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t tsid;
+
+ tsid.relid = TableSpaceRelationId;
+ tsid.secid = seclabelTransInput(tsid.relid, newLabel);
+
+ /* db_tuple:{update relabelfrom} */
+ sepgsql_tablespace_common(tablespaceOid,
+ SEPG_DB_TUPLE__UPDATE |
+ SEPG_DB_TUPLE__RELABELFROM, true);
+
+ /* db_procedure:{relabelto} */
+ sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__RELABELTO,
+ get_tablespace_name(tablespaceOid),
+ true);
+ return tsid.secid;
+ }
+#endif
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("SE-PostgreSQL is not available")));
+ return InvalidOid;
+}
+
+void
+sepgsql_tablespace_drop(Oid tablespaceOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_tablespace_common(tablespaceOid,
+ SEPG_DB_TUPLE__DELETE, true);
+ }
+#endif
+}
+
+void
+sepgsql_tablespace_grant(Oid tablespaceOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_tablespace_common(tablespaceOid,
+ SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_tablespace_getattr(Oid tablespaceOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_tablespace_common(tablespaceOid,
+ SEPG_DB_TUPLE__SELECT, true);
+ }
+#endif
+}
+
+void
+sepgsql_tablespace_comment(Oid tablespaceOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_tablespace_common(tablespaceOid,
+ SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
diff --git a/src/backend/sepgsql/tsearch.c b/src/backend/sepgsql/tsearch.c
new file mode 100644
index 0000000..3d4180d
--- /dev/null
+++ b/src/backend/sepgsql/tsearch.c
@@ -0,0 +1,524 @@
+/*
+ * tsearch.c
+ *
+ * SELinux hooks related to text searches
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "catalog/pg_ts_config.h"
+#include "catalog/pg_ts_dict.h"
+#include "catalog/pg_ts_parser.h"
+#include "catalog/pg_ts_template.h"
+#include "sepgsql/hooks.h"
+#include "sepgsql/sepgsql.h"
+#include "utils/syscache.h"
+
+#ifdef HAVE_SELINUX
+bool
+sepgsql_ts_config_common(Oid confOid, uint32 required, bool abort)
+{
+ sepgsql_sid_t tsid;
+ HeapTuple tuple;
+ const char *auname;
+ bool retval;
+
+ tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(confOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for text search configuration %u", confOid);
+
+ tsid.relid = TSConfigRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ auname = NameStr(((Form_pg_ts_config) GETSTRUCT(tuple))->cfgname);
+
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ required,
+ auname,
+ abort);
+ ReleaseSysCache(tuple);
+
+ return retval;
+}
+
+static Oid
+get_ts_config_namespace(Oid confOid)
+{
+ HeapTuple tuple;
+ Oid namespaceId = InvalidOid;
+
+ tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(confOid));
+ if (HeapTupleIsValid(tuple))
+ {
+ namespaceId = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgnamespace;
+
+ ReleaseSysCache(tuple);
+ }
+ return namespaceId;
+}
+
+bool
+sepgsql_ts_dict_common(Oid dictOid, uint32 required, bool abort)
+{
+ sepgsql_sid_t tsid;
+ HeapTuple tuple;
+ const char *auname;
+ bool retval;
+
+ tuple = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for text search dictionary %u", dictOid);
+
+ tsid.relid = TSDictionaryRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ auname = NameStr(((Form_pg_ts_dict) GETSTRUCT(tuple))->dictname);
+
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ required,
+ auname,
+ abort);
+ ReleaseSysCache(tuple);
+
+ return retval;
+}
+
+static Oid
+get_ts_dict_namespace(Oid dictOid)
+{
+ HeapTuple tuple;
+ Oid namespaceId = InvalidOid;
+
+ tuple = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictOid));
+ if (HeapTupleIsValid(tuple))
+ {
+ namespaceId = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictnamespace;
+
+ ReleaseSysCache(tuple);
+ }
+ return namespaceId;
+}
+
+bool
+sepgsql_ts_parser_common(Oid parseOid, uint32 required, bool abort)
+{
+ sepgsql_sid_t tsid;
+ HeapTuple tuple;
+ const char *auname;
+ bool retval;
+
+ tuple = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(parseOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for text search parser %u", parseOid);
+
+ tsid.relid = TSParserRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ auname = NameStr(((Form_pg_ts_parser) GETSTRUCT(tuple))->prsname);
+
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ required,
+ auname,
+ abort);
+ ReleaseSysCache(tuple);
+
+ return retval;
+}
+
+static Oid
+get_ts_parser_namespace(Oid parseOid)
+{
+ HeapTuple tuple;
+ Oid namespaceId = InvalidOid;
+
+ tuple = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(parseOid));
+ if (HeapTupleIsValid(tuple))
+ {
+ namespaceId = ((Form_pg_ts_parser) GETSTRUCT(tuple))->prsnamespace;
+
+ ReleaseSysCache(tuple);
+ }
+ return namespaceId;
+}
+
+bool
+sepgsql_ts_template_common(Oid templateOid, uint32 required, bool abort)
+{
+ sepgsql_sid_t tsid;
+ HeapTuple tuple;
+ const char *auname;
+ bool retval;
+
+ tuple = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(templateOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for text search template %u", templateOid);
+
+ tsid.relid = TSDictionaryRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ auname = NameStr(((Form_pg_ts_dict) GETSTRUCT(tuple))->dictname);
+
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ required,
+ auname,
+ abort);
+ ReleaseSysCache(tuple);
+
+ return retval;
+}
+
+static Oid
+get_ts_template_namespace(Oid templateOid)
+{
+ HeapTuple tuple;
+ Oid namespaceId = InvalidOid;
+
+ tuple = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(templateOid));
+ if (HeapTupleIsValid(tuple))
+ {
+ namespaceId = ((Form_pg_ts_template) GETSTRUCT(tuple))->tmplnamespace;
+
+ ReleaseSysCache(tuple);
+ }
+ return namespaceId;
+}
+
+#endif
+
+Oid
+sepgsql_ts_config_create(const char *confName, Oid namespaceId)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid
+ = sepgsql_get_default_tuple_secid(TSConfigRelationId);
+
+ /* db_schema:{add_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true);
+
+ /* db_tuple:{insert} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__INSERT,
+ confName,
+ true);
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_ts_config_alter(Oid confOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_ts_config_alter_rename(Oid confOid, const char *newName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_ts_config_namespace(confOid);
+
+ /* db_schema:{add_name remove_name} */
+ sepgsql_schema_common(namespaceId,
+ SEPG_DB_SCHEMA__ADD_NAME |
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_tuple:{update} */
+ sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_ts_config_drop(Oid confOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_ts_config_namespace(confOid);
+
+ /* db_schema:{remove_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_tuple:{delete} */
+ sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__DELETE, true);
+ }
+#endif
+}
+
+void
+sepgsql_ts_config_comment(Oid confOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+Oid
+sepgsql_ts_dict_create(const char *dictName, Oid namespaceId)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid
+ = sepgsql_get_default_tuple_secid(TSDictionaryRelationId);
+
+ /* db_schema:{add_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true);
+
+ /* db_tuple:{insert} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__INSERT,
+ dictName,
+ true);
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_ts_dict_alter(Oid dictOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_ts_dict_alter_rename(Oid dictOid, const char *newName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_ts_dict_namespace(dictOid);
+
+ /* db_schema:{add_name remove_name} */
+ sepgsql_schema_common(namespaceId,
+ SEPG_DB_SCHEMA__ADD_NAME |
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_tuple:{update} */
+ sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_ts_dict_drop(Oid dictOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_ts_dict_namespace(dictOid);
+
+ /* db_schema:{remove_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_tuple:{delete} */
+ sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__DELETE, true);
+ }
+#endif
+}
+
+void
+sepgsql_ts_dict_comment(Oid dictOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+Oid
+sepgsql_ts_parser_create(const char *parseName, Oid namespaceId,
+ Oid startFunc, Oid tokenFunc, Oid endFunc,
+ Oid headlineFunc, Oid lextypeFunc)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid
+ = sepgsql_get_default_tuple_secid(TSParserRelationId);
+
+ /* db_schema:{add_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true);
+
+ /* db_procedure:{install} */
+ if (OidIsValid(startFunc))
+ sepgsql_proc_common(startFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+ if (OidIsValid(tokenFunc))
+ sepgsql_proc_common(tokenFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+ if (OidIsValid(endFunc))
+ sepgsql_proc_common(endFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+ if (OidIsValid(headlineFunc))
+ sepgsql_proc_common(headlineFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+ if (OidIsValid(lextypeFunc))
+ sepgsql_proc_common(lextypeFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+
+ /* db_tuple:{insert} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__INSERT,
+ parseName,
+ true);
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_ts_parser_alter_rename(Oid parseOid, const char *newName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_ts_parser_namespace(parseOid);
+
+ /* db_schema:{add_name remove_name} */
+ sepgsql_schema_common(namespaceId,
+ SEPG_DB_SCHEMA__ADD_NAME |
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_tuple:{update} */
+ sepgsql_ts_parser_common(parseOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_ts_parser_drop(Oid parseOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_ts_parser_namespace(parseOid);
+
+ /* db_schema:{remove_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_tuple:{delete} */
+ sepgsql_ts_parser_common(parseOid, SEPG_DB_TUPLE__DELETE, true);
+ }
+#endif
+}
+
+void
+sepgsql_ts_parser_comment(Oid parseOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_ts_parser_common(parseOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+Oid
+sepgsql_ts_template_create(const char *templateName, Oid namespaceId,
+ Oid initFunc, Oid lexizeFunc)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid
+ = sepgsql_get_default_tuple_secid(TSTemplateRelationId);
+
+ /* db_schema:{add_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true);
+
+ /* db_procedure:{install} */
+ if (OidIsValid(initFunc))
+ sepgsql_proc_common(initFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+ if (OidIsValid(lexizeFunc))
+ sepgsql_proc_common(lexizeFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+
+ /* db_tuple:{insert} */
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__INSERT,
+ templateName,
+ true);
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_ts_template_alter_rename(Oid templateOid, const char *newName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_ts_template_namespace(templateOid);
+
+ /* db_schema:{add_name remove_name} */
+ sepgsql_schema_common(namespaceId,
+ SEPG_DB_SCHEMA__ADD_NAME |
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_tuple:{update} */
+ sepgsql_ts_template_common(templateOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_ts_template_drop(Oid templateOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_ts_template_namespace(templateOid);
+
+ /* db_schema:{remove_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_tuple:{delete} */
+ sepgsql_ts_template_common(templateOid, SEPG_DB_TUPLE__DELETE, true);
+ }
+#endif
+}
+
+void
+sepgsql_ts_template_comment(Oid templateOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_ts_template_common(templateOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
diff --git a/src/backend/sepgsql/type.c b/src/backend/sepgsql/type.c
new file mode 100644
index 0000000..8cd65ae
--- /dev/null
+++ b/src/backend/sepgsql/type.c
@@ -0,0 +1,314 @@
+/*
+ * type.c
+ *
+ * SELinux hooks related to types
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#include "postgres.h"
+
+#include "catalog/pg_cast.h"
+#include "catalog/pg_seclabel.h"
+#include "catalog/pg_type.h"
+#include "sepgsql/hooks.h"
+#include "sepgsql/sepgsql.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+#ifdef HAVE_SELINUX
+bool
+sepgsql_type_common(Oid typeOid, uint32 required, bool abort)
+{
+ sepgsql_sid_t tsid;
+ char *auname;
+ bool retval;
+
+ tsid.relid = TypeRelationId;
+ tsid.secid = GetSysCacheSecid1(TYPEOID,
+ ObjectIdGetDatum(typeOid));
+
+ auname = format_type_be(typeOid);
+
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ required,
+ auname,
+ abort);
+ pfree(auname);
+
+ return retval;
+}
+
+bool
+sepgsql_cast_common(Oid srcTypeOid, Oid dstTypeOid,
+ uint32 required, bool abort)
+{
+ HeapTuple tuple;
+ sepgsql_sid_t tsid;
+ bool retval;
+
+ tuple = SearchSysCache2(CASTSOURCETARGET,
+ ObjectIdGetDatum(srcTypeOid),
+ ObjectIdGetDatum(dstTypeOid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for cast (%u,%u)",
+ srcTypeOid, dstTypeOid);
+
+ tsid.relid = CastRelationId;
+ tsid.secid = HeapTupleGetSecid(tuple);
+
+ retval = sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ required,
+ NULL,
+ abort);
+ ReleaseSysCache(tuple);
+
+ return retval;
+}
+
+static Oid
+get_type_namespace(Oid typeOid)
+{
+ HeapTuple tuple;
+ Oid namespaceId;
+
+ tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid));
+ if (!HeapTupleIsValid(tuple))
+ return InvalidOid;
+
+ namespaceId = ((Form_pg_type) GETSTRUCT(tuple))->typnamespace;
+
+ ReleaseSysCache(tuple);
+
+ return namespaceId;
+}
+#endif
+
+Oid
+sepgsql_type_create(const char *typeName, Oid replaced,
+ Oid namespaceId, char typeType,
+ Oid inputFunc, Oid outputFunc,
+ Oid recvFunc, Oid sendFunc,
+ Oid modinFunc, Oid modoutFunc, Oid analyzeFunc)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+ uint32 required;
+
+ /* db_schema:{add_name} */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true);
+
+ /* db_tuple:{insert or update} */
+ if (!OidIsValid(replaced))
+ {
+ nsid = sepgsql_get_default_tuple_secid(TypeRelationId);
+ required = SEPG_DB_TUPLE__INSERT;
+ }
+ else
+ {
+ nsid.relid = TypeRelationId;
+ nsid.secid = GetSysCacheSecid1(TYPEOID,
+ ObjectIdGetDatum(replaced));
+ required = SEPG_DB_TUPLE__UPDATE;
+ }
+
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_TUPLE,
+ required,
+ typeName,
+ true);
+
+ /* db_procedure:{install} */
+ if (OidIsValid(inputFunc))
+ sepgsql_proc_common(inputFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+
+ /* db_procedure:{install} */
+ if (OidIsValid(outputFunc))
+ sepgsql_proc_common(outputFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+
+ /* db_procedure:{install} */
+ if (OidIsValid(recvFunc))
+ sepgsql_proc_common(recvFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+
+ /* db_procedure:{install} */
+ if (OidIsValid(sendFunc))
+ sepgsql_proc_common(sendFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+
+ /* db_procedure:{install} */
+ if (OidIsValid(modinFunc))
+ sepgsql_proc_common(modinFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+
+ /* db_procedure:{install} */
+ if (OidIsValid(modoutFunc))
+ sepgsql_proc_common(modoutFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+
+ /* db_procedure:{install} */
+ if (OidIsValid(analyzeFunc))
+ sepgsql_proc_common(analyzeFunc, SEPG_DB_PROCEDURE__INSTALL, true);
+
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_type_alter(Oid typeOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_type_alter_rename(Oid typeOid, const char *newName)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_type_namespace(typeOid);
+
+ /* db_schema:{add_name remove_name} */
+ sepgsql_schema_common(namespaceId,
+ SEPG_DB_SCHEMA__ADD_NAME |
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_tuple:{update} */
+ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+void
+sepgsql_type_alter_schema(Oid typeOid, Oid newSchema)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ Oid namespaceId = get_type_namespace(typeOid);
+
+ /* db_schema:{ remove_name } */
+ sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true);
+
+ /* db_schema:{ add_name } */
+ sepgsql_schema_common(newSchema, SEPG_DB_SCHEMA__ADD_NAME, true);
+
+ /* db_tuple:{update} */
+ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+Oid
+sepgsql_type_relabel(Oid typeOid, char *newLabel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t tsid;
+
+ tsid.relid = TypeRelationId;
+ tsid.secid = seclabelTransInput(tsid.relid, newLabel);
+
+ /* db_tuple:{update relabelfrom} */
+ sepgsql_type_common(typeOid,
+ SEPG_DB_TUPLE__UPDATE |
+ SEPG_DB_TUPLE__RELABELFROM, true);
+
+ /* db_procedure:{relabelto} */
+ sepgsql_client_perms(tsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__RELABELTO,
+ format_type_be(typeOid),
+ true);
+ return tsid.secid;
+ }
+#endif
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("SE-PostgreSQL is not available")));
+ return InvalidOid;
+}
+
+void
+sepgsql_type_drop(Oid typeOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{delete} */
+ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__DELETE, true);
+ }
+#endif
+}
+
+void
+sepgsql_type_comment(Oid typeOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ /* db_tuple:{update} */
+ sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
+
+Oid
+sepgsql_cast_create(Oid sourceTypeOid, Oid targetTypeOid,
+ char castMethod, Oid castFuncOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_sid_t nsid;
+
+ /* db_tuple:{insert} */
+ nsid = sepgsql_get_default_tuple_secid(CastRelationId);
+ sepgsql_client_perms(nsid,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_DB_TUPLE__INSERT,
+ NULL,
+ true);
+ /* db_procedure:{install} */
+ if (OidIsValid(castFuncOid))
+ sepgsql_proc_common(castFuncOid,
+ SEPG_DB_PROCEDURE__INSTALL, true);
+
+ return nsid.secid;
+ }
+#endif
+ return InvalidOid;
+}
+
+void
+sepgsql_cast_drop(Oid sourceTypeOid, Oid targetTypeOid, bool cascade)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_cast_common(sourceTypeOid, targetTypeOid,
+ SEPG_DB_TUPLE__DELETE, true);
+ }
+#endif
+}
+
+void
+sepgsql_cast_comment(Oid sourceTypeOid, Oid targetTypeOid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsql_is_enabled())
+ {
+ sepgsql_cast_common(sourceTypeOid, targetTypeOid,
+ SEPG_DB_TUPLE__UPDATE, true);
+ }
+#endif
+}
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index ca48cdd..bb0688f 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -197,14 +197,14 @@ getbytealen(bytea *data)
* in use.
*/
Oid
-inv_create(Oid lobjId)
+inv_create(Oid lobjId, Oid securityId)
{
Oid lobjId_new;
/*
* Create a new largeobject with empty data pages
*/
- lobjId_new = LargeObjectCreate(lobjId);
+ lobjId_new = LargeObjectCreate(lobjId, securityId);
/*
* dependency on the owner of largeobject
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
index 575fa86..eb2b5e8 100644
--- a/src/backend/tcop/fastpath.c
+++ b/src/backend/tcop/fastpath.c
@@ -26,6 +26,7 @@
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
+#include "sepgsql/hooks.h"
#include "tcop/fastpath.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
@@ -347,6 +348,10 @@ HandleFunctionRequest(StringInfo msgBuf)
aclcheck_error(aclresult, ACL_KIND_PROC,
get_func_name(fid));
+ /* SELinux checks */
+ sepgsql_schema_search(fip->namespace, true);
+ sepgsql_proc_execute(fid);
+
/*
* Prepare function call info block and insert arguments.
*/
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 8ad4915..54961d8 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -575,7 +575,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
Assert(pstmt->hasReturning);
portal->tupDesc =
ExecCleanTypeFromTL(pstmt->planTree->targetlist,
- false);
+ false, false);
}
/*
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 8960246..e8f8ea3 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -165,6 +165,7 @@ check_xact_readonly(Node *parsetree)
case T_AlterRoleSetStmt:
case T_AlterObjectSchemaStmt:
case T_AlterOwnerStmt:
+ case T_AlterSecLabelStmt:
case T_AlterSeqStmt:
case T_AlterTableStmt:
case T_RenameStmt:
@@ -696,6 +697,10 @@ standard_ProcessUtility(Node *parsetree,
ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree);
break;
+ case T_AlterSecLabelStmt:
+ ExecAlterSecLabelStmt((AlterSecLabelStmt *) parsetree);
+ break;
+
case T_AlterTableStmt:
{
List *stmts;
@@ -1760,6 +1765,46 @@ CreateCommandTag(Node *parsetree)
}
break;
+ case T_AlterSecLabelStmt:
+ switch (((AlterSecLabelStmt *) parsetree)->objectType)
+ {
+ case OBJECT_DATABASE:
+ tag = "ALTER DATABASE";
+ break;
+ case OBJECT_SCHEMA:
+ tag = "ALTER SCHEMA";
+ break;
+ case OBJECT_TABLE:
+ case OBJECT_COLUMN:
+ tag = "ALTER TABLE";
+ break;
+ case OBJECT_SEQUENCE:
+ tag = "ALTER SEQUENCE";
+ break;
+ case OBJECT_VIEW:
+ tag = "ALTER VIEW";
+ break;
+ case OBJECT_FUNCTION:
+ tag = "ALTER FUNCTION";
+ break;
+ case OBJECT_AGGREGATE:
+ tag = "ALTER AGGREGATE";
+ break;
+ case OBJECT_LARGEOBJECT:
+ tag = "ALTER LARGE OBJECT";
+ break;
+ case OBJECT_TYPE:
+ tag = "ALTER TYPE";
+ break;
+ case OBJECT_DOMAIN:
+ tag = "ALTER DOMAIN";
+ break;
+ default:
+ tag = "???";
+ break;
+ }
+ break;
+
case T_AlterTableStmt:
switch (((AlterTableStmt *) parsetree)->relkind)
{
@@ -2352,6 +2397,10 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_DDL;
break;
+ case T_AlterSecLabelStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_AlterTableStmt:
lev = LOGSTMT_DDL;
break;
diff --git a/src/backend/tsearch/wparser.c b/src/backend/tsearch/wparser.c
index 0fed35c..ca9e9d1 100644
--- a/src/backend/tsearch/wparser.c
+++ b/src/backend/tsearch/wparser.c
@@ -59,7 +59,7 @@ tt_setup_firstcall(FuncCallContext *funcctx, Oid prsid)
(Datum) 0));
funcctx->user_fctx = (void *) st;
- tupdesc = CreateTemplateTupleDesc(3, false);
+ tupdesc = CreateTemplateTupleDesc(3, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "tokid",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "alias",
@@ -205,7 +205,7 @@ prs_setup_firstcall(FuncCallContext *funcctx, Oid prsid, text *txt)
st->cur = 0;
funcctx->user_fctx = (void *) st;
- tupdesc = CreateTemplateTupleDesc(2, false);
+ tupdesc = CreateTemplateTupleDesc(2, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "tokid",
INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "token",
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index faad11e..53e6944 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -1695,7 +1695,7 @@ aclexplode(PG_FUNCTION_ARGS)
* build tupdesc for result tuples (matches out parameters in pg_proc
* entry)
*/
- tupdesc = CreateTemplateTupleDesc(4, false);
+ tupdesc = CreateTemplateTupleDesc(4, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "grantor",
OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "grantee",
diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c
index ed81ced..f3d4b99 100644
--- a/src/backend/utils/adt/datetime.c
+++ b/src/backend/utils/adt/datetime.c
@@ -4193,7 +4193,7 @@ pg_timezone_abbrevs(PG_FUNCTION_ARGS)
* build tupdesc for result tuples. This must match this function's
* pg_proc entry!
*/
- tupdesc = CreateTemplateTupleDesc(3, false);
+ tupdesc = CreateTemplateTupleDesc(3, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "abbrev",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "utc_offset",
@@ -4286,7 +4286,7 @@ pg_timezone_names(PG_FUNCTION_ARGS)
* build tupdesc for result tuples. This must match this function's
* pg_proc entry!
*/
- tupdesc = CreateTemplateTupleDesc(4, false);
+ tupdesc = CreateTemplateTupleDesc(4, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "abbrev",
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index a4e0252..36d07b7 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -21,6 +21,7 @@
#include "commands/dbcommands.h"
#include "commands/tablespace.h"
#include "miscadmin.h"
+#include "sepgsql/hooks.h"
#include "storage/fd.h"
#include "utils/acl.h"
#include "utils/builtins.h"
@@ -90,6 +91,9 @@ calculate_database_size(Oid dbOid)
aclcheck_error(aclresult, ACL_KIND_DATABASE,
get_database_name(dbOid));
+ /* SELinux checks */
+ sepgsql_database_getattr(dbOid);
+
/* Shared storage in pg_global is not counted */
/* Include pg_default storage */
@@ -178,6 +182,8 @@ calculate_tablespace_size(Oid tblspcOid)
aclcheck_error(aclresult, ACL_KIND_TABLESPACE,
get_tablespace_name(tblspcOid));
}
+ /* SELinux checks */
+ sepgsql_tablespace_getattr(tblspcOid);
if (tblspcOid == DEFAULTTABLESPACE_OID)
snprintf(tblspcPath, MAXPGPATH, "base");
diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
index e074b79..8bb707f 100644
--- a/src/backend/utils/adt/genfile.c
+++ b/src/backend/utils/adt/genfile.c
@@ -25,6 +25,7 @@
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "postmaster/syslogger.h"
+#include "sepgsql/hooks.h"
#include "storage/fd.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
@@ -100,6 +101,8 @@ pg_read_file(PG_FUNCTION_ARGS)
filename = convert_and_check_filename(filename_t);
+ sepgsql_file_read(filename);
+
if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
ereport(ERROR,
(errcode_for_file_access(),
@@ -164,6 +167,8 @@ pg_stat_file(PG_FUNCTION_ARGS)
filename = convert_and_check_filename(filename_t);
+ sepgsql_file_getattr(filename);
+
if (stat(filename, &fst) < 0)
ereport(ERROR,
(errcode_for_file_access(),
@@ -173,7 +178,7 @@ pg_stat_file(PG_FUNCTION_ARGS)
* This record type had better match the output parameters declared for me
* in pg_proc.h.
*/
- tupdesc = CreateTemplateTupleDesc(6, false);
+ tupdesc = CreateTemplateTupleDesc(6, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1,
"size", INT8OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2,
@@ -236,6 +241,8 @@ pg_ls_dir(PG_FUNCTION_ARGS)
fctx = palloc(sizeof(directory_fctx));
fctx->location = convert_and_check_filename(PG_GETARG_TEXT_P(0));
+ sepgsql_dir_list(fctx->location);
+
fctx->dirdesc = AllocateDir(fctx->location);
if (!fctx->dirdesc)
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index 07e6aab..e5c8182 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -85,7 +85,7 @@ pg_lock_status(PG_FUNCTION_ARGS)
/* build tupdesc for result tuples */
/* this had better match pg_locks view in system_views.sql */
- tupdesc = CreateTemplateTupleDesc(14, false);
+ tupdesc = CreateTemplateTupleDesc(14, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "locktype",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database",
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 11342b2..0ddf907 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -322,7 +322,7 @@ pg_get_keywords(PG_FUNCTION_ARGS)
funcctx = SRF_FIRSTCALL_INIT();
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
- tupdesc = CreateTemplateTupleDesc(3, false);
+ tupdesc = CreateTemplateTupleDesc(3, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "word",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "catcode",
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 8379407..d44655b 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -419,7 +419,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
- tupdesc = CreateTemplateTupleDesc(11, false);
+ tupdesc = CreateTemplateTupleDesc(11, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid", OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "procpid", INT4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid", OIDOID, -1, 0);
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 9db070d..6881976 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -30,6 +30,7 @@
#include "postgres.h"
+#include "access/sysattr.h"
#include "access/xact.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_operator.h"
@@ -39,6 +40,7 @@
#include "parser/parse_coerce.h"
#include "parser/parse_relation.h"
#include "miscadmin.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@@ -2624,6 +2626,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
char fkrelname[MAX_QUOTED_REL_NAME_LEN];
char pkattname[MAX_QUOTED_NAME_LEN + 3];
char fkattname[MAX_QUOTED_NAME_LEN + 3];
+ Bitmapset *pkColumns = NULL;
+ Bitmapset *fkColumns = NULL;
const char *sep;
int i;
int old_work_mem;
@@ -2645,6 +2649,18 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
ri_FetchConstraintInfo(&riinfo, trigger, fk_rel, false);
+ for (i = 0; i < riinfo.nkeys; i++)
+ {
+ fkColumns = bms_add_member(fkColumns, riinfo.fk_attnums[i]
+ - FirstLowInvalidHeapAttributeNumber);
+ pkColumns = bms_add_member(pkColumns, riinfo.pk_attnums[i]
+ - FirstLowInvalidHeapAttributeNumber);
+ }
+
+ if (!sepgsql_relation_perms(RelationGetRelid(pk_rel),
+ ACL_SELECT, pkColumns, NULL, false))
+ return false;
+
/*----------
* The query string built is:
* SELECT fk.keycols FROM ONLY relname fk
@@ -3259,6 +3275,7 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
int spi_result;
Oid save_userid;
int save_sec_context;
+ int save_rowlv, temp_rowlv;
Datum vals[RI_MAX_NUMKEYS * 2];
char nulls[RI_MAX_NUMKEYS * 2];
@@ -3342,6 +3359,10 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
+ /* Switch rowlv control mode */
+ temp_rowlv = (detectNewRows ? SEPGSQL_ROWLV_ABORT : SEPGSQL_ROWLV_FILTER);
+ save_rowlv = sepgsql_rowlv_set_mode(temp_rowlv);
+
/* Finally we can run the query. */
spi_result = SPI_execute_snapshot(qplan,
vals, nulls,
@@ -3351,6 +3372,9 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
/* Restore UID and security context */
SetUserIdAndSecContext(save_userid, save_sec_context);
+ /* Restore Rowlv control mode */
+ sepgsql_rowlv_set_mode(save_rowlv);
+
/* Check result */
if (spi_result < 0)
elog(ERROR, "SPI_execute_snapshot returned %d", spi_result);
diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c
index c837e67..1bf4eb8 100644
--- a/src/backend/utils/adt/tid.c
+++ b/src/backend/utils/adt/tid.c
@@ -27,6 +27,7 @@
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "parser/parsetree.h"
+#include "sepgsql/hooks.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/rel.h"
@@ -347,6 +348,8 @@ currtid_byreloid(PG_FUNCTION_ARGS)
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_CLASS,
RelationGetRelationName(rel));
+ /* SELinux checks */
+ sepgsql_relation_getattr(RelationGetRelid(rel));
if (rel->rd_rel->relkind == RELKIND_VIEW)
return currtid_for_view(rel, tid);
@@ -377,6 +380,8 @@ currtid_byrelname(PG_FUNCTION_ARGS)
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_CLASS,
RelationGetRelationName(rel));
+ /* SELinux checks */
+ sepgsql_relation_getattr(RelationGetRelid(rel));
if (rel->rd_rel->relkind == RELKIND_VIEW)
return currtid_for_view(rel, tid);
diff --git a/src/backend/utils/adt/trigfuncs.c b/src/backend/utils/adt/trigfuncs.c
index 70246fb..b996537 100644
--- a/src/backend/utils/adt/trigfuncs.c
+++ b/src/backend/utils/adt/trigfuncs.c
@@ -76,6 +76,10 @@ suppress_redundant_updates_trigger(PG_FUNCTION_ARGS)
!OidIsValid(HeapTupleHeaderGetOid(newheader)))
HeapTupleHeaderSetOid(newheader, HeapTupleHeaderGetOid(oldheader));
+ if (trigdata->tg_relation->rd_rel->relhassecids &&
+ !OidIsValid(HeapTupleHeaderGetSecid(newheader)))
+ HeapTupleHeaderSetSecid(newheader, HeapTupleHeaderGetSecid(oldheader));
+
/* if the tuple payload is the same ... */
if (newtuple->t_len == oldtuple->t_len &&
newheader->t_hoff == oldheader->t_hoff &&
diff --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c
index 78f08f4..4a44810 100644
--- a/src/backend/utils/adt/tsvector_op.c
+++ b/src/backend/utils/adt/tsvector_op.c
@@ -975,7 +975,7 @@ ts_setup_firstcall(FunctionCallInfo fcinfo, FuncCallContext *funcctx,
}
Assert(stat->stackpos <= stat->maxdepth);
- tupdesc = CreateTemplateTupleDesc(3, false);
+ tupdesc = CreateTemplateTupleDesc(3, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "word",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "ndoc",
diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c
index 88d8d8b..281acb0 100644
--- a/src/backend/utils/cache/plancache.c
+++ b/src/backend/utils/cache/plancache.c
@@ -924,12 +924,12 @@ PlanCacheComputeResultDesc(List *stmt_list)
if (IsA(node, Query))
{
query = (Query *) node;
- return ExecCleanTypeFromTL(query->targetList, false);
+ return ExecCleanTypeFromTL(query->targetList, false, false);
}
if (IsA(node, PlannedStmt))
{
pstmt = (PlannedStmt *) node;
- return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false);
+ return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false, false);
}
/* other cases shouldn't happen, but return NULL */
break;
@@ -940,13 +940,13 @@ PlanCacheComputeResultDesc(List *stmt_list)
{
query = (Query *) node;
Assert(query->returningList);
- return ExecCleanTypeFromTL(query->returningList, false);
+ return ExecCleanTypeFromTL(query->returningList, false, false);
}
if (IsA(node, PlannedStmt))
{
pstmt = (PlannedStmt *) node;
Assert(pstmt->hasReturning);
- return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false);
+ return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false, false);
}
/* other cases shouldn't happen, but return NULL */
break;
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index d462510..c704bc9 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -216,7 +216,7 @@ static void write_relcache_init_file(bool shared);
static void write_item(const void *data, Size len, FILE *fp);
static void formrdesc(const char *relationName, Oid relationReltype,
- bool isshared, bool hasoids,
+ bool isshared, bool hasoids, bool hassecids,
int natts, const FormData_pg_attribute *attrs);
static HeapTuple ScanPgRelation(Oid targetRelId, bool indexOK);
@@ -355,7 +355,8 @@ AllocateRelationDesc(Form_pg_class relp)
/* and allocate attribute tuple form storage */
relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts,
- relationForm->relhasoids);
+ relationForm->relhasoids,
+ relationForm->relhassecids);
/* which we mark as a reference-counted tupdesc */
relation->rd_att->tdrefcount = 1;
@@ -437,6 +438,7 @@ RelationBuildTupleDesc(Relation relation)
relation->rd_att->tdtypeid = relation->rd_rel->reltype;
relation->rd_att->tdtypmod = -1; /* unnecessary, but... */
relation->rd_att->tdhasoid = relation->rd_rel->relhasoids;
+ relation->rd_att->tdhassecid = relation->rd_rel->relhassecids;
constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext,
sizeof(TupleConstr));
@@ -1396,7 +1398,7 @@ LookupOpclassInfo(Oid operatorClassOid,
*/
static void
formrdesc(const char *relationName, Oid relationReltype,
- bool isshared, bool hasoids,
+ bool isshared, bool hasoids, bool hassecids,
int natts, const FormData_pg_attribute *attrs)
{
Relation relation;
@@ -1459,6 +1461,7 @@ formrdesc(const char *relationName, Oid relationReltype,
relation->rd_rel->reltuples = 1;
relation->rd_rel->relkind = RELKIND_RELATION;
relation->rd_rel->relhasoids = hasoids;
+ relation->rd_rel->relhassecids = hassecids;
relation->rd_rel->relnatts = (int16) natts;
/*
@@ -1468,7 +1471,7 @@ formrdesc(const char *relationName, Oid relationReltype,
* because it will never be replaced. The data comes from
* src/include/catalog/ headers via genbki.pl.
*/
- relation->rd_att = CreateTemplateTupleDesc(natts, hasoids);
+ relation->rd_att = CreateTemplateTupleDesc(natts, hasoids, hassecids);
relation->rd_att->tdrefcount = 1; /* mark as refcounted */
relation->rd_att->tdtypeid = relationReltype;
@@ -2551,6 +2554,7 @@ RelationBuildLocalRelation(const char *relname,
rel->rd_rel->relkind = RELKIND_UNCATALOGED;
rel->rd_rel->relhasoids = rel->rd_att->tdhasoid;
+ rel->rd_rel->relhassecids = rel->rd_att->tdhassecid;
rel->rd_rel->relnatts = natts;
rel->rd_rel->reltype = InvalidOid;
/* needed when bootstrapping: */
@@ -2793,11 +2797,11 @@ RelationCacheInitializePhase2(void)
if (!load_relcache_init_file(true))
{
formrdesc("pg_database", DatabaseRelation_Rowtype_Id, true,
- true, Natts_pg_database, Desc_pg_database);
+ true, true, Natts_pg_database, Desc_pg_database);
formrdesc("pg_authid", AuthIdRelation_Rowtype_Id, true,
- true, Natts_pg_authid, Desc_pg_authid);
+ true, true, Natts_pg_authid, Desc_pg_authid);
formrdesc("pg_auth_members", AuthMemRelation_Rowtype_Id, true,
- false, Natts_pg_auth_members, Desc_pg_auth_members);
+ false, false, Natts_pg_auth_members, Desc_pg_auth_members);
#define NUM_CRITICAL_SHARED_RELS 3 /* fix if you change list above */
}
@@ -2848,13 +2852,13 @@ RelationCacheInitializePhase3(void)
needNewCacheFile = true;
formrdesc("pg_class", RelationRelation_Rowtype_Id, false,
- true, Natts_pg_class, Desc_pg_class);
+ true, true, Natts_pg_class, Desc_pg_class);
formrdesc("pg_attribute", AttributeRelation_Rowtype_Id, false,
- false, Natts_pg_attribute, Desc_pg_attribute);
+ false, true, Natts_pg_attribute, Desc_pg_attribute);
formrdesc("pg_proc", ProcedureRelation_Rowtype_Id, false,
- true, Natts_pg_proc, Desc_pg_proc);
+ true, true, Natts_pg_proc, Desc_pg_proc);
formrdesc("pg_type", TypeRelation_Rowtype_Id, false,
- true, Natts_pg_type, Desc_pg_type);
+ true, true, Natts_pg_type, Desc_pg_type);
#define NUM_CRITICAL_LOCAL_RELS 4 /* fix if you change list above */
}
@@ -3007,6 +3011,7 @@ RelationCacheInitializePhase3(void)
Assert(relation->rd_att->tdtypeid == relp->reltype);
Assert(relation->rd_att->tdtypmod == -1);
Assert(relation->rd_att->tdhasoid == relp->relhasoids);
+ Assert(relation->rd_att->tdhassecid == relp->relhassecids);
ReleaseSysCache(htup);
@@ -3117,7 +3122,7 @@ load_critical_index(Oid indexoid, Oid heapoid)
*/
static TupleDesc
BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs,
- bool hasoids)
+ bool hasoids, bool hassecids)
{
TupleDesc result;
MemoryContext oldcxt;
@@ -3125,7 +3130,7 @@ BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs,
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
- result = CreateTemplateTupleDesc(natts, hasoids);
+ result = CreateTemplateTupleDesc(natts, hasoids, hassecids);
result->tdtypeid = RECORDOID; /* not right, but we don't care */
result->tdtypmod = -1;
@@ -3155,7 +3160,7 @@ GetPgClassDescriptor(void)
if (pgclassdesc == NULL)
pgclassdesc = BuildHardcodedDescriptor(Natts_pg_class,
Desc_pg_class,
- true);
+ true, true);
return pgclassdesc;
}
@@ -3169,7 +3174,7 @@ GetPgIndexDescriptor(void)
if (pgindexdesc == NULL)
pgindexdesc = BuildHardcodedDescriptor(Natts_pg_index,
Desc_pg_index,
- false);
+ false, false);
return pgindexdesc;
}
@@ -3985,7 +3990,8 @@ load_relcache_init_file(bool shared)
/* initialize attribute tuple forms */
rel->rd_att = CreateTemplateTupleDesc(relform->relnatts,
- relform->relhasoids);
+ relform->relhasoids,
+ relform->relhassecids);
rel->rd_att->tdrefcount = 1; /* mark as refcounted */
rel->rd_att->tdtypeid = relform->reltype;
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 61b06ac..b08654b 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -895,6 +895,30 @@ GetSysCacheOid(int cacheId,
return result;
}
+/*
+ * GetSysCacheSecid
+ *
+ * A convenience routine that does SearchSysCache and returns the
+ * security-id of the found tuple, or InvalidOid if no tuple could
+ * be found. No lock is retained on the syscache entry.
+ */
+Oid
+GetSysCacheSecid(int cacheId,
+ Datum key1,
+ Datum key2,
+ Datum key3,
+ Datum key4)
+{
+ HeapTuple tuple;
+ Oid result;
+
+ tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
+ if (!HeapTupleIsValid(tuple))
+ return InvalidOid;
+ result = HeapTupleGetSecid(tuple);
+ ReleaseSysCache(tuple);
+ return result;
+}
/*
* SearchSysCacheAttName
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index c3c0440..fb5b762 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -24,6 +24,7 @@
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "pgstat.h"
+#include "sepgsql/hooks.h"
#include "utils/builtins.h"
#include "utils/fmgrtab.h"
#include "utils/guc.h"
@@ -190,6 +191,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
finfo->fn_extra = NULL;
finfo->fn_mcxt = mcxt;
finfo->fn_expr = NULL; /* caller may set this later */
+ finfo->fn_seclabel = NULL;
if ((fbp = fmgr_isbuiltin(functionId)) != NULL)
{
@@ -228,15 +230,22 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
* ability to set the track_functions GUC as a local GUC parameter of an
* interesting function and have the right things happen.
*/
- if (!ignore_security &&
- (procedureStruct->prosecdef ||
- !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig)))
+ if (!ignore_security)
{
- finfo->fn_addr = fmgr_security_definer;
- finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */
- finfo->fn_oid = functionId;
- ReleaseSysCache(procedureTuple);
- return;
+ char *seclabel
+ = sepgsql_proc_domtrans(procedureTuple, mcxt);
+
+ if (procedureStruct->prosecdef ||
+ !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig) ||
+ seclabel != NULL)
+ {
+ finfo->fn_addr = fmgr_security_definer;
+ finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */
+ finfo->fn_oid = functionId;
+ finfo->fn_seclabel = seclabel;
+ ReleaseSysCache(procedureTuple);
+ return;
+ }
}
switch (procedureStruct->prolang)
@@ -877,6 +886,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
FmgrInfo *save_flinfo;
Oid save_userid;
int save_sec_context;
+ char *save_seclabel;
volatile int save_nestlevel;
PgStat_FunctionCallUsage fcusage;
@@ -939,6 +949,10 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
PGC_S_SESSION,
GUC_ACTION_SAVE);
}
+ if (fcinfo->flinfo->fn_seclabel)
+ save_seclabel = sepgsql_set_client_label(fcinfo->flinfo->fn_seclabel);
+ else
+ save_seclabel = NULL;
/*
* We don't need to restore GUC or userid settings on error, because the
@@ -978,6 +992,8 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
AtEOXact_GUC(true, save_nestlevel);
if (OidIsValid(fcache->userid))
SetUserIdAndSecContext(save_userid, save_sec_context);
+ if (fcinfo->flinfo->fn_seclabel)
+ sepgsql_set_client_label(save_seclabel);
return result;
}
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 382008c..e6eba47 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -1105,7 +1105,7 @@ build_function_result_tupdesc_d(Datum proallargtypes,
if (numoutargs < 2)
return NULL;
- desc = CreateTemplateTupleDesc(numoutargs, false);
+ desc = CreateTemplateTupleDesc(numoutargs, false, false);
for (i = 0; i < numoutargs; i++)
{
TupleDescInitEntry(desc, i + 1,
@@ -1220,7 +1220,7 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
/* OK, get the column alias */
attname = strVal(linitial(colaliases));
- tupdesc = CreateTemplateTupleDesc(1, false);
+ tupdesc = CreateTemplateTupleDesc(1, false, false);
TupleDescInitEntry(tupdesc,
(AttrNumber) 1,
attname,
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 179d009..4bd7314 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -37,6 +37,7 @@
#include "postmaster/autovacuum.h"
#include "postmaster/postmaster.h"
#include "replication/walsender.h"
+#include "sepgsql/hooks.h"
#include "storage/bufmgr.h"
#include "storage/fd.h"
#include "storage/ipc.h"
@@ -304,6 +305,9 @@ CheckMyDatabase(const char *name, bool am_superuser)
errmsg("permission denied for database \"%s\"", name),
errdetail("User does not have CONNECT privilege.")));
+ /* SELinux checks */
+ sepgsql_database_connect(MyDatabaseId);
+
/*
* Check connection limit for this database.
*
@@ -800,6 +804,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
/* set up ACL framework (so CheckMyDatabase can check permissions) */
initialize_acl();
+ /* set up enhanced security feature */
+ sepgsql_initialize();
+
/*
* Re-read the pg_database row for our database, check permissions and set
* up database-specific GUC settings. We can't do this until all the
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 920daeb..44fd3a9 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -31,6 +31,7 @@
#include "access/twophase.h"
#include "access/xact.h"
#include "catalog/namespace.h"
+#include "catalog/pg_seclabel.h"
#include "commands/async.h"
#include "commands/prepare.h"
#include "commands/vacuum.h"
@@ -56,6 +57,7 @@
#include "postmaster/syslogger.h"
#include "postmaster/walwriter.h"
#include "replication/walsender.h"
+#include "sepgsql/sepgsql.h"
#include "storage/bufmgr.h"
#include "storage/fd.h"
#include "tcop/tcopprot.h"
@@ -337,6 +339,18 @@ static const struct config_enum_entry constraint_exclusion_options[] = {
{NULL, 0, false}
};
+#ifdef HAVE_SELINUX
+static const struct config_enum_entry sepostgresql_options[] = {
+ {"default", SEPGSQL_MODE_DEFAULT, false},
+ {"enforcing", SEPGSQL_MODE_ENFORCING, false},
+ {"permissive", SEPGSQL_MODE_PERMISSIVE, false},
+ {"disabled", SEPGSQL_MODE_DISABLED, false},
+ {"on", SEPGSQL_MODE_DEFAULT, true},
+ {"off", SEPGSQL_MODE_DISABLED, true},
+ {NULL, 0, false},
+};
+#endif
+
/*
* Options for enum values stored in other modules
*/
@@ -366,6 +380,7 @@ bool log_btree_build_stats = false;
bool check_function_bodies = true;
bool default_with_oids = false;
+bool default_with_secids = true;
bool SQL_inheritance = true;
bool Password_encryption = true;
@@ -1094,6 +1109,14 @@ static struct config_bool ConfigureNamesBool[] =
false, NULL, NULL
},
{
+ {"default_with_secids", PGC_USERSET, CONN_AUTH_SETTINGS,
+ gettext_noop("Create new tables with security-ids by default."),
+ NULL
+ },
+ &default_with_secids,
+ true, NULL, NULL
+ },
+ {
{"logging_collector", PGC_POSTMASTER, LOGGING_WHERE,
gettext_noop("Start a subprocess to capture stderr output and/or csvlogs into log files."),
NULL
@@ -1261,6 +1284,36 @@ static struct config_bool ConfigureNamesBool[] =
false, NULL, NULL
},
+ {
+ {"ignore_security_label_input", PGC_USERSET, CONN_AUTH_SECURITY,
+ gettext_noop("Disables to assign used provided security label"),
+ NULL,
+ GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE
+ },
+ &ignore_security_label_input,
+ false, NULL, NULL
+ },
+#ifdef HAVE_SELINUX
+ {
+ {"sepostgresql_mcstrans", PGC_USERSET, CONN_AUTH_SECURITY,
+ gettext_noop("Enables to show security context in human-readable form"),
+ NULL,
+ GUC_NOT_IN_SAMPLE
+ },
+ &sepgsql_mcstrans,
+ true, NULL, NULL
+ },
+ {
+ {"sepostgresql_debug_audit", PGC_USERSET, CONN_AUTH_SECURITY,
+ gettext_noop("Enables to show audit logs for debugging"),
+ NULL,
+ GUC_NOT_IN_SAMPLE
+ },
+ &sepgsql_debug_audit,
+ false, NULL, NULL
+ },
+#endif
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL
@@ -2820,7 +2873,16 @@ static struct config_enum ConfigureNamesEnum[] =
&xmloption,
XMLOPTION_CONTENT, xmloption_options, NULL, NULL
},
-
+#ifdef HAVE_SELINUX
+ {
+ {"sepostgresql", PGC_POSTMASTER, CONN_AUTH_SECURITY,
+ gettext_noop("Enables to set SE-PostgreSQL's mode"),
+ NULL
+ },
+ &sepostgresql_mode,
+ SEPGSQL_MODE_DEFAULT, sepostgresql_options, NULL, sepgsql_show_mode
+ },
+#endif
/* End-of-list marker */
{
@@ -6113,7 +6175,7 @@ GetPGVariableResultDesc(const char *name)
if (guc_name_compare(name, "all") == 0)
{
/* need a tuple descriptor representing three TEXT columns */
- tupdesc = CreateTemplateTupleDesc(3, false);
+ tupdesc = CreateTemplateTupleDesc(3, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
@@ -6129,7 +6191,7 @@ GetPGVariableResultDesc(const char *name)
(void) GetConfigOptionByName(name, &varname);
/* need a tuple descriptor representing a single TEXT column */
- tupdesc = CreateTemplateTupleDesc(1, false);
+ tupdesc = CreateTemplateTupleDesc(1, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname,
TEXTOID, -1, 0);
}
@@ -6152,7 +6214,7 @@ ShowGUCConfigOption(const char *name, DestReceiver *dest)
value = GetConfigOptionByName(name, &varname);
/* need a tuple descriptor representing a single TEXT column */
- tupdesc = CreateTemplateTupleDesc(1, false);
+ tupdesc = CreateTemplateTupleDesc(1, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname,
TEXTOID, -1, 0);
@@ -6179,7 +6241,7 @@ ShowAllGUCConfig(DestReceiver *dest)
bool isnull[3] = {false, false, false};
/* need a tuple descriptor representing three TEXT columns */
- tupdesc = CreateTemplateTupleDesc(3, false);
+ tupdesc = CreateTemplateTupleDesc(3, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
@@ -6574,7 +6636,7 @@ show_all_settings(PG_FUNCTION_ARGS)
* need a tuple descriptor representing NUM_PG_SETTINGS_ATTS columns
* of the appropriate types
*/
- tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS, false);
+ tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 2ed8412..10f8947 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -76,6 +76,8 @@
# - Security and Authentication -
+#sepostgresql = disabled # default|enforcing|permissive|disabled
+
#authentication_timeout = 1min # 1s-600s
#ssl = off # (change requires restart)
#ssl_ciphers = 'ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH' # allowed SSL ciphers
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index ac62d45..f05a094 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -894,7 +894,7 @@ pg_cursor(PG_FUNCTION_ARGS)
* build tupdesc for result tuples. This must match the definition of the
* pg_cursors view in system_views.sql
*/
- tupdesc = CreateTemplateTupleDesc(6, false);
+ tupdesc = CreateTemplateTupleDesc(6, false, false);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index f40ad87..497bdf0 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -87,6 +87,7 @@ static bool debug = false;
static bool noclean = false;
static bool show_setting = false;
static char *xlog_dir = "";
+static bool enable_selinux = false;
/* internal vars */
@@ -1163,6 +1164,13 @@ setup_config(void)
"#default_text_search_config = 'pg_catalog.simple'",
repltok);
+ if (enable_selinux)
+ {
+ strcpy(repltok, "sepostgresql = default");
+ conflines = replace_token(conflines,
+ "#sepostgresql = disabled", repltok);
+ }
+
snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data);
writefile(path, conflines);
@@ -2394,6 +2402,7 @@ usage(const char *progname)
printf(_(" -U, --username=NAME database superuser name\n"));
printf(_(" -W, --pwprompt prompt for a password for the new superuser\n"));
printf(_(" -X, --xlogdir=XLOGDIR location for the transaction log directory\n"));
+ printf(_(" --enable-selinux enables SELinux support\n"));
printf(_("\nLess commonly used options:\n"));
printf(_(" -d, --debug generate lots of debugging output\n"));
printf(_(" -L DIRECTORY where to find the input files\n"));
@@ -2436,6 +2445,7 @@ main(int argc, char *argv[])
{"show", no_argument, NULL, 's'},
{"noclean", no_argument, NULL, 'n'},
{"xlogdir", required_argument, NULL, 'X'},
+ {"enable-selinux", no_argument, NULL, 10},
{NULL, 0, NULL, 0}
};
@@ -2545,6 +2555,9 @@ main(int argc, char *argv[])
case 9:
pwfilename = xstrdup(optarg);
break;
+ case 10:
+ enable_selinux = true;
+ break;
case 's':
show_setting = true;
break;
diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h
index 14fe652..40b7b40 100644
--- a/src/bin/pg_dump/pg_backup.h
+++ b/src/bin/pg_dump/pg_backup.h
@@ -103,6 +103,7 @@ typedef struct _restoreOptions
* restore */
int use_setsessauth;/* Use SET SESSION AUTHORIZATION commands
* instead of OWNER TO */
+ int noSecLabel; /* Don't try to restore security labels */
char *superuser; /* Username to use as superuser */
char *use_role; /* Issue SET ROLE to this */
int dataOnly;
@@ -167,7 +168,7 @@ extern void ArchiveEntry(Archive *AHX,
CatalogId catalogId, DumpId dumpId,
const char *tag,
const char *namespace, const char *tablespace,
- const char *owner, bool withOids,
+ const char *owner, bool withOids, bool withSecids,
const char *desc, teSection section,
const char *defn,
const char *dropStmt, const char *copyStmt,
diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c
index 6bbe2de..9da50b0 100644
--- a/src/bin/pg_dump/pg_backup_archiver.c
+++ b/src/bin/pg_dump/pg_backup_archiver.c
@@ -540,6 +540,8 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te,
}
else
{
+ bool need_reset = false;
+
_disableTriggersIfNecessary(AH, te, ropt);
/* Select owner and schema as necessary */
@@ -575,6 +577,16 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te,
"ONLY " : ""),
fmtId(te->tag));
}
+ /*
+ * If data section has security_label, but pg_restore works
+ * with --no-security-label, we set server flag to ignore
+ * the security label input.
+ */
+ if (ropt->noSecLabel && te->withSecids)
+ {
+ ahprintf(AH, "SET ignore_security_label_input = on;\n\n");
+ need_reset = true;
+ }
/*
* If we have a copy statement, use it. As of V1.3, these
@@ -595,6 +607,9 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te,
AH->writingCopyData = false;
+ if (need_reset)
+ ahprintf(AH, "RESET ignore_security_label_input;\n\n");
+
/* close out the transaction started above */
if (is_parallel && te->created)
CommitTransaction(AH);
@@ -712,7 +727,7 @@ ArchiveEntry(Archive *AHX,
const char *tag,
const char *namespace,
const char *tablespace,
- const char *owner, bool withOids,
+ const char *owner, bool withOids, bool withSecids,
const char *desc, teSection section,
const char *defn,
const char *dropStmt, const char *copyStmt,
@@ -744,6 +759,7 @@ ArchiveEntry(Archive *AHX,
newToc->tablespace = tablespace ? strdup(tablespace) : NULL;
newToc->owner = strdup(owner);
newToc->withOids = withOids;
+ newToc->withSecids = withSecids;
newToc->desc = strdup(desc);
newToc->defn = strdup(defn);
newToc->dropStmt = strdup(dropStmt);
@@ -2027,6 +2043,7 @@ WriteToc(ArchiveHandle *AH)
WriteStr(AH, te->tablespace);
WriteStr(AH, te->owner);
WriteStr(AH, te->withOids ? "true" : "false");
+ WriteStr(AH, te->withSecids ? "true" : "false");
/* Dump list of dependencies */
for (i = 0; i < te->nDeps; i++)
@@ -2138,6 +2155,16 @@ ReadToc(ArchiveHandle *AH)
else
te->withOids = true;
+ if (AH->version >= K_VERS_1_13)
+ {
+ if (strcmp(ReadStr(AH), "true") == 0)
+ te->withSecids = true;
+ else
+ te->withSecids = false;
+ }
+ else
+ te->withSecids = false;
+
/* Read TOC entry dependencies */
if (AH->version >= K_VERS_1_5)
{
@@ -2256,6 +2283,9 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
if ((!include_acls || ropt->aclsSkip) && _tocEntryIsACL(te))
return 0;
+ if (ropt->noSecLabel && strcmp(te->desc, "LABEL") == 0)
+ return 0;
+
/* Ignore DATABASE entry unless we should create it */
if (!ropt->create && strcmp(te->desc, "DATABASE") == 0)
return 0;
@@ -2322,6 +2352,8 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls)
(strcmp(te->desc, "ACL") == 0 &&
strncmp(te->tag, "LARGE OBJECT ", 13) == 0) ||
(strcmp(te->desc, "COMMENT") == 0 &&
+ strncmp(te->tag, "LARGE OBJECT ", 13) == 0) ||
+ (strcmp(te->desc, "LABEL") == 0 &&
strncmp(te->tag, "LARGE OBJECT ", 13) == 0))
res = res & REQ_DATA;
else
@@ -2473,6 +2505,36 @@ _doSetWithOids(ArchiveHandle *AH, const bool withOids)
destroyPQExpBuffer(cmd);
}
+/*
+ * Issue a SET default_with_secids command. Caller is responsible
+ * for updating state if appropriate.
+ */
+static void
+_doSetWithSecids(ArchiveHandle *AH, const bool withSecids)
+{
+ PQExpBuffer cmd = createPQExpBuffer();
+
+ appendPQExpBuffer(cmd, "SET default_with_secids = %s;", withSecids ?
+ "true" : "false");
+
+ if (RestoringToDB(AH))
+ {
+ PGresult *res;
+
+ res = PQexec(AH->connection, cmd->data);
+
+ if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
+ warn_or_die_horribly(AH, modulename,
+ "could not set default_with_secids: %s",
+ PQerrorMessage(AH->connection));
+
+ PQclear(res);
+ }
+ else
+ ahprintf(AH, "%s\n\n", cmd->data);
+
+ destroyPQExpBuffer(cmd);
+}
/*
* Issue the commands to connect to the specified database.
@@ -2571,6 +2633,18 @@ _setWithOids(ArchiveHandle *AH, TocEntry *te)
}
}
+/*
+ * Set the proper default_with_secids value for the table.
+ */
+static void
+_setWithSecids(ArchiveHandle *AH, TocEntry *te)
+{
+ if (AH->currWithSecids != te->withSecids)
+ {
+ _doSetWithSecids(AH, te->withSecids);
+ AH->currWithSecids = te->withSecids;
+ }
+}
/*
* Issue the commands to select the specified schema as the current schema
@@ -2808,9 +2882,12 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat
_selectOutputSchema(AH, te->namespace);
_selectTablespace(AH, te->tablespace);
- /* Set up OID mode too */
+ /* Set up OID/SECID mode too */
if (strcmp(te->desc, "TABLE") == 0)
+ {
_setWithOids(AH, te);
+ _setWithSecids(AH, te);
+ }
/* Emit header comment for item */
if (!AH->noTocComments)
diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h
index 2e944c1..67f62ac 100644
--- a/src/bin/pg_dump/pg_backup_archiver.h
+++ b/src/bin/pg_dump/pg_backup_archiver.h
@@ -63,7 +63,7 @@ typedef z_stream *z_streamp;
/* Current archive version number (the format we can output) */
#define K_VERS_MAJOR 1
-#define K_VERS_MINOR 12
+#define K_VERS_MINOR 13
#define K_VERS_REV 0
/* Data block types */
@@ -89,9 +89,10 @@ typedef z_stream *z_streamp;
* indicator */
#define K_VERS_1_12 (( (1 * 256 + 12) * 256 + 0) * 256 + 0) /* add separate BLOB
* entries */
-
+#define K_VERS_1_13 (( (1 * 256 + 13) * 256 + 0) * 256 + 0) /* add security label
+ * support */
/* Newest format we can read */
-#define K_VERS_MAX (( (1 * 256 + 12) * 256 + 255) * 256 + 0)
+#define K_VERS_MAX (( (1 * 256 + 13) * 256 + 255) * 256 + 0)
/* Flags to indicate disposition of offsets stored in files */
@@ -278,6 +279,7 @@ typedef struct _archiveHandle
char *currSchema; /* current schema, or NULL */
char *currTablespace; /* current tablespace, or NULL */
bool currWithOids; /* current default_with_oids setting */
+ bool currWithSecids; /* current default_with_secids setting */
void *lo_buf;
size_t lo_buf_used;
@@ -305,6 +307,7 @@ typedef struct _tocEntry
* means use database default */
char *owner;
bool withOids; /* Used only by "TABLE" tags */
+ bool withSecids; /* Used only by "TABLE" tags */
char *desc;
char *defn;
char *dropStmt;
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 2431d71..e7ef296 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -125,7 +125,7 @@ static int binary_upgrade = 0;
static int disable_dollar_quoting = 0;
static int dump_inserts = 0;
static int column_inserts = 0;
-
+static int security_label = 0;
static void help(const char *progname);
static void expand_schema_name_patterns(SimpleStringList *patterns,
@@ -183,6 +183,11 @@ static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
const char *tag, const char *nspname, const char *owner,
const char *acls);
+static void dumpSecLabel(Archive *fout, DumpId objDumpId,
+ const char *target,
+ const char *namespace,
+ const char *seclabel);
+
static void getDependencies(void);
static void getDomainConstraints(TypeInfo *tyinfo);
static void getTableData(TableInfo *tblinfo, int numTables, bool oids);
@@ -215,7 +220,7 @@ static bool binary_upgrade_set_type_oids_by_rel_oid(
static void binary_upgrade_set_relfilenodes(PQExpBuffer upgrade_buffer,
Oid pg_class_oid, bool is_index);
static const char *getAttrName(int attrnum, TableInfo *tblInfo);
-static const char *fmtCopyColumnList(const TableInfo *ti);
+static const char *fmtCopyColumnList(const TableInfo *ti, bool secids);
static void do_sql_command(PGconn *conn, const char *query);
static void check_sql_result(PGresult *res, PGconn *conn, const char *query,
ExecStatusType expected);
@@ -299,6 +304,7 @@ main(int argc, char **argv)
{"no-tablespaces", no_argument, &outputNoTablespaces, 1},
{"role", required_argument, NULL, 3},
{"use-set-session-authorization", no_argument, &use_setsessauth, 1},
+ {"security-label", no_argument, &security_label, 1},
{NULL, 0, NULL, 0}
};
@@ -447,6 +453,8 @@ main(int argc, char **argv)
outputNoTablespaces = 1;
else if (strcmp(optarg, "use-set-session-authorization") == 0)
use_setsessauth = 1;
+ else if (strcmp(optarg, "security-label") == 0)
+ security_label = 1;
else
{
fprintf(stderr,
@@ -515,6 +523,10 @@ main(int argc, char **argv)
exit(1);
}
+ /* Force column insertion mode, when --security-label mode is given. */
+ if (security_label && dump_inserts)
+ column_inserts = 1;
+
/* open the output file */
if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0)
{
@@ -833,6 +845,7 @@ help(const char *progname)
printf(_(" --use-set-session-authorization\n"
" use SET SESSION AUTHORIZATION commands instead of\n"
" ALTER OWNER commands to set ownership\n"));
+ printf(_(" --security-label dump schema/data with security label\n"));
printf(_("\nConnection options:\n"));
printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
@@ -1127,7 +1140,7 @@ dumpTableData_copy(Archive *fout, void *dcontext)
* cases involving ADD COLUMN and inheritance.)
*/
if (g_fout->remoteVersion >= 70300)
- column_list = fmtCopyColumnList(tbinfo);
+ column_list = fmtCopyColumnList(tbinfo, tdinfo->secids);
else
column_list = ""; /* can't select columns in COPY */
@@ -1251,14 +1264,16 @@ dumpTableData_insert(Archive *fout, void *dcontext)
if (fout->remoteVersion >= 70100)
{
appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR "
- "SELECT * FROM ONLY %s",
+ "SELECT %s* FROM ONLY %s",
+ (tdinfo->secids ? "security_label," : ""),
fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
classname));
}
else
{
appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR "
- "SELECT * FROM %s",
+ "SELECT %s* FROM %s",
+ (tdinfo->secids ? "security_label," : ""),
fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
classname));
}
@@ -1398,7 +1413,7 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo)
appendPQExpBuffer(copyBuf, "COPY %s ",
fmtId(tbinfo->dobj.name));
appendPQExpBuffer(copyBuf, "%s %sFROM stdin;\n",
- fmtCopyColumnList(tbinfo),
+ fmtCopyColumnList(tbinfo, tdinfo->secids),
(tdinfo->oids && tbinfo->hasoids) ? "WITH OIDS " : "");
copyStmt = copyBuf->data;
}
@@ -1412,7 +1427,7 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo)
ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId,
tbinfo->dobj.name, tbinfo->dobj.namespace->dobj.name,
NULL, tbinfo->rolname,
- false, "TABLE DATA", SECTION_DATA,
+ false, tdinfo->secids, "TABLE DATA", SECTION_DATA,
"", "", copyStmt,
tdinfo->dobj.dependencies, tdinfo->dobj.nDeps,
dumpFn, tdinfo);
@@ -1457,6 +1472,7 @@ getTableData(TableInfo *tblinfo, int numTables, bool oids)
tdinfo->dobj.namespace = tblinfo[i].dobj.namespace;
tdinfo->tdtable = &(tblinfo[i]);
tdinfo->oids = oids;
+ tdinfo->secids = (security_label && tblinfo[i].hassecids) ? true : false;
addObjectDependency(&tdinfo->dobj, tblinfo[i].dobj.dumpId);
tblinfo[i].dataObj = tdinfo;
@@ -1780,6 +1796,7 @@ dumpDatabase(Archive *AH)
NULL, /* Tablespace */
dba, /* Owner */
false, /* with oids */
+ false, /* with secids */
"DATABASE", /* Desc */
SECTION_PRE_DATA, /* Section */
creaQry->data, /* Create */
@@ -1825,7 +1842,7 @@ dumpDatabase(Archive *AH)
LargeObjectRelationId);
ArchiveEntry(AH, nilCatalogId, createDumpId(),
"pg_largeobject", NULL, NULL, "",
- false, "pg_largeobject", SECTION_PRE_DATA,
+ false, false, "pg_largeobject", SECTION_PRE_DATA,
loOutQry->data, "", NULL,
NULL, 0,
NULL, NULL);
@@ -1857,7 +1874,7 @@ dumpDatabase(Archive *AH)
appendPQExpBuffer(dbQry, ";\n");
ArchiveEntry(AH, dbCatId, createDumpId(), datname, NULL, NULL,
- dba, false, "COMMENT", SECTION_NONE,
+ dba, false, false, "COMMENT", SECTION_NONE,
dbQry->data, "", NULL,
&dbDumpId, 1, NULL, NULL);
}
@@ -1896,7 +1913,7 @@ dumpEncoding(Archive *AH)
ArchiveEntry(AH, nilCatalogId, createDumpId(),
"ENCODING", NULL, NULL, "",
- false, "ENCODING", SECTION_PRE_DATA,
+ false, false, "ENCODING", SECTION_PRE_DATA,
qry->data, "", NULL,
NULL, 0,
NULL, NULL);
@@ -1923,7 +1940,7 @@ dumpStdStrings(Archive *AH)
ArchiveEntry(AH, nilCatalogId, createDumpId(),
"STDSTRINGS", NULL, NULL, "",
- false, "STDSTRINGS", SECTION_PRE_DATA,
+ false, false, "STDSTRINGS", SECTION_PRE_DATA,
qry->data, "", NULL,
NULL, 0,
NULL, NULL);
@@ -1956,16 +1973,17 @@ getBlobs(Archive *AH)
/* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */
if (AH->remoteVersion >= 90000)
appendPQExpBuffer(blobQry,
- "SELECT oid, (%s lomowner) AS rolname, lomacl"
+ "SELECT oid, (%s lomowner) AS rolname, lomacl, %s"
" FROM pg_largeobject_metadata",
- username_subquery);
+ username_subquery,
+ security_label ? "security_label" : "NULL");
else if (AH->remoteVersion >= 70100)
appendPQExpBuffer(blobQry,
- "SELECT DISTINCT loid, NULL::oid, NULL::oid"
+ "SELECT DISTINCT loid, NULL::oid, NULL::oid, NULL::text"
" FROM pg_largeobject");
else
appendPQExpBuffer(blobQry,
- "SELECT oid, NULL::oid, NULL::oid"
+ "SELECT oid, NULL::oid, NULL::oid, NULL::text"
" FROM pg_class WHERE relkind = 'l'");
res = PQexec(g_conn, blobQry->data);
@@ -1995,6 +2013,11 @@ getBlobs(Archive *AH)
binfo[i].blobacl = strdup(PQgetvalue(res, i, 2));
else
binfo[i].blobacl = NULL;
+
+ if (!PQgetisnull(res, i, 3))
+ binfo[i].seclabel = strdup(PQgetvalue(res, i, 3));
+ else
+ binfo[i].seclabel = NULL;
}
/*
@@ -2034,7 +2057,7 @@ dumpBlob(Archive *AH, BlobInfo *binfo)
ArchiveEntry(AH, binfo->dobj.catId, binfo->dobj.dumpId,
binfo->dobj.name,
NULL, NULL,
- binfo->rolname, false,
+ binfo->rolname, false, false,
"BLOB", SECTION_PRE_DATA,
cquery->data, dquery->data, NULL,
binfo->dobj.dependencies, binfo->dobj.nDeps,
@@ -2049,6 +2072,10 @@ dumpBlob(Archive *AH, BlobInfo *binfo)
NULL, binfo->rolname,
binfo->dobj.catId, 0, binfo->dobj.dumpId);
+ /* Dump security label if any */
+ dumpSecLabel(AH, binfo->dobj.dumpId,
+ cquery->data, NULL, binfo->seclabel);
+
/* Dump ACL if any */
if (binfo->blobacl)
dumpACL(AH, binfo->dobj.catId, binfo->dobj.dumpId, "LARGE OBJECT",
@@ -2356,6 +2383,7 @@ getNamespaces(int *numNamespaces)
int i_nspname;
int i_rolname;
int i_nspacl;
+ int i_seclabel;
/*
* Before 7.3, there are no real namespaces; create two dummy entries, one
@@ -2372,6 +2400,7 @@ getNamespaces(int *numNamespaces)
nsinfo[0].dobj.name = strdup("public");
nsinfo[0].rolname = strdup("");
nsinfo[0].nspacl = strdup("");
+ nsinfo[0].seclabel = strdup("");
selectDumpableNamespace(&nsinfo[0]);
@@ -2382,6 +2411,7 @@ getNamespaces(int *numNamespaces)
nsinfo[1].dobj.name = strdup("pg_catalog");
nsinfo[1].rolname = strdup("");
nsinfo[1].nspacl = strdup("");
+ nsinfo[1].seclabel = strdup("");
selectDumpableNamespace(&nsinfo[1]);
@@ -2402,8 +2432,8 @@ getNamespaces(int *numNamespaces)
*/
appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, "
"(%s nspowner) AS rolname, "
- "nspacl FROM pg_namespace",
- username_subquery);
+ "nspacl, %s AS seclabel FROM pg_namespace",
+ username_subquery, security_label ? "security_label" : "NULL");
res = PQexec(g_conn, query->data);
check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
@@ -2417,6 +2447,7 @@ getNamespaces(int *numNamespaces)
i_nspname = PQfnumber(res, "nspname");
i_rolname = PQfnumber(res, "rolname");
i_nspacl = PQfnumber(res, "nspacl");
+ i_seclabel = PQfnumber(res, "seclabel");
for (i = 0; i < ntups; i++)
{
@@ -2427,6 +2458,7 @@ getNamespaces(int *numNamespaces)
nsinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_nspname));
nsinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname));
nsinfo[i].nspacl = strdup(PQgetvalue(res, i, i_nspacl));
+ nsinfo[i].seclabel = strdup(PQgetvalue(res, i, i_seclabel));
/* Decide whether to dump this namespace */
selectDumpableNamespace(&nsinfo[i]);
@@ -2515,6 +2547,7 @@ getTypes(int *numTypes)
int i_typtype;
int i_typisdefined;
int i_isarray;
+ int i_seclabel;
/*
* we include even the built-in types because those may be used as array
@@ -2547,8 +2580,10 @@ getTypes(int *numTypes)
"typtype, typisdefined, "
"typname[0] = '_' AND typelem != 0 AND "
"(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray "
+ ",%s AS seclabel "
"FROM pg_type",
- username_subquery);
+ username_subquery,
+ security_label ? "security_label" : "NULL");
}
else if (g_fout->remoteVersion >= 70300)
{
@@ -2561,6 +2596,7 @@ getTypes(int *numTypes)
"ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
"typtype, typisdefined, "
"typname[0] = '_' AND typelem != 0 AS isarray "
+ ",NULL AS seclabel "
"FROM pg_type",
username_subquery);
}
@@ -2575,6 +2611,7 @@ getTypes(int *numTypes)
"ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
"typtype, typisdefined, "
"typname[0] = '_' AND typelem != 0 AS isarray "
+ ",NULL AS seclabel "
"FROM pg_type",
username_subquery);
}
@@ -2591,6 +2628,7 @@ getTypes(int *numTypes)
"ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, "
"typtype, typisdefined, "
"typname[0] = '_' AND typelem != 0 AS isarray "
+ ",NULL AS seclabel "
"FROM pg_type",
username_subquery);
}
@@ -2615,6 +2653,7 @@ getTypes(int *numTypes)
i_typtype = PQfnumber(res, "typtype");
i_typisdefined = PQfnumber(res, "typisdefined");
i_isarray = PQfnumber(res, "isarray");
+ i_seclabel = PQfnumber(res, "seclabel");
for (i = 0; i < ntups; i++)
{
@@ -2642,6 +2681,8 @@ getTypes(int *numTypes)
else
tyinfo[i].isArray = false;
+ tyinfo[i].seclabel = strdup(PQgetvalue(res, i, i_seclabel));
+
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i]);
@@ -3407,6 +3448,7 @@ getTables(int *numTables)
int i_relhasindex;
int i_relhasrules;
int i_relhasoids;
+ int i_relhassecids;
int i_relfrozenxid;
int i_owning_tab;
int i_owning_col;
@@ -3414,6 +3456,7 @@ getTables(int *numTables)
int i_reloptions;
int i_toastreloptions;
int i_reloftype;
+ int i_seclabel;
/* Make sure we are in proper schema */
selectSourceSchema("pg_catalog");
@@ -3450,6 +3493,7 @@ getTables(int *numTables)
"(%s c.relowner) AS rolname, "
"c.relchecks, c.relhastriggers, "
"c.relhasindex, c.relhasrules, c.relhasoids, "
+ "c.relhassecids, "
"c.relfrozenxid, "
"CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, "
"d.refobjid AS owning_tab, "
@@ -3457,6 +3501,7 @@ getTables(int *numTables)
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"array_to_string(c.reloptions, ', ') AS reloptions, "
"array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
+ ",%s AS seclabel "
"FROM pg_class c "
"LEFT JOIN pg_depend d ON "
"(c.relkind = '%c' AND "
@@ -3467,6 +3512,7 @@ getTables(int *numTables)
"WHERE c.relkind in ('%c', '%c', '%c', '%c') "
"ORDER BY c.oid",
username_subquery,
+ security_label ? "c.security_label" : "NULL",
RELKIND_SEQUENCE,
RELKIND_RELATION, RELKIND_SEQUENCE,
RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
@@ -3483,6 +3529,7 @@ getTables(int *numTables)
"(%s c.relowner) AS rolname, "
"c.relchecks, c.relhastriggers, "
"c.relhasindex, c.relhasrules, c.relhasoids, "
+ "false AS relhasecids, "
"c.relfrozenxid, "
"NULL AS reloftype, "
"d.refobjid AS owning_tab, "
@@ -3490,6 +3537,7 @@ getTables(int *numTables)
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"array_to_string(c.reloptions, ', ') AS reloptions, "
"array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions "
+ ",%s AS seclabel "
"FROM pg_class c "
"LEFT JOIN pg_depend d ON "
"(c.relkind = '%c' AND "
@@ -3500,6 +3548,7 @@ getTables(int *numTables)
"WHERE c.relkind in ('%c', '%c', '%c', '%c') "
"ORDER BY c.oid",
username_subquery,
+ security_label ? "security_label" : "NULL",
RELKIND_SEQUENCE,
RELKIND_RELATION, RELKIND_SEQUENCE,
RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
@@ -3516,6 +3565,7 @@ getTables(int *numTables)
"(%s relowner) AS rolname, "
"relchecks, (reltriggers <> 0) AS relhastriggers, "
"relhasindex, relhasrules, relhasoids, "
+ "false AS relhassecids, "
"relfrozenxid, "
"NULL AS reloftype, "
"d.refobjid AS owning_tab, "
@@ -3523,6 +3573,7 @@ getTables(int *numTables)
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"array_to_string(c.reloptions, ', ') AS reloptions, "
"NULL AS toast_reloptions "
+ ",NULL AS seclabel "
"FROM pg_class c "
"LEFT JOIN pg_depend d ON "
"(c.relkind = '%c' AND "
@@ -3548,6 +3599,7 @@ getTables(int *numTables)
"(%s relowner) AS rolname, "
"relchecks, (reltriggers <> 0) AS relhastriggers, "
"relhasindex, relhasrules, relhasoids, "
+ "false AS relhassecids, "
"0 AS relfrozenxid, "
"NULL AS reloftype, "
"d.refobjid AS owning_tab, "
@@ -3555,6 +3607,7 @@ getTables(int *numTables)
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
"NULL AS reloptions, "
"NULL AS toast_reloptions "
+ ",NULL AS seclabel "
"FROM pg_class c "
"LEFT JOIN pg_depend d ON "
"(c.relkind = '%c' AND "
@@ -3580,6 +3633,7 @@ getTables(int *numTables)
"(%s relowner) AS rolname, "
"relchecks, (reltriggers <> 0) AS relhastriggers, "
"relhasindex, relhasrules, relhasoids, "
+ "false AS relhassecids, "
"0 AS relfrozenxid, "
"NULL AS reloftype, "
"d.refobjid AS owning_tab, "
@@ -3587,6 +3641,7 @@ getTables(int *numTables)
"NULL AS reltablespace, "
"NULL AS reloptions, "
"NULL AS toast_reloptions "
+ ",NULL AS seclabel "
"FROM pg_class c "
"LEFT JOIN pg_depend d ON "
"(c.relkind = '%c' AND "
@@ -3608,6 +3663,7 @@ getTables(int *numTables)
"(%s relowner) AS rolname, "
"relchecks, (reltriggers <> 0) AS relhastriggers, "
"relhasindex, relhasrules, relhasoids, "
+ "false AS relhassecids, "
"0 AS relfrozenxid, "
"NULL AS reloftype, "
"NULL::oid AS owning_tab, "
@@ -3615,6 +3671,7 @@ getTables(int *numTables)
"NULL AS reltablespace, "
"NULL AS reloptions, "
"NULL AS toast_reloptions "
+ ",NULL AS seclabel "
"FROM pg_class "
"WHERE relkind IN ('%c', '%c', '%c') "
"ORDER BY oid",
@@ -3631,6 +3688,7 @@ getTables(int *numTables)
"relchecks, (reltriggers <> 0) AS relhastriggers, "
"relhasindex, relhasrules, "
"'t'::bool AS relhasoids, "
+ "'f'::bool AS relhassecids, "
"0 AS relfrozenxid, "
"NULL AS reloftype, "
"NULL::oid AS owning_tab, "
@@ -3638,6 +3696,7 @@ getTables(int *numTables)
"NULL AS reltablespace, "
"NULL AS reloptions, "
"NULL AS toast_reloptions "
+ ",NULL AS seclabel "
"FROM pg_class "
"WHERE relkind IN ('%c', '%c', '%c') "
"ORDER BY oid",
@@ -3671,6 +3730,7 @@ getTables(int *numTables)
"NULL AS reltablespace, "
"NULL AS reloptions, "
"NULL AS toast_reloptions "
+ ",NULL AS seclabel "
"FROM pg_class c "
"WHERE relkind IN ('%c', '%c') "
"ORDER BY oid",
@@ -3709,6 +3769,7 @@ getTables(int *numTables)
i_relhasindex = PQfnumber(res, "relhasindex");
i_relhasrules = PQfnumber(res, "relhasrules");
i_relhasoids = PQfnumber(res, "relhasoids");
+ i_relhassecids = PQfnumber(res, "relhassecids");
i_relfrozenxid = PQfnumber(res, "relfrozenxid");
i_owning_tab = PQfnumber(res, "owning_tab");
i_owning_col = PQfnumber(res, "owning_col");
@@ -3716,6 +3777,7 @@ getTables(int *numTables)
i_reloptions = PQfnumber(res, "reloptions");
i_toastreloptions = PQfnumber(res, "toast_reloptions");
i_reloftype = PQfnumber(res, "reloftype");
+ i_seclabel = PQfnumber(res, "seclabel");
if (lockWaitTimeout && g_fout->remoteVersion >= 70300)
{
@@ -3748,6 +3810,7 @@ getTables(int *numTables)
tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0);
tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0);
tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0);
+ tblinfo[i].hassecids = (strcmp(PQgetvalue(res, i, i_relhassecids), "t")==0);
tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
if (PQgetisnull(res, i, i_reloftype))
tblinfo[i].reloftype = NULL;
@@ -3767,6 +3830,7 @@ getTables(int *numTables)
tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace));
tblinfo[i].reloptions = strdup(PQgetvalue(res, i, i_reloptions));
tblinfo[i].toast_reloptions = strdup(PQgetvalue(res, i, i_toastreloptions));
+ tblinfo[i].rellabel = strdup(PQgetvalue(res, i, i_seclabel));
/* other fields were zeroed above */
@@ -5082,6 +5146,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
int i_attalign;
int i_attislocal;
int i_attoptions;
+ int i_seclabel;
PGresult *res;
int ntups;
bool hasdefaults;
@@ -5128,11 +5193,13 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"a.attlen, a.attalign, a.attislocal, "
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"array_to_string(attoptions, ', ') AS attoptions "
+ ",%s AS seclabel "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
"AND a.attnum > 0::pg_catalog.int2 "
"ORDER BY a.attrelid, a.attnum",
+ security_label ? "a.security_label" : "NULL",
tbinfo->dobj.catId.oid);
}
else if (g_fout->remoteVersion >= 70300)
@@ -5144,6 +5211,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"a.attlen, a.attalign, a.attislocal, "
"pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
"'' AS attoptions "
+ ",NULL AS seclabel "
"FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::pg_catalog.oid "
@@ -5165,6 +5233,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"a.attalign, false AS attislocal, "
"format_type(t.oid,a.atttypmod) AS atttypname, "
"'' AS attoptions "
+ ",NULL AS seclabel "
"FROM pg_attribute a LEFT JOIN pg_type t "
"ON a.atttypid = t.oid "
"WHERE a.attrelid = '%u'::oid "
@@ -5183,6 +5252,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
"false AS attislocal, "
"(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname, "
"'' AS attoptions "
+ ",NULL AS seclabel "
"FROM pg_attribute a "
"WHERE attrelid = '%u'::oid "
"AND attnum > 0::int2 "
@@ -5209,6 +5279,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
i_attalign = PQfnumber(res, "attalign");
i_attislocal = PQfnumber(res, "attislocal");
i_attoptions = PQfnumber(res, "attoptions");
+ i_seclabel = PQfnumber(res, "seclabel");
tbinfo->numatts = ntups;
tbinfo->attnames = (char **) malloc(ntups * sizeof(char *));
@@ -5227,6 +5298,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool));
tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool));
tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool));
+ tbinfo->attlabels = (char **) malloc(ntups * sizeof(char *));
hasdefaults = false;
for (j = 0; j < ntups; j++)
@@ -5256,6 +5328,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
tbinfo->inhAttrs[j] = false;
tbinfo->inhAttrDef[j] = false;
tbinfo->inhNotNull[j] = false;
+ tbinfo->attlabels[j] = strdup(PQgetvalue(res, j, i_seclabel));
}
PQclear(res);
@@ -6146,7 +6219,7 @@ dumpComment(Archive *fout, const char *target,
*/
ArchiveEntry(fout, nilCatalogId, createDumpId(),
target, namespace, NULL, owner,
- false, "COMMENT", SECTION_NONE,
+ false, false, "COMMENT", SECTION_NONE,
query->data, "", NULL,
&(dumpId), 1,
NULL, NULL);
@@ -6207,7 +6280,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo,
target->data,
tbinfo->dobj.namespace->dobj.name,
NULL, tbinfo->rolname,
- false, "COMMENT", SECTION_NONE,
+ false, false, "COMMENT", SECTION_NONE,
query->data, "", NULL,
&(tbinfo->dobj.dumpId), 1,
NULL, NULL);
@@ -6229,7 +6302,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo,
target->data,
tbinfo->dobj.namespace->dobj.name,
NULL, tbinfo->rolname,
- false, "COMMENT", SECTION_NONE,
+ false, false, "COMMENT", SECTION_NONE,
query->data, "", NULL,
&(tbinfo->dobj.dumpId), 1,
NULL, NULL);
@@ -6509,7 +6582,7 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj)
case DO_BLOB_DATA:
ArchiveEntry(fout, dobj->catId, dobj->dumpId,
dobj->name, NULL, NULL, "",
- false, "BLOBS", SECTION_DATA,
+ false, false, "BLOBS", SECTION_DATA,
"", "", NULL,
dobj->dependencies, dobj->nDeps,
dumpBlobs, NULL);
@@ -6549,7 +6622,7 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo)
nspinfo->dobj.name,
NULL, NULL,
nspinfo->rolname,
- false, "SCHEMA", SECTION_PRE_DATA,
+ false, false, "SCHEMA", SECTION_PRE_DATA,
q->data, delq->data, NULL,
nspinfo->dobj.dependencies, nspinfo->dobj.nDeps,
NULL, NULL);
@@ -6561,6 +6634,9 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo)
NULL, nspinfo->rolname,
nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId);
+ dumpSecLabel(fout, nspinfo->dobj.dumpId,
+ q->data, NULL, nspinfo->seclabel);
+
dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA",
qnspname, NULL, nspinfo->dobj.name, NULL,
nspinfo->rolname, nspinfo->nspacl);
@@ -6678,7 +6754,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
tyinfo->dobj.name,
tyinfo->dobj.namespace->dobj.name,
NULL,
- tyinfo->rolname, false,
+ tyinfo->rolname, false, false,
"TYPE", SECTION_PRE_DATA,
q->data, delq->data, NULL,
tyinfo->dobj.dependencies, tyinfo->dobj.nDeps,
@@ -6692,6 +6768,12 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+ /* Dump type security label */
+ dumpSecLabel(fout, tyinfo->dobj.dumpId,
+ q->data,
+ tyinfo->dobj.namespace->dobj.name,
+ tyinfo->seclabel);
+
PQclear(res);
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
@@ -7054,7 +7136,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
tyinfo->dobj.name,
tyinfo->dobj.namespace->dobj.name,
NULL,
- tyinfo->rolname, false,
+ tyinfo->rolname, false, false,
"TYPE", SECTION_PRE_DATA,
q->data, delq->data, NULL,
tyinfo->dobj.dependencies, tyinfo->dobj.nDeps,
@@ -7068,6 +7150,12 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+ /* Dump type security label */
+ dumpSecLabel(fout, tyinfo->dobj.dumpId,
+ q->data,
+ tyinfo->dobj.namespace->dobj.name,
+ tyinfo->seclabel);
+
PQclear(res);
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
@@ -7178,7 +7266,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
tyinfo->dobj.name,
tyinfo->dobj.namespace->dobj.name,
NULL,
- tyinfo->rolname, false,
+ tyinfo->rolname, false, false,
"DOMAIN", SECTION_PRE_DATA,
q->data, delq->data, NULL,
tyinfo->dobj.dependencies, tyinfo->dobj.nDeps,
@@ -7192,6 +7280,12 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+ /* Dump domain security label */
+ dumpSecLabel(fout, tyinfo->dobj.dumpId,
+ q->data,
+ tyinfo->dobj.namespace->dobj.name,
+ tyinfo->seclabel);
+
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
destroyPQExpBuffer(query);
@@ -7283,7 +7377,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
tyinfo->dobj.name,
tyinfo->dobj.namespace->dobj.name,
NULL,
- tyinfo->rolname, false,
+ tyinfo->rolname, false, false,
"TYPE", SECTION_PRE_DATA,
q->data, delq->data, NULL,
tyinfo->dobj.dependencies, tyinfo->dobj.nDeps,
@@ -7298,6 +7392,12 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
tyinfo->dobj.namespace->dobj.name, tyinfo->rolname,
tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+ /* Dump type security label */
+ dumpSecLabel(fout, tyinfo->dobj.dumpId,
+ q->data,
+ tyinfo->dobj.namespace->dobj.name,
+ tyinfo->seclabel);
+
PQclear(res);
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
@@ -7402,7 +7502,7 @@ dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo)
target->data,
tyinfo->dobj.namespace->dobj.name,
NULL, tyinfo->rolname,
- false, "COMMENT", SECTION_NONE,
+ false, false, "COMMENT", SECTION_NONE,
query->data, "", NULL,
&(tyinfo->dobj.dumpId), 1,
NULL, NULL);
@@ -7454,7 +7554,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
stinfo->dobj.name,
stinfo->dobj.namespace->dobj.name,
NULL,
- stinfo->baseType->rolname, false,
+ stinfo->baseType->rolname, false, false,
"SHELL TYPE", SECTION_PRE_DATA,
q->data, "", NULL,
stinfo->dobj.dependencies, stinfo->dobj.nDeps,
@@ -7609,7 +7709,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang)
ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId,
plang->dobj.name,
lanschema, NULL, plang->lanowner,
- false, "PROCEDURAL LANGUAGE", SECTION_PRE_DATA,
+ false, false, "PROCEDURAL LANGUAGE", SECTION_PRE_DATA,
defqry->data, delqry->data, NULL,
plang->dobj.dependencies, plang->dobj.nDeps,
NULL, NULL);
@@ -7795,6 +7895,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
char **argmodes = NULL;
char **argnames = NULL;
char **configitems = NULL;
+ char *proseclabel;
int nconfigitems = 0;
int i;
@@ -7825,8 +7926,10 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
"proiswindow, provolatile, proisstrict, prosecdef, "
"proconfig, procost, prorows, "
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
+ ",%s AS seclabel "
"FROM pg_catalog.pg_proc "
"WHERE oid = '%u'::pg_catalog.oid",
+ security_label ? "security_label" : "NULL",
finfo->dobj.catId.oid);
}
else if (g_fout->remoteVersion >= 80300)
@@ -7838,6 +7941,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
"provolatile, proisstrict, prosecdef, "
"proconfig, procost, prorows, "
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
+ ",NULL AS seclabel "
"FROM pg_catalog.pg_proc "
"WHERE oid = '%u'::pg_catalog.oid",
finfo->dobj.catId.oid);
@@ -7851,6 +7955,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
"provolatile, proisstrict, prosecdef, "
"null AS proconfig, 0 AS procost, 0 AS prorows, "
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
+ ",NULL AS seclabel "
"FROM pg_catalog.pg_proc "
"WHERE oid = '%u'::pg_catalog.oid",
finfo->dobj.catId.oid);
@@ -7866,6 +7971,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
"provolatile, proisstrict, prosecdef, "
"null AS proconfig, 0 AS procost, 0 AS prorows, "
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
+ ",NULL AS seclabel "
"FROM pg_catalog.pg_proc "
"WHERE oid = '%u'::pg_catalog.oid",
finfo->dobj.catId.oid);
@@ -7881,6 +7987,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
"provolatile, proisstrict, prosecdef, "
"null AS proconfig, 0 AS procost, 0 AS prorows, "
"(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
+ ",NULL AS seclabel "
"FROM pg_catalog.pg_proc "
"WHERE oid = '%u'::pg_catalog.oid",
finfo->dobj.catId.oid);
@@ -7898,6 +8005,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
"false AS prosecdef, "
"null AS proconfig, 0 AS procost, 0 AS prorows, "
"(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname "
+ ",NULL AS seclabel "
"FROM pg_proc "
"WHERE oid = '%u'::oid",
finfo->dobj.catId.oid);
@@ -7915,6 +8023,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
"false AS prosecdef, "
"NULL AS proconfig, 0 AS procost, 0 AS prorows, "
"(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname "
+ ",NULL AS seclabel "
"FROM pg_proc "
"WHERE oid = '%u'::oid",
finfo->dobj.catId.oid);
@@ -7959,6 +8068,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
+ proseclabel = PQgetvalue(res, 0, PQfnumber(res, "seclabel"));
/*
* See backend/commands/functioncmds.c for details of how the 'AS' clause
@@ -8169,7 +8279,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
funcsig_tag,
finfo->dobj.namespace->dobj.name,
NULL,
- finfo->rolname, false,
+ finfo->rolname, false, false,
"FUNCTION", SECTION_PRE_DATA,
q->data, delqry->data, NULL,
finfo->dobj.dependencies, finfo->dobj.nDeps,
@@ -8182,6 +8292,12 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
finfo->dobj.namespace->dobj.name, finfo->rolname,
finfo->dobj.catId, 0, finfo->dobj.dumpId);
+ /* Dump Function security label */
+ dumpSecLabel(fout, finfo->dobj.dumpId,
+ q->data,
+ finfo->dobj.namespace->dobj.name,
+ proseclabel);
+
dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION",
funcsig, NULL, funcsig_tag,
finfo->dobj.namespace->dobj.name,
@@ -8323,7 +8439,7 @@ dumpCast(Archive *fout, CastInfo *cast)
ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId,
castsig->data,
"pg_catalog", NULL, "",
- false, "CAST", SECTION_PRE_DATA,
+ false, false, "CAST", SECTION_PRE_DATA,
defqry->data, delqry->data, NULL,
cast->dobj.dependencies, cast->dobj.nDeps,
NULL, NULL);
@@ -8567,7 +8683,7 @@ dumpOpr(Archive *fout, OprInfo *oprinfo)
oprinfo->dobj.namespace->dobj.name,
NULL,
oprinfo->rolname,
- false, "OPERATOR", SECTION_PRE_DATA,
+ false, false, "OPERATOR", SECTION_PRE_DATA,
q->data, delq->data, NULL,
oprinfo->dobj.dependencies, oprinfo->dobj.nDeps,
NULL, NULL);
@@ -9026,7 +9142,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo)
opcinfo->dobj.namespace->dobj.name,
NULL,
opcinfo->rolname,
- false, "OPERATOR CLASS", SECTION_PRE_DATA,
+ false, false, "OPERATOR CLASS", SECTION_PRE_DATA,
q->data, delq->data, NULL,
opcinfo->dobj.dependencies, opcinfo->dobj.nDeps,
NULL, NULL);
@@ -9307,7 +9423,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo)
opfinfo->dobj.namespace->dobj.name,
NULL,
opfinfo->rolname,
- false, "OPERATOR FAMILY", SECTION_PRE_DATA,
+ false, false, "OPERATOR FAMILY", SECTION_PRE_DATA,
q->data, delq->data, NULL,
opfinfo->dobj.dependencies, opfinfo->dobj.nDeps,
NULL, NULL);
@@ -9423,7 +9539,7 @@ dumpConversion(Archive *fout, ConvInfo *convinfo)
convinfo->dobj.namespace->dobj.name,
NULL,
convinfo->rolname,
- false, "CONVERSION", SECTION_PRE_DATA,
+ false, false, "CONVERSION", SECTION_PRE_DATA,
q->data, delq->data, NULL,
convinfo->dobj.dependencies, convinfo->dobj.nDeps,
NULL, NULL);
@@ -9504,11 +9620,13 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
int i_aggtranstype;
int i_agginitval;
int i_convertok;
+ int i_seclabel;
const char *aggtransfn;
const char *aggfinalfn;
const char *aggsortop;
const char *aggtranstype;
const char *agginitval;
+ const char *seclabel;
bool convertok;
/* Skip if not to be dumped */
@@ -9531,9 +9649,11 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
"aggsortop::pg_catalog.regoperator, "
"agginitval, "
"'t'::boolean AS convertok "
+ ",%s AS seclabel "
"FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
"WHERE a.aggfnoid = p.oid "
"AND p.oid = '%u'::pg_catalog.oid",
+ security_label ? "p.security_label" : "NULL",
agginfo->aggfn.dobj.catId.oid);
}
else if (g_fout->remoteVersion >= 70300)
@@ -9543,6 +9663,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
"0 AS aggsortop, "
"agginitval, "
"'t'::boolean AS convertok "
+ ",NULL AS seclabel "
"FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p "
"WHERE a.aggfnoid = p.oid "
"AND p.oid = '%u'::pg_catalog.oid",
@@ -9555,6 +9676,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
"0 AS aggsortop, "
"agginitval, "
"'t'::boolean AS convertok "
+ ",NULL AS seclabel "
"FROM pg_aggregate "
"WHERE oid = '%u'::oid",
agginfo->aggfn.dobj.catId.oid);
@@ -9567,6 +9689,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
"0 AS aggsortop, "
"agginitval1 AS agginitval, "
"(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) AS convertok "
+ ",NULL AS seclabel "
"FROM pg_aggregate "
"WHERE oid = '%u'::oid",
agginfo->aggfn.dobj.catId.oid);
@@ -9592,6 +9715,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
i_aggtranstype = PQfnumber(res, "aggtranstype");
i_agginitval = PQfnumber(res, "agginitval");
i_convertok = PQfnumber(res, "convertok");
+ i_seclabel = PQfnumber(res, "seclabel");
aggtransfn = PQgetvalue(res, 0, i_aggtransfn);
aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn);
@@ -9599,6 +9723,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
aggtranstype = PQgetvalue(res, 0, i_aggtranstype);
agginitval = PQgetvalue(res, 0, i_agginitval);
convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't');
+ seclabel = PQgetvalue(res, 0, i_seclabel);
aggsig = format_aggregate_signature(agginfo, fout, true);
aggsig_tag = format_aggregate_signature(agginfo, fout, false);
@@ -9667,7 +9792,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
agginfo->aggfn.dobj.namespace->dobj.name,
NULL,
agginfo->aggfn.rolname,
- false, "AGGREGATE", SECTION_PRE_DATA,
+ false, false, "AGGREGATE", SECTION_PRE_DATA,
q->data, delq->data, NULL,
agginfo->aggfn.dobj.dependencies, agginfo->aggfn.dobj.nDeps,
NULL, NULL);
@@ -9679,6 +9804,12 @@ dumpAgg(Archive *fout, AggInfo *agginfo)
agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname,
agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId);
+ /* Dump Aggregate security label */
+ dumpSecLabel(fout, agginfo->aggfn.dobj.dumpId,
+ q->data,
+ agginfo->aggfn.dobj.namespace->dobj.name,
+ seclabel);
+
/*
* Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL
* command look like a function's GRANT; in particular this affects the
@@ -9755,7 +9886,7 @@ dumpTSParser(Archive *fout, TSParserInfo *prsinfo)
prsinfo->dobj.namespace->dobj.name,
NULL,
"",
- false, "TEXT SEARCH PARSER", SECTION_PRE_DATA,
+ false, false, "TEXT SEARCH PARSER", SECTION_PRE_DATA,
q->data, delq->data, NULL,
prsinfo->dobj.dependencies, prsinfo->dobj.nDeps,
NULL, NULL);
@@ -9847,7 +9978,7 @@ dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo)
dictinfo->dobj.namespace->dobj.name,
NULL,
dictinfo->rolname,
- false, "TEXT SEARCH DICTIONARY", SECTION_PRE_DATA,
+ false, false, "TEXT SEARCH DICTIONARY", SECTION_PRE_DATA,
q->data, delq->data, NULL,
dictinfo->dobj.dependencies, dictinfo->dobj.nDeps,
NULL, NULL);
@@ -9907,7 +10038,7 @@ dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo)
tmplinfo->dobj.namespace->dobj.name,
NULL,
"",
- false, "TEXT SEARCH TEMPLATE", SECTION_PRE_DATA,
+ false, false, "TEXT SEARCH TEMPLATE", SECTION_PRE_DATA,
q->data, delq->data, NULL,
tmplinfo->dobj.dependencies, tmplinfo->dobj.nDeps,
NULL, NULL);
@@ -10040,7 +10171,7 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo)
cfginfo->dobj.namespace->dobj.name,
NULL,
cfginfo->rolname,
- false, "TEXT SEARCH CONFIGURATION", SECTION_PRE_DATA,
+ false, false, "TEXT SEARCH CONFIGURATION", SECTION_PRE_DATA,
q->data, delq->data, NULL,
cfginfo->dobj.dependencies, cfginfo->dobj.nDeps,
NULL, NULL);
@@ -10096,7 +10227,7 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo)
NULL,
NULL,
fdwinfo->rolname,
- false, "FOREIGN DATA WRAPPER", SECTION_PRE_DATA,
+ false, false, "FOREIGN DATA WRAPPER", SECTION_PRE_DATA,
q->data, delq->data, NULL,
fdwinfo->dobj.dependencies, fdwinfo->dobj.nDeps,
NULL, NULL);
@@ -10183,7 +10314,7 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo)
NULL,
NULL,
srvinfo->rolname,
- false, "SERVER", SECTION_PRE_DATA,
+ false, false, "SERVER", SECTION_PRE_DATA,
q->data, delq->data, NULL,
srvinfo->dobj.dependencies, srvinfo->dobj.nDeps,
NULL, NULL);
@@ -10279,7 +10410,7 @@ dumpUserMappings(Archive *fout, const char *target,
tag->data,
namespace,
NULL,
- owner, false,
+ owner, false, false,
"USER MAPPING", SECTION_PRE_DATA,
q->data, delq->data, NULL,
&dumpId, 1,
@@ -10350,7 +10481,7 @@ dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo)
daclinfo->dobj.namespace ? daclinfo->dobj.namespace->dobj.name : NULL,
NULL,
daclinfo->defaclrole,
- false, "DEFAULT ACL", SECTION_NONE,
+ false, false, "DEFAULT ACL", SECTION_NONE,
q->data, "", NULL,
daclinfo->dobj.dependencies, daclinfo->dobj.nDeps,
NULL, NULL);
@@ -10407,7 +10538,7 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
tag, nspname,
NULL,
owner ? owner : "",
- false, "ACL", SECTION_NONE,
+ false, false, "ACL", SECTION_NONE,
sql->data, "", NULL,
&(objDumpId), 1,
NULL, NULL);
@@ -10416,6 +10547,123 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId,
}
/*
+ * dumpSecLabel
+ *
+ * write out security label of the objects
+ */
+static void
+dumpSecLabel(Archive *fout, DumpId objDumpId,
+ const char *target, const char *namespace,
+ const char *seclabel)
+{
+ PQExpBuffer qry;
+
+ /* Do nothing, if security label dump is not given */
+ if (!security_label || strlen(seclabel) == 0)
+ return;
+
+ /* Do nothing, if --data-only for schemas, or --schema-only for blobs */
+ if (strncmp(target, "LARGE OBJECT", 13) == 0)
+ {
+ if (schemaOnly)
+ return;
+ }
+ else
+ {
+ if (dataOnly)
+ return;
+ }
+
+ /* Make ALTER xxx SECURITY LABEL TO command */
+ qry = createPQExpBuffer();
+
+ appendPQExpBuffer(qry, "ALTER %s SECURITY LABEL TO '%s';",
+ target, seclabel);
+
+ ArchiveEntry(fout,
+ nilCatalogId, /* catalog ID */
+ createDumpId(), /* dump ID */
+ target, /* name */
+ namespace, /* namespace */
+ NULL, /* tablespace */
+ "", /* owner */
+ false, /* with oids */
+ false, /* with secids */
+ "LABEL", /* desc */
+ SECTION_NONE, /* section */
+ qry->data, /* create */
+ "", /* delete*/
+ NULL, /* copy */
+ &(objDumpId), /* dependency */
+ 1, /* # deps */
+ NULL, /* dumper Func */
+ NULL); /* dumper Arg */
+
+ destroyPQExpBuffer(qry);
+}
+
+/*
+ * dumpTableSecLabel
+ *
+ * write out security label of the table
+ */
+static void
+dumpTableSecLabel(Archive *fout, DumpId objDumpId,
+ const char *namespace,
+ const char *table_name,
+ const char **column_names, int ncolumns,
+ const char *table_label, const char **column_labels)
+{
+ PQExpBuffer qry;
+ PQExpBuffer tag;
+ int i;
+
+ if (!security_label || dataOnly)
+ return;
+
+ /* Make ALTER xxx SECURITY LABEL TO command */
+ qry = createPQExpBuffer();
+ tag = createPQExpBuffer();
+
+ appendPQExpBuffer(tag, "TABLE %s", table_name);
+
+ if (strlen(table_label) > 0)
+ appendPQExpBuffer(qry, "ALTER TABLE %s SECURITY LABEL TO '%s';\n",
+ table_name, table_label);
+ for (i = 0; i < ncolumns; i++)
+ {
+ if (strlen(column_labels[i]) > 0)
+ appendPQExpBuffer(qry, "ALTER TABLE %s ALTER %s SECURITY LABEL TO '%s';\n",
+ table_name,
+ column_names[i],
+ column_labels[i]);
+ }
+
+ if (qry->len > 0)
+ ArchiveEntry(fout,
+ nilCatalogId, /* catalog ID */
+ createDumpId(), /* dump ID */
+ tag->data, /* name */
+ namespace, /* namespace */
+ NULL, /* tablespace */
+ "", /* owner */
+ false, /* with oids */
+ false, /* with secids */
+ "LABEL", /* desc */
+ SECTION_NONE, /* section */
+ qry->data, /* create */
+ "", /* delete*/
+ NULL, /* copy */
+ &(objDumpId), /* dependency */
+ 1, /* # deps */
+ NULL, /* dumper Func */
+ NULL); /* dumper Arg */
+
+ destroyPQExpBuffer(qry);
+ destroyPQExpBuffer(tag);
+}
+
+/*
* dumpTable
* write out to fout the declarations (not data) of a user-defined table
*/
@@ -10571,7 +10819,6 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
appendPQExpBuffer(q, "CREATE VIEW %s AS\n %s\n",
fmtId(tbinfo->dobj.name), viewdef);
-
PQclear(res);
}
else
@@ -10922,6 +11169,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
(tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace,
tbinfo->rolname,
(strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false,
+ (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hassecids : false,
reltypename, SECTION_PRE_DATA,
q->data, delq->data, NULL,
tbinfo->dobj.dependencies, tbinfo->dobj.nDeps,
@@ -10942,6 +11190,27 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
dumpTableConstraintComment(fout, constr);
}
+ /* Dump relation/attribute's security labels */
+ if (tbinfo->relkind == RELKIND_RELATION)
+ {
+ dumpTableSecLabel(fout, tbinfo->dobj.dumpId,
+ tbinfo->dobj.namespace->dobj.name,
+ tbinfo->dobj.name,
+ tbinfo->attnames, tbinfo->numatts,
+ tbinfo->rellabel, tbinfo->attlabels);
+ }
+ else
+ {
+ PQExpBuffer target = createPQExpBuffer();
+
+ appendPQExpBuffer(target, "%s %s", reltypename, fmtId(tbinfo->dobj.name));
+ dumpSecLabel(fout, tbinfo->dobj.dumpId,
+ target->data,
+ tbinfo->dobj.namespace->dobj.name,
+ tbinfo->rellabel);
+ destroyPQExpBuffer(target);
+ }
+
destroyPQExpBuffer(query);
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
@@ -10990,7 +11259,7 @@ dumpAttrDef(Archive *fout, AttrDefInfo *adinfo)
tbinfo->dobj.namespace->dobj.name,
NULL,
tbinfo->rolname,
- false, "DEFAULT", SECTION_PRE_DATA,
+ false, false, "DEFAULT", SECTION_PRE_DATA,
q->data, delq->data, NULL,
adinfo->dobj.dependencies, adinfo->dobj.nDeps,
NULL, NULL);
@@ -11086,7 +11355,7 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo)
indxinfo->dobj.name,
tbinfo->dobj.namespace->dobj.name,
indxinfo->tablespace,
- tbinfo->rolname, false,
+ tbinfo->rolname, false, false,
"INDEX", SECTION_POST_DATA,
q->data, delq->data, NULL,
indxinfo->dobj.dependencies, indxinfo->dobj.nDeps,
@@ -11211,7 +11480,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo)
coninfo->dobj.name,
tbinfo->dobj.namespace->dobj.name,
indxinfo->tablespace,
- tbinfo->rolname, false,
+ tbinfo->rolname, false, false,
"CONSTRAINT", SECTION_POST_DATA,
q->data, delq->data, NULL,
coninfo->dobj.dependencies, coninfo->dobj.nDeps,
@@ -11244,7 +11513,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo)
coninfo->dobj.name,
tbinfo->dobj.namespace->dobj.name,
NULL,
- tbinfo->rolname, false,
+ tbinfo->rolname, false, false,
"FK CONSTRAINT", SECTION_POST_DATA,
q->data, delq->data, NULL,
coninfo->dobj.dependencies, coninfo->dobj.nDeps,
@@ -11279,7 +11548,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo)
coninfo->dobj.name,
tbinfo->dobj.namespace->dobj.name,
NULL,
- tbinfo->rolname, false,
+ tbinfo->rolname, false, false,
"CHECK CONSTRAINT", SECTION_POST_DATA,
q->data, delq->data, NULL,
coninfo->dobj.dependencies, coninfo->dobj.nDeps,
@@ -11315,7 +11584,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo)
coninfo->dobj.name,
tyinfo->dobj.namespace->dobj.name,
NULL,
- tyinfo->rolname, false,
+ tyinfo->rolname, false, false,
"CHECK CONSTRAINT", SECTION_POST_DATA,
q->data, delq->data, NULL,
coninfo->dobj.dependencies, coninfo->dobj.nDeps,
@@ -11601,7 +11870,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
tbinfo->dobj.namespace->dobj.name,
NULL,
tbinfo->rolname,
- false, "SEQUENCE", SECTION_PRE_DATA,
+ false, false, "SEQUENCE", SECTION_PRE_DATA,
query->data, delqry->data, NULL,
tbinfo->dobj.dependencies, tbinfo->dobj.nDeps,
NULL, NULL);
@@ -11637,7 +11906,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
tbinfo->dobj.namespace->dobj.name,
NULL,
tbinfo->rolname,
- false, "SEQUENCE OWNED BY", SECTION_PRE_DATA,
+ false, false, "SEQUENCE OWNED BY", SECTION_PRE_DATA,
query->data, "", NULL,
&(tbinfo->dobj.dumpId), 1,
NULL, NULL);
@@ -11650,6 +11919,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
dumpComment(fout, query->data,
tbinfo->dobj.namespace->dobj.name, tbinfo->rolname,
tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId);
+
+ /* Dump sequence security label */
+ dumpSecLabel(fout, tbinfo->dobj.dumpId,
+ query->data,
+ tbinfo->dobj.namespace->dobj.name,
+ tbinfo->rellabel);
}
if (!schemaOnly)
@@ -11665,7 +11940,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
tbinfo->dobj.namespace->dobj.name,
NULL,
tbinfo->rolname,
- false, "SEQUENCE SET", SECTION_PRE_DATA,
+ false, false, "SEQUENCE SET", SECTION_PRE_DATA,
query->data, "", NULL,
&(tbinfo->dobj.dumpId), 1,
NULL, NULL);
@@ -11849,7 +12124,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo)
tginfo->dobj.name,
tbinfo->dobj.namespace->dobj.name,
NULL,
- tbinfo->rolname, false,
+ tbinfo->rolname, false, false,
"TRIGGER", SECTION_POST_DATA,
query->data, delqry->data, NULL,
tginfo->dobj.dependencies, tginfo->dobj.nDeps,
@@ -11969,7 +12244,7 @@ dumpRule(Archive *fout, RuleInfo *rinfo)
rinfo->dobj.name,
tbinfo->dobj.namespace->dobj.name,
NULL,
- tbinfo->rolname, false,
+ tbinfo->rolname, false, false,
"RULE", SECTION_POST_DATA,
cmd->data, delcmd->data, NULL,
rinfo->dobj.dependencies, rinfo->dobj.nDeps,
@@ -12334,7 +12609,7 @@ fmtQualifiedId(const char *schema, const char *id)
* "", not an invalid "()" column list.
*/
static const char *
-fmtCopyColumnList(const TableInfo *ti)
+fmtCopyColumnList(const TableInfo *ti, bool secids)
{
static PQExpBuffer q = NULL;
int numatts = ti->numatts;
@@ -12350,6 +12625,11 @@ fmtCopyColumnList(const TableInfo *ti)
appendPQExpBuffer(q, "(");
needComma = false;
+ if (secids)
+ {
+ appendPQExpBuffer(q, "security_label");
+ needComma = true;
+ }
for (i = 0; i < numatts; i++)
{
if (attisdropped[i])
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index c93bada..78a50bd 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -137,6 +137,7 @@ typedef struct _namespaceInfo
DumpableObject dobj;
char *rolname; /* name of owner, or empty string */
char *nspacl;
+ char *seclabel;
} NamespaceInfo;
typedef struct _typeInfo
@@ -153,6 +154,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
+ char *seclabel;
bool isDefined; /* true if typisdefined */
/* If it's a dumpable base type, we create a "shell type" entry for it */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
@@ -227,9 +229,11 @@ typedef struct _tableInfo
bool hasrules; /* does it have any rules? */
bool hastriggers; /* does it have any triggers? */
bool hasoids; /* does it have OIDs? */
+ bool hassecids; /* does it have security-Id? */
uint32 frozenxid; /* for restore frozen xid */
int ncheck; /* # of CHECK expressions */
char *reloftype; /* underlying type for typed table */
+ char *rellabel; /* relation's security label */
/* these two are set only if table is a sequence owned by a column: */
Oid owning_tab; /* OID of table owning sequence */
int owning_col; /* attr # of column owning sequence */
@@ -252,6 +256,7 @@ typedef struct _tableInfo
char *attalign; /* attribute align, used by binary_upgrade */
bool *attislocal; /* true if attr has local definition */
char **attoptions; /* per-attribute options */
+ char **attlabels; /* attribute's security label */
/*
* Note: we need to store per-attribute notnull, default, and constraint
@@ -287,6 +292,7 @@ typedef struct _tableDataInfo
DumpableObject dobj;
TableInfo *tdtable; /* link to table to dump */
bool oids; /* include OIDs in data? */
+ bool secids; /* include SecIDs in data? */
} TableDataInfo;
typedef struct _indxInfo
@@ -448,6 +454,7 @@ typedef struct _blobInfo
DumpableObject dobj;
char *rolname;
char *blobacl;
+ char *seclabel;
} BlobInfo;
/* global decls */
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
index 0c3f63f..83f1678 100644
--- a/src/bin/pg_dump/pg_dumpall.c
+++ b/src/bin/pg_dump/pg_dumpall.c
@@ -69,6 +69,7 @@ static int disable_triggers = 0;
static int inserts = 0;
static int no_tablespaces = 0;
static int use_setsessauth = 0;
+static int security_label = 0;
static int server_version;
static FILE *OPF;
@@ -132,6 +133,7 @@ main(int argc, char *argv[])
{"no-tablespaces", no_argument, &no_tablespaces, 1},
{"role", required_argument, NULL, 3},
{"use-set-session-authorization", no_argument, &use_setsessauth, 1},
+ {"security-label", no_argument, &security_label, 1},
{NULL, 0, NULL, 0}
};
@@ -285,6 +287,8 @@ main(int argc, char *argv[])
no_tablespaces = 1;
else if (strcmp(optarg, "use-set-session-authorization") == 0)
use_setsessauth = 1;
+ else if (strcmp(optarg, "security-label") == 0)
+ security_label = 1;
else
{
fprintf(stderr,
@@ -330,6 +334,8 @@ main(int argc, char *argv[])
appendPQExpBuffer(pgdumpopts, " --no-tablespaces");
if (use_setsessauth)
appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization");
+ if (security_label)
+ appendPQExpBuffer(pgdumpopts, " --security-label");
if (optind < argc)
{
@@ -561,6 +567,7 @@ help(void)
printf(_(" --use-set-session-authorization\n"
" use SET SESSION AUTHORIZATION commands instead of\n"
" ALTER OWNER commands to set ownership\n"));
+ printf(_(" --security-label dump schema/data with security label\n"));
printf(_("\nConnection options:\n"));
printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
@@ -949,6 +956,7 @@ dropTablespaces(PGconn *conn)
static void
dumpTablespaces(PGconn *conn)
{
+ PQExpBuffer qry = createPQExpBuffer();
PGresult *res;
int i;
@@ -957,31 +965,37 @@ dumpTablespaces(PGconn *conn)
* pg_xxx)
*/
if (server_version >= 90000)
- res = executeQuery(conn, "SELECT spcname, "
+ appendPQExpBuffer(qry, "SELECT spcname, "
"pg_catalog.pg_get_userbyid(spcowner) AS spcowner, "
"spclocation, spcacl, "
"array_to_string(spcoptions, ', '),"
"pg_catalog.shobj_description(oid, 'pg_tablespace') "
+ ",%s AS seclabel "
"FROM pg_catalog.pg_tablespace "
"WHERE spcname !~ '^pg_' "
- "ORDER BY 1");
+ "ORDER BY 1",
+ security_label ? "security_label" : "NULL");
else if (server_version >= 80200)
- res = executeQuery(conn, "SELECT spcname, "
+ appendPQExpBuffer(qry, "SELECT spcname, "
"pg_catalog.pg_get_userbyid(spcowner) AS spcowner, "
"spclocation, spcacl, null, "
"pg_catalog.shobj_description(oid, 'pg_tablespace') "
+ ",NULL AS seclabel"
"FROM pg_catalog.pg_tablespace "
"WHERE spcname !~ '^pg_' "
"ORDER BY 1");
else
- res = executeQuery(conn, "SELECT spcname, "
+ appendPQExpBuffer(qry, "SELECT spcname, "
"pg_catalog.pg_get_userbyid(spcowner) AS spcowner, "
"spclocation, spcacl, "
"null, null "
+ ",NULL AS seclabel"
"FROM pg_catalog.pg_tablespace "
"WHERE spcname !~ '^pg_' "
"ORDER BY 1");
+ res = PQexec(conn, qry->data);
+
if (PQntuples(res) > 0)
fprintf(OPF, "--\n-- Tablespaces\n--\n\n");
@@ -994,6 +1008,7 @@ dumpTablespaces(PGconn *conn)
char *spcacl = PQgetvalue(res, i, 3);
char *spcoptions = PQgetvalue(res, i, 4);
char *spccomment = PQgetvalue(res, i, 5);
+ char *spcseclabel = PQgetvalue(res, i, 6);
char *fspcname;
/* needed for buildACLCommands() */
@@ -1010,6 +1025,10 @@ dumpTablespaces(PGconn *conn)
appendPQExpBuffer(buf, "ALTER TABLESPACE %s SET (%s);\n",
fspcname, spcoptions);
+ if (security_label && strlen(spcseclabel) > 0)
+ appendPQExpBuffer(buf, "ALTER TABLESPACE %s SECURITY LABEL TO '%s';\n",
+ fspcname, spcseclabel);
+
if (!skip_acls &&
!buildACLCommands(fspcname, NULL, "TABLESPACE", spcacl, spcowner,
"", server_version, buf))
@@ -1153,48 +1172,48 @@ dumpCreateDB(PGconn *conn)
/* Now collect all the information about databases to dump */
if (server_version >= 80400)
- res = executeQuery(conn,
- "SELECT datname, "
+ appendPQExpBuffer(buf, "SELECT datname, "
"coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), "
"pg_encoding_to_char(d.encoding), "
"datcollate, datctype, datfrozenxid, "
"datistemplate, datacl, datconnlimit, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace "
+ ",%s AS seclabel "
"FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) "
- "WHERE datallowconn ORDER BY 1");
+ "WHERE datallowconn ORDER BY 1",
+ security_label ? "d.security_label" : "NULL");
else if (server_version >= 80100)
- res = executeQuery(conn,
- "SELECT datname, "
+ appendPQExpBuffer(buf, "SELECT datname, "
"coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), "
"pg_encoding_to_char(d.encoding), "
"null::text AS datcollate, null::text AS datctype, datfrozenxid, "
"datistemplate, datacl, datconnlimit, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace "
+ ",NULL AS seclabel "
"FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) "
"WHERE datallowconn ORDER BY 1");
else if (server_version >= 80000)
- res = executeQuery(conn,
- "SELECT datname, "
+ appendPQExpBuffer(buf, "SELECT datname, "
"coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), "
"pg_encoding_to_char(d.encoding), "
"null::text AS datcollate, null::text AS datctype, datfrozenxid, "
"datistemplate, datacl, -1 as datconnlimit, "
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace "
+ ",NULL AS seclabel "
"FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) "
"WHERE datallowconn ORDER BY 1");
else if (server_version >= 70300)
- res = executeQuery(conn,
- "SELECT datname, "
+ appendPQExpBuffer(buf, "SELECT datname, "
"coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), "
"pg_encoding_to_char(d.encoding), "
"null::text AS datcollate, null::text AS datctype, datfrozenxid, "
"datistemplate, datacl, -1 as datconnlimit, "
"'pg_default' AS dattablespace "
+ ",NULL AS seclabel "
"FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) "
"WHERE datallowconn ORDER BY 1");
else if (server_version >= 70100)
- res = executeQuery(conn,
- "SELECT datname, "
+ appendPQExpBuffer(buf, "SELECT datname, "
"coalesce("
"(select usename from pg_shadow where usesysid=datdba), "
"(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), "
@@ -1202,6 +1221,7 @@ dumpCreateDB(PGconn *conn)
"null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, "
"datistemplate, '' as datacl, -1 as datconnlimit, "
"'pg_default' AS dattablespace "
+ ",NULL AS seclabel "
"FROM pg_database d "
"WHERE datallowconn ORDER BY 1");
else
@@ -1210,18 +1230,20 @@ dumpCreateDB(PGconn *conn)
* Note: 7.0 fails to cope with sub-select in COALESCE, so just deal
* with getting a NULL by not printing any OWNER clause.
*/
- res = executeQuery(conn,
- "SELECT datname, "
+ appendPQExpBuffer(buf, "SELECT datname, "
"(select usename from pg_shadow where usesysid=datdba), "
"pg_encoding_to_char(d.encoding), "
"null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, "
"'f' as datistemplate, "
"'' as datacl, -1 as datconnlimit, "
"'pg_default' AS dattablespace "
+ ",NULL AS seclabel "
"FROM pg_database d "
"ORDER BY 1");
}
+ res = PQexec(conn, buf->data);
+
for (i = 0; i < PQntuples(res); i++)
{
char *dbname = PQgetvalue(res, i, 0);
@@ -1234,6 +1256,7 @@ dumpCreateDB(PGconn *conn)
char *dbacl = PQgetvalue(res, i, 7);
char *dbconnlimit = PQgetvalue(res, i, 8);
char *dbtablespace = PQgetvalue(res, i, 9);
+ char *dbseclabel = PQgetvalue(res, i, 10);
char *fdbname;
fdbname = strdup(fmtId(dbname));
@@ -1309,6 +1332,9 @@ dumpCreateDB(PGconn *conn)
appendPQExpBuffer(buf, ";\n");
}
}
+ if (security_label && strlen(dbseclabel) > 0)
+ appendPQExpBuffer(buf, "ALTER DATABASE %s SECURITY LABEL TO '%s';\n",
+ dbname, dbseclabel);
if (!skip_acls &&
!buildACLCommands(fdbname, NULL, "DATABASE", dbacl, dbowner,
diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c
index b0bcbc2..cdb2592 100644
--- a/src/bin/pg_dump/pg_restore.c
+++ b/src/bin/pg_dump/pg_restore.c
@@ -76,6 +76,7 @@ main(int argc, char **argv)
static int no_data_for_failed_tables = 0;
static int outputNoTablespaces = 0;
static int use_setsessauth = 0;
+ static int no_security_label = 0;
struct option cmdopts[] = {
{"clean", 0, NULL, 'c'},
@@ -116,6 +117,7 @@ main(int argc, char **argv)
{"no-tablespaces", no_argument, &outputNoTablespaces, 1},
{"role", required_argument, NULL, 2},
{"use-set-session-authorization", no_argument, &use_setsessauth, 1},
+ {"no-security-label", no_argument, &no_security_label, 1},
{NULL, 0, NULL, 0}
};
@@ -262,6 +264,8 @@ main(int argc, char **argv)
outputNoTablespaces = 1;
else if (strcmp(optarg, "use-set-session-authorization") == 0)
use_setsessauth = 1;
+ else if (strcmp(optarg, "no-security-label") == 0)
+ no_security_label = 1;
else
{
fprintf(stderr,
@@ -326,6 +330,7 @@ main(int argc, char **argv)
opts->noDataForFailedTables = no_data_for_failed_tables;
opts->noTablespace = outputNoTablespaces;
opts->use_setsessauth = use_setsessauth;
+ opts->noSecLabel = no_security_label;
if (opts->formatName)
{
@@ -437,6 +442,7 @@ usage(const char *progname)
" ALTER OWNER commands to set ownership\n"));
printf(_(" -1, --single-transaction\n"
" restore as a single transaction\n"));
+ printf(_(" --no-security-label skip restoration of security labels\n"));
printf(_("\nConnection options:\n"));
printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index d87e888..270ff67 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -163,7 +163,7 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
#define HEAP_HASVARWIDTH 0x0002 /* has variable-width attribute(s) */
#define HEAP_HASEXTERNAL 0x0004 /* has external stored attribute(s) */
#define HEAP_HASOID 0x0008 /* has an object-id field */
-/* bit 0x0010 is available */
+#define HEAP_HASSECID 0x0010 /* has an security-id field */
#define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */
#define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */
#define HEAP_XMAX_SHARED_LOCK 0x0080 /* xmax is shared locker */
@@ -292,6 +292,9 @@ do { \
(tup)->t_choice.t_datum.datum_typmod = (typmod) \
)
+#define HeapTupleHeaderHasOid(tup) \
+ ((tup)->t_infomask & HEAP_HASOID)
+
#define HeapTupleHeaderGetOid(tup) \
( \
((tup)->t_infomask & HEAP_HASOID) ? \
@@ -351,6 +354,25 @@ do { \
(tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \
)
+#define HeapTupleHeaderHasSecid(tup) \
+ ((tup)->t_infomask & HEAP_HASSECID)
+
+#define HeapTupleHeaderGetSecid(tup) \
+ ( \
+ HeapTupleHeaderHasSecid(tup) \
+ ? (*(Oid *)((char *)(tup) + (tup)->t_hoff \
+ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \
+ - sizeof(Oid))) \
+ : InvalidOid \
+ )
+
+#define HeapTupleHeaderSetSecid(tup, secid) \
+ do { \
+ Assert(HeapTupleHeaderHasSecid(tup)); \
+ *((Oid *)((char *)(tup) + (tup)->t_hoff \
+ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \
+ - sizeof(Oid))) = (secid); \
+ } while(0)
/*
* BITMAPLEN(NATTS) -
@@ -545,12 +567,23 @@ typedef HeapTupleData *HeapTuple;
#define HeapTupleClearHeapOnly(tuple) \
HeapTupleHeaderClearHeapOnly((tuple)->t_data)
+#define HeapTupleHasOid(tuple) \
+ HeapTupleHeaderHasOid((tuple)->t_data)
+
#define HeapTupleGetOid(tuple) \
HeapTupleHeaderGetOid((tuple)->t_data)
#define HeapTupleSetOid(tuple, oid) \
HeapTupleHeaderSetOid((tuple)->t_data, (oid))
+#define HeapTupleHasSecid(tuple) \
+ HeapTupleHeaderHasSecid((tuple)->t_data)
+
+#define HeapTupleGetSecid(tuple) \
+ HeapTupleHeaderGetSecid((tuple)->t_data)
+
+#define HeapTupleSetSecid(tuple, secid) \
+ HeapTupleHeaderSetSecid((tuple)->t_data, (secid))
/*
* WAL record definitions for heapam.c's WAL operations
diff --git a/src/include/access/sysattr.h b/src/include/access/sysattr.h
index 59cd2cd..ad5903b 100644
--- a/src/include/access/sysattr.h
+++ b/src/include/access/sysattr.h
@@ -25,7 +25,8 @@
#define MaxTransactionIdAttributeNumber (-5)
#define MaxCommandIdAttributeNumber (-6)
#define TableOidAttributeNumber (-7)
-#define FirstLowInvalidHeapAttributeNumber (-8)
+#define SecurityLabelAttributeNumber (-8)
+#define FirstLowInvalidHeapAttributeNumber (-9)
#endif /* SYSATTR_H */
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index 53e0be6..dea713b 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -75,13 +75,14 @@ typedef struct tupleDesc
Oid tdtypeid; /* composite type ID for tuple type */
int32 tdtypmod; /* typmod for tuple type */
bool tdhasoid; /* tuple has oid attribute in its header */
+ bool tdhassecid; /* tuple has security id in its header */
int tdrefcount; /* reference count, or -1 if not counting */
} *TupleDesc;
-extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid);
+extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid, bool hassecid);
-extern TupleDesc CreateTupleDesc(int natts, bool hasoid,
+extern TupleDesc CreateTupleDesc(int natts, bool hasoid, bool hassecid,
Form_pg_attribute *attrs);
extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc);
diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h
index 49a32e3..f088876 100644
--- a/src/include/bootstrap/bootstrap.h
+++ b/src/include/bootstrap/bootstrap.h
@@ -24,6 +24,7 @@ typedef enum
BgWriterProcess,
WalWriterProcess,
WalReceiverProcess,
+ SecurityWorkerProcess,
NUM_AUXPROCTYPES /* Must be last! */
} AuxProcType;
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 8292273..e2a86e8 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -61,7 +61,8 @@ extern Oid heap_create_with_catalog(const char *relname,
OnCommitAction oncommit,
Datum reloptions,
bool use_user_acl,
- bool allow_system_table_mods);
+ bool allow_system_table_mods,
+ Oid *secLabels);
extern void heap_drop_with_catalog(Oid relid);
@@ -75,13 +76,15 @@ extern List *heap_truncate_find_FKs(List *relationIds);
extern void InsertPgAttributeTuple(Relation pg_attribute_rel,
Form_pg_attribute new_attribute,
- CatalogIndexState indstate);
+ CatalogIndexState indstate,
+ Oid securityId);
extern void InsertPgClassTuple(Relation pg_class_desc,
Relation new_rel_desc,
Oid new_rel_oid,
Datum relacl,
- Datum reloptions);
+ Datum reloptions,
+ Oid securityId);
extern List *AddRelationNewConstraints(Relation rel,
List *newColDefaults,
@@ -106,10 +109,13 @@ extern void RemoveAttrDefaultById(Oid attrdefId);
extern void RemoveStatistics(Oid relid, AttrNumber attnum);
extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno,
- bool relhasoids);
+ bool relhasoids, bool relhassecids);
extern Form_pg_attribute SystemAttributeByName(const char *attname,
- bool relhasoids);
+ bool relhasoids, bool relhassecids);
+
+extern bool SystemAttributeWritable(AttrNumber attno,
+ bool relhasoids, bool relhassecids);
extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind,
bool allow_system_table_mods);
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 4f437fd..069256f 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -255,6 +255,11 @@ DECLARE_UNIQUE_INDEX(pg_type_oid_index, 2703, on pg_type using btree(oid oid_ops
DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typname name_ops, typnamespace oid_ops));
#define TypeNameNspIndexId 2704
+DECLARE_UNIQUE_INDEX(pg_seclabel_secid_index, 3401, on pg_seclabel using btree(secid oid_ops, datid oid_ops, relid oid_ops));
+#define SecLabelSecidIndexId 3401
+DECLARE_INDEX(pg_seclabel_label_index, 3402, on pg_seclabel using btree(datid oid_ops, relid oid_ops, label text_ops));
+#define SecLabelLabelIndexId 3402
+
DECLARE_UNIQUE_INDEX(pg_foreign_data_wrapper_oid_index, 112, on pg_foreign_data_wrapper using btree(oid oid_ops));
#define ForeignDataWrapperOidIndexId 112
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 5ea514d..0f07482 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -60,6 +60,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO
*/
int2 relchecks; /* # of CHECK constraints for class */
bool relhasoids; /* T if we generate OIDs for rows of rel */
+ bool relhassecids; /* T if we generate SIDs for rows of rel */
bool relhaspkey; /* has (or has had) PRIMARY KEY index */
bool relhasexclusion; /* has (or has had) exclusion constraint */
bool relhasrules; /* has (or has had) any rules */
@@ -93,7 +94,7 @@ typedef FormData_pg_class *Form_pg_class;
* ----------------
*/
-#define Natts_pg_class 27
+#define Natts_pg_class 28
#define Anum_pg_class_relname 1
#define Anum_pg_class_relnamespace 2
#define Anum_pg_class_reltype 3
@@ -113,14 +114,15 @@ typedef FormData_pg_class *Form_pg_class;
#define Anum_pg_class_relnatts 17
#define Anum_pg_class_relchecks 18
#define Anum_pg_class_relhasoids 19
-#define Anum_pg_class_relhaspkey 20
-#define Anum_pg_class_relhasexclusion 21
-#define Anum_pg_class_relhasrules 22
-#define Anum_pg_class_relhastriggers 23
-#define Anum_pg_class_relhassubclass 24
-#define Anum_pg_class_relfrozenxid 25
-#define Anum_pg_class_relacl 26
-#define Anum_pg_class_reloptions 27
+#define Anum_pg_class_relhassecids 20
+#define Anum_pg_class_relhaspkey 21
+#define Anum_pg_class_relhasexclusion 22
+#define Anum_pg_class_relhasrules 23
+#define Anum_pg_class_relhastriggers 24
+#define Anum_pg_class_relhassubclass 25
+#define Anum_pg_class_relfrozenxid 26
+#define Anum_pg_class_relacl 27
+#define Anum_pg_class_reloptions 28
/* ----------------
* initial contents of pg_class
@@ -132,13 +134,13 @@ typedef FormData_pg_class *Form_pg_class;
*/
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
-DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t f f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t t f f f f f 3 _null_ _null_ ));
DESCR("");
-DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f t f f f f f 3 _null_ _null_ ));
DESCR("");
-DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f f r 25 0 t f f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f f r 25 0 t t f f f f f 3 _null_ _null_ ));
DESCR("");
-DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f f r 27 0 t f f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t t f f f f f 3 _null_ _null_ ));
DESCR("");
#define RELKIND_INDEX 'i' /* secondary index */
diff --git a/src/include/catalog/pg_conversion_fn.h b/src/include/catalog/pg_conversion_fn.h
index d40dea6..add5fd9 100644
--- a/src/include/catalog/pg_conversion_fn.h
+++ b/src/include/catalog/pg_conversion_fn.h
@@ -17,7 +17,7 @@
extern Oid ConversionCreate(const char *conname, Oid connamespace,
Oid conowner,
int32 conforencoding, int32 contoencoding,
- Oid conproc, bool def);
+ Oid conproc, bool def, Oid securityId);
extern void RemoveConversionById(Oid conversionOid);
extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding);
diff --git a/src/include/catalog/pg_largeobject.h b/src/include/catalog/pg_largeobject.h
index c4c4a26..6025f56 100644
--- a/src/include/catalog/pg_largeobject.h
+++ b/src/include/catalog/pg_largeobject.h
@@ -51,9 +51,10 @@ typedef FormData_pg_largeobject *Form_pg_largeobject;
#define Anum_pg_largeobject_pageno 2
#define Anum_pg_largeobject_data 3
-extern Oid LargeObjectCreate(Oid loid);
+extern Oid LargeObjectCreate(Oid loid, Oid securityId);
extern void LargeObjectDrop(Oid loid);
extern void LargeObjectAlterOwner(Oid loid, Oid newOwnerId);
+extern void LargeObjectAlterSecLabel(Oid loid, char *new_label);
extern bool LargeObjectExists(Oid loid);
#endif /* PG_LARGEOBJECT_H */
diff --git a/src/include/catalog/pg_namespace.h b/src/include/catalog/pg_namespace.h
index 425f039..0f8043d 100644
--- a/src/include/catalog/pg_namespace.h
+++ b/src/include/catalog/pg_namespace.h
@@ -77,6 +77,6 @@ DESCR("standard public schema");
/*
* prototypes for functions in pg_namespace.c
*/
-extern Oid NamespaceCreate(const char *nspName, Oid ownerId);
+extern Oid NamespaceCreate(const char *nspName, Oid ownerId, Oid secid);
#endif /* PG_NAMESPACE_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index f2751a4..3e024b3 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3719,6 +3719,10 @@ DESCR("current user privilege on role by role name");
DATA(insert OID = 2710 ( pg_has_role PGNSP PGUID 12 1 0 0 f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ pg_has_role_id _null_ _null_ _null_ ));
DESCR("current user privilege on role by role oid");
+/* SE-PostgreSQL related stuff */
+DATA(insert OID = 3405 ( seclabel_to_secid PGNSP PGUID 12 1 0 0 f f f t f v 1 0 26 "2249" _null_ _null_ _null_ _null_ seclabel_to_secid _null_ _null_ _null_ ));
+DATA(insert OID = 3407 ( sepgsql_getcon PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ sepgsql_getcon _null_ _null_ _null_));
+
DATA(insert OID = 1269 ( pg_column_size PGNSP PGUID 12 1 0 0 f f f t f s 1 0 23 "2276" _null_ _null_ _null_ _null_ pg_column_size _null_ _null_ _null_ ));
DESCR("bytes required to store the value, perhaps with compression");
DATA(insert OID = 2322 ( pg_tablespace_size PGNSP PGUID 12 1 0 0 f f f t f v 1 0 20 "26" _null_ _null_ _null_ _null_ pg_tablespace_size_oid _null_ _null_ _null_ ));
diff --git a/src/include/catalog/pg_proc_fn.h b/src/include/catalog/pg_proc_fn.h
index c886f81..f2351c1 100644
--- a/src/include/catalog/pg_proc_fn.h
+++ b/src/include/catalog/pg_proc_fn.h
@@ -37,7 +37,8 @@ extern Oid ProcedureCreate(const char *procedureName,
List *parameterDefaults,
Datum proconfig,
float4 procost,
- float4 prorows);
+ float4 prorows,
+ Oid prosecid);
extern bool function_parse_error_transpose(const char *prosrc);
diff --git a/src/include/catalog/pg_seclabel.h b/src/include/catalog/pg_seclabel.h
new file mode 100644
index 0000000..21b25de
--- /dev/null
+++ b/src/include/catalog/pg_seclabel.h
@@ -0,0 +1,79 @@
+/*
+ * pg_seclabel.h
+ * Definition of the security label relation (pg_seclabel)
+ *
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ */
+#ifndef PG_SECLABEL_H
+#define PG_SECLABEL_H
+
+#include "catalog/genbki.h"
+
+#include "access/htup.h"
+#include "access/skey.h"
+#include "utils/relcache.h"
+
+#define SecLabelRelationId 3400
+
+CATALOG(pg_seclabel,3400) BKI_SHARED_RELATION BKI_WITHOUT_OIDS
+{
+ /* Identifier of the security label */
+ Oid secid;
+
+ /* OID of the database which referes the entry */
+ Oid datid;
+
+ /* OID of the table which refers the entry */
+ Oid relid;
+
+ /* Text representation of the security label */
+ text label;
+} FormData_pg_seclabel;
+
+/*
+ * Form_pg_seclabel corresponds to a pointer to a tuple with
+ * the format of pg_seclabel relation.
+ */
+typedef FormData_pg_seclabel *Form_pg_seclabel;
+
+/*
+ * Compiler constants for pg_seclabel
+ */
+#define Natts_pg_seclabel 4
+#define Anum_pg_seclabel_secid 1
+#define Anum_pg_seclabel_datid 2
+#define Anum_pg_seclabel_relid 3
+#define Anum_pg_seclabel_label 4
+
+/*
+ * Functions to translate between security label and identifier
+ */
+extern bool ignore_security_label_input;
+
+extern bool seclabelCatalogHasSysAttr(Oid relOid);
+extern void seclabelPostBootstrap(void);
+extern void seclabelOnCreateDatabase(Oid src_datOid, Oid dst_datOid);
+extern void seclabelOnDropDatabase(Oid datOid);
+extern void seclabelOnDropTable(Oid relOid);
+
+extern Oid *seclabelMakeRelationDefaults(TupleDesc tupdesc, List *supOids);
+extern Oid *seclabelMakeToastDefaults(TupleDesc tupdesc, Oid relOid);
+
+extern Oid seclabelGetNewSecid(Relation rel, HeapTuple tuple);
+
+extern Oid seclabelRawInput(Oid relOid, char *seclabel);
+extern char *seclabelRawOutput(Oid relOid, Oid secid);
+extern Oid seclabelTransInput(Oid relOid, char *seclabel);
+extern char *seclabelTransOutput(Oid relOid, Oid secid);
+
+extern Oid seclabelMoveSecid(Oid dst_relid, Oid src_relid, Oid secid);
+extern bool seclabelCompareSecid(Oid relid1, Oid secid1,
+ Oid relid2, Oid secid2);
+
+extern Datum seclabelSysattOutput(Oid relOid, HeapTuple tuple);
+
+extern void seclabelRelationReclaim(Oid relOid);
+extern Datum seclabel_to_secid(PG_FUNCTION_ARGS);
+
+#endif /* PG_SECLABEL_H */
diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h
index 5cf90af..1ca2dd4 100644
--- a/src/include/catalog/pg_type_fn.h
+++ b/src/include/catalog/pg_type_fn.h
@@ -50,7 +50,8 @@ extern Oid TypeCreate(Oid newTypeOid,
char storage,
int32 typeMod,
int32 typNDims,
- bool typeNotNull);
+ bool typeNotNull,
+ Oid securityId);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h
index fb6f678..f642ded 100644
--- a/src/include/catalog/toasting.h
+++ b/src/include/catalog/toasting.h
@@ -58,5 +58,8 @@ DECLARE_TOAST(pg_shdescription, 2846, 2847);
DECLARE_TOAST(pg_db_role_setting, 2966, 2967);
#define PgDbRoleSettingToastTable 2966
#define PgDbRoleSettingToastIndex 2967
+DECLARE_TOAST(pg_seclabel, 3403, 3404);
+#define PgSecLabelToastTable 3403
+#define PgSecLabelToastIndex 3404
#endif /* TOASTING_H */
diff --git a/src/include/commands/alter.h b/src/include/commands/alter.h
index b1e04fb..6cd19ba 100644
--- a/src/include/commands/alter.h
+++ b/src/include/commands/alter.h
@@ -19,5 +19,6 @@
extern void ExecRenameStmt(RenameStmt *stmt);
extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt);
extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt);
+extern void ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt);
#endif /* ALTER_H */
diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h
index 542fc27..874e07a 100644
--- a/src/include/commands/dbcommands.h
+++ b/src/include/commands/dbcommands.h
@@ -58,6 +58,7 @@ extern void RenameDatabase(const char *oldname, const char *newname);
extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel);
extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt);
extern void AlterDatabaseOwner(const char *dbname, Oid newOwnerId);
+extern void AlterDatabaseSecLabel(const char *dbname, char *new_label);
extern Oid get_database_oid(const char *dbname);
extern char *get_database_name(Oid dbid);
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index e8dbe81..7fc4922 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -60,6 +60,8 @@ extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType);
extern void RenameFunction(List *name, List *argtypes, const char *newname);
extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId);
extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId);
+extern void AlterFunctionSecLabel(List *name, List *argtypes,
+ bool isagg, char *new_label);
extern void AlterFunction(AlterFunctionStmt *stmt);
extern void CreateCast(CreateCastStmt *stmt);
extern void DropCast(DropCastStmt *stmt);
diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h
index c914bd5..edd5abd 100644
--- a/src/include/commands/schemacmds.h
+++ b/src/include/commands/schemacmds.h
@@ -26,5 +26,6 @@ extern void RemoveSchemaById(Oid schemaOid);
extern void RenameSchema(const char *oldname, const char *newname);
extern void AlterSchemaOwner(const char *name, Oid newOwnerId);
extern void AlterSchemaOwner_oid(Oid schemaOid, Oid newOwnerId);
+extern void AlterSchemaSecLabel(const char *name, char *new_label);
#endif /* SCHEMACMDS_H */
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index f9269cc..a531f45 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -35,6 +35,13 @@ extern void AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
Oid oldNspOid, Oid newNspOid,
bool hasDependEntry);
+extern void AlterRelationSecLabel(RangeVar *relation, const char *attname,
+ ObjectType objtype, char *new_label);
+extern void AlterRelationSecLabelInternal(Oid relOid, Oid securityId,
+ int expected_parents);
+extern void AlterAttributeSecLabelInternal(Oid relOid, const char *attname,
+ Oid securityId, int expected_parents);
+
extern void CheckTableNotInUse(Relation rel, const char *stmt);
extern void ExecuteTruncate(TruncateStmt *stmt);
diff --git a/src/include/commands/tablespace.h b/src/include/commands/tablespace.h
index cf005ee..e9e8a55 100644
--- a/src/include/commands/tablespace.h
+++ b/src/include/commands/tablespace.h
@@ -44,6 +44,7 @@ extern void DropTableSpace(DropTableSpaceStmt *stmt);
extern void RenameTableSpace(const char *oldname, const char *newname);
extern void AlterTableSpaceOwner(const char *name, Oid newOwnerId);
extern void AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt);
+extern void AlterTableSpaceSecLabel(const char *tspaceName, char *newLabel);
extern void TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo);
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 8e5b610..f8c5872 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -43,5 +43,7 @@ extern void AlterTypeNamespace(List *names, const char *newschema);
extern void AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
bool isImplicitArray,
bool errorOnTableType);
+extern void AlterTypeSecLabel(List *name, char *new_label);
+extern void AlterTypeSecLabelInternal(Oid typeOid, Oid securityId);
#endif /* TYPECMDS_H */
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 820314c..7873b44 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -131,8 +131,8 @@ extern TupleHashEntry FindTupleHashEntry(TupleHashTable hashtable,
/*
* prototypes from functions in execJunk.c
*/
-extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid,
- TupleTableSlot *slot);
+extern JunkFilter *ExecInitJunkFilter(List *targetList,
+ bool hasoid, bool hassecid, TupleTableSlot *slot);
extern JunkFilter *ExecInitJunkFilterConversion(List *targetList,
TupleDesc cleanTupType,
TupleTableSlot *slot);
@@ -164,6 +164,7 @@ extern void InitResultRelInfo(ResultRelInfo *resultRelInfo,
int instrument_options);
extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid);
extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
+extern bool ExecContextForcesSecids(PlanState *planstate, bool *hassecid);
extern void ExecConstraints(ResultRelInfo *resultRelInfo,
TupleTableSlot *slot, EState *estate);
extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate,
@@ -234,8 +235,8 @@ extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate);
extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate);
extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate,
TupleDesc tupType);
-extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid);
-extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid);
+extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid, bool hassecid);
+extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hassecid);
extern TupleDesc ExecTypeFromExprList(List *exprList);
extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg);
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index d1a0dc1..e9de809 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -51,6 +51,7 @@ typedef struct FmgrInfo
bool fn_retset; /* function returns a set */
unsigned char fn_stats; /* collect stats if track_functions > this */
void *fn_extra; /* extra space for use by handler */
+ char *fn_seclabel; /* function is trusted procedure, or NULL */
MemoryContext fn_mcxt; /* memory context to store fn_extra in */
fmNodePtr fn_expr; /* expression parse tree for call, or NULL */
} FmgrInfo;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 1b5e476..37ffdfa 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -346,6 +346,7 @@ typedef enum NodeTag
T_AlterUserMappingStmt,
T_DropUserMappingStmt,
T_AlterTableSpaceOptionsStmt,
+ T_AlterSecLabelStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b591073..53609e8 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -731,6 +731,7 @@ typedef struct RangeTblEntry
Oid checkAsUser; /* if valid, check access as this role */
Bitmapset *selectedCols; /* columns needing SELECT permission */
Bitmapset *modifiedCols; /* columns needing INSERT/UPDATE permission */
+ uint32 rowlvPerms; /* permissions for row-level access controls */
} RangeTblEntry;
/*
@@ -1134,6 +1135,8 @@ typedef enum AlterTableType
AT_DropCluster, /* SET WITHOUT CLUSTER */
AT_AddOids, /* SET WITH OIDS */
AT_DropOids, /* SET WITHOUT OIDS */
+ AT_AddSecLabel, /* SET WITH SECURITY LABEL */
+ AT_DropSecLabel, /* SET WITHOUT SECURITY LABEL */
AT_SetTableSpace, /* SET TABLESPACE */
AT_SetRelOptions, /* SET (...) -- AM specific parameters */
AT_ResetRelOptions, /* RESET (...) -- AM specific parameters */
@@ -2073,6 +2076,20 @@ typedef struct AlterOwnerStmt
char *newowner; /* the new owner */
} AlterOwnerStmt;
+/* ----------------------
+ * Alter Security Label Statement
+ * ----------------------
+ */
+typedef struct AlterSecLabelStmt
+{
+ NodeTag type;
+ ObjectType objectType; /* OBJECT_TABLE, OBJECT_TYPE, etc */
+ RangeVar *relation; /* in case it's a table */
+ List *object; /* in case it's some other object */
+ List *objarg; /* argument types, if applicable */
+ char *addname; /* additional name if needed */
+ Value *secLabel; /* the new security label */
+} AlterSecLabelStmt;
/* ----------------------
* Create Rule Statement
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 49d4b6c..13c88a1 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -208,6 +208,7 @@ PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD)
PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD)
+PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD)
PG_KEYWORD("language", LANGUAGE, UNRESERVED_KEYWORD)
PG_KEYWORD("large", LARGE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("last", LAST_P, UNRESERVED_KEYWORD)
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 684aed4..2080c95 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -412,6 +412,9 @@
/* Define to 1 if you have the <security/pam_appl.h> header file. */
#undef HAVE_SECURITY_PAM_APPL_H
+/* Define to 1 if you enable SELinux support */
+#undef HAVE_SELINUX
+
/* Define to 1 if you have the `setproctitle' function. */
#undef HAVE_SETPROCTITLE
diff --git a/src/include/sepgsql/hooks.h b/src/include/sepgsql/hooks.h
new file mode 100644
index 0000000..b929e6d
--- /dev/null
+++ b/src/include/sepgsql/hooks.h
@@ -0,0 +1,293 @@
+/*
+ * sepgsql/hooks.h
+ *
+ * Header of SE-PostgreSQL Hooks
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#ifndef SEPGSQL_HOOKS_H
+#define SEPGSQL_HOOKS_H
+
+#include "executor/tuptable.h"
+#include "nodes/plannodes.h"
+#include "nodes/relation.h"
+#include "storage/fd.h"
+#include "utils/acl.h"
+#include "utils/rel.h"
+#include "utils/snapshot.h"
+
+/*
+ * misc.c
+ */
+extern char *sepgsql_get_client_label(void);
+extern char *sepgsql_set_client_label(char *new_label);
+extern void sepgsql_post_bootstraping(void);
+extern void sepgsql_initialize(void);
+extern bool sepgsql_worker_needed(void);
+extern void sepgsql_worker_main(void);
+
+extern Datum sepgsql_getcon(PG_FUNCTION_ARGS);
+
+/*
+ * database.c
+ */
+extern Oid sepgsql_database_create(const char *datName, Oid templateOid);
+extern void sepgsql_database_alter(Oid databaseOid);
+extern Oid sepgsql_database_relabel(Oid databaseOid, char *new_label);
+extern void sepgsql_database_drop(Oid databaseOid, bool cascade);
+extern void sepgsql_database_grant(Oid databaseOid);
+extern void sepgsql_database_comment(Oid databaseOid);
+extern void sepgsql_database_connect(Oid databaseOid);
+extern void sepgsql_database_reindex(Oid databaseOid);
+extern void sepgsql_database_getattr(Oid databaseOid);
+
+/*
+ * schema.c
+ */
+extern Oid sepgsql_schema_create(const char *nspName, bool is_temp);
+extern void sepgsql_schema_alter(Oid namespaceOid);
+extern Oid sepgsql_schema_relabel(Oid namespaceOid, char *new_label);
+extern void sepgsql_schema_drop(Oid namespaceOid, bool cascade);
+extern void sepgsql_schema_grant(Oid namespaceOid);
+extern bool sepgsql_schema_search(Oid namespaceOid, bool abort);
+extern void sepgsql_schema_comment(Oid namespaceOid);
+
+/*
+ * relation.c
+ */
+extern bool sepgsql_relation_perms(Oid relOid, AclMode aclmask,
+ Bitmapset *selectedCols,
+ Bitmapset *modifiedCols, bool abort);
+extern Oid *sepgsql_relation_create(const char *relName,
+ char relkind,
+ TupleDesc tupDesc,
+ Oid namespaceId,
+ List *supOids,
+ bool createAs);
+extern void sepgsql_relation_alter(Oid relationOid);
+extern void sepgsql_relation_alter_schema(Oid relationOid, Oid newSchema);
+extern void sepgsql_relation_alter_rename(Oid relationOid,
+ const char *newName);
+extern void sepgsql_relation_alter_inherit(Oid childOid, Oid parentOid);
+extern Oid sepgsql_relation_relabel(Oid relationOid, char *new_label);
+extern void sepgsql_relation_drop(Oid relationOid, bool cascade);
+extern void sepgsql_relation_getattr(Oid relationOid);
+extern void sepgsql_relation_grant(Oid relationOid);
+extern void sepgsql_relation_comment(Oid relationOid);
+extern bool sepgsql_relation_cluster(Oid relationOid, bool abort);
+extern void sepgsql_relation_truncate(Relation rel);
+extern void sepgsql_relation_lock(Relation rel);
+extern void sepgsql_relation_reindex(Oid relOid);
+extern void sepgsql_view_replace(Oid viewOid);
+extern void sepgsql_index_create(Oid relationOid, Oid namespaceOid);
+extern void sepgsql_index_reindex(Oid indexOid);
+extern void sepgsql_sequence_get_value(Oid sequenceOid);
+extern void sepgsql_sequence_next_value(Oid sequenceOid);
+extern void sepgsql_sequence_set_value(Oid sequenceOid);
+extern void sepgsql_rule_create(Oid relationOid,
+ const char *ruleName);
+extern void sepgsql_rule_drop(Oid relationOid,
+ const char *ruleName, bool cascade);
+extern void sepgsql_rule_comment(Oid relationOid, const char *ruleName);
+extern void sepgsql_trigger_create(Oid relationOid, const char *triggerName,
+ Oid constrrelid, Oid funcOid);
+extern void sepgsql_trigger_alter(Oid relOid, const char *tgName);
+extern void sepgsql_trigger_drop(Oid relOid, const char *tgName, bool cascade);
+extern void sepgsql_trigger_comment(Oid relOid, const char *tgName);
+extern void sepgsql_constraint_comment(Oid relOid, const char *constName);
+
+/*
+ * attribute.c
+ */
+extern Oid sepgsql_attribute_create(Oid relOid, const char *attName);
+extern void sepgsql_attribute_alter(Oid relOid, const char *attName);
+extern Oid sepgsql_attribute_relabel(Oid relOid, const char *attName,
+ char *new_label);
+extern void sepgsql_attribute_drop(Oid relOid, const char *attName, bool cascade);
+extern void sepgsql_attribute_grant(Oid relOid, AttrNumber attnum);
+extern void sepgsql_attribute_comment(Oid relOid, AttrNumber attnum);
+
+/*
+ * proc.c
+ */
+extern Oid sepgsql_proc_create(const char *proName, Oid replaced,
+ Oid namespaceOid, Oid langageOid);
+extern void sepgsql_proc_alter(Oid procOid);
+extern void sepgsql_proc_alter_rename(Oid procOid, const char *newName);
+extern void sepgsql_proc_alter_schema(Oid procOid, Oid newSchema);
+extern Oid sepgsql_proc_relabel(Oid procOid, char *new_label);
+extern void sepgsql_proc_drop(Oid procOid, bool cascade);
+extern void sepgsql_proc_grant(Oid procOid);
+extern void sepgsql_proc_comment(Oid procOid);
+extern void sepgsql_proc_execute(Oid procOid);
+extern bool sepgsql_proc_be_inlined(HeapTuple protup);
+extern char *sepgsql_proc_domtrans(HeapTuple protup, MemoryContext mcxt);
+extern Oid sepgsql_aggregate_create(const char *aggName, Oid namespaceId,
+ Oid transFunc, Oid finalFunc);
+extern void sepgsql_aggregate_execute(Oid aggOid);
+
+/*
+ * type.c
+ */
+extern Oid sepgsql_type_create(const char *typeName, Oid replaced,
+ Oid namespaceId, char typeType,
+ Oid inputFunc, Oid outputFunc,
+ Oid recvFunc, Oid sendFunc,
+ Oid modinFunc, Oid modoutFunc,
+ Oid analyzeFunc);
+extern void sepgsql_type_alter(Oid typeOid);
+extern void sepgsql_type_alter_rename(Oid typeOid, const char *newName);
+extern void sepgsql_type_alter_schema(Oid typeOid, Oid newSchema);
+extern Oid sepgsql_type_relabel(Oid typeOid, char *newLabel);
+extern void sepgsql_type_drop(Oid typeOid, bool cascade);
+extern void sepgsql_type_comment(Oid typeOid);
+extern Oid sepgsql_cast_create(Oid sourceTypeOid, Oid targetTypeOid,
+ char castMethod, Oid castFuncOid);
+extern void sepgsql_cast_drop(Oid srcTypeOid, Oid dstTypeOid, bool cascade);
+extern void sepgsql_cast_comment(Oid srcTypeOid, Oid dstTypeOid);
+
+/*
+ * tablespace.h
+ */
+extern Oid sepgsql_tablespace_create(const char *tablespaceName);
+extern void sepgsql_tablespace_alter(Oid tablespaceOid);
+extern Oid sepgsql_tablespace_relabel(Oid tablespaceOid, char *newLabel);
+extern void sepgsql_tablespace_drop(Oid tablespaceOid, bool cascade);
+extern void sepgsql_tablespace_grant(Oid tablespaceOid);
+extern void sepgsql_tablespace_getattr(Oid tablespaceOid);
+extern void sepgsql_tablespace_comment(Oid tablespaceOid);
+
+/*
+ * operator.h
+ */
+extern Oid sepgsql_operator_create(const char *operName, Oid replaced,
+ Oid namespaceId,
+ Oid codeFn, Oid restrictFn, Oid joinFn,
+ Oid commutatorOp, Oid negatorOp);
+extern void sepgsql_operator_alter(Oid operOid);
+extern Oid sepgsql_operator_relabel(Oid operOid, char *newLabel);
+extern void sepgsql_operator_drop(Oid operOid, bool cascade);
+extern void sepgsql_operator_comment(Oid operOid);
+
+extern Oid sepgsql_opclass_create(const char *opcName, Oid namespaceId,
+ Oid typeOid, Oid opfamilyOid, Oid storageOid);
+extern void sepgsql_opclass_alter(Oid opcOid);
+extern void sepgsql_opclass_alter_rename(Oid opcOid, const char *newName);
+extern void sepgsql_opclass_drop(Oid opcOid, bool cascade);
+extern void sepgsql_opclass_comment(Oid opcOid);
+
+extern Oid sepgsql_opfamily_create(const char *opfName,
+ Oid namespaceId, Oid amOid);
+extern void sepgsql_opfamily_alter(Oid opfOid, bool isDrop, Oid amOid,
+ List *operators, List *procedures);
+extern void sepgsql_opfamily_alter_rename(Oid opfOid, const char *newName);
+extern void sepgsql_opfamily_alter_owner(Oid opfOid, Oid newOwner);
+extern void sepgsql_opfamily_drop(Oid opfOid, bool cascade);
+extern void sepgsql_opfamily_comment(Oid opfOid);
+
+/*
+ * role.c
+ */
+extern Oid sepgsql_role_create(const char *roleName);
+extern void sepgsql_role_alter(Oid roleOid);
+extern Oid sepgsql_role_relabel(Oid roleOid, char *newLabel);
+extern void sepgsql_role_drop(Oid roleOid, bool cascade);
+extern void sepgsql_role_grant(Oid roleOid, bool is_grant, List *memberIds);
+extern void sepgsql_role_comment(Oid roleOid);
+
+/*
+ * blob.c
+ */
+extern Oid sepgsql_largeobject_create(Oid loid);
+extern void sepgsql_largeobject_alter(Oid loid);
+extern Oid sepgsql_largeobject_relabel(Oid loid, char *newLabel);
+extern void sepgsql_largeobject_drop(Oid loid, bool cascade);
+extern void sepgsql_largeobject_read(Oid loid, Snapshot snapshot);
+extern void sepgsql_largeobject_write(Oid loid, Snapshot snapshot);
+extern Oid sepgsql_largeobject_import(Oid loid, const char *filename);
+extern void sepgsql_largeobject_export(Oid loid, Snapshot snapshot,
+ const char *filename);
+extern void sepgsql_largeobject_grant(Oid loid);
+extern void sepgsql_largeobject_comment(Oid loid);
+
+/*
+ * conversion.c
+ */
+extern Oid sepgsql_conversion_create(const char *convName,
+ Oid namespaceId, Oid conversionFunc);
+extern void sepgsql_conversion_alter(Oid convOid);
+extern void sepgsql_conversion_alter_rename(Oid convOid, const char *newName);
+extern void sepgsql_conversion_drop(Oid convOid, bool cascade);
+extern void sepgsql_conversion_comment(Oid convOid);
+
+/*
+ * tsearch.c
+ */
+extern Oid sepgsql_ts_config_create(const char *confName, Oid namespaceId);
+extern void sepgsql_ts_config_alter(Oid confOid);
+extern void sepgsql_ts_config_alter_rename(Oid confOid, const char *newName);
+extern void sepgsql_ts_config_drop(Oid confOid, bool cascade);
+extern void sepgsql_ts_config_comment(Oid confOid);
+extern Oid sepgsql_ts_dict_create(const char *dictName, Oid namespaceId);
+extern void sepgsql_ts_dict_alter(Oid dictOid);
+extern void sepgsql_ts_dict_alter_rename(Oid dictOid, const char *newName);
+extern void sepgsql_ts_dict_drop(Oid dictOid, bool cascade);
+extern void sepgsql_ts_dict_comment(Oid dictOid);
+extern Oid sepgsql_ts_parser_create(const char *parseName, Oid namespaceId,
+ Oid startFunc, Oid tokenFunc, Oid endFunc,
+ Oid headlineFunc, Oid lextypeFunc);
+extern void sepgsql_ts_parser_alter_rename(Oid parseOid, const char *newName);
+extern void sepgsql_ts_parser_drop(Oid parseOid, bool cascade);
+extern void sepgsql_ts_parser_comment(Oid parseOid);
+extern Oid sepgsql_ts_template_create(const char *templateName,
+ Oid namespaceId,
+ Oid initFunc, Oid lexizeFunc);
+extern void sepgsql_ts_template_alter_rename(Oid templateOid,
+ const char *newName);
+extern void sepgsql_ts_template_drop(Oid templateOid, bool cascade);
+extern void sepgsql_ts_template_comment(Oid templateOid);
+
+/*
+ * fdw.c
+ */
+extern Oid sepgsql_fdw_create(const char *fdwName, Oid validatorFunc);
+extern void sepgsql_fdw_alter(Oid fdwOid, Oid newValidator);
+extern void sepgsql_fdw_drop(Oid fdwOid, bool cascade);
+extern void sepgsql_fdw_grant(Oid fdwOid);
+
+extern Oid sepgsql_fserver_create(const char *fservName, Oid fdwOid);
+extern void sepgsql_fserver_alter(Oid fservOid);
+extern void sepgsql_fserver_drop(Oid fservOid, bool cascade);
+extern void sepgsql_fserver_grant(Oid fservOid);
+
+extern Oid sepgsql_user_mapping_create(Oid mappedRoleId, Oid fservOid);
+extern void sepgsql_user_mapping_alter(Oid umapOid);
+extern void sepgsql_user_mapping_drop(Oid umapOid, bool cascade);
+
+extern void sepgsql_file_getattr(const char *filename);
+extern void sepgsql_file_read(const char *filename);
+extern void sepgsql_file_write(const char *filename, bool may_create);
+extern void sepgsql_file_append(const char *filename);
+extern void sepgsql_file_unlink(const char *filename);
+extern void sepgsql_file_rename(const char *oldpath, const char *newpath);
+extern void sepgsql_dir_list(const char *dirname);
+
+/*
+ * row-level access controls
+ */
+#define SEPGSQL_ROWLV_FILTER 1
+#define SEPGSQL_ROWLV_ABORT 2
+#define SEPGSQL_ROWLV_BYPASS 3
+
+extern int sepgsql_rowlv_get_mode(void);
+extern int sepgsql_rowlv_set_mode(int new_mode);
+extern bool sepgsql_rowlv_quals(Relation rel, uint32 required,
+ TupleTableSlot *slot, bool abort);
+extern bool sepgsql_rowlv_copyto(Relation rel, HeapTuple tuple);
+extern uint32 sepgsql_rowlv_permissions(RangeTblEntry *rte);
+
+extern void sepgsql_tuple_insert(Relation rel, HeapTuple tuple);
+extern void sepgsql_tuple_update(Relation rel, ItemPointer otid, HeapTuple newtup);
+
+#endif /* SEPGSQL_HOOKS_H */
diff --git a/src/include/sepgsql/sepgsql.h b/src/include/sepgsql/sepgsql.h
new file mode 100644
index 0000000..5fd7195
--- /dev/null
+++ b/src/include/sepgsql/sepgsql.h
@@ -0,0 +1,318 @@
+/*
+ * sepgsql/sepgsql.h
+ *
+ * Header of SE-PostgreSQL Internal
+ *
+ * Copyright (C) 2006-2010, NEC Corporation
+ * KaiGai Kohei <kaigai@ak.jp.nec.com>
+ */
+#ifndef SEPGSQL_H
+#define SEPGSQL_H
+
+#include "utils/snapshot.h"
+
+/* GUC : sepostgresql */
+extern int sepostgresql_mode;
+
+#define SEPGSQL_MODE_DEFAULT 1
+#define SEPGSQL_MODE_ENFORCING 2
+#define SEPGSQL_MODE_PERMISSIVE 3
+#define SEPGSQL_MODE_INTERNAL 4
+#define SEPGSQL_MODE_DISABLED 5
+
+/* GUC: sepostgresql_mcstrans */
+extern bool sepgsql_mcstrans;
+
+/* GUC: sepostgresql_debug_audit */
+extern bool sepgsql_debug_audit;
+
+/* Objject classes and permissions internally used */
+enum SepgsqlClasses
+{
+ SEPG_CLASS_PROCESS = 0,
+ SEPG_CLASS_FILE,
+ SEPG_CLASS_DIR,
+ SEPG_CLASS_LNK_FILE,
+ SEPG_CLASS_CHR_FILE,
+ SEPG_CLASS_BLK_FILE,
+ SEPG_CLASS_SOCK_FILE,
+ SEPG_CLASS_FIFO_FILE,
+ SEPG_CLASS_DB_DATABASE,
+ SEPG_CLASS_DB_SCHEMA,
+ SEPG_CLASS_DB_TABLE,
+ SEPG_CLASS_DB_VIEW,
+ SEPG_CLASS_DB_SEQUENCE,
+ SEPG_CLASS_DB_PROCEDURE,
+ SEPG_CLASS_DB_COLUMN,
+ SEPG_CLASS_DB_TUPLE,
+ SEPG_CLASS_DB_BLOB,
+ SEPG_CLASS_DB_LANGUAGE,
+ SEPG_CLASS_MAX,
+};
+
+#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_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)
+
+#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__IMPLEMENTE (1<<6)
+#define SEPG_DB_LANGUAGE__EXECUTE (1<<7)
+
+/*
+ * sepgsql_sid_t : alternative representation of security context
+ */
+typedef struct {
+ Oid relid;
+ Oid secid;
+} sepgsql_sid_t;
+
+struct av_decision;
+
+/*
+ * selinux.c
+ */
+extern bool sepgsql_is_enabled(void);
+extern bool sepgsql_get_enforce(void);
+extern const char *sepgsql_show_mode(void);
+
+extern Size sepgsql_shmem_size(void);
+
+extern void sepgsql_audit_log(bool denied,
+ char *scontext,
+ char *tcontext,
+ uint16 tclass,
+ uint32 audited,
+ const char *audit_name);
+extern void sepgsql_compute_avd(char *scontext,
+ char *tcontext,
+ uint16 tclass,
+ struct av_decision *avd);
+extern bool sepgsql_compute_perms(char *scontext,
+ char *tcontext,
+ uint16 tclass,
+ uint32 required,
+ const char *audit_name,
+ bool abort);
+extern char *sepgsql_compute_create(char *scontext,
+ char *tcontext,
+ uint16 tclass);
+extern bool sepgsql_client_perms(sepgsql_sid_t tsid,
+ uint16 tclass,
+ uint32 required,
+ const char *audit_name,
+ bool abort);
+extern sepgsql_sid_t sepgsql_client_create_secid(sepgsql_sid_t tsid,
+ uint16 tclass,
+ Oid nrelid);
+extern char *sepgsql_client_create_label(sepgsql_sid_t tsid,
+ uint16 tclass);
+extern void sepgsql_avc_worker_main(void);
+
+/*
+ * avc.c
+ */
+extern Size sepgsql_shmem_size(void);
+extern void sepgsql_avc_init(void);
+extern void sepgsql_avc_switch(const char *scontext);
+
+
+/*
+ * label.c
+ */
+extern sepgsql_sid_t sepgsql_move_secid(Oid dst_relid, sepgsql_sid_t ssid);
+
+extern sepgsql_sid_t sepgsql_get_default_database_secid(Oid templateOid);
+extern sepgsql_sid_t sepgsql_get_default_schema_secid(Oid databaseOid);
+extern sepgsql_sid_t sepgsql_get_default_table_secid(Oid namespaceOid);
+extern sepgsql_sid_t sepgsql_get_default_sequence_secid(Oid namespaceOid);
+extern sepgsql_sid_t sepgsql_get_default_view_secid(Oid namespaceOid);
+extern sepgsql_sid_t sepgsql_get_default_proc_secid(Oid namespaceOid);
+extern sepgsql_sid_t sepgsql_get_default_column_secid(Oid tableOid);
+extern sepgsql_sid_t sepgsql_get_default_tuple_secid(Oid tableOid);
+extern sepgsql_sid_t sepgsql_get_default_blob_secid(Oid databaseOid);
+extern Oid sepgsql_get_default_secid(Relation rel, HeapTuple tuple);
+
+extern void sepgsql_initial_labeling(void);
+
+extern char *sepgsql_mcstrans_out(char *label);
+extern char *sepgsql_mcstrans_in(char *label);
+extern char *sepgsql_rawlabel_out(char *label);
+extern char *sepgsql_rawlabel_in(char *label);
+
+/*
+ * sepgsql_(object)_common
+ */
+extern bool sepgsql_database_common(Oid datOid, uint32 required, bool abort);
+extern bool sepgsql_schema_common(Oid nspOid, uint32 required, bool abort);
+extern bool sepgsql_relation_common(Oid relOid, uint32 required, bool abort);
+extern bool sepgsql_attribute_common(Oid relOid, AttrNumber attno,
+ uint32 required, bool abort);
+extern bool sepgsql_proc_common(Oid procOid, uint32 required, bool abort);
+extern bool sepgsql_type_common(Oid typeOid, uint32 required, bool abort);
+extern bool sepgsql_cast_common(Oid srcTypeOid, Oid dstTypeOid,
+ uint32 required, bool abort);
+extern bool sepgsql_tablespace_common(Oid tspaceOid, uint32 required, bool abort);
+extern bool sepgsql_operator_common(Oid operOid, uint32 required, bool abort);
+extern bool sepgsql_opclass_common(Oid opcOid, uint32 required, bool abort);
+extern bool sepgsql_opfamily_common(Oid opfOid, uint32 required, bool abort);
+extern bool sepgsql_role_common(Oid roleOid, uint32 required, bool abort);
+extern bool sepgsql_largeobejct_common(Oid loid, Snapshot snapshot,
+ uint32 required, bool abort);
+extern bool sepgsql_conversion_common(Oid convOid, uint32 required, bool abort);
+extern bool sepgsql_largeobject_common(Oid loid, Snapshot snapshot,
+ uint32 required, bool abort);
+extern bool sepgsql_ts_config_common(Oid confOid, uint32 required, bool abort);
+extern bool sepgsql_ts_dict_common(Oid dictOid, uint32 required, bool abort);
+extern bool sepgsql_ts_parser_common(Oid parseOid, uint32 required, bool abort);
+extern bool sepgsql_ts_template_common(Oid templateOid, uint32 required, bool abort);
+extern bool sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort);
+extern bool sepgsql_fserver_common(Oid fservOid, uint32 required, bool abort);
+extern bool sepgsql_user_mapping_common(Oid umapOid, uint32 required, bool abort);
+
+#endif /* SEPGSQL_H */
diff --git a/src/include/storage/large_object.h b/src/include/storage/large_object.h
index 43a61f3..6385b5c 100644
--- a/src/include/storage/large_object.h
+++ b/src/include/storage/large_object.h
@@ -70,7 +70,7 @@ typedef struct LargeObjectDesc
/* inversion stuff in inv_api.c */
extern void close_lo_relation(bool isCommit);
-extern Oid inv_create(Oid lobjId);
+extern Oid inv_create(Oid lobjId, Oid securityId);
extern LargeObjectDesc *inv_open(Oid lobjId, int flags, MemoryContext mcxt);
extern void inv_close(LargeObjectDesc *obj_desc);
extern int inv_drop(Oid lobjId);
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 4eece8b..4176eac 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -70,6 +70,7 @@ typedef enum LWLockId
RelationMappingLock,
AsyncCtlLock,
AsyncQueueLock,
+ SepgsqlAvcLock,
/* Individual lock IDs end here */
FirstBufMappingLock,
FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS,
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index c22c65a..5b94283 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -166,6 +166,7 @@ extern bool log_btree_build_stats;
extern PGDLLIMPORT bool check_function_bodies;
extern bool default_with_oids;
+extern bool default_with_secids;
extern bool SQL_inheritance;
extern int log_min_error_statement;
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 2f19e5c..6744782 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -101,6 +101,8 @@ extern bool SearchSysCacheExists(int cacheId,
Datum key1, Datum key2, Datum key3, Datum key4);
extern Oid GetSysCacheOid(int cacheId,
Datum key1, Datum key2, Datum key3, Datum key4);
+extern Oid GetSysCacheSecid(int cacheId,
+ Datum key1, Datum key2, Datum key3, Datum key4);
extern HeapTuple SearchSysCacheAttName(Oid relid, const char *attname);
extern HeapTuple SearchSysCacheCopyAttName(Oid relid, const char *attname);
@@ -154,6 +156,15 @@ extern struct catclist *SearchSysCacheList(int cacheId, int nkeys,
#define GetSysCacheOid4(cacheId, key1, key2, key3, key4) \
GetSysCacheOid(cacheId, key1, key2, key3, key4)
+#define GetSysCacheSecid1(cacheId, key1) \
+ GetSysCacheSecid(cacheId, key1, 0, 0, 0)
+#define GetSysCacheSecid2(cacheId, key1, key2) \
+ GetSysCacheSecid(cacheId, key1, key2, 0, 0)
+#define GetSysCacheSecid3(cacheId, key1, key2, key3) \
+ GetSysCacheSecid(cacheId, key1, key2, key3, 0)
+#define GetSysCacheSecid4(cacheId, key1, key2, key3, key4) \
+ GetSysCacheSecid(cacheId, key1, key2, key3, key4)
+
#define SearchSysCacheList1(cacheId, key1) \
SearchSysCacheList(cacheId, 1, key1, 0, 0, 0)
#define SearchSysCacheList2(cacheId, key1, key2) \
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 656ea73..2ce4e94 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -1936,7 +1936,7 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars)
row = palloc0(sizeof(PLpgSQL_row));
row->dtype = PLPGSQL_DTYPE_ROW;
- row->rowtupdesc = CreateTemplateTupleDesc(numvars, false);
+ row->rowtupdesc = CreateTemplateTupleDesc(numvars, false, false);
row->nfields = numvars;
row->fieldnames = palloc(numvars * sizeof(char *));
row->varnos = palloc(numvars * sizeof(int));
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 1d9e110..9596b0b 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -114,6 +114,7 @@ SELECT relname, relhasindex
pg_pltemplate | t
pg_proc | t
pg_rewrite | t
+ pg_seclabel | t
pg_shdepend | t
pg_shdescription | t
pg_statistic | t
@@ -153,7 +154,7 @@ SELECT relname, relhasindex
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
-(142 rows)
+(143 rows)
--
-- another sanity check: every system catalog that has OIDs should have