17591 lines
516 KiB
Diff
17591 lines
516 KiB
Diff
diff --git a/configure b/configure
|
|
index 98a55b5..ddbdb07 100755
|
|
--- a/configure
|
|
+++ b/configure
|
|
@@ -710,6 +710,8 @@ with_libxml
|
|
with_libxslt
|
|
with_system_tzdata
|
|
with_zlib
|
|
+enable_selinux
|
|
+SELINUX_LIBS
|
|
GREP
|
|
EGREP
|
|
ELF_SYS
|
|
@@ -1378,6 +1380,7 @@ Optional Features:
|
|
--enable-thread-safety make client libraries thread-safe
|
|
--enable-thread-safety-force
|
|
force thread-safety despite thread test failure
|
|
+ --enable-selinux enable to build with SELinux support
|
|
--disable-float4-byval disable float4 passed by value
|
|
--disable-float8-byval disable float8 passed by value
|
|
--disable-largefile omit support for large files
|
|
@@ -5532,6 +5535,183 @@ fi
|
|
|
|
|
|
#
|
|
+# SELinux support
|
|
+#
|
|
+
|
|
+pgac_args="$pgac_args enable_selinux"
|
|
+
|
|
+# Check whether --enable-selinux was given.
|
|
+if test "${enable_selinux+set}" = set; then
|
|
+ enableval=$enable_selinux;
|
|
+ case $enableval in
|
|
+ yes)
|
|
+ :
|
|
+ ;;
|
|
+ no)
|
|
+ :
|
|
+ ;;
|
|
+ *)
|
|
+ { { echo "$as_me:$LINENO: error: no argument expected for --enable-selinux option" >&5
|
|
+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
|
|
+ SELINUX_LIBS="-lselinux"
|
|
+ { echo "$as_me:$LINENO: checking for avc_netlink_loop in -lselinux" >&5
|
|
+echo $ECHO_N "checking for avc_netlink_loop in -lselinux... $ECHO_C" >&6; }
|
|
+if test "${ac_cv_lib_selinux_avc_netlink_loop+set}" = set; then
|
|
+ echo $ECHO_N "(cached) $ECHO_C" >&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_netlink_loop ();
|
|
+int
|
|
+main ()
|
|
+{
|
|
+return avc_netlink_loop ();
|
|
+ ;
|
|
+ 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 "echo \"\$as_me:$LINENO: $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
|
|
+ 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 &&
|
|
+ $as_test_x conftest$ac_exeext; then
|
|
+ ac_cv_lib_selinux_avc_netlink_loop=yes
|
|
+else
|
|
+ echo "$as_me: failed program was:" >&5
|
|
+sed 's/^/| /' conftest.$ac_ext >&5
|
|
+
|
|
+ ac_cv_lib_selinux_avc_netlink_loop=no
|
|
+fi
|
|
+
|
|
+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
|
|
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_avc_netlink_loop" >&5
|
|
+echo "${ECHO_T}$ac_cv_lib_selinux_avc_netlink_loop" >&6; }
|
|
+if test $ac_cv_lib_selinux_avc_netlink_loop = yes; then
|
|
+
|
|
+cat >>confdefs.h <<_ACEOF
|
|
+#define HAVE_SELINUX 1
|
|
+_ACEOF
|
|
+
|
|
+else
|
|
+ { { echo "$as_me:$LINENO: error: \"--enable-selinux requires libselinux.\"" >&5
|
|
+echo "$as_me: error: \"--enable-selinux requires libselinux.\"" >&2;}
|
|
+ { (exit 1); exit 1; }; }
|
|
+fi
|
|
+
|
|
+ { echo "$as_me:$LINENO: checking for audit_open in -laudit" >&5
|
|
+echo $ECHO_N "checking for audit_open in -laudit... $ECHO_C" >&6; }
|
|
+if test "${ac_cv_lib_audit_audit_open+set}" = set; then
|
|
+ echo $ECHO_N "(cached) $ECHO_C" >&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 "echo \"\$as_me:$LINENO: $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
|
|
+ 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 &&
|
|
+ $as_test_x conftest$ac_exeext; then
|
|
+ ac_cv_lib_audit_audit_open=yes
|
|
+else
|
|
+ echo "$as_me: failed program was:" >&5
|
|
+sed 's/^/| /' conftest.$ac_ext >&5
|
|
+
|
|
+ ac_cv_lib_audit_audit_open=no
|
|
+fi
|
|
+
|
|
+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
|
|
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_audit_audit_open" >&5
|
|
+echo "${ECHO_T}$ac_cv_lib_audit_audit_open" >&6; }
|
|
+if test $ac_cv_lib_audit_audit_open = yes; then
|
|
+ cat >>confdefs.h <<_ACEOF
|
|
+#define HAVE_LIBAUDIT 1
|
|
+_ACEOF
|
|
+
|
|
+ SELINUX_LIBS="$SELINUX_LIBS -laudit"
|
|
+fi
|
|
+
|
|
+
|
|
+
|
|
+fi
|
|
+
|
|
+#
|
|
# Elf
|
|
#
|
|
|
|
@@ -28137,11 +28317,11 @@ with_libxml!$with_libxml$ac_delim
|
|
with_libxslt!$with_libxslt$ac_delim
|
|
with_system_tzdata!$with_system_tzdata$ac_delim
|
|
with_zlib!$with_zlib$ac_delim
|
|
+enable_selinux!$enable_selinux$ac_delim
|
|
+SELINUX_LIBS!$SELINUX_LIBS$ac_delim
|
|
GREP!$GREP$ac_delim
|
|
EGREP!$EGREP$ac_delim
|
|
ELF_SYS!$ELF_SYS$ac_delim
|
|
-LDFLAGS_SL!$LDFLAGS_SL$ac_delim
|
|
-LD!$LD$ac_delim
|
|
_ACEOF
|
|
|
|
if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then
|
|
@@ -28183,6 +28363,8 @@ _ACEOF
|
|
ac_delim='%!_!# '
|
|
for ac_last_try in false false false false false :; do
|
|
cat >conf$$subs.sed <<_ACEOF
|
|
+LDFLAGS_SL!$LDFLAGS_SL$ac_delim
|
|
+LD!$LD$ac_delim
|
|
with_gnu_ld!$with_gnu_ld$ac_delim
|
|
ld_R_works!$ld_R_works$ac_delim
|
|
RANLIB!$RANLIB$ac_delim
|
|
@@ -28245,7 +28427,7 @@ vpath_build!$vpath_build$ac_delim
|
|
LTLIBOBJS!$LTLIBOBJS$ac_delim
|
|
_ACEOF
|
|
|
|
- if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 60; then
|
|
+ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 62; then
|
|
break
|
|
elif $ac_last_try; then
|
|
{ { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
|
|
diff --git a/configure.in b/configure.in
|
|
index 1a68732..16f6eab 100644
|
|
--- a/configure.in
|
|
+++ b/configure.in
|
|
@@ -764,6 +764,24 @@ PGAC_ARG_BOOL(with, zlib, yes,
|
|
AC_SUBST(with_zlib)
|
|
|
|
#
|
|
+# SELinux support
|
|
+#
|
|
+PGAC_ARG_BOOL(enable, selinux, no,
|
|
+ [enable to build with SELinux support])
|
|
+if test "$enable_selinux" = yes; then
|
|
+ SELINUX_LIBS="-lselinux"
|
|
+ AC_CHECK_LIB(selinux, avc_netlink_loop,
|
|
+ AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1,
|
|
+ [SE-PostgreSQL feature is enabled]),
|
|
+ AC_MSG_ERROR("--enable-selinux requires libselinux."))
|
|
+ AC_CHECK_LIB(audit, audit_open,
|
|
+ AC_DEFINE_UNQUOTED(HAVE_LIBAUDIT, 1)
|
|
+ SELINUX_LIBS="$SELINUX_LIBS -laudit")
|
|
+ AC_SUBST(enable_selinux)
|
|
+ AC_SUBST(SELINUX_LIBS)
|
|
+fi
|
|
+
|
|
+#
|
|
# Elf
|
|
#
|
|
|
|
diff --git a/src/Makefile.global.in b/src/Makefile.global.in
|
|
index 6244e8a..39e2493 100644
|
|
--- a/src/Makefile.global.in
|
|
+++ b/src/Makefile.global.in
|
|
@@ -165,6 +165,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@
|
|
@@ -184,6 +185,8 @@ TCL_INCLUDE_SPEC = @TCL_INCLUDE_SPEC@
|
|
TCL_SHARED_BUILD = @TCL_SHARED_BUILD@
|
|
TCL_SHLIB_LD_LIBS = @TCL_SHLIB_LD_LIBS@
|
|
|
|
+SELINUX_LIBS = @SELINUX_LIBS@
|
|
+
|
|
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
|
|
PTHREAD_LIBS = @PTHREAD_LIBS@
|
|
|
|
diff --git a/src/backend/Makefile b/src/backend/Makefile
|
|
index baa45e1..bc13bb9 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 rewrite \
|
|
- storage tcop tsearch utils $(top_builddir)/src/timezone
|
|
+ security storage tcop tsearch utils $(top_builddir)/src/timezone
|
|
|
|
include $(srcdir)/common.mk
|
|
|
|
@@ -40,6 +40,9 @@ 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 Libraries
|
|
+LIBS += $(SELINUX_LIBS)
|
|
+
|
|
##########################################################################
|
|
|
|
all: submake-libpgport postgres $(POSTGRES_IMP)
|
|
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
|
|
index a86716e..7c4d1f6 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_security.h"
|
|
#include "executor/tuptable.h"
|
|
|
|
|
|
@@ -287,6 +288,7 @@ heap_attisnull(HeapTuple tup, int attnum)
|
|
case MinCommandIdAttributeNumber:
|
|
case MaxTransactionIdAttributeNumber:
|
|
case MaxCommandIdAttributeNumber:
|
|
+ case SecurityAttributeNumber:
|
|
/* these are never null */
|
|
break;
|
|
|
|
@@ -599,6 +601,9 @@ heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
|
|
case TableOidAttributeNumber:
|
|
result = ObjectIdGetDatum(tup->t_tableOid);
|
|
break;
|
|
+ case SecurityAttributeNumber:
|
|
+ result = securitySysattSecLabelOut(tup->t_tableOid, tup);
|
|
+ break;
|
|
default:
|
|
elog(ERROR, "invalid attnum: %d", attnum);
|
|
result = 0; /* keep compiler quiet */
|
|
@@ -722,6 +727,8 @@ heap_form_tuple(TupleDesc tupleDescriptor,
|
|
|
|
if (tupleDescriptor->tdhasoid)
|
|
len += sizeof(Oid);
|
|
+ if (tupleDescriptor->tdhassecid)
|
|
+ len += sizeof(Oid);
|
|
|
|
hoff = len = MAXALIGN(len); /* align user data safely */
|
|
|
|
@@ -753,6 +760,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,
|
|
@@ -864,6 +873,8 @@ heap_modify_tuple(HeapTuple tuple,
|
|
newTuple->t_tableOid = tuple->t_tableOid;
|
|
if (tupleDesc->tdhasoid)
|
|
HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple));
|
|
+ if (HeapTupleHasSecid(newTuple))
|
|
+ HeapTupleSetSecid(newTuple, HeapTupleGetSecid(tuple));
|
|
|
|
return newTuple;
|
|
}
|
|
@@ -1474,6 +1485,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 */
|
|
|
|
@@ -1495,6 +1508,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 8582a7d..7df1631 100644
|
|
--- a/src/backend/access/common/tupdesc.c
|
|
+++ b/src/backend/access/common/tupdesc.c
|
|
@@ -88,6 +88,7 @@ CreateTemplateTupleDesc(int natts, bool hasoid)
|
|
desc->tdtypeid = RECORDOID;
|
|
desc->tdtypmod = -1;
|
|
desc->tdhasoid = hasoid;
|
|
+ desc->tdhassecid = false;
|
|
desc->tdrefcount = -1; /* assume not reference-counted */
|
|
|
|
return desc;
|
|
@@ -121,6 +122,7 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
|
|
desc->tdtypeid = RECORDOID;
|
|
desc->tdtypmod = -1;
|
|
desc->tdhasoid = hasoid;
|
|
+ desc->tdhassecid = false;
|
|
desc->tdrefcount = -1; /* assume not reference-counted */
|
|
|
|
return desc;
|
|
@@ -150,6 +152,7 @@ CreateTupleDescCopy(TupleDesc tupdesc)
|
|
|
|
desc->tdtypeid = tupdesc->tdtypeid;
|
|
desc->tdtypmod = tupdesc->tdtypmod;
|
|
+ desc->tdhassecid = tupdesc->tdhassecid;
|
|
|
|
return desc;
|
|
}
|
|
@@ -208,6 +211,7 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc)
|
|
|
|
desc->tdtypeid = tupdesc->tdtypeid;
|
|
desc->tdtypmod = tupdesc->tdtypmod;
|
|
+ desc->tdhassecid = tupdesc->tdhassecid;
|
|
|
|
return desc;
|
|
}
|
|
@@ -314,6 +318,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++)
|
|
{
|
|
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
|
|
index b0a911e..697946b 100644
|
|
--- a/src/backend/access/heap/heapam.c
|
|
+++ b/src/backend/access/heap/heapam.c
|
|
@@ -54,6 +54,7 @@
|
|
#include "catalog/namespace.h"
|
|
#include "miscadmin.h"
|
|
#include "pgstat.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "storage/freespace.h"
|
|
#include "storage/lmgr.h"
|
|
@@ -2016,6 +2017,12 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
|
|
Oid
|
|
simple_heap_insert(Relation relation, HeapTuple tup)
|
|
{
|
|
+ /*
|
|
+ * SELinux assigns default security label for the tuple,
|
|
+ * but does not check permissions to the internal operations.
|
|
+ */
|
|
+ sepgsqlHeapTupleInsert(relation, tup, true);
|
|
+
|
|
return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL);
|
|
}
|
|
|
|
@@ -2558,6 +2565,11 @@ l2:
|
|
Assert(!(newtup->t_data->t_infomask & HEAP_HASOID));
|
|
}
|
|
|
|
+ /* Preserve SecurityId, if not changed */
|
|
+ if (HeapTupleHasSecid(newtup) &&
|
|
+ !OidIsValid(HeapTupleGetSecid(newtup)))
|
|
+ HeapTupleSetSecid(newtup, HeapTupleGetSecid(&oldtup));
|
|
+
|
|
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);
|
|
@@ -3499,6 +3511,8 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
|
|
memcpy((char *) htup + htup->t_hoff,
|
|
(char *) tuple->t_data + tuple->t_data->t_hoff,
|
|
newlen);
|
|
+ if (HeapTupleHeaderGetSecid(htup) != HeapTupleGetSecid(tuple))
|
|
+ HeapTupleHeaderSetSecid(htup, HeapTupleGetSecid(tuple));
|
|
|
|
MarkBufferDirty(buffer);
|
|
|
|
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
|
|
index 7b2ebe5..6179e52 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 */
|
|
@@ -864,6 +866,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,
|
|
@@ -1015,6 +1019,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,
|
|
@@ -1213,6 +1219,12 @@ toast_save_datum(Relation rel, Datum value, int options)
|
|
memcpy(VARDATA(&chunk_data), data_p, chunk_size);
|
|
toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull);
|
|
|
|
+ /*
|
|
+ * NOTE: SE-PostgreSQL does not assign any security label
|
|
+ * for tuples within the TOASTVALUE relation, so we omit
|
|
+ * to put sepgsqlHeapTupleInsert() hook here.
|
|
+ */
|
|
+
|
|
heap_insert(toastrel, toasttup, mycid, options, NULL);
|
|
|
|
/*
|
|
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
|
|
index abf1ca1..e6db40a 100644
|
|
--- a/src/backend/access/transam/xact.c
|
|
+++ b/src/backend/access/transam/xact.c
|
|
@@ -36,6 +36,8 @@
|
|
#include "libpq/be-fsstubs.h"
|
|
#include "miscadmin.h"
|
|
#include "pgstat.h"
|
|
+#include "security/rowlevel.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "storage/fd.h"
|
|
#include "storage/lmgr.h"
|
|
@@ -140,6 +142,8 @@ typedef struct TransactionStateData
|
|
Oid prevUser; /* previous CurrentUserId setting */
|
|
int prevSecContext; /* previous SecurityRestrictionContext */
|
|
bool prevXactReadOnly; /* entry-time xact r/o state */
|
|
+ char *prevSecLabel; /* previous security label of client */
|
|
+ int prevRowlv; /* previous Row-level control behavior */
|
|
struct TransactionStateData *parent; /* back link to parent */
|
|
} TransactionStateData;
|
|
|
|
@@ -168,6 +172,8 @@ static TransactionStateData TopTransactionStateData = {
|
|
InvalidOid, /* previous CurrentUserId setting */
|
|
0, /* previous SecurityRestrictionContext */
|
|
false, /* entry-time xact r/o state */
|
|
+ NULL, /* previous security label of client */
|
|
+ ROWLV_FILTER_MODE, /* previous Row-level control behavior */
|
|
NULL /* link to parent state block */
|
|
};
|
|
|
|
@@ -1527,6 +1533,9 @@ StartTransaction(void)
|
|
/* SecurityRestrictionContext should never be set outside a transaction */
|
|
Assert(s->prevSecContext == 0);
|
|
|
|
+ s->prevSecLabel = sepgsqlGetClientLabel();
|
|
+ s->prevRowlv = rowlvGetPerformingMode();
|
|
+
|
|
/*
|
|
* initialize other subsystems for new transaction
|
|
*/
|
|
@@ -2031,6 +2040,12 @@ AbortTransaction(void)
|
|
SetUserIdAndSecContext(s->prevUser, s->prevSecContext);
|
|
|
|
/*
|
|
+ * Reset SELinux features
|
|
+ */
|
|
+ sepgsqlSetClientLabel(s->prevSecLabel);
|
|
+ rowlvSetPerformingMode(s->prevRowlv);
|
|
+
|
|
+ /*
|
|
* do abort processing
|
|
*/
|
|
AfterTriggerEndXact(false);
|
|
@@ -3874,6 +3889,12 @@ AbortSubTransaction(void)
|
|
SetUserIdAndSecContext(s->prevUser, s->prevSecContext);
|
|
|
|
/*
|
|
+ * Reset SELinux features
|
|
+ */
|
|
+ sepgsqlSetClientLabel(s->prevSecLabel);
|
|
+ rowlvSetPerformingMode(s->prevRowlv);
|
|
+
|
|
+ /*
|
|
* We can skip all this stuff if the subxact failed before creating a
|
|
* ResourceOwner...
|
|
*/
|
|
@@ -4015,6 +4036,8 @@ PushTransaction(void)
|
|
s->blockState = TBLOCK_SUBBEGIN;
|
|
GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext);
|
|
s->prevXactReadOnly = XactReadOnly;
|
|
+ s->prevSecLabel = sepgsqlGetClientLabel();
|
|
+ s->prevRowlv = rowlvGetPerformingMode();
|
|
|
|
CurrentTransactionState = s;
|
|
|
|
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
|
|
index 8d3f6c2..8700483 100644
|
|
--- a/src/backend/bootstrap/bootparse.y
|
|
+++ b/src/backend/bootstrap/bootparse.y
|
|
@@ -42,6 +42,7 @@
|
|
#include "nodes/pg_list.h"
|
|
#include "nodes/primnodes.h"
|
|
#include "rewrite/prs2lock.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/block.h"
|
|
#include "storage/fd.h"
|
|
#include "storage/ipc.h"
|
|
@@ -211,6 +212,13 @@ Boot_CreateStmt:
|
|
else
|
|
{
|
|
Oid id;
|
|
+ Oid *secLabels =
|
|
+ sepgsql_relation_create(LexIDStr($5),
|
|
+ RELKIND_RELATION,
|
|
+ tupdesc,
|
|
+ PG_CATALOG_NAMESPACE,
|
|
+ NULL, NIL,
|
|
+ false, false);
|
|
|
|
id = heap_create_with_catalog(LexIDStr($5),
|
|
PG_CATALOG_NAMESPACE,
|
|
@@ -225,7 +233,8 @@ Boot_CreateStmt:
|
|
0,
|
|
ONCOMMIT_NOOP,
|
|
(Datum) 0,
|
|
- true);
|
|
+ true,
|
|
+ secLabels);
|
|
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 13d5bcb..0de0c85 100644
|
|
--- a/src/backend/bootstrap/bootstrap.c
|
|
+++ b/src/backend/bootstrap/bootstrap.c
|
|
@@ -26,12 +26,14 @@
|
|
#include "access/xact.h"
|
|
#include "bootstrap/bootstrap.h"
|
|
#include "catalog/index.h"
|
|
+#include "catalog/pg_security.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "libpq/pqsignal.h"
|
|
#include "miscadmin.h"
|
|
#include "nodes/makefuncs.h"
|
|
#include "postmaster/bgwriter.h"
|
|
#include "postmaster/walwriter.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "storage/ipc.h"
|
|
#include "storage/proc.h"
|
|
@@ -338,6 +340,11 @@ AuxiliaryProcessMain(int argc, char *argv[])
|
|
case WalWriterProcess:
|
|
statmsg = "wal writer process";
|
|
break;
|
|
+#ifdef HAVE_SELINUX
|
|
+ case SelinuxReceiverProcess:
|
|
+ statmsg = "selinux netlink receiver";
|
|
+ break;
|
|
+#endif
|
|
default:
|
|
statmsg = "??? process";
|
|
break;
|
|
@@ -430,6 +437,12 @@ AuxiliaryProcessMain(int argc, char *argv[])
|
|
WalWriterMain();
|
|
proc_exit(1); /* should never return */
|
|
|
|
+#ifdef HAVE_SELINUX
|
|
+ case SelinuxReceiverProcess:
|
|
+ sepgsqlReceiverMain();
|
|
+ proc_exit(1); /* should nener return */
|
|
+#endif
|
|
+
|
|
default:
|
|
elog(PANIC, "unrecognized process type: %d", auxType);
|
|
proc_exit(1);
|
|
@@ -497,6 +510,11 @@ BootstrapModeMain(void)
|
|
*/
|
|
boot_yyparse();
|
|
|
|
+ /*
|
|
+ * SELinux initial labeling
|
|
+ */
|
|
+ sepgsqlPostBootstrapingMode();
|
|
+
|
|
/* Perform a checkpoint to ensure everything's down to disk */
|
|
SetProcessingMode(NormalProcessing);
|
|
CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE);
|
|
@@ -794,6 +812,8 @@ InsertOneTuple(Oid objectid)
|
|
tupDesc = CreateTupleDesc(numattr,
|
|
RelationGetForm(boot_reldesc)->relhasoids,
|
|
attrtypes);
|
|
+ tupDesc->tdhassecid = RelationGetDescr(boot_reldesc)->tdhassecid;
|
|
+
|
|
tuple = heap_form_tuple(tupDesc, values, Nulls);
|
|
if (objectid != (Oid) 0)
|
|
HeapTupleSetOid(tuple, objectid);
|
|
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
|
|
index 3644ca6..632e3ed 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_shdepend.o pg_type.o storage.o toasting.o
|
|
+ pg_security.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_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_security.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 \
|
|
toasting.h indexing.h \
|
|
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
|
|
index 1be417c..6139e4b 100644
|
|
--- a/src/backend/catalog/aclchk.c
|
|
+++ b/src/backend/catalog/aclchk.c
|
|
@@ -37,6 +37,7 @@
|
|
#include "catalog/pg_operator.h"
|
|
#include "catalog/pg_opfamily.h"
|
|
#include "catalog/pg_proc.h"
|
|
+#include "catalog/pg_security.h"
|
|
#include "catalog/pg_tablespace.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "catalog/pg_ts_config.h"
|
|
@@ -45,6 +46,7 @@
|
|
#include "foreign/foreign.h"
|
|
#include "miscadmin.h"
|
|
#include "parser/parse_func.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/fmgroids.h"
|
|
#include "utils/lsyscache.h"
|
|
@@ -699,6 +701,12 @@ expand_all_col_privileges(Oid table_oid, Form_pg_class classForm,
|
|
if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids)
|
|
continue;
|
|
|
|
+ /* Skip OID column, if it doesn't exist */
|
|
+ if (curr_att == SecurityAttributeNumber &&
|
|
+ (classForm->relkind != RELKIND_RELATION ||
|
|
+ table_oid == SecurityRelationId))
|
|
+ continue;
|
|
+
|
|
/* Views don't have any system columns at all */
|
|
if (classForm->relkind == RELKIND_VIEW && curr_att < 0)
|
|
continue;
|
|
@@ -801,6 +809,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.
|
|
@@ -1056,6 +1066,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.
|
|
@@ -1244,6 +1256,8 @@ ExecGrant_Database(InternalGrant *istmt)
|
|
datId, grantorId, ACL_KIND_DATABASE,
|
|
NameStr(pg_database_tuple->datname),
|
|
0, NULL);
|
|
+ /* SELinux permission checks */
|
|
+ sepgsql_database_grant(datId);
|
|
|
|
/*
|
|
* Generate new ACL.
|
|
@@ -1362,6 +1376,8 @@ ExecGrant_Fdw(InternalGrant *istmt)
|
|
fdwid, grantorId, ACL_KIND_FDW,
|
|
NameStr(pg_fdw_tuple->fdwname),
|
|
0, NULL);
|
|
+ /* SELinux permission checks */
|
|
+ sepgsql_fdw_grant(fdwid);
|
|
|
|
/*
|
|
* Generate new ACL.
|
|
@@ -1481,6 +1497,8 @@ ExecGrant_ForeignServer(InternalGrant *istmt)
|
|
srvid, grantorId, ACL_KIND_FOREIGN_SERVER,
|
|
NameStr(pg_server_tuple->srvname),
|
|
0, NULL);
|
|
+ /* SELinux checks */
|
|
+ sepgsql_foreign_server_grant(srvid);
|
|
|
|
/*
|
|
* Generate new ACL.
|
|
@@ -1599,6 +1617,8 @@ ExecGrant_Function(InternalGrant *istmt)
|
|
funcId, grantorId, ACL_KIND_PROC,
|
|
NameStr(pg_proc_tuple->proname),
|
|
0, NULL);
|
|
+ /* SELinux: db_procedure:{setattr} */
|
|
+ sepgsql_proc_grant(funcId);
|
|
|
|
/*
|
|
* Generate new ACL.
|
|
@@ -1723,6 +1743,8 @@ ExecGrant_Language(InternalGrant *istmt)
|
|
langId, grantorId, ACL_KIND_LANGUAGE,
|
|
NameStr(pg_language_tuple->lanname),
|
|
0, NULL);
|
|
+ /* SELinux checks */
|
|
+ sepgsql_language_grant(langId);
|
|
|
|
/*
|
|
* Generate new ACL.
|
|
@@ -1974,6 +1996,9 @@ ExecGrant_Namespace(InternalGrant *istmt)
|
|
NameStr(pg_namespace_tuple->nspname),
|
|
0, NULL);
|
|
|
|
+ /* SELinux: db_schema:{setattr} */
|
|
+ sepgsql_schema_grant(nspid);
|
|
+
|
|
/*
|
|
* Generate new ACL.
|
|
*
|
|
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
|
|
index d168694..efb4333 100644
|
|
--- a/src/backend/catalog/catalog.c
|
|
+++ b/src/backend/catalog/catalog.c
|
|
@@ -31,6 +31,7 @@
|
|
#include "catalog/pg_database.h"
|
|
#include "catalog/pg_namespace.h"
|
|
#include "catalog/pg_pltemplate.h"
|
|
+#include "catalog/pg_security.h"
|
|
#include "catalog/pg_shdepend.h"
|
|
#include "catalog/pg_shdescription.h"
|
|
#include "catalog/pg_tablespace.h"
|
|
@@ -304,6 +305,7 @@ IsSharedRelation(Oid relationId)
|
|
relationId == AuthMemRelationId ||
|
|
relationId == DatabaseRelationId ||
|
|
relationId == PLTemplateRelationId ||
|
|
+ relationId == SecurityRelationId ||
|
|
relationId == SharedDescriptionRelationId ||
|
|
relationId == SharedDependRelationId ||
|
|
relationId == TableSpaceRelationId)
|
|
@@ -316,6 +318,8 @@ IsSharedRelation(Oid relationId)
|
|
relationId == DatabaseNameIndexId ||
|
|
relationId == DatabaseOidIndexId ||
|
|
relationId == PLTemplateNameIndexId ||
|
|
+ relationId == SecuritySecidIndexId ||
|
|
+ relationId == SecuritySecattrIndexId ||
|
|
relationId == SharedDescriptionObjIndexId ||
|
|
relationId == SharedDependDependerIndexId ||
|
|
relationId == SharedDependReferenceIndexId ||
|
|
@@ -327,6 +331,8 @@ IsSharedRelation(Oid relationId)
|
|
relationId == PgAuthidToastIndex ||
|
|
relationId == PgDatabaseToastTable ||
|
|
relationId == PgDatabaseToastIndex ||
|
|
+ relationId == PgSecurityToastTable ||
|
|
+ relationId == PgSecurityToastIndex ||
|
|
relationId == PgShdescriptionToastTable ||
|
|
relationId == PgShdescriptionToastIndex)
|
|
return true;
|
|
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
|
|
index 416c149..4e447f0 100644
|
|
--- a/src/backend/catalog/dependency.c
|
|
+++ b/src/backend/catalog/dependency.c
|
|
@@ -64,6 +64,7 @@
|
|
#include "nodes/nodeFuncs.h"
|
|
#include "parser/parsetree.h"
|
|
#include "rewrite/rewriteRemove.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/lmgr.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/fmgroids.h"
|
|
@@ -162,7 +163,8 @@ static void reportDependentObjects(const ObjectAddresses *targetObjects,
|
|
DropBehavior behavior,
|
|
int msglevel,
|
|
const ObjectAddress *origObject);
|
|
-static void deleteOneObject(const ObjectAddress *object, Relation depRel);
|
|
+static void deleteOneObject(const ObjectAddress *object,
|
|
+ Relation depRel, bool permission);
|
|
static void doDeletion(const ObjectAddress *object);
|
|
static void AcquireDeletionLock(const ObjectAddress *object);
|
|
static void ReleaseDeletionLock(const ObjectAddress *object);
|
|
@@ -194,9 +196,9 @@ static void getOpFamilyDescription(StringInfo buffer, Oid opfid);
|
|
* are variants on the same theme; if you change anything here you'll likely
|
|
* need to fix them too.
|
|
*/
|
|
-void
|
|
-performDeletion(const ObjectAddress *object,
|
|
- DropBehavior behavior)
|
|
+static void
|
|
+performDeletionInternal(const ObjectAddress *object,
|
|
+ DropBehavior behavior, bool permission)
|
|
{
|
|
Relation depRel;
|
|
ObjectAddresses *targetObjects;
|
|
@@ -242,7 +244,7 @@ performDeletion(const ObjectAddress *object,
|
|
{
|
|
ObjectAddress *thisobj = targetObjects->refs + i;
|
|
|
|
- deleteOneObject(thisobj, depRel);
|
|
+ deleteOneObject(thisobj, depRel, permission);
|
|
}
|
|
|
|
/* And clean up */
|
|
@@ -251,6 +253,18 @@ performDeletion(const ObjectAddress *object,
|
|
heap_close(depRel, RowExclusiveLock);
|
|
}
|
|
|
|
+void
|
|
+performDeletion(const ObjectAddress *object, DropBehavior behavior)
|
|
+{
|
|
+ performDeletionInternal(object, behavior, true);
|
|
+}
|
|
+
|
|
+void
|
|
+performDeletionNoPerms(const ObjectAddress *object, DropBehavior behavior)
|
|
+{
|
|
+ performDeletionInternal(object, behavior, false);
|
|
+}
|
|
+
|
|
/*
|
|
* performMultipleDeletions: Similar to performDeletion, but act on multiple
|
|
* objects at once.
|
|
@@ -324,7 +338,8 @@ performMultipleDeletions(const ObjectAddresses *objects,
|
|
{
|
|
ObjectAddress *thisobj = targetObjects->refs + i;
|
|
|
|
- deleteOneObject(thisobj, depRel);
|
|
+ /* currently, all the caller path need permission checks */
|
|
+ deleteOneObject(thisobj, depRel, true);
|
|
}
|
|
|
|
/* And clean up */
|
|
@@ -395,7 +410,7 @@ deleteWhatDependsOn(const ObjectAddress *object,
|
|
if (thisextra->flags & DEPFLAG_ORIGINAL)
|
|
continue;
|
|
|
|
- deleteOneObject(thisobj, depRel);
|
|
+ deleteOneObject(thisobj, depRel, false);
|
|
}
|
|
|
|
/* And clean up */
|
|
@@ -945,13 +960,17 @@ reportDependentObjects(const ObjectAddresses *targetObjects,
|
|
* depRel is the already-open pg_depend relation.
|
|
*/
|
|
static void
|
|
-deleteOneObject(const ObjectAddress *object, Relation depRel)
|
|
+deleteOneObject(const ObjectAddress *object, Relation depRel, bool permission)
|
|
{
|
|
ScanKeyData key[3];
|
|
int nkeys;
|
|
SysScanDesc scan;
|
|
HeapTuple tup;
|
|
|
|
+ /* SELinux checks db_xxx:{drop}, if necessary */
|
|
+ if (permission)
|
|
+ sepgsql_sysobj_drop(object);
|
|
+
|
|
/*
|
|
* First remove any pg_depend records that link from this object to
|
|
* others. (Any records linking to this object should be gone already.)
|
|
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
|
|
index 7557400..b5161c7 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_security.h"
|
|
#include "catalog/pg_statistic.h"
|
|
#include "catalog/pg_tablespace.h"
|
|
#include "catalog/pg_type.h"
|
|
@@ -56,6 +57,7 @@
|
|
#include "parser/parse_coerce.h"
|
|
#include "parser/parse_expr.h"
|
|
#include "parser/parse_relation.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "storage/freespace.h"
|
|
#include "storage/smgr.h"
|
|
@@ -74,7 +76,8 @@ static void AddNewRelationTuple(Relation pg_class_desc,
|
|
Oid new_rel_oid, Oid new_type_oid,
|
|
Oid relowner,
|
|
char relkind,
|
|
- Datum reloptions);
|
|
+ Datum reloptions,
|
|
+ Oid *secLabels);
|
|
static Oid AddNewRelationType(const char *typeName,
|
|
Oid typeNamespace,
|
|
Oid new_rel_oid,
|
|
@@ -158,7 +161,16 @@ static FormData_pg_attribute a7 = {
|
|
true, 'p', 'i', true, false, false, true, 0, {0}
|
|
};
|
|
|
|
-static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7};
|
|
+/*
|
|
+ * System columns for enhanced security features
|
|
+ */
|
|
+static FormData_pg_attribute a8 = {
|
|
+ 0, {SecurityAttributeName}, TEXTOID, 0, -1,
|
|
+ SecurityAttributeNumber, 0, -1, -1,
|
|
+ false, 'x', 'i', true, false, false, true, 0, {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.
|
|
@@ -198,6 +210,17 @@ SystemAttributeByName(const char *attname, bool relhasoids)
|
|
return NULL;
|
|
}
|
|
|
|
+/*
|
|
+ * If the given attribute number is writable, returns true.
|
|
+ */
|
|
+bool
|
|
+SystemAttributeIsWritable(AttrNumber attnum)
|
|
+{
|
|
+ if (attnum == SecurityAttributeNumber)
|
|
+ return true;
|
|
+
|
|
+ return false;
|
|
+}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* XXX END OF UGLY HARD CODED BADNESS XXX
|
|
@@ -293,6 +316,11 @@ heap_create(const char *relname,
|
|
relid,
|
|
reltablespace,
|
|
shared_relation);
|
|
+ /*
|
|
+ * Does the relation have security attribute?
|
|
+ */
|
|
+ RelationGetDescr(rel)->tdhassecid
|
|
+ = securityTupleDescHasSecid(relid, relkind);
|
|
|
|
/*
|
|
* Have the storage manager create the relation's disk file, if needed.
|
|
@@ -487,7 +515,8 @@ CheckAttributeType(const char *attname, Oid atttypid)
|
|
void
|
|
InsertPgAttributeTuple(Relation pg_attribute_rel,
|
|
Form_pg_attribute new_attribute,
|
|
- CatalogIndexState indstate)
|
|
+ CatalogIndexState indstate,
|
|
+ Oid new_att_secid)
|
|
{
|
|
Datum values[Natts_pg_attribute];
|
|
bool nulls[Natts_pg_attribute];
|
|
@@ -520,6 +549,9 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
|
|
|
|
tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls);
|
|
|
|
+ if (HeapTupleHasSecid(tup))
|
|
+ HeapTupleSetSecid(tup, new_att_secid);
|
|
+
|
|
/* finally insert the new tuple, update the indexes, and clean up */
|
|
simple_heap_insert(pg_attribute_rel, tup);
|
|
|
|
@@ -543,13 +575,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 new_att_secid;
|
|
ObjectAddress myself,
|
|
referenced;
|
|
|
|
@@ -573,7 +607,11 @@ AddNewAttributeTuples(Oid new_rel_oid,
|
|
attr->attstattarget = -1;
|
|
attr->attcacheoff = -1;
|
|
|
|
- InsertPgAttributeTuple(rel, attr, indstate);
|
|
+ /* Security label of the column */
|
|
+ new_att_secid = (!secLabels ? InvalidOid
|
|
+ : secLabels[i - FirstLowInvalidHeapAttributeNumber]);
|
|
+
|
|
+ InsertPgAttributeTuple(rel, attr, indstate, new_att_secid);
|
|
|
|
/* Add dependency info */
|
|
myself.classId = RelationRelationId;
|
|
@@ -601,6 +639,12 @@ AddNewAttributeTuples(Oid new_rel_oid,
|
|
SysAtt[i]->attnum == ObjectIdAttributeNumber)
|
|
continue;
|
|
|
|
+ /* skip Secid where appropriate */
|
|
+ if (SysAtt[i]->attnum == SecurityAttributeNumber &&
|
|
+ (relkind != RELKIND_RELATION ||
|
|
+ new_rel_oid == SecurityRelationId))
|
|
+ continue;
|
|
+
|
|
memcpy(&attStruct, (char *) SysAtt[i], sizeof(FormData_pg_attribute));
|
|
|
|
/* Fill in the correct relation OID in the copied tuple */
|
|
@@ -613,7 +657,11 @@ AddNewAttributeTuples(Oid new_rel_oid,
|
|
attStruct.attinhcount = oidinhcount;
|
|
}
|
|
|
|
- InsertPgAttributeTuple(rel, &attStruct, indstate);
|
|
+ /* Security label of the system column */
|
|
+ new_att_secid = (!secLabels ? InvalidOid
|
|
+ : secLabels[SysAtt[i]->attnum - FirstLowInvalidHeapAttributeNumber]);
|
|
+
|
|
+ InsertPgAttributeTuple(rel, &attStruct, indstate, new_att_secid);
|
|
}
|
|
}
|
|
|
|
@@ -641,7 +689,8 @@ void
|
|
InsertPgClassTuple(Relation pg_class_desc,
|
|
Relation new_rel_desc,
|
|
Oid new_rel_oid,
|
|
- Datum reloptions)
|
|
+ Datum reloptions,
|
|
+ Oid new_rel_secid)
|
|
{
|
|
Form_pg_class rd_rel = new_rel_desc->rd_rel;
|
|
Datum values[Natts_pg_class];
|
|
@@ -690,6 +739,9 @@ InsertPgClassTuple(Relation pg_class_desc,
|
|
*/
|
|
HeapTupleSetOid(tup, new_rel_oid);
|
|
|
|
+ if (HeapTupleHasSecid(tup))
|
|
+ HeapTupleSetSecid(tup, new_rel_secid);
|
|
+
|
|
/* finally insert the new tuple, update the indexes, and clean up */
|
|
simple_heap_insert(pg_class_desc, tup);
|
|
|
|
@@ -712,9 +764,11 @@ AddNewRelationTuple(Relation pg_class_desc,
|
|
Oid new_type_oid,
|
|
Oid relowner,
|
|
char relkind,
|
|
- Datum reloptions)
|
|
+ Datum reloptions,
|
|
+ Oid *secLabels)
|
|
{
|
|
Form_pg_class new_rel_reltup;
|
|
+ Oid new_rel_secid = InvalidOid;
|
|
|
|
/*
|
|
* first we update some of the information in our uncataloged relation's
|
|
@@ -771,8 +825,12 @@ AddNewRelationTuple(Relation pg_class_desc,
|
|
|
|
new_rel_desc->rd_att->tdtypeid = new_type_oid;
|
|
|
|
+ if (secLabels)
|
|
+ new_rel_secid = secLabels[0];
|
|
+
|
|
/* Now build and insert the tuple */
|
|
- InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions);
|
|
+ InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid,
|
|
+ reloptions, new_rel_secid);
|
|
}
|
|
|
|
|
|
@@ -843,7 +901,8 @@ heap_create_with_catalog(const char *relname,
|
|
int oidinhcount,
|
|
OnCommitAction oncommit,
|
|
Datum reloptions,
|
|
- bool allow_system_table_mods)
|
|
+ bool allow_system_table_mods,
|
|
+ Oid *secLabels)
|
|
{
|
|
Relation pg_class_desc;
|
|
Relation new_rel_desc;
|
|
@@ -1019,13 +1078,14 @@ heap_create_with_catalog(const char *relname,
|
|
new_type_oid,
|
|
ownerid,
|
|
relkind,
|
|
- 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
|
|
@@ -1484,6 +1544,11 @@ heap_drop_with_catalog(Oid relid)
|
|
* delete relation tuple
|
|
*/
|
|
DeleteRelationTuple(relid);
|
|
+
|
|
+ /*
|
|
+ * delete orphan pg_security entries
|
|
+ */
|
|
+ securityReclaimOnDropTable(relid);
|
|
}
|
|
|
|
|
|
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
|
|
index ad679ea..828d51a 100644
|
|
--- a/src/backend/catalog/index.c
|
|
+++ b/src/backend/catalog/index.c
|
|
@@ -48,6 +48,7 @@
|
|
#include "nodes/nodeFuncs.h"
|
|
#include "optimizer/clauses.h"
|
|
#include "optimizer/var.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "storage/lmgr.h"
|
|
#include "storage/procarray.h"
|
|
@@ -352,7 +353,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, InvalidOid);
|
|
}
|
|
|
|
CatalogCloseIndexes(indstate);
|
|
@@ -653,7 +655,7 @@ index_create(Oid heapRelationId,
|
|
*/
|
|
InsertPgClassTuple(pg_class, indexRelation,
|
|
RelationGetRelid(indexRelation),
|
|
- reloptions);
|
|
+ reloptions, InvalidOid);
|
|
|
|
/* done with pg_class */
|
|
heap_close(pg_class, RowExclusiveLock);
|
|
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
|
|
index 2b0cb35..64de050 100644
|
|
--- a/src/backend/catalog/namespace.c
|
|
+++ b/src/backend/catalog/namespace.c
|
|
@@ -39,6 +39,7 @@
|
|
#include "miscadmin.h"
|
|
#include "nodes/makefuncs.h"
|
|
#include "parser/parse_func.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/backendid.h"
|
|
#include "storage/ipc.h"
|
|
#include "utils/acl.h"
|
|
@@ -2105,7 +2106,10 @@ LookupExplicitNamespace(const char *nspname)
|
|
if (strcmp(nspname, "pg_temp") == 0)
|
|
{
|
|
if (OidIsValid(myTempNamespace))
|
|
+ {
|
|
+ sepgsql_schema_search(myTempNamespace, true);
|
|
return myTempNamespace;
|
|
+ }
|
|
|
|
/*
|
|
* Since this is used only for looking up existing objects, there is
|
|
@@ -2127,6 +2131,7 @@ LookupExplicitNamespace(const char *nspname)
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
nspname);
|
|
+ sepgsql_schema_search(namespaceId, true);
|
|
|
|
return namespaceId;
|
|
}
|
|
@@ -2722,7 +2727,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);
|
|
}
|
|
}
|
|
@@ -2731,7 +2737,8 @@ recomputeNamespacePath(void)
|
|
/* pg_temp --- substitute temp namespace, if any */
|
|
if (OidIsValid(myTempNamespace))
|
|
{
|
|
- if (!list_member_oid(oidlist, myTempNamespace))
|
|
+ if (!list_member_oid(oidlist, myTempNamespace) &&
|
|
+ sepgsql_schema_search(myTempNamespace, false))
|
|
oidlist = lappend_oid(oidlist, myTempNamespace);
|
|
}
|
|
else
|
|
@@ -2750,7 +2757,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);
|
|
}
|
|
}
|
|
@@ -2816,6 +2824,7 @@ InitTempTableNamespace(void)
|
|
char namespaceName[NAMEDATALEN];
|
|
Oid namespaceId;
|
|
Oid toastspaceId;
|
|
+ Oid nspsecid;
|
|
|
|
Assert(!OidIsValid(myTempNamespace));
|
|
|
|
@@ -2836,6 +2845,9 @@ InitTempTableNamespace(void)
|
|
errmsg("permission denied to create temporary tables in database \"%s\"",
|
|
get_database_name(MyDatabaseId))));
|
|
|
|
+ /* SELinux checks permission to create temp schema */
|
|
+ nspsecid = sepgsql_schema_create(namespaceName, true, NULL);
|
|
+
|
|
snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId);
|
|
|
|
namespaceId = GetSysCacheOid(NAMESPACENAME,
|
|
@@ -2851,7 +2863,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,
|
|
+ nspsecid);
|
|
/* Advance command counter to make namespace visible */
|
|
CommandCounterIncrement();
|
|
}
|
|
@@ -2877,7 +2891,9 @@ InitTempTableNamespace(void)
|
|
0, 0, 0);
|
|
if (!OidIsValid(toastspaceId))
|
|
{
|
|
- toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID);
|
|
+ toastspaceId = NamespaceCreate(namespaceName,
|
|
+ BOOTSTRAP_SUPERUSERID,
|
|
+ nspsecid);
|
|
/* Advance command counter to make namespace visible */
|
|
CommandCounterIncrement();
|
|
}
|
|
@@ -3030,6 +3046,13 @@ RemoveTempRelations(Oid tempNamespaceId)
|
|
object.objectId = tempNamespaceId;
|
|
object.objectSubId = 0;
|
|
|
|
+ /*
|
|
+ * TODO:
|
|
+ * SELinux should not check db_xxx:{drop} permission during cleaning
|
|
+ * up all the temporary objects. It may be necessary a bool argument
|
|
+ * to control MAC permission check on deleteOneObject() called from
|
|
+ * deleteWhatDependsOn() and so on.
|
|
+ */
|
|
deleteWhatDependsOn(&object, false);
|
|
}
|
|
|
|
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
|
|
index 845322e..cc1c59a 100644
|
|
--- a/src/backend/catalog/pg_aggregate.c
|
|
+++ b/src/backend/catalog/pg_aggregate.c
|
|
@@ -231,7 +231,8 @@ AggregateCreate(const char *aggName,
|
|
NIL, /* parameterDefaults */
|
|
PointerGetDatum(NULL), /* proconfig */
|
|
1, /* procost */
|
|
- 0); /* prorows */
|
|
+ 0, /* prorows */
|
|
+ NULL); /* proseclabel*/
|
|
|
|
/*
|
|
* Okay to create the pg_aggregate entry.
|
|
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
|
|
index d4a8183..30f2604 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, Oid consecid, bool def)
|
|
{
|
|
int i;
|
|
Relation rel;
|
|
@@ -104,6 +104,8 @@ ConversionCreate(const char *conname, Oid connamespace,
|
|
values[Anum_pg_conversion_condefault - 1] = BoolGetDatum(def);
|
|
|
|
tup = heap_form_tuple(tupDesc, values, nulls);
|
|
+ if (HeapTupleHasSecid(tup))
|
|
+ HeapTupleSetSecid(tup, consecid);
|
|
|
|
/* insert a new tuple */
|
|
oid = simple_heap_insert(rel, tup);
|
|
diff --git a/src/backend/catalog/pg_largeobject.c b/src/backend/catalog/pg_largeobject.c
|
|
index 54d992f..67faa30 100644
|
|
--- a/src/backend/catalog/pg_largeobject.c
|
|
+++ b/src/backend/catalog/pg_largeobject.c
|
|
@@ -25,6 +25,7 @@
|
|
#include "catalog/pg_largeobject_metadata.h"
|
|
#include "catalog/toasting.h"
|
|
#include "miscadmin.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/fmgroids.h"
|
|
@@ -40,7 +41,7 @@
|
|
* will appear to exist with size 0.
|
|
*/
|
|
Oid
|
|
-LargeObjectCreate(Oid loid)
|
|
+LargeObjectCreate(Oid loid, Oid secid)
|
|
{
|
|
Relation pg_lo_meta;
|
|
HeapTuple ntup;
|
|
@@ -65,6 +66,8 @@ LargeObjectCreate(Oid loid)
|
|
values, nulls);
|
|
if (OidIsValid(loid))
|
|
HeapTupleSetOid(ntup, loid);
|
|
+ if (HeapTupleHasSecid(ntup))
|
|
+ HeapTupleSetSecid(ntup, secid);
|
|
|
|
loid_new = simple_heap_insert(pg_lo_meta, ntup);
|
|
Assert(!OidIsValid(loid) || loid == loid_new);
|
|
@@ -205,6 +208,9 @@ LargeObjectAlterOwner(Oid loid, Oid newOwnerId)
|
|
|
|
/* Must be able to become new owner */
|
|
check_is_member_of_role(GetUserId(), newOwnerId);
|
|
+
|
|
+ /* SELinux: db_blob:{setattr} */
|
|
+ sepgsql_largeobject_alter(loid);
|
|
}
|
|
|
|
memset(values, 0, sizeof(values));
|
|
diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c
|
|
index 8fac0b6..62802a9 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 nspsecid)
|
|
{
|
|
Relation nspdesc;
|
|
HeapTuple tup;
|
|
@@ -66,6 +66,8 @@ NamespaceCreate(const char *nspName, Oid ownerId)
|
|
tupDesc = nspdesc->rd_att;
|
|
|
|
tup = heap_form_tuple(tupDesc, values, nulls);
|
|
+ if (HeapTupleHasSecid(tup))
|
|
+ HeapTupleSetSecid(tup, nspsecid);
|
|
|
|
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 af307b7..6fe025c 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 "security/sepgsql.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 secid;
|
|
int i;
|
|
HeapTuple tup;
|
|
Datum values[Natts_pg_operator];
|
|
@@ -220,6 +222,10 @@ OperatorShellMake(const char *operatorName,
|
|
errmsg("\"%s\" is not a valid operator name",
|
|
operatorName)));
|
|
|
|
+ /* SELinux permission check */
|
|
+ secid = sepgsql_operator_create(operatorName, InvalidOid,
|
|
+ operatorNamespace,
|
|
+ InvalidOid, InvalidOid, InvalidOid);
|
|
/*
|
|
* initialize our *nulls and *values arrays
|
|
*/
|
|
@@ -260,6 +266,8 @@ OperatorShellMake(const char *operatorName,
|
|
* create a new operator tuple
|
|
*/
|
|
tup = heap_form_tuple(tupDesc, values, nulls);
|
|
+ if (HeapTupleHasSecid(tup) && OidIsValid(secid))
|
|
+ HeapTupleSetSecid(tup, secid);
|
|
|
|
/*
|
|
* insert our "shell" operator tuple
|
|
@@ -347,6 +355,7 @@ OperatorCreate(const char *operatorName,
|
|
bool selfCommutator = false;
|
|
NameData oname;
|
|
TupleDesc tupDesc;
|
|
+ Oid secid;
|
|
int i;
|
|
|
|
/*
|
|
@@ -476,6 +485,10 @@ OperatorCreate(const char *operatorName,
|
|
else
|
|
negatorId = InvalidOid;
|
|
|
|
+ /* SELinux permission checks */
|
|
+ secid = sepgsql_operator_create(operatorName, operatorObjectId,
|
|
+ operatorNamespace,
|
|
+ procedureId, restrictionId, joinId);
|
|
/*
|
|
* set up values in the operator tuple
|
|
*/
|
|
@@ -523,6 +536,8 @@ OperatorCreate(const char *operatorName,
|
|
values,
|
|
nulls,
|
|
replaces);
|
|
+ if (HeapTupleHasSecid(tup))
|
|
+ HeapTupleSetSecid(tup, secid);
|
|
|
|
simple_heap_update(pg_operator_desc, &tup->t_self, tup);
|
|
}
|
|
@@ -530,6 +545,8 @@ OperatorCreate(const char *operatorName,
|
|
{
|
|
tupDesc = pg_operator_desc->rd_att;
|
|
tup = heap_form_tuple(tupDesc, values, nulls);
|
|
+ if (HeapTupleHasSecid(tup))
|
|
+ HeapTupleSetSecid(tup, secid);
|
|
|
|
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 2980d2b..55e66e5 100644
|
|
--- a/src/backend/catalog/pg_proc.c
|
|
+++ b/src/backend/catalog/pg_proc.c
|
|
@@ -29,6 +29,7 @@
|
|
#include "miscadmin.h"
|
|
#include "nodes/nodeFuncs.h"
|
|
#include "parser/parse_type.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "tcop/pquery.h"
|
|
#include "tcop/tcopprot.h"
|
|
#include "utils/acl.h"
|
|
@@ -78,7 +79,8 @@ ProcedureCreate(const char *procedureName,
|
|
List *parameterDefaults,
|
|
Datum proconfig,
|
|
float4 procost,
|
|
- float4 prorows)
|
|
+ float4 prorows,
|
|
+ Node *proseclabel)
|
|
{
|
|
Oid retval;
|
|
int parameterCount;
|
|
@@ -97,6 +99,7 @@ ProcedureCreate(const char *procedureName,
|
|
Datum values[Natts_pg_proc];
|
|
bool replaces[Natts_pg_proc];
|
|
Oid relid;
|
|
+ Oid prosecid = InvalidOid;
|
|
NameData procname;
|
|
TupleDesc tupDesc;
|
|
bool is_update;
|
|
@@ -344,6 +347,11 @@ ProcedureCreate(const char *procedureName,
|
|
ObjectIdGetDatum(procNamespace),
|
|
0);
|
|
|
|
+ /* Check permission to create/replace a function */
|
|
+ prosecid = sepgsql_proc_create(procedureName, oldtup,
|
|
+ procNamespace, languageObjectId,
|
|
+ (DefElem *)proseclabel);
|
|
+
|
|
if (HeapTupleIsValid(oldtup))
|
|
{
|
|
/* There is one; okay to replace it? */
|
|
@@ -481,6 +489,8 @@ ProcedureCreate(const char *procedureName,
|
|
|
|
/* Okay, do it... */
|
|
tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces);
|
|
+ if (HeapTupleHasSecid(tup))
|
|
+ HeapTupleSetSecid(tup, prosecid);
|
|
simple_heap_update(rel, &tup->t_self, tup);
|
|
|
|
ReleaseSysCache(oldtup);
|
|
@@ -490,6 +500,8 @@ ProcedureCreate(const char *procedureName,
|
|
{
|
|
/* Creating a new procedure */
|
|
tup = heap_form_tuple(tupDesc, values, nulls);
|
|
+ if (HeapTupleHasSecid(tup))
|
|
+ HeapTupleSetSecid(tup, prosecid);
|
|
simple_heap_insert(rel, tup);
|
|
is_update = false;
|
|
}
|
|
diff --git a/src/backend/catalog/pg_security.c b/src/backend/catalog/pg_security.c
|
|
new file mode 100644
|
|
index 0000000..0db05e2
|
|
--- /dev/null
|
|
+++ b/src/backend/catalog/pg_security.c
|
|
@@ -0,0 +1,483 @@
|
|
+/*
|
|
+ * src/backend/catalog/pg_security.c
|
|
+ * routines to support security label management
|
|
+ *
|
|
+ * Portions Copyright (c) 1996-2009, 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/xact.h"
|
|
+#include "catalog/catalog.h"
|
|
+#include "catalog/indexing.h"
|
|
+#include "catalog/pg_proc.h"
|
|
+#include "catalog/pg_security.h"
|
|
+#include "catalog/pg_type.h"
|
|
+#include "executor/spi.h"
|
|
+#include "miscadmin.h"
|
|
+#include "security/rowlevel.h"
|
|
+#include "security/sepgsql.h"
|
|
+#include "utils/builtins.h"
|
|
+#include "utils/fmgroids.h"
|
|
+#include "utils/memutils.h"
|
|
+#include "utils/rel.h"
|
|
+#include "utils/lsyscache.h"
|
|
+#include "utils/syscache.h"
|
|
+#include "utils/tqual.h"
|
|
+
|
|
+bool
|
|
+securityTupleDescHasSecid(Oid relid, char relkind)
|
|
+{
|
|
+ return sepgsqlTupleDescHasSecid(relid, relkind);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * securityOnCreateDatabase
|
|
+ * copies all the entries refered by source database
|
|
+ */
|
|
+void
|
|
+securityOnCreateDatabase(Oid src_datid, Oid dst_datid)
|
|
+{
|
|
+ Relation rel;
|
|
+ ScanKeyData keys[1];
|
|
+ SysScanDesc scan;
|
|
+ HeapTuple oldtup, newtup;
|
|
+ Datum values[Natts_pg_security];
|
|
+ bool nulls[Natts_pg_security];
|
|
+ bool replaces[Natts_pg_security];
|
|
+
|
|
+ /* Scan all entries with pg_security.datid = src_datid */
|
|
+ ScanKeyInit(&keys[0],
|
|
+ Anum_pg_security_datid,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(src_datid));
|
|
+
|
|
+ rel = heap_open(SecurityRelationId, RowExclusiveLock);
|
|
+
|
|
+ scan = systable_beginscan(rel, SecuritySecidIndexId, true,
|
|
+ SnapshotNow, 1, keys);
|
|
+
|
|
+ /* pg_security.datid shall be replaced */
|
|
+ memset(values, 0, sizeof(values));
|
|
+ memset(nulls, false, sizeof(nulls));
|
|
+ memset(replaces, false, sizeof(replaces));
|
|
+
|
|
+ values[Anum_pg_security_datid - 1] = ObjectIdGetDatum(dst_datid);
|
|
+ replaces[Anum_pg_security_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);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * securityOnDropDatabase
|
|
+ * drops all the entries refered by dropped database
|
|
+ */
|
|
+void
|
|
+securityOnDropDatabase(Oid datid)
|
|
+{
|
|
+ Relation rel;
|
|
+ ScanKeyData keys[1];
|
|
+ SysScanDesc scan;
|
|
+ HeapTuple tuple;
|
|
+
|
|
+ /* Scan all entries with pg_security.datid = datid */
|
|
+ ScanKeyInit(&keys[0],
|
|
+ Anum_pg_security_datid,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(datid));
|
|
+
|
|
+ rel = heap_open(SecurityRelationId, RowExclusiveLock);
|
|
+
|
|
+ scan = systable_beginscan(rel, SecuritySecidIndexId, true,
|
|
+ SnapshotNow, 1, keys);
|
|
+
|
|
+ while (HeapTupleIsValid(tuple = systable_getnext(scan)))
|
|
+ {
|
|
+ simple_heap_delete(rel, &tuple->t_self);
|
|
+ }
|
|
+
|
|
+ systable_endscan(scan);
|
|
+
|
|
+ heap_close(rel, RowExclusiveLock);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * InputSecurityAttr
|
|
+ */
|
|
+static Oid
|
|
+InputSecurityAttr(Oid relid, const char *secattr)
|
|
+{
|
|
+ LOCKMODE lockmode = AccessShareLock;
|
|
+ Relation rel;
|
|
+ ScanKeyData skey[3];
|
|
+ SysScanDesc scan;
|
|
+ HeapTuple tuple;
|
|
+ Oid datid;
|
|
+ Oid secid;
|
|
+ Datum values[Natts_pg_security];
|
|
+ bool nulls[Natts_pg_security];
|
|
+
|
|
+ datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId);
|
|
+
|
|
+retry:
|
|
+ /*
|
|
+ * Lookup pg_security catalog first
|
|
+ */
|
|
+ rel = heap_open(SecurityRelationId, lockmode);
|
|
+
|
|
+ ScanKeyInit(&skey[0],
|
|
+ Anum_pg_security_datid,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(datid));
|
|
+ ScanKeyInit(&skey[1],
|
|
+ Anum_pg_security_relid,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(relid));
|
|
+ ScanKeyInit(&skey[2],
|
|
+ Anum_pg_security_secattr,
|
|
+ BTEqualStrategyNumber, F_TEXTEQ,
|
|
+ CStringGetTextDatum(secattr));
|
|
+
|
|
+ scan = systable_beginscan(rel, SecuritySecattrIndexId, true,
|
|
+ SnapshotToast, 3, skey);
|
|
+
|
|
+ tuple = systable_getnext(scan);
|
|
+ if (HeapTupleIsValid(tuple))
|
|
+ {
|
|
+ secid = ((Form_pg_security) GETSTRUCT(tuple))->secid;
|
|
+
|
|
+ systable_endscan(scan);
|
|
+
|
|
+ heap_close(rel, lockmode);
|
|
+
|
|
+ return secid;
|
|
+ }
|
|
+
|
|
+ systable_endscan(scan);
|
|
+
|
|
+ /*
|
|
+ * If not exist, try to insert a new entry.
|
|
+ */
|
|
+ if (lockmode == AccessShareLock)
|
|
+ {
|
|
+ heap_close(rel, lockmode);
|
|
+
|
|
+ lockmode = RowExclusiveLock;
|
|
+
|
|
+ goto retry;
|
|
+ }
|
|
+
|
|
+ memset(nulls, false, sizeof(nulls));
|
|
+ secid = GetNewOidWithIndex(rel, SecuritySecidIndexId,
|
|
+ Anum_pg_security_secid);
|
|
+ values[Anum_pg_security_secid - 1] = ObjectIdGetDatum(secid);
|
|
+ values[Anum_pg_security_datid - 1] = ObjectIdGetDatum(datid);
|
|
+ values[Anum_pg_security_relid - 1] = ObjectIdGetDatum(relid);
|
|
+ values[Anum_pg_security_secattr - 1] = CStringGetTextDatum(secattr);
|
|
+
|
|
+ tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
|
|
+
|
|
+ simple_heap_insert(rel, tuple);
|
|
+
|
|
+ CatalogUpdateIndexes(rel, tuple);
|
|
+
|
|
+ heap_close(rel, lockmode);
|
|
+
|
|
+ return secid;
|
|
+}
|
|
+
|
|
+static char *
|
|
+OutputSecurityAttr(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_security catalog first
|
|
+ */
|
|
+ rel = heap_open(SecurityRelationId, AccessShareLock);
|
|
+
|
|
+ ScanKeyInit(&skey[0],
|
|
+ Anum_pg_security_secid,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(secid));
|
|
+ ScanKeyInit(&skey[1],
|
|
+ Anum_pg_security_datid,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(datid));
|
|
+ ScanKeyInit(&skey[2],
|
|
+ Anum_pg_security_relid,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(relid));
|
|
+
|
|
+ scan = systable_beginscan(rel, SecuritySecidIndexId, true,
|
|
+ SnapshotToast, 3, skey);
|
|
+
|
|
+ tuple = systable_getnext(scan);
|
|
+ if (HeapTupleIsValid(tuple))
|
|
+ {
|
|
+ Datum datum;
|
|
+ bool isnull;
|
|
+
|
|
+ datum = heap_getattr(tuple,
|
|
+ Anum_pg_security_secattr,
|
|
+ RelationGetDescr(rel), &isnull);
|
|
+ if (!isnull)
|
|
+ result = TextDatumGetCString(datum);
|
|
+ }
|
|
+
|
|
+ systable_endscan(scan);
|
|
+
|
|
+ heap_close(rel, AccessShareLock);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * input/output handler
|
|
+ */
|
|
+Oid
|
|
+securityRawSecLabelIn(Oid relid, char *seclabel)
|
|
+{
|
|
+ seclabel = sepgsqlRawSecLabelIn(seclabel);
|
|
+
|
|
+ return InputSecurityAttr(relid, seclabel);
|
|
+}
|
|
+
|
|
+char *
|
|
+securityRawSecLabelOut(Oid relid, Oid secid)
|
|
+{
|
|
+ char *seclabel = OutputSecurityAttr(relid, secid);
|
|
+
|
|
+ return sepgsqlRawSecLabelOut(seclabel);
|
|
+}
|
|
+
|
|
+Oid
|
|
+securityTransSecLabelIn(Oid relid, char *seclabel)
|
|
+{
|
|
+ seclabel = sepgsqlTransSecLabelIn(seclabel);
|
|
+
|
|
+ return securityRawSecLabelIn(relid, seclabel);
|
|
+}
|
|
+
|
|
+char *
|
|
+securityTransSecLabelOut(Oid relid, Oid secid)
|
|
+{
|
|
+ char *seclabel = securityRawSecLabelOut(relid, secid);
|
|
+
|
|
+ return sepgsqlTransSecLabelOut(seclabel);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Output handler for system columns
|
|
+ */
|
|
+Datum
|
|
+securitySysattSecLabelOut(Oid relid, HeapTuple tuple)
|
|
+{
|
|
+ char *seclabel;
|
|
+
|
|
+ seclabel = sepgsqlSysattSecLabelOut(relid, tuple);
|
|
+ if (!seclabel)
|
|
+ seclabel = "unlabled";
|
|
+
|
|
+ return CStringGetTextDatum(seclabel);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * securityReclaimOnDropTable
|
|
+ * drop orphan entries within pg_security on drop table
|
|
+ */
|
|
+void
|
|
+securityReclaimOnDropTable(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_security_datid,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(database_oid));
|
|
+ ScanKeyInit(&key[1],
|
|
+ Anum_pg_security_relid,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(relid));
|
|
+
|
|
+ rel = heap_open(SecurityRelationId, RowExclusiveLock);
|
|
+ scan = systable_beginscan(rel, SecuritySecattrIndexId, true,
|
|
+ SnapshotNow, 2, key);
|
|
+ while (HeapTupleIsValid(tuple = systable_getnext(scan)))
|
|
+ simple_heap_delete(rel, &tuple->t_self);
|
|
+
|
|
+ systable_endscan(scan);
|
|
+
|
|
+ heap_close(rel, RowExclusiveLock);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * security_quote_relation
|
|
+ * returns palloc'de identifier with explicit namespace
|
|
+ */
|
|
+static char *
|
|
+security_quote_relation(Oid relid)
|
|
+{
|
|
+ Oid nspoid = get_rel_namespace(relid);
|
|
+ char *nspname;
|
|
+ char *relname;
|
|
+
|
|
+ nspname = get_namespace_name(nspoid);
|
|
+ relname = get_rel_name(relid);
|
|
+
|
|
+ return quote_qualified_identifier(nspname, relname);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * security_reclaim_table
|
|
+ * reclaims orphan entries associated to a certain table
|
|
+ */
|
|
+static int
|
|
+seclabelRelationReclaimExec(Oid relOid)
|
|
+{
|
|
+ StringInfoData query;
|
|
+ SPIPlanPtr plan;
|
|
+ Oid types[2];
|
|
+ Datum values[2];
|
|
+ Oid proc_oid;
|
|
+ Oid database_oid;
|
|
+ char *relname_full;
|
|
+ char *attname_datid;
|
|
+ char *attname_relid;
|
|
+ char *attname_secid;
|
|
+ char *attname_seckind;
|
|
+ char *attname_secattr;
|
|
+ char *sec_proname;
|
|
+ char *sec_nspname;
|
|
+ Form_pg_proc proForm;
|
|
+ HeapTuple protup;
|
|
+
|
|
+ /*
|
|
+ * LOCK the target table
|
|
+ */
|
|
+ initStringInfo(&query);
|
|
+ relname_full = security_quote_relation(relOid);
|
|
+ appendStringInfo(&query, "LOCK %s IN SHARE MODE", relname_full);
|
|
+ if (SPI_execute(query.data, false, 0) != SPI_OK_UTILITY)
|
|
+ elog(ERROR, "SPI_execute failed on %s", query.data);
|
|
+
|
|
+ /*
|
|
+ * DELETE orphan entries
|
|
+ */
|
|
+ initStringInfo(&query);
|
|
+ attname_secid = get_attname(SecurityRelationId, Anum_pg_security_secid);
|
|
+ attname_datid = get_attname(SecurityRelationId, Anum_pg_security_datid);
|
|
+ attname_relid = get_attname(SecurityRelationId, Anum_pg_security_relid);
|
|
+ attname_secattr = get_attname(SecurityRelationId, Anum_pg_security_secattr);
|
|
+
|
|
+ appendStringInfo(&query,
|
|
+ "DELETE FROM %s "
|
|
+ "WHERE %s = $1 AND %s = $2 AND %s NOT IN ",
|
|
+ security_quote_relation(SecurityRelationId),
|
|
+ quote_identifier(attname_datid),
|
|
+ quote_identifier(attname_relid),
|
|
+ quote_identifier(attname_secid));
|
|
+
|
|
+ protup = SearchSysCache(PROCOID,
|
|
+ ObjectIdGetDatum(F_SECLABEL_TO_SECID),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(protup))
|
|
+ elog(ERROR, "cache lookup failed for procedure: %u", F_SECLABEL_TO_SECID);
|
|
+
|
|
+ proForm = (Form_pg_proc) GETSTRUCT(protup);
|
|
+ sec_proname = NameStr(proForm->proname);
|
|
+ sec_nspname = get_namespace_name(proForm->pronamespace);
|
|
+
|
|
+ appendStringInfo(&query,
|
|
+ "(SELECT %s.%s(%s) FROM ONLY %s)",
|
|
+ quote_identifier(sec_nspname),
|
|
+ quote_identifier(sec_proname),
|
|
+ quote_identifier(get_rel_name(relOid)),
|
|
+ relname_full);
|
|
+ ReleaseSysCache(protup);
|
|
+
|
|
+ /*
|
|
+ * Setup and execute query
|
|
+ */
|
|
+ types[0] = OIDOID;
|
|
+ types[1] = OIDOID;
|
|
+ plan = SPI_prepare(query.data, 2, types);
|
|
+ if (!plan)
|
|
+ elog(ERROR, "SPI_prepare failed on %s", query.data);
|
|
+
|
|
+ database_oid = (IsSharedRelation(relOid) ? InvalidOid : MyDatabaseId);
|
|
+
|
|
+ values[0] = ObjectIdGetDatum(database_oid);
|
|
+ values[1] = ObjectIdGetDatum(relOid);
|
|
+ if (SPI_execute_plan(plan, values, NULL, false, 0) != SPI_OK_DELETE)
|
|
+ elog(ERROR, "SPI_execute_plan failed on %s", query.data);
|
|
+
|
|
+ SPI_freetuptable(SPI_tuptable);
|
|
+
|
|
+ return SPI_processed;
|
|
+}
|
|
+
|
|
+void
|
|
+seclabelRelationReclaim(Oid relOid)
|
|
+{
|
|
+ int save_mode;
|
|
+
|
|
+ if (!superuser() ||
|
|
+ get_rel_relkind(relOid) != RELKIND_RELATION)
|
|
+ return;
|
|
+
|
|
+ save_mode = sepostgresql_mode;
|
|
+ sepostgresql_mode = SEPGSQL_MODE_INTERNAL;
|
|
+ PG_TRY();
|
|
+ {
|
|
+ if (SPI_connect() != SPI_OK_CONNECT)
|
|
+ elog(ERROR, "SPI_connect failed");
|
|
+
|
|
+ seclabelRelationReclaimExec(relOid);
|
|
+
|
|
+ if (SPI_finish() != SPI_OK_FINISH)
|
|
+ elog(ERROR, "SPI_finish failed");
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ sepostgresql_mode = save_mode;
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+ sepostgresql_mode = save_mode;
|
|
+}
|
|
+
|
|
+Datum
|
|
+seclabel_to_secid(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ HeapTupleHeader tuphdr = PG_GETARG_HEAPTUPLEHEADER(0);
|
|
+
|
|
+ PG_RETURN_OID(HeapTupleHeaderGetSecid(tuphdr));
|
|
+}
|
|
diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c
|
|
index 451724f..b95b414 100644
|
|
--- a/src/backend/catalog/pg_shdepend.c
|
|
+++ b/src/backend/catalog/pg_shdepend.c
|
|
@@ -37,6 +37,7 @@
|
|
#include "commands/schemacmds.h"
|
|
#include "commands/tablecmds.h"
|
|
#include "commands/typecmds.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/lmgr.h"
|
|
#include "miscadmin.h"
|
|
#include "utils/acl.h"
|
|
@@ -1340,6 +1341,8 @@ shdepReassignOwned(List *roleids, Oid newrole)
|
|
break;
|
|
|
|
case TypeRelationId:
|
|
+ /* SELinux checks */
|
|
+ sepgsql_type_alter(sdepForm->objid, NULL, InvalidOid);
|
|
AlterTypeOwnerInternal(sdepForm->objid, newrole, true);
|
|
break;
|
|
|
|
@@ -1352,7 +1355,8 @@ shdepReassignOwned(List *roleids, Oid newrole)
|
|
break;
|
|
|
|
case RelationRelationId:
|
|
-
|
|
+ /* SELinux checks */
|
|
+ sepgsql_relation_alter(sdepForm->objid, NULL, InvalidOid);
|
|
/*
|
|
* Pass recursing = true so that we don't fail on indexes,
|
|
* owned sequences, etc when we happen to visit them
|
|
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
|
|
index 7696480..4586056 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 "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/fmgroids.h"
|
|
@@ -56,10 +57,17 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
|
|
Datum values[Natts_pg_type];
|
|
bool nulls[Natts_pg_type];
|
|
Oid typoid;
|
|
+ Oid typsid;
|
|
NameData name;
|
|
|
|
Assert(PointerIsValid(typeName));
|
|
|
|
+ /* SELinux check permission to create a shell type */
|
|
+ typsid = sepgsql_type_create(typeName, InvalidOid, typeNamespace,
|
|
+ F_SHELL_IN, F_SHELL_OUT,
|
|
+ InvalidOid, InvalidOid,
|
|
+ InvalidOid, InvalidOid, InvalidOid);
|
|
+
|
|
/*
|
|
* open pg_type
|
|
*/
|
|
@@ -201,6 +209,7 @@ TypeCreate(Oid newTypeOid,
|
|
{
|
|
Relation pg_type_desc;
|
|
Oid typeObjectId;
|
|
+ Oid typeSecid = InvalidOid;
|
|
bool rebuildDeps = false;
|
|
HeapTuple tup;
|
|
bool nulls[Natts_pg_type];
|
|
@@ -367,6 +376,15 @@ TypeCreate(Oid newTypeOid,
|
|
CStringGetDatum(typeName),
|
|
ObjectIdGetDatum(typeNamespace),
|
|
0, 0);
|
|
+
|
|
+ /* SELinux checks to create/replace type */
|
|
+ if (!isImplicitArray && typeType != TYPTYPE_COMPOSITE)
|
|
+ typeSecid = sepgsql_type_create(typeName, tup, typeNamespace,
|
|
+ inputProcedure, outputProcedure,
|
|
+ receiveProcedure, sendProcedure,
|
|
+ typmodinProcedure, typmodoutProcedure,
|
|
+ analyzeProcedure);
|
|
+
|
|
if (HeapTupleIsValid(tup))
|
|
{
|
|
/*
|
|
@@ -412,6 +430,8 @@ TypeCreate(Oid newTypeOid,
|
|
/* Force the OID if requested by caller, else heap_insert does it */
|
|
if (OidIsValid(newTypeOid))
|
|
HeapTupleSetOid(tup, newTypeOid);
|
|
+ if (HeapTupleHasSecid(tup))
|
|
+ HeapTupleSetSecid(tup, typeSecid);
|
|
|
|
typeObjectId = simple_heap_insert(pg_type_desc, tup);
|
|
}
|
|
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
|
|
index 6e7b5cf..10ea3a2 100644
|
|
--- a/src/backend/catalog/toasting.c
|
|
+++ b/src/backend/catalog/toasting.c
|
|
@@ -28,6 +28,7 @@
|
|
#include "catalog/toasting.h"
|
|
#include "miscadmin.h"
|
|
#include "nodes/makefuncs.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/syscache.h"
|
|
|
|
@@ -125,6 +126,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
|
|
char toast_relname[NAMEDATALEN];
|
|
char toast_idxname[NAMEDATALEN];
|
|
IndexInfo *indexInfo;
|
|
+ Oid *secLabels;
|
|
Oid classObjectId[2];
|
|
int16 coloptions[2];
|
|
ObjectAddress baseobject,
|
|
@@ -199,6 +201,11 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
|
|
else
|
|
namespaceid = PG_TOAST_NAMESPACE;
|
|
|
|
+ secLabels = sepgsql_relation_create(toast_relname,
|
|
+ RELKIND_TOASTVALUE,
|
|
+ tupdesc, namespaceid,
|
|
+ NULL, NIL, false, false);
|
|
+
|
|
toast_relid = heap_create_with_catalog(toast_relname,
|
|
namespaceid,
|
|
rel->rd_rel->reltablespace,
|
|
@@ -212,7 +219,8 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
|
|
0,
|
|
ONCOMMIT_NOOP,
|
|
reloptions,
|
|
- 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 fd3f336..dfca678 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 "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/lsyscache.h"
|
|
@@ -311,6 +312,9 @@ RenameAggregate(List *name, List *args, const char *newname)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(namespaceOid));
|
|
|
|
+ /* SELinux permission checks */
|
|
+ sepgsql_proc_alter(procOid, newname, InvalidOid);
|
|
+
|
|
/* 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 46bc4df..79131d5 100644
|
|
--- a/src/backend/commands/alter.c
|
|
+++ b/src/backend/commands/alter.c
|
|
@@ -289,3 +289,32 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
|
|
(int) stmt->objectType);
|
|
}
|
|
}
|
|
+
|
|
+void
|
|
+ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt)
|
|
+{
|
|
+ DefElem *seclabel = (DefElem *)stmt->secLabel;
|
|
+
|
|
+ switch (stmt->objectType)
|
|
+ {
|
|
+ case OBJECT_DATABASE:
|
|
+ AlterDatabaseSecLabel(strVal(linitial(stmt->object)), seclabel);
|
|
+ break;
|
|
+ case OBJECT_SCHEMA:
|
|
+ AlterSchemaSecLabel(strVal(linitial(stmt->object)), seclabel);
|
|
+ break;
|
|
+ case OBJECT_TABLE:
|
|
+ case OBJECT_SEQUENCE:
|
|
+ case OBJECT_COLUMN:
|
|
+ CheckRelationOwnership(stmt->relation, true);
|
|
+ AlterRelationSecLabel(stmt->relation, stmt->subname,
|
|
+ stmt->objectType, seclabel);
|
|
+ break;
|
|
+ case OBJECT_FUNCTION:
|
|
+ AlterFunctionSecLabel(stmt->object, stmt->objarg, 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 a6ba2ec..b990a33 100644
|
|
--- a/src/backend/commands/cluster.c
|
|
+++ b/src/backend/commands/cluster.c
|
|
@@ -36,6 +36,7 @@
|
|
#include "commands/trigger.h"
|
|
#include "commands/vacuum.h"
|
|
#include "miscadmin.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "storage/procarray.h"
|
|
#include "utils/acl.h"
|
|
@@ -617,8 +618,9 @@ rebuild_relation(Relation OldHeap, Oid indexOid)
|
|
/*
|
|
* The new relation is local to our transaction and we know nothing
|
|
* depends on it, so DROP_RESTRICT should be OK.
|
|
+ * SELinux does not check any permissions here.
|
|
*/
|
|
- performDeletion(&object, DROP_RESTRICT);
|
|
+ performDeletionNoPerms(&object, DROP_RESTRICT);
|
|
|
|
/* performDeletion does CommandCounterIncrement at end */
|
|
|
|
@@ -712,7 +714,8 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace)
|
|
0,
|
|
ONCOMMIT_NOOP,
|
|
reloptions,
|
|
- allowSystemTableMods);
|
|
+ allowSystemTableMods,
|
|
+ sepgsql_relation_copy(OldHeap));
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
@@ -924,6 +927,10 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
|
|
if (NewHeap->rd_rel->relhasoids)
|
|
HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple));
|
|
|
|
+ /* Preserve SID, if any */
|
|
+ if (HeapTupleHasSecid(copiedTuple))
|
|
+ HeapTupleSetSecid(copiedTuple, HeapTupleGetSecid(tuple));
|
|
+
|
|
/* The heap rewrite module does the rest */
|
|
rewrite_heap_tuple(rwstate, tuple, copiedTuple);
|
|
|
|
diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c
|
|
index 045ffca..97914d5 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 "security/sepgsql.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,9 @@ 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 +119,7 @@ CreateConversionCommand(CreateConversionStmt *stmt)
|
|
* name)
|
|
*/
|
|
ConversionCreate(conversion_name, namespaceId, GetUserId(),
|
|
- from_encoding, to_encoding, funcoid, stmt->def);
|
|
+ from_encoding, to_encoding, funcoid, secid, stmt->def);
|
|
}
|
|
|
|
/*
|
|
@@ -240,6 +245,9 @@ RenameConversion(List *name, const char *newname)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(namespaceOid));
|
|
|
|
+ /* SELinux checks */
|
|
+ sepgsql_conversion_alter(conversionOid, newname);
|
|
+
|
|
/* rename */
|
|
namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname);
|
|
simple_heap_update(rel, &tup->t_self, tup);
|
|
@@ -336,6 +344,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), NULL);
|
|
|
|
/*
|
|
* 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 a151999..ee7344d 100644
|
|
--- a/src/backend/commands/copy.c
|
|
+++ b/src/backend/commands/copy.c
|
|
@@ -21,8 +21,11 @@
|
|
#include <arpa/inet.h>
|
|
|
|
#include "access/heapam.h"
|
|
+#include "access/sysattr.h"
|
|
#include "access/xact.h"
|
|
+#include "catalog/heap.h"
|
|
#include "catalog/namespace.h"
|
|
+#include "catalog/pg_security.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "commands/copy.h"
|
|
#include "commands/trigger.h"
|
|
@@ -34,6 +37,8 @@
|
|
#include "optimizer/planner.h"
|
|
#include "parser/parse_relation.h"
|
|
#include "rewrite/rewriteHandler.h"
|
|
+#include "security/rowlevel.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/fd.h"
|
|
#include "tcop/tcopprot.h"
|
|
#include "utils/acl.h"
|
|
@@ -160,6 +165,10 @@ 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;
|
|
@@ -243,8 +252,8 @@ 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,
|
|
- Datum *values, bool *nulls);
|
|
+static void CopyOneRowTo(CopyState cstate, HeapTuple tuple,
|
|
+ Datum *values, bool *nulls);
|
|
static void CopyFrom(CopyState cstate);
|
|
static bool CopyReadLine(CopyState cstate);
|
|
static bool CopyReadLineText(CopyState cstate);
|
|
@@ -958,12 +967,19 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
|
|
errmsg("CSV quote character must not appear in the NULL specification")));
|
|
|
|
/* Disallow file COPY except to superusers. */
|
|
- if (!pipe && !superuser())
|
|
- ereport(ERROR,
|
|
- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
- errmsg("must be superuser to COPY to or from a file"),
|
|
- errhint("Anyone can COPY to stdout or from stdin. "
|
|
- "psql's \\copy command also works for anyone.")));
|
|
+ if (!pipe)
|
|
+ {
|
|
+ if (!superuser())
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
+ errmsg("must be superuser to COPY to or from a file"),
|
|
+ errhint("Anyone can COPY to stdout or from stdin. "
|
|
+ "psql's \\copy command also works for anyone.")));
|
|
+ if (is_from)
|
|
+ sepgsql_file_read(stmt->filename);
|
|
+ else
|
|
+ sepgsql_file_write(stmt->filename);
|
|
+ }
|
|
|
|
if (stmt->relation)
|
|
{
|
|
@@ -1090,6 +1106,9 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
|
|
|
|
num_phys_attrs = tupDesc->natts;
|
|
|
|
+ /* SELinux: check table/column level permission */
|
|
+ sepgsqlCheckCopyTable(cstate->rel, cstate->attnumlist, is_from);
|
|
+
|
|
/* Convert FORCE QUOTE name list to per-column flags, check validity */
|
|
cstate->force_quote_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool));
|
|
if (force_quote)
|
|
@@ -1104,11 +1123,31 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
|
|
int attnum = lfirst_int(cur);
|
|
|
|
if (!list_member_int(cstate->attnumlist, attnum))
|
|
+ {
|
|
+ Form_pg_attribute attForm;
|
|
+
|
|
+ if (SystemAttributeIsWritable(attnum))
|
|
+ attForm = SystemAttributeDefinition(attnum, true);
|
|
+ else
|
|
+ attForm = tupDesc->attrs[attnum - 1];
|
|
+
|
|
+ Assert(attForm != NULL);
|
|
+
|
|
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))));
|
|
+ }
|
|
+
|
|
+ switch (attnum)
|
|
+ {
|
|
+ case SecurityAttributeNumber:
|
|
+ cstate->seclabel_force_quot = true;
|
|
+ break;
|
|
+ default:
|
|
+ cstate->force_quote_flags[attnum - 1] = true;
|
|
+ break;
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -1126,10 +1165,23 @@ DoCopy(const CopyStmt *stmt, const char *queryString)
|
|
int attnum = lfirst_int(cur);
|
|
|
|
if (!list_member_int(cstate->attnumlist, attnum))
|
|
+ {
|
|
+ Form_pg_attribute attForm;
|
|
+
|
|
+ if (SystemAttributeIsWritable(attnum))
|
|
+ attForm = SystemAttributeDefinition(attnum, true);
|
|
+ else
|
|
+ attForm = tupDesc->attrs[attnum - 1];
|
|
+
|
|
+ Assert(attForm != NULL);
|
|
+
|
|
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))));
|
|
+ }
|
|
+ if (SystemAttributeIsWritable(attnum))
|
|
+ continue; /* ignore, if specified */
|
|
cstate->force_notnull_flags[attnum - 1] = true;
|
|
}
|
|
}
|
|
@@ -1321,16 +1373,31 @@ CopyTo(CopyState cstate)
|
|
int attnum = lfirst_int(cur);
|
|
Oid out_func_oid;
|
|
bool isvarlena;
|
|
+ FmgrInfo *out_fmgr;
|
|
+ Form_pg_attribute attForm;
|
|
+
|
|
+ switch (attnum)
|
|
+ {
|
|
+ case SecurityAttributeNumber:
|
|
+ attForm = SystemAttributeDefinition(attnum, true);
|
|
+ out_fmgr = &cstate->seclabel_out_function;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ attForm = attr[attnum - 1];
|
|
+ out_fmgr = &cstate->out_functions[attnum - 1];
|
|
+ break;
|
|
+ }
|
|
|
|
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);
|
|
}
|
|
|
|
/*
|
|
@@ -1385,7 +1452,14 @@ CopyTo(CopyState cstate)
|
|
CopySendChar(cstate, cstate->delim[0]);
|
|
hdr_delim = true;
|
|
|
|
- colname = NameStr(attr[attnum - 1]->attname);
|
|
+ if (SystemAttributeIsWritable(attnum))
|
|
+ {
|
|
+ Form_pg_attribute attForm
|
|
+ = SystemAttributeDefinition(attnum, true);
|
|
+ colname = NameStr(attForm->attname);
|
|
+ }
|
|
+ else
|
|
+ colname = NameStr(attr[attnum - 1]->attname);
|
|
|
|
CopyAttributeOutCSV(cstate, colname, false,
|
|
list_length(cstate->attnumlist) == 1);
|
|
@@ -1411,11 +1485,15 @@ CopyTo(CopyState cstate)
|
|
{
|
|
CHECK_FOR_INTERRUPTS();
|
|
|
|
+ /* check Row-level permission on the tuple */
|
|
+ if (!rowlvCopyToTuple(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);
|
|
@@ -1441,7 +1519,8 @@ 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;
|
|
@@ -1461,7 +1540,7 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls)
|
|
{
|
|
/* Hack --- assume Oid is same size as int32 */
|
|
CopySendInt32(cstate, sizeof(int32));
|
|
- CopySendInt32(cstate, tupleOid);
|
|
+ CopySendInt32(cstate, HeapTupleGetOid(tuple));
|
|
}
|
|
}
|
|
else
|
|
@@ -1471,7 +1550,7 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls)
|
|
if (cstate->oids)
|
|
{
|
|
string = DatumGetCString(DirectFunctionCall1(oidout,
|
|
- ObjectIdGetDatum(tupleOid)));
|
|
+ ObjectIdGetDatum(HeapTupleGetOid(tuple))));
|
|
CopySendString(cstate, string);
|
|
need_delim = true;
|
|
}
|
|
@@ -1480,8 +1559,11 @@ 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];
|
|
+ Oid relid;
|
|
+ Datum value;
|
|
+ bool isnull;
|
|
+ bool force_quot;
|
|
+ FmgrInfo *out_fmgr;
|
|
|
|
if (!cstate->binary)
|
|
{
|
|
@@ -1490,6 +1572,24 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls)
|
|
need_delim = true;
|
|
}
|
|
|
|
+ switch (attnum)
|
|
+ {
|
|
+ case SecurityAttributeNumber:
|
|
+ relid = RelationGetRelid(cstate->rel);
|
|
+ value = securitySysattSecLabelOut(relid, tuple);
|
|
+ isnull = false;
|
|
+ force_quot = cstate->seclabel_force_quot;
|
|
+ out_fmgr = &cstate->seclabel_out_function;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ value = values[attnum - 1];
|
|
+ isnull = nulls[attnum - 1];
|
|
+ force_quot = cstate->force_quote_flags[attnum - 1];
|
|
+ out_fmgr = &out_functions[attnum - 1];
|
|
+ break;
|
|
+ }
|
|
+
|
|
if (isnull)
|
|
{
|
|
if (!cstate->binary)
|
|
@@ -1501,11 +1601,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);
|
|
@@ -1514,8 +1612,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);
|
|
@@ -1649,8 +1746,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;
|
|
@@ -1888,6 +1987,18 @@ CopyFrom(CopyState cstate)
|
|
fmgr_info(in_func_oid, &oid_in_function);
|
|
}
|
|
|
|
+ if (list_member_int(cstate->attnumlist,
|
|
+ SecurityAttributeNumber))
|
|
+ {
|
|
+ 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));
|
|
|
|
@@ -1922,6 +2033,7 @@ CopyFrom(CopyState cstate)
|
|
{
|
|
bool skip_tuple;
|
|
Oid loaded_oid = InvalidOid;
|
|
+ Oid loaded_seclabel = InvalidOid;
|
|
|
|
CHECK_FOR_INTERRUPTS();
|
|
|
|
@@ -1993,14 +2105,21 @@ CopyFrom(CopyState cstate)
|
|
/* Loop to read the user attributes on the line. */
|
|
foreach(cur, cstate->attnumlist)
|
|
{
|
|
+ Form_pg_attribute attForm;
|
|
+ Datum dat;
|
|
int attnum = lfirst_int(cur);
|
|
int m = attnum - 1;
|
|
|
|
+ if (SystemAttributeIsWritable(attnum))
|
|
+ attForm = SystemAttributeDefinition(attnum, 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 &&
|
|
@@ -2010,14 +2129,40 @@ 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;
|
|
+
|
|
+ switch (attnum)
|
|
+ {
|
|
+ case SecurityAttributeNumber:
|
|
+ if (!string)
|
|
+ break;
|
|
+
|
|
+ dat = InputFunctionCall(&seclabel_in_function,
|
|
+ string,
|
|
+ seclabel_typioparam,
|
|
+ attForm->atttypmod);
|
|
+ loaded_seclabel
|
|
+ = securityTransSecLabelIn(RelationGetRelid(cstate->rel),
|
|
+ TextDatumGetCString(dat));
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ 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;
|
|
+ break;
|
|
+ }
|
|
cstate->cur_attname = NULL;
|
|
cstate->cur_attval = NULL;
|
|
}
|
|
@@ -2063,17 +2208,41 @@ CopyFrom(CopyState cstate)
|
|
i = 0;
|
|
foreach(cur, cstate->attnumlist)
|
|
{
|
|
+ Form_pg_attribute attForm;
|
|
+ Datum dat;
|
|
int attnum = lfirst_int(cur);
|
|
int m = attnum - 1;
|
|
|
|
- cstate->cur_attname = NameStr(attr[m]->attname);
|
|
+ if (SystemAttributeIsWritable(attnum))
|
|
+ attForm = SystemAttributeDefinition(attnum, false);
|
|
+ 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]);
|
|
+
|
|
+ switch (attnum)
|
|
+ {
|
|
+ case SecurityAttributeNumber:
|
|
+ dat = CopyReadBinaryAttribute(cstate, i,
|
|
+ &seclabel_in_function,
|
|
+ seclabel_typioparam,
|
|
+ attForm->atttypmod,
|
|
+ &isnull);
|
|
+ if (!isnull)
|
|
+ loaded_seclabel
|
|
+ = securityTransSecLabelIn(RelationGetRelid(cstate->rel),
|
|
+ TextDatumGetCString(dat));
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ values[m] = CopyReadBinaryAttribute(cstate, i,
|
|
+ &in_functions[m],
|
|
+ typioparams[m],
|
|
+ attr[m]->atttypmod,
|
|
+ &nulls[m]);
|
|
+ break;
|
|
+ }
|
|
cstate->cur_attname = NULL;
|
|
}
|
|
}
|
|
@@ -2094,6 +2263,8 @@ CopyFrom(CopyState cstate)
|
|
|
|
if (cstate->oids && file_has_oids)
|
|
HeapTupleSetOid(tuple, loaded_oid);
|
|
+ if (HeapTupleHasSecid(tuple))
|
|
+ HeapTupleSetSecid(tuple, loaded_seclabel);
|
|
|
|
/* Triggers and stuff need to be invoked in query context. */
|
|
MemoryContextSwitchTo(oldcontext);
|
|
@@ -2118,6 +2289,9 @@ CopyFrom(CopyState cstate)
|
|
}
|
|
|
|
if (!skip_tuple)
|
|
+ sepgsqlHeapTupleInsert(cstate->rel, tuple, false);
|
|
+
|
|
+ if (!skip_tuple)
|
|
{
|
|
/* Place tuple in tuple slot */
|
|
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
|
|
@@ -3398,6 +3572,13 @@ CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist)
|
|
}
|
|
if (attnum == InvalidAttrNumber)
|
|
{
|
|
+ Form_pg_attribute attForm
|
|
+ = SystemAttributeByName(name, tupDesc->tdhasoid);
|
|
+ if (attForm && SystemAttributeIsWritable(attForm->attnum))
|
|
+ attnum = attForm->attnum;
|
|
+ }
|
|
+ if (attnum == InvalidAttrNumber)
|
|
+ {
|
|
if (rel != NULL)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
|
@@ -3445,7 +3626,8 @@ 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 ec1db5a..ea35f05 100644
|
|
--- a/src/backend/commands/dbcommands.c
|
|
+++ b/src/backend/commands/dbcommands.c
|
|
@@ -33,6 +33,7 @@
|
|
#include "catalog/indexing.h"
|
|
#include "catalog/pg_authid.h"
|
|
#include "catalog/pg_database.h"
|
|
+#include "catalog/pg_security.h"
|
|
#include "catalog/pg_tablespace.h"
|
|
#include "commands/comment.h"
|
|
#include "commands/dbcommands.h"
|
|
@@ -41,6 +42,7 @@
|
|
#include "miscadmin.h"
|
|
#include "pgstat.h"
|
|
#include "postmaster/bgwriter.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "storage/fd.h"
|
|
#include "storage/lmgr.h"
|
|
@@ -111,6 +113,7 @@ createdb(const CreatedbStmt *stmt)
|
|
bool new_record_nulls[Natts_pg_database];
|
|
Oid dboid;
|
|
Oid datdba;
|
|
+ Oid datsecid;
|
|
ListCell *option;
|
|
DefElem *dtablespacename = NULL;
|
|
DefElem *downer = NULL;
|
|
@@ -119,6 +122,7 @@ createdb(const CreatedbStmt *stmt)
|
|
DefElem *dcollate = NULL;
|
|
DefElem *dctype = NULL;
|
|
DefElem *dconnlimit = NULL;
|
|
+ DefElem *dseclabel = NULL;
|
|
char *dbname = stmt->dbname;
|
|
char *dbowner = NULL;
|
|
const char *dbtemplate = NULL;
|
|
@@ -200,6 +204,14 @@ createdb(const CreatedbStmt *stmt)
|
|
errmsg("LOCATION is not supported anymore"),
|
|
errhint("Consider using tablespaces instead.")));
|
|
}
|
|
+ else if (strcmp(defel->defname, "security_context") == 0)
|
|
+ {
|
|
+ if (dseclabel)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_SYNTAX_ERROR),
|
|
+ errmsg("conflicting or redundant options")));
|
|
+ dseclabel = defel;
|
|
+ }
|
|
else
|
|
elog(ERROR, "option \"%s\" not recognized",
|
|
defel->defname);
|
|
@@ -294,6 +306,9 @@ createdb(const CreatedbStmt *stmt)
|
|
errmsg("template database \"%s\" does not exist",
|
|
dbtemplate)));
|
|
|
|
+ /* SELinux checks db_database:{create} */
|
|
+ datsecid = sepgsql_database_create(dbname, src_dboid, dseclabel);
|
|
+
|
|
/*
|
|
* Permission check: to copy a DB that's not marked datistemplate, you
|
|
* must be superuser or the owner thereof.
|
|
@@ -557,6 +572,8 @@ createdb(const CreatedbStmt *stmt)
|
|
new_record, new_record_nulls);
|
|
|
|
HeapTupleSetOid(tuple, dboid);
|
|
+ if (HeapTupleHasSecid(tuple))
|
|
+ HeapTupleSetSecid(tuple, datsecid);
|
|
|
|
simple_heap_insert(pg_database_rel, tuple);
|
|
|
|
@@ -573,6 +590,9 @@ createdb(const CreatedbStmt *stmt)
|
|
/* Create pg_shdepend entries for objects within database */
|
|
copyTemplateDependencies(src_dboid, dboid);
|
|
|
|
+ /* Create pg_security entries for objects within database */
|
|
+ securityOnCreateDatabase(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
|
|
@@ -776,6 +796,9 @@ dropdb(const char *dbname, bool missing_ok)
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
|
|
dbname);
|
|
|
|
+ /* SELinux checks db_database:{drop} permission */
|
|
+ sepgsql_database_drop(db_id);
|
|
+
|
|
/*
|
|
* Disallow dropping a DB that is marked istemplate. This is just to
|
|
* prevent people from accidentally dropping template0 or template1; they
|
|
@@ -829,6 +852,11 @@ dropdb(const char *dbname, bool missing_ok)
|
|
dropDatabaseDependencies(db_id);
|
|
|
|
/*
|
|
+ * Remove pg_security entries for the database.
|
|
+ */
|
|
+ securityOnDropDatabase(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...
|
|
@@ -913,6 +941,9 @@ RenameDatabase(const char *oldname, const char *newname)
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
errmsg("permission denied to rename database")));
|
|
|
|
+ /* SELinux: check db_database:{setattr} */
|
|
+ sepgsql_database_alter(db_id);
|
|
+
|
|
/*
|
|
* Make sure the new name doesn't exist. See notes for same error in
|
|
* CREATE DATABASE.
|
|
@@ -1025,6 +1056,9 @@ movedb(const char *dbname, const char *tblspcname)
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
|
|
dbname);
|
|
|
|
+ /* SELinux checks db_database:{setattr} */
|
|
+ sepgsql_database_alter(db_id);
|
|
+
|
|
/*
|
|
* Obviously can't move the tables of my own database
|
|
*/
|
|
@@ -1377,6 +1411,9 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel)
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
|
|
stmt->dbname);
|
|
|
|
+ /* SELinux checks db_database:{setattr} */
|
|
+ sepgsql_database_alter(HeapTupleGetOid(tuple));
|
|
+
|
|
/*
|
|
* Build an updated tuple, perusing the information just obtained
|
|
*/
|
|
@@ -1449,6 +1486,9 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt)
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE,
|
|
stmt->dbname);
|
|
|
|
+ /* SELinux checks db_database:{setattr} */
|
|
+ sepgsql_database_alter(HeapTupleGetOid(tuple));
|
|
+
|
|
memset(repl_repl, false, sizeof(repl_repl));
|
|
repl_repl[Anum_pg_database_datconfig - 1] = true;
|
|
|
|
@@ -1571,6 +1611,9 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
errmsg("permission denied to change owner of database")));
|
|
|
|
+ /* SELinux checks db_database:{setattr} */
|
|
+ sepgsql_database_alter(HeapTupleGetOid(tuple));
|
|
+
|
|
memset(repl_null, false, sizeof(repl_null));
|
|
memset(repl_repl, false, sizeof(repl_repl));
|
|
|
|
@@ -1615,6 +1658,58 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId)
|
|
*/
|
|
}
|
|
|
|
+/*
|
|
+ * ALTER DATABASE name SECURITY_LABEL [=] newlabel
|
|
+ */
|
|
+void
|
|
+AlterDatabaseSecLabel(const char *dbname, DefElem *seclabel)
|
|
+{
|
|
+ Relation rel;
|
|
+ HeapTuple oldtup;
|
|
+ HeapTuple newtup;
|
|
+ ScanKeyData scankey;
|
|
+ SysScanDesc scan;
|
|
+ Oid secid;
|
|
+ bool replaces[Natts_pg_database];
|
|
+
|
|
+ /* Fetch the old tuple */
|
|
+ rel = heap_open(DatabaseRelationId, RowExclusiveLock);
|
|
+ ScanKeyInit(&scankey,
|
|
+ Anum_pg_database_datname,
|
|
+ BTEqualStrategyNumber, F_NAMEEQ,
|
|
+ NameGetDatum(dbname));
|
|
+ scan = systable_beginscan(rel, DatabaseNameIndexId, true,
|
|
+ SnapshotNow, 1, &scankey);
|
|
+ oldtup = systable_getnext(scan);
|
|
+ if (!HeapTupleIsValid(oldtup))
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_UNDEFINED_DATABASE),
|
|
+ errmsg("database \"%s\" does not exist", dbname)));
|
|
+
|
|
+ memset(replaces, false, sizeof(replaces));
|
|
+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
|
|
+ NULL, NULL, replaces);
|
|
+ if (!HeapTupleHasSecid(newtup))
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("Unable to set security label on \"%s\"", dbname)));
|
|
+ systable_endscan(scan);
|
|
+
|
|
+ /* check DAC permission */
|
|
+ if (!pg_database_ownercheck(HeapTupleGetOid(newtup), GetUserId()))
|
|
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname);
|
|
+
|
|
+ /* SELinux checks db_database:{setattr relabelfrom relabelto} */
|
|
+ secid = sepgsql_database_relabel(HeapTupleGetOid(newtup), seclabel);
|
|
+ HeapTupleSetSecid(newtup, secid);
|
|
+
|
|
+ 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/foreigncmds.c b/src/backend/commands/foreigncmds.c
|
|
index 46493b1..7236279 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 "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/lsyscache.h"
|
|
@@ -233,6 +234,9 @@ AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId)
|
|
|
|
if (form->fdwowner != newOwnerId)
|
|
{
|
|
+ /* SELinux permission check */
|
|
+ sepgsql_fdw_alter(fdwId, InvalidOid);
|
|
+
|
|
form->fdwowner = newOwnerId;
|
|
|
|
simple_heap_update(rel, &tup->t_self, tup);
|
|
@@ -297,6 +301,8 @@ AlterForeignServerOwner(const char *name, Oid newOwnerId)
|
|
aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
|
|
}
|
|
}
|
|
+ /* SELinux permission checks */
|
|
+ sepgsql_foreign_server_alter(srvId);
|
|
|
|
form->srvowner = newOwnerId;
|
|
|
|
@@ -342,6 +348,7 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
|
|
Oid fdwvalidator;
|
|
Datum fdwoptions;
|
|
Oid ownerId;
|
|
+ Oid secid;
|
|
|
|
/* Must be super user */
|
|
if (!superuser())
|
|
@@ -380,6 +387,9 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
|
|
else
|
|
fdwvalidator = InvalidOid;
|
|
|
|
+ /* SELinux permission checks */
|
|
+ secid = sepgsql_fdw_create(stmt->fdwname, fdwvalidator);
|
|
+
|
|
values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = fdwvalidator;
|
|
|
|
nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true;
|
|
@@ -393,6 +403,8 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt)
|
|
nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true;
|
|
|
|
tuple = heap_form_tuple(rel->rd_att, values, nulls);
|
|
+ if (HeapTupleHasSecid(tuple))
|
|
+ HeapTupleSetSecid(tuple, secid);
|
|
|
|
fdwId = simple_heap_insert(rel, tuple);
|
|
CatalogUpdateIndexes(rel, tuple);
|
|
@@ -487,6 +499,9 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt)
|
|
fdwvalidator = DatumGetObjectId(datum);
|
|
}
|
|
|
|
+ /* SELinux permission checks */
|
|
+ sepgsql_fdw_alter(fdwId, fdwvalidator);
|
|
+
|
|
/*
|
|
* Options specified, validate and update.
|
|
*/
|
|
@@ -609,6 +624,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
|
|
HeapTuple tuple;
|
|
Oid srvId;
|
|
Oid ownerId;
|
|
+ Oid secid;
|
|
AclResult aclresult;
|
|
ObjectAddress myself;
|
|
ObjectAddress referenced;
|
|
@@ -636,6 +652,8 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname);
|
|
|
|
+ secid = sepgsql_foreign_server_create(stmt->fdwname);
|
|
+
|
|
/*
|
|
* Insert tuple into pg_foreign_server.
|
|
*/
|
|
@@ -676,6 +694,8 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
|
|
nulls[Anum_pg_foreign_server_srvoptions - 1] = true;
|
|
|
|
tuple = heap_form_tuple(rel->rd_att, values, nulls);
|
|
+ if (HeapTupleHasSecid(tuple))
|
|
+ HeapTupleSetSecid(tuple, secid);
|
|
|
|
srvId = simple_heap_insert(rel, tuple);
|
|
|
|
@@ -732,6 +752,9 @@ AlterForeignServer(AlterForeignServerStmt *stmt)
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER,
|
|
stmt->servername);
|
|
|
|
+ /* SELinux permission checks */
|
|
+ sepgsql_foreign_server_alter(srvId);
|
|
+
|
|
memset(repl_val, 0, sizeof(repl_val));
|
|
memset(repl_null, false, sizeof(repl_null));
|
|
memset(repl_repl, false, sizeof(repl_repl));
|
|
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
|
|
index f0989bf..0668bd7 100644
|
|
--- a/src/backend/commands/functioncmds.c
|
|
+++ b/src/backend/commands/functioncmds.c
|
|
@@ -53,6 +53,7 @@
|
|
#include "parser/parse_expr.h"
|
|
#include "parser/parse_func.h"
|
|
#include "parser/parse_type.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/fmgroids.h"
|
|
@@ -517,7 +518,8 @@ compute_attributes_sql_style(List *options,
|
|
bool *security_definer,
|
|
ArrayType **proconfig,
|
|
float4 *procost,
|
|
- float4 *prorows)
|
|
+ float4 *prorows,
|
|
+ Node **proseclabel)
|
|
{
|
|
ListCell *option;
|
|
DefElem *as_item = NULL;
|
|
@@ -529,6 +531,7 @@ compute_attributes_sql_style(List *options,
|
|
List *set_items = NIL;
|
|
DefElem *cost_item = NULL;
|
|
DefElem *rows_item = NULL;
|
|
+ DefElem *seclabel_item = NULL;
|
|
|
|
foreach(option, options)
|
|
{
|
|
@@ -558,6 +561,14 @@ compute_attributes_sql_style(List *options,
|
|
errmsg("conflicting or redundant options")));
|
|
windowfunc_item = defel;
|
|
}
|
|
+ else if (strcmp(defel->defname, "security_context") == 0)
|
|
+ {
|
|
+ if (seclabel_item)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_SYNTAX_ERROR),
|
|
+ errmsg("conflicting or redundant options")));
|
|
+ seclabel_item = defel;
|
|
+ }
|
|
else if (compute_common_attribute(defel,
|
|
&volatility_item,
|
|
&strict_item,
|
|
@@ -622,6 +633,8 @@ compute_attributes_sql_style(List *options,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("ROWS must be positive")));
|
|
}
|
|
+ if (seclabel_item)
|
|
+ *proseclabel = (Node *)seclabel_item;
|
|
}
|
|
|
|
|
|
@@ -762,6 +775,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
|
|
ArrayType *proconfig;
|
|
float4 procost;
|
|
float4 prorows;
|
|
+ Node *proseclabel;
|
|
HeapTuple languageTuple;
|
|
Form_pg_language languageStruct;
|
|
List *as_clause;
|
|
@@ -784,13 +798,14 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
|
|
proconfig = NULL;
|
|
procost = -1; /* indicates not set */
|
|
prorows = -1; /* indicates not set */
|
|
+ proseclabel = NULL;
|
|
|
|
/* override attributes from explicit list */
|
|
compute_attributes_sql_style(stmt->options,
|
|
&as_clause, &language,
|
|
&isWindowFunc, &volatility,
|
|
&isStrict, &security,
|
|
- &proconfig, &procost, &prorows);
|
|
+ &proconfig, &procost, &prorows, &proseclabel);
|
|
|
|
/* Convert language name to canonical case */
|
|
languageName = case_translate_language_name(language);
|
|
@@ -926,7 +941,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString)
|
|
parameterDefaults,
|
|
PointerGetDatum(proconfig),
|
|
procost,
|
|
- prorows);
|
|
+ prorows,
|
|
+ proseclabel);
|
|
}
|
|
|
|
|
|
@@ -1112,6 +1128,9 @@ RenameFunction(List *name, List *argtypes, const char *newname)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(namespaceOid));
|
|
|
|
+ /* SELinux permission checks */
|
|
+ sepgsql_proc_alter(procOid, newname, InvalidOid);
|
|
+
|
|
/* rename */
|
|
namestrcpy(&(procForm->proname), newname);
|
|
simple_heap_update(rel, &tup->t_self, tup);
|
|
@@ -1220,6 +1239,8 @@ AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(procForm->pronamespace));
|
|
}
|
|
+ /* SELinux permission checks */
|
|
+ sepgsql_proc_alter(procOid, NULL, InvalidOid);
|
|
|
|
memset(repl_null, false, sizeof(repl_null));
|
|
memset(repl_repl, false, sizeof(repl_repl));
|
|
@@ -1258,6 +1279,59 @@ AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
|
|
}
|
|
|
|
/*
|
|
+ * ALTER FUNCTION name(args,...) SECURITY_LABEL [=] newlabel
|
|
+ */
|
|
+void
|
|
+AlterFunctionSecLabel(List *name, List *argtypes, DefElem *seclabel)
|
|
+{
|
|
+ Relation rel;
|
|
+ HeapTuple oldtup;
|
|
+ HeapTuple newtup;
|
|
+ Oid procOid;
|
|
+ Oid secid;
|
|
+ bool replaces[Natts_pg_proc];
|
|
+
|
|
+ /* open pg_proc system catalog */
|
|
+ rel = heap_open(ProcedureRelationId, RowExclusiveLock);
|
|
+
|
|
+ procOid = LookupFuncNameTypeNames(name, argtypes, false);
|
|
+
|
|
+ oldtup = SearchSysCache(PROCOID,
|
|
+ ObjectIdGetDatum(procOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(oldtup))
|
|
+ elog(ERROR, "cache lookup failed for function %u", procOid);
|
|
+
|
|
+ memset(replaces, false, sizeof(replaces));
|
|
+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
|
|
+ NULL, NULL, replaces);
|
|
+
|
|
+ if (!HeapTupleHasSecid(newtup))
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("Unable to set security label on \"%s\"",
|
|
+ get_func_name(procOid))));
|
|
+
|
|
+ ReleaseSysCache(oldtup);
|
|
+
|
|
+ /* DAC permission checks */
|
|
+ if (!pg_proc_ownercheck(HeapTupleGetOid(newtup), GetUserId()))
|
|
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
|
|
+ get_func_name(HeapTupleGetOid(newtup)));
|
|
+
|
|
+ /* SELinux permission checks */
|
|
+ secid = sepgsql_proc_relabel(procOid, seclabel);
|
|
+ HeapTupleSetSecid(newtup, secid);
|
|
+
|
|
+ simple_heap_update(rel, &newtup->t_self, newtup);
|
|
+ CatalogUpdateIndexes(rel, newtup);
|
|
+
|
|
+ heap_freetuple(newtup);
|
|
+
|
|
+ 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).
|
|
@@ -1296,6 +1370,9 @@ AlterFunction(AlterFunctionStmt *stmt)
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
|
|
NameListToString(stmt->func->funcname));
|
|
|
|
+ /* SELinux checks permissions */
|
|
+ sepgsql_proc_alter(funcOid, NULL, InvalidOid);
|
|
+
|
|
if (procForm->proisagg)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
@@ -1473,6 +1550,7 @@ CreateCast(CreateCastStmt *stmt)
|
|
char sourcetyptype;
|
|
char targettyptype;
|
|
Oid funcid;
|
|
+ Oid secid;
|
|
int nargs;
|
|
char castcontext;
|
|
char castmethod;
|
|
@@ -1674,6 +1752,8 @@ CreateCast(CreateCastStmt *stmt)
|
|
castcontext = 0; /* keep compiler quiet */
|
|
break;
|
|
}
|
|
+ /* SELinux permission check */
|
|
+ secid = sepgsql_cast_create(sourcetypeid, targettypeid, funcid);
|
|
|
|
relation = heap_open(CastRelationId, RowExclusiveLock);
|
|
|
|
@@ -1704,6 +1784,9 @@ CreateCast(CreateCastStmt *stmt)
|
|
|
|
tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls);
|
|
|
|
+ if (HeapTupleHasSecid(tuple))
|
|
+ HeapTupleSetSecid(tuple, secid);
|
|
+
|
|
simple_heap_insert(relation, tuple);
|
|
|
|
CatalogUpdateIndexes(relation, tuple);
|
|
@@ -1897,6 +1980,9 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg,
|
|
NameStr(proc->proname),
|
|
newschema)));
|
|
|
|
+ /* SELinux checks permissions */
|
|
+ sepgsql_proc_alter(procOid, NULL, nspOid);
|
|
+
|
|
/* OK, modify the pg_proc row */
|
|
|
|
/* tup is a copy, so we can scribble directly on it */
|
|
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
|
|
index 99ab0e5..78b4455 100644
|
|
--- a/src/backend/commands/indexcmds.c
|
|
+++ b/src/backend/commands/indexcmds.c
|
|
@@ -37,6 +37,7 @@
|
|
#include "parser/parse_coerce.h"
|
|
#include "parser/parse_func.h"
|
|
#include "parser/parsetree.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/lmgr.h"
|
|
#include "storage/proc.h"
|
|
#include "storage/procarray.h"
|
|
@@ -197,6 +198,9 @@ DefineIndex(RangeVar *heapRelation,
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(namespaceId));
|
|
+
|
|
+ /* SELinux checks */
|
|
+ sepgsql_index_create(relationId, namespaceId);
|
|
}
|
|
|
|
/*
|
|
diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c
|
|
index 1e5c92e..6949b54 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 "security/sepgsql.h"
|
|
#include "storage/lmgr.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/lsyscache.h"
|
|
@@ -140,6 +141,9 @@ LockTableRecurse(Oid reloid, RangeVar *rv,
|
|
errmsg("\"%s\" is not a table",
|
|
RelationGetRelationName(rel))));
|
|
|
|
+ /* SELinux: check db_table:{lock} permission */
|
|
+ sepgsql_relation_lock(reloid);
|
|
+
|
|
/*
|
|
* 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 84dc2ce..b23919d 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 "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/fmgroids.h"
|
|
@@ -177,6 +178,7 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
|
|
HeapTuple tup;
|
|
Datum values[Natts_pg_opfamily];
|
|
bool nulls[Natts_pg_opfamily];
|
|
+ Oid opfSecid;
|
|
NameData opfName;
|
|
ObjectAddress myself,
|
|
referenced;
|
|
@@ -197,6 +199,9 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
|
|
errmsg("operator family \"%s\" for access method \"%s\" already exists",
|
|
opfname, amname)));
|
|
|
|
+ /* SELinux check permission */
|
|
+ opfSecid = sepgsql_opfamily_create(opfname, namespaceoid);
|
|
+
|
|
/*
|
|
* Okay, let's create the pg_opfamily entry.
|
|
*/
|
|
@@ -210,6 +215,8 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid)
|
|
values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId());
|
|
|
|
tup = heap_form_tuple(rel->rd_att, values, nulls);
|
|
+ if (HeapTupleHasSecid(tup))
|
|
+ HeapTupleSetSecid(tup, opfSecid);
|
|
|
|
opfamilyoid = simple_heap_insert(rel, tup);
|
|
|
|
@@ -265,6 +272,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
|
|
Form_pg_am pg_am;
|
|
Datum values[Natts_pg_opclass];
|
|
bool nulls[Natts_pg_opclass];
|
|
+ Oid opcSecid;
|
|
AclResult aclresult;
|
|
NameData opcName;
|
|
ObjectAddress myself,
|
|
@@ -353,6 +361,9 @@ DefineOpClass(CreateOpClassStmt *stmt)
|
|
NameListToString(stmt->opfamilyname), stmt->amname)));
|
|
opfamilyoid = HeapTupleGetOid(tup);
|
|
|
|
+ /* SELinux checks permission */
|
|
+ sepgsql_opfamily_alter(opfamilyoid, NULL);
|
|
+
|
|
/*
|
|
* XXX given the superuser check above, there's no need for an
|
|
* ownership check here
|
|
@@ -371,6 +382,9 @@ DefineOpClass(CreateOpClassStmt *stmt)
|
|
{
|
|
opfamilyoid = HeapTupleGetOid(tup);
|
|
|
|
+ /* SELinux checks permission */
|
|
+ sepgsql_opfamily_alter(opfamilyoid, NULL);
|
|
+
|
|
/*
|
|
* XXX given the superuser check above, there's no need for an
|
|
* ownership check here
|
|
@@ -441,6 +455,8 @@ DefineOpClass(CreateOpClassStmt *stmt)
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
|
|
get_func_name(funcOid));
|
|
#endif
|
|
+ /* SELinux check permission */
|
|
+ sepgsql_opfamily_add_operator(opfamilyoid, operOid);
|
|
|
|
/* Save the info */
|
|
member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
|
|
@@ -465,6 +481,8 @@ DefineOpClass(CreateOpClassStmt *stmt)
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
|
|
get_func_name(funcOid));
|
|
#endif
|
|
+ /* SELinux check permission */
|
|
+ sepgsql_opfamily_add_procedure(opfamilyoid, funcOid);
|
|
|
|
/* Save the info */
|
|
member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
|
|
@@ -531,6 +549,9 @@ DefineOpClass(CreateOpClassStmt *stmt)
|
|
errmsg("operator class \"%s\" for access method \"%s\" already exists",
|
|
opcname, stmt->amname)));
|
|
|
|
+ /* SELinux permission check */
|
|
+ opcSecid = sepgsql_opclass_create(opcname, namespaceoid);
|
|
+
|
|
/*
|
|
* If we are creating a default opclass, check there isn't one already.
|
|
* (Note we do not restrict this test to visible opclasses; this ensures
|
|
@@ -657,6 +678,7 @@ DefineOpFamily(CreateOpFamilyStmt *stmt)
|
|
HeapTuple tup;
|
|
Datum values[Natts_pg_opfamily];
|
|
bool nulls[Natts_pg_opfamily];
|
|
+ Oid opfSecid;
|
|
AclResult aclresult;
|
|
NameData opfName;
|
|
ObjectAddress myself,
|
|
@@ -699,6 +721,9 @@ DefineOpFamily(CreateOpFamilyStmt *stmt)
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
errmsg("must be superuser to create an operator family")));
|
|
|
|
+ /* SELinux permission check */
|
|
+ opfSecid = sepgsql_opfamily_create(opfname, namespaceoid);
|
|
+
|
|
rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock);
|
|
|
|
/*
|
|
@@ -773,6 +798,7 @@ AlterOpFamily(AlterOpFamilyStmt *stmt)
|
|
int maxOpNumber, /* amstrategies value */
|
|
maxProcNumber; /* amsupport value */
|
|
HeapTuple tup;
|
|
+ Oid opfSecid;
|
|
Form_pg_am pg_am;
|
|
|
|
/* Get necessary info about access method */
|
|
@@ -805,6 +831,7 @@ AlterOpFamily(AlterOpFamilyStmt *stmt)
|
|
errmsg("operator family \"%s\" does not exist for access method \"%s\"",
|
|
NameListToString(stmt->opfamilyname), stmt->amname)));
|
|
opfamilyoid = HeapTupleGetOid(tup);
|
|
+ opfSecid = HeapTupleGetSecid(tup);
|
|
ReleaseSysCache(tup);
|
|
|
|
/*
|
|
@@ -817,6 +844,9 @@ AlterOpFamily(AlterOpFamilyStmt *stmt)
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
errmsg("must be superuser to alter an operator family")));
|
|
|
|
+ /* SELinux permission checks */
|
|
+ sepgsql_opfamily_alter(opfamilyoid, NULL);
|
|
+
|
|
/*
|
|
* ADD and DROP cases need separate code from here on down.
|
|
*/
|
|
@@ -893,6 +923,8 @@ AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
|
|
get_func_name(funcOid));
|
|
#endif
|
|
+ /* SELinux permission check */
|
|
+ sepgsql_opfamily_add_operator(opfamilyoid, operOid);
|
|
|
|
/* Save the info */
|
|
member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
|
|
@@ -917,6 +949,8 @@ AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid,
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
|
|
get_func_name(funcOid));
|
|
#endif
|
|
+ /* SELinux permission check */
|
|
+ sepgsql_opfamily_add_procedure(opfamilyoid, funcOid);
|
|
|
|
/* Save the info */
|
|
member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember));
|
|
@@ -1815,6 +1849,9 @@ RenameOpClass(List *name, const char *access_method, const char *newname)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(namespaceOid));
|
|
|
|
+ /* SELinux permission checks */
|
|
+ sepgsql_opclass_alter(opcOid, newname);
|
|
+
|
|
/* rename */
|
|
namestrcpy(&(((Form_pg_opclass) GETSTRUCT(tup))->opcname), newname);
|
|
simple_heap_update(rel, &tup->t_self, tup);
|
|
@@ -1915,6 +1952,9 @@ RenameOpFamily(List *name, const char *access_method, const char *newname)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(namespaceOid));
|
|
|
|
+ /* SELinux check permissions */
|
|
+ sepgsql_opfamily_alter(opfOid, newname);
|
|
+
|
|
/* rename */
|
|
namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname);
|
|
simple_heap_update(rel, &tup->t_self, tup);
|
|
@@ -2035,6 +2075,8 @@ AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(namespaceOid));
|
|
}
|
|
+ /* SELinux permission check */
|
|
+ sepgsql_opclass_alter(HeapTupleGetOid(tup), NULL);
|
|
|
|
/*
|
|
* Modify the owner --- okay to scribble on tup because it's a copy
|
|
@@ -2162,6 +2204,8 @@ AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(namespaceOid));
|
|
}
|
|
+ /* SELinux permission checks */
|
|
+ sepgsql_opfamily_alter(HeapTupleGetOid(tup), NULL);
|
|
|
|
/*
|
|
* 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 6c05611..c934424 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 "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "utils/rel.h"
|
|
@@ -432,6 +433,8 @@ AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(oprForm->oprnamespace));
|
|
}
|
|
+ /* SELinux permission check */
|
|
+ sepgsql_operator_alter(operOid);
|
|
|
|
/*
|
|
* Modify the owner --- okay to scribble on tup because it's a copy
|
|
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
|
|
index 3faf445..8f1e212 100644
|
|
--- a/src/backend/commands/proclang.c
|
|
+++ b/src/backend/commands/proclang.c
|
|
@@ -30,6 +30,7 @@
|
|
#include "miscadmin.h"
|
|
#include "parser/gramparse.h"
|
|
#include "parser/parse_func.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/fmgroids.h"
|
|
@@ -151,7 +152,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
|
NIL,
|
|
PointerGetDatum(NULL),
|
|
1,
|
|
- 0);
|
|
+ 0,
|
|
+ NULL);
|
|
}
|
|
|
|
/*
|
|
@@ -186,7 +188,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
|
|
NIL,
|
|
PointerGetDatum(NULL),
|
|
1,
|
|
- 0);
|
|
+ 0,
|
|
+ NULL);
|
|
}
|
|
}
|
|
else
|
|
@@ -275,10 +278,16 @@ create_proc_lang(const char *languageName,
|
|
bool nulls[Natts_pg_language];
|
|
NameData langname;
|
|
HeapTuple tup;
|
|
+ Oid langSecid;
|
|
ObjectAddress myself,
|
|
referenced;
|
|
|
|
/*
|
|
+ * SELinux permission checks
|
|
+ */
|
|
+ langSecid = sepgsql_language_create(languageName, handlerOid, valOid);
|
|
+
|
|
+ /*
|
|
* Insert the new language into pg_language
|
|
*/
|
|
rel = heap_open(LanguageRelationId, RowExclusiveLock);
|
|
@@ -297,6 +306,8 @@ create_proc_lang(const char *languageName,
|
|
nulls[Anum_pg_language_lanacl - 1] = true;
|
|
|
|
tup = heap_form_tuple(tupDesc, values, nulls);
|
|
+ if (HeapTupleHasSecid(tup))
|
|
+ HeapTupleSetSecid(tup, langSecid);
|
|
|
|
simple_heap_insert(rel, tup);
|
|
|
|
@@ -518,6 +529,9 @@ RenameLanguage(const char *oldname, const char *newname)
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE,
|
|
oldname);
|
|
|
|
+ /* SELinux permission checks */
|
|
+ sepgsql_language_alter(HeapTupleGetOid(tup));
|
|
+
|
|
/* rename */
|
|
namestrcpy(&(((Form_pg_language) GETSTRUCT(tup))->lanname), newname);
|
|
simple_heap_update(rel, &tup->t_self, tup);
|
|
@@ -613,6 +627,9 @@ AlterLanguageOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
|
|
/* Must be able to become new owner */
|
|
check_is_member_of_role(GetUserId(), newOwnerId);
|
|
|
|
+ /* SELinux permission checks */
|
|
+ sepgsql_language_alter(HeapTupleGetOid(tup));
|
|
+
|
|
memset(repl_null, false, sizeof(repl_null));
|
|
memset(repl_repl, false, sizeof(repl_repl));
|
|
|
|
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
|
|
index 0d047cf..748bdd6 100644
|
|
--- a/src/backend/commands/schemacmds.c
|
|
+++ b/src/backend/commands/schemacmds.c
|
|
@@ -25,6 +25,7 @@
|
|
#include "commands/schemacmds.h"
|
|
#include "miscadmin.h"
|
|
#include "parser/parse_utilcmd.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "tcop/utility.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
@@ -48,6 +49,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
|
|
ListCell *parsetree_item;
|
|
Oid owner_uid;
|
|
Oid saved_uid;
|
|
+ Oid nspsecid;
|
|
int save_sec_context;
|
|
AclResult aclresult;
|
|
|
|
@@ -75,6 +77,10 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
|
|
|
|
check_is_member_of_role(saved_uid, owner_uid);
|
|
|
|
+ /* SELinux checks db_schema:{create} */
|
|
+ nspsecid = sepgsql_schema_create(schemaName, false,
|
|
+ (DefElem *)stmt->secLabel);
|
|
+
|
|
/* 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, nspsecid);
|
|
|
|
/* Advance cmd counter to make the namespace visible */
|
|
CommandCounterIncrement();
|
|
@@ -268,8 +274,7 @@ RenameSchema(const char *oldname, const char *newname)
|
|
errmsg("schema \"%s\" does not exist", oldname)));
|
|
|
|
/* make sure the new name doesn't exist */
|
|
- if (HeapTupleIsValid(
|
|
- SearchSysCache(NAMESPACENAME,
|
|
+ if (HeapTupleIsValid(SearchSysCache(NAMESPACENAME,
|
|
CStringGetDatum(newname),
|
|
0, 0, 0)))
|
|
ereport(ERROR,
|
|
@@ -287,6 +292,9 @@ RenameSchema(const char *oldname, const char *newname)
|
|
aclcheck_error(aclresult, ACL_KIND_DATABASE,
|
|
get_database_name(MyDatabaseId));
|
|
|
|
+ /* SELinux checks db_schema:{setattr} */
|
|
+ sepgsql_schema_alter(HeapTupleGetOid(tup));
|
|
+
|
|
if (!allowSystemTableMods && IsReservedName(newname))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_RESERVED_NAME),
|
|
@@ -398,6 +406,9 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
|
|
aclcheck_error(aclresult, ACL_KIND_DATABASE,
|
|
get_database_name(MyDatabaseId));
|
|
|
|
+ /* SELinux checks db_schema:{setattr} */
|
|
+ sepgsql_schema_alter(HeapTupleGetOid(tup));
|
|
+
|
|
memset(repl_null, false, sizeof(repl_null));
|
|
memset(repl_repl, false, sizeof(repl_repl));
|
|
|
|
@@ -432,3 +443,51 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId)
|
|
}
|
|
|
|
}
|
|
+
|
|
+/*
|
|
+ * ALTER SCHEMA name SECURITY_LABEL [=] newlabel
|
|
+ */
|
|
+void
|
|
+AlterSchemaSecLabel(const char *name, DefElem *secLabel)
|
|
+{
|
|
+ Relation rel;
|
|
+ HeapTuple oldtup;
|
|
+ HeapTuple newtup;
|
|
+ Oid secid;
|
|
+ bool replaces[Natts_pg_namespace];
|
|
+
|
|
+ /* open pg_namespace relation */
|
|
+ rel = heap_open(NamespaceRelationId, RowExclusiveLock);
|
|
+ oldtup = SearchSysCache(NAMESPACENAME,
|
|
+ CStringGetDatum(name),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(oldtup))
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_UNDEFINED_SCHEMA),
|
|
+ errmsg("schema \"%s\" does not exist", name)));
|
|
+
|
|
+ memset(replaces, false, sizeof(replaces));
|
|
+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
|
|
+ NULL, NULL, replaces);
|
|
+ if (!HeapTupleHasSecid(newtup))
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("Unable to set security label on \"%s\"", name)));
|
|
+
|
|
+ ReleaseSysCache(oldtup);
|
|
+
|
|
+ /* DAC permission check */
|
|
+ if (!pg_namespace_ownercheck(HeapTupleGetOid(newtup), GetUserId()))
|
|
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, name);
|
|
+ /* SELinux checks db_schema:{setattr relabelfrom relabelto} */
|
|
+ secid = sepgsql_schema_relabel(HeapTupleGetOid(newtup), secLabel);
|
|
+ HeapTupleSetSecid(newtup, secid);
|
|
+
|
|
+ simple_heap_update(rel, &newtup->t_self, newtup);
|
|
+
|
|
+ CatalogUpdateIndexes(rel, newtup);
|
|
+
|
|
+ heap_freetuple(newtup);
|
|
+
|
|
+ heap_close(rel, RowExclusiveLock);
|
|
+}
|
|
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
|
|
index abc65aa..9507ef1 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 "security/sepgsql.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "storage/lmgr.h"
|
|
#include "storage/proc.h"
|
|
@@ -201,6 +202,7 @@ DefineSequence(CreateSeqStmt *seq)
|
|
stmt->options = list_make1(defWithOids(false));
|
|
stmt->oncommit = ONCOMMIT_NOOP;
|
|
stmt->tablespacename = NULL;
|
|
+ stmt->secLabel = seq->secLabel;
|
|
|
|
seqoid = DefineRelation(stmt, RELKIND_SEQUENCE);
|
|
|
|
@@ -328,6 +330,8 @@ AlterSequence(AlterSeqStmt *stmt)
|
|
if (!pg_class_ownercheck(relid, GetUserId()))
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
|
|
stmt->sequence->relname);
|
|
+ /* SELinux checks db_sequence:{setattr} */
|
|
+ sepgsql_relation_alter(relid, NULL, InvalidOid);
|
|
|
|
/* do the work */
|
|
AlterSequenceInternal(relid, stmt->options);
|
|
@@ -467,6 +471,9 @@ nextval_internal(Oid relid)
|
|
errmsg("permission denied for sequence %s",
|
|
RelationGetRelationName(seqrel))));
|
|
|
|
+ /* SELinux check db_sequence:{next_value} */
|
|
+ sepgsql_sequence_next_value(elm->relid);
|
|
+
|
|
if (elm->last != elm->cached) /* some numbers were cached */
|
|
{
|
|
Assert(elm->last_valid);
|
|
@@ -662,6 +669,9 @@ currval_oid(PG_FUNCTION_ARGS)
|
|
errmsg("permission denied for sequence %s",
|
|
RelationGetRelationName(seqrel))));
|
|
|
|
+ /* SELinux check db_sequence:{get_value} */
|
|
+ sepgsql_sequence_get_value(elm->relid);
|
|
+
|
|
if (!elm->last_valid)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
|
@@ -706,6 +716,9 @@ lastval(PG_FUNCTION_ARGS)
|
|
errmsg("permission denied for sequence %s",
|
|
RelationGetRelationName(seqrel))));
|
|
|
|
+ /* SELinux check db_sequence:{get_value} */
|
|
+ sepgsql_sequence_get_value(last_used_seq->relid);
|
|
+
|
|
result = last_used_seq->last;
|
|
relation_close(seqrel, NoLock);
|
|
|
|
@@ -742,6 +755,9 @@ do_setval(Oid relid, int64 next, bool iscalled)
|
|
errmsg("permission denied for sequence %s",
|
|
RelationGetRelationName(seqrel))));
|
|
|
|
+ /* SELinux check db_sequence:{set_value} */
|
|
+ sepgsql_sequence_set_value(elm->relid);
|
|
+
|
|
/* lock page' buffer and read tuple */
|
|
seq = read_info(elm, seqrel, &buf);
|
|
|
|
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
|
|
index 96dda00..85b8800 100644
|
|
--- a/src/backend/commands/tablecmds.c
|
|
+++ b/src/backend/commands/tablecmds.c
|
|
@@ -62,6 +62,7 @@
|
|
#include "parser/parser.h"
|
|
#include "rewrite/rewriteDefine.h"
|
|
#include "rewrite/rewriteHandler.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "storage/lmgr.h"
|
|
#include "storage/smgr.h"
|
|
@@ -260,8 +261,8 @@ static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
|
static void ATRewriteTables(List **wqueue);
|
|
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
|
|
static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
|
|
-static void ATSimplePermissions(Relation rel, bool allowView);
|
|
-static void ATSimplePermissionsRelationOrIndex(Relation rel);
|
|
+static void ATSimplePermissions(Relation rel, const char *colname, bool allowView);
|
|
+static void ATSimplePermissionsRelationOrIndex(Relation rel, const char *colname);
|
|
static void ATSimpleRecursion(List **wqueue, Relation rel,
|
|
AlterTableCmd *cmd, bool recurse);
|
|
static void ATOneLevelRecursion(List **wqueue, Relation rel,
|
|
@@ -351,6 +352,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
|
|
List *rawDefaults;
|
|
List *cookedDefaults;
|
|
Datum reloptions;
|
|
+ Oid *secLabels;
|
|
ListCell *listptr;
|
|
AttrNumber attnum;
|
|
static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
|
|
@@ -454,6 +456,16 @@ DefineRelation(CreateStmt *stmt, char relkind)
|
|
localHasOids = interpretOidsOption(stmt->options);
|
|
descriptor->tdhasoid = (localHasOids || parentOidCount > 0);
|
|
|
|
+ /* SELinux checks db_table:{create} and db_column:{create} */
|
|
+ secLabels = sepgsql_relation_create(relname,
|
|
+ relkind,
|
|
+ descriptor,
|
|
+ namespaceId,
|
|
+ (DefElem *)stmt->secLabel,
|
|
+ schema,
|
|
+ false,
|
|
+ true);
|
|
+
|
|
/*
|
|
* Find columns with default values and prepare for insertion of the
|
|
* defaults. Pre-cooked (that is, inherited) defaults go into a list of
|
|
@@ -523,7 +535,8 @@ DefineRelation(CreateStmt *stmt, char relkind)
|
|
parentOidCount,
|
|
stmt->oncommit,
|
|
reloptions,
|
|
- allowSystemTableMods);
|
|
+ allowSystemTableMods,
|
|
+ secLabels);
|
|
|
|
StoreCatalogInheritance(relationId, inheritOids);
|
|
|
|
@@ -897,6 +910,8 @@ ExecuteTruncate(TruncateStmt *stmt)
|
|
if (!pg_class_ownercheck(seq_relid, GetUserId()))
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
|
|
RelationGetRelationName(seq_rel));
|
|
+ /* SELinux checks */
|
|
+ sepgsql_relation_alter(seq_relid, NULL, InvalidOid);
|
|
|
|
seq_relids = lappend_oid(seq_relids, seq_relid);
|
|
|
|
@@ -1052,6 +1067,9 @@ truncate_check_rel(Relation rel)
|
|
errmsg("permission denied: \"%s\" is a system catalog",
|
|
RelationGetRelationName(rel))));
|
|
|
|
+ /* SELinux: check db_table:{delete} permission */
|
|
+ sepgsql_relation_truncate(rel);
|
|
+
|
|
/*
|
|
* We can never allow truncation of shared or nailed-in-cache relations,
|
|
* because we can't support changing their relfilenode values.
|
|
@@ -1226,6 +1244,8 @@ MergeAttributes(List *schema, List *supers, bool istemp,
|
|
if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
|
|
RelationGetRelationName(relation));
|
|
+ /* SELinux checks db_table:{setattr} */
|
|
+ sepgsql_relation_alter(RelationGetRelid(relation), NULL, InvalidOid);
|
|
|
|
/*
|
|
* Reject duplications in the list of parents.
|
|
@@ -1931,6 +1951,9 @@ renameatt(Oid myrelid,
|
|
errmsg("cannot rename system column \"%s\"",
|
|
oldattname)));
|
|
|
|
+ /* SELinux checks db_column:{setattr} */
|
|
+ sepgsql_attribute_alter(myrelid, oldattname);
|
|
+
|
|
/*
|
|
* if the attribute is inherited, forbid the renaming, unless we are
|
|
* already inside a recursive rename.
|
|
@@ -2036,6 +2059,9 @@ RenameRelation(Oid myrelid, const char *newrelname, ObjectType reltype)
|
|
Oid namespaceId;
|
|
char relkind;
|
|
|
|
+ /* SELinux checks */
|
|
+ sepgsql_relation_alter(myrelid, newrelname, InvalidOid);
|
|
+
|
|
/*
|
|
* Grab an exclusive lock on the target table, index, sequence or view,
|
|
* which we will NOT release until end of transaction.
|
|
@@ -2369,14 +2395,14 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
|
|
switch (cmd->subtype)
|
|
{
|
|
case AT_AddColumn: /* ADD COLUMN */
|
|
- ATSimplePermissions(rel, false);
|
|
+ ATSimplePermissions(rel, NULL, false);
|
|
/* Performs own recursion */
|
|
ATPrepAddColumn(wqueue, rel, recurse, cmd);
|
|
pass = AT_PASS_ADD_COL;
|
|
break;
|
|
case AT_AddColumnToView: /* add column via CREATE OR REPLACE
|
|
* VIEW */
|
|
- ATSimplePermissions(rel, true);
|
|
+ ATSimplePermissions(rel, NULL, true);
|
|
/* Performs own recursion */
|
|
ATPrepAddColumn(wqueue, rel, recurse, cmd);
|
|
pass = AT_PASS_ADD_COL;
|
|
@@ -2389,19 +2415,19 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
|
|
* substitutes default values into INSERTs before it expands
|
|
* rules.
|
|
*/
|
|
- ATSimplePermissions(rel, true);
|
|
+ ATSimplePermissions(rel, cmd->name, true);
|
|
ATSimpleRecursion(wqueue, rel, cmd, recurse);
|
|
/* No command-specific prep needed */
|
|
pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP;
|
|
break;
|
|
case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
|
|
- ATSimplePermissions(rel, false);
|
|
+ ATSimplePermissions(rel, cmd->name, false);
|
|
ATSimpleRecursion(wqueue, rel, cmd, recurse);
|
|
/* No command-specific prep needed */
|
|
pass = AT_PASS_DROP;
|
|
break;
|
|
case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
|
|
- ATSimplePermissions(rel, false);
|
|
+ ATSimplePermissions(rel, cmd->name, false);
|
|
ATSimpleRecursion(wqueue, rel, cmd, recurse);
|
|
/* No command-specific prep needed */
|
|
pass = AT_PASS_ADD_CONSTR;
|
|
@@ -2413,13 +2439,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
|
|
pass = AT_PASS_COL_ATTRS;
|
|
break;
|
|
case AT_SetStorage: /* ALTER COLUMN STORAGE */
|
|
- ATSimplePermissions(rel, false);
|
|
+ ATSimplePermissions(rel, cmd->name, false);
|
|
ATSimpleRecursion(wqueue, rel, cmd, recurse);
|
|
/* No command-specific prep needed */
|
|
pass = AT_PASS_COL_ATTRS;
|
|
break;
|
|
case AT_DropColumn: /* DROP COLUMN */
|
|
- ATSimplePermissions(rel, false);
|
|
+ ATSimplePermissions(rel, NULL, false);
|
|
/* Recursion occurs during execution phase */
|
|
/* No command-specific prep needed except saving recurse flag */
|
|
if (recurse)
|
|
@@ -2427,13 +2453,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
|
|
pass = AT_PASS_DROP;
|
|
break;
|
|
case AT_AddIndex: /* ADD INDEX */
|
|
- ATSimplePermissions(rel, false);
|
|
+ ATSimplePermissions(rel, NULL, false);
|
|
/* This command never recurses */
|
|
/* No command-specific prep needed */
|
|
pass = AT_PASS_ADD_INDEX;
|
|
break;
|
|
case AT_AddConstraint: /* ADD CONSTRAINT */
|
|
- ATSimplePermissions(rel, false);
|
|
+ ATSimplePermissions(rel, NULL, false);
|
|
/* Recursion occurs during execution phase */
|
|
/* No command-specific prep needed except saving recurse flag */
|
|
if (recurse)
|
|
@@ -2441,7 +2467,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
|
|
pass = AT_PASS_ADD_CONSTR;
|
|
break;
|
|
case AT_DropConstraint: /* DROP CONSTRAINT */
|
|
- ATSimplePermissions(rel, false);
|
|
+ ATSimplePermissions(rel, NULL, false);
|
|
/* Recursion occurs during execution phase */
|
|
/* No command-specific prep needed except saving recurse flag */
|
|
if (recurse)
|
|
@@ -2449,7 +2475,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
|
|
pass = AT_PASS_DROP;
|
|
break;
|
|
case AT_AlterColumnType: /* ALTER COLUMN TYPE */
|
|
- ATSimplePermissions(rel, false);
|
|
+ ATSimplePermissions(rel, cmd->name, false);
|
|
/* Performs own recursion */
|
|
ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd);
|
|
pass = AT_PASS_ALTER_TYPE;
|
|
@@ -2461,20 +2487,20 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
|
|
break;
|
|
case AT_ClusterOn: /* CLUSTER ON */
|
|
case AT_DropCluster: /* SET WITHOUT CLUSTER */
|
|
- ATSimplePermissions(rel, false);
|
|
+ ATSimplePermissions(rel, NULL, false);
|
|
/* These commands never recurse */
|
|
/* No command-specific prep needed */
|
|
pass = AT_PASS_MISC;
|
|
break;
|
|
case AT_AddOids: /* SET WITH OIDS */
|
|
- ATSimplePermissions(rel, false);
|
|
+ ATSimplePermissions(rel, NULL, false);
|
|
/* Performs own recursion */
|
|
if (!rel->rd_rel->relhasoids || recursing)
|
|
ATPrepAddOids(wqueue, rel, recurse, cmd);
|
|
pass = AT_PASS_ADD_COL;
|
|
break;
|
|
case AT_DropOids: /* SET WITHOUT OIDS */
|
|
- ATSimplePermissions(rel, false);
|
|
+ ATSimplePermissions(rel, NULL, false);
|
|
/* Performs own recursion */
|
|
if (rel->rd_rel->relhasoids)
|
|
{
|
|
@@ -2488,14 +2514,14 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
|
|
pass = AT_PASS_DROP;
|
|
break;
|
|
case AT_SetTableSpace: /* SET TABLESPACE */
|
|
- ATSimplePermissionsRelationOrIndex(rel);
|
|
+ ATSimplePermissionsRelationOrIndex(rel, NULL);
|
|
/* This command never recurses */
|
|
ATPrepSetTableSpace(tab, rel, cmd->name);
|
|
pass = AT_PASS_MISC; /* doesn't actually matter */
|
|
break;
|
|
case AT_SetRelOptions: /* SET (...) */
|
|
case AT_ResetRelOptions: /* RESET (...) */
|
|
- ATSimplePermissionsRelationOrIndex(rel);
|
|
+ ATSimplePermissionsRelationOrIndex(rel, NULL);
|
|
/* This command never recurses */
|
|
/* No command-specific prep needed */
|
|
pass = AT_PASS_MISC;
|
|
@@ -2514,7 +2540,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
|
|
case AT_DisableRule:
|
|
case AT_AddInherit: /* INHERIT / NO INHERIT */
|
|
case AT_DropInherit:
|
|
- ATSimplePermissions(rel, false);
|
|
+ ATSimplePermissions(rel, NULL, false);
|
|
/* These commands never recurse */
|
|
/* No command-specific prep needed */
|
|
pass = AT_PASS_MISC;
|
|
@@ -2860,8 +2886,9 @@ ATRewriteTables(List **wqueue)
|
|
/*
|
|
* The new relation is local to our transaction and we know
|
|
* nothing depends on it, so DROP_RESTRICT should be OK.
|
|
+ * SELinux does not apply any permission checks here.
|
|
*/
|
|
- performDeletion(&object, DROP_RESTRICT);
|
|
+ performDeletionNoPerms(&object, DROP_RESTRICT);
|
|
/* performDeletion does CommandCounterIncrement at end */
|
|
|
|
/*
|
|
@@ -3086,11 +3113,14 @@ 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 (HeapTupleHasSecid(tuple))
|
|
+ tupSecid = HeapTupleGetSecid(tuple);
|
|
|
|
/* Set dropped attributes to null in new tuple */
|
|
foreach(lc, dropped_attrs)
|
|
@@ -3122,6 +3152,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
|
|
/* Preserve OID, if any */
|
|
if (newTupDesc->tdhasoid)
|
|
HeapTupleSetOid(tuple, tupOid);
|
|
+ /* Preserve SID, if any */
|
|
+ if (HeapTupleHasSecid(tuple))
|
|
+ HeapTupleSetSecid(tuple, tupSecid);
|
|
}
|
|
|
|
/* Now check any constraints on the possibly-changed tuple */
|
|
@@ -3223,7 +3256,7 @@ ATGetQueueEntry(List **wqueue, Relation rel)
|
|
* - Ensure that it is not a system table
|
|
*/
|
|
static void
|
|
-ATSimplePermissions(Relation rel, bool allowView)
|
|
+ATSimplePermissions(Relation rel, const char *colName, bool allowView)
|
|
{
|
|
if (rel->rd_rel->relkind != RELKIND_RELATION)
|
|
{
|
|
@@ -3247,6 +3280,12 @@ ATSimplePermissions(Relation rel, bool allowView)
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
|
|
RelationGetRelationName(rel));
|
|
|
|
+ /* SELinux checks */
|
|
+ if (!colName)
|
|
+ sepgsql_relation_alter(RelationGetRelid(rel), NULL, InvalidOid);
|
|
+ else
|
|
+ sepgsql_attribute_alter(RelationGetRelid(rel), colName);
|
|
+
|
|
if (!allowSystemTableMods && IsSystemRelation(rel))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
@@ -3262,7 +3301,7 @@ ATSimplePermissions(Relation rel, bool allowView)
|
|
* - Ensure that it is not a system table
|
|
*/
|
|
static void
|
|
-ATSimplePermissionsRelationOrIndex(Relation rel)
|
|
+ATSimplePermissionsRelationOrIndex(Relation rel, const char *colName)
|
|
{
|
|
if (rel->rd_rel->relkind != RELKIND_RELATION &&
|
|
rel->rd_rel->relkind != RELKIND_INDEX)
|
|
@@ -3276,6 +3315,12 @@ ATSimplePermissionsRelationOrIndex(Relation rel)
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
|
|
RelationGetRelationName(rel));
|
|
|
|
+ /* SELinux checks */
|
|
+ if (!colName)
|
|
+ sepgsql_relation_alter(RelationGetRelid(rel), NULL, InvalidOid);
|
|
+ else
|
|
+ sepgsql_attribute_alter(RelationGetRelid(rel), colName);
|
|
+
|
|
if (!allowSystemTableMods && IsSystemRelation(rel))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
@@ -3519,6 +3564,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
|
|
HeapTuple typeTuple;
|
|
Oid typeOid;
|
|
int32 typmod;
|
|
+ Oid attsecid;
|
|
Form_pg_type tform;
|
|
Expr *defval;
|
|
|
|
@@ -3556,6 +3602,9 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
|
|
errmsg("child table \"%s\" has a conflicting \"%s\" column",
|
|
RelationGetRelationName(rel), colDef->colname)));
|
|
|
|
+ /* SELinux checks db_column:{setattr} */
|
|
+ sepgsql_attribute_alter(myrelid, colDef->colname);
|
|
+
|
|
/* Bump the existing child att's inhcount */
|
|
childatt->attinhcount++;
|
|
simple_heap_update(attrdesc, &tuple->t_self, tuple);
|
|
@@ -3595,6 +3644,9 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
|
|
errmsg("column \"%s\" of relation \"%s\" already exists",
|
|
colDef->colname, RelationGetRelationName(rel))));
|
|
|
|
+ /* SELinux checks db_column:{create} */
|
|
+ attsecid = sepgsql_attribute_create(myrelid, colDef);
|
|
+
|
|
/* Determine the new attribute's number */
|
|
if (isOid)
|
|
newattnum = ObjectIdAttributeNumber;
|
|
@@ -3637,7 +3689,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
|
|
|
|
ReleaseSysCache(typeTuple);
|
|
|
|
- InsertPgAttributeTuple(attrdesc, &attribute, NULL);
|
|
+ InsertPgAttributeTuple(attrdesc, &attribute, NULL, attsecid);
|
|
|
|
heap_close(attrdesc, RowExclusiveLock);
|
|
|
|
@@ -4026,6 +4078,8 @@ ATPrepSetStatistics(Relation rel, const char *colName, Node *flagValue)
|
|
if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId()))
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
|
|
RelationGetRelationName(rel));
|
|
+ /* SELinux checks */
|
|
+ sepgsql_attribute_alter(RelationGetRelid(rel), colName);
|
|
}
|
|
|
|
static void
|
|
@@ -4181,7 +4235,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
|
|
|
|
/* At top level, permission check was done in ATPrepCmd, else do it */
|
|
if (recursing)
|
|
- ATSimplePermissions(rel, false);
|
|
+ ATSimplePermissions(rel, NULL, false);
|
|
|
|
/*
|
|
* get the number of the attribute
|
|
@@ -4483,7 +4537,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
|
|
|
|
/* At top level, permission check was done in ATPrepCmd, else do it */
|
|
if (recursing)
|
|
- ATSimplePermissions(rel, false);
|
|
+ ATSimplePermissions(rel, NULL, false);
|
|
|
|
/*
|
|
* Call AddRelationNewConstraints to do the work, making sure it works on
|
|
@@ -5385,7 +5439,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
|
|
|
|
/* At top level, permission check was done in ATPrepCmd, else do it */
|
|
if (recursing)
|
|
- ATSimplePermissions(rel, false);
|
|
+ ATSimplePermissions(rel, NULL, false);
|
|
|
|
conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
|
|
|
|
@@ -6319,6 +6373,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(namespaceOid));
|
|
}
|
|
+ /* SELinux checks db_table:{setattr} */
|
|
+ sepgsql_relation_alter(relationOid, NULL, InvalidOid);
|
|
}
|
|
|
|
memset(repl_null, false, sizeof(repl_null));
|
|
@@ -6923,7 +6979,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent)
|
|
* Must be owner of both parent and child -- child was checked by
|
|
* ATSimplePermissions call in ATPrepCmd
|
|
*/
|
|
- ATSimplePermissions(parent_rel, false);
|
|
+ ATSimplePermissions(parent_rel, NULL, false);
|
|
|
|
/* Permanent rels cannot inherit from temporary ones */
|
|
if (parent_rel->rd_istemp && !child_rel->rd_istemp)
|
|
@@ -7581,6 +7637,9 @@ AlterTableNamespace(RangeVar *relation, const char *newschema,
|
|
RelationGetRelationName(rel),
|
|
newschema)));
|
|
|
|
+ /* SELinux checks */
|
|
+ sepgsql_relation_alter(relid, NULL, nspOid);
|
|
+
|
|
/* disallow renaming into or out of temp schemas */
|
|
if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid))
|
|
ereport(ERROR,
|
|
@@ -7773,6 +7832,134 @@ AlterSeqNamespaces(Relation classRel, Relation rel,
|
|
relation_close(depRel, AccessShareLock);
|
|
}
|
|
|
|
+/*
|
|
+ * ALTER TABLE/SEQUENCE name SECURITY_LABEL [=] newlabel
|
|
+ * ALTER TABLE/SEQUENCE name ALTER column SECURITY_LABEL [=] newlabel
|
|
+ */
|
|
+static void
|
|
+ExecRelationSetSecLabel(Oid relid, DefElem *seclabel)
|
|
+{
|
|
+ Relation rel;
|
|
+ HeapTuple oldtup;
|
|
+ HeapTuple newtup;
|
|
+ Oid secid;
|
|
+ bool replaces[Natts_pg_class];
|
|
+
|
|
+ rel = heap_open(RelationRelationId, RowExclusiveLock);
|
|
+ oldtup = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(relid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(oldtup))
|
|
+ elog(ERROR, "cache lookup failed for relation: %u", relid);
|
|
+
|
|
+ memset(replaces, false, sizeof(replaces));
|
|
+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
|
|
+ NULL, NULL, replaces);
|
|
+ if (!HeapTupleHasSecid(newtup))
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("Unable to set security label on \"%s\"",
|
|
+ get_rel_name(relid))));
|
|
+
|
|
+ ReleaseSysCache(oldtup);
|
|
+
|
|
+ /* SELinux checks db_table:{setattr relabelfrom relabelto} */
|
|
+ secid = sepgsql_relation_relabel(relid, seclabel);
|
|
+
|
|
+ HeapTupleSetSecid(newtup, secid);
|
|
+
|
|
+ simple_heap_update(rel, &newtup->t_self, newtup);
|
|
+
|
|
+ CatalogUpdateIndexes(rel, newtup);
|
|
+
|
|
+ heap_freetuple(newtup);
|
|
+
|
|
+ heap_close(rel, RowExclusiveLock);
|
|
+}
|
|
+
|
|
+static void
|
|
+ExecAttributeSetSecLabel(Oid relid, const char *attname, DefElem *seclabel)
|
|
+{
|
|
+ Relation rel;
|
|
+ HeapTuple oldtup;
|
|
+ HeapTuple newtup;
|
|
+ AttrNumber attnum;
|
|
+ Oid secid;
|
|
+ bool replaces[Natts_pg_attribute];
|
|
+
|
|
+ rel = heap_open(AttributeRelationId, RowExclusiveLock);
|
|
+ oldtup = SearchSysCacheAttName(relid, attname);
|
|
+ if (!HeapTupleIsValid(oldtup))
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
|
|
+ errmsg("column \"%s\" of relation \"%s\" does not exist",
|
|
+ attname, get_rel_name(relid))));
|
|
+ attnum = ((Form_pg_attribute) GETSTRUCT(oldtup))->attnum;
|
|
+
|
|
+ memset(replaces, false, sizeof(replaces));
|
|
+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
|
|
+ NULL, NULL, replaces);
|
|
+ if (!HeapTupleHasSecid(newtup))
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("Unable to set security context on \"%s.%s\"",
|
|
+ get_rel_name(relid), attname)));
|
|
+
|
|
+ ReleaseSysCache(oldtup);
|
|
+
|
|
+ /* SELinux checks db_column:{setattr relabelfrom relabelto} */
|
|
+ secid = sepgsql_attribute_relabel(relid, attnum, seclabel);
|
|
+
|
|
+ HeapTupleSetSecid(newtup, secid);
|
|
+
|
|
+ simple_heap_update(rel, &newtup->t_self, newtup);
|
|
+
|
|
+ CatalogUpdateIndexes(rel, newtup);
|
|
+
|
|
+ heap_freetuple(newtup);
|
|
+
|
|
+ heap_close(rel, RowExclusiveLock);
|
|
+}
|
|
+
|
|
+void
|
|
+AlterRelationSecLabel(RangeVar *relation, const char *attname,
|
|
+ ObjectType objtype, DefElem *seclabel)
|
|
+{
|
|
+ Oid relid;
|
|
+ char relkind;
|
|
+
|
|
+ /* Check relation type against type specified in the ALTER command */
|
|
+ relid = RangeVarGetRelid(relation, false);
|
|
+ relkind = get_rel_relkind(relid);
|
|
+
|
|
+ switch (objtype)
|
|
+ {
|
|
+ case OBJECT_TABLE:
|
|
+ case OBJECT_COLUMN:
|
|
+ if (relkind != RELKIND_RELATION)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
+ errmsg("\"%s\" is not a table", get_rel_name(relid))));
|
|
+ break;
|
|
+
|
|
+ case OBJECT_SEQUENCE:
|
|
+ if (relkind != RELKIND_SEQUENCE)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
+ errmsg("\"%s\" is not a sequence", get_rel_name(relid))));
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ elog(ERROR, "unrecognized object type: %d", (int)objtype);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Exec set security label */
|
|
+ if (objtype != OBJECT_COLUMN)
|
|
+ ExecRelationSetSecLabel(relid, seclabel);
|
|
+ else
|
|
+ ExecAttributeSetSecLabel(relid, attname, seclabel);
|
|
+}
|
|
|
|
/*
|
|
* This code supports
|
|
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
|
|
index f432e74..469b5cb 100644
|
|
--- a/src/backend/commands/trigger.c
|
|
+++ b/src/backend/commands/trigger.c
|
|
@@ -33,6 +33,7 @@
|
|
#include "nodes/makefuncs.h"
|
|
#include "parser/parse_func.h"
|
|
#include "pgstat.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "tcop/utility.h"
|
|
#include "utils/acl.h"
|
|
@@ -182,6 +183,10 @@ CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid, bool checkPermissions)
|
|
NameListToString(stmt->funcname))));
|
|
}
|
|
|
|
+ /* SELinux checks */
|
|
+ if (checkPermissions)
|
|
+ sepgsql_trigger_create(RelationGetRelid(rel), stmt->trigname, 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
|
|
@@ -746,6 +751,7 @@ DropTrigger(Oid relid, const char *trigname, DropBehavior behavior,
|
|
if (!pg_class_ownercheck(relid, GetUserId()))
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
|
|
get_rel_name(relid));
|
|
+ sepgsql_trigger_drop(relid, trigname);
|
|
|
|
object.classId = TriggerRelationId;
|
|
object.objectId = HeapTupleGetOid(tup);
|
|
@@ -862,6 +868,9 @@ renametrig(Oid relid,
|
|
*/
|
|
targetrel = heap_open(relid, AccessExclusiveLock);
|
|
|
|
+ /* SELinux checks */
|
|
+ sepgsql_trigger_alter(relid, oldname);
|
|
+
|
|
/*
|
|
* Scan pg_trigger twice for existing triggers on relation. We do this in
|
|
* order to ensure a trigger does not exist with newname (The unique index
|
|
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
|
|
index a1f301b..16cfa5d 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 "security/sepgsql.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 secid;
|
|
|
|
if (!superuser())
|
|
ereport(ERROR,
|
|
@@ -250,12 +252,22 @@ DefineTSParser(List *names, List *parameters)
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
errmsg("text search parser lextypes method is required")));
|
|
|
|
+ /* Permission checks */
|
|
+ secid = 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
|
|
*/
|
|
prsRel = heap_open(TSParserRelationId, RowExclusiveLock);
|
|
|
|
tup = heap_form_tuple(prsRel->rd_att, values, nulls);
|
|
+ if (HeapTupleHasSecid(tup))
|
|
+ HeapTupleSetSecid(tup, secid);
|
|
|
|
prsOid = simple_heap_insert(prsRel, tup);
|
|
|
|
@@ -372,6 +384,9 @@ RenameTSParser(List *oldname, const char *newname)
|
|
|
|
prsId = TSParserGetPrsid(oldname, false);
|
|
|
|
+ /* SELinux checks */
|
|
+ sepgsql_ts_parser_alter(prsId, newname);
|
|
+
|
|
tup = SearchSysCacheCopy(TSPARSEROID,
|
|
ObjectIdGetDatum(prsId),
|
|
0, 0, 0);
|
|
@@ -503,6 +518,7 @@ DefineTSDictionary(List *names, List *parameters)
|
|
List *dictoptions = NIL;
|
|
Oid dictOid;
|
|
Oid namespaceoid;
|
|
+ Oid secid;
|
|
AclResult aclresult;
|
|
char *dictname;
|
|
|
|
@@ -515,6 +531,9 @@ DefineTSDictionary(List *names, List *parameters)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(namespaceoid));
|
|
|
|
+ /* SELinux check */
|
|
+ secid = sepgsql_ts_dict_create(dictname, namespaceoid);
|
|
+
|
|
/*
|
|
* loop over the definition list and extract the information we need.
|
|
*/
|
|
@@ -563,6 +582,8 @@ DefineTSDictionary(List *names, List *parameters)
|
|
dictRel = heap_open(TSDictionaryRelationId, RowExclusiveLock);
|
|
|
|
tup = heap_form_tuple(dictRel->rd_att, values, nulls);
|
|
+ if (HeapTupleHasSecid(tup))
|
|
+ HeapTupleSetSecid(tup, secid);
|
|
|
|
dictOid = simple_heap_insert(dictRel, tup);
|
|
|
|
@@ -621,6 +642,9 @@ RenameTSDictionary(List *oldname, const char *newname)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(namespaceOid));
|
|
|
|
+ /* SELinux checks */
|
|
+ sepgsql_ts_dict_alter(dictId, newname);
|
|
+
|
|
namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname);
|
|
simple_heap_update(rel, &tup->t_self, tup);
|
|
CatalogUpdateIndexes(rel, tup);
|
|
@@ -762,6 +786,9 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt)
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY,
|
|
NameListToString(stmt->dictname));
|
|
|
|
+ /* SELinux checks */
|
|
+ sepgsql_ts_dict_alter(dictId, NULL);
|
|
+
|
|
/* deserialize the existing set of options */
|
|
opt = SysCacheGetAttr(TSDICTOID, tup,
|
|
Anum_pg_ts_dict_dictinitoption,
|
|
@@ -889,6 +916,8 @@ AlterTSDictionaryOwner(List *name, Oid newOwnerId)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(namespaceOid));
|
|
}
|
|
+ /* SELinux checks */
|
|
+ sepgsql_ts_dict_alter(dictId, NULL);
|
|
|
|
form->dictowner = newOwnerId;
|
|
|
|
@@ -999,6 +1028,7 @@ DefineTSTemplate(List *names, List *parameters)
|
|
NameData dname;
|
|
int i;
|
|
Oid dictOid;
|
|
+ Oid dictSecid;
|
|
Oid namespaceoid;
|
|
char *tmplname;
|
|
|
|
@@ -1054,6 +1084,11 @@ DefineTSTemplate(List *names, List *parameters)
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
errmsg("text search template lexize method is required")));
|
|
|
|
+ /* SELinux checks */
|
|
+ dictSecid = 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
|
|
*/
|
|
@@ -1061,6 +1096,8 @@ DefineTSTemplate(List *names, List *parameters)
|
|
tmplRel = heap_open(TSTemplateRelationId, RowExclusiveLock);
|
|
|
|
tup = heap_form_tuple(tmplRel->rd_att, values, nulls);
|
|
+ if (HeapTupleHasSecid(tup))
|
|
+ HeapTupleSetSecid(tup, dictSecid);
|
|
|
|
dictOid = simple_heap_insert(tmplRel, tup);
|
|
|
|
@@ -1093,6 +1130,9 @@ RenameTSTemplate(List *oldname, const char *newname)
|
|
|
|
tmplId = TSTemplateGetTmplid(oldname, false);
|
|
|
|
+ /* Permission checks */
|
|
+ sepgsql_ts_template_alter(tmplId, newname);
|
|
+
|
|
tup = SearchSysCacheCopy(TSTEMPLATEOID,
|
|
ObjectIdGetDatum(tmplId),
|
|
0, 0, 0);
|
|
@@ -1335,6 +1375,7 @@ DefineTSConfiguration(List *names, List *parameters)
|
|
Oid sourceOid = InvalidOid;
|
|
Oid prsOid = InvalidOid;
|
|
Oid cfgOid;
|
|
+ Oid cfgSecid;
|
|
ListCell *pl;
|
|
|
|
/* Convert list of names to a name and namespace */
|
|
@@ -1399,6 +1440,9 @@ DefineTSConfiguration(List *names, List *parameters)
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
errmsg("text search parser is required")));
|
|
|
|
+ /* SELinux checks */
|
|
+ cfgSecid = sepgsql_ts_config_create(cfgname, namespaceoid);
|
|
+
|
|
/*
|
|
* Looks good, build tuple and insert
|
|
*/
|
|
@@ -1414,6 +1458,8 @@ DefineTSConfiguration(List *names, List *parameters)
|
|
cfgRel = heap_open(TSConfigRelationId, RowExclusiveLock);
|
|
|
|
tup = heap_form_tuple(cfgRel->rd_att, values, nulls);
|
|
+ if (HeapTupleHasSecid(tup))
|
|
+ HeapTupleSetSecid(tup, cfgSecid);
|
|
|
|
cfgOid = simple_heap_insert(cfgRel, tup);
|
|
|
|
@@ -1519,6 +1565,9 @@ RenameTSConfiguration(List *oldname, const char *newname)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(namespaceOid));
|
|
|
|
+ /* permission checks */
|
|
+ sepgsql_ts_config_alter(cfgId, newname);
|
|
+
|
|
namestrcpy(&(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname), newname);
|
|
simple_heap_update(rel, &tup->t_self, tup);
|
|
CatalogUpdateIndexes(rel, tup);
|
|
@@ -1690,6 +1739,8 @@ AlterTSConfigurationOwner(List *name, Oid newOwnerId)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(namespaceOid));
|
|
}
|
|
+ /* SELinux checks */
|
|
+ sepgsql_ts_config_alter(cfgId, NULL);
|
|
|
|
form->cfgowner = newOwnerId;
|
|
|
|
@@ -1727,6 +1778,9 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION,
|
|
NameListToString(stmt->cfgname));
|
|
|
|
+ /* SELinux checks */
|
|
+ sepgsql_ts_config_alter(HeapTupleGetOid(tup), NULL);
|
|
+
|
|
relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock);
|
|
|
|
/* Add or drop mappings */
|
|
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
|
|
index 528a917..e10b914 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 "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/fmgroids.h"
|
|
@@ -1543,6 +1544,7 @@ 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, NULL, InvalidOid);
|
|
|
|
/* Setup new tuple */
|
|
MemSet(new_record, (Datum) 0, sizeof(new_record));
|
|
@@ -1671,6 +1673,7 @@ 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, NULL, InvalidOid);
|
|
|
|
/* Is the domain already set to the desired constraint? */
|
|
if (typTup->typnotnull == notNull)
|
|
@@ -1772,6 +1775,7 @@ 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, NULL, InvalidOid);
|
|
|
|
/* Grab an appropriate lock on the pg_constraint relation */
|
|
conrel = heap_open(ConstraintRelationId, RowExclusiveLock);
|
|
@@ -1848,6 +1852,7 @@ 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, NULL, InvalidOid);
|
|
|
|
/* Check for unsupported constraint types */
|
|
if (IsA(newConstraint, FkConstraint))
|
|
@@ -2470,6 +2475,9 @@ RenameType(List *names, const char *newTypeName)
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
|
|
format_type_be(typeOid));
|
|
|
|
+ /* SELinux check permission */
|
|
+ sepgsql_type_alter(typeOid, newTypeName, InvalidOid);
|
|
+
|
|
/*
|
|
* 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
|
|
@@ -2590,6 +2598,8 @@ AlterTypeOwner(List *names, Oid newOwnerId)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(typTup->typnamespace));
|
|
}
|
|
+ /* SELinux checks permissions */
|
|
+ sepgsql_type_alter(HeapTupleGetOid(tup), NULL, InvalidOid);
|
|
|
|
/*
|
|
* If it's a composite type, invoke ATExecChangeOwner so that we fix
|
|
@@ -2706,6 +2716,9 @@ AlterTypeNamespace(List *names, const char *newschema)
|
|
errhint("You can alter type %s, which will alter the array type as well.",
|
|
format_type_be(elemOid))));
|
|
|
|
+ /* SELinux checks permissions */
|
|
+ sepgsql_type_alter(typeOid, NULL, nspOid);
|
|
+
|
|
/* and do the work */
|
|
AlterTypeNamespaceInternal(typeOid, nspOid, false, true);
|
|
}
|
|
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
|
|
index dcb30f8..737b58a 100644
|
|
--- a/src/backend/commands/vacuum.c
|
|
+++ b/src/backend/commands/vacuum.c
|
|
@@ -32,6 +32,7 @@
|
|
#include "catalog/namespace.h"
|
|
#include "catalog/pg_database.h"
|
|
#include "catalog/pg_namespace.h"
|
|
+#include "catalog/pg_security.h"
|
|
#include "catalog/storage.h"
|
|
#include "commands/dbcommands.h"
|
|
#include "commands/vacuum.h"
|
|
@@ -1209,6 +1210,9 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound,
|
|
/* all done with this class, but hold lock until commit */
|
|
relation_close(onerel, NoLock);
|
|
|
|
+ /* Also reclaim orphan security label */
|
|
+ seclabelRelationReclaim(relid);
|
|
+
|
|
/*
|
|
* Complete the transaction and free all temporary memory used.
|
|
*/
|
|
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
|
|
index dfbce72..1bfab03 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 "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/lsyscache.h"
|
|
@@ -166,6 +167,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 564347f..154b3c4 100644
|
|
--- a/src/backend/executor/execJunk.c
|
|
+++ b/src/backend/executor/execJunk.c
|
|
@@ -60,7 +60,8 @@
|
|
* An optional resultSlot can be passed as well.
|
|
*/
|
|
JunkFilter *
|
|
-ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot)
|
|
+ExecInitJunkFilter(List *targetList, bool hasoid, bool hasseclabel,
|
|
+ TupleTableSlot *slot)
|
|
{
|
|
JunkFilter *junkfilter;
|
|
TupleDesc cleanTupType;
|
|
@@ -72,7 +73,7 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot)
|
|
/*
|
|
* Compute the tuple descriptor for the cleaned tuple.
|
|
*/
|
|
- cleanTupType = ExecCleanTypeFromTL(targetList, hasoid);
|
|
+ cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, hasseclabel);
|
|
|
|
/*
|
|
* 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 131be22..5c4205b 100644
|
|
--- a/src/backend/executor/execMain.c
|
|
+++ b/src/backend/executor/execMain.c
|
|
@@ -39,6 +39,7 @@
|
|
#include "access/xact.h"
|
|
#include "catalog/heap.h"
|
|
#include "catalog/namespace.h"
|
|
+#include "catalog/pg_security.h"
|
|
#include "catalog/toasting.h"
|
|
#include "commands/tablespace.h"
|
|
#include "commands/trigger.h"
|
|
@@ -50,6 +51,7 @@
|
|
#include "optimizer/clauses.h"
|
|
#include "parser/parse_clause.h"
|
|
#include "parser/parsetree.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "storage/lmgr.h"
|
|
#include "storage/smgr.h"
|
|
@@ -442,7 +444,10 @@ ExecCheckRTPerms(List *rangeTable)
|
|
|
|
foreach(l, rangeTable)
|
|
{
|
|
- ExecCheckRTEPerms((RangeTblEntry *) lfirst(l));
|
|
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
|
|
+
|
|
+ ExecCheckRTEPerms(rte);
|
|
+ sepgsqlCheckRTEPerms(rte);
|
|
}
|
|
}
|
|
|
|
@@ -901,16 +906,16 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
|
for (i = 0; i < as_nplans; i++)
|
|
{
|
|
PlanState *subplan = appendplans[i];
|
|
+ Relation resultRel = resultRelInfo->ri_RelationDesc;
|
|
JunkFilter *j;
|
|
|
|
if (operation == CMD_UPDATE)
|
|
- ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc,
|
|
- subplan->plan->targetlist);
|
|
+ ExecCheckPlanOutput(resultRel, subplan->plan->targetlist);
|
|
|
|
j = ExecInitJunkFilter(subplan->plan->targetlist,
|
|
- resultRelInfo->ri_RelationDesc->rd_att->tdhasoid,
|
|
- ExecAllocTableSlot(estate->es_tupleTable));
|
|
-
|
|
+ RelationGetDescr(resultRel)->tdhasoid,
|
|
+ RelationGetDescr(resultRel)->tdhassecid,
|
|
+ ExecAllocTableSlot(estate->es_tupleTable));
|
|
/*
|
|
* Since it must be UPDATE/DELETE, there had better be a
|
|
* "ctid" junk attribute in the tlist ... but ctid could
|
|
@@ -953,6 +958,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
|
|
|
j = ExecInitJunkFilter(planstate->plan->targetlist,
|
|
tupType->tdhasoid,
|
|
+ tupType->tdhassecid,
|
|
ExecAllocTableSlot(estate->es_tupleTable));
|
|
estate->es_junkFilter = j;
|
|
if (estate->es_result_relation_info)
|
|
@@ -1023,7 +1029,7 @@ InitPlan(QueryDesc *queryDesc, int eflags)
|
|
* We assume all the sublists will generate the same output tupdesc.
|
|
*/
|
|
tupType = ExecTypeFromTL((List *) linitial(plannedstmt->returningLists),
|
|
- false);
|
|
+ false, false);
|
|
|
|
/* Set up a slot for the output of the RETURNING projection(s) */
|
|
slot = ExecAllocTableSlot(estate->es_tupleTable);
|
|
@@ -1346,6 +1352,37 @@ ExecContextForcesOids(PlanState *planstate, bool *hasoids)
|
|
return false;
|
|
}
|
|
|
|
+/*
|
|
+ * ExecContextForcesSecids
|
|
+ *
|
|
+ * We need to ensure that result tuples have space for security identifier.
|
|
+ * if the security feature need to store it within the given relation.
|
|
+ */
|
|
+bool ExecContextForcesSecids(PlanState *planstate, bool *hassecid)
|
|
+{
|
|
+ if (planstate->state->es_select_into)
|
|
+ {
|
|
+ *hassecid = securityTupleDescHasSecid(InvalidOid,
|
|
+ RELKIND_RELATION);
|
|
+ return true;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ ResultRelInfo *ri = planstate->state->es_result_relation_info;
|
|
+
|
|
+ if (ri && ri->ri_RelationDesc)
|
|
+ {
|
|
+ Oid relid = RelationGetRelid(ri->ri_RelationDesc);
|
|
+ char relkind = RelationGetForm(ri->ri_RelationDesc)->relkind;
|
|
+
|
|
+ *hassecid = securityTupleDescHasSecid(relid, relkind);
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
/* ----------------------------------------------------------------
|
|
* ExecEndPlan
|
|
*
|
|
@@ -1426,6 +1463,58 @@ ExecEndPlan(PlanState *planstate, EState *estate)
|
|
}
|
|
}
|
|
|
|
+/*
|
|
+ * fetchWritableSystemAttribute() fetches writable system column data
|
|
+ * using Junkfilter, and saves them at TupleTableSlot temporary.
|
|
+ *
|
|
+ * storeWritableSystemAttribute() copies these fetched data into
|
|
+ * header structure of HeapTuple.
|
|
+ */
|
|
+static void
|
|
+fetchWritableSystemAttribute(JunkFilter *junkfilter, TupleTableSlot *slot,
|
|
+ Datum *tts_seclabel)
|
|
+{
|
|
+ AttrNumber attno;
|
|
+ Datum datum;
|
|
+ bool isnull;
|
|
+
|
|
+ /* for Security Label */
|
|
+ attno = ExecFindJunkAttribute(junkfilter, SecurityAttributeName);
|
|
+ if (attno != InvalidAttrNumber)
|
|
+ {
|
|
+ datum = ExecGetJunkAttribute(slot, attno, &isnull);
|
|
+ if (isnull)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("Unable to set NULL on \"%s\"",
|
|
+ SecurityAttributeName)));
|
|
+ *tts_seclabel = datum;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+storeWritableSystemAttribute(Relation rel, TupleTableSlot *slot, HeapTuple tuple)
|
|
+{
|
|
+ Oid relid = RelationGetRelid(rel);
|
|
+ Oid secid;
|
|
+
|
|
+ /* "security_label" */
|
|
+ if (DatumGetPointer(slot->tts_seclabel) != NULL)
|
|
+ {
|
|
+ char *seclabel = TextDatumGetCString(slot->tts_seclabel);
|
|
+
|
|
+ if (!HeapTupleHasSecid(tuple))
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("Unable to assign security label on \"%s\"",
|
|
+ RelationGetRelationName(rel))));
|
|
+ secid = securityTransSecLabelIn(relid, seclabel);
|
|
+ HeapTupleSetSecid(tuple, secid);
|
|
+ }
|
|
+ else if (HeapTupleHasSecid(tuple))
|
|
+ HeapTupleSetSecid(tuple, InvalidOid);
|
|
+}
|
|
+
|
|
/* ----------------------------------------------------------------
|
|
* ExecutePlan
|
|
*
|
|
@@ -1487,6 +1576,8 @@ ExecutePlan(EState *estate,
|
|
*/
|
|
for (;;)
|
|
{
|
|
+ Datum tts_seclabel = PointerGetDatum(NULL);
|
|
+
|
|
/* Reset the per-output-tuple exprcontext */
|
|
ResetPerTupleExprContext(estate);
|
|
|
|
@@ -1631,6 +1722,11 @@ lnext: ;
|
|
}
|
|
|
|
/*
|
|
+ * extract writable system attribute
|
|
+ */
|
|
+ fetchWritableSystemAttribute(junkfilter, slot, &tts_seclabel);
|
|
+
|
|
+ /*
|
|
* extract the 'ctid' junk attribute.
|
|
*/
|
|
if (operation == CMD_UPDATE || operation == CMD_DELETE)
|
|
@@ -1657,6 +1753,7 @@ lnext: ;
|
|
if (operation != CMD_DELETE)
|
|
slot = ExecFilterJunk(junkfilter, slot);
|
|
}
|
|
+ slot->tts_seclabel = tts_seclabel;
|
|
|
|
/*
|
|
* now that we have a tuple, do the appropriate thing with it.. either
|
|
@@ -1781,6 +1878,8 @@ ExecInsert(TupleTableSlot *slot,
|
|
if (resultRelationDesc->rd_rel->relhasoids)
|
|
HeapTupleSetOid(tuple, InvalidOid);
|
|
|
|
+ storeWritableSystemAttribute(resultRelationDesc, slot, tuple);
|
|
+
|
|
/* BEFORE ROW INSERT Triggers */
|
|
if (resultRelInfo->ri_TrigDesc &&
|
|
resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0)
|
|
@@ -1811,6 +1910,12 @@ ExecInsert(TupleTableSlot *slot,
|
|
}
|
|
|
|
/*
|
|
+ * SELinux assigns default security label, and
|
|
+ * it also checks db_tuple:{insert} permission
|
|
+ */
|
|
+ sepgsqlHeapTupleInsert(resultRelationDesc, tuple, false);
|
|
+
|
|
+ /*
|
|
* Check the constraints of the tuple
|
|
*/
|
|
if (resultRelationDesc->rd_att->constr)
|
|
@@ -2018,6 +2123,8 @@ ExecUpdate(TupleTableSlot *slot,
|
|
resultRelInfo = estate->es_result_relation_info;
|
|
resultRelationDesc = resultRelInfo->ri_RelationDesc;
|
|
|
|
+ storeWritableSystemAttribute(resultRelationDesc, slot, tuple);
|
|
+
|
|
/* BEFORE ROW UPDATE Triggers */
|
|
if (resultRelInfo->ri_TrigDesc &&
|
|
resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0)
|
|
@@ -2048,6 +2155,9 @@ ExecUpdate(TupleTableSlot *slot,
|
|
}
|
|
}
|
|
|
|
+ /* SELinux checks db_tuple:{relabelfrom relabelto}, if needed */
|
|
+ sepgsqlHeapTupleUpdate(resultRelationDesc, tupleid, tuple);
|
|
+
|
|
/*
|
|
* Check the constraints of the tuple
|
|
*
|
|
@@ -2843,6 +2953,7 @@ OpenIntoRel(QueryDesc *queryDesc)
|
|
Oid namespaceId;
|
|
Oid tablespaceId;
|
|
Datum reloptions;
|
|
+ Oid *secLabels;
|
|
AclResult aclresult;
|
|
Oid intoRelationId;
|
|
TupleDesc tupdesc;
|
|
@@ -2886,6 +2997,14 @@ OpenIntoRel(QueryDesc *queryDesc)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(namespaceId));
|
|
|
|
+ /* SELinux checks */
|
|
+ secLabels = sepgsql_relation_create(intoName,
|
|
+ RELKIND_RELATION,
|
|
+ queryDesc->tupDesc,
|
|
+ namespaceId,
|
|
+ NULL, NIL,
|
|
+ true, true);
|
|
+
|
|
/*
|
|
* Select tablespace to use. If not specified, use default tablespace
|
|
* (which may in turn default to database's default).
|
|
@@ -2944,7 +3063,8 @@ OpenIntoRel(QueryDesc *queryDesc)
|
|
0,
|
|
into->onCommit,
|
|
reloptions,
|
|
- allowSystemTableMods);
|
|
+ allowSystemTableMods,
|
|
+ secLabels);
|
|
|
|
FreeTupleDesc(tupdesc);
|
|
|
|
@@ -3069,6 +3189,10 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self)
|
|
if (myState->rel->rd_rel->relhasoids)
|
|
HeapTupleSetOid(tuple, InvalidOid);
|
|
|
|
+ storeWritableSystemAttribute(myState->rel, slot, tuple);
|
|
+ /* SELinux checks db_tuple:{insert} */
|
|
+ sepgsqlHeapTupleInsert(myState->rel, tuple, false);
|
|
+
|
|
heap_insert(myState->rel,
|
|
tuple,
|
|
myState->estate->es_output_cid,
|
|
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
|
|
index 119ddbc..66f918e 100644
|
|
--- a/src/backend/executor/execQual.c
|
|
+++ b/src/backend/executor/execQual.c
|
|
@@ -47,6 +47,7 @@
|
|
#include "nodes/nodeFuncs.h"
|
|
#include "optimizer/planner.h"
|
|
#include "pgstat.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/lsyscache.h"
|
|
@@ -1034,6 +1035,7 @@ init_fcache(Oid foid, FuncExprState *fcache,
|
|
aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE);
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid));
|
|
+ sepgsql_proc_execute(foid);
|
|
|
|
/*
|
|
* Safety check on nargs. Under normal circumstances this should never
|
|
@@ -4032,6 +4034,7 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, ACL_KIND_PROC,
|
|
get_func_name(acoerce->elemfuncid));
|
|
+ sepgsql_proc_execute(acoerce->elemfuncid);
|
|
|
|
/* Set up the primary fmgr lookup information */
|
|
fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc),
|
|
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
|
|
index 19fa4e6..44021f2 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 "security/rowlevel.h"
|
|
#include "utils/memutils.h"
|
|
|
|
|
|
@@ -53,6 +54,7 @@ ExecScan(ScanState *node,
|
|
ProjectionInfo *projInfo;
|
|
ExprDoneCond isDone;
|
|
TupleTableSlot *resultSlot;
|
|
+ Scan *scan = (Scan *)node->ps.plan;
|
|
|
|
/*
|
|
* Fetch data from node
|
|
@@ -64,7 +66,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 && !scan->rowlvPerms)
|
|
return (*accessMtd) (node);
|
|
|
|
/*
|
|
@@ -128,9 +130,18 @@ ExecScan(ScanState *node,
|
|
* when the qual is nil ... saves only a few cycles, but they add up
|
|
* ...
|
|
*/
|
|
- if (!qual || ExecQual(qual, econtext, false))
|
|
+ if (rowlvExecScanFilter(scan, node->ss_currentRelation, slot)
|
|
+ && (!qual || ExecQual(qual, econtext, false)))
|
|
{
|
|
/*
|
|
+ * NOTE: On FK checks, the Row-level feature needs to raise
|
|
+ * an error after evaluation of all the given quals to avoid
|
|
+ * incorrect error reporting. We assume FK implementation
|
|
+ * does not use malicious functions as the quals.
|
|
+ */
|
|
+ rowlvExecScanAbort(scan, node->ss_currentRelation, slot);
|
|
+
|
|
+ /*
|
|
* Found a satisfactory scan tuple.
|
|
*/
|
|
if (projInfo)
|
|
@@ -197,6 +208,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 */
|
|
@@ -240,12 +252,16 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc
|
|
return false; /* tlist too long */
|
|
|
|
/*
|
|
- * If the plan context requires a particular hasoid setting, then that has
|
|
- * to match, too.
|
|
+ * If the plan context requires a particular hasoid or hassecid setting,
|
|
+ * then that has to match, too.
|
|
*/
|
|
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 06142c9..c5f614a 100644
|
|
--- a/src/backend/executor/execTuples.c
|
|
+++ b/src/backend/executor/execTuples.c
|
|
@@ -100,7 +100,7 @@
|
|
|
|
|
|
static TupleDesc ExecTypeFromTLInternal(List *targetList,
|
|
- bool hasoid, bool skipjunk);
|
|
+ bool hasoid, bool hasseclabel, bool skipjunk);
|
|
|
|
|
|
/* ----------------------------------------------------------------
|
|
@@ -968,9 +968,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);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
@@ -980,13 +980,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;
|
|
@@ -998,6 +999,7 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk)
|
|
else
|
|
len = ExecTargetListLength(targetList);
|
|
typeInfo = CreateTemplateTupleDesc(len, hasoid);
|
|
+ typeInfo->tdhassecid = hassecid;
|
|
|
|
foreach(l, targetList)
|
|
{
|
|
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
|
|
index 7033189..34faaeb 100644
|
|
--- a/src/backend/executor/execUtils.c
|
|
+++ b/src/backend/executor/execUtils.c
|
|
@@ -512,6 +512,7 @@ void
|
|
ExecAssignResultTypeFromTL(PlanState *planstate)
|
|
{
|
|
bool hasoid;
|
|
+ bool hassecid;
|
|
TupleDesc tupDesc;
|
|
|
|
if (ExecContextForcesOids(planstate, &hasoid))
|
|
@@ -524,12 +525,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 1d679a9..8b46999 100644
|
|
--- a/src/backend/executor/functions.c
|
|
+++ b/src/backend/executor/functions.c
|
|
@@ -1135,7 +1135,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)
|
|
{
|
|
@@ -1167,7 +1167,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
|
|
COERCE_DONTCARE);
|
|
/* 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 */
|
|
}
|
|
}
|
|
@@ -1180,7 +1180,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 d7cccc5..6ba72ca 100644
|
|
--- a/src/backend/executor/nodeAgg.c
|
|
+++ b/src/backend/executor/nodeAgg.c
|
|
@@ -81,6 +81,7 @@
|
|
#include "parser/parse_agg.h"
|
|
#include "parser/parse_coerce.h"
|
|
#include "parser/parse_oper.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/lsyscache.h"
|
|
@@ -1431,6 +1432,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, ACL_KIND_PROC,
|
|
get_func_name(aggref->aggfnoid));
|
|
+ sepgsql_proc_execute(aggref->aggfnoid);
|
|
|
|
peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn;
|
|
peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn;
|
|
@@ -1454,6 +1456,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, ACL_KIND_PROC,
|
|
get_func_name(transfn_oid));
|
|
+ sepgsql_proc_execute(transfn_oid);
|
|
if (OidIsValid(finalfn_oid))
|
|
{
|
|
aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner,
|
|
@@ -1461,6 +1464,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, ACL_KIND_PROC,
|
|
get_func_name(finalfn_oid));
|
|
+ sepgsql_proc_execute(finalfn_oid);
|
|
}
|
|
}
|
|
|
|
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
|
|
index b6143e6..4394855 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 "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "utils/memutils.h"
|
|
@@ -215,6 +216,7 @@ MJExamineQuals(List *mergeclauses,
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, ACL_KIND_PROC,
|
|
get_func_name(cmpproc));
|
|
+ sepgsql_proc_execute(cmpproc);
|
|
|
|
/* Set up the fmgr lookup information */
|
|
fmgr_info(cmpproc, &(clause->cmpfinfo));
|
|
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
|
|
index aff7a63..41dede3 100644
|
|
--- a/src/backend/executor/nodeSubplan.c
|
|
+++ b/src/backend/executor/nodeSubplan.c
|
|
@@ -869,7 +869,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 = ExecAllocTableSlot(tupTable);
|
|
ExecSetSlotDescriptor(slot, tupDesc);
|
|
sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
|
|
@@ -877,7 +877,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
|
|
slot,
|
|
NULL);
|
|
|
|
- tupDesc = ExecTypeFromTL(rightptlist, false);
|
|
+ tupDesc = ExecTypeFromTL(rightptlist, false, false);
|
|
slot = ExecAllocTableSlot(tupTable);
|
|
ExecSetSlotDescriptor(slot, tupDesc);
|
|
sstate->projRight = ExecBuildProjectionInfo(righttlist,
|
|
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
|
|
index 6674f67..fec5c4a 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 "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/datum.h"
|
|
@@ -1224,6 +1225,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, ACL_KIND_PROC,
|
|
get_func_name(wfunc->winfnoid));
|
|
+ sepgsql_proc_execute(wfunc->winfnoid);
|
|
|
|
/* Fill in the perfuncstate data */
|
|
perfuncstate->wfuncstate = wfuncstate;
|
|
@@ -1418,6 +1420,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, ACL_KIND_PROC,
|
|
get_func_name(transfn_oid));
|
|
+ sepgsql_proc_execute(transfn_oid);
|
|
if (OidIsValid(finalfn_oid))
|
|
{
|
|
aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner,
|
|
@@ -1425,6 +1428,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc,
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, ACL_KIND_PROC,
|
|
get_func_name(finalfn_oid));
|
|
+ sepgsql_proc_execute(finalfn_oid);
|
|
}
|
|
}
|
|
|
|
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
|
|
index 4cd7b0b..f661849 100644
|
|
--- a/src/backend/executor/spi.c
|
|
+++ b/src/backend/executor/spi.c
|
|
@@ -705,6 +705,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 (HeapTupleHasSecid(mtuple))
|
|
+ HeapTupleSetSecid(mtuple, HeapTupleGetSecid(tuple));
|
|
}
|
|
else
|
|
{
|
|
diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c
|
|
index b6c46c1..6445ea8 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 "security/sepgsql.h"
|
|
#include "storage/fd.h"
|
|
#include "storage/large_object.h"
|
|
#include "utils/acl.h"
|
|
@@ -173,6 +174,9 @@ lo_read(int fd, char *buf, int len)
|
|
errmsg("permission denied for large object %u",
|
|
cookies[fd]->id)));
|
|
|
|
+ /* SELinux db_blob:{read} checks */
|
|
+ sepgsql_largeobject_read(cookies[fd]->id, cookies[fd]->snapshot);
|
|
+
|
|
status = inv_read(cookies[fd], buf, len);
|
|
|
|
return status;
|
|
@@ -205,6 +209,9 @@ lo_write(int fd, const char *buf, int len)
|
|
errmsg("permission denied for large object %u",
|
|
cookies[fd]->id)));
|
|
|
|
+ /* SELinux db_blob:{write} */
|
|
+ sepgsql_largeobject_write(cookies[fd]->id, cookies[fd]->snapshot);
|
|
+
|
|
status = inv_write(cookies[fd], buf, len);
|
|
|
|
return status;
|
|
@@ -233,6 +240,10 @@ Datum
|
|
lo_creat(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid lobjId;
|
|
+ Oid secid;
|
|
+
|
|
+ /* SELinux: db_blob:{create} */
|
|
+ secid = sepgsql_largeobject_create(InvalidOid, NULL);
|
|
|
|
/*
|
|
* We don't actually need to store into fscxt, but create it anyway to
|
|
@@ -240,7 +251,7 @@ lo_creat(PG_FUNCTION_ARGS)
|
|
*/
|
|
CreateFSContext();
|
|
|
|
- lobjId = inv_create(InvalidOid);
|
|
+ lobjId = inv_create(InvalidOid, secid);
|
|
|
|
PG_RETURN_OID(lobjId);
|
|
}
|
|
@@ -249,6 +260,10 @@ Datum
|
|
lo_create(PG_FUNCTION_ARGS)
|
|
{
|
|
Oid lobjId = PG_GETARG_OID(0);
|
|
+ Oid secid;
|
|
+
|
|
+ /* SELinux: db_blob:{create} */
|
|
+ secid = sepgsql_largeobject_create(lobjId, NULL);
|
|
|
|
/*
|
|
* We don't actually need to store into fscxt, but create it anyway to
|
|
@@ -256,7 +271,7 @@ lo_create(PG_FUNCTION_ARGS)
|
|
*/
|
|
CreateFSContext();
|
|
|
|
- lobjId = inv_create(lobjId);
|
|
+ lobjId = inv_create(lobjId, secid);
|
|
|
|
PG_RETURN_OID(lobjId);
|
|
}
|
|
@@ -286,6 +301,9 @@ lo_unlink(PG_FUNCTION_ARGS)
|
|
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
errmsg("must be owner of large object %u", lobjId)));
|
|
|
|
+ /* SELinux: db_blob:{drop} */
|
|
+ sepgsql_largeobject_drop(lobjId);
|
|
+
|
|
/*
|
|
* If there are any open LO FDs referencing that ID, close 'em.
|
|
*/
|
|
@@ -381,9 +399,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 secid;
|
|
|
|
#ifndef ALLOW_DANGEROUS_LO_FUNCTIONS
|
|
if (!superuser())
|
|
@@ -392,13 +411,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
|
|
+ /* SELinux: db_blob:{create import} */
|
|
+ secid = sepgsql_largeobject_import(lobjOid, fnamebuf);
|
|
|
|
CreateFSContext();
|
|
|
|
/*
|
|
* 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 +429,7 @@ lo_import_internal(text *filename, Oid lobjOid)
|
|
/*
|
|
* create an inversion object
|
|
*/
|
|
- oid = inv_create(lobjOid);
|
|
+ oid = inv_create(lobjOid, secid);
|
|
|
|
/*
|
|
* read in from the filesystem and write to the inversion object
|
|
@@ -447,7 +467,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,6 +478,8 @@ 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
|
|
+ /* SELinux: db_blob:{read export} */
|
|
+ sepgsql_largeobject_export(lobjId, fnamebuf);
|
|
|
|
CreateFSContext();
|
|
|
|
@@ -528,6 +550,9 @@ lo_truncate(PG_FUNCTION_ARGS)
|
|
errmsg("permission denied for large object %u",
|
|
cookies[fd]->id)));
|
|
|
|
+ /* SELinux: db_blob:{write} */
|
|
+ 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 13c82a6..ca21326 100644
|
|
--- a/src/backend/nodes/copyfuncs.c
|
|
+++ b/src/backend/nodes/copyfuncs.c
|
|
@@ -259,6 +259,7 @@ CopyScanFields(Scan *from, Scan *newnode)
|
|
CopyPlanFields((Plan *) from, (Plan *) newnode);
|
|
|
|
COPY_SCALAR_FIELD(scanrelid);
|
|
+ COPY_SCALAR_FIELD(rowlvPerms);
|
|
}
|
|
|
|
/*
|
|
@@ -2075,6 +2076,7 @@ _copyColumnDef(ColumnDef *from)
|
|
COPY_NODE_FIELD(raw_default);
|
|
COPY_NODE_FIELD(cooked_default);
|
|
COPY_NODE_FIELD(constraints);
|
|
+ COPY_NODE_FIELD(secLabel);
|
|
|
|
return newnode;
|
|
}
|
|
@@ -2414,6 +2416,7 @@ _copyCreateStmt(CreateStmt *from)
|
|
COPY_NODE_FIELD(options);
|
|
COPY_SCALAR_FIELD(oncommit);
|
|
COPY_STRING_FIELD(tablespacename);
|
|
+ COPY_NODE_FIELD(secLabel);
|
|
|
|
return newnode;
|
|
}
|
|
@@ -2638,6 +2641,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(subname);
|
|
+ COPY_NODE_FIELD(secLabel);
|
|
+
|
|
+ return newnode;
|
|
+}
|
|
+
|
|
static RuleStmt *
|
|
_copyRuleStmt(RuleStmt *from)
|
|
{
|
|
@@ -2887,6 +2905,7 @@ _copyCreateSeqStmt(CreateSeqStmt *from)
|
|
|
|
COPY_NODE_FIELD(sequence);
|
|
COPY_NODE_FIELD(options);
|
|
+ COPY_NODE_FIELD(secLabel);
|
|
|
|
return newnode;
|
|
}
|
|
@@ -3819,6 +3838,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 06a06b3..0752771 100644
|
|
--- a/src/backend/nodes/equalfuncs.c
|
|
+++ b/src/backend/nodes/equalfuncs.c
|
|
@@ -1078,6 +1078,7 @@ _equalCreateStmt(CreateStmt *a, CreateStmt *b)
|
|
COMPARE_NODE_FIELD(options);
|
|
COMPARE_SCALAR_FIELD(oncommit);
|
|
COMPARE_STRING_FIELD(tablespacename);
|
|
+ COMPARE_NODE_FIELD(secLabel);
|
|
|
|
return true;
|
|
}
|
|
@@ -1271,6 +1272,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(subname);
|
|
+ COMPARE_NODE_FIELD(secLabel);
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static bool
|
|
_equalRuleStmt(RuleStmt *a, RuleStmt *b)
|
|
{
|
|
COMPARE_NODE_FIELD(relation);
|
|
@@ -1477,6 +1491,7 @@ _equalCreateSeqStmt(CreateSeqStmt *a, CreateSeqStmt *b)
|
|
{
|
|
COMPARE_NODE_FIELD(sequence);
|
|
COMPARE_NODE_FIELD(options);
|
|
+ COMPARE_NODE_FIELD(secLabel);
|
|
|
|
return true;
|
|
}
|
|
@@ -2054,6 +2069,7 @@ _equalColumnDef(ColumnDef *a, ColumnDef *b)
|
|
COMPARE_NODE_FIELD(raw_default);
|
|
COMPARE_NODE_FIELD(cooked_default);
|
|
COMPARE_NODE_FIELD(constraints);
|
|
+ COMPARE_NODE_FIELD(secLabel);
|
|
|
|
return true;
|
|
}
|
|
@@ -2596,6 +2612,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 1c8691a..ffc2f19 100644
|
|
--- a/src/backend/nodes/outfuncs.c
|
|
+++ b/src/backend/nodes/outfuncs.c
|
|
@@ -285,6 +285,7 @@ _outScanInfo(StringInfo str, Scan *node)
|
|
_outPlanInfo(str, (Plan *) node);
|
|
|
|
WRITE_UINT_FIELD(scanrelid);
|
|
+ WRITE_UINT_FIELD(rowlvPerms);
|
|
}
|
|
|
|
/*
|
|
@@ -1534,6 +1535,7 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node)
|
|
WRITE_BOOL_FIELD(has_eclass_joins);
|
|
WRITE_BITMAPSET_FIELD(index_outer_relids);
|
|
WRITE_NODE_FIELD(index_inner_paths);
|
|
+ WRITE_UINT_FIELD(rowlvPerms);
|
|
}
|
|
|
|
static void
|
|
@@ -1717,6 +1719,7 @@ _outCreateStmt(StringInfo str, CreateStmt *node)
|
|
WRITE_NODE_FIELD(options);
|
|
WRITE_ENUM_FIELD(oncommit, OnCommitAction);
|
|
WRITE_STRING_FIELD(tablespacename);
|
|
+ WRITE_NODE_FIELD(secLabel);
|
|
}
|
|
|
|
static void
|
|
@@ -1839,6 +1842,7 @@ _outColumnDef(StringInfo str, ColumnDef *node)
|
|
WRITE_NODE_FIELD(raw_default);
|
|
WRITE_NODE_FIELD(cooked_default);
|
|
WRITE_NODE_FIELD(constraints);
|
|
+ WRITE_NODE_FIELD(secLabel);
|
|
}
|
|
|
|
static void
|
|
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
|
|
index 6e5c251..8156719 100644
|
|
--- a/src/backend/optimizer/plan/createplan.c
|
|
+++ b/src/backend/optimizer/plan/createplan.c
|
|
@@ -305,6 +305,9 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
|
|
break;
|
|
}
|
|
|
|
+ /* Copy of row-level permissions to Scan node */
|
|
+ ((Scan *)plan)->rowlvPerms = rel->rowlvPerms;
|
|
+
|
|
/*
|
|
* If there are any pseudoconstant clauses attached to this node, insert a
|
|
* gating Result node that evaluates the pseudoconstants as one-time
|
|
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
|
|
index be75590..694a40c 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 "security/sepgsql.h"
|
|
#include "tcop/tcopprot.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
@@ -3502,6 +3503,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
|
|
funcform->prosecdef ||
|
|
funcform->proretset ||
|
|
!heap_attisnull(func_tuple, Anum_pg_proc_proconfig) ||
|
|
+ !sepgsql_proc_hint_inlined(func_tuple) ||
|
|
funcform->pronargs != list_length(args))
|
|
return NULL;
|
|
|
|
@@ -3970,6 +3972,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
|
|
funcform->prosecdef ||
|
|
!funcform->proretset ||
|
|
!heap_attisnull(func_tuple, Anum_pg_proc_proconfig) ||
|
|
+ !sepgsql_proc_hint_inlined(func_tuple) ||
|
|
funcform->pronargs != list_length(fexpr->args))
|
|
{
|
|
ReleaseSysCache(func_tuple);
|
|
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
|
|
index 1d93203..3d3c455 100644
|
|
--- a/src/backend/optimizer/util/relnode.c
|
|
+++ b/src/backend/optimizer/util/relnode.c
|
|
@@ -21,6 +21,7 @@
|
|
#include "optimizer/plancat.h"
|
|
#include "optimizer/restrictinfo.h"
|
|
#include "parser/parsetree.h"
|
|
+#include "security/rowlevel.h"
|
|
#include "utils/hsearch.h"
|
|
|
|
|
|
@@ -91,6 +92,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
|
|
rel->has_eclass_joins = false;
|
|
rel->index_outer_relids = NULL;
|
|
rel->index_inner_paths = NIL;
|
|
+ rel->rowlvPerms = rowlvSetupPermissions(rte);
|
|
|
|
/* Check type of rtable entry */
|
|
switch (rte->rtekind)
|
|
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
|
|
index f110463..a236e3c 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"
|
|
@@ -660,7 +661,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,
|
|
@@ -775,6 +776,48 @@ transformInsertRow(ParseState *pstate, List *exprlist,
|
|
return result;
|
|
}
|
|
|
|
+static void
|
|
+transformSelectIntoSystemColumn(ParseState *pstate, Query *qry)
|
|
+{
|
|
+ ListCell *l;
|
|
+ uint32 system_attrs = 0;
|
|
+ bool relhasoids
|
|
+ = interpretOidsOption(qry->intoClause->options);
|
|
+
|
|
+ foreach (l, qry->targetList)
|
|
+ {
|
|
+ Form_pg_attribute attr;
|
|
+ TargetEntry *tle = lfirst(l);
|
|
+
|
|
+ if (tle->resjunk)
|
|
+ continue;
|
|
+
|
|
+ attr = SystemAttributeByName(tle->resname, relhasoids);
|
|
+ if (attr && SystemAttributeIsWritable(attr->attnum))
|
|
+ {
|
|
+ uint32 mask = (1<<(-attr->attnum));
|
|
+
|
|
+ /* duplication checks */
|
|
+ if (system_attrs & mask)
|
|
+ continue;
|
|
+ system_attrs |= mask;
|
|
+
|
|
+ if (exprType((Node *) tle->expr) != attr->atttypid)
|
|
+ {
|
|
+ tle->expr =
|
|
+ (Expr *) coerce_to_target_type(pstate,
|
|
+ (Node *) tle->expr,
|
|
+ exprType((Node *) tle->expr),
|
|
+ attr->atttypid,
|
|
+ attr->atttypmod,
|
|
+ COERCION_IMPLICIT,
|
|
+ COERCE_IMPLICIT_CAST,
|
|
+ -1);
|
|
+ }
|
|
+ tle->resjunk = true;
|
|
+ }
|
|
+ }
|
|
+}
|
|
|
|
/*
|
|
* transformSelectStmt -
|
|
@@ -879,6 +922,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
|
|
if (stmt->intoClause)
|
|
{
|
|
qry->intoClause = stmt->intoClause;
|
|
+ transformSelectIntoSystemColumn(pstate, qry);
|
|
if (stmt->intoClause->colNames)
|
|
applyColumnNames(qry->targetList, stmt->intoClause->colNames);
|
|
}
|
|
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
|
|
index d13b0f0..80a1f4a 100644
|
|
--- a/src/backend/parser/gram.y
|
|
+++ b/src/backend/parser/gram.y
|
|
@@ -58,6 +58,7 @@
|
|
#include "nodes/makefuncs.h"
|
|
#include "nodes/nodeFuncs.h"
|
|
#include "parser/gramparse.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/lmgr.h"
|
|
#include "utils/date.h"
|
|
#include "utils/datetime.h"
|
|
@@ -184,7 +185,7 @@ static TypeName *TableFuncTypeName(List *columns);
|
|
%type <node> stmt schema_stmt
|
|
AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt
|
|
AlterForeignServerStmt AlterGroupStmt
|
|
- AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt
|
|
+ AlterObjectSchemaStmt AlterOwnerStmt AlterSecLabelStmt AlterSeqStmt AlterTableStmt
|
|
AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt
|
|
AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
|
|
ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
|
|
@@ -402,6 +403,10 @@ static TypeName *TableFuncTypeName(List *columns);
|
|
%type <str> OptTableSpace OptConsTableSpace OptTableSpaceOwner
|
|
%type <list> opt_check_option
|
|
|
|
+%type <node> OptSecLabel SecLabelItem SecLabelToItem
|
|
+%type <list> OptTableSecLabel TableSecLabelList
|
|
+%type <defelt> TableSecLabelItem
|
|
+
|
|
%type <target> xml_attribute_el
|
|
%type <list> xml_attribute_list xml_attributes
|
|
%type <node> xml_root_version opt_xml_root_standalone
|
|
@@ -437,7 +442,7 @@ static TypeName *TableFuncTypeName(List *columns);
|
|
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
|
|
CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT
|
|
COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS
|
|
- CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB
|
|
+ CONTENT_P CONTEXT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB
|
|
CREATEROLE CREATEUSER CROSS CSV CURRENT_P
|
|
CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA
|
|
CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
|
|
@@ -608,6 +613,7 @@ stmt :
|
|
| AlterGroupStmt
|
|
| AlterObjectSchemaStmt
|
|
| AlterOwnerStmt
|
|
+ | AlterSecLabelStmt
|
|
| AlterSeqStmt
|
|
| AlterTableStmt
|
|
| AlterRoleSetStmt
|
|
@@ -1042,7 +1048,7 @@ DropGroupStmt:
|
|
*****************************************************************************/
|
|
|
|
CreateSchemaStmt:
|
|
- CREATE SCHEMA OptSchemaName AUTHORIZATION RoleId OptSchemaEltList
|
|
+ CREATE SCHEMA OptSchemaName AUTHORIZATION RoleId OptSecLabel OptSchemaEltList
|
|
{
|
|
CreateSchemaStmt *n = makeNode(CreateSchemaStmt);
|
|
/* One can omit the schema name or the authorization id. */
|
|
@@ -1051,16 +1057,18 @@ CreateSchemaStmt:
|
|
else
|
|
n->schemaname = $5;
|
|
n->authid = $5;
|
|
- n->schemaElts = $6;
|
|
+ n->secLabel = $6;
|
|
+ n->schemaElts = $7;
|
|
$$ = (Node *)n;
|
|
}
|
|
- | CREATE SCHEMA ColId OptSchemaEltList
|
|
+ | CREATE SCHEMA ColId OptSecLabel OptSchemaEltList
|
|
{
|
|
CreateSchemaStmt *n = makeNode(CreateSchemaStmt);
|
|
/* ...but not both */
|
|
n->schemaname = $3;
|
|
n->authid = NULL;
|
|
- n->schemaElts = $4;
|
|
+ n->secLabel = $4;
|
|
+ n->schemaElts = $5;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
@@ -2037,7 +2045,7 @@ opt_using:
|
|
*****************************************************************************/
|
|
|
|
CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
|
|
- OptInherit OptWith OnCommitOption OptTableSpace
|
|
+ OptInherit OptWith OnCommitOption OptTableSpace OptTableSecLabel
|
|
{
|
|
CreateStmt *n = makeNode(CreateStmt);
|
|
$4->istemp = $2;
|
|
@@ -2048,10 +2056,11 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
|
|
n->options = $9;
|
|
n->oncommit = $10;
|
|
n->tablespacename = $11;
|
|
+ n->secLabel = $12;
|
|
$$ = (Node *)n;
|
|
}
|
|
| CREATE OptTemp TABLE qualified_name OF qualified_name
|
|
- '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace
|
|
+ '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace OptTableSecLabel
|
|
{
|
|
/* SQL99 CREATE TABLE OF <UDT> (cols) seems to be satisfied
|
|
* by our inheritance capabilities. Let's try it...
|
|
@@ -2065,6 +2074,7 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
|
|
n->options = $10;
|
|
n->oncommit = $11;
|
|
n->tablespacename = $12;
|
|
+ n->secLabel = $13;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
@@ -2114,6 +2124,7 @@ columnDef: ColId Typename ColQualList
|
|
n->typename = $2;
|
|
n->constraints = $3;
|
|
n->is_local = true;
|
|
+ n->secLabel = NULL;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
@@ -2585,12 +2596,13 @@ opt_with_data:
|
|
*****************************************************************************/
|
|
|
|
CreateSeqStmt:
|
|
- CREATE OptTemp SEQUENCE qualified_name OptSeqOptList
|
|
+ CREATE OptTemp SEQUENCE qualified_name OptSeqOptList OptSecLabel
|
|
{
|
|
CreateSeqStmt *n = makeNode(CreateSeqStmt);
|
|
$4->istemp = $2;
|
|
n->sequence = $4;
|
|
n->options = $5;
|
|
+ n->secLabel = $6;
|
|
$$ = (Node *)n;
|
|
}
|
|
;
|
|
@@ -4893,6 +4905,10 @@ createfunc_opt_item:
|
|
{
|
|
$$ = makeDefElem("window", (Node *)makeInteger(TRUE));
|
|
}
|
|
+ | SecLabelItem
|
|
+ {
|
|
+ $$ = makeDefElem("security_context", $1);
|
|
+ }
|
|
| common_func_opt_item
|
|
{
|
|
$$ = $1;
|
|
@@ -5607,6 +5623,101 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId
|
|
}
|
|
;
|
|
|
|
+/*****************************************************************************
|
|
+ *
|
|
+ * ALTER THING name SECURITY CONTEXT TO <newlabel>
|
|
+ *
|
|
+ *****************************************************************************/
|
|
+
|
|
+AlterSecLabelStmt: ALTER DATABASE database_name SecLabelToItem
|
|
+ {
|
|
+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt);
|
|
+ n->objectType = OBJECT_DATABASE;
|
|
+ n->object = list_make1(makeString($3));
|
|
+ n->secLabel = $4;
|
|
+ $$ = (Node *) n;
|
|
+ }
|
|
+ | ALTER SCHEMA name SecLabelToItem
|
|
+ {
|
|
+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt);
|
|
+ n->objectType = OBJECT_SCHEMA;
|
|
+ n->object = list_make1(makeString($3));
|
|
+ n->secLabel = $4;
|
|
+ $$ = (Node *) n;
|
|
+ }
|
|
+ | ALTER TABLE relation_expr SecLabelToItem
|
|
+ {
|
|
+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt);
|
|
+ n->objectType = OBJECT_TABLE;
|
|
+ n->relation = $3;
|
|
+ n->secLabel = $4;
|
|
+ $$ = (Node *) n;
|
|
+ }
|
|
+ | ALTER TABLE relation_expr ALTER opt_column ColId SecLabelToItem
|
|
+ {
|
|
+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt);
|
|
+ n->objectType = OBJECT_COLUMN;
|
|
+ n->relation = $3;
|
|
+ n->subname = $6;
|
|
+ n->secLabel = $7;
|
|
+ $$ = (Node *) n;
|
|
+ }
|
|
+ | ALTER SEQUENCE relation_expr SecLabelToItem
|
|
+ {
|
|
+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt);
|
|
+ n->objectType = OBJECT_SEQUENCE;
|
|
+ n->relation = $3;
|
|
+ n->secLabel = $4;
|
|
+ $$ = (Node *) n;
|
|
+ }
|
|
+ | ALTER FUNCTION function_with_argtypes SecLabelToItem
|
|
+ {
|
|
+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt);
|
|
+ n->objectType = OBJECT_FUNCTION;
|
|
+ n->object = $3->funcname;
|
|
+ n->objarg = $3->funcargs;
|
|
+ n->secLabel = $4;
|
|
+ $$ = (Node *) n;
|
|
+ }
|
|
+ | ALTER LARGE_P OBJECT_P Iconst SecLabelToItem
|
|
+ {
|
|
+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt);
|
|
+ n->objectType = OBJECT_LARGEOBJECT;
|
|
+ n->object = list_make1(makeInteger($4));
|
|
+ n->secLabel = $5;
|
|
+ $$ = (Node *) n;
|
|
+ }
|
|
+ ;
|
|
+
|
|
+OptTableSecLabel: SECURITY CONTEXT_P '(' TableSecLabelList ')' { $$ = $4; }
|
|
+ | /* EMPTY */ { $$ = NIL; }
|
|
+ ;
|
|
+
|
|
+TableSecLabelList: TableSecLabelItem { $$ = list_make1($1); }
|
|
+ | TableSecLabelList ',' TableSecLabelItem { $$ = lappend($1, $3); }
|
|
+ ;
|
|
+
|
|
+TableSecLabelItem: Sconst
|
|
+ { $$ = makeDefElem(NULL, (Node *)makeString($1)); }
|
|
+ | ColId '=' Sconst
|
|
+ { $$ = makeDefElem($1, (Node *)makeString($3)); }
|
|
+ ;
|
|
+
|
|
+OptSecLabel: SecLabelItem { $$ = $1; }
|
|
+ | /* EMPTY */ { $$ = NULL; }
|
|
+ ;
|
|
+
|
|
+SecLabelItem: SECURITY CONTEXT_P '(' Sconst ')'
|
|
+ {
|
|
+ $$ = (Node *) makeString($4);
|
|
+ }
|
|
+ ;
|
|
+
|
|
+SecLabelToItem: SECURITY CONTEXT_P TO Sconst
|
|
+ {
|
|
+ $$ = (Node *) makeString($4);
|
|
+ }
|
|
+ ;
|
|
|
|
/*****************************************************************************
|
|
*
|
|
@@ -6049,6 +6160,10 @@ createdb_opt_item:
|
|
{
|
|
$$ = makeDefElem("owner", NULL);
|
|
}
|
|
+ | SecLabelItem
|
|
+ {
|
|
+ $$ = makeDefElem("security_context", $1);
|
|
+ }
|
|
;
|
|
|
|
/*
|
|
@@ -10175,6 +10290,7 @@ unreserved_keyword:
|
|
| CONNECTION
|
|
| CONSTRAINTS
|
|
| CONTENT_P
|
|
+ | CONTEXT_P
|
|
| CONTINUE_P
|
|
| CONVERSION_P
|
|
| COPY
|
|
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
|
|
index 08b8edb..97aa7aa 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"
|
|
@@ -361,16 +362,33 @@ transformAssignedExpr(ParseState *pstate,
|
|
Oid attrtype; /* type of target column */
|
|
int32 attrtypmod;
|
|
Relation rd = pstate->p_target_relation;
|
|
+ bool relhasoids = RelationGetForm(rd)->relhasoids;
|
|
|
|
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 attForm
|
|
+ = SystemAttributeDefinition(attrno, relhasoids);
|
|
+ if (attForm && SystemAttributeIsWritable(attrno))
|
|
+ {
|
|
+ attrtype = attForm->atttypid;
|
|
+ attrtypmod = attForm->atttypmod;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("cannot assign to system column \"%s\"",
|
|
+ colname),
|
|
+ parser_errposition(pstate, location)));
|
|
+ return NULL; /* compiler kindness */
|
|
+ }
|
|
+ }
|
|
|
|
/*
|
|
* If the expression is a DEFAULT placeholder, insert the attribute's
|
|
@@ -515,6 +533,9 @@ updateTargetListEntry(ParseState *pstate,
|
|
*/
|
|
tle->resno = (AttrNumber) attrno;
|
|
tle->resname = colname;
|
|
+
|
|
+ if (SystemAttributeIsWritable(attrno))
|
|
+ tle->resjunk = true;
|
|
}
|
|
|
|
|
|
@@ -789,6 +810,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
|
|
Bitmapset *wholecols = NULL;
|
|
Bitmapset *partialcols = NULL;
|
|
ListCell *tl;
|
|
+ uint32 system_attrs = 0UL;
|
|
|
|
foreach(tl, cols)
|
|
{
|
|
@@ -797,14 +819,37 @@ 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)
|
|
+ {
|
|
+ if (SystemAttributeIsWritable(attrno))
|
|
+ {
|
|
+ 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(pstate->p_target_relation)),
|
|
+ parser_errposition(pstate, col->location)));
|
|
+ }
|
|
|
|
/*
|
|
* Check for duplicates, but only of whole columns --- we allow
|
|
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
|
|
index 1a9e387..c5ea995 100644
|
|
--- a/src/backend/parser/parse_utilcmd.c
|
|
+++ b/src/backend/parser/parse_utilcmd.c
|
|
@@ -49,6 +49,7 @@
|
|
#include "parser/parse_type.h"
|
|
#include "parser/parse_utilcmd.h"
|
|
#include "rewrite/rewriteManip.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/lsyscache.h"
|
|
@@ -565,6 +566,8 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, ACL_KIND_CLASS,
|
|
RelationGetRelationName(relation));
|
|
+ /* SELinux checks */
|
|
+ sepgsql_relation_copy_definition(RelationGetRelid(relation));
|
|
|
|
tupleDesc = RelationGetDescr(relation);
|
|
constr = tupleDesc->constr;
|
|
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
|
|
index 3e1a056..48d0642 100644
|
|
--- a/src/backend/postmaster/autovacuum.c
|
|
+++ b/src/backend/postmaster/autovacuum.c
|
|
@@ -2004,7 +2004,7 @@ do_autovacuum(void)
|
|
object.classId = RelationRelationId;
|
|
object.objectId = relid;
|
|
object.objectSubId = 0;
|
|
- performDeletion(&object, DROP_CASCADE);
|
|
+ performDeletionNoPerms(&object, DROP_CASCADE);
|
|
}
|
|
else
|
|
{
|
|
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
|
|
index d63214b..e88b469 100644
|
|
--- a/src/backend/postmaster/postmaster.c
|
|
+++ b/src/backend/postmaster/postmaster.c
|
|
@@ -108,6 +108,7 @@
|
|
#include "postmaster/pgarch.h"
|
|
#include "postmaster/postmaster.h"
|
|
#include "postmaster/syslogger.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/fd.h"
|
|
#include "storage/ipc.h"
|
|
#include "storage/pg_shmem.h"
|
|
@@ -209,7 +210,8 @@ static pid_t StartupPID = 0,
|
|
AutoVacPID = 0,
|
|
PgArchPID = 0,
|
|
PgStatPID = 0,
|
|
- SysLoggerPID = 0;
|
|
+ SysLoggerPID = 0,
|
|
+ sepgsqlReceiverPID = 0;
|
|
|
|
/* Startup/shutdown state */
|
|
#define NoShutdown 0
|
|
@@ -445,6 +447,7 @@ static void ShmemBackendArrayRemove(Backend *bn);
|
|
#define StartupDataBase() StartChildProcess(StartupProcess)
|
|
#define StartBackgroundWriter() StartChildProcess(BgWriterProcess)
|
|
#define StartWalWriter() StartChildProcess(WalWriterProcess)
|
|
+#define StartSELinuxReceiver() StartChildProcess(SelinuxReceiverProcess)
|
|
|
|
/* Macros to check exit status of a child process */
|
|
#define EXIT_STATUS_0(st) ((st) == 0)
|
|
@@ -1436,6 +1439,11 @@ ServerLoop(void)
|
|
if (PgStatPID == 0 && pmState == PM_RUN)
|
|
PgStatPID = pgstat_start();
|
|
|
|
+ /* if we have lost the selinux netlink receiver, try to start */
|
|
+ if (sepgsqlReceiverPID == 0 && pmState == PM_RUN &&
|
|
+ sepgsqlReceiverStart())
|
|
+ sepgsqlReceiverPID = StartSELinuxReceiver();
|
|
+
|
|
/* If we need to signal the autovacuum launcher, do so now */
|
|
if (avlauncher_needs_signal)
|
|
{
|
|
@@ -2055,6 +2063,8 @@ SIGHUP_handler(SIGNAL_ARGS)
|
|
signal_child(SysLoggerPID, SIGHUP);
|
|
if (PgStatPID != 0)
|
|
signal_child(PgStatPID, SIGHUP);
|
|
+ if (sepgsqlReceiverPID != 0)
|
|
+ signal_child(sepgsqlReceiverPID, SIGHUP);
|
|
|
|
/* Reload authentication config files too */
|
|
if (!load_hba())
|
|
@@ -2115,6 +2125,9 @@ pmdie(SIGNAL_ARGS)
|
|
/* and the walwriter too */
|
|
if (WalWriterPID != 0)
|
|
signal_child(WalWriterPID, SIGTERM);
|
|
+ /* and the selinux netlink receiver too */
|
|
+ if (sepgsqlReceiverPID != 0)
|
|
+ signal_child(sepgsqlReceiverPID, SIGTERM);
|
|
pmState = PM_WAIT_BACKUP;
|
|
}
|
|
|
|
@@ -2162,6 +2175,9 @@ pmdie(SIGNAL_ARGS)
|
|
/* and the walwriter too */
|
|
if (WalWriterPID != 0)
|
|
signal_child(WalWriterPID, SIGTERM);
|
|
+ /* and the selinux netlink receiver too */
|
|
+ if (sepgsqlReceiverPID != 0)
|
|
+ signal_child(sepgsqlReceiverPID, SIGTERM);
|
|
pmState = PM_WAIT_BACKENDS;
|
|
}
|
|
|
|
@@ -2195,6 +2211,8 @@ pmdie(SIGNAL_ARGS)
|
|
signal_child(PgArchPID, SIGQUIT);
|
|
if (PgStatPID != 0)
|
|
signal_child(PgStatPID, SIGQUIT);
|
|
+ if (sepgsqlReceiverPID != 0)
|
|
+ signal_child(sepgsqlReceiverPID, SIGQUIT);
|
|
ExitPostmaster(0);
|
|
break;
|
|
}
|
|
@@ -2457,6 +2475,16 @@ reaper(SIGNAL_ARGS)
|
|
continue;
|
|
}
|
|
|
|
+ /* Was it the selinux netlink receiver process? */
|
|
+ if (pid == sepgsqlReceiverPID)
|
|
+ {
|
|
+ sepgsqlReceiverPID = 0;
|
|
+ if (!EXIT_STATUS_0(exitstatus))
|
|
+ LogChildExit(LOG, _("SELinux netlink receiver process"),
|
|
+ pid, exitstatus);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
/*
|
|
* Else do standard backend child cleanup.
|
|
*/
|
|
@@ -2648,6 +2676,18 @@ HandleChildCrash(int pid, int exitstatus, const char *procname)
|
|
signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT));
|
|
}
|
|
|
|
+ /* Take care of the selinux netlink receiver too */
|
|
+ if (pid == sepgsqlReceiverPID)
|
|
+ sepgsqlReceiverPID = 0;
|
|
+ else if (sepgsqlReceiverPID != 0 && !FatalError)
|
|
+ {
|
|
+ ereport(DEBUG2,
|
|
+ (errmsg_internal("sending %s to process %d",
|
|
+ (SendStop ? "SIGSTOP" : "SIGQUIT"),
|
|
+ (int) sepgsqlReceiverPID)));
|
|
+ signal_child(sepgsqlReceiverPID, (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
|
|
@@ -2780,7 +2820,8 @@ PostmasterStateMachine(void)
|
|
StartupPID == 0 &&
|
|
(BgWriterPID == 0 || !FatalError) &&
|
|
WalWriterPID == 0 &&
|
|
- AutoVacPID == 0)
|
|
+ AutoVacPID == 0 &&
|
|
+ sepgsqlReceiverPID == 0)
|
|
{
|
|
if (FatalError)
|
|
{
|
|
@@ -4323,6 +4364,12 @@ StartChildProcess(AuxProcType type)
|
|
ereport(LOG,
|
|
(errmsg("could not fork WAL writer process: %m")));
|
|
break;
|
|
+#ifdef HAVE_SELINUX
|
|
+ case SelinuxReceiverProcess:
|
|
+ ereport(LOG,
|
|
+ (errmsg("could not fork selinux receiver process: %m")));
|
|
+ break;
|
|
+#endif
|
|
default:
|
|
ereport(LOG,
|
|
(errmsg("could not fork process: %m")));
|
|
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
|
|
index 0b4f279..5fe6fd0 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 "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/inval.h"
|
|
@@ -266,6 +267,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 a65b020..b967838 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 "security/sepgsql.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/fmgroids.h"
|
|
#include "utils/inval.h"
|
|
@@ -78,6 +79,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);
|
|
+
|
|
/*
|
|
* Do the deletion
|
|
*/
|
|
diff --git a/src/backend/security/Makefile b/src/backend/security/Makefile
|
|
new file mode 100644
|
|
index 0000000..23e51d6
|
|
--- /dev/null
|
|
+++ b/src/backend/security/Makefile
|
|
@@ -0,0 +1,13 @@
|
|
+#
|
|
+# Makefile for the enhanced security subsystem
|
|
+#
|
|
+
|
|
+subdir = src/backend/security
|
|
+top_builddir = ../../..
|
|
+include $(top_builddir)/src/Makefile.global
|
|
+
|
|
+SUBDIRS = sepgsql
|
|
+
|
|
+OBJS = rowlevel.o
|
|
+
|
|
+include $(top_srcdir)/src/backend/common.mk
|
|
diff --git a/src/backend/security/rowlevel.c b/src/backend/security/rowlevel.c
|
|
new file mode 100644
|
|
index 0000000..e08d4cc
|
|
--- /dev/null
|
|
+++ b/src/backend/security/rowlevel.c
|
|
@@ -0,0 +1,121 @@
|
|
+/*
|
|
+ * src/backend/security/common.c
|
|
+ * common facilities for row-level access controls both of DAC and MAC
|
|
+ *
|
|
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
|
+ * Portions Copyright (c) 1994, Regents of the University of California
|
|
+ */
|
|
+#include "postgres.h"
|
|
+
|
|
+#include "catalog/pg_security.h"
|
|
+#include "security/rowlevel.h"
|
|
+#include "security/sepgsql.h"
|
|
+#include "storage/bufmgr.h"
|
|
+#include "storage/bufpage.h"
|
|
+#include "utils/rel.h"
|
|
+#include "utils/tqual.h"
|
|
+
|
|
+/*
|
|
+ * rowlvGetPerformingMode
|
|
+ * rowlvSetPerformingMode
|
|
+ * enables to control the behavior of row-level features
|
|
+ * when violated tuples are detected.
|
|
+ * The default is ROWLV_FILTER_MODE which filters out
|
|
+ * violated tuples from result set, ROWLV_ABORT_MODE
|
|
+ * raises an error and ROWLV_BYPASS_MODE do nothing.
|
|
+ */
|
|
+static int rowlv_mode = ROWLV_FILTER_MODE;
|
|
+
|
|
+int rowlvGetPerformingMode(void)
|
|
+{
|
|
+ return rowlv_mode;
|
|
+}
|
|
+
|
|
+int rowlvSetPerformingMode(int new_mode)
|
|
+{
|
|
+ int old_mode = new_mode;
|
|
+
|
|
+ rowlv_mode = new_mode;
|
|
+
|
|
+ return old_mode;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * rowlvSetupPermissions
|
|
+ * setups permissions for row-level access controls.
|
|
+ */
|
|
+uint32
|
|
+rowlvSetupPermissions(RangeTblEntry *rte)
|
|
+{
|
|
+ return sepgsqlSetupTuplePerms(rte);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * rowlvExecScan
|
|
+ * a hook to filter out invisible/untouchable tuples.
|
|
+ */
|
|
+static bool
|
|
+rowlvExecScan(Scan *scan, Relation rel, TupleTableSlot *slot, bool abort)
|
|
+{
|
|
+ HeapTuple tuple;
|
|
+ uint32 perms = scan->rowlvPerms;
|
|
+
|
|
+ if (!perms)
|
|
+ return true;
|
|
+
|
|
+ tuple = ExecMaterializeSlot(slot);
|
|
+
|
|
+ return sepgsqlExecScan(rel, tuple, perms, abort);
|
|
+}
|
|
+
|
|
+bool
|
|
+rowlvExecScanFilter(Scan *scan, Relation rel, TupleTableSlot *slot)
|
|
+{
|
|
+ if (!rel || !scan->rowlvPerms || rowlv_mode != ROWLV_FILTER_MODE)
|
|
+ return true;
|
|
+
|
|
+ return rowlvExecScan(scan, rel, slot, false);
|
|
+}
|
|
+
|
|
+void
|
|
+rowlvExecScanAbort(Scan *scan, Relation rel, TupleTableSlot *slot)
|
|
+{
|
|
+ if (!rel || !scan->rowlvPerms || rowlv_mode != ROWLV_ABORT_MODE)
|
|
+ return;
|
|
+
|
|
+ rowlvExecScan(scan, rel, slot, true);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * rowlvCopyToTuple
|
|
+ * checks permission on fetched tuple
|
|
+ */
|
|
+bool
|
|
+rowlvCopyToTuple(Relation rel, HeapTuple tuple)
|
|
+{
|
|
+ if (!sepgsqlExecScan(rel, tuple, SEPG_DB_TUPLE__SELECT, false))
|
|
+ return false;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * rowlvHeapTupleInsert
|
|
+ * assign default security attribute, and check permission
|
|
+ * if necessary.
|
|
+ */
|
|
+void
|
|
+rowlvHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal)
|
|
+{
|
|
+ sepgsqlHeapTupleInsert(rel, newtup, internal);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * rowlvHeapTupleUpdate
|
|
+ * check permission to change security attribute, if necesary
|
|
+ */
|
|
+void
|
|
+rowlvHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup)
|
|
+{
|
|
+ sepgsqlHeapTupleUpdate(rel, otid, newtup);
|
|
+}
|
|
diff --git a/src/backend/security/sepgsql/Makefile b/src/backend/security/sepgsql/Makefile
|
|
new file mode 100644
|
|
index 0000000..2417aae
|
|
--- /dev/null
|
|
+++ b/src/backend/security/sepgsql/Makefile
|
|
@@ -0,0 +1,15 @@
|
|
+#
|
|
+# Makefile
|
|
+# Makefile for utils/sepgsql : SE-PostgreSQL
|
|
+#
|
|
+
|
|
+subdir = src/backend/security/sepgsql
|
|
+top_builddir = ../../../..
|
|
+include $(top_builddir)/src/Makefile.global
|
|
+
|
|
+OBJS = misc.o
|
|
+ifeq ($(enable_selinux), yes)
|
|
+OBJS += selinux.o checker.o bridge.o label.o
|
|
+endif
|
|
+
|
|
+include $(top_srcdir)/src/backend/common.mk
|
|
diff --git a/src/backend/security/sepgsql/avc.c b/src/backend/security/sepgsql/avc.c
|
|
new file mode 100644
|
|
index 0000000..8a88bcb
|
|
--- /dev/null
|
|
+++ b/src/backend/security/sepgsql/avc.c
|
|
@@ -0,0 +1,881 @@
|
|
+/*
|
|
+ * src/backend/security/sepgsql/avc.c
|
|
+ * SE-PostgreSQL userspace access vector cache
|
|
+ *
|
|
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
|
+ * Portions Copyright (c) 1994, Regents of the University of California
|
|
+ */
|
|
+#include "postgres.h"
|
|
+
|
|
+#include "access/hash.h"
|
|
+#include "catalog/pg_security.h"
|
|
+#include "libpq/pqsignal.h"
|
|
+#include "miscadmin.h"
|
|
+#include "postmaster/postmaster.h"
|
|
+#include "security/sepgsql.h"
|
|
+#include "storage/ipc.h"
|
|
+#include "storage/lwlock.h"
|
|
+#include "utils/memutils.h"
|
|
+#include <signal.h>
|
|
+#include <unistd.h>
|
|
+#include <selinux/avc.h>
|
|
+
|
|
+/*
|
|
+ * AVC: userspace access vector cache
|
|
+ *
|
|
+ * SE-PostgreSQL asks in-kernel SELinux to make its decision whether
|
|
+ * the required accesses should be allowed, or not, based on the unified
|
|
+ * security policy. It needs a system call invocation to communicate
|
|
+ * a kernel feature, such as SELinux, but it is a heavy task in most cases
|
|
+ * due to the context switching.
|
|
+ *
|
|
+ * The userspace avc enables to minimize the number of system call
|
|
+ * invocations, using a chache mechanim for the certain pair of security
|
|
+ * contexts and object classes (it means the kind of actions).
|
|
+ * It enables to hold recently fetched results from the in-kernel SELinux,
|
|
+ * and make a decision without context switching, if the cache hit.
|
|
+ *
|
|
+ * When the state of security policy is changed, the cached results
|
|
+ * shall to be invalidated. The state monitoring process launched by
|
|
+ * postmaster can receives the notification messages from the kernel
|
|
+ * space, and invalidate the current version of avc.
|
|
+ */
|
|
+static MemoryContext AvcMemCtx = NULL;
|
|
+
|
|
+#define AVC_HASH_NUM_SLOTS 256
|
|
+#define AVC_HASH_NUM_NODES 180
|
|
+
|
|
+#define AVC_DATUM_NSID_SLOTS 19
|
|
+typedef struct
|
|
+{
|
|
+ uint32 hash_key;
|
|
+
|
|
+ security_class_t tclass;
|
|
+ sepgsql_sid_t tsid;
|
|
+ sepgsql_sid_t nsid[AVC_DATUM_NSID_SLOTS];
|
|
+
|
|
+ access_vector_t allowed;
|
|
+ access_vector_t decided;
|
|
+ access_vector_t auditallow;
|
|
+ access_vector_t auditdeny;
|
|
+
|
|
+ bool hot_cache;
|
|
+ bool permissive;
|
|
+
|
|
+ char ncontext[1];
|
|
+} avc_datum;
|
|
+
|
|
+typedef struct avc_page
|
|
+{
|
|
+ struct avc_page *next;
|
|
+
|
|
+ security_context_t scontext;
|
|
+
|
|
+ List *slot[AVC_HASH_NUM_SLOTS];
|
|
+
|
|
+ uint32 avc_count;
|
|
+ uint32 lru_hint;
|
|
+} avc_page;
|
|
+
|
|
+static avc_page *current_page = NULL;
|
|
+
|
|
+static int avc_version;
|
|
+
|
|
+/*
|
|
+ * selinux_state
|
|
+ *
|
|
+ * It is deployed on the shared memory region, to show the system
|
|
+ * state of SELinux and its security policy.
|
|
+ *
|
|
+ * The selinux_state->version should be checked prior to avc accesses.
|
|
+ * If it does not match with the local avc_version, it means that
|
|
+ * system security policy was reloaded or system state (enforcing
|
|
+ * or permissive) was changed.
|
|
+ *
|
|
+ * The state monitoring worker process receives messages from the
|
|
+ * kernel using libselinux, and it updates the selinux_state.
|
|
+ */
|
|
+struct
|
|
+{
|
|
+ int version;
|
|
+
|
|
+ bool enforcing;
|
|
+
|
|
+} *selinux_state = NULL;
|
|
+
|
|
+Size
|
|
+sepgsqlShmemSize(void)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return 0;
|
|
+
|
|
+ return sizeof(*selinux_state);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsql_shmem_init
|
|
+ * attaches shared memory segment.
|
|
+ */
|
|
+static void
|
|
+sepgsqlShmemInit(void)
|
|
+{
|
|
+ bool found;
|
|
+
|
|
+ selinux_state = ShmemInitStruct("SELinux policy state",
|
|
+ sepgsqlShmemSize(), &found);
|
|
+ if (!found)
|
|
+ {
|
|
+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE);
|
|
+
|
|
+ selinux_state->version = 0;
|
|
+ selinux_state->enforcing = (security_getenforce() > 0);
|
|
+
|
|
+ LWLockRelease(SepgsqlAvcLock);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlAvcReset
|
|
+ *
|
|
+ * It invalidate access vector cache. It has to be called on errors,
|
|
+ * because avc entries for newly created context is uncertain whether
|
|
+ * it is still valid, or not.
|
|
+ * If error happens before avc initialization, we simply skip it.
|
|
+ */
|
|
+void
|
|
+sepgsqlAvcReset(void)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled() || !AvcMemCtx)
|
|
+ return;
|
|
+
|
|
+ MemoryContextReset(AvcMemCtx);
|
|
+
|
|
+ current_page = NULL;
|
|
+
|
|
+ sepgsqlAvcSwitchClient(sepgsqlGetClientLabel());
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlAvcCheckValid
|
|
+ *
|
|
+ * It checks whether the current AVC pages are valid, or not.
|
|
+ * If state monitoring process already received an invalidation
|
|
+ * message from the kernel, it clears current AVC pages and
|
|
+ * returns false.
|
|
+ */
|
|
+static bool
|
|
+sepgsqlAvcCheckValid(void)
|
|
+{
|
|
+ bool result = true;
|
|
+
|
|
+ LWLockAcquire(SepgsqlAvcLock, LW_SHARED);
|
|
+ if (avc_version != selinux_state->version)
|
|
+ {
|
|
+ /* reset invalid avc pages, and makes an empty one */
|
|
+ MemoryContextReset(AvcMemCtx);
|
|
+
|
|
+ current_page = NULL;
|
|
+
|
|
+ sepgsqlAvcSwitchClient(sepgsqlGetClientLabel());
|
|
+
|
|
+ /* copy current version to local */
|
|
+ avc_version = selinux_state->version;
|
|
+
|
|
+ result = false;
|
|
+ }
|
|
+ LWLockRelease(SepgsqlAvcLock);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlAvcInitialize
|
|
+ *
|
|
+ * It allocates a memory context for userspace AVC,
|
|
+ * map shared memory segment, and initialize avc_page
|
|
+ * for the current client's privilege.
|
|
+ *
|
|
+ * If the current backend is not associated with a certain
|
|
+ * client process, it switches to permissive mode to avoid
|
|
+ * to prevent any internal processes.
|
|
+ */
|
|
+void
|
|
+sepgsqlAvcInitialize(void)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ /*
|
|
+ * local memory context
|
|
+ */
|
|
+ AvcMemCtx = AllocSetContextCreate(TopMemoryContext,
|
|
+ "SE-PostgreSQL userspace avc",
|
|
+ ALLOCSET_DEFAULT_MINSIZE,
|
|
+ ALLOCSET_DEFAULT_INITSIZE,
|
|
+ ALLOCSET_DEFAULT_MAXSIZE);
|
|
+ sepgsqlShmemInit();
|
|
+
|
|
+ /*
|
|
+ * Switch to local permissive mode
|
|
+ */
|
|
+ if (!MyProcPort)
|
|
+ sepgsqlSetEnforce(0);
|
|
+
|
|
+ /*
|
|
+ * selinux_state->version is never negative value,
|
|
+ * so this call always reset local avc.
|
|
+ */
|
|
+ avc_version = -1;
|
|
+ sepgsqlAvcCheckValid();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlGetEnforce
|
|
+ * sepgsqlSetEnforce
|
|
+ *
|
|
+ * SELinux has two working mode called Enforcing/Permissive.
|
|
+ * In enforcing mode, it checks security policy and actually
|
|
+ * applies its access controls. In permissive mode, it also
|
|
+ * checks security policy, but does not apply any access
|
|
+ * controls. It is used to collect access denied logs to
|
|
+ * debug security policy.
|
|
+ *
|
|
+ * sepgsqlGetEnforce() returns the current working mode, and
|
|
+ * sepgsqlSetEnforce() switches the current working mode
|
|
+ * temporary. When we switches the mode, any errors have to
|
|
+ * be acquired, and it should be restored correctly.
|
|
+ */
|
|
+static int local_enforce = -1; /* undefined */
|
|
+
|
|
+bool
|
|
+sepgsqlGetEnforce(void)
|
|
+{
|
|
+ bool rc;
|
|
+
|
|
+ if (local_enforce < 0)
|
|
+ {
|
|
+ LWLockAcquire(SepgsqlAvcLock, LW_SHARED);
|
|
+ rc = selinux_state->enforcing;
|
|
+ LWLockRelease(SepgsqlAvcLock);
|
|
+
|
|
+ return rc;
|
|
+ }
|
|
+
|
|
+ return (local_enforce > 0 ? true : false);
|
|
+}
|
|
+
|
|
+int
|
|
+sepgsqlSetEnforce(int new_mode)
|
|
+{
|
|
+ int old_mode = local_enforce;
|
|
+
|
|
+ local_enforce = new_mode;
|
|
+
|
|
+ return old_mode;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlAvcAudit
|
|
+ *
|
|
+ * It write out audit message, when auditdeny or auditallow
|
|
+ * matches the required permission bits.
|
|
+ * If external module support sepgsqlAvcAuditHook, it allows
|
|
+ * to write audit logs to external log manager, such as system
|
|
+ * auditd.
|
|
+ */
|
|
+
|
|
+PGDLLIMPORT sepgsqlAvcAuditHook_t sepgsqlAvcAuditHook = NULL;
|
|
+
|
|
+static void
|
|
+sepgsqlAvcAudit(bool denied, char *scontext, char *tcontext,
|
|
+ uint16 tclass, uint32 audited, const char *audit_name)
|
|
+{
|
|
+ StringInfoData buf;
|
|
+ uint32 mask;
|
|
+ const char *tclass_name;
|
|
+
|
|
+ /* translate to human readable form */
|
|
+ scontext = sepgsqlTransSecLabelOut(scontext);
|
|
+ tcontext = sepgsqlTransSecLabelOut(tcontext);
|
|
+
|
|
+ /* permissions in text representation */
|
|
+ initStringInfo(&buf);
|
|
+ appendStringInfo(&buf, "{");
|
|
+ for (mask = 1; audited != 0; mask <<= 1)
|
|
+ {
|
|
+ if (audited & mask)
|
|
+ appendStringInfo(&buf, " %s", sepgsqlGetPermString(tclass, mask));
|
|
+
|
|
+ audited &= ~mask;
|
|
+ }
|
|
+ appendStringInfo(&buf, " }");
|
|
+
|
|
+ tclass_name = sepgsqlGetClassString(tclass);
|
|
+
|
|
+ /* call external audit module, if loaded */
|
|
+ if (sepgsqlAvcAuditHook)
|
|
+ (*sepgsqlAvcAuditHook) (denied, scontext, tcontext,
|
|
+ tclass_name, buf.data, audit_name);
|
|
+ else
|
|
+ {
|
|
+ appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s",
|
|
+ scontext, tcontext, tclass_name);
|
|
+ if (audit_name)
|
|
+ appendStringInfo(&buf, " name=%s", audit_name);
|
|
+
|
|
+ ereport(LOG,
|
|
+ (errcode(ERRCODE_SELINUX_AUDIT),
|
|
+ errmsg("SELinux: %s %s",
|
|
+ denied ? "denied" : "granted", buf.data)));
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlAvcReclaim
|
|
+ *
|
|
+ * It wipes recently unused AVC entries, when the number of entries
|
|
+ * reaches AVC_HASH_NUM_NODES..
|
|
+ */
|
|
+static void
|
|
+sepgsqlAvcReclaim(avc_page *page)
|
|
+{
|
|
+ ListCell *l;
|
|
+ avc_datum *cache;
|
|
+
|
|
+ while (page->avc_count > AVC_HASH_NUM_NODES)
|
|
+ {
|
|
+ foreach (l, page->slot[page->lru_hint])
|
|
+ {
|
|
+ cache = lfirst(l);
|
|
+
|
|
+ if (cache->hot_cache)
|
|
+ cache->hot_cache = false;
|
|
+ else
|
|
+ {
|
|
+ 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;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlAvcMakeEntry
|
|
+ *
|
|
+ * It makes a new AVC entry and insert it on the avc_page.
|
|
+ * If is hold more than AVC_HASH_NUM_NODES entries, recently unused
|
|
+ * avc_datum shall be reclaimed.
|
|
+ */
|
|
+#define avc_hash_key(trelid,tsecid,tclass) \
|
|
+ (hash_uint32((trelid) ^ (tsecid) ^ ((tclass) << 3)))
|
|
+
|
|
+static avc_datum *
|
|
+sepgsqlAvcMakeEntry(avc_page *page, sepgsql_sid_t tsid, uint16 tclass)
|
|
+{
|
|
+ security_context_t scontext, tcontext, ncontext;
|
|
+ security_class_t tclass_ex;
|
|
+ MemoryContext oldctx;
|
|
+ struct av_decision avd;
|
|
+ avc_datum *cache;
|
|
+ uint32 hash_key, index;
|
|
+
|
|
+ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass);
|
|
+ index = hash_key % AVC_HASH_NUM_SLOTS;
|
|
+
|
|
+ scontext = page->scontext;
|
|
+ tcontext = securityRawSecLabelOut(tsid.relid, tsid.secid);
|
|
+
|
|
+ /*
|
|
+ * Compute SELinux permission
|
|
+ */
|
|
+ tclass_ex = sepgsqlTransToExternalClass(tclass);
|
|
+ if (tclass_ex > 0)
|
|
+ {
|
|
+ if (security_compute_av_flags_raw(scontext, tcontext,
|
|
+ tclass_ex, 0, &avd) < 0)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_SELINUX_ERROR),
|
|
+ errmsg("SELinux: unable to compute av_decision: "
|
|
+ "scontext=%s tcontext=%s tclass=%s",
|
|
+ scontext, tcontext,
|
|
+ sepgsqlGetClassString(tclass))));
|
|
+ sepgsqlTransToInternalPerms(tclass, &avd);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* fill it up as undefined class */
|
|
+ avd.allowed = (security_deny_unknown() ? 0 : ~0UL);
|
|
+ avd.decided = ~0UL;
|
|
+ avd.auditallow = 0UL;
|
|
+ avd.auditdeny = ~0UL;
|
|
+ avd.flags = 0;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Compute New security context
|
|
+ */
|
|
+ if (security_compute_create_raw(scontext, tcontext,
|
|
+ tclass_ex, &ncontext) < 0)
|
|
+ {
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_SELINUX_ERROR),
|
|
+ errmsg("SELinux: unable to compute new context: "
|
|
+ "scontext=%s tcontext=%s tclass=%s",
|
|
+ scontext, tcontext, sepgsqlGetClassString(tclass))));
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Copy them to avc_datum
|
|
+ */
|
|
+ oldctx = MemoryContextSwitchTo(AvcMemCtx);
|
|
+ PG_TRY();
|
|
+ {
|
|
+ cache = palloc0(sizeof(avc_datum) + strlen(ncontext));
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(ncontext);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+
|
|
+ cache->hash_key = hash_key;
|
|
+ cache->tclass = tclass;
|
|
+ cache->tsid.relid = tsid.relid;
|
|
+ cache->tsid.secid = tsid.secid;
|
|
+ /* cache->nsid shall be set later */
|
|
+
|
|
+ cache->allowed = avd.allowed;
|
|
+ cache->decided = avd.decided;
|
|
+ cache->auditallow = avd.auditallow;
|
|
+ cache->auditdeny = avd.auditdeny;
|
|
+
|
|
+ cache->hot_cache = true;
|
|
+ if (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE)
|
|
+ cache->permissive = true;
|
|
+ strcpy(cache->ncontext, ncontext);
|
|
+ freecon(ncontext);
|
|
+
|
|
+ sepgsqlAvcReclaim(page);
|
|
+
|
|
+ page->slot[index] = lcons(cache, page->slot[index]);
|
|
+ page->avc_count++;
|
|
+
|
|
+ MemoryContextSwitchTo(oldctx);
|
|
+
|
|
+ return cache;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlAvcLookup
|
|
+ *
|
|
+ * It lookups required AVC entry.
|
|
+ */
|
|
+static avc_datum *
|
|
+sepgsqlAvcLookup(avc_page *page, sepgsql_sid_t tsid, uint16 tclass)
|
|
+{
|
|
+ avc_datum *cache = NULL;
|
|
+ uint32 hash_key, index;
|
|
+ ListCell *l;
|
|
+
|
|
+ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass);
|
|
+ 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->hot_cache = true;
|
|
+ return cache;
|
|
+ }
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlAvcSwitchClientLabel()
|
|
+ *
|
|
+ * It switches the current avc_page.
|
|
+ * An avc_page is a set of cached access control decisions associated
|
|
+ * with a certain privilege of the client. This structure enables to
|
|
+ * lookup required avc_datum without any comparison to the subject
|
|
+ * label.
|
|
+ */
|
|
+void
|
|
+sepgsqlAvcSwitchClient(const char *scontext)
|
|
+{
|
|
+ MemoryContext oldctx;
|
|
+ avc_page *new_page;
|
|
+ int i;
|
|
+
|
|
+ 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, create a new avc_page */
|
|
+ oldctx = MemoryContextSwitchTo(AvcMemCtx);
|
|
+ new_page = palloc0(sizeof(avc_page));
|
|
+ new_page->scontext = pstrdup(scontext);
|
|
+ MemoryContextSwitchTo(oldctx);
|
|
+
|
|
+ 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;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlClientHasPerms
|
|
+ *
|
|
+ * It checks client's privileges on the given object using avc.
|
|
+ */
|
|
+bool
|
|
+sepgsqlClientHasPerms(sepgsql_sid_t tsid,
|
|
+ uint16 tclass, uint32 required,
|
|
+ const char *audit_name, bool abort)
|
|
+{
|
|
+ avc_datum *cache;
|
|
+ uint32 denied, audited;
|
|
+ bool result = true;
|
|
+
|
|
+ Assert(required != 0);
|
|
+
|
|
+ do {
|
|
+ cache = sepgsqlAvcLookup(current_page, tsid, tclass);
|
|
+ if (!cache)
|
|
+ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass);
|
|
+ } while (!sepgsqlAvcCheckValid());
|
|
+
|
|
+ denied = required & ~cache->allowed;
|
|
+ audited = denied ? (denied & cache->auditdeny)
|
|
+ : (required & cache->auditallow);
|
|
+ if (audited)
|
|
+ {
|
|
+ sepgsqlAvcAudit(!!denied,
|
|
+ current_page->scontext,
|
|
+ securityRawSecLabelOut(tsid.relid, tsid.secid),
|
|
+ cache->tclass, audited, audit_name);
|
|
+ }
|
|
+
|
|
+ if (denied)
|
|
+ {
|
|
+ if (!sepgsqlGetEnforce() || cache->permissive)
|
|
+ cache->allowed |= required; /* prevent flood of audit log */
|
|
+ else
|
|
+ {
|
|
+ if (abort)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_SELINUX_ERROR),
|
|
+ errmsg("SELinux: security policy violation")));
|
|
+ result = false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlClientCreateSecid
|
|
+ * sepgsqlClientCreateLabel
|
|
+ */
|
|
+sepgsql_sid_t
|
|
+sepgsqlClientCreateSecid(sepgsql_sid_t tsid, uint16 tclass, Oid nrelid)
|
|
+{
|
|
+ sepgsql_sid_t nsid;
|
|
+ avc_datum *cache;
|
|
+ int index;
|
|
+
|
|
+ do {
|
|
+ cache = sepgsqlAvcLookup(current_page, tsid, tclass);
|
|
+ if (!cache)
|
|
+ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass);
|
|
+
|
|
+ index = (nrelid % AVC_DATUM_NSID_SLOTS);
|
|
+ if (cache->nsid[index].relid != nrelid)
|
|
+ {
|
|
+ cache->nsid[index].secid
|
|
+ = securityRawSecLabelIn(nrelid, cache->ncontext);
|
|
+ cache->nsid[index].relid = nrelid;
|
|
+ }
|
|
+ nsid = cache->nsid[index];
|
|
+ } while (!sepgsqlAvcCheckValid());
|
|
+
|
|
+ return nsid;
|
|
+}
|
|
+
|
|
+security_context_t
|
|
+sepgsqlClientCreateLabel(sepgsql_sid_t tsid, uint16 tclass)
|
|
+{
|
|
+ avc_datum *cache;
|
|
+
|
|
+ do {
|
|
+ cache = sepgsqlAvcLookup(current_page, tsid, tclass);
|
|
+ if (!cache)
|
|
+ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass);
|
|
+ } while (!sepgsqlAvcCheckValid());
|
|
+
|
|
+ return cache->ncontext;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlComputePerms
|
|
+ * sepgsqlComputeCreate
|
|
+ *
|
|
+ * The following two functions make a query to in-kernel SELinux
|
|
+ * without userspace caches, due to some reasons.
|
|
+ * The AVC can cover most of cases, but some of corner cases are
|
|
+ * not suitable for AVC structure, so we need uncached interfaces.
|
|
+ * For example, AVC is unavailable when we tries to load a shared
|
|
+ * library module, because security context of the library does not
|
|
+ * have its security identifier, so we cannot put it on AVC.
|
|
+ */
|
|
+bool
|
|
+sepgsqlComputePerms(char *scontext, char *tcontext,
|
|
+ uint16 tclass_in, uint32 required,
|
|
+ const char *audit_name, bool abort)
|
|
+{
|
|
+ access_vector_t denied, audited;
|
|
+ security_class_t tclass_ex;
|
|
+ struct av_decision avd;
|
|
+
|
|
+ Assert(required != 0);
|
|
+
|
|
+ tclass_ex = sepgsqlTransToExternalClass(tclass_in);
|
|
+ if (tclass_ex > 0)
|
|
+ {
|
|
+ /*
|
|
+ * security_compute_av_flags_raw() is a SELinux's API that
|
|
+ * returns its access control decision based on the security
|
|
+ * policy, to the given combination of user's privilege
|
|
+ * (scontext; security label of the client process),
|
|
+ * target's attribute (tcontext; security label of the
|
|
+ * object) and type of actions (tclass; object classes).
|
|
+ *
|
|
+ * The returned avd.allowed is a bitmap of allowed actions.
|
|
+ */
|
|
+ if (security_compute_av_flags_raw(scontext, tcontext,
|
|
+ tclass_ex, 0, &avd) < 0)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_SELINUX_ERROR),
|
|
+ errmsg("SELinux: could not compute av_decision: "
|
|
+ "scontext=%s tcontext=%s tclass=%s",
|
|
+ scontext, tcontext,
|
|
+ sepgsqlGetClassString(tclass_in))));
|
|
+ sepgsqlTransToInternalPerms(tclass_in, &avd);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /*
|
|
+ * If security policy does not support database related
|
|
+ * permissions, it fulls up permission bits by dummy
|
|
+ * data.
|
|
+ * If security_deny_unknown() returns positive value,
|
|
+ * undefined permissions should not be allowed.
|
|
+ * Otherwise, it shall be allowed.
|
|
+ */
|
|
+ avd.allowed = (security_deny_unknown() > 0 ? 0 : ~0UL);
|
|
+ avd.decided = ~0UL;
|
|
+ avd.auditallow = 0UL;
|
|
+ avd.auditdeny = ~0UL;
|
|
+ avd.flags = 0;
|
|
+ }
|
|
+
|
|
+ denied = required & ~avd.allowed;
|
|
+ audited = denied ? (denied & avd.auditdeny)
|
|
+ : (required & avd.auditallow);
|
|
+ if (audited)
|
|
+ {
|
|
+ /*
|
|
+ * If security policy requires to generate an audit log
|
|
+ * record for the given request, it should be logged.
|
|
+ */
|
|
+ sepgsqlAvcAudit(!!denied, scontext, tcontext,
|
|
+ tclass_in, audited, audit_name);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * If any required permissions are not allowed, and
|
|
+ * SE-PgSQL performs in enforcing mode, and the given
|
|
+ * combination of subject, object and action does not
|
|
+ * have special flag to be handled as permission,
|
|
+ * SE-PgSQL returns false or raises an error.
|
|
+ * Otherwise, it returns true that means required
|
|
+ * actions are allowed.
|
|
+ */
|
|
+ if (!denied || /* no policy violation */
|
|
+ !sepgsqlGetEnforce() || /* permissive mode */
|
|
+ (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) != 0) /* permissive domain */
|
|
+ return true;
|
|
+
|
|
+ if (abort)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_SELINUX_ERROR),
|
|
+ errmsg("SELinux: security policy violation")));
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+char *
|
|
+sepgsqlComputeCreate(char *scontext, char *tcontext, uint16 tclass_in)
|
|
+{
|
|
+ security_context_t ncontext, result;
|
|
+ security_class_t tclass_ex;
|
|
+
|
|
+ tclass_ex = sepgsqlTransToExternalClass(tclass_in);
|
|
+ /*
|
|
+ * security_compute_create_raw() is a SELinux's API that
|
|
+ * returns a default security context to be assigned on
|
|
+ * a new object (categorized by object class) when a client
|
|
+ * labeled as scontext tries to create a new one under the
|
|
+ * parent object labeled as tcontext.
|
|
+ */
|
|
+ if (security_compute_create_raw(scontext, tcontext,
|
|
+ tclass_ex, &ncontext) < 0)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_SELINUX_ERROR),
|
|
+ errmsg("SELinux: could not compute a new context "
|
|
+ "scontext=%s tcontext=%s tclass=%s",
|
|
+ scontext, tcontext, sepgsqlGetClassString(tclass_in))));
|
|
+ PG_TRY();
|
|
+ {
|
|
+ result = pstrdup(ncontext);
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(ncontext);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+ freecon(ncontext);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * SELinux state monitoring process
|
|
+ *
|
|
+ * This process is forked from postmaster to monitor the state of SELinux.
|
|
+ * SELinux can make a notifier message to userspace object manager via
|
|
+ * netlink socket. When it receives the message, it updates selinux_state
|
|
+ * structure assigned on shared memory region to make any instance reset
|
|
+ * its AVC soon.
|
|
+ */
|
|
+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,
|
|
+ (errcode(ERRCODE_SELINUX_INFO),
|
|
+ errmsg("%s", buffer)));
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+sepgsql_cb_setenforce(int enforce)
|
|
+{
|
|
+ /* switch enforcing/permissive */
|
|
+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE);
|
|
+ selinux_state->enforcing = (enforce ? true : false);
|
|
+ selinux_state->version++;
|
|
+ LWLockRelease(SepgsqlAvcLock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+sepgsql_cb_policyload(int seqno)
|
|
+{
|
|
+ /* invalidate local avc */
|
|
+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE);
|
|
+ selinux_state->version++;
|
|
+ LWLockRelease(SepgsqlAvcLock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsqlReceiverMain(void)
|
|
+{
|
|
+ union selinux_callback cb;
|
|
+
|
|
+ Assert(sepgsqlIsEnabled());
|
|
+
|
|
+#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
|
|
+ */
|
|
+ sepgsqlShmemInit();
|
|
+
|
|
+ ereport(LOG,
|
|
+ (errcode(ERRCODE_SELINUX_INFO),
|
|
+ errmsg("SELinux: security policy monitor (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/security/sepgsql/bridge.c b/src/backend/security/sepgsql/bridge.c
|
|
new file mode 100644
|
|
index 0000000..3a3630c
|
|
--- /dev/null
|
|
+++ b/src/backend/security/sepgsql/bridge.c
|
|
@@ -0,0 +1,2920 @@
|
|
+/*
|
|
+ * src/backend/security/sepgsql/bridge.c
|
|
+ *
|
|
+ * New style security hooks for SE-PostgreSQL
|
|
+ *
|
|
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
|
+ * Portions Copyright (c) 1994, Regents of the University of California
|
|
+ */
|
|
+#include "postgres.h"
|
|
+
|
|
+#include "access/sysattr.h"
|
|
+#include "catalog/heap.h"
|
|
+#include "catalog/indexing.h"
|
|
+#include "catalog/pg_authid.h"
|
|
+#include "catalog/pg_cast.h"
|
|
+#include "catalog/pg_conversion.h"
|
|
+#include "catalog/pg_database.h"
|
|
+#include "catalog/pg_foreign_data_wrapper.h"
|
|
+#include "catalog/pg_foreign_server.h"
|
|
+#include "catalog/pg_language.h"
|
|
+#include "catalog/pg_largeobject_metadata.h"
|
|
+#include "catalog/pg_namespace.h"
|
|
+#include "catalog/pg_operator.h"
|
|
+#include "catalog/pg_opclass.h"
|
|
+#include "catalog/pg_opfamily.h"
|
|
+#include "catalog/pg_proc.h"
|
|
+#include "catalog/pg_rewrite.h"
|
|
+#include "catalog/pg_security.h"
|
|
+#include "catalog/pg_tablespace.h"
|
|
+#include "catalog/pg_ts_parser.h"
|
|
+#include "catalog/pg_ts_dict.h"
|
|
+#include "catalog/pg_ts_template.h"
|
|
+#include "catalog/pg_ts_config.h"
|
|
+#include "catalog/pg_type.h"
|
|
+#include "catalog/pg_user_mapping.h"
|
|
+#include "commands/dbcommands.h"
|
|
+#include "miscadmin.h"
|
|
+#include "security/sepgsql.h"
|
|
+#include "utils/builtins.h"
|
|
+#include "utils/fmgroids.h"
|
|
+#include "utils/lsyscache.h"
|
|
+#include "utils/syscache.h"
|
|
+#include "utils/tqual.h"
|
|
+
|
|
+#include <libgen.h>
|
|
+#include <sys/stat.h>
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ * Common Helper Routines
|
|
+ * ------------------------------------------------------------ */
|
|
+static bool sepgsql_database_common(Oid datOid, uint32 required, bool abort);
|
|
+static bool sepgsql_schema_common(Oid nspOid, uint32 required, bool abort);
|
|
+static bool sepgsql_attribute_common(Oid relOid, AttrNumber attnum,
|
|
+ uint32 required, bool abort);
|
|
+static bool sepgsql_relation_common(Oid relOid, uint32 required, bool abort);
|
|
+static bool sepgsql_proc_common(Oid procOid, uint32 required, bool abort);
|
|
+static bool sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort);
|
|
+static bool sepgsql_foreign_server_common(Oid fsrvOid, uint32 required, bool abort);
|
|
+static bool sepgsql_language_common(Oid langOid, uint32 required, bool abort);
|
|
+static bool sepgsql_operator_common(Oid oprOid, uint32 required, bool abort);
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_database related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+static bool
|
|
+sepgsql_database_common(Oid datOid, uint32 required, bool abort)
|
|
+{
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+ const char *auname;
|
|
+ bool rc;
|
|
+
|
|
+ tuple = SearchSysCache(DATABASEOID,
|
|
+ ObjectIdGetDatum(datOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for database: %u", datOid);
|
|
+
|
|
+ auname = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(DatabaseRelationId, tuple, &tclass);
|
|
+
|
|
+ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+Oid
|
|
+sepgsql_database_create(const char *datName, Oid srcDatOid, DefElem *newLabel)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ if (!newLabel)
|
|
+ sid = sepgsqlGetDefaultDatabaseSecid(srcDatOid);
|
|
+ else
|
|
+ {
|
|
+ sid.relid = DatabaseRelationId;
|
|
+ sid.secid = securityTransSecLabelIn(sid.relid,
|
|
+ strVal(newLabel->arg));
|
|
+ }
|
|
+
|
|
+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_DATABASE,
|
|
+ SEPG_DB_DATABASE__CREATE,
|
|
+ datName, true);
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_database_alter(Oid datOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_database_common(datOid, SEPG_DB_DATABASE__SETATTR, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_database_drop(Oid datOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_database_common(datOid, SEPG_DB_DATABASE__DROP, true);
|
|
+}
|
|
+
|
|
+Oid
|
|
+sepgsql_database_relabel(Oid datOid, DefElem *newLabel)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ {
|
|
+ if (newLabel)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("SELinux is disabled now")));
|
|
+
|
|
+ return InvalidOid;
|
|
+ }
|
|
+ sid.relid = DatabaseRelationId;
|
|
+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg));
|
|
+
|
|
+ /* db_database:{setattr relabelfrom} to older seclabel */
|
|
+ sepgsql_database_common(datOid,
|
|
+ SEPG_DB_DATABASE__SETATTR |
|
|
+ SEPG_DB_DATABASE__RELABELFROM, true);
|
|
+
|
|
+ /* db_database:{relabelto} to newer seclabel */
|
|
+ sepgsqlClientHasPerms(sid,
|
|
+ SEPG_CLASS_DB_DATABASE,
|
|
+ SEPG_DB_DATABASE__RELABELTO,
|
|
+ get_database_name(datOid), true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_database_grant(Oid datOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_database_common(datOid, SEPG_DB_DATABASE__SETATTR, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_database_access(Oid datOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_database_common(datOid, SEPG_DB_DATABASE__ACCESS, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_database_load_module(Oid datOid, const char *filename)
|
|
+{
|
|
+ HeapTuple tuple;
|
|
+ security_context_t filecon;
|
|
+ security_context_t datcon;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+ /*
|
|
+ * It assumes preloaded libraries are secure,
|
|
+ * because it can be set up using guc variable
|
|
+ * not any SQL statements.
|
|
+ */
|
|
+ if (GetProcessingMode() == InitProcessing)
|
|
+ return;
|
|
+
|
|
+ /* Get database context */
|
|
+ tuple = SearchSysCache(DATABASEOID,
|
|
+ ObjectIdGetDatum(datOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for database: %u", datOid);
|
|
+
|
|
+ datcon = securityRawSecLabelOut(DatabaseRelationId,
|
|
+ HeapTupleGetSecid(tuple));
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ /* Get library context */
|
|
+ if (getfilecon_raw(filename, &filecon) < 0)
|
|
+ ereport(ERROR,
|
|
+ (errcode_for_file_access(),
|
|
+ errmsg("could not access file \"%s\": %m", filename)));
|
|
+ PG_TRY();
|
|
+ {
|
|
+ sepgsqlComputePerms(datcon,
|
|
+ filecon,
|
|
+ SEPG_CLASS_DB_DATABASE,
|
|
+ SEPG_DB_DATABASE__LOAD_MODULE,
|
|
+ filename, true);
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(filecon);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+ freecon(filecon);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_namespace related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+static bool
|
|
+sepgsql_schema_common(Oid nspOid, uint32 required, bool abort)
|
|
+{
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+ const char *auname;
|
|
+ bool rc;
|
|
+
|
|
+ tuple = SearchSysCache(NAMESPACEOID,
|
|
+ ObjectIdGetDatum(nspOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for namespace: %u", nspOid);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(NamespaceRelationId, tuple, &tclass);
|
|
+
|
|
+ auname = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname);
|
|
+
|
|
+ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+Oid
|
|
+sepgsql_schema_create(const char *nspName, bool isTemp, DefElem *newLabel)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ if (!newLabel)
|
|
+ sid = sepgsqlGetDefaultSchemaSecid(MyDatabaseId);
|
|
+ else
|
|
+ {
|
|
+ sid.relid = NamespaceRelationId;
|
|
+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg));
|
|
+ }
|
|
+
|
|
+ sepgsqlClientHasPerms(sid,
|
|
+ SEPG_CLASS_DB_SCHEMA,
|
|
+ SEPG_DB_SCHEMA__CREATE,
|
|
+ nspName, true);
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_schema_alter(Oid nspOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SETATTR, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_schema_drop(Oid nspOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__DROP, true);
|
|
+}
|
|
+
|
|
+Oid
|
|
+sepgsql_schema_relabel(Oid nspOid, DefElem *newLabel)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ {
|
|
+ if (newLabel)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("SELinux is disabled now")));
|
|
+ return InvalidOid;
|
|
+ }
|
|
+ sid.relid = NamespaceRelationId;
|
|
+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg));
|
|
+
|
|
+ /* db_schema:{setattr relabelfrom} for older seclabel */
|
|
+ sepgsql_schema_common(nspOid,
|
|
+ SEPG_DB_SCHEMA__SETATTR |
|
|
+ SEPG_DB_SCHEMA__RELABELFROM, true);
|
|
+
|
|
+ /* db_schema:{relabelto} for newer seclabel */
|
|
+ sepgsqlClientHasPerms(sid,
|
|
+ SEPG_CLASS_DB_SCHEMA,
|
|
+ SEPG_DB_SCHEMA__RELABELTO,
|
|
+ get_namespace_name(nspOid), true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_schema_grant(Oid nspOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SETATTR, true);
|
|
+}
|
|
+
|
|
+bool
|
|
+sepgsql_schema_search(Oid nspOid, bool abort)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return true;
|
|
+
|
|
+ return sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SEARCH, abort);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_attribute related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+static bool
|
|
+sepgsql_attribute_common(Oid relOid, AttrNumber attnum,
|
|
+ uint32 required, bool abort)
|
|
+{
|
|
+ Form_pg_attribute attForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+ char auname[NAMEDATALEN * 2 + 3];
|
|
+ bool rc = true;
|
|
+
|
|
+ /* Caller prevent case when relkind != RELKIND_RELATION */
|
|
+ Assert(get_rel_relkind(relOid) == RELKIND_RELATION);
|
|
+
|
|
+ tuple = SearchSysCache(ATTNUM,
|
|
+ ObjectIdGetDatum(relOid),
|
|
+ Int16GetDatum(attnum),
|
|
+ 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
|
|
+ attnum, relOid);
|
|
+ attForm = (Form_pg_attribute) GETSTRUCT(tuple);
|
|
+
|
|
+ /*
|
|
+ * NOTE: when a table to be dropped, corresponding attributes
|
|
+ * are also removed. Some of them can be already logically
|
|
+ * dropped using ALTER TABLE ... DROP statement.
|
|
+ * In this case, SE-PostgreSQL does not check anything.
|
|
+ * If any other situation touches dropped column, it is a bug.
|
|
+ */
|
|
+ if (attForm->attisdropped)
|
|
+ goto skip;
|
|
+
|
|
+ sprintf(auname, "%s.%s", get_rel_name(relOid), NameStr(attForm->attname));
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(AttributeRelationId, tuple, &tclass);
|
|
+
|
|
+ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort);
|
|
+
|
|
+skip:
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+Oid
|
|
+sepgsql_attribute_create(Oid relOid, ColumnDef *cdef)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+ char relkind;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ {
|
|
+ if (cdef->secLabel)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("SELinux is disabled now")));
|
|
+ return InvalidOid;
|
|
+ }
|
|
+
|
|
+ relkind = get_rel_relkind(relOid);
|
|
+ if (relkind == RELKIND_RELATION)
|
|
+ {
|
|
+ char auname[NAMEDATALEN * 2 + 3];
|
|
+
|
|
+ if (!cdef->secLabel)
|
|
+ sid = sepgsqlGetDefaultColumnSecid(relOid);
|
|
+ else
|
|
+ {
|
|
+ char *label = strVal(((DefElem *)cdef->secLabel)->arg);
|
|
+
|
|
+ sid.relid = AttributeRelationId;
|
|
+ sid.secid = securityTransSecLabelIn(sid.relid, label);
|
|
+ }
|
|
+
|
|
+ sprintf(auname, "%s.%s", get_rel_name(relOid), cdef->colname);
|
|
+ sepgsqlClientHasPerms(sid,
|
|
+ SEPG_CLASS_DB_COLUMN,
|
|
+ SEPG_DB_COLUMN__CREATE,
|
|
+ auname, true);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* no need to check for toast relation */
|
|
+ if (relkind != RELKIND_TOASTVALUE)
|
|
+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true);
|
|
+ return InvalidOid;
|
|
+ }
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_attribute_alter(Oid relOid, const char *attname)
|
|
+{
|
|
+ AttrNumber attno;
|
|
+ char relkind;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ /*
|
|
+ * If the target attribute does not exist, an error
|
|
+ * shall be raised later.
|
|
+ */
|
|
+ attno = get_attnum(relOid, attname);
|
|
+ if (attno == InvalidAttrNumber)
|
|
+ return;
|
|
+
|
|
+ relkind = get_rel_relkind(relOid);
|
|
+ if (relkind == RELKIND_RELATION)
|
|
+ {
|
|
+ sepgsql_attribute_common(relOid, attno, SEPG_DB_COLUMN__SETATTR, true);
|
|
+ }
|
|
+ else if (relkind != RELKIND_TOASTVALUE)
|
|
+ {
|
|
+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true);
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_attribute_drop(Oid relOid, AttrNumber attnum)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ /*
|
|
+ * We only need to check db_column:{drop} when relkind equals
|
|
+ * RELKIND_RELATION, because db_xxx:{drop} permission is already
|
|
+ * checked in other cases. (e.g DROP SEQUENCE, ...)
|
|
+ */
|
|
+ if (get_rel_relkind(relOid) == RELKIND_RELATION)
|
|
+ sepgsql_attribute_common(relOid, attnum,
|
|
+ SEPG_DB_COLUMN__DROP, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_attribute_grant(Oid relOid, AttrNumber attnum)
|
|
+{
|
|
+ char relkind;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ relkind = get_rel_relkind(relOid);
|
|
+ if (relkind == RELKIND_RELATION)
|
|
+ {
|
|
+ sepgsql_attribute_common(relOid, attnum, SEPG_DB_COLUMN__SETATTR, true);
|
|
+ }
|
|
+ else if (relkind != RELKIND_TOASTVALUE)
|
|
+ {
|
|
+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true);
|
|
+ }
|
|
+}
|
|
+
|
|
+Oid
|
|
+sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, DefElem *newLabel)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+ char auname[NAMEDATALEN * 2 + 3];
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ {
|
|
+ if (!newLabel)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("SELinux is disabled now")));
|
|
+ return InvalidOid;
|
|
+ }
|
|
+
|
|
+ Assert(get_rel_relkind(relOid) == RELKIND_RELATION);
|
|
+
|
|
+ sid.relid = AttributeRelationId;
|
|
+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg));
|
|
+
|
|
+ /* db_column:{setattr relabelfrom} */
|
|
+ sepgsql_attribute_common(relOid, attnum,
|
|
+ SEPG_DB_COLUMN__SETATTR |
|
|
+ SEPG_DB_COLUMN__RELABELFROM, true);
|
|
+
|
|
+ /* db_column:{relabelto} */
|
|
+ sprintf(auname, "%s.%s",
|
|
+ get_rel_name(relOid),
|
|
+ get_attname(relOid, attnum));
|
|
+ sepgsqlClientHasPerms(sid,
|
|
+ SEPG_CLASS_DB_COLUMN,
|
|
+ SEPG_DB_COLUMN__RELABELTO,
|
|
+ auname, true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_class related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+static bool
|
|
+sepgsql_relation_common(Oid relOid, uint32 required, bool abort)
|
|
+{
|
|
+ Form_pg_class relForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+ bool rc;
|
|
+
|
|
+ tuple = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(relOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for relation %u", relOid);
|
|
+ relForm = (Form_pg_class) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(RelationRelationId, tuple, &tclass);
|
|
+ rc = sepgsqlClientHasPerms(sid, tclass, required,
|
|
+ NameStr(relForm->relname), abort);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsql_relation_create
|
|
+ * It returns an array of security identifier for the new table
|
|
+ * and columns to be assigned. The corresponding security labels
|
|
+ * are already checked for db_table/db_sequence/db_column:{create}
|
|
+ * permission.
|
|
+ * In the default labeling rule, a column inherits the security
|
|
+ * label of its table, but we cannot refer it using system caches,
|
|
+ * because the command counter is not incremented under the
|
|
+ * heap_create_with_catalog(). Thus, we need to compute and check
|
|
+ * them prior to the actual creation of table and columns.
|
|
+ */
|
|
+Oid *
|
|
+sepgsql_relation_create(const char *relName,
|
|
+ char relkind,
|
|
+ TupleDesc tupDesc,
|
|
+ Oid nspOid,
|
|
+ DefElem *relLabel,
|
|
+ List *colList,
|
|
+ bool createAs,
|
|
+ bool permission)
|
|
+{
|
|
+ Oid *secLabels;
|
|
+ sepgsql_sid_t relsid;
|
|
+ uint16 tclass;
|
|
+ uint32 required;
|
|
+ int index;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return NULL;
|
|
+
|
|
+ switch (relkind)
|
|
+ {
|
|
+ case RELKIND_RELATION:
|
|
+ if (!relLabel)
|
|
+ relsid = sepgsqlGetDefaultTableSecid(nspOid);
|
|
+ else
|
|
+ {
|
|
+ relsid.relid = RelationRelationId;
|
|
+ relsid.secid = securityTransSecLabelIn(relsid.relid,
|
|
+ strVal(relLabel->arg));
|
|
+ }
|
|
+ tclass = SEPG_CLASS_DB_TABLE;
|
|
+ required = SEPG_DB_TABLE__CREATE;
|
|
+ if (createAs)
|
|
+ required |= SEPG_DB_TABLE__INSERT;
|
|
+ break;
|
|
+
|
|
+ case RELKIND_SEQUENCE:
|
|
+ if (!relLabel)
|
|
+ relsid = sepgsqlGetDefaultSequenceSecid(nspOid);
|
|
+ else
|
|
+ {
|
|
+ relsid.relid = RelationRelationId;
|
|
+ relsid.secid = securityTransSecLabelIn(relsid.relid,
|
|
+ strVal(relLabel->arg));
|
|
+ }
|
|
+ tclass = SEPG_CLASS_DB_SEQUENCE;
|
|
+ required = SEPG_DB_SEQUENCE__CREATE;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ if (!relLabel)
|
|
+ relsid = sepgsqlGetDefaultTupleSecid(RelationRelationId);
|
|
+ else
|
|
+ {
|
|
+ /* should not be happen */
|
|
+ relsid.relid = RelationRelationId;
|
|
+ relsid.secid = securityTransSecLabelIn(relsid.relid,
|
|
+ strVal(relLabel->arg));
|
|
+ }
|
|
+ tclass = SEPG_CLASS_DB_TUPLE;
|
|
+ required = SEPG_DB_TUPLE__INSERT;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * The secLabeld array stores security identifiers to be assigned
|
|
+ * on the new table and columns.
|
|
+ *
|
|
+ * secLabels[0] is security identifier of the table.
|
|
+ * secLabels[attnum - FirstLowInvalidHeapAttributeNumber]
|
|
+ * is security identifier of columns (if necessary).
|
|
+ */
|
|
+ secLabels = palloc0(sizeof(Oid) * (tupDesc->natts
|
|
+ - FirstLowInvalidHeapAttributeNumber));
|
|
+
|
|
+ /* relation's security identifier to be assigned on */
|
|
+ secLabels[0] = relsid.secid;
|
|
+
|
|
+ /*
|
|
+ * Note that this hook can be called during initdb processes.
|
|
+ * It is an exception of access controls, so we skip any checks.
|
|
+ *
|
|
+ * And, we don't need any checks for toast relations, because
|
|
+ * it is a quite internal stuff.
|
|
+ */
|
|
+ if (permission)
|
|
+ {
|
|
+ /* db_schema:{add_name} */
|
|
+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true);
|
|
+
|
|
+ /* db_table:{create}, db_sequence:{create} or db_tuple:{insert} */
|
|
+ sepgsqlClientHasPerms(relsid, tclass, required, relName, true);
|
|
+ }
|
|
+
|
|
+ /* no individual security context expect for RELKIND_RELATION */
|
|
+ if (relkind != RELKIND_RELATION)
|
|
+ return secLabels;
|
|
+
|
|
+ /*
|
|
+ * db_column:{create} permission
|
|
+ */
|
|
+ for (index = FirstLowInvalidHeapAttributeNumber + 1;
|
|
+ index < tupDesc->natts;
|
|
+ index++)
|
|
+ {
|
|
+ Form_pg_attribute attr;
|
|
+ sepgsql_sid_t attsid = { InvalidOid, InvalidOid };
|
|
+ char attname[NAMEDATALEN * 2 + 3];
|
|
+ ListCell *l;
|
|
+
|
|
+ /* skip unnecessary attributes */
|
|
+ if (index == ObjectIdAttributeNumber && !tupDesc->tdhasoid)
|
|
+ continue;
|
|
+
|
|
+ if (index < 0)
|
|
+ attr = SystemAttributeDefinition(index, tupDesc->tdhasoid);
|
|
+ else
|
|
+ attr = tupDesc->attrs[index];
|
|
+
|
|
+ /* Is there any given security context? */
|
|
+ foreach (l, colList)
|
|
+ {
|
|
+ ColumnDef *cdef = lfirst(l);
|
|
+
|
|
+ if (cdef->secLabel &&
|
|
+ strcmp(cdef->colname, NameStr(attr->attname)) == 0)
|
|
+ {
|
|
+ attsid.relid = AttributeRelationId;
|
|
+ attsid.secid = securityTransSecLabelIn(attsid.relid,
|
|
+ strVal(((DefElem *)cdef->secLabel)->arg));
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* default security context, if not given */
|
|
+ if (!SidIsValid(attsid))
|
|
+ attsid = sepgsqlClientCreateSecid(relsid,
|
|
+ SEPG_CLASS_DB_COLUMN,
|
|
+ AttributeRelationId);
|
|
+ if (permission)
|
|
+ {
|
|
+ required = SEPG_DB_COLUMN__CREATE;
|
|
+
|
|
+ if (createAs)
|
|
+ required |= SEPG_DB_COLUMN__INSERT;
|
|
+
|
|
+ /* db_column:{create (insert)} */
|
|
+ sprintf(attname, "%s.%s", relName, NameStr(attr->attname));
|
|
+ sepgsqlClientHasPerms(attsid,
|
|
+ SEPG_CLASS_DB_COLUMN,
|
|
+ required, attname, true);
|
|
+ }
|
|
+ /* column's security identifier to be assigend on */
|
|
+ secLabels[index - FirstLowInvalidHeapAttributeNumber] = attsid.secid;
|
|
+ }
|
|
+
|
|
+ return secLabels;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsql_relation_copy
|
|
+ * It returns an array of security identifier of table and columns
|
|
+ * to be copied on make_new_heap(). It actually create a new temporary
|
|
+ * relation and insert all the tuples within original one into the
|
|
+ * temporary one, but swap_relation_files() swaps their file nodes.
|
|
+ * Thus, there are no changes from the viewpoint of users.
|
|
+ * SE-PostgreSQL also does not check and change anything. It simply
|
|
+ * copies security identifier of the source relation to the destination
|
|
+ * relation.
|
|
+ */
|
|
+Oid *
|
|
+sepgsql_relation_copy(Relation src)
|
|
+{
|
|
+ Oid *secLabels;
|
|
+ HeapTuple tuple;
|
|
+ Oid relOid = RelationGetRelid(src);
|
|
+ int index;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return NULL;
|
|
+
|
|
+ /* see the comment at sepgsqlCreateTableColumn*/
|
|
+ secLabels = palloc0(sizeof(Oid) * (RelationGetDescr(src)->natts
|
|
+ - FirstLowInvalidHeapAttributeNumber));
|
|
+
|
|
+ /* copy table's security identifier */
|
|
+ tuple = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(relOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for relation \"%s\"",
|
|
+ RelationGetRelationName(src));
|
|
+
|
|
+ secLabels[0] = HeapTupleGetSecid(tuple);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ /* copy column's security identifier */
|
|
+ for (index = FirstLowInvalidHeapAttributeNumber + 1;
|
|
+ index < RelationGetDescr(src)->natts;
|
|
+ index++)
|
|
+ {
|
|
+ Form_pg_attribute attr;
|
|
+
|
|
+ if (index < 0)
|
|
+ attr = SystemAttributeDefinition(index, true);
|
|
+ else
|
|
+ attr = RelationGetDescr(src)->attrs[index];
|
|
+
|
|
+ tuple = SearchSysCache(ATTNUM,
|
|
+ ObjectIdGetDatum(relOid),
|
|
+ Int16GetDatum(attr->attnum),
|
|
+ 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ continue;
|
|
+
|
|
+ secLabels[index - FirstLowInvalidHeapAttributeNumber]
|
|
+ = HeapTupleGetSecid(tuple);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+ }
|
|
+
|
|
+ return secLabels;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_relation_alter(Oid relOid, const char *newName, Oid newNsp)
|
|
+{
|
|
+ Form_pg_class relForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ tuple = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(relOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for relation %u", relOid);
|
|
+ relForm = (Form_pg_class) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(RelationRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TABLE__SETATTR,
|
|
+ NameStr(relForm->relname), true);
|
|
+
|
|
+ /* db_schema:{add_name remove_name}, if necessary */
|
|
+ if (newName || OidIsValid(newNsp))
|
|
+ {
|
|
+ if (!OidIsValid(newNsp))
|
|
+ sepgsql_schema_common(relForm->relnamespace,
|
|
+ SEPG_DB_SCHEMA__ADD_NAME |
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+ else
|
|
+ {
|
|
+ sepgsql_schema_common(relForm->relnamespace,
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+ sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true);
|
|
+ }
|
|
+ }
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_relation_drop(Oid relOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ if (get_rel_relkind(relOid) == RELKIND_TOASTVALUE)
|
|
+ return;
|
|
+
|
|
+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__DROP, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_relation_grant(Oid relOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true);
|
|
+}
|
|
+
|
|
+Oid
|
|
+sepgsql_relation_relabel(Oid relOid, DefElem *newLabel)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+ char relkind;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ {
|
|
+ if (newLabel)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("SELinux is disabled now")));
|
|
+ return InvalidOid;
|
|
+ }
|
|
+
|
|
+ relkind = get_rel_relkind(relOid);
|
|
+ if (relkind != RELKIND_RELATION && relkind != RELKIND_SEQUENCE)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("Unable to set security label on \"%s\"",
|
|
+ get_rel_name(relOid))));
|
|
+
|
|
+ /* input security context */
|
|
+ sid.relid = RelationRelationId;
|
|
+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg));
|
|
+
|
|
+ /* db_table/db_sequence:{setattr relabelfrom} */
|
|
+ sepgsql_relation_common(relOid,
|
|
+ SEPG_DB_TABLE__SETATTR |
|
|
+ SEPG_DB_TABLE__RELABELFROM, true);
|
|
+
|
|
+ /* db_table/db_sequence:{relabelto} */
|
|
+ sepgsqlClientHasPerms(sid,
|
|
+ (relkind == RELKIND_RELATION
|
|
+ ? SEPG_CLASS_DB_TABLE
|
|
+ : SEPG_CLASS_DB_SEQUENCE),
|
|
+ SEPG_DB_TABLE__RELABELTO,
|
|
+ get_rel_name(relOid), true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_relation_get_transaction_id(Oid relOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__GETATTR, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_relation_copy_definition(Oid relOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__GETATTR, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_relation_truncate(Relation rel)
|
|
+{
|
|
+ HeapScanDesc scan;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION);
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ /* check db_table:{delete} permission */
|
|
+ sepgsql_relation_common(RelationGetRelid(rel),
|
|
+ SEPG_DB_TABLE__DELETE, true);
|
|
+
|
|
+ /* row-level access control is enabled? */
|
|
+ if (!sepostgresql_row_level)
|
|
+ return;
|
|
+
|
|
+ /* check db_tuple:{delete} permission */
|
|
+ scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
|
|
+
|
|
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
|
+ {
|
|
+ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__DELETE,
|
|
+ NULL, true);
|
|
+ }
|
|
+ heap_endscan(scan);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_relation_lock(Oid relOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ if (get_rel_relkind(relOid) != RELKIND_RELATION)
|
|
+ return;
|
|
+
|
|
+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__LOCK, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_view_replace(Oid viewOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ Assert(get_rel_relkind(viewOid) == RELKIND_VIEW);
|
|
+
|
|
+ sepgsql_relation_common(viewOid, SEPG_DB_TABLE__SETATTR, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_index_create(Oid relOid, Oid nspOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ /* db_table:{setattr} */
|
|
+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true);
|
|
+
|
|
+ /* db_schema:{add_name} */
|
|
+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_sequence_get_value(Oid seqOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE);
|
|
+
|
|
+ sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__GET_VALUE, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_sequence_next_value(Oid seqOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE);
|
|
+
|
|
+ sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__NEXT_VALUE, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_sequence_set_value(Oid seqOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE);
|
|
+
|
|
+ sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__SET_VALUE, true);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_proc related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+static bool
|
|
+sepgsql_proc_common(Oid procOid, uint32 required, bool abort)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+ HeapTuple tuple;
|
|
+ uint16 tclass;
|
|
+ const char *auname;
|
|
+ bool rc;
|
|
+
|
|
+ tuple = SearchSysCache(PROCOID,
|
|
+ ObjectIdGetDatum(procOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for procedure: %u", procOid);
|
|
+
|
|
+ auname = NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname);
|
|
+ sid = sepgsqlGetTupleSecid(ProcedureRelationId, tuple, &tclass);
|
|
+
|
|
+ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+Oid
|
|
+sepgsql_proc_create(const char *procName, HeapTuple oldTup,
|
|
+ Oid nspOid, Oid langOid, DefElem *newLabel)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+ //HeapTuple tuple;
|
|
+ uint32 required;
|
|
+ //bool trusted;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ if (!HeapTupleIsValid(oldTup))
|
|
+ {
|
|
+ /* create a new function */
|
|
+ required = SEPG_DB_PROCEDURE__CREATE;
|
|
+ if (!newLabel)
|
|
+ sid = sepgsqlGetDefaultProcedureSecid(nspOid);
|
|
+ else
|
|
+ {
|
|
+ sid.relid = ProcedureRelationId;
|
|
+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg));
|
|
+ }
|
|
+ }
|
|
+ else if (!newLabel)
|
|
+ {
|
|
+ /* replace an existing function, without any label */
|
|
+ required = SEPG_DB_PROCEDURE__SETATTR;
|
|
+ sid = sepgsqlGetTupleSecid(ProcedureRelationId, oldTup, NULL);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* replace an existing function, with relabeling */
|
|
+ sepgsql_proc_common(HeapTupleGetOid(oldTup),
|
|
+ SEPG_DB_PROCEDURE__SETATTR |
|
|
+ SEPG_DB_PROCEDURE__RELABELFROM, true);
|
|
+
|
|
+ required = SEPG_DB_PROCEDURE__RELABELTO;
|
|
+ sid = sepgsqlGetTupleSecid(ProcedureRelationId, oldTup, NULL);
|
|
+ }
|
|
+
|
|
+#if 0
|
|
+ /* Procedural language is trusted? */
|
|
+ tuple = SearchSysCache(LANGOID,
|
|
+ ObjectIdGetDatum(langOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for procedural langugage: %u", langOid);
|
|
+
|
|
+ trusted = ((Form_pg_language) GETSTRUCT(tuple))->lanpltrusted;
|
|
+ if (!trusted)
|
|
+ required |= SEPG_DB_PROCEDURE__UNTRUSTED;
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+#endif
|
|
+
|
|
+ /* check it */
|
|
+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_PROCEDURE,
|
|
+ required, procName, true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_proc_alter(Oid procOid, const char *newName, Oid newNsp)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true);
|
|
+ if (newName || OidIsValid(newNsp))
|
|
+ {
|
|
+ HeapTuple tuple;
|
|
+ Oid oldNsp;
|
|
+
|
|
+ tuple = SearchSysCache(PROCOID,
|
|
+ ObjectIdGetDatum(procOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for function %u", procOid);
|
|
+
|
|
+ oldNsp = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace;
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ if (!OidIsValid(newNsp))
|
|
+ {
|
|
+ sepgsql_schema_common(oldNsp,
|
|
+ SEPG_DB_SCHEMA__ADD_NAME |
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ sepgsql_schema_common(oldNsp, SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+ sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_proc_drop(Oid procOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__DROP, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_proc_grant(Oid procOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true);
|
|
+}
|
|
+
|
|
+Oid
|
|
+sepgsql_proc_relabel(Oid procOid, DefElem *newLabel)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ {
|
|
+ if (newLabel)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("SELinux is disabled now")));
|
|
+ return InvalidOid;
|
|
+ }
|
|
+
|
|
+ sid.relid = ProcedureRelationId;
|
|
+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg));
|
|
+
|
|
+ /* db_procedure:{setattr relabelfrom} for older seclabel */
|
|
+ sepgsql_proc_common(procOid,
|
|
+ SEPG_DB_PROCEDURE__SETATTR |
|
|
+ SEPG_DB_PROCEDURE__RELABELFROM, true);
|
|
+ /* db_procedure:{relabelto} for newer seclabel */
|
|
+ sepgsqlClientHasPerms(sid,
|
|
+ SEPG_CLASS_DB_PROCEDURE,
|
|
+ SEPG_DB_PROCEDURE__RELABELTO,
|
|
+ get_func_name(procOid), true);
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_proc_execute(Oid procOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__EXECUTE, true);
|
|
+}
|
|
+
|
|
+bool
|
|
+sepgsql_proc_hint_inlined(HeapTuple protup)
|
|
+{
|
|
+ security_context_t newcon;
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return true;
|
|
+
|
|
+ if (!sepgsql_proc_common(HeapTupleGetOid(protup),
|
|
+ SEPG_DB_PROCEDURE__EXECUTE, false))
|
|
+ return false;
|
|
+ /*
|
|
+ * If the security context of client is unchange
|
|
+ * before or after invocation of the functions,
|
|
+ * it is not a trusted procedure, so it can be
|
|
+ * inlined due to performance purpose.
|
|
+ */
|
|
+ sid = sepgsqlGetTupleSecid(ProcedureRelationId, protup, NULL);
|
|
+
|
|
+ newcon = sepgsqlClientCreateLabel(sid, SEPG_CLASS_PROCESS);
|
|
+
|
|
+ if (strcmp(sepgsqlGetClientLabel(), newcon) == 0)
|
|
+ return true;
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+bool
|
|
+sepgsql_proc_entrypoint(HeapTuple protup)
|
|
+{
|
|
+ security_context_t newcon;
|
|
+ sepgsql_sid_t proSid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return false;
|
|
+
|
|
+ proSid = sepgsqlGetTupleSecid(ProcedureRelationId,
|
|
+ protup, NULL);
|
|
+
|
|
+ newcon = sepgsqlClientCreateLabel(proSid, SEPG_CLASS_PROCESS);
|
|
+
|
|
+ /* Do nothing, if it is not a trusted procedure */
|
|
+ if (strcmp(newcon, sepgsqlGetClientLabel()) == 0)
|
|
+ return false;
|
|
+
|
|
+ /* check db_procedure:{entrypoint} */
|
|
+ sepgsqlClientHasPerms(proSid,
|
|
+ SEPG_CLASS_DB_PROCEDURE,
|
|
+ SEPG_DB_PROCEDURE__ENTRYPOINT,
|
|
+ NULL, true);
|
|
+
|
|
+ /* check process:{transition} */
|
|
+ sepgsqlComputePerms(sepgsqlGetClientLabel(),
|
|
+ newcon,
|
|
+ SEPG_CLASS_PROCESS,
|
|
+ SEPG_PROCESS__TRANSITION,
|
|
+ NULL, true);
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+char *
|
|
+sepgsql_proc_trusted(HeapTuple protup, MemoryContext mcxt)
|
|
+{
|
|
+ MemoryContext oldcxt;
|
|
+ security_context_t newcon;
|
|
+ sepgsql_sid_t proSid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return NULL;
|
|
+
|
|
+ proSid = sepgsqlGetTupleSecid(ProcedureRelationId, protup, NULL);
|
|
+
|
|
+ oldcxt = MemoryContextSwitchTo(mcxt);
|
|
+
|
|
+ newcon = sepgsqlClientCreateLabel(proSid, SEPG_CLASS_PROCESS);
|
|
+
|
|
+ MemoryContextSwitchTo(oldcxt);
|
|
+
|
|
+ return newcon;
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_cast related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+Oid
|
|
+sepgsql_cast_create(Oid sourceTypOid, Oid targetTypOid, Oid funcOid)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+ char audit_buffer[2*NAMEDATALEN+10];
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ sid = sepgsqlGetDefaultTupleSecid(CastRelationId);
|
|
+
|
|
+ snprintf(audit_buffer, sizeof(audit_buffer), "%s::%s",
|
|
+ format_type_be(sourceTypOid), format_type_be(targetTypOid));
|
|
+
|
|
+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE,
|
|
+ SEPG_DB_TUPLE__INSERT,
|
|
+ audit_buffer, true);
|
|
+
|
|
+ if (OidIsValid(funcOid))
|
|
+ sepgsql_proc_common(funcOid, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_cast_drop(Oid castOid)
|
|
+{
|
|
+ Form_pg_cast castForm;
|
|
+ Relation rel;
|
|
+ HeapTuple tuple;
|
|
+ ScanKeyData skey;
|
|
+ SysScanDesc scan;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+ char audit_buffer[2*NAMEDATALEN+10];
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ rel = heap_open(CastRelationId, AccessShareLock);
|
|
+
|
|
+ ScanKeyInit(&skey,
|
|
+ ObjectIdAttributeNumber,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(castOid));
|
|
+
|
|
+ scan = systable_beginscan(rel, CastOidIndexId, true,
|
|
+ SnapshotNow, 1, &skey);
|
|
+ tuple = systable_getnext(scan);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "could not find tuple for cast: %u", castOid);
|
|
+
|
|
+ castForm = (Form_pg_cast) GETSTRUCT(tuple);
|
|
+
|
|
+ snprintf(audit_buffer, sizeof(audit_buffer), "%s::%s",
|
|
+ format_type_be(castForm->castsource),
|
|
+ format_type_be(castForm->casttarget));
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(CastRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__DELETE,
|
|
+ audit_buffer, true);
|
|
+
|
|
+ systable_endscan(scan);
|
|
+
|
|
+ heap_close(rel, AccessShareLock);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_conversion related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+Oid
|
|
+sepgsql_conversion_create(const char *convName, Oid nspOid, Oid procOid)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ sid = sepgsqlGetDefaultTupleSecid(ConversionRelationId);
|
|
+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE,
|
|
+ SEPG_DB_TUPLE__INSERT,
|
|
+ convName, true);
|
|
+
|
|
+ /* db_schema:{add_name} */
|
|
+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true);
|
|
+
|
|
+ /* db_procedure:{install} */
|
|
+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_conversion_alter(Oid convOid, const char *newName)
|
|
+{
|
|
+ Form_pg_conversion convForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ tuple = SearchSysCache(CONVOID,
|
|
+ ObjectIdGetDatum(convOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for conversion %u", convOid);
|
|
+ convForm = (Form_pg_conversion) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(ConversionRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__UPDATE,
|
|
+ NameStr(convForm->conname), true);
|
|
+ if (newName)
|
|
+ {
|
|
+ Oid nspOid = convForm->connamespace;
|
|
+
|
|
+ sepgsql_schema_common(nspOid,
|
|
+ SEPG_DB_SCHEMA__ADD_NAME |
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+ }
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_conversion_drop(Oid convOid)
|
|
+{
|
|
+ Form_pg_conversion convForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ tuple = SearchSysCache(CONVOID,
|
|
+ ObjectIdGetDatum(convOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for conversion %u", convOid);
|
|
+ convForm = (Form_pg_conversion) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(ConversionRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__UPDATE,
|
|
+ NameStr(convForm->conname), true);
|
|
+
|
|
+ /* db_schema:{remove_name} */
|
|
+ sepgsql_schema_common(convForm->connamespace,
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_foreign_data_wrapper related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+static bool
|
|
+sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort)
|
|
+{
|
|
+ Form_pg_foreign_data_wrapper fdwForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+ bool rc;
|
|
+
|
|
+ tuple = SearchSysCache(FOREIGNDATAWRAPPEROID,
|
|
+ ObjectIdGetDatum(fdwOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for FDW: %u", fdwOid);
|
|
+ fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(ForeignDataWrapperRelationId, tuple, &tclass);
|
|
+ rc = sepgsqlClientHasPerms(sid, tclass, required,
|
|
+ NameStr(fdwForm->fdwname), abort);
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+Oid
|
|
+sepgsql_fdw_create(const char *fdwName, Oid fdwValidator)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ sid = sepgsqlGetDefaultTupleSecid(ForeignDataWrapperRelationId);
|
|
+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE,
|
|
+ SEPG_DB_TUPLE__INSERT,
|
|
+ fdwName, true);
|
|
+
|
|
+ /* db_procedure:{install} */
|
|
+ if (OidIsValid(fdwValidator))
|
|
+ sepgsql_proc_common(fdwValidator, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_fdw_alter(Oid fdwOid, Oid newValidator)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true);
|
|
+
|
|
+ /* db_procedure:{install} */
|
|
+ if (OidIsValid(newValidator))
|
|
+ sepgsql_proc_common(newValidator, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_fdw_drop(Oid fdwOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__DELETE, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_fdw_grant(Oid fdwOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_foreign_server related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+static bool
|
|
+sepgsql_foreign_server_common(Oid fsrvOid, uint32 required, bool abort)
|
|
+{
|
|
+ Form_pg_foreign_server fsrvForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+ bool rc;
|
|
+
|
|
+ tuple = SearchSysCache(FOREIGNSERVEROID,
|
|
+ ObjectIdGetDatum(fsrvOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for foreign server %u", fsrvOid);
|
|
+ fsrvForm = (Form_pg_foreign_server) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(ForeignServerRelationId, tuple, &tclass);
|
|
+ rc = sepgsqlClientHasPerms(sid, tclass, required,
|
|
+ NameStr(fsrvForm->srvname), abort);
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+Oid
|
|
+sepgsql_foreign_server_create(const char *fsrvName)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ sid = sepgsqlGetDefaultTupleSecid(ForeignServerRelationId);
|
|
+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE,
|
|
+ SEPG_DB_TUPLE__INSERT,
|
|
+ fsrvName, true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_foreign_server_alter(Oid fsrvOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__UPDATE, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_foreign_server_drop(Oid fsrvOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__DELETE, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_foreign_server_grant(Oid fsrvOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__UPDATE, true);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_language related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+static bool
|
|
+sepgsql_language_common(Oid langOid, uint32 required, bool abort)
|
|
+{
|
|
+ Form_pg_language langForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+ bool rc;
|
|
+
|
|
+ tuple = SearchSysCache(LANGOID,
|
|
+ ObjectIdGetDatum(langOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for language %u", langOid);
|
|
+ langForm = (Form_pg_language) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(LanguageRelationId, tuple, &tclass);
|
|
+ rc = sepgsqlClientHasPerms(sid, tclass, required,
|
|
+ NameStr(langForm->lanname), abort);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+Oid
|
|
+sepgsql_language_create(const char *langName, Oid handlerOid, Oid validatorOid)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ sid = sepgsqlGetDefaultTupleSecid(LanguageRelationId);
|
|
+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE,
|
|
+ SEPG_DB_TUPLE__INSERT, langName, true);
|
|
+
|
|
+ /* db_procedure:{install} */
|
|
+ if (OidIsValid(handlerOid))
|
|
+ sepgsql_proc_common(handlerOid, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+ if (OidIsValid(validatorOid))
|
|
+ sepgsql_proc_common(validatorOid, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_language_alter(Oid langOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_language_common(langOid, SEPG_DB_TUPLE__UPDATE, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_language_drop(Oid langOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_language_common(langOid, SEPG_DB_TUPLE__DELETE, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_language_grant(Oid langOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_language_common(langOid, SEPG_DB_TUPLE__UPDATE, true);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_largeobject related security hooks
|
|
+ * (need to backport v8.5 feature)
|
|
+ * ------------------------------------------------------------ */
|
|
+static bool
|
|
+sepgsql_largeobject_common(Oid loid, uint32 required, Snapshot snapshot)
|
|
+{
|
|
+ Relation rel;
|
|
+ ScanKeyData skey;
|
|
+ SysScanDesc scan;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+ char auname[64];
|
|
+ bool rc;
|
|
+
|
|
+ rel = heap_open(LargeObjectMetadataRelationId, AccessShareLock);
|
|
+
|
|
+ ScanKeyInit(&skey,
|
|
+ ObjectIdAttributeNumber,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(loid));
|
|
+
|
|
+ scan = systable_beginscan(rel, LargeObjectMetadataOidIndexId,
|
|
+ true, snapshot, 1, &skey);
|
|
+
|
|
+ tuple = systable_getnext(scan);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "largeobject %u lookup failed", loid);
|
|
+
|
|
+ snprintf(auname, sizeof(auname), "blob:%u", loid);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), tuple, &tclass);
|
|
+
|
|
+ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, true);
|
|
+
|
|
+ systable_endscan(scan);
|
|
+
|
|
+ heap_close(rel, AccessShareLock);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+Oid
|
|
+sepgsql_largeobject_create(Oid loid, Value *secLabel)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ if (!secLabel)
|
|
+ sid = sepgsqlGetDefaultBlobSecid(MyDatabaseId);
|
|
+ else
|
|
+ {
|
|
+ sid.relid = LargeObjectMetadataRelationId;
|
|
+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(secLabel));
|
|
+ }
|
|
+
|
|
+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_BLOB,
|
|
+ SEPG_DB_BLOB__CREATE,
|
|
+ NULL, true);
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_largeobject_alter(Oid loid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_largeobject_common(loid, SEPG_DB_BLOB__SETATTR, SnapshotNow);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_largeobject_drop(Oid loid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_largeobject_common(loid, SEPG_DB_BLOB__DROP, SnapshotNow);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_largeobject_read(Oid loid, Snapshot snapshot)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_largeobject_common(loid, SEPG_DB_BLOB__READ, snapshot);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_largeobject_write(Oid loid, Snapshot snapshot)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_largeobject_common(loid, SEPG_DB_BLOB__WRITE, snapshot);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_largeobject_export(Oid loid, const char *filename)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_largeobject_common(loid,
|
|
+ SEPG_DB_BLOB__READ |
|
|
+ SEPG_DB_BLOB__EXPORT, SnapshotNow);
|
|
+
|
|
+ sepgsql_file_write(filename);
|
|
+}
|
|
+
|
|
+Oid
|
|
+sepgsql_largeobject_import(Oid loid, const char *filename)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ sid = sepgsqlGetDefaultBlobSecid(MyDatabaseId);
|
|
+
|
|
+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_BLOB,
|
|
+ SEPG_DB_BLOB__CREATE |
|
|
+ SEPG_DB_BLOB__WRITE |
|
|
+ SEPG_DB_BLOB__IMPORT,
|
|
+ NULL, true);
|
|
+
|
|
+ sepgsql_file_read(filename);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_opclass related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+Oid
|
|
+sepgsql_opclass_create(const char *opcName, Oid nspOid)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ sid = sepgsqlGetDefaultTupleSecid(OperatorClassRelationId);
|
|
+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE,
|
|
+ SEPG_DB_TUPLE__INSERT,
|
|
+ opcName, true);
|
|
+
|
|
+ /* db_schema:{add_name} */
|
|
+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_opclass_alter(Oid opcOid, const char *newName)
|
|
+{
|
|
+ Form_pg_opclass opcForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ tuple = SearchSysCache(CLAOID,
|
|
+ ObjectIdGetDatum(opcOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for opclass %u", opcOid);
|
|
+ opcForm = (Form_pg_opclass) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(OperatorClassRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__UPDATE,
|
|
+ NameStr(opcForm->opcname), true);
|
|
+
|
|
+ /* db_schema:{add_name remove_name} */
|
|
+ if (newName)
|
|
+ {
|
|
+ sepgsql_schema_common(opcForm->opcnamespace,
|
|
+ SEPG_DB_SCHEMA__ADD_NAME |
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+ }
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_opclass_drop(Oid opcOid)
|
|
+{
|
|
+ Form_pg_opclass opcForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ tuple = SearchSysCache(CLAOID,
|
|
+ ObjectIdGetDatum(opcOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for opclass %u", opcOid);
|
|
+ opcForm = (Form_pg_opclass) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(OperatorClassRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__UPDATE,
|
|
+ NameStr(opcForm->opcname), true);
|
|
+
|
|
+ /* db_schema:{remove_name} */
|
|
+ sepgsql_schema_common(opcForm->opcnamespace,
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_opfamily related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+Oid
|
|
+sepgsql_opfamily_create(const char *opfName, Oid nspOid)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ sid = sepgsqlGetDefaultTupleSecid(OperatorFamilyRelationId);
|
|
+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE,
|
|
+ SEPG_DB_TUPLE__INSERT,
|
|
+ opfName, true);
|
|
+
|
|
+ /* db_schema:{add_name} */
|
|
+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_opfamily_alter(Oid opfOid, const char *newName)
|
|
+{
|
|
+ Form_pg_opfamily opfForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ tuple = SearchSysCache(OPFAMILYOID,
|
|
+ ObjectIdGetDatum(opfOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for operator family: %u", opfOid);
|
|
+ opfForm = (Form_pg_opfamily) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__UPDATE,
|
|
+ NameStr(opfForm->opfname), true);
|
|
+ if (newName)
|
|
+ {
|
|
+ sepgsql_schema_common(opfForm->opfnamespace,
|
|
+ SEPG_DB_SCHEMA__ADD_NAME |
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+ }
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_opfamily_drop(Oid opfOid)
|
|
+{
|
|
+ Form_pg_opfamily opfForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ tuple = SearchSysCache(OPFAMILYOID,
|
|
+ ObjectIdGetDatum(opfOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for operator family: %u", opfOid);
|
|
+ opfForm = (Form_pg_opfamily) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__DELETE,
|
|
+ NameStr(opfForm->opfname), true);
|
|
+
|
|
+ /* db_schema:{remove_name} */
|
|
+ sepgsql_schema_common(opfForm->opfnamespace,
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_opfamily_add_operator(Oid opfOid, Oid operOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ /* currently, do nothing here */
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_opfamily_add_procedure(Oid opfOid, Oid procOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ /*
|
|
+ * Note that db_tuple:{setattr} is already checked at the
|
|
+ * earlier phase, so db_procedure:{install} is only needed.
|
|
+ */
|
|
+ if (OidIsValid(procOid))
|
|
+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_operator related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+static bool
|
|
+sepgsql_operator_common(Oid oprOid, uint32 required, bool abort)
|
|
+{
|
|
+ Form_pg_operator oprForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+ bool rc;
|
|
+
|
|
+ tuple = SearchSysCache(OPEROID,
|
|
+ ObjectIdGetDatum(oprOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for operator: %u", oprOid);
|
|
+ oprForm = (Form_pg_operator) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(OperatorRelationId, tuple, &tclass);
|
|
+ rc = sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__DELETE,
|
|
+ NameStr(oprForm->oprname), abort);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+Oid
|
|
+sepgsql_operator_create(const char *oprName, Oid oprOid, Oid nspOid,
|
|
+ Oid codeFn, Oid restFn, Oid joinFn)
|
|
+{
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint32 required;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ if (!OidIsValid(oprOid))
|
|
+ {
|
|
+ sid = sepgsqlGetDefaultTupleSecid(OperatorRelationId);
|
|
+ required = SEPG_DB_TUPLE__INSERT;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ tuple = SearchSysCache(OPEROID,
|
|
+ ObjectIdGetDatum(oprOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for operator %u", oprOid);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(OperatorRelationId, tuple, NULL);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ required = SEPG_DB_TUPLE__UPDATE;
|
|
+ }
|
|
+
|
|
+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE,
|
|
+ required, oprName, true);
|
|
+
|
|
+ /* db_schema:{add_name} checks */
|
|
+ if (!OidIsValid(oprOid))
|
|
+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true);
|
|
+
|
|
+ /* db_procedure:{install} checks */
|
|
+ if (OidIsValid(codeFn))
|
|
+ sepgsql_proc_common(codeFn, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+ if (OidIsValid(restFn))
|
|
+ sepgsql_proc_common(restFn, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+ if (OidIsValid(joinFn))
|
|
+ sepgsql_proc_common(joinFn, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_operator_alter(Oid oprOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_operator_common(oprOid, SEPG_DB_TUPLE__UPDATE, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_operator_drop(Oid oprOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_operator_common(oprOid, SEPG_DB_TUPLE__DELETE, true);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_rewrite related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+void
|
|
+sepgsql_rule_create(Oid relOid, const char *ruleName)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_rule_drop(Oid relOid, const char *ruleName)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_trigger related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+void
|
|
+sepgsql_trigger_create(Oid relOid, const char *trigName, Oid procOid)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ /* db_table:{setattr} */
|
|
+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true);
|
|
+
|
|
+ /* db_procedure:{install} */
|
|
+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_trigger_alter(Oid relOid, const char *trigName)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ /* db_table:{setattr} */
|
|
+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_trigger_drop(Oid relOid, const char *trigName)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ /* db_table:{setattr} */
|
|
+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_type related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+Oid
|
|
+sepgsql_ts_config_create(const char *cfgName, Oid nspOid)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ sid = sepgsqlGetDefaultTupleSecid(TSConfigRelationId);
|
|
+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE,
|
|
+ SEPG_DB_TUPLE__INSERT,
|
|
+ cfgName, true);
|
|
+
|
|
+ /* db_schema:{add_name} */
|
|
+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_ts_config_alter(Oid cfgOid, const char *newName)
|
|
+{
|
|
+ Form_pg_ts_config cfgForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ tuple = SearchSysCache(TSCONFIGOID,
|
|
+ ObjectIdGetDatum(cfgOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for text search dictionary %u", cfgOid);
|
|
+ cfgForm = (Form_pg_ts_config) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(TSConfigRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__UPDATE,
|
|
+ NameStr(cfgForm->cfgname), true);
|
|
+ if (newName)
|
|
+ {
|
|
+ sepgsql_schema_common(cfgForm->cfgnamespace,
|
|
+ SEPG_DB_SCHEMA__ADD_NAME |
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+ }
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_ts_config_drop(Oid cfgOid)
|
|
+{
|
|
+ Form_pg_ts_config cfgForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ tuple = SearchSysCache(TSCONFIGOID,
|
|
+ ObjectIdGetDatum(cfgOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for text search dictionary %u", cfgOid);
|
|
+ cfgForm = (Form_pg_ts_config) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(TSConfigRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__DELETE,
|
|
+ NameStr(cfgForm->cfgname), true);
|
|
+
|
|
+ /* db_schema:{remove_name} */
|
|
+ sepgsql_schema_common(cfgForm->cfgnamespace,
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_type related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+Oid
|
|
+sepgsql_ts_dict_create(const char *dictName, Oid nspOid)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ sid = sepgsqlGetDefaultTupleSecid(TSDictionaryRelationId);
|
|
+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE,
|
|
+ SEPG_DB_TUPLE__INSERT,
|
|
+ dictName, true);
|
|
+
|
|
+ /* db_schema:{add_name} */
|
|
+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_ts_dict_alter(Oid dictOid, const char *newName)
|
|
+{
|
|
+ Form_pg_ts_dict dictForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ tuple = SearchSysCache(TSDICTOID,
|
|
+ ObjectIdGetDatum(dictOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for text search dictionary %u", dictOid);
|
|
+ dictForm = (Form_pg_ts_dict) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(TSDictionaryRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__UPDATE,
|
|
+ NameStr(dictForm->dictname), true);
|
|
+
|
|
+ /* db_schema:{add_name remove_name} */
|
|
+ if (newName)
|
|
+ {
|
|
+ sepgsql_schema_common(dictForm->dictnamespace,
|
|
+ SEPG_DB_SCHEMA__ADD_NAME |
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+ }
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_ts_dict_drop(Oid dictOid)
|
|
+{
|
|
+ Form_pg_ts_dict dictForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ tuple = SearchSysCache(TSDICTOID,
|
|
+ ObjectIdGetDatum(dictOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for text search dictionary %u", dictOid);
|
|
+ dictForm = (Form_pg_ts_dict) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(TSDictionaryRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__DELETE,
|
|
+ NameStr(dictForm->dictname), true);
|
|
+
|
|
+ /* db_schema:{remove_name} */
|
|
+ sepgsql_schema_common(dictForm->dictnamespace,
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_type related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+Oid
|
|
+sepgsql_ts_parser_create(const char *prsName, Oid nspOid,
|
|
+ Oid startFn, Oid tokenFn, Oid sendFn,
|
|
+ Oid headlineFn, Oid lextypeFn)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ sid = sepgsqlGetDefaultTupleSecid(TSParserRelationId);
|
|
+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE,
|
|
+ SEPG_DB_TUPLE__INSERT,
|
|
+ prsName, true);
|
|
+
|
|
+ /* db_schema:{add_name} */
|
|
+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true);
|
|
+
|
|
+ /* db_procedure:{install} */
|
|
+ if (OidIsValid(startFn))
|
|
+ sepgsql_proc_common(startFn, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+ if (OidIsValid(tokenFn))
|
|
+ sepgsql_proc_common(tokenFn, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+ if (OidIsValid(sendFn))
|
|
+ sepgsql_proc_common(sendFn, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+ if (OidIsValid(headlineFn))
|
|
+ sepgsql_proc_common(headlineFn, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+ if (OidIsValid(lextypeFn))
|
|
+ sepgsql_proc_common(lextypeFn, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_ts_parser_alter(Oid prsOid, const char *newName)
|
|
+{
|
|
+ Form_pg_ts_parser prsForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ tuple = SearchSysCache(TSPARSEROID,
|
|
+ ObjectIdGetDatum(prsOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for text search parser %u", prsOid);
|
|
+
|
|
+ prsForm = (Form_pg_ts_parser) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(TSParserRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__UPDATE,
|
|
+ NameStr(prsForm->prsname), true);
|
|
+ if (newName)
|
|
+ {
|
|
+ sepgsql_schema_common(prsForm->prsnamespace,
|
|
+ SEPG_DB_SCHEMA__ADD_NAME |
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+ }
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_ts_parser_drop(Oid prsOid)
|
|
+{
|
|
+ Form_pg_ts_parser prsForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ tuple = SearchSysCache(TSPARSEROID,
|
|
+ ObjectIdGetDatum(prsOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for text search parser %u", prsOid);
|
|
+
|
|
+ prsForm = (Form_pg_ts_parser) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(TSParserRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__DELETE,
|
|
+ NameStr(prsForm->prsname), true);
|
|
+
|
|
+ /* db_schema:{remove_name} */
|
|
+ sepgsql_schema_common(prsForm->prsnamespace,
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_type related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+Oid
|
|
+sepgsql_ts_template_create(const char *tmplName, Oid nspOid,
|
|
+ Oid initFn, Oid lexizeFn)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ sid = sepgsqlGetDefaultTupleSecid(TSTemplateRelationId);
|
|
+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE,
|
|
+ SEPG_DB_TUPLE__INSERT,
|
|
+ tmplName, true);
|
|
+
|
|
+ /* db_schema:{add_name} */
|
|
+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true);
|
|
+
|
|
+ /* db_procedure:{install} */
|
|
+ if (OidIsValid(initFn))
|
|
+ sepgsql_proc_common(initFn, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+ if (OidIsValid(lexizeFn))
|
|
+ sepgsql_proc_common(lexizeFn, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_ts_template_alter(Oid tmplOid, const char *newName)
|
|
+{
|
|
+ Form_pg_ts_template tmplForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ tuple = SearchSysCache(TSTEMPLATEOID,
|
|
+ ObjectIdGetDatum(tmplOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for text search template %u", tmplOid);
|
|
+ tmplForm = (Form_pg_ts_template) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(TSTemplateRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__UPDATE,
|
|
+ NameStr(tmplForm->tmplname), true);
|
|
+ if (newName)
|
|
+ {
|
|
+ sepgsql_schema_common(tmplForm->tmplnamespace,
|
|
+ SEPG_DB_SCHEMA__ADD_NAME |
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+ }
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_ts_template_drop(Oid tmplOid)
|
|
+{
|
|
+ Form_pg_ts_template tmplForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ tuple = SearchSysCache(TSTEMPLATEOID,
|
|
+ ObjectIdGetDatum(tmplOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for text search template %u", tmplOid);
|
|
+ tmplForm = (Form_pg_ts_template) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(TSTemplateRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__DELETE,
|
|
+ NameStr(tmplForm->tmplname), true);
|
|
+
|
|
+ /* db_schema:{remove_name} */
|
|
+ sepgsql_schema_common(tmplForm->tmplnamespace,
|
|
+ SEPG_DB_SCHEMA__ADD_NAME |
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Pg_type related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+Oid
|
|
+sepgsql_type_create(const char *typName, HeapTuple oldTup, Oid nspOid,
|
|
+ Oid inputProc, Oid outputProc, Oid recvProc, Oid sendProc,
|
|
+ Oid modinProc, Oid modoutProc, Oid analyzeProc)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+ uint32 required;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return InvalidOid;
|
|
+
|
|
+ if (!HeapTupleIsValid(oldTup))
|
|
+ {
|
|
+ sid = sepgsqlGetDefaultTupleSecid(TypeRelationId);
|
|
+ required = SEPG_DB_TUPLE__INSERT;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ sid = sepgsqlGetTupleSecid(TypeRelationId, oldTup, NULL);
|
|
+ required = SEPG_DB_TUPLE__UPDATE;
|
|
+ }
|
|
+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE,
|
|
+ required, typName, true);
|
|
+ /* db_schema:{add_name} */
|
|
+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true);
|
|
+
|
|
+ /* db_procedure:{install} */
|
|
+ if (OidIsValid(inputProc))
|
|
+ sepgsql_proc_common(inputProc, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+ if (OidIsValid(outputProc))
|
|
+ sepgsql_proc_common(outputProc, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+ if (OidIsValid(recvProc))
|
|
+ sepgsql_proc_common(recvProc, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+ if (OidIsValid(sendProc))
|
|
+ sepgsql_proc_common(sendProc, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+ if (OidIsValid(modinProc))
|
|
+ sepgsql_proc_common(modinProc, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+ if (OidIsValid(modoutProc))
|
|
+ sepgsql_proc_common(modoutProc, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+ if (OidIsValid(analyzeProc))
|
|
+ sepgsql_proc_common(analyzeProc, SEPG_DB_PROCEDURE__INSTALL, true);
|
|
+
|
|
+ return sid.secid;
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_type_alter(Oid typOid, const char *newName, Oid newNsp)
|
|
+{
|
|
+ Form_pg_type typForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ tuple = SearchSysCache(TYPEOID,
|
|
+ ObjectIdGetDatum(typOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for type: %u", typOid);
|
|
+ typForm = (Form_pg_type) GETSTRUCT(tuple);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(TypeRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__UPDATE,
|
|
+ NameStr(typForm->typname), true);
|
|
+
|
|
+ if (newName || OidIsValid(newNsp))
|
|
+ {
|
|
+ Oid oldNsp = typForm->typnamespace;
|
|
+
|
|
+ if (!OidIsValid(newNsp))
|
|
+ {
|
|
+ sepgsql_schema_common(oldNsp,
|
|
+ SEPG_DB_SCHEMA__ADD_NAME |
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ sepgsql_schema_common(oldNsp, SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+ sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true);
|
|
+ }
|
|
+ }
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_type_drop(Oid typOid)
|
|
+{
|
|
+ Form_pg_type typForm;
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ tuple = SearchSysCache(TYPEOID,
|
|
+ ObjectIdGetDatum(typOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for type: %u", typOid);
|
|
+ typForm = (Form_pg_type) GETSTRUCT(tuple);
|
|
+
|
|
+ if (typForm->typtype == TYPTYPE_COMPOSITE ||
|
|
+ (typForm->typtype == TYPTYPE_BASE && OidIsValid(typForm->typarray)))
|
|
+ {
|
|
+ /*
|
|
+ * No need to check for composite type and implicitly
|
|
+ * declared array type here.
|
|
+ */
|
|
+ ReleaseSysCache(tuple);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(TypeRelationId, tuple, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__DELETE,
|
|
+ NameStr(typForm->typname), true);
|
|
+
|
|
+ /* db_schema:{remove_name} */
|
|
+ sepgsql_schema_common(typForm->typnamespace,
|
|
+ SEPG_DB_SCHEMA__REMOVE_NAME, true);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Misc system object related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+
|
|
+void
|
|
+sepgsql_sysobj_drop(const ObjectAddress *object)
|
|
+{
|
|
+ switch (object->classId)
|
|
+ {
|
|
+ case RelationRelationId:
|
|
+ if (object->objectSubId == 0)
|
|
+ sepgsql_relation_drop(object->objectId);
|
|
+ else
|
|
+ sepgsql_attribute_drop(object->objectId,
|
|
+ object->objectSubId);
|
|
+ break;
|
|
+
|
|
+ case ProcedureRelationId:
|
|
+ sepgsql_proc_drop(object->objectId);
|
|
+ break;
|
|
+
|
|
+ case TypeRelationId:
|
|
+ sepgsql_type_drop(object->objectId);
|
|
+ break;
|
|
+
|
|
+ case CastRelationId:
|
|
+ sepgsql_cast_drop(object->objectId);
|
|
+ break;
|
|
+
|
|
+ case ConversionRelationId:
|
|
+ sepgsql_conversion_drop(object->objectId);
|
|
+ break;
|
|
+
|
|
+ case LanguageRelationId:
|
|
+ sepgsql_language_drop(object->objectId);
|
|
+ break;
|
|
+
|
|
+ case OperatorRelationId:
|
|
+ sepgsql_operator_drop(object->objectId);
|
|
+ break;
|
|
+
|
|
+ case OperatorClassRelationId:
|
|
+ sepgsql_opclass_drop(object->objectId);
|
|
+ break;
|
|
+
|
|
+ case OperatorFamilyRelationId:
|
|
+ sepgsql_opfamily_drop(object->objectId);
|
|
+ break;
|
|
+
|
|
+ case NamespaceRelationId:
|
|
+ sepgsql_schema_drop(object->objectId);
|
|
+ break;
|
|
+
|
|
+ case TSParserRelationId:
|
|
+ sepgsql_ts_parser_drop(object->objectId);
|
|
+ break;
|
|
+
|
|
+ case TSDictionaryRelationId:
|
|
+ sepgsql_ts_dict_drop(object->objectId);
|
|
+ break;
|
|
+
|
|
+ case TSTemplateRelationId:
|
|
+ sepgsql_ts_template_drop(object->objectId);
|
|
+ break;
|
|
+
|
|
+ case TSConfigRelationId:
|
|
+ sepgsql_ts_config_drop(object->objectId);
|
|
+ break;
|
|
+
|
|
+ case AuthIdRelationId:
|
|
+ break;
|
|
+
|
|
+ case DatabaseRelationId:
|
|
+ sepgsql_database_drop(object->objectId);
|
|
+ break;
|
|
+
|
|
+ case TableSpaceRelationId:
|
|
+ break;
|
|
+
|
|
+ case ForeignDataWrapperRelationId:
|
|
+ sepgsql_fdw_drop(object->objectId);
|
|
+ break;
|
|
+
|
|
+ case ForeignServerRelationId:
|
|
+ sepgsql_foreign_server_drop(object->objectId);
|
|
+ break;
|
|
+
|
|
+ case UserMappingRelationId:
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ /* do nothing */
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------ *
|
|
+ *
|
|
+ * Filesystem object related security hooks
|
|
+ *
|
|
+ * ------------------------------------------------------------ */
|
|
+static char *
|
|
+sepgsql_getfilecon(const char *path)
|
|
+{
|
|
+ security_context_t context;
|
|
+ char *result;
|
|
+
|
|
+ if (getfilecon_raw(path, &context) < 0)
|
|
+ ereport(ERROR,
|
|
+ (errcode_for_file_access(),
|
|
+ errmsg("could not get context of \"%s\": %m", path)));
|
|
+
|
|
+ PG_TRY();
|
|
+ {
|
|
+ result = pstrdup(context);
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(context);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+ freecon(context);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+static void
|
|
+sepgsql_file_common(const char *filename, uint32 required, bool may_create)
|
|
+{
|
|
+ struct stat stbuf;
|
|
+
|
|
+ if (stat(filename, &stbuf) == 0)
|
|
+ {
|
|
+ uint16 tclass;
|
|
+
|
|
+ /*
|
|
+ * Get file object class
|
|
+ */
|
|
+ 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;
|
|
+
|
|
+ /*
|
|
+ * Check permission (no cached operation)
|
|
+ */
|
|
+ sepgsqlComputePerms(sepgsqlGetClientLabel(),
|
|
+ sepgsql_getfilecon(filename),
|
|
+ tclass, required,
|
|
+ filename, true);
|
|
+ }
|
|
+ else if (may_create)
|
|
+ {
|
|
+ /*
|
|
+ * If the required file is not found, we check permission to
|
|
+ * create a new file and required permission on the new file.
|
|
+ */
|
|
+ security_context_t dcontext;
|
|
+ security_context_t ncontext;
|
|
+ char *copy = pstrdup(filename);
|
|
+
|
|
+ /*
|
|
+ * Compute a security context for the new file
|
|
+ */
|
|
+ dcontext = sepgsql_getfilecon(dirname(copy));
|
|
+
|
|
+ ncontext = sepgsqlComputeCreate(sepgsqlGetServerLabel(),
|
|
+ dcontext,
|
|
+ SEPG_CLASS_FILE);
|
|
+ /*
|
|
+ * Check permission (no cached operation)
|
|
+ */
|
|
+ required |= SEPG_FILE__CREATE;
|
|
+
|
|
+ sepgsqlComputePerms(sepgsqlGetClientLabel(),
|
|
+ sepgsql_getfilecon(filename),
|
|
+ SEPG_CLASS_FILE,
|
|
+ required, filename, true);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ ereport(ERROR,
|
|
+ (errcode_for_file_access(),
|
|
+ errmsg("could not stat file \"%s\": %m", filename)));
|
|
+ }
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_file_stat(const char *filename)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_file_common(filename, SEPG_FILE__GETATTR, false);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_file_read(const char *filename)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_file_common(filename, SEPG_FILE__READ, false);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsql_file_write(const char *filename)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ sepgsql_file_common(filename, SEPG_FILE__WRITE, true);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * TODO: add check for pg_ls_dir()
|
|
+ */
|
|
diff --git a/src/backend/security/sepgsql/checker.c b/src/backend/security/sepgsql/checker.c
|
|
new file mode 100644
|
|
index 0000000..9e573c3
|
|
--- /dev/null
|
|
+++ b/src/backend/security/sepgsql/checker.c
|
|
@@ -0,0 +1,432 @@
|
|
+/*
|
|
+ * src/backend/security/sepgsql/checker.c
|
|
+ * walks on given Query tree and applies checks
|
|
+ *
|
|
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
|
+ * Portions Copyright (c) 1994, Regents of the University of California
|
|
+ */
|
|
+#include "postgres.h"
|
|
+
|
|
+#include "access/sysattr.h"
|
|
+#include "catalog/catalog.h"
|
|
+#include "catalog/pg_largeobject.h"
|
|
+#include "catalog/pg_security.h"
|
|
+#include "miscadmin.h"
|
|
+#include "security/sepgsql.h"
|
|
+#include "storage/bufmgr.h"
|
|
+#include "utils/lsyscache.h"
|
|
+#include "utils/syscache.h"
|
|
+#include "utils/tqual.h"
|
|
+
|
|
+/*
|
|
+ * fixupWholeRowReference
|
|
+ */
|
|
+static Bitmapset *
|
|
+fixupWholeRowReference(Oid relid, int nattrs, 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 <= nattrs; attno++)
|
|
+ {
|
|
+ Form_pg_attribute attform;
|
|
+ HeapTuple atttup;
|
|
+
|
|
+ atttup = SearchSysCache(ATTNUM,
|
|
+ ObjectIdGetDatum(relid),
|
|
+ Int16GetDatum(attno),
|
|
+ 0, 0);
|
|
+ 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;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * checkTabelColumnPerms
|
|
+ * This functions applies table/column level permissions for
|
|
+ * all the appeared ones in user's query, and raises an error
|
|
+ * if violated.
|
|
+ * It also applies a few hardwired policy which prevent to
|
|
+ * modified some of system catalogs.
|
|
+ */
|
|
+static void
|
|
+checkTabelColumnPerms(Oid relid, Bitmapset *selected, Bitmapset *modified,
|
|
+ access_vector_t required)
|
|
+{
|
|
+ Bitmapset *columns;
|
|
+ Bitmapset *selected_ex;
|
|
+ Bitmapset *modified_ex;
|
|
+ Form_pg_class relForm;
|
|
+ HeapTuple reltup;
|
|
+ sepgsql_sid_t relsid;
|
|
+ sepgsql_sid_t attsid;
|
|
+ AttrNumber attno;
|
|
+ uint16 tclass;
|
|
+
|
|
+ /*
|
|
+ * Hardwired Policy:
|
|
+ * SE-PostgreSQL enforces that clients cannot modify system
|
|
+ * catalogs and access toast values using DML statements,
|
|
+ * except initial setting up phase.
|
|
+ */
|
|
+ if (sepgsqlGetEnforce())
|
|
+ {
|
|
+ if (IsSystemNamespace(get_rel_namespace(relid)) &&
|
|
+ (required & (SEPG_DB_TABLE__UPDATE |
|
|
+ SEPG_DB_TABLE__INSERT |
|
|
+ SEPG_DB_TABLE__DELETE)) != 0)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("SE-PostgreSQL prevents to modidy \"%s\"",
|
|
+ get_rel_name(relid))));
|
|
+ if (get_rel_relkind(relid) == RELKIND_TOASTVALUE)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("SE-PostgreSQL prevents to access \"%s\"",
|
|
+ get_rel_name(relid))));
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Check db_table:{...} or db_sequence permissions
|
|
+ */
|
|
+ reltup = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(relid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(reltup))
|
|
+ elog(ERROR, "SELinux: cache lookup failed for relation %u", relid);
|
|
+
|
|
+ relForm = (Form_pg_class) GETSTRUCT(reltup);
|
|
+
|
|
+ relsid = sepgsqlGetTupleSecid(RelationRelationId, reltup, &tclass);
|
|
+
|
|
+ if (tclass != SEPG_CLASS_DB_TABLE)
|
|
+ {
|
|
+ /* check db_sequence:{xxx} permission */
|
|
+ if (tclass == SEPG_CLASS_DB_SEQUENCE)
|
|
+ {
|
|
+ if (required & SEPG_DB_TABLE__SELECT)
|
|
+ {
|
|
+ sepgsqlClientHasPerms(relsid, tclass,
|
|
+ SEPG_DB_SEQUENCE__GET_VALUE,
|
|
+ NameStr(relForm->relname), true);
|
|
+ }
|
|
+ }
|
|
+ ReleaseSysCache(reltup);
|
|
+ return;
|
|
+ }
|
|
+ sepgsqlClientHasPerms(relsid, tclass, required,
|
|
+ NameStr(relForm->relname), true);
|
|
+
|
|
+ /*
|
|
+ * Check db_column:{...} permissions
|
|
+ */
|
|
+ selected_ex = fixupWholeRowReference(relid, relForm->relnatts, selected);
|
|
+ modified_ex = fixupWholeRowReference(relid, relForm->relnatts, modified);
|
|
+ columns = bms_union(selected_ex, modified_ex);
|
|
+
|
|
+ while ((attno = bms_first_member(columns)) >= 0)
|
|
+ {
|
|
+ Form_pg_attribute attForm;
|
|
+ HeapTuple atttup;
|
|
+ uint32 attperms = 0;
|
|
+ char auname[2 * NAMEDATALEN + 3];
|
|
+
|
|
+ if (bms_is_member(attno, selected_ex))
|
|
+ attperms |= SEPG_DB_COLUMN__SELECT;
|
|
+ if (bms_is_member(attno, modified_ex))
|
|
+ {
|
|
+ if (required & SEPG_DB_TABLE__UPDATE)
|
|
+ attperms |= SEPG_DB_COLUMN__UPDATE;
|
|
+ if (required & SEPG_DB_TABLE__INSERT)
|
|
+ attperms |= SEPG_DB_COLUMN__INSERT;
|
|
+ }
|
|
+ if (attperms == 0)
|
|
+ continue;
|
|
+
|
|
+ /* remove the attribute number offset */
|
|
+ attno += FirstLowInvalidHeapAttributeNumber;
|
|
+ atttup = SearchSysCache(ATTNUM,
|
|
+ ObjectIdGetDatum(relid),
|
|
+ Int16GetDatum(attno),
|
|
+ 0, 0);
|
|
+ if (!HeapTupleIsValid(atttup))
|
|
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
|
|
+ attno, relid);
|
|
+
|
|
+ attForm = (Form_pg_attribute) GETSTRUCT(atttup);
|
|
+ if (attForm->attisdropped)
|
|
+ elog(ERROR, "attribute %d of relation %u does not exist",
|
|
+ attno, relid);
|
|
+
|
|
+ snprintf(auname, sizeof(auname), "%s.%s",
|
|
+ NameStr(relForm->relname),
|
|
+ NameStr(attForm->attname));
|
|
+ attsid = sepgsqlGetTupleSecid(AttributeRelationId,
|
|
+ atttup, &tclass);
|
|
+ sepgsqlClientHasPerms(attsid, tclass, attperms, auname, true);
|
|
+
|
|
+ ReleaseSysCache(atttup);
|
|
+ }
|
|
+
|
|
+ ReleaseSysCache(reltup);
|
|
+
|
|
+ if (selected_ex != selected)
|
|
+ bms_free(selected_ex);
|
|
+
|
|
+ if (modified_ex != modified)
|
|
+ bms_free(modified_ex);
|
|
+
|
|
+ bms_free(columns);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlCheckQueryPerms
|
|
+ * It checks permission for all the required tables/columns on
|
|
+ * generic user queries.
|
|
+ */
|
|
+void
|
|
+sepgsqlCheckRTEPerms(RangeTblEntry *rte)
|
|
+{
|
|
+ access_vector_t required = 0;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ if (rte->rtekind != RTE_RELATION)
|
|
+ return;
|
|
+
|
|
+ if (rte->requiredPerms & ACL_SELECT)
|
|
+ required |= SEPG_DB_TABLE__SELECT;
|
|
+ if (rte->requiredPerms & ACL_INSERT)
|
|
+ required |= SEPG_DB_TABLE__INSERT;
|
|
+ if (rte->requiredPerms & ACL_UPDATE)
|
|
+ {
|
|
+ /*
|
|
+ * ACL_SELECT_FOR_UPDATE is defined as an aliase of ACL_UPDATE,
|
|
+ * so we cannot determine whether the given relation is accessed
|
|
+ * with UPDATE statement or SELECT FOR SHARE/UPDATE immediately.
|
|
+ * UPDATE statements set a bit on rte->modifiedCols at least,
|
|
+ * so we use it as a watermark.
|
|
+ */
|
|
+ if (!bms_is_empty(rte->modifiedCols))
|
|
+ required |= SEPG_DB_TABLE__UPDATE;
|
|
+ else
|
|
+ required |= SEPG_DB_TABLE__LOCK;
|
|
+ }
|
|
+ if (rte->requiredPerms & ACL_DELETE)
|
|
+ required |= SEPG_DB_TABLE__DELETE;
|
|
+
|
|
+ if (required == 0)
|
|
+ return;
|
|
+
|
|
+ checkTabelColumnPerms(rte->relid,
|
|
+ rte->selectedCols,
|
|
+ rte->modifiedCols,
|
|
+ required);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlCheckCopyTable
|
|
+ * It checks permissions on COPY TO/FROM.
|
|
+ */
|
|
+void
|
|
+sepgsqlCheckCopyTable(Relation rel, List *attnumlist, bool is_from)
|
|
+{
|
|
+ Bitmapset *selected = NULL;
|
|
+ Bitmapset *modified = NULL;
|
|
+ ListCell *l;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ /* all checkes are done in sepgsqlCheckRTEPerms */
|
|
+ if (!rel)
|
|
+ return;
|
|
+
|
|
+ foreach (l, attnumlist)
|
|
+ {
|
|
+ AttrNumber attno = lfirst_int(l);
|
|
+
|
|
+ attno -= FirstLowInvalidHeapAttributeNumber;
|
|
+ if (is_from)
|
|
+ modified = bms_add_member(modified, attno);
|
|
+ else
|
|
+ selected = bms_add_member(selected, attno);
|
|
+ }
|
|
+
|
|
+ checkTabelColumnPerms(RelationGetRelid(rel),
|
|
+ selected, modified,
|
|
+ is_from ? SEPG_DB_TABLE__INSERT
|
|
+ : SEPG_DB_TABLE__SELECT);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlExecScan
|
|
+ * makes a decision on the given tuple.
|
|
+ */
|
|
+bool
|
|
+sepgsqlExecScan(Relation rel, HeapTuple tuple, uint32 required, bool abort)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled() ||
|
|
+ !required ||
|
|
+ RelationGetForm(rel)->relkind != RELKIND_RELATION ||
|
|
+ RelationGetRelid(rel) == SecurityRelationId)
|
|
+ return true;
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), tuple, &tclass);
|
|
+ /*
|
|
+ * Insert/Delete to an external attribute is equivalent to
|
|
+ * the set-attribute on the master
|
|
+ */
|
|
+ if (sid.relid != RelationGetRelid(rel) &&
|
|
+ (required & (SEPG_DB_TUPLE__INSERT | SEPG_DB_TUPLE__DELETE)))
|
|
+ {
|
|
+ required &= ~(SEPG_DB_TUPLE__INSERT | SEPG_DB_TUPLE__DELETE);
|
|
+ required |= SEPG_DB_TUPLE__UPDATE;
|
|
+ }
|
|
+
|
|
+ return sepgsqlClientHasPerms(sid, tclass, required, NULL, abort);
|
|
+}
|
|
+
|
|
+uint32
|
|
+sepgsqlSetupTuplePerms(RangeTblEntry *rte)
|
|
+{
|
|
+ AclMode perms = 0;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return 0;
|
|
+
|
|
+ if (rte->rtekind != RTE_RELATION)
|
|
+ return 0;
|
|
+
|
|
+ if (rte->requiredPerms & ACL_SELECT)
|
|
+ perms |= SEPG_DB_TUPLE__SELECT;
|
|
+ if (rte->requiredPerms & ACL_UPDATE && !bms_is_empty(rte->modifiedCols))
|
|
+ perms |= SEPG_DB_TUPLE__UPDATE;
|
|
+ if (rte->requiredPerms & ACL_DELETE)
|
|
+ perms |= SEPG_DB_TUPLE__DELETE;
|
|
+
|
|
+ /*
|
|
+ * Special case in pg_largeobject
|
|
+ */
|
|
+ if (rte->relid == LargeObjectRelationId &&
|
|
+ bms_is_member(Anum_pg_largeobject_data
|
|
+ - FirstLowInvalidHeapAttributeNumber,
|
|
+ rte->selectedCols))
|
|
+ perms |= SEPG_DB_BLOB__READ;
|
|
+
|
|
+ return perms;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlHeapTupleInsert
|
|
+ * It assigns a default security label, if no explicit security labels
|
|
+ * were given. In addition, it also checks db_tuple:{insert} for the
|
|
+ * tuple newly inserted, when it invoked from user's query.
|
|
+ */
|
|
+void
|
|
+sepgsqlHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ /*
|
|
+ * Assign a default security label, if necessary
|
|
+ */
|
|
+ if (HeapTupleHasSecid(newtup) &&
|
|
+ !OidIsValid(HeapTupleGetSecid(newtup)))
|
|
+ sepgsqlSetDefaultSecid(rel, newtup);
|
|
+
|
|
+ /*
|
|
+ * It does not check permission for the new tuples
|
|
+ * inserted by system internal stuff using
|
|
+ * simple_heap_insert();
|
|
+ */
|
|
+ if (internal)
|
|
+ return;
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel),
|
|
+ newtup, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass, SEPG_DB_TUPLE__INSERT, NULL, true);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlHeapTupleUpdate
|
|
+ * It checks db_tuple:{relabelfrom relabelto} permission on
|
|
+ * the user queries. (Please note that it does not check
|
|
+ * system internal stuff via simple_heap_update)
|
|
+ */
|
|
+void
|
|
+sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup)
|
|
+{
|
|
+ Oid secid;
|
|
+ HeapTupleData oldtup;
|
|
+ Buffer oldbuf;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ /*
|
|
+ * heap_update() preserves the original security label
|
|
+ * of the given tuple, if no explicit security label
|
|
+ * is assigned on the newer version.
|
|
+ * In this case, db_tuple:{update} is already checked
|
|
+ * at the sepgsqlExecScan() hook, so we don't need to
|
|
+ * check anything more.
|
|
+ */
|
|
+ secid = HeapTupleGetSecid(newtup);
|
|
+ if (!OidIsValid(secid))
|
|
+ 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");
|
|
+
|
|
+ if (secid != HeapTupleGetSecid(&oldtup))
|
|
+ {
|
|
+ sepgsql_sid_t sid;
|
|
+ uint16 tclass;
|
|
+
|
|
+ /* db_tuple:{relabelfrom} for older security context */
|
|
+ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel),
|
|
+ &oldtup, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__RELABELFROM,
|
|
+ NULL, true);
|
|
+
|
|
+ /* db_tuple:{relabelto} for newer security label */
|
|
+ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel),
|
|
+ newtup, &tclass);
|
|
+ sepgsqlClientHasPerms(sid, tclass,
|
|
+ SEPG_DB_TUPLE__RELABELTO,
|
|
+ NULL, true);
|
|
+ }
|
|
+ ReleaseBuffer(oldbuf);
|
|
+}
|
|
diff --git a/src/backend/security/sepgsql/dummy.c b/src/backend/security/sepgsql/dummy.c
|
|
new file mode 100644
|
|
index 0000000..6df24d3
|
|
--- /dev/null
|
|
+++ b/src/backend/security/sepgsql/dummy.c
|
|
@@ -0,0 +1,79 @@
|
|
+/*
|
|
+ * src/backend/utils/sepgsql/dummy.c
|
|
+ * A set of stubs when SE-PostgreSQL is not activated
|
|
+ *
|
|
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
|
+ * Portions Copyright (c) 1994, Regents of the University of California
|
|
+ */
|
|
+#include "postgres.h"
|
|
+
|
|
+#include "security/sepgsql.h"
|
|
+
|
|
+static Datum
|
|
+unavailable_function(const char *fn_name)
|
|
+{
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_SELINUX_ERROR),
|
|
+ errmsg("function \"%s\" is not available", fn_name)));
|
|
+ PG_RETURN_VOID();
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_getcon(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ return unavailable_function(__FUNCTION__);
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_server_getcon(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ return unavailable_function(__FUNCTION__);
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_get_user(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ return unavailable_function(__FUNCTION__);
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_get_role(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ return unavailable_function(__FUNCTION__);
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_get_type(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ return unavailable_function(__FUNCTION__);
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_get_range(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ return unavailable_function(__FUNCTION__);
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_set_user(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ return unavailable_function(__FUNCTION__);
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_set_role(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ return unavailable_function(__FUNCTION__);
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_set_type(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ return unavailable_function(__FUNCTION__);
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_set_range(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ return unavailable_function(__FUNCTION__);
|
|
+}
|
|
diff --git a/src/backend/security/sepgsql/label.c b/src/backend/security/sepgsql/label.c
|
|
new file mode 100644
|
|
index 0000000..e91f8c9
|
|
--- /dev/null
|
|
+++ b/src/backend/security/sepgsql/label.c
|
|
@@ -0,0 +1,1213 @@
|
|
+/*
|
|
+ * src/backend/security/sepgsql/label.c
|
|
+ * SE-PostgreSQL security label management
|
|
+ *
|
|
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
|
+ * Portions Copyright (c) 1994, Regents of the University of California
|
|
+ */
|
|
+#include "postgres.h"
|
|
+
|
|
+#include "access/sysattr.h"
|
|
+#include "access/xact.h"
|
|
+#include "catalog/catalog.h"
|
|
+#include "catalog/pg_constraint.h"
|
|
+#include "catalog/heap.h"
|
|
+#include "catalog/indexing.h"
|
|
+#include "catalog/namespace.h"
|
|
+#include "catalog/pg_aggregate.h"
|
|
+#include "catalog/pg_amop.h"
|
|
+#include "catalog/pg_amproc.h"
|
|
+#include "catalog/pg_attrdef.h"
|
|
+#include "catalog/pg_attribute.h"
|
|
+#include "catalog/pg_auth_members.h"
|
|
+#include "catalog/pg_authid.h"
|
|
+#include "catalog/pg_cast.h"
|
|
+#include "catalog/pg_class.h"
|
|
+#include "catalog/pg_conversion.h"
|
|
+#include "catalog/pg_database.h"
|
|
+#include "catalog/pg_description.h"
|
|
+#include "catalog/pg_enum.h"
|
|
+#include "catalog/pg_foreign_data_wrapper.h"
|
|
+#include "catalog/pg_foreign_server.h"
|
|
+#include "catalog/pg_inherits.h"
|
|
+#include "catalog/pg_language.h"
|
|
+#include "catalog/pg_largeobject.h"
|
|
+#include "catalog/pg_largeobject_metadata.h"
|
|
+#include "catalog/pg_namespace.h"
|
|
+#include "catalog/pg_opclass.h"
|
|
+#include "catalog/pg_operator.h"
|
|
+#include "catalog/pg_opfamily.h"
|
|
+#include "catalog/pg_proc.h"
|
|
+#include "catalog/pg_rewrite.h"
|
|
+#include "catalog/pg_security.h"
|
|
+#include "catalog/pg_shdescription.h"
|
|
+#include "catalog/pg_statistic.h"
|
|
+#include "catalog/pg_tablespace.h"
|
|
+#include "catalog/pg_trigger.h"
|
|
+#include "catalog/pg_ts_config.h"
|
|
+#include "catalog/pg_ts_config_map.h"
|
|
+#include "catalog/pg_ts_dict.h"
|
|
+#include "catalog/pg_ts_parser.h"
|
|
+#include "catalog/pg_ts_template.h"
|
|
+#include "catalog/pg_type.h"
|
|
+#include "catalog/pg_user_mapping.h"
|
|
+#include "miscadmin.h"
|
|
+#include "nodes/makefuncs.h"
|
|
+#include "security/sepgsql.h"
|
|
+#include "storage/fd.h"
|
|
+#include "utils/fmgroids.h"
|
|
+#include "utils/lsyscache.h"
|
|
+#include "utils/syscache.h"
|
|
+#include "utils/tqual.h"
|
|
+
|
|
+/* GUC: to turn on/off row level controls in SE-PostgreSQL */
|
|
+bool sepostgresql_row_level;
|
|
+
|
|
+/* GUC parameter to turn on/off mcstrans */
|
|
+bool sepostgresql_mcstrans;
|
|
+
|
|
+/*
|
|
+ * sepgsqlTupleDescHasSecid
|
|
+ *
|
|
+ * returns a hint whether we should allocate a field to store
|
|
+ * security label on the given relation, or not.
|
|
+ */
|
|
+bool
|
|
+sepgsqlTupleDescHasSecid(Oid relid, char relkind)
|
|
+{
|
|
+ /*
|
|
+ * sepgsqlIsEnabled() is not available because it always returns
|
|
+ * false in bootstraping mode
|
|
+ */
|
|
+ if (sepostgresql_mode == SEPGSQL_MODE_DISABLED ||
|
|
+ is_selinux_enabled() < 1)
|
|
+ return false;
|
|
+
|
|
+ if (!OidIsValid(relid))
|
|
+ return sepostgresql_row_level; /* Target of SELECT INTO */
|
|
+
|
|
+ /* These system catalogs always have its secid */
|
|
+ if (relid == DatabaseRelationId ||
|
|
+ relid == NamespaceRelationId ||
|
|
+ relid == RelationRelationId ||
|
|
+ relid == AttributeRelationId ||
|
|
+ relid == ProcedureRelationId)
|
|
+ return true;
|
|
+
|
|
+ /* These system catalogs are an external attributes */
|
|
+ if (relid == AggregateRelationId ||
|
|
+ relid == AccessMethodOperatorRelationId ||
|
|
+ relid == AccessMethodProcedureRelationId ||
|
|
+ relid == AttrDefaultRelationId ||
|
|
+ relid == AuthMemRelationId ||
|
|
+ relid == ConstraintRelationId ||
|
|
+ relid == DescriptionRelationId ||
|
|
+ relid == EnumRelationId ||
|
|
+ relid == IndexRelationId ||
|
|
+ relid == InheritsRelationId ||
|
|
+ relid == LargeObjectRelationId ||
|
|
+ relid == RewriteRelationId ||
|
|
+ relid == SecurityRelationId ||
|
|
+ relid == SharedDescriptionRelationId ||
|
|
+ relid == StatisticRelationId ||
|
|
+ relid == TriggerRelationId)
|
|
+ return false;
|
|
+
|
|
+ return sepostgresql_row_level;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * defaultSecidWithXXXX
|
|
+ */
|
|
+static sepgsql_sid_t
|
|
+defaultSecidWithDatabase(Oid relOid, Oid datOid, uint16 tclass)
|
|
+{
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t datSid;
|
|
+
|
|
+ tuple = SearchSysCache(DATABASEOID,
|
|
+ ObjectIdGetDatum(datOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for database: %u", datOid);
|
|
+
|
|
+ datSid.relid = DatabaseRelationId;
|
|
+ datSid.secid = HeapTupleGetSecid(tuple);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ return sepgsqlClientCreateSecid(datSid, tclass, relOid);
|
|
+}
|
|
+
|
|
+static sepgsql_sid_t
|
|
+defaultSecidWithSchema(Oid relOid, Oid nspOid, uint16 tclass)
|
|
+{
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t nspSid;
|
|
+
|
|
+ tuple = SearchSysCache(NAMESPACEOID,
|
|
+ ObjectIdGetDatum(nspOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for schema: %u", nspOid);
|
|
+
|
|
+ nspSid.relid = NamespaceRelationId;
|
|
+ nspSid.secid = HeapTupleGetSecid(tuple);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ return sepgsqlClientCreateSecid(nspSid, tclass, relOid);
|
|
+}
|
|
+
|
|
+static sepgsql_sid_t
|
|
+defaultSecidWithTable(Oid relOid, Oid tblOid, uint16 tclass)
|
|
+{
|
|
+ HeapTuple tuple;
|
|
+ sepgsql_sid_t tblSid;
|
|
+
|
|
+ tuple = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(tblOid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ elog(ERROR, "cache lookup failed for relation: %u", tblOid);
|
|
+
|
|
+ tblSid.relid = RelationRelationId;
|
|
+ tblSid.secid = HeapTupleGetSecid(tuple);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ return sepgsqlClientCreateSecid(tblSid, tclass, relOid);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlGetDefaultDatabaseSecid
|
|
+ * It returns the default security label of a database object.
|
|
+ */
|
|
+sepgsql_sid_t
|
|
+sepgsqlGetDefaultDatabaseSecid(Oid source_database_oid)
|
|
+{
|
|
+ return defaultSecidWithDatabase(DatabaseRelationId,
|
|
+ source_database_oid,
|
|
+ SEPG_CLASS_DB_DATABASE);
|
|
+}
|
|
+
|
|
+sepgsql_sid_t
|
|
+sepgsqlGetDefaultSchemaSecid(Oid database_oid)
|
|
+{
|
|
+ return defaultSecidWithDatabase(NamespaceRelationId,
|
|
+ database_oid,
|
|
+ SEPG_CLASS_DB_SCHEMA);
|
|
+}
|
|
+
|
|
+sepgsql_sid_t
|
|
+sepgsqlGetDefaultTableSecid(Oid namespace_oid)
|
|
+{
|
|
+ return defaultSecidWithSchema(RelationRelationId,
|
|
+ namespace_oid,
|
|
+ SEPG_CLASS_DB_TABLE);
|
|
+}
|
|
+
|
|
+sepgsql_sid_t
|
|
+sepgsqlGetDefaultSequenceSecid(Oid namespace_oid)
|
|
+{
|
|
+ return defaultSecidWithSchema(RelationRelationId,
|
|
+ namespace_oid,
|
|
+ SEPG_CLASS_DB_SEQUENCE);
|
|
+}
|
|
+
|
|
+sepgsql_sid_t
|
|
+sepgsqlGetDefaultProcedureSecid(Oid namespace_oid)
|
|
+{
|
|
+ return defaultSecidWithSchema(ProcedureRelationId,
|
|
+ namespace_oid,
|
|
+ SEPG_CLASS_DB_PROCEDURE);
|
|
+}
|
|
+
|
|
+sepgsql_sid_t
|
|
+sepgsqlGetDefaultColumnSecid(Oid table_oid)
|
|
+{
|
|
+ return defaultSecidWithTable(AttributeRelationId,
|
|
+ table_oid,
|
|
+ SEPG_CLASS_DB_COLUMN);
|
|
+}
|
|
+
|
|
+sepgsql_sid_t
|
|
+sepgsqlGetDefaultTupleSecid(Oid table_oid)
|
|
+{
|
|
+ return defaultSecidWithTable(table_oid,
|
|
+ table_oid,
|
|
+ SEPG_CLASS_DB_TUPLE);
|
|
+}
|
|
+
|
|
+sepgsql_sid_t
|
|
+sepgsqlGetDefaultBlobSecid(Oid database_oid)
|
|
+{
|
|
+ return defaultSecidWithDatabase(LargeObjectMetadataRelationId,
|
|
+ MyDatabaseId,
|
|
+ SEPG_CLASS_DB_BLOB);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsqlSetDefaultSecid(Relation rel, HeapTuple tuple)
|
|
+{
|
|
+ sepgsql_sid_t newSid;
|
|
+ Oid relOid = RelationGetRelid(rel);
|
|
+ Oid nspOid, tblOid;
|
|
+ char relkind;
|
|
+
|
|
+ if (!HeapTupleHasSecid(tuple))
|
|
+ return;
|
|
+
|
|
+ /* initialize */
|
|
+ newSid.relid = relOid;
|
|
+ newSid.secid = InvalidOid;
|
|
+
|
|
+ switch (relOid)
|
|
+ {
|
|
+ case DatabaseRelationId:
|
|
+ /* should be never happen */
|
|
+ elog(WARNING, "bug? pg_database tuple without security label");
|
|
+ break;
|
|
+
|
|
+ case NamespaceRelationId:
|
|
+ newSid = sepgsqlGetDefaultSchemaSecid(MyDatabaseId);
|
|
+ break;
|
|
+
|
|
+ case RelationRelationId:
|
|
+ nspOid = ((Form_pg_class) GETSTRUCT(tuple))->relnamespace;
|
|
+ relkind = ((Form_pg_class) GETSTRUCT(tuple))->relkind;
|
|
+
|
|
+ switch (relkind)
|
|
+ {
|
|
+ case RELKIND_RELATION:
|
|
+ newSid = sepgsqlGetDefaultTableSecid(nspOid);
|
|
+ break;
|
|
+
|
|
+ case RELKIND_SEQUENCE:
|
|
+ newSid = sepgsqlGetDefaultSequenceSecid(nspOid);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ newSid = sepgsqlGetDefaultTupleSecid(relOid);
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case ProcedureRelationId:
|
|
+ nspOid = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace;
|
|
+ newSid = sepgsqlGetDefaultProcedureSecid(nspOid);
|
|
+ break;
|
|
+
|
|
+ case AttributeRelationId:
|
|
+ tblOid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid;
|
|
+ if (get_rel_relkind(tblOid) == RELKIND_RELATION)
|
|
+ newSid = sepgsqlGetDefaultColumnSecid(tblOid);
|
|
+ break;
|
|
+
|
|
+ case LargeObjectMetadataRelationId:
|
|
+ newSid = sepgsqlGetDefaultBlobSecid(MyDatabaseId);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ newSid = sepgsqlGetDefaultTupleSecid(relOid);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ HeapTupleSetSecid(tuple, newSid.secid);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlPostBootstrapingMode
|
|
+ *
|
|
+ * Assign initial security context
|
|
+ */
|
|
+static void
|
|
+sepgsqlInitialLabeling(Oid relOid, char *seclabels[])
|
|
+{
|
|
+ Relation rel;
|
|
+ HeapScanDesc scan;
|
|
+ HeapTuple tuple;
|
|
+ HeapTuple newtup;
|
|
+
|
|
+ rel = heap_open(relOid, RowExclusiveLock);
|
|
+
|
|
+ scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
|
|
+
|
|
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
|
+ {
|
|
+ Oid secid = InvalidOid;
|
|
+ Oid attrelid;
|
|
+ char relkind;
|
|
+
|
|
+ if (!HeapTupleHasSecid(tuple))
|
|
+ continue;
|
|
+
|
|
+ switch (relOid)
|
|
+ {
|
|
+ case DatabaseRelationId:
|
|
+ secid = securityRawSecLabelIn(relOid, seclabels[0]);
|
|
+ break;
|
|
+
|
|
+ case NamespaceRelationId:
|
|
+ secid = securityRawSecLabelIn(relOid, seclabels[1]);
|
|
+ break;
|
|
+
|
|
+ case RelationRelationId:
|
|
+ relkind = ((Form_pg_class) GETSTRUCT(tuple))->relkind;
|
|
+ switch (relkind)
|
|
+ {
|
|
+ case RELKIND_RELATION:
|
|
+ secid = securityRawSecLabelIn(relOid, seclabels[2]);
|
|
+ break;
|
|
+ case RELKIND_SEQUENCE:
|
|
+ secid = securityRawSecLabelIn(relOid, seclabels[3]);
|
|
+ break;
|
|
+ default:
|
|
+ secid = securityRawSecLabelIn(relOid, seclabels[6]);
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case AttributeRelationId:
|
|
+ attrelid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid;
|
|
+ if (get_rel_relkind(attrelid) == RELKIND_RELATION)
|
|
+ secid = securityRawSecLabelIn(relOid, seclabels[5]);
|
|
+ break;
|
|
+
|
|
+ case ProcedureRelationId:
|
|
+ secid = securityRawSecLabelIn(relOid, seclabels[4]);
|
|
+ break;
|
|
+
|
|
+ case LargeObjectMetadataRelationId:
|
|
+ secid = securityRawSecLabelIn(relOid, seclabels[7]);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ secid = securityRawSecLabelIn(relOid, seclabels[6]);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Inplace update
|
|
+ */
|
|
+ newtup = heap_copytuple(tuple);
|
|
+
|
|
+ HeapTupleSetSecid(newtup, secid);
|
|
+
|
|
+ heap_inplace_update(rel, newtup);
|
|
+ }
|
|
+ heap_endscan(scan);
|
|
+
|
|
+ heap_close(rel, RowExclusiveLock);
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsqlPostBootstrapingMode(void)
|
|
+{
|
|
+ Form_pg_class classForm;
|
|
+ Relation rel;
|
|
+ ScanKeyData skey;
|
|
+ HeapScanDesc scan;
|
|
+ HeapTuple tuple;
|
|
+ char *scontext;
|
|
+ char *seclabels[8];
|
|
+
|
|
+ /*
|
|
+ * sepgsqlIsEnabled() is not available because it always returns
|
|
+ * false in bootstraping mode
|
|
+ */
|
|
+ Assert(IsBootstrapProcessingMode());
|
|
+ if (sepostgresql_mode == SEPGSQL_MODE_DISABLED ||
|
|
+ is_selinux_enabled() < 1)
|
|
+ return;
|
|
+
|
|
+ /*
|
|
+ * Compute default initial security context
|
|
+ */
|
|
+ if (getprevcon_raw(&scontext) < 0)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INTERNAL_ERROR),
|
|
+ errmsg("could not obtain current context")));
|
|
+
|
|
+ seclabels[0] = sepgsqlComputeCreate(scontext, scontext,
|
|
+ SEPG_CLASS_DB_DATABASE);
|
|
+ seclabels[1] = sepgsqlComputeCreate(scontext, seclabels[0],
|
|
+ SEPG_CLASS_DB_SCHEMA);
|
|
+ seclabels[2] = sepgsqlComputeCreate(scontext, seclabels[1],
|
|
+ SEPG_CLASS_DB_TABLE);
|
|
+ seclabels[3] = sepgsqlComputeCreate(scontext, seclabels[1],
|
|
+ SEPG_CLASS_DB_SEQUENCE);
|
|
+ seclabels[4] = sepgsqlComputeCreate(scontext, seclabels[1],
|
|
+ SEPG_CLASS_DB_PROCEDURE);
|
|
+ seclabels[5] = sepgsqlComputeCreate(scontext, seclabels[2],
|
|
+ SEPG_CLASS_DB_COLUMN);
|
|
+ seclabels[6] = sepgsqlComputeCreate(scontext, seclabels[2],
|
|
+ SEPG_CLASS_DB_TUPLE);
|
|
+ seclabels[7] = sepgsqlComputeCreate(scontext, seclabels[0],
|
|
+ SEPG_CLASS_DB_BLOB);
|
|
+ /*
|
|
+ * Inplace update
|
|
+ */
|
|
+ StartTransactionCommand();
|
|
+
|
|
+ rel = heap_open(RelationRelationId, AccessShareLock);
|
|
+
|
|
+ ScanKeyInit(&skey,
|
|
+ Anum_pg_class_relkind,
|
|
+ BTEqualStrategyNumber, F_CHAREQ,
|
|
+ CharGetDatum(RELKIND_RELATION));
|
|
+
|
|
+ scan = heap_beginscan(rel, SnapshotNow, 1, &skey);
|
|
+
|
|
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
|
|
+ sepgsqlInitialLabeling(HeapTupleGetOid(tuple), seclabels);
|
|
+
|
|
+ heap_endscan(scan);
|
|
+
|
|
+ heap_close(rel, AccessShareLock);
|
|
+
|
|
+ CommitTransactionCommand();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlGetSysobjSecid
|
|
+ *
|
|
+ * It returns a pair of relid/secid for the given OID.
|
|
+ */
|
|
+static sepgsql_sid_t
|
|
+getSysobjSecidDirect(Oid classOid, Oid indexOid, Oid objectId, uint16 *tclass)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+ Relation rel;
|
|
+ HeapTuple tup;
|
|
+ ScanKeyData skey;
|
|
+ SysScanDesc scan;
|
|
+
|
|
+ rel = heap_open(CastRelationId, AccessShareLock);
|
|
+
|
|
+ ScanKeyInit(&skey,
|
|
+ ObjectIdAttributeNumber,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(objectId));
|
|
+
|
|
+ scan = systable_beginscan(rel, CastOidIndexId, true,
|
|
+ SnapshotNow, 1, &skey);
|
|
+ tup = systable_getnext(scan);
|
|
+
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "system object lookup failed for oid %u on relation %u",
|
|
+ objectId, classOid);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(classOid, tup, tclass);
|
|
+
|
|
+ systable_endscan(scan);
|
|
+
|
|
+ heap_close(rel, AccessShareLock);
|
|
+
|
|
+ return sid;
|
|
+}
|
|
+
|
|
+sepgsql_sid_t
|
|
+sepgsqlGetSysobjSecid(Oid classOid, Oid objectId, int32 objsubId, uint16 *tclass)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+ HeapTuple tup;
|
|
+
|
|
+ switch (classOid)
|
|
+ {
|
|
+ case AccessMethodRelationId:
|
|
+ tup = SearchSysCache(AMOID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for access method: %u", objectId);
|
|
+ break;
|
|
+
|
|
+ case AccessMethodOperatorRelationId:
|
|
+ return getSysobjSecidDirect(AccessMethodOperatorRelationId,
|
|
+ AccessMethodOperatorOidIndexId,
|
|
+ objectId, tclass);
|
|
+
|
|
+ case AccessMethodProcedureRelationId:
|
|
+ return getSysobjSecidDirect(AccessMethodProcedureRelationId,
|
|
+ AccessMethodProcedureOidIndexId,
|
|
+ objectId, tclass);
|
|
+
|
|
+ case AuthIdRelationId:
|
|
+ tup = SearchSysCache(AUTHOID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for role: %u", objectId);
|
|
+ break;
|
|
+
|
|
+ case CastRelationId:
|
|
+ return getSysobjSecidDirect(CastRelationId,
|
|
+ CastOidIndexId,
|
|
+ objectId, tclass);
|
|
+
|
|
+ case ConstraintRelationId:
|
|
+ tup = SearchSysCache(CONSTROID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for constraint: %u", objectId);
|
|
+ break;
|
|
+
|
|
+ case ConversionRelationId:
|
|
+ tup = SearchSysCache(CONVOID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for conversion: %u", objectId);
|
|
+ break;
|
|
+
|
|
+ case DatabaseRelationId:
|
|
+ tup = SearchSysCache(DATABASEOID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for database: %u", objectId);
|
|
+ break;
|
|
+
|
|
+ case ForeignDataWrapperRelationId:
|
|
+ tup = SearchSysCache(FOREIGNDATAWRAPPEROID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for FDW: %u", objectId);
|
|
+ break;
|
|
+
|
|
+ case ForeignServerRelationId:
|
|
+ tup = SearchSysCache(FOREIGNSERVEROID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for foreign server: %u", objectId);
|
|
+ break;
|
|
+
|
|
+ case LanguageRelationId:
|
|
+ tup = SearchSysCache(LANGOID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ break;
|
|
+
|
|
+ case LargeObjectRelationId:
|
|
+ case LargeObjectMetadataRelationId:
|
|
+ {
|
|
+ Relation rel;
|
|
+ ScanKeyData skey;
|
|
+ SysScanDesc scan;
|
|
+
|
|
+ rel = heap_open(LargeObjectMetadataRelationId, AccessShareLock);
|
|
+
|
|
+ ScanKeyInit(&skey,
|
|
+ ObjectIdAttributeNumber,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(objectId));
|
|
+
|
|
+ scan = systable_beginscan(rel, LargeObjectMetadataOidIndexId,
|
|
+ true, SnapshotNow, 1, &skey);
|
|
+
|
|
+ tup = systable_getnext(scan);
|
|
+
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "largeobject %u lookup failed", objectId);
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(classOid, tup, tclass);
|
|
+ systable_endscan(scan);
|
|
+
|
|
+ heap_close(rel, AccessShareLock);
|
|
+ }
|
|
+ return sid;
|
|
+
|
|
+ case RelationRelationId:
|
|
+ if (objsubId != 0)
|
|
+ {
|
|
+ classOid = AttributeRelationId;
|
|
+ tup = SearchSysCache(ATTNUM,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ Int16GetDatum(objsubId),
|
|
+ 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for attribute %d of relation %u",
|
|
+ objsubId, objectId);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ classOid = RelationRelationId;
|
|
+ tup = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for relation %u", objectId);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case NamespaceRelationId:
|
|
+ tup = SearchSysCache(NAMESPACEOID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for schema %u", objectId);
|
|
+ break;
|
|
+
|
|
+ case OperatorClassRelationId:
|
|
+ tup = SearchSysCache(CLAOID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for opclass %u", objectId);
|
|
+ break;
|
|
+
|
|
+ case OperatorFamilyRelationId:
|
|
+ tup = SearchSysCache(OPFAMILYOID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for opfamily %u", objectId);
|
|
+ break;
|
|
+
|
|
+ case OperatorRelationId:
|
|
+ tup = SearchSysCache(OPEROID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for operator %u", objectId);
|
|
+ break;
|
|
+
|
|
+ case ProcedureRelationId:
|
|
+ tup = SearchSysCache(PROCOID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for procedure %u", objectId);
|
|
+ break;
|
|
+
|
|
+ case RewriteRelationId:
|
|
+ return getSysobjSecidDirect(RewriteRelationId,
|
|
+ RewriteOidIndexId,
|
|
+ objectId, tclass);
|
|
+
|
|
+ case TableSpaceRelationId:
|
|
+ return getSysobjSecidDirect(TableSpaceRelationId,
|
|
+ TablespaceOidIndexId,
|
|
+ objectId, tclass);
|
|
+
|
|
+ case TriggerRelationId:
|
|
+ return getSysobjSecidDirect(TriggerRelationId,
|
|
+ TriggerOidIndexId,
|
|
+ objectId, tclass);
|
|
+
|
|
+ case TSConfigRelationId:
|
|
+ tup = SearchSysCache(TSCONFIGOID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for text search configuration %u", objectId);
|
|
+ break;
|
|
+
|
|
+ case TSDictionaryRelationId:
|
|
+ tup = SearchSysCache(TSDICTOID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for text search dictionary %u", objectId);
|
|
+ break;
|
|
+
|
|
+ case TSParserRelationId:
|
|
+ tup = SearchSysCache(TSPARSEROID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for text search parser %u", objectId);
|
|
+ break;
|
|
+
|
|
+ case TSTemplateRelationId:
|
|
+ tup = SearchSysCache(TSTEMPLATEOID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for text search template %u", objectId);
|
|
+ break;
|
|
+
|
|
+ case TypeRelationId:
|
|
+ tup = SearchSysCache(TYPEOID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for type %u", objectId);
|
|
+ break;
|
|
+
|
|
+ case UserMappingRelationId:
|
|
+ tup = SearchSysCache(USERMAPPINGOID,
|
|
+ ObjectIdGetDatum(objectId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tup))
|
|
+ elog(ERROR, "cache lookup failed for user mapping %u", objectId);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ elog(ERROR, "unexpected class OID: %u", classOid);
|
|
+ tup = NULL; /* for compiler quiet */
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ Assert(HeapTupleIsValid(tup));
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(classOid, tup, tclass);
|
|
+
|
|
+ ReleaseSysCache(tup);
|
|
+
|
|
+ return sid;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlGetTupleSecid
|
|
+ *
|
|
+ * It returns a pair of relid/secid for the given HeapTuple.
|
|
+ * A few system catalogs is handled as an attribute of other
|
|
+ * system objects.
|
|
+ * E.g) pg_attrdef is an attribute of a certain pg_attribute
|
|
+ */
|
|
+sepgsql_sid_t
|
|
+sepgsqlGetTupleSecid(Oid tableOid, HeapTuple tuple, uint16 *tclass)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+ HeapTuple exttup;
|
|
+ Oid extid;
|
|
+ Oid extcls;
|
|
+ AttrNumber extsub;
|
|
+
|
|
+ /* initialize (unlabeled security context) */
|
|
+ sid.relid = tableOid;
|
|
+ sid.secid = InvalidOid;
|
|
+ if (tclass)
|
|
+ *tclass = SEPG_CLASS_DB_TUPLE;
|
|
+
|
|
+ switch (tableOid)
|
|
+ {
|
|
+ case AggregateRelationId:
|
|
+ extid = ((Form_pg_aggregate) GETSTRUCT(tuple))->aggfnoid;
|
|
+ exttup = SearchSysCache(PROCOID,
|
|
+ ObjectIdGetDatum(extid),
|
|
+ 0, 0, 0);
|
|
+ if (HeapTupleIsValid(exttup))
|
|
+ {
|
|
+ sid = sepgsqlGetTupleSecid(ProcedureRelationId,
|
|
+ exttup, tclass);
|
|
+ ReleaseSysCache(exttup);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case AccessMethodOperatorRelationId:
|
|
+ extid = ((Form_pg_amop) GETSTRUCT(tuple))->amopfamily;
|
|
+ exttup = SearchSysCache(OPFAMILYOID,
|
|
+ ObjectIdGetDatum(extid),
|
|
+ 0, 0, 0);
|
|
+ if (HeapTupleIsValid(exttup))
|
|
+ {
|
|
+ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId,
|
|
+ exttup, tclass);
|
|
+ ReleaseSysCache(exttup);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case AccessMethodProcedureRelationId:
|
|
+ extid = ((Form_pg_amproc) GETSTRUCT(tuple))->amprocfamily;
|
|
+ exttup = SearchSysCache(OPFAMILYOID,
|
|
+ ObjectIdGetDatum(extid),
|
|
+ 0, 0, 0);
|
|
+ if (HeapTupleIsValid(exttup))
|
|
+ {
|
|
+ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId,
|
|
+ exttup, tclass);
|
|
+ ReleaseSysCache(exttup);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case AttrDefaultRelationId:
|
|
+ extid = ((Form_pg_attrdef) GETSTRUCT(tuple))->adrelid;
|
|
+ extsub = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum;
|
|
+ exttup = SearchSysCache(ATTNUM,
|
|
+ ObjectIdGetDatum(extid),
|
|
+ Int16GetDatum(extsub),
|
|
+ 0, 0);
|
|
+ if (HeapTupleIsValid(exttup))
|
|
+ {
|
|
+ sid = sepgsqlGetTupleSecid(AttributeRelationId,
|
|
+ exttup, tclass);
|
|
+ ReleaseSysCache(exttup);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case AttributeRelationId:
|
|
+ extid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid;
|
|
+ exttup = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(extid),
|
|
+ 0, 0, 0);
|
|
+ if (HeapTupleIsValid(exttup))
|
|
+ {
|
|
+ char relkind = ((Form_pg_class) GETSTRUCT(exttup))->relkind;
|
|
+
|
|
+ if (relkind == RELKIND_RELATION)
|
|
+ {
|
|
+ if (tclass)
|
|
+ *tclass = SEPG_CLASS_DB_COLUMN;
|
|
+ sid.secid = HeapTupleGetSecid(tuple);
|
|
+ }
|
|
+ else
|
|
+ sid = sepgsqlGetTupleSecid(RelationRelationId,
|
|
+ exttup, tclass);
|
|
+ ReleaseSysCache(exttup);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case AuthMemRelationId:
|
|
+ extid = ((Form_pg_auth_members) GETSTRUCT(tuple))->roleid;
|
|
+ exttup = SearchSysCache(AUTHOID,
|
|
+ ObjectIdGetDatum(extid),
|
|
+ 0, 0, 0);
|
|
+ if (HeapTupleIsValid(exttup))
|
|
+ {
|
|
+ sid = sepgsqlGetTupleSecid(AuthIdRelationId,
|
|
+ exttup, tclass);
|
|
+ ReleaseSysCache(exttup);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case ConstraintRelationId:
|
|
+ /* CHECK constraint is an attribute of the relation */
|
|
+ extid = ((Form_pg_constraint) GETSTRUCT(tuple))->conrelid;
|
|
+ if (OidIsValid(extid))
|
|
+ {
|
|
+ exttup = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(extid),
|
|
+ 0, 0, 0);
|
|
+ if (HeapTupleIsValid(exttup))
|
|
+ {
|
|
+ sid = sepgsqlGetTupleSecid(RelationRelationId,
|
|
+ exttup, tclass);
|
|
+ ReleaseSysCache(exttup);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ /* DOMAIN constraint is an attribute of the domain type */
|
|
+ extid = ((Form_pg_constraint) GETSTRUCT(tuple))->contypid;
|
|
+ if (OidIsValid(extid))
|
|
+ {
|
|
+ sid.relid = TypeRelationId;
|
|
+ exttup = SearchSysCache(TYPEOID,
|
|
+ ObjectIdGetDatum(extid),
|
|
+ 0, 0, 0);
|
|
+ if (HeapTupleIsValid(exttup))
|
|
+ {
|
|
+ sid = sepgsqlGetTupleSecid(TypeRelationId,
|
|
+ exttup, tclass);
|
|
+ ReleaseSysCache(exttup);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ /* Database's context for global assertion */
|
|
+ exttup = SearchSysCache(DATABASEOID,
|
|
+ ObjectIdGetDatum(MyDatabaseId),
|
|
+ 0, 0, 0);
|
|
+ if (HeapTupleIsValid(exttup))
|
|
+ {
|
|
+ sid = sepgsqlGetTupleSecid(DatabaseRelationId,
|
|
+ exttup, tclass);
|
|
+ ReleaseSysCache(exttup);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case DatabaseRelationId:
|
|
+ sid.secid = HeapTupleGetSecid(tuple);
|
|
+ if (tclass)
|
|
+ *tclass = SEPG_CLASS_DB_DATABASE;
|
|
+ break;
|
|
+
|
|
+ case DescriptionRelationId:
|
|
+ /* recursive call */
|
|
+ extid = ((Form_pg_description) GETSTRUCT(tuple))->objoid;
|
|
+ extcls = ((Form_pg_description) GETSTRUCT(tuple))->classoid;
|
|
+ return sepgsqlGetSysobjSecid(extcls, extid, 0, tclass);
|
|
+
|
|
+ case EnumRelationId:
|
|
+ extid = ((Form_pg_enum) GETSTRUCT(tuple))->enumtypid;
|
|
+ exttup = SearchSysCache(TYPEOID,
|
|
+ ObjectIdGetDatum(extid),
|
|
+ 0, 0, 0);
|
|
+ if (HeapTupleIsValid(exttup))
|
|
+ {
|
|
+ sid = sepgsqlGetTupleSecid(TypeRelationId,
|
|
+ exttup, tclass);
|
|
+ ReleaseSysCache(exttup);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case IndexRelationId:
|
|
+ extid = ((Form_pg_index) GETSTRUCT(tuple))->indrelid;
|
|
+ exttup = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(extid),
|
|
+ 0, 0, 0);
|
|
+ if (HeapTupleIsValid(exttup))
|
|
+ {
|
|
+ sid = sepgsqlGetTupleSecid(RelationRelationId,
|
|
+ exttup, tclass);
|
|
+ ReleaseSysCache(exttup);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case InheritsRelationId:
|
|
+ extid = ((Form_pg_inherits) GETSTRUCT(tuple))->inhrelid;
|
|
+ exttup = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(extid),
|
|
+ 0, 0, 0);
|
|
+ if (HeapTupleIsValid(exttup))
|
|
+ {
|
|
+ sid = sepgsqlGetTupleSecid(RelationRelationId,
|
|
+ exttup, tclass);
|
|
+ ReleaseSysCache(exttup);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case LargeObjectRelationId:
|
|
+ extid = ((Form_pg_largeobject) GETSTRUCT(tuple))->loid;
|
|
+ extcls = LargeObjectMetadataRelationId;
|
|
+ return sepgsqlGetSysobjSecid(extcls, extid, 0, tclass);
|
|
+
|
|
+ case LargeObjectMetadataRelationId:
|
|
+ sid.secid = HeapTupleGetSecid(tuple);
|
|
+ if (tclass)
|
|
+ *tclass = SEPG_CLASS_DB_BLOB;
|
|
+ break;
|
|
+
|
|
+ case NamespaceRelationId:
|
|
+ sid.secid = HeapTupleGetSecid(tuple);
|
|
+ if (tclass)
|
|
+ *tclass = SEPG_CLASS_DB_SCHEMA;
|
|
+ break;
|
|
+
|
|
+ case ProcedureRelationId:
|
|
+ sid.secid = HeapTupleGetSecid(tuple);
|
|
+ if (tclass)
|
|
+ *tclass = SEPG_CLASS_DB_PROCEDURE;
|
|
+ break;
|
|
+
|
|
+ case RelationRelationId:
|
|
+ sid.secid = HeapTupleGetSecid(tuple);
|
|
+ if (tclass)
|
|
+ {
|
|
+ char relkind = ((Form_pg_class) GETSTRUCT(tuple))->relkind;
|
|
+
|
|
+ switch (relkind)
|
|
+ {
|
|
+ case RELKIND_RELATION:
|
|
+ *tclass = SEPG_CLASS_DB_TABLE;
|
|
+ break;
|
|
+
|
|
+ case RELKIND_SEQUENCE:
|
|
+ *tclass = SEPG_CLASS_DB_SEQUENCE;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ *tclass = SEPG_CLASS_DB_TUPLE;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case RewriteRelationId:
|
|
+ extid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
|
|
+ exttup = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(extid),
|
|
+ 0, 0, 0);
|
|
+ if (HeapTupleIsValid(exttup))
|
|
+ {
|
|
+ sid = sepgsqlGetTupleSecid(RelationRelationId,
|
|
+ exttup, tclass);
|
|
+ ReleaseSysCache(exttup);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case SharedDescriptionRelationId:
|
|
+ /* recursive invocation */
|
|
+ extid = ((Form_pg_shdescription) GETSTRUCT(tuple))->objoid;
|
|
+ extcls = ((Form_pg_shdescription) GETSTRUCT(tuple))->classoid;
|
|
+ return sepgsqlGetSysobjSecid(extcls, extid, 0, tclass);
|
|
+
|
|
+ case StatisticRelationId:
|
|
+ extid = ((Form_pg_statistic) GETSTRUCT(tuple))->starelid;
|
|
+ extsub = ((Form_pg_statistic) GETSTRUCT(tuple))->staattnum;
|
|
+ exttup = SearchSysCache(ATTNUM,
|
|
+ ObjectIdGetDatum(extid),
|
|
+ Int16GetDatum(extsub),
|
|
+ 0, 0);
|
|
+ if (HeapTupleIsValid(exttup))
|
|
+ {
|
|
+ sid = sepgsqlGetTupleSecid(AttributeRelationId,
|
|
+ exttup, tclass);
|
|
+ ReleaseSysCache(exttup);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case TriggerRelationId:
|
|
+ extid = ((Form_pg_trigger) GETSTRUCT(tuple))->tgrelid;
|
|
+ exttup = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(extid),
|
|
+ 0, 0, 0);
|
|
+ if (HeapTupleIsValid(exttup))
|
|
+ {
|
|
+ sid = sepgsqlGetTupleSecid(RelationRelationId,
|
|
+ exttup, tclass);
|
|
+ ReleaseSysCache(exttup);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ case TSConfigMapRelationId:
|
|
+ extid = ((Form_pg_ts_config_map) GETSTRUCT(tuple))->mapcfg;
|
|
+ exttup = SearchSysCache(TSCONFIGOID,
|
|
+ ObjectIdGetDatum(extid),
|
|
+ 0, 0, 0);
|
|
+ if (HeapTupleIsValid(exttup))
|
|
+ {
|
|
+ sid = sepgsqlGetTupleSecid(TSConfigRelationId,
|
|
+ exttup, tclass);
|
|
+ ReleaseSysCache(exttup);
|
|
+ }
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ /* No external lookups (normal case) */
|
|
+ sid.secid = HeapTupleGetSecid(tuple);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return sid;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlRawSecLabelIn
|
|
+ * correctness checks for the given security context
|
|
+ */
|
|
+char *
|
|
+sepgsqlRawSecLabelIn(char *seclabel)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return seclabel;
|
|
+
|
|
+ if (!seclabel || security_check_context_raw(seclabel) < 0)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INVALID_SECURITY_LABEL),
|
|
+ errmsg("Invalid security context: \"%s\"", seclabel)));
|
|
+
|
|
+ return seclabel;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlRawSecLabelOut
|
|
+ * correctness checks for the given security context,
|
|
+ * and replace it if invalid security context
|
|
+ */
|
|
+char *
|
|
+sepgsqlRawSecLabelOut(char *seclabel)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return seclabel;
|
|
+
|
|
+ if (!seclabel || security_check_context_raw(seclabel) < 0)
|
|
+ {
|
|
+ security_context_t unlabeledcon;
|
|
+
|
|
+ if (security_get_initial_context_raw("unlabeled",
|
|
+ &unlabeledcon) < 0)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INTERNAL_ERROR),
|
|
+ errmsg("Unabled to get unlabeled security context")));
|
|
+ PG_TRY();
|
|
+ {
|
|
+ seclabel = pstrdup(unlabeledcon);
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(unlabeledcon);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+ freecon(unlabeledcon);
|
|
+ }
|
|
+ return seclabel;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlTransSecLabelIn
|
|
+ * sepgsqlTransSecLabelOut
|
|
+ * translation between human-readable and raw format
|
|
+ */
|
|
+char *
|
|
+sepgsqlTransSecLabelIn(char *seclabel)
|
|
+{
|
|
+ security_context_t rawlabel;
|
|
+ security_context_t result;
|
|
+
|
|
+ if (!sepgsqlIsEnabled() ||
|
|
+ !sepostgresql_mcstrans)
|
|
+ return seclabel;
|
|
+
|
|
+ if (selinux_trans_to_raw_context(seclabel, &rawlabel) < 0)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INTERNAL_ERROR),
|
|
+ errmsg("SELinux: failed to translate \"%s\"", seclabel)));
|
|
+ PG_TRY();
|
|
+ {
|
|
+ result = pstrdup(rawlabel);
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(rawlabel);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+ freecon(rawlabel);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+char *
|
|
+sepgsqlTransSecLabelOut(char *seclabel)
|
|
+{
|
|
+ security_context_t translabel;
|
|
+ security_context_t result;
|
|
+
|
|
+ if (!sepgsqlIsEnabled() ||
|
|
+ !sepostgresql_mcstrans)
|
|
+ return seclabel;
|
|
+
|
|
+ if (selinux_raw_to_trans_context(seclabel, &translabel) < 0)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INTERNAL_ERROR),
|
|
+ errmsg("SELinux: failed to translate \"%s\"", seclabel)));
|
|
+ PG_TRY();
|
|
+ {
|
|
+ result = pstrdup(translabel);
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(translabel);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+ freecon(translabel);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+char *
|
|
+sepgsqlSysattSecLabelOut(Oid relid, HeapTuple tuple)
|
|
+{
|
|
+ sepgsql_sid_t sid;
|
|
+
|
|
+ sid = sepgsqlGetTupleSecid(relid, tuple, NULL);
|
|
+
|
|
+ return securityTransSecLabelOut(sid.relid, sid.secid);
|
|
+}
|
|
diff --git a/src/backend/security/sepgsql/misc.c b/src/backend/security/sepgsql/misc.c
|
|
new file mode 100644
|
|
index 0000000..2f7c466
|
|
--- /dev/null
|
|
+++ b/src/backend/security/sepgsql/misc.c
|
|
@@ -0,0 +1,214 @@
|
|
+/*
|
|
+ * src/backend/security/sepgsql/misc.c
|
|
+ * Miscellaneous facilities in SE-PostgreSQL
|
|
+ *
|
|
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
|
+ * Portions Copyright (c) 1994, Regents of the University of California
|
|
+ */
|
|
+#include "postgres.h"
|
|
+#include "libpq/libpq-be.h"
|
|
+#include "miscadmin.h"
|
|
+#include "security/sepgsql.h"
|
|
+#include "utils/builtins.h"
|
|
+
|
|
+/*
|
|
+ * SE-PostgreSQL specific functions
|
|
+ */
|
|
+Datum
|
|
+sepgsql_getcon(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ security_context_t context;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("SELinux: disabled now")));
|
|
+
|
|
+ context = sepgsqlGetClientLabel();
|
|
+ context = sepgsqlTransSecLabelOut(context);
|
|
+ return CStringGetTextDatum(context);
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_server_getcon(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ char *context;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("SELinux: disabled now")));
|
|
+
|
|
+ context = sepgsqlGetServerLabel();
|
|
+ context = sepgsqlTransSecLabelOut(context);
|
|
+
|
|
+ return CStringGetTextDatum(context);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsql_(get|set)_(user|role|type|range)
|
|
+ * get/set a component of security context.
|
|
+ */
|
|
+static void
|
|
+parse_security_context(security_context_t context,
|
|
+ char **user, char **role, char **type, char **range)
|
|
+{
|
|
+ security_context_t raw_context;
|
|
+ char *tok;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
+ errmsg("SELinux: disabled now")));
|
|
+
|
|
+ if (selinux_trans_to_raw_context(context, &raw_context) < 0)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INTERNAL_ERROR),
|
|
+ errmsg("could not translate mls label: %s", context)));
|
|
+
|
|
+ PG_TRY();
|
|
+ {
|
|
+ tok = strtok(raw_context, ":");
|
|
+ if (user)
|
|
+ *user = (!tok ? NULL : pstrdup(tok));
|
|
+
|
|
+ tok = strtok(NULL, ":");
|
|
+ if (role)
|
|
+ *role = (!tok ? NULL : pstrdup(tok));
|
|
+
|
|
+ tok = strtok(NULL, ":");
|
|
+ if (type)
|
|
+ *type = (!tok ? NULL : pstrdup(tok));
|
|
+
|
|
+ tok = strtok(NULL, "\0");
|
|
+ if (range)
|
|
+ *range = (!tok ? NULL : pstrdup(tok));
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(raw_context);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+ freecon(raw_context);
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_get_user(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0));
|
|
+ char *user;
|
|
+
|
|
+ parse_security_context(context, &user, NULL, NULL, NULL);
|
|
+ if (!user)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INVALID_SECURITY_LABEL),
|
|
+ errmsg("could not extract user of \"%s\"", context)));
|
|
+
|
|
+ PG_RETURN_TEXT_P(CStringGetTextDatum(user));
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_get_role(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0));
|
|
+ char *role;
|
|
+
|
|
+ parse_security_context(context, NULL, &role, NULL, NULL);
|
|
+ if (!role)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INVALID_SECURITY_LABEL),
|
|
+ errmsg("could not extract role of \"%s\"", context)));
|
|
+
|
|
+ PG_RETURN_TEXT_P(CStringGetTextDatum(role));
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_get_type(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0));
|
|
+ char *type;
|
|
+
|
|
+ parse_security_context(context, NULL, NULL, &type, NULL);
|
|
+ if (!type)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INVALID_SECURITY_LABEL),
|
|
+ errmsg("could not extract type of \"%s\"", context)));
|
|
+
|
|
+ PG_RETURN_TEXT_P(CStringGetTextDatum(type));
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_get_range(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0));
|
|
+ char *range;
|
|
+
|
|
+ parse_security_context(context, NULL, NULL, NULL, &range);
|
|
+ if (!range)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INVALID_SECURITY_LABEL),
|
|
+ errmsg("could not extract range of \"%s\"", context)));
|
|
+
|
|
+ PG_RETURN_TEXT_P(CStringGetTextDatum(range));
|
|
+}
|
|
+
|
|
+static Datum
|
|
+sepgsql_set_common(char *context,
|
|
+ char *user, char *role, char *type, char *range)
|
|
+{
|
|
+ StringInfoData newcon;
|
|
+
|
|
+ parse_security_context(context,
|
|
+ !user ? &user : NULL,
|
|
+ !role ? &role : NULL,
|
|
+ !type ? &type : NULL,
|
|
+ !range ? &range : NULL);
|
|
+ if (!user || !role || !type)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INVALID_SECURITY_LABEL),
|
|
+ errmsg("invalid security context: \"%s\"", context)));
|
|
+
|
|
+ initStringInfo(&newcon);
|
|
+ appendStringInfo(&newcon, "%s:%s:%s", user, role, type);
|
|
+ if (range)
|
|
+ appendStringInfo(&newcon, ":%s", range);
|
|
+
|
|
+ return CStringGetTextDatum(sepgsqlTransSecLabelOut(newcon.data));
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_set_user(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0));
|
|
+ char *user = TextDatumGetCString(PG_GETARG_TEXT_P(1));
|
|
+
|
|
+ return sepgsql_set_common(context, user, NULL, NULL, NULL);
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_set_role(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0));
|
|
+ char *role = TextDatumGetCString(PG_GETARG_TEXT_P(1));
|
|
+
|
|
+ return sepgsql_set_common(context, NULL, role, NULL, NULL);
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_set_type(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0));
|
|
+ char *type = TextDatumGetCString(PG_GETARG_TEXT_P(1));
|
|
+
|
|
+ return sepgsql_set_common(context, NULL, NULL, type, NULL);
|
|
+}
|
|
+
|
|
+Datum
|
|
+sepgsql_set_range(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0));
|
|
+ char *range = TextDatumGetCString(PG_GETARG_TEXT_P(1));
|
|
+
|
|
+ return sepgsql_set_common(context, NULL, NULL, NULL, range);
|
|
+}
|
|
diff --git a/src/backend/security/sepgsql/perms.c b/src/backend/security/sepgsql/perms.c
|
|
new file mode 100644
|
|
index 0000000..5943f40
|
|
--- /dev/null
|
|
+++ b/src/backend/security/sepgsql/perms.c
|
|
@@ -0,0 +1,597 @@
|
|
+/*
|
|
+ * src/backend/utils/sepgsql/perms.c
|
|
+ * SE-PostgreSQL permission checks
|
|
+ *
|
|
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
|
+ * Portions Copyright (c) 1994, Regents of the University of California
|
|
+ */
|
|
+#include "postgres.h"
|
|
+
|
|
+#include "catalog/pg_database.h"
|
|
+#include "catalog/pg_proc.h"
|
|
+#include "catalog/pg_largeobject.h"
|
|
+#include "catalog/pg_namespace.h"
|
|
+#include "catalog/pg_type.h"
|
|
+#include "miscadmin.h"
|
|
+#include "security/sepgsql.h"
|
|
+#include "utils/lsyscache.h"
|
|
+
|
|
+/*
|
|
+ * Dynamic object class/permissions mapping
|
|
+ *
|
|
+ * SELinux exports the list of object classes and permissions at
|
|
+ * /selinux/class. The libselinux provides an interface to translate
|
|
+ * between their names and codes.
|
|
+ */
|
|
+static struct
|
|
+{
|
|
+ const char *class_name;
|
|
+ security_class_t class_code;
|
|
+ struct
|
|
+ {
|
|
+ const char *perm_name;
|
|
+ access_vector_t perm_code;
|
|
+ } av[sizeof(access_vector_t) * 8];
|
|
+} selinux_catalog[] = {
|
|
+ {
|
|
+ "process", SEPG_CLASS_PROCESS,
|
|
+ {
|
|
+ {"translation", SEPG_PROCESS__TRANSITION },
|
|
+ {NULL, 0}
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "file", SEPG_CLASS_FILE,
|
|
+ {
|
|
+ {"read", SEPG_FILE__READ },
|
|
+ {"write", SEPG_FILE__WRITE },
|
|
+ {"create", SEPG_FILE__CREATE },
|
|
+ {"getattr", SEPG_FILE__GETATTR },
|
|
+ {NULL, 0}
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "dir", SEPG_CLASS_DIR,
|
|
+ {
|
|
+ {"read", SEPG_DIR__READ },
|
|
+ {"write", SEPG_DIR__WRITE },
|
|
+ {"create", SEPG_DIR__CREATE },
|
|
+ {"getattr", SEPG_DIR__GETATTR },
|
|
+ {NULL,0}
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "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 },
|
|
+ {NULL,0}
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "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 },
|
|
+ {NULL,0}
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "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 },
|
|
+ {NULL,0}
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "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 },
|
|
+ {NULL,0}
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "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 },
|
|
+ {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 },
|
|
+ { "install_module", SEPG_DB_DATABASE__INSTALL_MODULE },
|
|
+ { "load_module", SEPG_DB_DATABASE__LOAD_MODULE },
|
|
+ { "superuser", SEPG_DB_DATABASE__SUPERUSER },
|
|
+ { 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 },
|
|
+ { "reference", SEPG_DB_TABLE__REFERENCE },
|
|
+ { 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 },
|
|
+ { "untrusted", SEPG_DB_PROCEDURE__UNTRUSTED },
|
|
+ { 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 },
|
|
+ { "reference", SEPG_DB_COLUMN__REFERENCE },
|
|
+ { 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 },
|
|
+ }
|
|
+ }
|
|
+};
|
|
+
|
|
+/*
|
|
+ * sepgsqlTransToExternalClass
|
|
+ * It translate the given class code (defined as SEPGCLASS_(class)) into
|
|
+ * external code which is necessary to communicate in-kernel SELinux
|
|
+ */
|
|
+extern security_class_t
|
|
+sepgsqlTransToExternalClass(uint16 tclass)
|
|
+{
|
|
+ Assert(tclass < SEPG_CLASS_MAX);
|
|
+
|
|
+ return string_to_security_class(selinux_catalog[tclass].class_name);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlTransToInternalPerms
|
|
+ * It translate the given permission masks into internal representation
|
|
+ * defined as SEPG_(class)_(permission).
|
|
+ */
|
|
+extern void
|
|
+sepgsqlTransToInternalPerms(security_class_t tclass, struct av_decision *avd)
|
|
+{
|
|
+ security_class_t tclass_ex;
|
|
+ struct av_decision i_avd;
|
|
+ int i, deny_unknown;
|
|
+
|
|
+ Assert(tclass < SEPG_CLASS_MAX);
|
|
+
|
|
+ memset(&i_avd, 0, sizeof(struct av_decision));
|
|
+
|
|
+ deny_unknown = security_deny_unknown();
|
|
+
|
|
+ tclass_ex = sepgsqlTransToExternalClass(tclass);
|
|
+ for (i=0; selinux_catalog[tclass].av[i].perm_name; i++)
|
|
+ {
|
|
+ const char *perm_name = selinux_catalog[tclass].av[i].perm_name;
|
|
+ access_vector_t perm_code = selinux_catalog[tclass].av[i].perm_code;
|
|
+ access_vector_t perm_code_ex;
|
|
+
|
|
+ perm_code_ex = string_to_av_perm(tclass_ex, perm_name);
|
|
+ if (!perm_code_ex)
|
|
+ {
|
|
+ /* fill up undefined permission */
|
|
+ if (!deny_unknown)
|
|
+ i_avd.allowed |= perm_code;
|
|
+ i_avd.decided |= perm_code;
|
|
+ i_avd.auditdeny |= perm_code;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (avd->allowed & perm_code_ex)
|
|
+ i_avd.allowed |= perm_code;
|
|
+ if (avd->decided & perm_code_ex)
|
|
+ i_avd.decided |= perm_code;
|
|
+ if (avd->auditallow & perm_code_ex)
|
|
+ i_avd.auditallow |= perm_code;
|
|
+ if (avd->auditdeny & perm_code_ex)
|
|
+ i_avd.auditdeny |= perm_code;
|
|
+ }
|
|
+
|
|
+ avd->allowed = i_avd.allowed;
|
|
+ avd->decided = i_avd.decided;
|
|
+ avd->auditallow = i_avd.auditallow;
|
|
+ avd->auditdeny = i_avd.auditdeny;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlGetClassString
|
|
+ * sepgsqlGetPermissionString
|
|
+ * It returns text representation of object classes/permissions
|
|
+ */
|
|
+const char *
|
|
+sepgsqlGetClassString(uint16 tclass)
|
|
+{
|
|
+ Assert(tclass < SEPG_CLASS_MAX);
|
|
+
|
|
+ return selinux_catalog[tclass].class_name;
|
|
+}
|
|
+
|
|
+const char *
|
|
+sepgsqlGetPermString(uint16 tclass, uint32 permission)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ Assert(tclass < SEPG_CLASS_MAX);
|
|
+
|
|
+ for (i=0; selinux_catalog[tclass].av[i].perm_name; i++)
|
|
+ {
|
|
+ if (selinux_catalog[tclass].av[i].perm_code == permission)
|
|
+ return selinux_catalog[tclass].av[i].perm_name;
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+#if 0
|
|
+
|
|
+/*
|
|
+ * sepgsqlFileObjectClass
|
|
+ *
|
|
+ * It returns proper object class of filesystem object already opened.
|
|
+ * It is necessary to check privileges voluntarily.
|
|
+ */
|
|
+uint16
|
|
+sepgsqlFileObjectClass(int fdesc)
|
|
+{
|
|
+ struct stat stbuf;
|
|
+
|
|
+ if (fstat(fdesc, &stbuf) != 0)
|
|
+ ereport(ERROR,
|
|
+ (errcode_for_file_access(),
|
|
+ errmsg("could not stat file descriptor: %d", fdesc)));
|
|
+
|
|
+ if (S_ISDIR(stbuf.st_mode))
|
|
+ return SEPG_CLASS_DIR;
|
|
+ else if (S_ISCHR(stbuf.st_mode))
|
|
+ return SEPG_CLASS_CHR_FILE;
|
|
+ else if (S_ISBLK(stbuf.st_mode))
|
|
+ return SEPG_CLASS_BLK_FILE;
|
|
+ else if (S_ISFIFO(stbuf.st_mode))
|
|
+ return SEPG_CLASS_FIFO_FILE;
|
|
+ else if (S_ISLNK(stbuf.st_mode))
|
|
+ return SEPG_CLASS_LNK_FILE;
|
|
+ else if (S_ISSOCK(stbuf.st_mode))
|
|
+ return SEPG_CLASS_SOCK_FILE;
|
|
+
|
|
+ return SEPG_CLASS_FILE;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlTupleObjectClass
|
|
+ *
|
|
+ * It returns correct object class of given tuple
|
|
+ */
|
|
+uint16
|
|
+sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple)
|
|
+{
|
|
+ Form_pg_class clsForm;
|
|
+ Form_pg_attribute attForm;
|
|
+
|
|
+ switch (relid)
|
|
+ {
|
|
+ case DatabaseRelationId:
|
|
+ return SEPG_CLASS_DB_DATABASE;
|
|
+
|
|
+ case NamespaceRelationId:
|
|
+ return SEPG_CLASS_DB_SCHEMA;
|
|
+
|
|
+ case RelationRelationId:
|
|
+ clsForm = (Form_pg_class) GETSTRUCT(tuple);
|
|
+ if (clsForm->relkind == RELKIND_RELATION)
|
|
+ return SEPG_CLASS_DB_TABLE;
|
|
+ if (clsForm->relkind == RELKIND_SEQUENCE)
|
|
+ return SEPG_CLASS_DB_SEQUENCE;
|
|
+ break;
|
|
+
|
|
+ case AttributeRelationId:
|
|
+ attForm = (Form_pg_attribute) GETSTRUCT(tuple);
|
|
+ if (IsBootstrapProcessingMode() &&
|
|
+ (attForm->attrelid == TypeRelationId ||
|
|
+ attForm->attrelid == ProcedureRelationId ||
|
|
+ attForm->attrelid == AttributeRelationId ||
|
|
+ attForm->attrelid == RelationRelationId))
|
|
+ return SEPG_CLASS_DB_COLUMN;
|
|
+
|
|
+ if (get_rel_relkind(attForm->attrelid) == RELKIND_RELATION)
|
|
+ return SEPG_CLASS_DB_COLUMN;
|
|
+ break;
|
|
+
|
|
+ case ProcedureRelationId:
|
|
+ return SEPG_CLASS_DB_PROCEDURE;
|
|
+
|
|
+ case LargeObjectRelationId:
|
|
+ return SEPG_CLASS_DB_BLOB;
|
|
+ }
|
|
+ return SEPG_CLASS_DB_TUPLE;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlTupleNamespace
|
|
+ *
|
|
+ * It returns an OID of the namespace, if the given system object is
|
|
+ * deployed under a certain namespace.
|
|
+ */
|
|
+Oid
|
|
+sepgsqlTupleNamespace(Oid relOid, HeapTuple tuple)
|
|
+{
|
|
+ Oid nspOid;
|
|
+
|
|
+ switch (relOid)
|
|
+ {
|
|
+ case RelationRelationId:
|
|
+ nspOid = ((Form_pg_class) GETSTRUCT(tuple))->relnamespace;
|
|
+ break;
|
|
+
|
|
+ case ConstraintRelationId:
|
|
+ nspOid = ((Form_pg_constraint) GETSTRUCT(tuple))->connamespace;
|
|
+ break;
|
|
+
|
|
+ case ConversionRelationId:
|
|
+ nspOid = ((Form_pg_conversion) GETSTRUCT(tuple))->connamespace;
|
|
+ break;
|
|
+
|
|
+ case OperatorClassRelationId:
|
|
+ nspOid = ((Form_pg_opclass) GETSTRUCT(tuple))->opcnamespace;
|
|
+ break;
|
|
+
|
|
+ case OperatorRelationId:
|
|
+ nspOid = ((Form_pg_operator) GETSTRUCT(tuple))->oprnamespace;
|
|
+ break;
|
|
+
|
|
+ case OperatorFamilyRelationId:
|
|
+ nspOid = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfnamespace;
|
|
+ break;
|
|
+
|
|
+ case ProcedureRelationId:
|
|
+ nspOid = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace;
|
|
+ break;
|
|
+
|
|
+ case TSConfigRelationId:
|
|
+ nspOid = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgnamespace;
|
|
+ break;
|
|
+
|
|
+ case TSDictionaryRelationId:
|
|
+ nspOid = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictnamespace;
|
|
+ break;
|
|
+
|
|
+ case TSParserRelationId:
|
|
+ nspOid = ((Form_pg_ts_parser) GETSTRUCT(tuple))->prsnamespace;
|
|
+ break;
|
|
+
|
|
+ case TSTemplateRelationId:
|
|
+ nspOid = ((Form_pg_ts_template) GETSTRUCT(tuple))->tmplnamespace;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ /* no specific namespace */
|
|
+ nspOid = InvalidOid;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return nspOid;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlTupleAuditName
|
|
+ *
|
|
+ * It returns an OID of the namespace, if the given system object is
|
|
+ * deployed under a certain namespace.
|
|
+ */
|
|
+void
|
|
+sepgsqlTupleAuditName(Oid relid, HeapTuple tuple, char *auname_buf)
|
|
+{
|
|
+ char *name;
|
|
+ Oid extid;
|
|
+
|
|
+ switch (relid)
|
|
+ {
|
|
+ case AccessMethodRelationId:
|
|
+ name = NameStr(((Form_pg_am) GETSTRUCT(tuple))->amname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ case AttributeRelationId:
|
|
+ name = NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname);
|
|
+ extid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid;
|
|
+ sprintf(audit_name, "%s.%s", name, extid);
|
|
+ return;
|
|
+
|
|
+ case AuthIdRelationId:
|
|
+ name = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ case ConversionRelationId:
|
|
+ name = NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ case DatabaseRelationId:
|
|
+ name = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ case ForeignDataWrapperRelationId:
|
|
+ name = NameStr(((Form_pg_foreign_data_wrapper) GETSTRUCT(tuple))->fdwname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ case ForeignServerRelationId:
|
|
+ name = NameStr(((Form_pg_foreign_server) GETSTRUCT(tuple))->srvname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ case LanguageRelationId:
|
|
+ name = NameStr(((Form_pg_language) GETSTRUCT(tuple))->lanname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ case NamespaceRelationId:
|
|
+ name = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ case OperatorClassRelationId:
|
|
+ name = NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ case OperatorRelationId:
|
|
+ name = NameStr(((Form_pg_operator) GETSTRUCT(tuple))->oprname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ case OperatorFamilyRelationId:
|
|
+ name = NameStr(((Form_pg_opfamily) GETSTRUCT(tuple))->opfname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ case ProcedureRelationId:
|
|
+ name = NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ case RelationRelationId:
|
|
+ name = NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ case TableSpaceRelationId:
|
|
+ name = NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ case TSConfigRelationId:
|
|
+ name = NameStr(((Form_pg_ts_config) GETSTRUCT(tuple))->cfgname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ case TSDictionaryRelationId:
|
|
+ name = NameStr(((Form_pg_ts_dict) GETSTRUCT(tuple))->dictname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ case TSParserRelationId:
|
|
+ name = NameStr(((Form_pg_ts_parser) GETSTRUCT(tuple))->prsname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ case TSTemplateRelationId:
|
|
+ name = NameStr(((Form_pg_templace) GETSTRUCT(tuple))->tmplname);
|
|
+ strncpy(auname_buf, name, NAMEDATALEN);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ /* no auditable name */
|
|
+ auname_buf[0] = '\0';
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+#endif
|
|
diff --git a/src/backend/security/sepgsql/policy/Makefile b/src/backend/security/sepgsql/policy/Makefile
|
|
new file mode 100644
|
|
index 0000000..fc71b0c
|
|
--- /dev/null
|
|
+++ b/src/backend/security/sepgsql/policy/Makefile
|
|
@@ -0,0 +1,28 @@
|
|
+#
|
|
+# Makefile for SE-PostgreSQL security policy module
|
|
+#
|
|
+top_builddir = ../../../../..
|
|
+include $(top_builddir)/src/Makefile.global
|
|
+
|
|
+POLICY_BASEDIR := $(DESTDIR)/usr/share/selinux
|
|
+POLICY_MAKEFILE := $(POLICY_BASEDIR)/devel/Makefile
|
|
+POLICY_INSTDIR := $(POLICY_BASEDIR)/packages
|
|
+PREFIX_RULE := "s/%%__prefix__%%/$(shell echo $(prefix)|sed 's/\//\\\//g')/g"
|
|
+BINDIR_RULE := "s/%%__bindir__%%/$(shell echo $(bindir)|sed 's/\//\\\//g')/g"
|
|
+LIBDIR_RULE := "s/%%__libdir__%%/$(shell echo $(pkglibdir)|sed 's/\//\\\//g')/g"
|
|
+
|
|
+all: sepostgresql-devel.pp
|
|
+
|
|
+install: all
|
|
+ test -d $(POLICY_INSTDIR) || mkdir -p $(POLICY_INSTDIR)
|
|
+ install -p -m 0644 sepostgresql-devel.pp $(POLICY_INSTDIR)
|
|
+
|
|
+sepostgresql-devel.pp: sepostgresql-devel.te sepostgresql-devel.fc
|
|
+ $(MAKE) -f $(POLICY_MAKEFILE)
|
|
+
|
|
+sepostgresql-devel.fc: sepostgresql-devel.fc.template
|
|
+ cat $< | sed -e $(PREFIX_RULE) -e $(BINDIR_RULE) -e $(LIBDIR_RULE) > $@
|
|
+
|
|
+clean:
|
|
+ $(MAKE) -f $(POLICY_MAKEFILE) clean
|
|
+ rm -f *.fc
|
|
diff --git a/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template b/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template
|
|
new file mode 100644
|
|
index 0000000..380ada4
|
|
--- /dev/null
|
|
+++ b/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template
|
|
@@ -0,0 +1,12 @@
|
|
+#
|
|
+# SE-PostgreSQL install path
|
|
+#
|
|
+%%__prefix__%%(/.*)? -- gen_context(system_u:object_r:usr_t,s0)
|
|
+
|
|
+%%__bindir__%%/(se)?postgres -- gen_context(system_u:object_r:postgresql_exec_t,s0)
|
|
+%%__bindir__%%/(se)?pg_ctl -- gen_context(system_u:object_r:initrc_exec_t,s0)
|
|
+%%__bindir__%%/initdb(\.sepgsql)? -- gen_context(system_u:object_r:postgresql_exec_t,s0)
|
|
+%%__bindir__%%(/.*)? -- gen_context(system_u:object_r:bin_t,s0)
|
|
+
|
|
+%%__libdir__%%(/.*)? -- gen_context(system_u:object_r:lib_t,s0)
|
|
+
|
|
diff --git a/src/backend/security/sepgsql/policy/sepostgresql-devel.te b/src/backend/security/sepgsql/policy/sepostgresql-devel.te
|
|
new file mode 100644
|
|
index 0000000..4b960a8
|
|
--- /dev/null
|
|
+++ b/src/backend/security/sepgsql/policy/sepostgresql-devel.te
|
|
@@ -0,0 +1,123 @@
|
|
+policy_module(sepostgresql-devel, 3.29)
|
|
+
|
|
+gen_require(`
|
|
+ class db_database all_db_database_perms;
|
|
+ class db_table all_db_table_perms;
|
|
+ class db_procedure all_db_procedure_perms;
|
|
+ class db_column all_db_column_perms;
|
|
+ class db_tuple all_db_tuple_perms;
|
|
+ class db_blob all_db_blob_perms;
|
|
+
|
|
+ attribute sepgsql_client_type;
|
|
+ attribute sepgsql_unconfined_type;
|
|
+
|
|
+ attribute sepgsql_database_type;
|
|
+ attribute sepgsql_table_type;
|
|
+ attribute sepgsql_sysobj_table_type;
|
|
+ attribute sepgsql_procedure_type;
|
|
+ attribute sepgsql_blob_type;
|
|
+ attribute sepgsql_module_type;
|
|
+
|
|
+ # for regression test
|
|
+ type bin_t;
|
|
+ type user_home_t;
|
|
+ type sepgsql_trusted_proc_exec_t;
|
|
+
|
|
+ attribute tmpfile;
|
|
+')
|
|
+
|
|
+#################################
|
|
+#
|
|
+# Domain for Testcases
|
|
+#
|
|
+
|
|
+role sepgsql_test_r;
|
|
+
|
|
+userdom_unpriv_user_template(sepgsql_test)
|
|
+postgresql_role(sepgsql_test_r, sepgsql_test_t)
|
|
+
|
|
+allow sepgsql_test_t tmpfile : dir search_dir_perms;
|
|
+allow sepgsql_test_t tmpfile : file rw_file_perms;
|
|
+
|
|
+optional_policy(`
|
|
+ term_write_all_terms(sepgsql_test_t)
|
|
+')
|
|
+
|
|
+optional_policy(`
|
|
+ gen_require(`
|
|
+ type unconfined_t;
|
|
+ role unconfined_r;
|
|
+ ')
|
|
+
|
|
+ tunable_policy(`sepgsql_regression_test_mode',`
|
|
+ allow unconfined_t sepgsql_test_t : process transition;
|
|
+ ')
|
|
+ unconfined_rw_pipes(sepgsql_test_t)
|
|
+ role unconfined_r types sepgsql_test_t;
|
|
+ role unconfined_r types sepgsql_trusted_proc_t;
|
|
+')
|
|
+
|
|
+#################################
|
|
+#
|
|
+# SE-PostgreSQL Declarations
|
|
+#
|
|
+
|
|
+## <desc>
|
|
+## <p>
|
|
+## Allow to generate auditallow logs
|
|
+## </p>
|
|
+## </desc>
|
|
+gen_tunable(sepgsql_enable_auditallow, false)
|
|
+
|
|
+## <desc>
|
|
+## <p>
|
|
+## Allow to generate auditdeny logs
|
|
+## </p>
|
|
+## </desc>
|
|
+gen_tunable(sepgsql_enable_auditdeny, true)
|
|
+
|
|
+## <desc>
|
|
+## <p>
|
|
+## Allow widespread permissions for regression test
|
|
+## Don't set TRUE on operation phase
|
|
+## </p>
|
|
+## </desc>
|
|
+gen_tunable(sepgsql_regression_test_mode, false)
|
|
+
|
|
+########################################
|
|
+#
|
|
+# SE-PostgreSQL audit switch for debugging
|
|
+#
|
|
+tunable_policy(`sepgsql_enable_auditallow',`
|
|
+ auditallow domain sepgsql_database_type : db_database *;
|
|
+ auditallow domain sepgsql_table_type : db_table *;
|
|
+ auditallow domain sepgsql_table_type : db_column *;
|
|
+ auditallow domain sepgsql_table_type : db_tuple { relabelfrom relabelto };
|
|
+ auditallow domain sepgsql_sysobj_table_type : db_tuple *;
|
|
+ auditallow domain sepgsql_procedure_type : db_procedure *;
|
|
+ auditallow domain sepgsql_blob_type : db_blob *;
|
|
+ auditallow domain sepgsql_module_type : db_database { install_module };
|
|
+ auditallow sepgsql_database_type sepgsql_module_type : db_database { load_module };
|
|
+')
|
|
+
|
|
+tunable_policy(`! sepgsql_enable_auditdeny',`
|
|
+ dontaudit domain sepgsql_database_type : db_database *;
|
|
+ dontaudit domain sepgsql_table_type : db_table *;
|
|
+ dontaudit domain sepgsql_table_type : db_column *;
|
|
+ dontaudit domain sepgsql_table_type : db_tuple { relabelfrom relabelto };
|
|
+ dontaudit domain sepgsql_sysobj_table_type : db_tuple *;
|
|
+ dontaudit domain sepgsql_procedure_type : db_procedure *;
|
|
+ dontaudit domain sepgsql_blob_type : db_blob *;
|
|
+ dontaudit domain sepgsql_module_type : db_database { install_module };
|
|
+ dontaudit sepgsql_database_type sepgsql_module_type : db_database { load_module };
|
|
+')
|
|
+
|
|
+########################################
|
|
+#
|
|
+# SE-PostgreSQL regression test mode switch
|
|
+#
|
|
+tunable_policy(`sepgsql_regression_test_mode',`
|
|
+ allow sepgsql_client_type user_home_t : db_database { install_module };
|
|
+ allow sepgsql_unconfined_type user_home_t : db_database { install_module };
|
|
+ allow sepgsql_database_type user_home_t : db_database { load_module };
|
|
+')
|
|
diff --git a/src/backend/security/sepgsql/selinux.c b/src/backend/security/sepgsql/selinux.c
|
|
new file mode 100644
|
|
index 0000000..16f50e0
|
|
--- /dev/null
|
|
+++ b/src/backend/security/sepgsql/selinux.c
|
|
@@ -0,0 +1,1305 @@
|
|
+/*
|
|
+ * src/backend/security/sepgsql/selinux.c
|
|
+ * Routines to communicate with SELinux.
|
|
+ *
|
|
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
|
+ * Portions Copyright (c) 1994, Regents of the University of California
|
|
+ */
|
|
+#include "postgres.h"
|
|
+
|
|
+#include "access/hash.h"
|
|
+#include "access/xact.h"
|
|
+#include "catalog/pg_security.h"
|
|
+#include "lib/stringinfo.h"
|
|
+#include "libpq/libpq-be.h"
|
|
+#include "libpq/pqsignal.h"
|
|
+#include "miscadmin.h"
|
|
+#include "security/sepgsql.h"
|
|
+#include "storage/fd.h"
|
|
+#include "utils/builtins.h"
|
|
+#include "utils/memutils.h"
|
|
+
|
|
+#include <unistd.h>
|
|
+#include <selinux/selinux.h>
|
|
+#include <selinux/avc.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, 0}
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "file", SEPG_CLASS_FILE,
|
|
+ {
|
|
+ {"read", SEPG_FILE__READ },
|
|
+ {"write", SEPG_FILE__WRITE },
|
|
+ {"create", SEPG_FILE__CREATE },
|
|
+ {"getattr", SEPG_FILE__GETATTR },
|
|
+ {NULL, 0}
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "dir", SEPG_CLASS_DIR,
|
|
+ {
|
|
+ {"read", SEPG_DIR__READ },
|
|
+ {"write", SEPG_DIR__WRITE },
|
|
+ {"create", SEPG_DIR__CREATE },
|
|
+ {"getattr", SEPG_DIR__GETATTR },
|
|
+ {NULL,0}
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "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 },
|
|
+ {NULL,0}
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "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 },
|
|
+ {NULL,0}
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "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 },
|
|
+ {NULL,0}
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "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 },
|
|
+ {NULL,0}
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "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 },
|
|
+ {NULL, 0UL }
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "db_database", SEPG_CLASS_DB_DATABASE,
|
|
+ {
|
|
+ { "create", SEPG_DB_DATABASE__CREATE },
|
|
+ { "drop", SEPG_DB_DATABASE__DROP },
|
|
+ { "getattr", SEPG_DB_DATABASE__GETATTR },
|
|
+ { "setattr", SEPG_DB_DATABASE__SETATTR },
|
|
+ { "relabelfrom", SEPG_DB_DATABASE__RELABELFROM },
|
|
+ { "relabelto", SEPG_DB_DATABASE__RELABELTO },
|
|
+ { "access", SEPG_DB_DATABASE__ACCESS },
|
|
+ { "load_module", SEPG_DB_DATABASE__LOAD_MODULE },
|
|
+ { NULL, 0UL },
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "db_schema", SEPG_CLASS_DB_SCHEMA,
|
|
+ {
|
|
+ { "create", SEPG_DB_SCHEMA__CREATE },
|
|
+ { "drop", SEPG_DB_SCHEMA__DROP },
|
|
+ { "getattr", SEPG_DB_SCHEMA__GETATTR },
|
|
+ { "setattr", SEPG_DB_SCHEMA__SETATTR },
|
|
+ { "relabelfrom", SEPG_DB_SCHEMA__RELABELFROM },
|
|
+ { "relabelto", SEPG_DB_SCHEMA__RELABELTO },
|
|
+ { "search", SEPG_DB_SCHEMA__SEARCH },
|
|
+ { "add_name", SEPG_DB_SCHEMA__ADD_NAME },
|
|
+ { "remove_name", SEPG_DB_SCHEMA__REMOVE_NAME },
|
|
+ { NULL, 0UL },
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "db_table", SEPG_CLASS_DB_TABLE,
|
|
+ {
|
|
+ { "create", SEPG_DB_TABLE__CREATE },
|
|
+ { "drop", SEPG_DB_TABLE__DROP },
|
|
+ { "getattr", SEPG_DB_TABLE__GETATTR },
|
|
+ { "setattr", SEPG_DB_TABLE__SETATTR },
|
|
+ { "relabelfrom", SEPG_DB_TABLE__RELABELFROM },
|
|
+ { "relabelto", SEPG_DB_TABLE__RELABELTO },
|
|
+ { "select", SEPG_DB_TABLE__SELECT },
|
|
+ { "update", SEPG_DB_TABLE__UPDATE },
|
|
+ { "insert", SEPG_DB_TABLE__INSERT },
|
|
+ { "delete", SEPG_DB_TABLE__DELETE },
|
|
+ { "lock", SEPG_DB_TABLE__LOCK },
|
|
+ { NULL, 0UL },
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ "db_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 },
|
|
+ { "usage", SEPG_DB_VIEW__USAGE },
|
|
+ { 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 },
|
|
+ }
|
|
+ }
|
|
+};
|
|
+
|
|
+/*
|
|
+ * 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;
|
|
+
|
|
+/*
|
|
+ * userspace access vector cache
|
|
+ *
|
|
+ * It enables to cache access control decisions in userspace, and minimize
|
|
+ * the number of system call invocations.
|
|
+ */
|
|
+static MemoryContext AvcMemCtx = NULL;
|
|
+
|
|
+#define AVC_HASH_NUM_SLOTS 256
|
|
+#define AVC_HASH_NUM_NODES 180
|
|
+
|
|
+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;
|
|
+
|
|
+/*
|
|
+ * selinux_state
|
|
+ *
|
|
+ * It is deployed on the shared memory region, to show the system
|
|
+ * state of SELinux and its security policy.
|
|
+ *
|
|
+ * The selinux_state->version should be checked prior to avc accesses.
|
|
+ * If it does not match with the local avc_version, it means that
|
|
+ * system security policy was reloaded or system state (enforcing
|
|
+ * or permissive) was changed.
|
|
+ *
|
|
+ * The state monitoring worker process receives messages from the
|
|
+ * kernel using libselinux, and it updates the selinux_state.
|
|
+ */
|
|
+struct
|
|
+{
|
|
+ int version;
|
|
+
|
|
+ bool enforcing;
|
|
+} *selinux_state = NULL;
|
|
+
|
|
+/*
|
|
+ * sepgsqlShmemSize
|
|
+ *
|
|
+ * It returns required size for shared memory segment
|
|
+ */
|
|
+Size
|
|
+sepgsqlShmemSize(void)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return 0;
|
|
+
|
|
+ return sizeof(*selinux_state);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlShmemInit
|
|
+ *
|
|
+ * It attaches shared memory segment.
|
|
+ */
|
|
+static void
|
|
+sepgsqlShmemInit(void)
|
|
+{
|
|
+ bool found;
|
|
+
|
|
+ selinux_state = ShmemInitStruct("SELinux system state",
|
|
+ sepgsqlShmemSize(), &found);
|
|
+ if (!found)
|
|
+ {
|
|
+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE);
|
|
+
|
|
+ selinux_state->version = 0;
|
|
+ selinux_state->enforcing = (security_getenforce() > 0);
|
|
+
|
|
+ LWLockRelease(SepgsqlAvcLock);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlIsEnabled
|
|
+ * sepgsqlIsEnabledBootstrap
|
|
+ *
|
|
+ * If it returns true, SE-PgSQL is enabled. Otherwise, it is disabled.
|
|
+ */
|
|
+bool
|
|
+sepgsqlIsEnabledBootstrap(void)
|
|
+{
|
|
+ static int enabled = -1;
|
|
+
|
|
+ /*
|
|
+ * If sepostgresql = off, it is always disabled.
|
|
+ */
|
|
+ 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;
|
|
+}
|
|
+
|
|
+bool
|
|
+sepgsqlIsEnabled(void)
|
|
+{
|
|
+ /*
|
|
+ * SE-PgSQL is not ready in bootstraping mode,
|
|
+ * except for initial labeling process
|
|
+ */
|
|
+ if (IsBootstrapProcessingMode())
|
|
+ return false;
|
|
+
|
|
+ return sepgsqlIsEnabledBootstrap();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlGetEnforce
|
|
+ *
|
|
+ * 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
|
|
+sepgsqlGetEnforce(void)
|
|
+{
|
|
+ if (sepostgresql_mode == SEPGSQL_MODE_DEFAULT)
|
|
+ {
|
|
+ bool rc;
|
|
+
|
|
+ LWLockAcquire(SepgsqlAvcLock, LW_SHARED);
|
|
+ rc = selinux_state->enforcing;
|
|
+ LWLockRelease(SepgsqlAvcLock);
|
|
+
|
|
+ return rc;
|
|
+ }
|
|
+ else if (sepostgresql_mode == SEPGSQL_MODE_ENFORCING)
|
|
+ return true;
|
|
+
|
|
+ return false;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlShowMode
|
|
+ *
|
|
+ * It returns the current performing mode ('selinux_support')
|
|
+ * in human readable form.
|
|
+ */
|
|
+char *
|
|
+sepgsqlShowMode(void)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return "disabled";
|
|
+
|
|
+ if (!sepgsqlGetEnforce())
|
|
+ return "permissive";
|
|
+
|
|
+ return "enforcing";
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlGetClientLabel
|
|
+ * sepgsqlSetClientLabel
|
|
+ * sepgsqlGetServerLabel
|
|
+ */
|
|
+static char *clientLabel = NULL;
|
|
+
|
|
+char *
|
|
+sepgsqlGetClientLabel(void)
|
|
+{
|
|
+ if (clientLabel)
|
|
+ return clientLabel;
|
|
+
|
|
+ if (!MyProcPort)
|
|
+ {
|
|
+ /*
|
|
+ * When this server process was launched in single-user mode,
|
|
+ * it does not have any client socket, and the server process also
|
|
+ * performs as a client in same time. So, we apply a security context
|
|
+ * of the current process as a client's one.
|
|
+ * The getcon_raw(3) is an libselinux API to obtain security context
|
|
+ * of the current process in raw format.
|
|
+ */
|
|
+ if (getprevcon_raw(&clientLabel) < 0)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INTERNAL_ERROR),
|
|
+ errmsg("could not get server's security context")));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /*
|
|
+ * Otherwise, SE-PgSQL obtains the security context of the client
|
|
+ * process using getpeercon(3). It is an API of SELinux to obtain
|
|
+ * the security context of the peer process for the given file
|
|
+ * descriptor of the client socket.
|
|
+ * For example, a process labeled as "system_u:system_r:httpd_t:s0"
|
|
+ * (which is typically apache/httpd) connect to the PgSQL server,
|
|
+ * getpeercon_raw() in server side returns the security context
|
|
+ * in client side.
|
|
+ * If MyProcPort->sock came from unix domain socket, we don't need
|
|
+ * any special configuration. OS handles them correctly.
|
|
+ * If it is tcp/ip socket, either labeled ipsec or static fallback
|
|
+ * context should be configured.
|
|
+ * The labeled ipsec is a feature to deliver the security context
|
|
+ * of remote peer processes with an enhancement of key exchange
|
|
+ * server (racoon). If SELinux is also available in the client host
|
|
+ * also, it is the most preferable option.
|
|
+ * The static fallback context is a feature to assign an alternative
|
|
+ * security context based on the source address and network device
|
|
+ * in usage. It can be applied, even if Windows is run on the client.
|
|
+ */
|
|
+ if (getpeercon_raw(MyProcPort->sock, &clientLabel) < 0)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INTERNAL_ERROR),
|
|
+ errmsg("could not get client's security context")));
|
|
+ }
|
|
+ return clientLabel;
|
|
+}
|
|
+
|
|
+char *
|
|
+sepgsqlSetClientLabel(char *new_label)
|
|
+{
|
|
+ char *old_label = clientLabel;
|
|
+ avc_page *new_page;
|
|
+ int i, length;
|
|
+
|
|
+ /*
|
|
+ * (1) Set new security context
|
|
+ */
|
|
+ clientLabel = new_label;
|
|
+
|
|
+ /*
|
|
+ * (2) Switch current AVC page
|
|
+ */
|
|
+ if (current_page)
|
|
+ {
|
|
+ new_page = current_page;
|
|
+ do {
|
|
+ if (strcmp(new_page->scontext, new_label) == 0)
|
|
+ {
|
|
+ current_page = new_page;
|
|
+ return old_label;
|
|
+ }
|
|
+ new_page = new_page->next;
|
|
+ } while (new_page != current_page);
|
|
+ }
|
|
+
|
|
+ /* Not found, create a new avc_page */
|
|
+ length = sizeof(avc_page) + strlen(new_label);
|
|
+ new_page = MemoryContextAllocZero(AvcMemCtx, length);
|
|
+
|
|
+ strcpy(new_page->scontext, new_label);
|
|
+ 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;
|
|
+
|
|
+ /* return old label */
|
|
+ return old_label;
|
|
+}
|
|
+
|
|
+char *
|
|
+sepgsqlGetServerLabel(void)
|
|
+{
|
|
+ static char *serverLabel = NULL;
|
|
+
|
|
+ if (!serverLabel)
|
|
+ {
|
|
+ if (getcon_raw(&serverLabel) < 0)
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INTERNAL_ERROR),
|
|
+ errmsg("could not get server's security context")));
|
|
+ }
|
|
+ return serverLabel;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlAuditLog
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+static void
|
|
+sepgsqlAuditLog(bool denied, char *scontext, char *tcontext,
|
|
+ uint16 tclass, uint32 audited, const char *audit_name)
|
|
+{
|
|
+ //static int auditfd = -2;
|
|
+ StringInfoData buf;
|
|
+ const char *tclass_name;
|
|
+ const char *perm_name;
|
|
+ int i;
|
|
+
|
|
+ /*
|
|
+ * translation of security contexts to human readable format,
|
|
+ * if sepgsql_mcstrans is turned on.
|
|
+ */
|
|
+ scontext = sepgsqlTransSecLabelOut(scontext);
|
|
+ tcontext = sepgsqlTransSecLabelOut(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, " }");
|
|
+
|
|
+ appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s",
|
|
+ scontext, tcontext, tclass_name);
|
|
+ if (audit_name)
|
|
+ appendStringInfo(&buf, " name=%s", audit_name);
|
|
+
|
|
+ ereport(LOG,
|
|
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
|
|
+ errmsg("SELinux: %s %s",
|
|
+ (denied ? "denied" : "allowed"), buf.data)));
|
|
+}
|
|
+
|
|
+/*
|
|
+ * computePermsInternal
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+static void
|
|
+computePermsInternal(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);
|
|
+ Assert(tclass == selinux_catalog[tclass].class_code);
|
|
+
|
|
+ tclass_name = selinux_catalog[tclass].class_name;
|
|
+ tclass_ex = string_to_security_class(tclass_name);
|
|
+
|
|
+ if (tclass_ex == 0)
|
|
+ {
|
|
+ /*
|
|
+ * If the current security policy does not support permissions
|
|
+ * corresponding to database objects, we fill up them with dummy
|
|
+ * data.
|
|
+ * If security_deny_unknown() returns positive value, undefined
|
|
+ * permissions should be denied. Otherwise, allowed
|
|
+ */
|
|
+ avd->allowed = (deny_unknown > 0 ? 0 : ~0UL);
|
|
+ avd->auditallow = 0UL;
|
|
+ avd->auditdeny = ~0UL;
|
|
+ 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;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlComputePerms
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+extern bool
|
|
+sepgsqlComputePerms(char *scontext, char *tcontext,
|
|
+ uint16 tclass, uint32 required,
|
|
+ const char *audit_name, bool abort)
|
|
+{
|
|
+ struct av_decision avd;
|
|
+ uint32 denied;
|
|
+ uint32 audited;
|
|
+
|
|
+ computePermsInternal(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;
|
|
+ audited = denied ? (denied & avd.auditdeny)
|
|
+ : (required & avd.auditallow);
|
|
+
|
|
+ if (audited && sepostgresql_mode != SEPGSQL_MODE_INTERNAL)
|
|
+ {
|
|
+ sepgsqlAuditLog(!!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 ||
|
|
+ !sepgsqlGetEnforce() ||
|
|
+ (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;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlComputeCreate
|
|
+ *
|
|
+ * 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 *
|
|
+sepgsqlComputeCreate(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);
|
|
+ Assert(tclass == selinux_catalog[tclass].class_code);
|
|
+
|
|
+ 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;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlAvcReset
|
|
+ *
|
|
+ * Invalidate all the cached access control decision
|
|
+ */
|
|
+static void
|
|
+sepgsqlAvcReset(void)
|
|
+{
|
|
+ Assert(AvcMemCtx != NULL);
|
|
+
|
|
+ MemoryContextReset(AvcMemCtx);
|
|
+
|
|
+ current_page = NULL;
|
|
+
|
|
+ sepgsqlSetClientLabel(sepgsqlGetClientLabel());
|
|
+}
|
|
+
|
|
+static void
|
|
+sepgsqlAvcResetOnAbort(XactEvent event, void *arg)
|
|
+{
|
|
+ if (event == XACT_EVENT_ABORT)
|
|
+ sepgsqlAvcReset();
|
|
+}
|
|
+
|
|
+static void
|
|
+sepgsqlAvcResetOnSubAbort(SubXactEvent event, SubTransactionId mySubid,
|
|
+ SubTransactionId parentSubid, void *arg)
|
|
+{
|
|
+ if (event == SUBXACT_EVENT_ABORT_SUB)
|
|
+ sepgsqlAvcReset();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlAvcCheckValid
|
|
+ *
|
|
+ * It checks whether the current AVC pages are valid, or not.
|
|
+ */
|
|
+static bool
|
|
+sepgsqlAvcCheckValid(void)
|
|
+{
|
|
+ bool result = true;
|
|
+
|
|
+ LWLockAcquire(SepgsqlAvcLock, LW_SHARED);
|
|
+ if (avc_version != selinux_state->version)
|
|
+ {
|
|
+ sepgsqlAvcReset();
|
|
+
|
|
+ /* Copy the current version to local */
|
|
+ avc_version = selinux_state->version;
|
|
+
|
|
+ result = false;
|
|
+ }
|
|
+ LWLockRelease(SepgsqlAvcLock);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlAvcReclaim
|
|
+ *
|
|
+ * It wipes recently unused AVC entries, if necessary.
|
|
+ */
|
|
+static void
|
|
+sepgsqlAvcReclaim(avc_page *page)
|
|
+{
|
|
+ ListCell *l;
|
|
+ avc_datum *cache;
|
|
+
|
|
+ while (page->avc_count > AVC_HASH_NUM_NODES - 10)
|
|
+ {
|
|
+ foreach (l, page->slot[page->lru_hint])
|
|
+ {
|
|
+ cache = lfirst(l);
|
|
+
|
|
+ if (cache->hot_cache)
|
|
+ cache->hot_cache = false;
|
|
+ else
|
|
+ {
|
|
+ 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;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlAvcMakeEntry
|
|
+ *
|
|
+ * It makes a new avc entry, and insert it to the given page.
|
|
+ */
|
|
+#define avc_hash_key(trelid, tsecid, tclass, nrelid) \
|
|
+ (hash_uint32((trelid) ^ (tsecid) ^ ((tclass) << 3) ^ (nrelid)))
|
|
+
|
|
+static avc_datum *
|
|
+sepgsqlAvcMakeEntry(avc_page *page, sepgsql_sid_t tsid, uint16 tclass, Oid nrelid)
|
|
+{
|
|
+ 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 = securityRawSecLabelOut(tsid.relid, tsid.secid);
|
|
+ ncontext = sepgsqlComputeCreate(scontext, tcontext, tclass);
|
|
+
|
|
+ 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;
|
|
+ cache->nsid.relid = nrelid;
|
|
+
|
|
+ if (OidIsValid(nrelid))
|
|
+ cache->nsid.secid = securityRawSecLabelIn(nrelid, ncontext);
|
|
+ else
|
|
+ cache->nsid.secid = InvalidOid;
|
|
+
|
|
+ if (!OidIsValid(nrelid))
|
|
+ {
|
|
+ struct av_decision avd;
|
|
+
|
|
+ computePermsInternal(scontext, tcontext, tclass, &avd);
|
|
+ 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)
|
|
+ sepgsqlAvcReclaim(page);
|
|
+
|
|
+ page->slot[index] = lcons(cache, page->slot[index]);
|
|
+ page->avc_count++;
|
|
+
|
|
+ MemoryContextSwitchTo(oldctx);
|
|
+
|
|
+ return cache;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlAvcLookup
|
|
+ *
|
|
+ * It lookups required AVC entry
|
|
+ */
|
|
+static avc_datum *
|
|
+sepgsqlAvcLookup(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;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlClientHasPerms
|
|
+ *
|
|
+ * It checks client's privileges on the given object using avc.
|
|
+ */
|
|
+bool
|
|
+sepgsqlClientHasPerms(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 = sepgsqlAvcLookup(current_page, tsid, tclass, InvalidOid);
|
|
+ if (!cache)
|
|
+ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass, InvalidOid);
|
|
+ } while (!sepgsqlAvcCheckValid());
|
|
+
|
|
+ denied = required & ~cache->allowed;
|
|
+ audited = denied ? (denied & cache->auditdeny)
|
|
+ : (required & cache->auditallow);
|
|
+ if (audited)
|
|
+ {
|
|
+ sepgsqlAuditLog(!!denied,
|
|
+ current_page->scontext,
|
|
+ securityRawSecLabelOut(tsid.relid, tsid.secid),
|
|
+ cache->tclass, audited, audit_name);
|
|
+ }
|
|
+
|
|
+ if (denied)
|
|
+ {
|
|
+ if (!sepgsqlGetEnforce() || 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;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlClientCreateSecid
|
|
+ * sepgsqlClientCreateLabel
|
|
+ */
|
|
+sepgsql_sid_t
|
|
+sepgsqlClientCreateSecid(sepgsql_sid_t tsid, uint16 tclass, Oid nrelid)
|
|
+{
|
|
+ avc_datum *cache;
|
|
+
|
|
+ do {
|
|
+ cache = sepgsqlAvcLookup(current_page, tsid, tclass, nrelid);
|
|
+ if (!cache)
|
|
+ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass, nrelid);
|
|
+ } while (!sepgsqlAvcCheckValid());
|
|
+
|
|
+ return cache->nsid;
|
|
+}
|
|
+
|
|
+security_context_t
|
|
+sepgsqlClientCreateLabel(sepgsql_sid_t tsid, uint16 tclass)
|
|
+{
|
|
+ avc_datum *cache;
|
|
+
|
|
+ do {
|
|
+ cache = sepgsqlAvcLookup(current_page, tsid, tclass, InvalidOid);
|
|
+ if (!cache)
|
|
+ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass, InvalidOid);
|
|
+ } while (!sepgsqlAvcCheckValid());
|
|
+
|
|
+ return cache->ncontext;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * SELinux state monitoring process
|
|
+ *
|
|
+ * This process is forked from postmaster to monitor the state of SELinux.
|
|
+ * SELinux can make a notifier message to userspace object manager via
|
|
+ * netlink socket. When it receives the message, it updates selinux_state
|
|
+ * structure assigned on shared memory region to make any instance reset
|
|
+ * its AVC soon.
|
|
+ */
|
|
+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)
|
|
+{
|
|
+ /* switch enforcing/permissive */
|
|
+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE);
|
|
+ selinux_state->enforcing = (enforce ? true : false);
|
|
+ selinux_state->version++;
|
|
+ LWLockRelease(SepgsqlAvcLock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+sepgsql_cb_policyload(int seqno)
|
|
+{
|
|
+ /* invalidate local avc */
|
|
+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE);
|
|
+ selinux_state->version++;
|
|
+ LWLockRelease(SepgsqlAvcLock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+bool
|
|
+sepgsqlReceiverStart(void)
|
|
+{
|
|
+ return sepgsqlIsEnabled();
|
|
+}
|
|
+
|
|
+void
|
|
+sepgsqlReceiverMain(void)
|
|
+{
|
|
+ union selinux_callback cb;
|
|
+
|
|
+ Assert(sepgsqlIsEnabled());
|
|
+
|
|
+#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
|
|
+ */
|
|
+ sepgsqlShmemInit();
|
|
+
|
|
+ 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);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * sepgsqlInitialize
|
|
+ *
|
|
+ * It sets up the privilege (security context) of the client and initializes
|
|
+ * a few internal stuff.
|
|
+ */
|
|
+void
|
|
+sepgsqlInitialize(void)
|
|
+{
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ /*
|
|
+ * SE-PgSQL does not prevent anything in single-user mode.
|
|
+ */
|
|
+ if (!MyProcPort)
|
|
+ sepostgresql_mode = SEPGSQL_MODE_INTERNAL;
|
|
+
|
|
+ sepgsqlShmemInit();
|
|
+
|
|
+ AvcMemCtx = AllocSetContextCreate(TopMemoryContext,
|
|
+ "SE-PgSQL userspace AVC",
|
|
+ ALLOCSET_DEFAULT_MINSIZE,
|
|
+ ALLOCSET_DEFAULT_INITSIZE,
|
|
+ ALLOCSET_DEFAULT_MAXSIZE);
|
|
+
|
|
+ RegisterXactCallback(sepgsqlAvcResetOnAbort, NULL);
|
|
+ RegisterSubXactCallback(sepgsqlAvcResetOnSubAbort, NULL);
|
|
+
|
|
+ /*
|
|
+ * Set client's security context
|
|
+ */
|
|
+ sepgsqlSetClientLabel(sepgsqlGetClientLabel());
|
|
+}
|
|
diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c
|
|
index fd248fc..b24239d 100644
|
|
--- a/src/backend/storage/file/fd.c
|
|
+++ b/src/backend/storage/file/fd.c
|
|
@@ -1329,6 +1329,13 @@ FileTruncate(File file, off_t offset)
|
|
return returnCode;
|
|
}
|
|
|
|
+int
|
|
+FileRawDescriptor(File file)
|
|
+{
|
|
+ Assert(FileIsValid(file));
|
|
+
|
|
+ return VfdCache[file].fd;
|
|
+}
|
|
|
|
/*
|
|
* Routines that want to use stdio (ie, FILE*) should use AllocateFile
|
|
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
|
|
index 3022867..cf6fc3d 100644
|
|
--- a/src/backend/storage/ipc/ipci.c
|
|
+++ b/src/backend/storage/ipc/ipci.c
|
|
@@ -25,6 +25,7 @@
|
|
#include "postmaster/autovacuum.h"
|
|
#include "postmaster/bgwriter.h"
|
|
#include "postmaster/postmaster.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "storage/ipc.h"
|
|
#include "storage/pg_shmem.h"
|
|
@@ -119,6 +120,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
|
|
#ifdef EXEC_BACKEND
|
|
size = add_size(size, ShmemBackendArraySize());
|
|
#endif
|
|
+ size = add_size(size, sepgsqlShmemSize());
|
|
|
|
/* freeze the addin request size and include it */
|
|
addin_request_allowed = false;
|
|
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
|
|
index a946972..7abf8e3 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 secid)
|
|
{
|
|
Oid lobjId_new;
|
|
|
|
/*
|
|
* Create a new largeobject with empty data pages
|
|
*/
|
|
- lobjId_new = LargeObjectCreate(lobjId);
|
|
+ lobjId_new = LargeObjectCreate(lobjId, secid);
|
|
|
|
/*
|
|
* dependency on the owner of largeobject
|
|
diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c
|
|
index ab2249a..bcc5448 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 "security/sepgsql.h"
|
|
#include "tcop/fastpath.h"
|
|
#include "tcop/tcopprot.h"
|
|
#include "utils/acl.h"
|
|
@@ -343,11 +344,13 @@ HandleFunctionRequest(StringInfo msgBuf)
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
|
|
get_namespace_name(fip->namespace));
|
|
+ sepgsql_schema_search(fip->namespace, true);
|
|
|
|
aclresult = pg_proc_aclcheck(fid, GetUserId(), ACL_EXECUTE);
|
|
if (aclresult != ACLCHECK_OK)
|
|
aclcheck_error(aclresult, ACL_KIND_PROC,
|
|
get_func_name(fid));
|
|
+ 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 61b329d..5a379aa 100644
|
|
--- a/src/backend/tcop/pquery.c
|
|
+++ b/src/backend/tcop/pquery.c
|
|
@@ -573,7 +573,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
|
|
Assert(pstmt->returningLists);
|
|
portal->tupDesc =
|
|
ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists),
|
|
- false);
|
|
+ false, false);
|
|
}
|
|
|
|
/*
|
|
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
|
|
index 9e82a48..96d25c6 100644
|
|
--- a/src/backend/tcop/utility.c
|
|
+++ b/src/backend/tcop/utility.c
|
|
@@ -50,6 +50,7 @@
|
|
#include "postmaster/bgwriter.h"
|
|
#include "rewrite/rewriteDefine.h"
|
|
#include "rewrite/rewriteRemove.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/fd.h"
|
|
#include "tcop/pquery.h"
|
|
#include "tcop/utility.h"
|
|
@@ -162,6 +163,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:
|
|
@@ -634,6 +636,10 @@ ProcessUtility(Node *parsetree,
|
|
ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree);
|
|
break;
|
|
|
|
+ case T_AlterSecLabelStmt:
|
|
+ ExecAlterSecLabelStmt((AlterSecLabelStmt *) parsetree);
|
|
+ break;
|
|
+
|
|
case T_AlterTableStmt:
|
|
{
|
|
List *stmts;
|
|
@@ -917,6 +923,7 @@ ProcessUtility(Node *parsetree,
|
|
LoadStmt *stmt = (LoadStmt *) parsetree;
|
|
|
|
closeAllVfds(); /* probably not necessary... */
|
|
+
|
|
/* Allowed names are restricted if you're not superuser */
|
|
load_file(stmt->filename, !superuser());
|
|
}
|
|
@@ -1664,6 +1671,31 @@ 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_FUNCTION:
|
|
+ tag = "ALTER FUNCTION";
|
|
+ break;
|
|
+ default:
|
|
+ tag = "???";
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+
|
|
case T_AlterTableStmt:
|
|
switch (((AlterTableStmt *) parsetree)->relkind)
|
|
{
|
|
@@ -2242,6 +2274,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/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c
|
|
index e1328dc..7041047 100644
|
|
--- a/src/backend/utils/adt/genfile.c
|
|
+++ b/src/backend/utils/adt/genfile.c
|
|
@@ -24,6 +24,7 @@
|
|
#include "funcapi.h"
|
|
#include "miscadmin.h"
|
|
#include "postmaster/syslogger.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/fd.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/memutils.h"
|
|
@@ -99,6 +100,9 @@ pg_read_file(PG_FUNCTION_ARGS)
|
|
|
|
filename = convert_and_check_filename(filename_t);
|
|
|
|
+ /* SELinux: check file:{read} permission */
|
|
+ sepgsql_file_read(filename);
|
|
+
|
|
if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL)
|
|
ereport(ERROR,
|
|
(errcode_for_file_access(),
|
|
@@ -159,6 +163,8 @@ pg_stat_file(PG_FUNCTION_ARGS)
|
|
(errmsg("must be superuser to get file information"))));
|
|
|
|
filename = convert_and_check_filename(filename_t);
|
|
+ /* SELinux: check file:{getattr} permission */
|
|
+ sepgsql_file_stat(filename);
|
|
|
|
if (stat(filename, &fst) < 0)
|
|
ereport(ERROR,
|
|
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
|
|
index 81684e3..b681214 100644
|
|
--- a/src/backend/utils/adt/ri_triggers.c
|
|
+++ b/src/backend/utils/adt/ri_triggers.c
|
|
@@ -39,6 +39,7 @@
|
|
#include "parser/parse_coerce.h"
|
|
#include "parser/parse_relation.h"
|
|
#include "miscadmin.h"
|
|
+#include "security/rowlevel.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/fmgroids.h"
|
|
@@ -2627,6 +2628,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
|
|
const char *sep;
|
|
int i;
|
|
int old_work_mem;
|
|
+ int save_rowlv;
|
|
char workmembuf[32];
|
|
int spi_result;
|
|
SPIPlanPtr qplan;
|
|
@@ -2759,6 +2761,11 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
|
|
SPI_result, querybuf.data);
|
|
|
|
/*
|
|
+ * Disables the Row-level stuff during the internal consistency checks.
|
|
+ */
|
|
+ save_rowlv = rowlvSetPerformingMode(ROWLV_BYPASS_MODE);
|
|
+
|
|
+ /*
|
|
* Run the plan. For safety we force a current snapshot to be used. (In
|
|
* serializable mode, this arguably violates serializability, but we
|
|
* really haven't got much choice.) We don't need to register the
|
|
@@ -2771,6 +2778,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
|
|
InvalidSnapshot,
|
|
true, false, 1);
|
|
|
|
+ /* Restore Row-level stuff */
|
|
+ rowlvSetPerformingMode(save_rowlv);
|
|
+
|
|
/* Check result */
|
|
if (spi_result != SPI_OK_SELECT)
|
|
elog(ERROR, "SPI_execute_snapshot returned %d", spi_result);
|
|
@@ -3265,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];
|
|
|
|
@@ -3348,12 +3359,19 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan,
|
|
SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner,
|
|
save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
|
|
|
|
+ /* Switch Row-level stuff behavior on FK checks, if necessary */
|
|
+ temp_rowlv = (detectNewRows ? ROWLV_ABORT_MODE : ROWLV_FILTER_MODE);
|
|
+ save_rowlv = rowlvSetPerformingMode(temp_rowlv);
|
|
+
|
|
/* Finally we can run the query. */
|
|
spi_result = SPI_execute_snapshot(qplan,
|
|
vals, nulls,
|
|
test_snapshot, crosscheck_snapshot,
|
|
false, false, limit);
|
|
|
|
+ /* Restore Row-level stuff behavior */
|
|
+ rowlvSetPerformingMode(save_rowlv);
|
|
+
|
|
/* Restore UID and security context */
|
|
SetUserIdAndSecContext(save_userid, save_sec_context);
|
|
|
|
diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c
|
|
index 5bb166d..ef9c2e9 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 "security/sepgsql.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_get_transaction_id(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_get_transaction_id(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 ceab88d..80db58d 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 (HeapTupleHeaderHasSecid(newheader) &&
|
|
+ !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/cache/plancache.c b/src/backend/utils/cache/plancache.c
|
|
index 8fc2d5a..b943e80 100644
|
|
--- a/src/backend/utils/cache/plancache.c
|
|
+++ b/src/backend/utils/cache/plancache.c
|
|
@@ -859,12 +859,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;
|
|
@@ -875,13 +875,14 @@ 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->returningLists);
|
|
- return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false);
|
|
+ return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists),
|
|
+ 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 df0a172..a48d474 100644
|
|
--- a/src/backend/utils/cache/relcache.c
|
|
+++ b/src/backend/utils/cache/relcache.c
|
|
@@ -55,6 +55,7 @@
|
|
#include "optimizer/prep.h"
|
|
#include "optimizer/var.h"
|
|
#include "rewrite/rewriteDefine.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/fd.h"
|
|
#include "storage/lmgr.h"
|
|
#include "storage/smgr.h"
|
|
@@ -865,6 +866,10 @@ RelationBuildDesc(Oid targetRelId, Relation oldrelation)
|
|
/* extract reloptions if any */
|
|
RelationParseRelOptions(relation, pg_class_tuple);
|
|
|
|
+ /* Fixup relation->rd_att->tdhassecid */
|
|
+ RelationGetDescr(relation)->tdhassecid
|
|
+ = securityTupleDescHasSecid(relid, relp->relkind);
|
|
+
|
|
/*
|
|
* initialize the relation lock manager information
|
|
*/
|
|
@@ -1460,6 +1465,11 @@ formrdesc(const char *relationName, Oid relationReltype,
|
|
RelationGetRelid(relation) = relation->rd_att->attrs[0]->attrelid;
|
|
relation->rd_rel->relfilenode = RelationGetRelid(relation);
|
|
|
|
+ /* Fixup relation->rd_att->tdhassecid */
|
|
+ RelationGetDescr(relation)->tdhassecid
|
|
+ = securityTupleDescHasSecid(RelationGetRelid(relation),
|
|
+ RELKIND_RELATION);
|
|
+
|
|
/*
|
|
* initialize the relation lock manager information
|
|
*/
|
|
@@ -2749,6 +2759,13 @@ BuildHardcodedDescriptor(int natts, Form_pg_attribute attrs, bool hasoids)
|
|
result = CreateTemplateTupleDesc(natts, hasoids);
|
|
result->tdtypeid = RECORDOID; /* not right, but we don't care */
|
|
result->tdtypmod = -1;
|
|
+ /*
|
|
+ * NOTE: we assume the returned TupleDesc is only used for
|
|
+ * references to toast'ed data, and it is not delivered to
|
|
+ * heap_form_tuple(), so TupleDesc->tdhassecid don't give us
|
|
+ * any effect.
|
|
+ * We omit to invoke securityTupleDescHasSecid() here.
|
|
+ */
|
|
|
|
for (i = 0; i < natts; i++)
|
|
{
|
|
@@ -3503,6 +3520,11 @@ load_relcache_init_file(void)
|
|
rel->rd_options = NULL;
|
|
}
|
|
|
|
+ /* Fixup rel->rd_att->tdhassecid */
|
|
+ RelationGetDescr(rel)->tdhassecid
|
|
+ = securityTupleDescHasSecid(RelationGetRelid(rel),
|
|
+ RelationGetForm(rel)->relkind);
|
|
+
|
|
/* mark not-null status */
|
|
if (has_not_null)
|
|
{
|
|
diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c
|
|
index 529940c..fe06e2f 100644
|
|
--- a/src/backend/utils/fmgr/dfmgr.c
|
|
+++ b/src/backend/utils/fmgr/dfmgr.c
|
|
@@ -23,6 +23,7 @@
|
|
#endif
|
|
#include "lib/stringinfo.h"
|
|
#include "miscadmin.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "utils/dynamic_loader.h"
|
|
#include "utils/hsearch.h"
|
|
|
|
@@ -109,6 +110,9 @@ load_external_function(char *filename, char *funcname,
|
|
/* Expand the possibly-abbreviated filename to an exact path name */
|
|
fullname = expand_dynamic_library_name(filename);
|
|
|
|
+ /* SELinux checks db_database:{load_module} */
|
|
+ sepgsql_database_load_module(MyDatabaseId, fullname);
|
|
+
|
|
/* Load the shared library, unless we already did */
|
|
lib_handle = internal_load_library(fullname);
|
|
|
|
@@ -149,6 +153,9 @@ load_file(const char *filename, bool restricted)
|
|
/* Expand the possibly-abbreviated filename to an exact path name */
|
|
fullname = expand_dynamic_library_name(filename);
|
|
|
|
+ /* SELinux checks db_database:{load_module} */
|
|
+ sepgsql_database_load_module(MyDatabaseId, fullname);
|
|
+
|
|
/* Unload the library if currently loaded */
|
|
internal_unload_library(fullname);
|
|
|
|
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
|
|
index 9a1da59..76264f3 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 "security/sepgsql.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/fmgrtab.h"
|
|
#include "utils/guc.h"
|
|
@@ -232,6 +233,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt,
|
|
*/
|
|
if (!ignore_security &&
|
|
(procedureStruct->prosecdef ||
|
|
+ sepgsql_proc_entrypoint(procedureTuple) ||
|
|
!heap_attisnull(procedureTuple, Anum_pg_proc_proconfig)))
|
|
{
|
|
finfo->fn_addr = fmgr_security_definer;
|
|
@@ -860,6 +862,7 @@ struct fmgr_security_definer_cache
|
|
{
|
|
FmgrInfo flinfo; /* lookup info for target function */
|
|
Oid userid; /* userid to set, or InvalidOid */
|
|
+ char *seclabel; /* security label to set, or NULL */
|
|
ArrayType *proconfig; /* GUC values to set, or NULL */
|
|
};
|
|
|
|
@@ -881,6 +884,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
|
|
FmgrInfo *save_flinfo;
|
|
Oid save_userid;
|
|
int save_sec_context;
|
|
+ char *save_label = NULL;
|
|
volatile int save_nestlevel;
|
|
PgStat_FunctionCallUsage fcusage;
|
|
|
|
@@ -910,6 +914,9 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
|
|
if (procedureStruct->prosecdef)
|
|
fcache->userid = procedureStruct->proowner;
|
|
|
|
+ fcache->seclabel
|
|
+ = sepgsql_proc_trusted(tuple, fcinfo->flinfo->fn_mcxt);
|
|
+
|
|
datum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proconfig,
|
|
&isnull);
|
|
if (!isnull)
|
|
@@ -936,6 +943,8 @@ fmgr_security_definer(PG_FUNCTION_ARGS)
|
|
if (OidIsValid(fcache->userid))
|
|
SetUserIdAndSecContext(fcache->userid,
|
|
save_sec_context | SECURITY_LOCAL_USERID_CHANGE);
|
|
+ if (fcache->seclabel)
|
|
+ save_label = sepgsqlSetClientLabel(fcache->seclabel);
|
|
|
|
if (fcache->proconfig)
|
|
{
|
|
@@ -983,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 (fcache->seclabel)
|
|
+ sepgsqlSetClientLabel(save_label);
|
|
|
|
return result;
|
|
}
|
|
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
|
|
index 327ba7c..0fc1c8d 100644
|
|
--- a/src/backend/utils/init/postinit.c
|
|
+++ b/src/backend/utils/init/postinit.c
|
|
@@ -32,6 +32,7 @@
|
|
#include "pgstat.h"
|
|
#include "postmaster/autovacuum.h"
|
|
#include "postmaster/postmaster.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/backendid.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "storage/fd.h"
|
|
@@ -201,7 +202,7 @@ CheckMyDatabase(const char *name, bool am_superuser)
|
|
name)));
|
|
|
|
/*
|
|
- * Check privilege to connect to the database. (The am_superuser test
|
|
+ * Check privilege to connect to the database. (The am_superuser test
|
|
* is redundant, but since we have the flag, might as well check it
|
|
* and save a few cycles.)
|
|
*/
|
|
@@ -213,6 +214,9 @@ CheckMyDatabase(const char *name, bool am_superuser)
|
|
errmsg("permission denied for database \"%s\"", name),
|
|
errdetail("User does not have CONNECT privilege.")));
|
|
|
|
+ /* SELinux: db_database:{access} */
|
|
+ sepgsql_database_access(MyDatabaseId);
|
|
+
|
|
/*
|
|
* Check connection limit for this database.
|
|
*
|
|
@@ -607,6 +611,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
|
|
/* set up ACL framework (so CheckMyDatabase can check permissions) */
|
|
initialize_acl();
|
|
|
|
+ /* Initialize SE-PostgreSQL */
|
|
+ sepgsqlInitialize();
|
|
+
|
|
/*
|
|
* Read the real 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 db1d933..568ef80 100644
|
|
--- a/src/backend/utils/misc/guc.c
|
|
+++ b/src/backend/utils/misc/guc.c
|
|
@@ -57,6 +57,7 @@
|
|
#include "postmaster/syslogger.h"
|
|
#include "postmaster/walwriter.h"
|
|
#include "regex/regex.h"
|
|
+#include "security/sepgsql.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "storage/fd.h"
|
|
#include "tcop/tcopprot.h"
|
|
@@ -257,6 +258,18 @@ static const struct config_enum_entry isolation_level_options[] = {
|
|
{NULL, 0}
|
|
};
|
|
|
|
+#ifdef HAVE_SELINUX
|
|
+static const struct config_enum_entry sepostgresql_mode_options [] = {
|
|
+ {"on", SEPGSQL_MODE_DEFAULT, true},
|
|
+ {"off", SEPGSQL_MODE_DISABLED, true},
|
|
+ {"default", SEPGSQL_MODE_DEFAULT, false},
|
|
+ {"permissive", SEPGSQL_MODE_PERMISSIVE, false},
|
|
+ {"enforcing", SEPGSQL_MODE_ENFORCING, false},
|
|
+ {"disabled", SEPGSQL_MODE_DISABLED, false},
|
|
+ {NULL, 0}
|
|
+};
|
|
+#endif
|
|
+
|
|
static const struct config_enum_entry session_replication_role_options[] = {
|
|
{"origin", SESSION_REPLICATION_ROLE_ORIGIN, false},
|
|
{"replica", SESSION_REPLICATION_ROLE_REPLICA, false},
|
|
@@ -1221,6 +1234,24 @@ static struct config_bool ConfigureNamesBool[] =
|
|
&IgnoreSystemIndexes,
|
|
false, NULL, NULL
|
|
},
|
|
+#ifdef HAVE_SELINUX
|
|
+ {
|
|
+ {"sepostgresql_row_level", PGC_POSTMASTER, CONN_AUTH_SECURITY,
|
|
+ gettext_noop("Row-level access controls on SE-PostgreSQL"),
|
|
+ NULL,
|
|
+ },
|
|
+ &sepostgresql_row_level,
|
|
+ true, NULL, NULL
|
|
+ },
|
|
+ {
|
|
+ {"sepostgresql_mcstrans", PGC_USERSET, CONN_AUTH_SECURITY,
|
|
+ gettext_noop("SE-PostgreSQL uses mcstrans on printing security labels"),
|
|
+ NULL,
|
|
+ },
|
|
+ &sepostgresql_mcstrans,
|
|
+ true, NULL, NULL
|
|
+ },
|
|
+#endif
|
|
|
|
{
|
|
{"lo_compat_privileges", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS,
|
|
@@ -2640,7 +2671,17 @@ static struct config_enum ConfigureNamesEnum[] =
|
|
®ex_flavor,
|
|
REG_ADVANCED, regex_flavor_options, NULL, NULL
|
|
},
|
|
-
|
|
+#ifdef HAVE_SELINUX
|
|
+ {
|
|
+ {"sepostgresql", PGC_POSTMASTER, CONN_AUTH_SECURITY,
|
|
+ gettext_noop("SE-PostgreSQL performing mode"),
|
|
+ NULL,
|
|
+ },
|
|
+ &sepostgresql_mode,
|
|
+ SEPGSQL_MODE_DISABLED, sepostgresql_mode_options,
|
|
+ NULL, sepgsqlShowMode
|
|
+ },
|
|
+#endif
|
|
{
|
|
{"session_replication_role", PGC_SUSET, CLIENT_CONN_STATEMENT,
|
|
gettext_noop("Sets the session's behavior for triggers and rewrite rules."),
|
|
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
|
|
index 85acc4e..e298197 100644
|
|
--- a/src/backend/utils/misc/postgresql.conf.sample
|
|
+++ b/src/backend/utils/misc/postgresql.conf.sample
|
|
@@ -51,7 +51,7 @@
|
|
|
|
|
|
#------------------------------------------------------------------------------
|
|
-# CONNECTIONS AND AUTHENTICATION
|
|
+# CONNECTIONS, AUTHENTICATION AND SECURITY
|
|
#------------------------------------------------------------------------------
|
|
|
|
# - Connection Settings -
|
|
@@ -95,7 +95,7 @@
|
|
# 0 selects the system default
|
|
#tcp_keepalives_count = 0 # TCP_KEEPCNT;
|
|
# 0 selects the system default
|
|
-
|
|
+#sepostgresql = off # SE-PostgreSQL support
|
|
|
|
#------------------------------------------------------------------------------
|
|
# RESOURCE USAGE (except WAL)
|
|
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
|
|
index 1fcf590..620ac5c 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 */
|
|
@@ -1205,6 +1206,13 @@ setup_config(void)
|
|
"#default_text_search_config = 'pg_catalog.simple'",
|
|
repltok);
|
|
|
|
+ if (enable_selinux)
|
|
+ {
|
|
+ strcpy(repltok, "sepostgresql = on");
|
|
+ conflines = replace_token(conflines,
|
|
+ "#sepostgresql = off", repltok);
|
|
+ }
|
|
+
|
|
snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data);
|
|
|
|
writefile(path, conflines);
|
|
@@ -2444,6 +2452,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, if compiled\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"));
|
|
@@ -2479,6 +2488,7 @@ main(int argc, char *argv[])
|
|
{"auth", required_argument, NULL, 'A'},
|
|
{"pwprompt", no_argument, NULL, 'W'},
|
|
{"pwfile", required_argument, NULL, 9},
|
|
+ {"enable-selinux", no_argument, NULL, 10},
|
|
{"username", required_argument, NULL, 'U'},
|
|
{"help", no_argument, NULL, '?'},
|
|
{"version", no_argument, NULL, 'V'},
|
|
@@ -2595,6 +2605,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_dump.c b/src/bin/pg_dump/pg_dump.c
|
|
index 57b5f7d..4e48601 100644
|
|
--- a/src/bin/pg_dump/pg_dump.c
|
|
+++ b/src/bin/pg_dump/pg_dump.c
|
|
@@ -112,6 +112,8 @@ static int disable_dollar_quoting = 0;
|
|
static int dump_inserts = 0;
|
|
static int column_inserts = 0;
|
|
|
|
+/* flag to turn on/off security_context */
|
|
+static int security_context = 0;
|
|
|
|
static void help(const char *progname);
|
|
static void expand_schema_name_patterns(SimpleStringList *patterns,
|
|
@@ -277,6 +279,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-context", no_argument, &security_context, 1},
|
|
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
@@ -425,6 +428,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-context") == 0)
|
|
+ security_context = 1;
|
|
else
|
|
{
|
|
fprintf(stderr,
|
|
@@ -573,6 +578,28 @@ main(int argc, char **argv)
|
|
std_strings = PQparameterStatus(g_conn, "standard_conforming_strings");
|
|
g_fout->std_strings = (std_strings && strcmp(std_strings, "on") == 0);
|
|
|
|
+ /* Check availability of SE-PostgreSQL */
|
|
+ if (security_context > 0)
|
|
+ {
|
|
+ PGresult *res;
|
|
+
|
|
+ res = PQexec(g_conn, "SHOW sepostgresql");
|
|
+ if (PQresultStatus(res) != PGRES_TUPLES_OK ||
|
|
+ PQntuples(res) != 1 ||
|
|
+ strcmp(PQgetvalue(res, 0, 0), "on") != 0)
|
|
+ {
|
|
+ write_msg(NULL, "SE-PostgreSQL is not available now.");
|
|
+ exit(1);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * It needs to force column insertion mode, when --inserts
|
|
+ * and either --security-label or --security-acl is given.
|
|
+ */
|
|
+ if (security_context > 0 && dump_inserts)
|
|
+ column_inserts = 1;
|
|
+
|
|
/* Set the role if requested */
|
|
if (use_role && g_fout->remoteVersion >= 80100)
|
|
{
|
|
@@ -826,6 +853,8 @@ 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 SE-PostgreSQL security labels\n"));
|
|
+ printf(_(" --security-acl dump row-level database ACLs\n"));
|
|
|
|
printf(_("\nConnection options:\n"));
|
|
printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
|
|
@@ -1227,7 +1256,8 @@ 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",
|
|
+ (security_context > 0 ? "security_context, " : ""),
|
|
fmtQualifiedId(tbinfo->dobj.namespace->dobj.name,
|
|
classname));
|
|
}
|
|
@@ -1583,7 +1613,8 @@ dumpDatabase(Archive *AH)
|
|
i_collate,
|
|
i_ctype,
|
|
i_frozenxid,
|
|
- i_tablespace;
|
|
+ i_tablespace,
|
|
+ i_seclabel;
|
|
CatalogId dbCatId;
|
|
DumpId dbDumpId;
|
|
const char *datname,
|
|
@@ -1591,7 +1622,8 @@ dumpDatabase(Archive *AH)
|
|
*encoding,
|
|
*collate,
|
|
*ctype,
|
|
- *tablespace;
|
|
+ *tablespace,
|
|
+ *seclabel;
|
|
uint32 frozenxid;
|
|
|
|
datname = PQdb(g_conn);
|
|
@@ -1610,11 +1642,12 @@ dumpDatabase(Archive *AH)
|
|
"pg_encoding_to_char(encoding) AS encoding, "
|
|
"datcollate, datctype, datfrozenxid, "
|
|
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
|
|
- "shobj_description(oid, 'pg_database') AS description "
|
|
-
|
|
+ "shobj_description(oid, 'pg_database') AS description, "
|
|
+ "%s as security_context "
|
|
"FROM pg_database "
|
|
"WHERE datname = ",
|
|
- username_subquery);
|
|
+ username_subquery,
|
|
+ security_context ? "security_context" : "NULL");
|
|
appendStringLiteralAH(dbQry, datname, AH);
|
|
}
|
|
else if (g_fout->remoteVersion >= 80200)
|
|
@@ -1624,8 +1657,8 @@ dumpDatabase(Archive *AH)
|
|
"pg_encoding_to_char(encoding) AS encoding, "
|
|
"NULL AS datcollate, NULL AS datctype, datfrozenxid, "
|
|
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
|
|
- "shobj_description(oid, 'pg_database') AS description "
|
|
-
|
|
+ "shobj_description(oid, 'pg_database') AS description, "
|
|
+ "NULL as security_context "
|
|
"FROM pg_database "
|
|
"WHERE datname = ",
|
|
username_subquery);
|
|
@@ -1637,7 +1670,8 @@ dumpDatabase(Archive *AH)
|
|
"(%s datdba) AS dba, "
|
|
"pg_encoding_to_char(encoding) AS encoding, "
|
|
"NULL AS datcollate, NULL AS datctype, datfrozenxid, "
|
|
- "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace "
|
|
+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, "
|
|
+ "NULL as security_context "
|
|
"FROM pg_database "
|
|
"WHERE datname = ",
|
|
username_subquery);
|
|
@@ -1650,7 +1684,8 @@ dumpDatabase(Archive *AH)
|
|
"pg_encoding_to_char(encoding) AS encoding, "
|
|
"NULL AS datcollate, NULL AS datctype, "
|
|
"0 AS datfrozenxid, "
|
|
- "NULL AS tablespace "
|
|
+ "NULL AS tablespace, "
|
|
+ "NULL AS security_context "
|
|
"FROM pg_database "
|
|
"WHERE datname = ",
|
|
username_subquery);
|
|
@@ -1665,7 +1700,8 @@ dumpDatabase(Archive *AH)
|
|
"pg_encoding_to_char(encoding) AS encoding, "
|
|
"NULL AS datcollate, NULL AS datctype, "
|
|
"0 AS datfrozenxid, "
|
|
- "NULL AS tablespace "
|
|
+ "NULL AS tablespace, "
|
|
+ "NULL as security_context "
|
|
"FROM pg_database "
|
|
"WHERE datname = ",
|
|
username_subquery);
|
|
@@ -1699,6 +1735,7 @@ dumpDatabase(Archive *AH)
|
|
i_ctype = PQfnumber(res, "datctype");
|
|
i_frozenxid = PQfnumber(res, "datfrozenxid");
|
|
i_tablespace = PQfnumber(res, "tablespace");
|
|
+ i_seclabel = PQfnumber(res, "security_context");
|
|
|
|
dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid));
|
|
dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid));
|
|
@@ -1708,6 +1745,7 @@ dumpDatabase(Archive *AH)
|
|
ctype = PQgetvalue(res, 0, i_ctype);
|
|
frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
|
|
tablespace = PQgetvalue(res, 0, i_tablespace);
|
|
+ seclabel = PQgetvalue(res, 0, i_seclabel);
|
|
|
|
appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0",
|
|
fmtId(datname));
|
|
@@ -1729,6 +1767,9 @@ dumpDatabase(Archive *AH)
|
|
if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0)
|
|
appendPQExpBuffer(creaQry, " TABLESPACE = %s",
|
|
fmtId(tablespace));
|
|
+ if (strlen(seclabel) > 0)
|
|
+ appendPQExpBuffer(creaQry, " SECURITY_CONTEXT = '%s'", seclabel);
|
|
+
|
|
appendPQExpBuffer(creaQry, ";\n");
|
|
|
|
if (binary_upgrade)
|
|
@@ -3230,6 +3271,7 @@ getTables(int *numTables)
|
|
int i_reltablespace;
|
|
int i_reloptions;
|
|
int i_toastreloptions;
|
|
+ int i_relseclabel;
|
|
|
|
/* Make sure we are in proper schema */
|
|
selectSourceSchema("pg_catalog");
|
|
@@ -3271,7 +3313,8 @@ getTables(int *numTables)
|
|
"d.refobjsubid AS owning_col, "
|
|
"(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 "
|
|
+ "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions, "
|
|
+ "%s as security_context "
|
|
"FROM pg_class c "
|
|
"LEFT JOIN pg_depend d ON "
|
|
"(c.relkind = '%c' AND "
|
|
@@ -3282,6 +3325,7 @@ getTables(int *numTables)
|
|
"WHERE c.relkind in ('%c', '%c', '%c', '%c') "
|
|
"ORDER BY c.oid",
|
|
username_subquery,
|
|
+ security_context ? "c.security_context" : "NULL",
|
|
RELKIND_SEQUENCE,
|
|
RELKIND_RELATION, RELKIND_SEQUENCE,
|
|
RELKIND_VIEW, RELKIND_COMPOSITE_TYPE);
|
|
@@ -3303,7 +3347,8 @@ getTables(int *numTables)
|
|
"d.refobjsubid AS owning_col, "
|
|
"(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 toast_reloptions, "
|
|
+ "NULL as security_context "
|
|
"FROM pg_class c "
|
|
"LEFT JOIN pg_depend d ON "
|
|
"(c.relkind = '%c' AND "
|
|
@@ -3334,7 +3379,8 @@ getTables(int *numTables)
|
|
"d.refobjsubid AS owning_col, "
|
|
"(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, "
|
|
"NULL AS reloptions, "
|
|
- "NULL AS toast_reloptions "
|
|
+ "NULL AS toast_reloptions, "
|
|
+ "NULL as security_context "
|
|
"FROM pg_class c "
|
|
"LEFT JOIN pg_depend d ON "
|
|
"(c.relkind = '%c' AND "
|
|
@@ -3365,7 +3411,8 @@ getTables(int *numTables)
|
|
"d.refobjsubid AS owning_col, "
|
|
"NULL AS reltablespace, "
|
|
"NULL AS reloptions, "
|
|
- "NULL AS toast_reloptions "
|
|
+ "NULL AS toast_reloptions, "
|
|
+ "NULL as security_context "
|
|
"FROM pg_class c "
|
|
"LEFT JOIN pg_depend d ON "
|
|
"(c.relkind = '%c' AND "
|
|
@@ -3392,7 +3439,8 @@ getTables(int *numTables)
|
|
"NULL::int4 AS owning_col, "
|
|
"NULL AS reltablespace, "
|
|
"NULL AS reloptions, "
|
|
- "NULL AS toast_reloptions "
|
|
+ "NULL AS toast_reloptions, "
|
|
+ "NULL AS security_context "
|
|
"FROM pg_class "
|
|
"WHERE relkind IN ('%c', '%c', '%c') "
|
|
"ORDER BY oid",
|
|
@@ -3414,7 +3462,8 @@ getTables(int *numTables)
|
|
"NULL::int4 AS owning_col, "
|
|
"NULL AS reltablespace, "
|
|
"NULL AS reloptions, "
|
|
- "NULL AS toast_reloptions "
|
|
+ "NULL AS toast_reloptions, "
|
|
+ "NULL AS security_context "
|
|
"FROM pg_class "
|
|
"WHERE relkind IN ('%c', '%c', '%c') "
|
|
"ORDER BY oid",
|
|
@@ -3446,7 +3495,8 @@ getTables(int *numTables)
|
|
"NULL::int4 AS owning_col, "
|
|
"NULL AS reltablespace, "
|
|
"NULL AS reloptions, "
|
|
- "NULL AS toast_reloptions "
|
|
+ "NULL AS toast_reloptions, "
|
|
+ "NULL as security_context "
|
|
"FROM pg_class c "
|
|
"WHERE relkind IN ('%c', '%c') "
|
|
"ORDER BY oid",
|
|
@@ -3491,6 +3541,7 @@ getTables(int *numTables)
|
|
i_reltablespace = PQfnumber(res, "reltablespace");
|
|
i_reloptions = PQfnumber(res, "reloptions");
|
|
i_toastreloptions = PQfnumber(res, "toast_reloptions");
|
|
+ i_relseclabel = PQfnumber(res, "security_context");
|
|
|
|
if (lockWaitTimeout && g_fout->remoteVersion >= 70300)
|
|
{
|
|
@@ -3538,6 +3589,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].relseclabel = strdup(PQgetvalue(res, i, i_relseclabel));
|
|
|
|
/* other fields were zeroed above */
|
|
|
|
@@ -4737,6 +4789,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
|
|
int i_attlen;
|
|
int i_attalign;
|
|
int i_attislocal;
|
|
+ int i_attseclabel;
|
|
PGresult *res;
|
|
int ntups;
|
|
bool hasdefaults;
|
|
@@ -4781,12 +4834,14 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
|
|
"a.attstattarget, a.attstorage, t.typstorage, "
|
|
"a.attnotnull, a.atthasdef, a.attisdropped, "
|
|
"a.attlen, a.attalign, a.attislocal, "
|
|
- "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname "
|
|
+ "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, "
|
|
+ "%s as security_context "
|
|
"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_context ? "a.security_context" : "NULL",
|
|
tbinfo->dobj.catId.oid);
|
|
}
|
|
else if (g_fout->remoteVersion >= 70100)
|
|
@@ -4801,7 +4856,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
|
|
"t.typstorage, a.attnotnull, a.atthasdef, "
|
|
"false AS attisdropped, a.attlen, "
|
|
"a.attalign, false AS attislocal, "
|
|
- "format_type(t.oid,a.atttypmod) AS atttypname "
|
|
+ "format_type(t.oid,a.atttypmod) AS atttypname, "
|
|
+ "NULL as security_context "
|
|
"FROM pg_attribute a LEFT JOIN pg_type t "
|
|
"ON a.atttypid = t.oid "
|
|
"WHERE a.attrelid = '%u'::oid "
|
|
@@ -4818,7 +4874,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
|
|
"attnotnull, atthasdef, false AS attisdropped, "
|
|
"attlen, attalign, "
|
|
"false AS attislocal, "
|
|
- "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname "
|
|
+ "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname, "
|
|
+ "NULL as security_context "
|
|
"FROM pg_attribute a "
|
|
"WHERE attrelid = '%u'::oid "
|
|
"AND attnum > 0::int2 "
|
|
@@ -4844,6 +4901,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
|
|
i_attlen = PQfnumber(res, "attlen");
|
|
i_attalign = PQfnumber(res, "attalign");
|
|
i_attislocal = PQfnumber(res, "attislocal");
|
|
+ i_attseclabel = PQfnumber(res, "security_context");
|
|
|
|
tbinfo->numatts = ntups;
|
|
tbinfo->attnames = (char **) malloc(ntups * sizeof(char *));
|
|
@@ -4856,6 +4914,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
|
|
tbinfo->attlen = (int *) malloc(ntups * sizeof(int));
|
|
tbinfo->attalign = (char *) malloc(ntups * sizeof(char));
|
|
tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool));
|
|
+ tbinfo->attseclabel = (char **) malloc(ntups * sizeof(char *));
|
|
tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool));
|
|
tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *));
|
|
tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool));
|
|
@@ -4881,6 +4940,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables)
|
|
tbinfo->attlen[j] = atoi(PQgetvalue(res, j, i_attlen));
|
|
tbinfo->attalign[j] = *(PQgetvalue(res, j, i_attalign));
|
|
tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't');
|
|
+ tbinfo->attseclabel[j] = strdup(PQgetvalue(res, j, i_attseclabel));
|
|
tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't');
|
|
tbinfo->attrdefs[j] = NULL; /* fix below */
|
|
if (PQgetvalue(res, j, i_atthasdef)[0] == 't')
|
|
@@ -7131,6 +7191,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|
char *proconfig;
|
|
char *procost;
|
|
char *prorows;
|
|
+ char *proseclabel;
|
|
char *lanname;
|
|
char *rettypename;
|
|
int nallargs;
|
|
@@ -7167,9 +7228,11 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|
"pg_catalog.pg_get_function_result(oid) AS funcresult, "
|
|
"proiswindow, provolatile, proisstrict, prosecdef, "
|
|
"proconfig, procost, prorows, "
|
|
- "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
|
+ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, "
|
|
+ "%s as security_context "
|
|
"FROM pg_catalog.pg_proc "
|
|
"WHERE oid = '%u'::pg_catalog.oid",
|
|
+ security_context ? "security_context" : "NULL",
|
|
finfo->dobj.catId.oid);
|
|
}
|
|
else if (g_fout->remoteVersion >= 80300)
|
|
@@ -7180,7 +7243,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|
"false AS proiswindow, "
|
|
"provolatile, proisstrict, prosecdef, "
|
|
"proconfig, procost, prorows, "
|
|
- "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
|
+ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, "
|
|
+ "NULL AS security_context "
|
|
"FROM pg_catalog.pg_proc "
|
|
"WHERE oid = '%u'::pg_catalog.oid",
|
|
finfo->dobj.catId.oid);
|
|
@@ -7193,7 +7257,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|
"false AS proiswindow, "
|
|
"provolatile, proisstrict, prosecdef, "
|
|
"null AS proconfig, 0 AS procost, 0 AS prorows, "
|
|
- "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
|
+ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, "
|
|
+ "NULL AS security_context "
|
|
"FROM pg_catalog.pg_proc "
|
|
"WHERE oid = '%u'::pg_catalog.oid",
|
|
finfo->dobj.catId.oid);
|
|
@@ -7208,7 +7273,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|
"false AS proiswindow, "
|
|
"provolatile, proisstrict, prosecdef, "
|
|
"null AS proconfig, 0 AS procost, 0 AS prorows, "
|
|
- "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
|
+ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, "
|
|
+ "NULL AS security_context "
|
|
"FROM pg_catalog.pg_proc "
|
|
"WHERE oid = '%u'::pg_catalog.oid",
|
|
finfo->dobj.catId.oid);
|
|
@@ -7223,7 +7289,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|
"false AS proiswindow, "
|
|
"provolatile, proisstrict, prosecdef, "
|
|
"null AS proconfig, 0 AS procost, 0 AS prorows, "
|
|
- "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname "
|
|
+ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, "
|
|
+ "NULL AS security_context "
|
|
"FROM pg_catalog.pg_proc "
|
|
"WHERE oid = '%u'::pg_catalog.oid",
|
|
finfo->dobj.catId.oid);
|
|
@@ -7240,7 +7307,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|
"proisstrict, "
|
|
"false AS prosecdef, "
|
|
"null AS proconfig, 0 AS procost, 0 AS prorows, "
|
|
- "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname "
|
|
+ "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname, "
|
|
+ "NULL AS security_context "
|
|
"FROM pg_proc "
|
|
"WHERE oid = '%u'::oid",
|
|
finfo->dobj.catId.oid);
|
|
@@ -7257,7 +7325,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|
"false AS proisstrict, "
|
|
"false AS prosecdef, "
|
|
"NULL AS proconfig, 0 AS procost, 0 AS prorows, "
|
|
- "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname "
|
|
+ "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname, "
|
|
+ "NULL AS security_context "
|
|
"FROM pg_proc "
|
|
"WHERE oid = '%u'::oid",
|
|
finfo->dobj.catId.oid);
|
|
@@ -7301,6 +7370,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|
proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig"));
|
|
procost = PQgetvalue(res, 0, PQfnumber(res, "procost"));
|
|
prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows"));
|
|
+ proseclabel = PQgetvalue(res, 0, PQfnumber(res, "security_context"));
|
|
lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname"));
|
|
|
|
/*
|
|
@@ -7459,6 +7529,9 @@ dumpFunc(Archive *fout, FuncInfo *finfo)
|
|
if (prosecdef[0] == 't')
|
|
appendPQExpBuffer(q, " SECURITY DEFINER");
|
|
|
|
+ if (security_context > 0 && strlen(proseclabel) > 0)
|
|
+ appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'", proseclabel);
|
|
+
|
|
/*
|
|
* COST and ROWS are emitted only if present and not default, so as not to
|
|
* break backwards-compatibility of the dump without need. Keep this code
|
|
@@ -9917,6 +9990,17 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
|
|
if (tbinfo->notnull[j] &&
|
|
(!tbinfo->inhNotNull[j] || binary_upgrade))
|
|
appendPQExpBuffer(q, " NOT NULL");
|
|
+
|
|
+ /*
|
|
+ * Security label -- if SE-PostgreSQL enabled
|
|
+ */
|
|
+ if (security_context > 0 &&
|
|
+ strlen(tbinfo->attseclabel[j]) > 0 &&
|
|
+ strcmp(tbinfo->relseclabel, tbinfo->attseclabel[j]) != 0)
|
|
+ appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'",
|
|
+ tbinfo->attseclabel[j]);
|
|
+
|
|
+ actual_atts++;
|
|
}
|
|
}
|
|
|
|
@@ -9979,6 +10063,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
|
|
appendPQExpBuffer(q, ")");
|
|
}
|
|
|
|
+ if (security_context > 0 && strlen(tbinfo->relseclabel) > 0)
|
|
+ appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'", tbinfo->relseclabel);
|
|
+
|
|
appendPQExpBuffer(q, ";\n");
|
|
|
|
/*
|
|
@@ -11550,6 +11637,13 @@ fmtCopyColumnList(const TableInfo *ti)
|
|
|
|
appendPQExpBuffer(q, "(");
|
|
needComma = false;
|
|
+
|
|
+ if (security_context > 0)
|
|
+ {
|
|
+ appendPQExpBuffer(q, "security_context");
|
|
+ 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 a9b3dae..350025c 100644
|
|
--- a/src/bin/pg_dump/pg_dump.h
|
|
+++ b/src/bin/pg_dump/pg_dump.h
|
|
@@ -228,6 +228,7 @@ typedef struct _tableInfo
|
|
bool hasoids; /* does it have OIDs? */
|
|
uint32 frozenxid; /* for restore frozen xid */
|
|
int ncheck; /* # of CHECK expressions */
|
|
+ char *relseclabel; /* security labels of relation */
|
|
/* 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 */
|
|
@@ -249,6 +250,7 @@ typedef struct _tableInfo
|
|
int *attlen; /* attribute length, used by binary_upgrade */
|
|
char *attalign; /* attribute align, used by binary_upgrade */
|
|
bool *attislocal; /* true if attr has local definition */
|
|
+ char **attseclabel; /* security labels of attributes */
|
|
|
|
/*
|
|
* Note: we need to store per-attribute notnull, default, and constraint
|
|
diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c
|
|
index b265398..5011551 100644
|
|
--- a/src/bin/pg_dump/pg_dumpall.c
|
|
+++ b/src/bin/pg_dump/pg_dumpall.c
|
|
@@ -69,6 +69,9 @@ static int no_tablespaces = 0;
|
|
static int use_setsessauth = 0;
|
|
static int server_version;
|
|
|
|
+static int security_label = 0;
|
|
+static int security_acl = 0;
|
|
+
|
|
static FILE *OPF;
|
|
static char *filename = NULL;
|
|
|
|
@@ -130,6 +133,8 @@ 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},
|
|
+ {"security-acl", no_argument, &security_acl, 1},
|
|
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
@@ -283,6 +288,10 @@ 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 if (strcmp(optarg, "security-acl") == 0)
|
|
+ security_acl = 1;
|
|
else
|
|
{
|
|
fprintf(stderr,
|
|
@@ -328,6 +337,10 @@ 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 (security_acl)
|
|
+ appendPQExpBuffer(pgdumpopts, " --security-acl");
|
|
|
|
if (optind < argc)
|
|
{
|
|
@@ -403,6 +416,19 @@ main(int argc, char *argv[])
|
|
}
|
|
}
|
|
|
|
+ if (security_label > 0)
|
|
+ {
|
|
+ PGresult *res
|
|
+ = PQexec(conn, "SHOW sepostgresql");
|
|
+ if (PQresultStatus(res) != PGRES_TUPLES_OK ||
|
|
+ PQntuples(res) != 1 ||
|
|
+ strcmp(PQgetvalue(res, 0, 0), "on") != 0)
|
|
+ {
|
|
+ fprintf(stderr, "SE-PostgreSQL is not available now.");
|
|
+ exit(1);
|
|
+ }
|
|
+ }
|
|
+
|
|
/*
|
|
* Open the output file if required, otherwise use stdout
|
|
*/
|
|
@@ -1130,55 +1156,56 @@ 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 "
|
|
+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, "
|
|
+ "%s AS security_label "
|
|
"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 ? "sepgsql_raw_to_trans(datselabel)" : "null::text");
|
|
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 "
|
|
+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, "
|
|
+ "null::text "
|
|
"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 "
|
|
+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, "
|
|
+ "null::text "
|
|
"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 "
|
|
+ "'pg_default' AS dattablespace, "
|
|
+ "null::text "
|
|
"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'))), "
|
|
"pg_encoding_to_char(d.encoding), "
|
|
"null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, "
|
|
"datistemplate, '' as datacl, -1 as datconnlimit, "
|
|
- "'pg_default' AS dattablespace "
|
|
+ "'pg_default' AS dattablespace, "
|
|
+ "null::text "
|
|
"FROM pg_database d "
|
|
"WHERE datallowconn ORDER BY 1");
|
|
else
|
|
@@ -1187,18 +1214,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 "
|
|
+ "'pg_default' AS dattablespace, "
|
|
+ "null::text "
|
|
"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);
|
|
@@ -1211,6 +1240,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, 9);
|
|
char *fdbname;
|
|
|
|
fdbname = strdup(fmtId(dbname));
|
|
@@ -1266,6 +1296,10 @@ dumpCreateDB(PGconn *conn)
|
|
appendPQExpBuffer(buf, " CONNECTION LIMIT = %s",
|
|
dbconnlimit);
|
|
|
|
+ if (security_label > 0 && strlen(dbseclabel) > 0)
|
|
+ appendPQExpBuffer(buf, " SECURITY_LABEL = '%s'",
|
|
+ dbseclabel);
|
|
+
|
|
appendPQExpBuffer(buf, ";\n");
|
|
|
|
if (strcmp(dbistemplate, "t") == 0)
|
|
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
|
|
index f271cbc..adf12c0 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 */
|
|
@@ -290,6 +290,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) ? \
|
|
@@ -349,6 +352,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) -
|
|
@@ -549,6 +571,14 @@ typedef HeapTupleData *HeapTuple;
|
|
#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 f8fa910..762219f 100644
|
|
--- a/src/include/access/sysattr.h
|
|
+++ b/src/include/access/sysattr.h
|
|
@@ -25,7 +25,19 @@
|
|
#define MaxTransactionIdAttributeNumber (-5)
|
|
#define MaxCommandIdAttributeNumber (-6)
|
|
#define TableOidAttributeNumber (-7)
|
|
-#define FirstLowInvalidHeapAttributeNumber (-8)
|
|
+#define SecurityAttributeNumber (-8)
|
|
+#define FirstLowInvalidHeapAttributeNumber (-9)
|
|
|
|
+/*
|
|
+ * Attribute names for the system-defined attributes
|
|
+ */
|
|
+#define SelfItemPointerAttributeName "ctid"
|
|
+#define ObjectIdAttributeName "oid"
|
|
+#define MinTransactionIdAttributeName "xmin"
|
|
+#define MinCommandIdAttributeName "cmin"
|
|
+#define MaxTransactionIdAttributeName "xmax"
|
|
+#define MaxCommandIdAttributeName "cmax"
|
|
+#define TableOidAttributeName "tableoid"
|
|
+#define SecurityAttributeName "security_context"
|
|
|
|
#endif /* SYSATTR_H */
|
|
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
|
|
index 87c931a..b102e90 100644
|
|
--- a/src/include/access/tupdesc.h
|
|
+++ b/src/include/access/tupdesc.h
|
|
@@ -75,6 +75,7 @@ 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 secid attribute in its header */
|
|
int tdrefcount; /* reference count, or -1 if not counting */
|
|
} *TupleDesc;
|
|
|
|
diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h
|
|
index b43408f..c4342b2 100644
|
|
--- a/src/include/bootstrap/bootstrap.h
|
|
+++ b/src/include/bootstrap/bootstrap.h
|
|
@@ -70,7 +70,8 @@ typedef enum
|
|
BootstrapProcess,
|
|
StartupProcess,
|
|
BgWriterProcess,
|
|
- WalWriterProcess
|
|
+ WalWriterProcess,
|
|
+ SelinuxReceiverProcess,
|
|
} AuxProcType;
|
|
|
|
#endif /* BOOTSTRAP_H */
|
|
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
|
|
index 62b9a52..5c008b9 100644
|
|
--- a/src/include/catalog/dependency.h
|
|
+++ b/src/include/catalog/dependency.h
|
|
@@ -156,6 +156,9 @@ typedef enum ObjectClass
|
|
extern void performDeletion(const ObjectAddress *object,
|
|
DropBehavior behavior);
|
|
|
|
+extern void performDeletionNoPerms(const ObjectAddress *object,
|
|
+ DropBehavior behavior);
|
|
+
|
|
extern void performMultipleDeletions(const ObjectAddresses *objects,
|
|
DropBehavior behavior);
|
|
|
|
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
|
|
index d618319..e660545 100644
|
|
--- a/src/include/catalog/heap.h
|
|
+++ b/src/include/catalog/heap.h
|
|
@@ -56,7 +56,8 @@ extern Oid heap_create_with_catalog(const char *relname,
|
|
int oidinhcount,
|
|
OnCommitAction oncommit,
|
|
Datum reloptions,
|
|
- bool allow_system_table_mods);
|
|
+ bool allow_system_table_mods,
|
|
+ Oid *secLabels);
|
|
|
|
extern void heap_drop_with_catalog(Oid relid);
|
|
|
|
@@ -68,12 +69,14 @@ 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 new_att_secid);
|
|
|
|
extern void InsertPgClassTuple(Relation pg_class_desc,
|
|
Relation new_rel_desc,
|
|
Oid new_rel_oid,
|
|
- Datum reloptions);
|
|
+ Datum reloptions,
|
|
+ Oid new_rel_secid);
|
|
|
|
extern List *AddRelationNewConstraints(Relation rel,
|
|
List *newColDefaults,
|
|
@@ -103,6 +106,8 @@ extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno,
|
|
extern Form_pg_attribute SystemAttributeByName(const char *attname,
|
|
bool relhasoids);
|
|
|
|
+extern bool SystemAttributeIsWritable(AttrNumber attnum);
|
|
+
|
|
extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind);
|
|
|
|
extern void CheckAttributeType(const char *attname, Oid atttypid);
|
|
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
|
|
index 0a46611..70024b1 100644
|
|
--- a/src/include/catalog/indexing.h
|
|
+++ b/src/include/catalog/indexing.h
|
|
@@ -252,6 +252,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_security_secid_index, 3401, on pg_security using btree(secid oid_ops, datid oid_ops, relid oid_ops));
|
|
+#define SecuritySecidIndexId 3401
|
|
+DECLARE_INDEX(pg_security_secattr_index, 3402, on pg_security using btree(datid oid_ops, relid oid_ops, secattr text_ops));
|
|
+#define SecuritySecattrIndexId 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_attribute.h b/src/include/catalog/pg_attribute.h
|
|
index eaa405f..165b54d 100644
|
|
--- a/src/include/catalog/pg_attribute.h
|
|
+++ b/src/include/catalog/pg_attribute.h
|
|
@@ -276,6 +276,7 @@ DATA(insert ( 1247 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0 _null_));
|
|
DATA(insert ( 1247 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_));
|
|
DATA(insert ( 1247 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_));
|
|
DATA(insert ( 1247 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_));
|
|
+DATA(insert ( 1247 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_));
|
|
|
|
/* ----------------
|
|
* pg_proc
|
|
@@ -340,6 +341,7 @@ DATA(insert ( 1255 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0 _null_));
|
|
DATA(insert ( 1255 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_));
|
|
DATA(insert ( 1255 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_));
|
|
DATA(insert ( 1255 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_));
|
|
+DATA(insert ( 1255 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_));
|
|
|
|
/* ----------------
|
|
* pg_attribute
|
|
@@ -390,6 +392,7 @@ DATA(insert ( 1249 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0 _null_));
|
|
DATA(insert ( 1249 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_));
|
|
DATA(insert ( 1249 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_));
|
|
DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_));
|
|
+DATA(insert ( 1249 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_));
|
|
|
|
/* ----------------
|
|
* pg_class
|
|
@@ -454,6 +457,7 @@ DATA(insert ( 1259 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0 _null_));
|
|
DATA(insert ( 1259 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_));
|
|
DATA(insert ( 1259 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_));
|
|
DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_));
|
|
+DATA(insert ( 1259 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_));
|
|
|
|
/* ----------------
|
|
* pg_index
|
|
diff --git a/src/include/catalog/pg_conversion_fn.h b/src/include/catalog/pg_conversion_fn.h
|
|
index 83cf657..eecebd3 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, Oid consecid, bool def);
|
|
extern void RemoveConversionById(Oid conversionOid);
|
|
extern Oid FindConversion(const char *conname, Oid connamespace);
|
|
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 6dd2fb0..b84c0d2 100644
|
|
--- a/src/include/catalog/pg_largeobject.h
|
|
+++ b/src/include/catalog/pg_largeobject.h
|
|
@@ -51,7 +51,7 @@ 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 secid);
|
|
extern void LargeObjectDrop(Oid loid);
|
|
extern void LargeObjectAlterOwner(Oid loid, Oid newOwnerId);
|
|
extern bool LargeObjectExists(Oid loid);
|
|
diff --git a/src/include/catalog/pg_namespace.h b/src/include/catalog/pg_namespace.h
|
|
index 9168079..7e8487a 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 nspsecid);
|
|
|
|
#endif /* PG_NAMESPACE_H */
|
|
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
|
|
index 0285acd..c6a5247 100644
|
|
--- a/src/include/catalog/pg_proc.h
|
|
+++ b/src/include/catalog/pg_proc.h
|
|
@@ -4335,6 +4335,19 @@ DESCR("I/O");
|
|
DATA(insert OID = 2963 ( uuid_hash PGNSP PGUID 12 1 0 0 f f f t f i 1 0 23 "2950" _null_ _null_ _null_ _null_ uuid_hash _null_ _null_ _null_ ));
|
|
DESCR("hash");
|
|
|
|
+/* SE-PostgreSQL related functions */
|
|
+DATA(insert OID = 3415 ( 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 = 3416 ( 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 = 3417 ( sepgsql_server_getcon PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ sepgsql_server_getcon _null_ _null_ _null_ ));
|
|
+DATA(insert OID = 3418 ( sepgsql_get_user PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_user _null_ _null_ _null_ ));
|
|
+DATA(insert OID = 3419 ( sepgsql_set_user PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_user _null_ _null_ _null_ ));
|
|
+DATA(insert OID = 3420 ( sepgsql_get_role PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_role _null_ _null_ _null_ ));
|
|
+DATA(insert OID = 3421 ( sepgsql_set_role PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_role _null_ _null_ _null_ ));
|
|
+DATA(insert OID = 3422 ( sepgsql_get_type PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_type _null_ _null_ _null_ ));
|
|
+DATA(insert OID = 3423 ( sepgsql_set_type PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_type _null_ _null_ _null_ ));
|
|
+DATA(insert OID = 3424 ( sepgsql_get_range PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_range _null_ _null_ _null_ ));
|
|
+DATA(insert OID = 3425 ( sepgsql_set_range PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_range _null_ _null_ _null_ ));
|
|
+
|
|
/* enum related procs */
|
|
DATA(insert OID = 3504 ( anyenum_in PGNSP PGUID 12 1 0 0 f f f t f i 1 0 3500 "2275" _null_ _null_ _null_ _null_ anyenum_in _null_ _null_ _null_ ));
|
|
DESCR("I/O");
|
|
diff --git a/src/include/catalog/pg_proc_fn.h b/src/include/catalog/pg_proc_fn.h
|
|
index e3453f2..c25af6b 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,
|
|
+ Node *proseclabel);
|
|
|
|
extern bool function_parse_error_transpose(const char *prosrc);
|
|
|
|
diff --git a/src/include/catalog/pg_security.h b/src/include/catalog/pg_security.h
|
|
new file mode 100644
|
|
index 0000000..973df01
|
|
--- /dev/null
|
|
+++ b/src/include/catalog/pg_security.h
|
|
@@ -0,0 +1,89 @@
|
|
+/*
|
|
+ * src/include/catalog/pg_security.h
|
|
+ * Definition of the security label relation (pg_security)
|
|
+ *
|
|
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
|
+ * Portions Copyright (c) 1994, Regents of the University of California
|
|
+ */
|
|
+#ifndef PG_SECURITY_H
|
|
+#define PG_SECURITY_H
|
|
+
|
|
+#include "catalog/genbki.h"
|
|
+
|
|
+#include "access/htup.h"
|
|
+#include "nodes/parsenodes.h"
|
|
+#include "utils/acl.h"
|
|
+#include "utils/relcache.h"
|
|
+
|
|
+#define SecurityRelationId 3400
|
|
+
|
|
+CATALOG(pg_security,3400) BKI_SHARED_RELATION BKI_WITHOUT_OIDS
|
|
+{
|
|
+ /* Identifier of the security attribute */
|
|
+ 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 security attribute */
|
|
+ text secattr;
|
|
+} FormData_pg_security;
|
|
+
|
|
+/*
|
|
+ * Form_pg_security corresponds to a pointer to a tuple with
|
|
+ * the format of pg_security relation.
|
|
+ */
|
|
+typedef FormData_pg_security *Form_pg_security;
|
|
+
|
|
+/*
|
|
+ * Compiler constants for pg_security
|
|
+ */
|
|
+#define Natts_pg_security 4
|
|
+#define Anum_pg_security_secid 1
|
|
+#define Anum_pg_security_datid 2
|
|
+#define Anum_pg_security_relid 3
|
|
+#define Anum_pg_security_secattr 4
|
|
+
|
|
+/*
|
|
+ * Functions to translate between security label and identifier
|
|
+ */
|
|
+extern void
|
|
+securityPostBootstrapingMode(void);
|
|
+
|
|
+extern void
|
|
+securityOnCreateDatabase(Oid src_datid, Oid dst_datid);
|
|
+
|
|
+extern void
|
|
+securityOnDropDatabase(Oid datid);
|
|
+
|
|
+extern bool
|
|
+securityTupleDescHasSecid(Oid relid, char relkind);
|
|
+
|
|
+extern Oid
|
|
+securityRawSecLabelIn(Oid relid, char *seclabel);
|
|
+
|
|
+extern char *
|
|
+securityRawSecLabelOut(Oid relid, Oid secid);
|
|
+
|
|
+extern Oid
|
|
+securityTransSecLabelIn(Oid relid, char *seclabel);
|
|
+
|
|
+extern char *
|
|
+securityTransSecLabelOut(Oid relid, Oid secid);
|
|
+
|
|
+extern Datum
|
|
+securitySysattSecLabelOut(Oid relid, HeapTuple tuple);
|
|
+
|
|
+extern void
|
|
+securityReclaimOnDropTable(Oid relid);
|
|
+
|
|
+extern void
|
|
+seclabelRelationReclaim(Oid relOid);
|
|
+
|
|
+extern Datum
|
|
+seclabel_to_secid(PG_FUNCTION_ARGS);
|
|
+
|
|
+#endif /* PG_SECURITY_H */
|
|
diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h
|
|
index bd6e0cf..b7c39f0 100644
|
|
--- a/src/include/catalog/toasting.h
|
|
+++ b/src/include/catalog/toasting.h
|
|
@@ -58,5 +58,8 @@ DECLARE_TOAST(pg_database, 2844, 2845);
|
|
DECLARE_TOAST(pg_shdescription, 2846, 2847);
|
|
#define PgShdescriptionToastTable 2846
|
|
#define PgShdescriptionToastIndex 2847
|
|
+DECLARE_TOAST(pg_security, 3403, 3404);
|
|
+#define PgSecurityToastTable 3403
|
|
+#define PgSecurityToastIndex 3404
|
|
|
|
#endif /* TOASTING_H */
|
|
diff --git a/src/include/commands/alter.h b/src/include/commands/alter.h
|
|
index ab5d6f4..c332b71 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 0ec23d7..3980056 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, DefElem *seclabel);
|
|
|
|
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 4356492..8f903af 100644
|
|
--- a/src/include/commands/defrem.h
|
|
+++ b/src/include/commands/defrem.h
|
|
@@ -53,6 +53,7 @@ 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, DefElem *seclabel);
|
|
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 5f384a1..1fc113b 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, DefElem *seclabel);
|
|
|
|
#endif /* SCHEMACMDS_H */
|
|
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
|
|
index 21b067c..2ac7b3c 100644
|
|
--- a/src/include/commands/tablecmds.h
|
|
+++ b/src/include/commands/tablecmds.h
|
|
@@ -35,6 +35,9 @@ extern void AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
|
|
Oid oldNspOid, Oid newNspOid,
|
|
bool hasDependEntry);
|
|
|
|
+extern void AlterRelationSecLabel(RangeVar *relation, const char *attname,
|
|
+ ObjectType objtype, DefElem *seclabel);
|
|
+
|
|
extern void CheckTableNotInUse(Relation rel, const char *stmt);
|
|
|
|
extern void ExecuteTruncate(TruncateStmt *stmt);
|
|
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
|
|
index 43395e9..09d6148 100644
|
|
--- a/src/include/executor/executor.h
|
|
+++ b/src/include/executor/executor.h
|
|
@@ -130,7 +130,7 @@ extern TupleHashEntry FindTupleHashEntry(TupleHashTable hashtable,
|
|
/*
|
|
* prototypes from functions in execJunk.c
|
|
*/
|
|
-extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid,
|
|
+extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, bool hasseclabel,
|
|
TupleTableSlot *slot);
|
|
extern JunkFilter *ExecInitJunkFilterConversion(List *targetList,
|
|
TupleDesc cleanTupType,
|
|
@@ -163,6 +163,7 @@ extern void InitResultRelInfo(ResultRelInfo *resultRelInfo,
|
|
bool doInstrument);
|
|
extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid);
|
|
extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids);
|
|
+extern bool ExecContextForcesSecids(PlanState *planstate, bool *hassecids);
|
|
extern void ExecConstraints(ResultRelInfo *resultRelInfo,
|
|
TupleTableSlot *slot, EState *estate);
|
|
extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti,
|
|
@@ -216,8 +217,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 hasseclabel);
|
|
+extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hasseclabel);
|
|
extern TupleDesc ExecTypeFromExprList(List *exprList);
|
|
extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg);
|
|
|
|
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
|
|
index e40082d..7fd299c 100644
|
|
--- a/src/include/executor/tuptable.h
|
|
+++ b/src/include/executor/tuptable.h
|
|
@@ -127,6 +127,7 @@ typedef struct TupleTableSlot
|
|
MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */
|
|
HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */
|
|
long tts_off; /* saved state for slot_deform_tuple */
|
|
+ Datum tts_seclabel; /* temp storage for the given security_label */
|
|
} TupleTableSlot;
|
|
|
|
#define TTS_HAS_PHYSICAL_TUPLE(slot) \
|
|
diff --git a/src/include/libpq/be-fsstubs.h b/src/include/libpq/be-fsstubs.h
|
|
index 862b014..03ca71b 100644
|
|
--- a/src/include/libpq/be-fsstubs.h
|
|
+++ b/src/include/libpq/be-fsstubs.h
|
|
@@ -37,6 +37,9 @@ extern Datum lo_tell(PG_FUNCTION_ARGS);
|
|
extern Datum lo_unlink(PG_FUNCTION_ARGS);
|
|
extern Datum lo_truncate(PG_FUNCTION_ARGS);
|
|
|
|
+extern Datum lo_get_security(PG_FUNCTION_ARGS);
|
|
+extern Datum lo_set_security(PG_FUNCTION_ARGS);
|
|
+
|
|
/*
|
|
* compatibility option for access control
|
|
*/
|
|
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
|
|
index 925375b..0285a39 100644
|
|
--- a/src/include/nodes/nodes.h
|
|
+++ b/src/include/nodes/nodes.h
|
|
@@ -337,6 +337,7 @@ typedef enum NodeTag
|
|
T_CreateUserMappingStmt,
|
|
T_AlterUserMappingStmt,
|
|
T_DropUserMappingStmt,
|
|
+ T_AlterSecLabelStmt,
|
|
|
|
/*
|
|
* TAGS FOR PARSE TREE NODES (parsenodes.h)
|
|
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
|
|
index 487a226..cdb49d4 100644
|
|
--- a/src/include/nodes/parsenodes.h
|
|
+++ b/src/include/nodes/parsenodes.h
|
|
@@ -463,6 +463,7 @@ typedef struct ColumnDef
|
|
Node *raw_default; /* default value (untransformed parse tree) */
|
|
Node *cooked_default; /* default value (transformed expr tree) */
|
|
List *constraints; /* other constraints on column */
|
|
+ Node *secLabel; /* security label of column */
|
|
} ColumnDef;
|
|
|
|
/*
|
|
@@ -1069,6 +1070,7 @@ typedef struct CreateSchemaStmt
|
|
NodeTag type;
|
|
char *schemaname; /* the name of the schema to create */
|
|
char *authid; /* the owner of the created schema */
|
|
+ Node *secLabel; /* explicitly specified security label */
|
|
List *schemaElts; /* schema components (list of parsenodes) */
|
|
} CreateSchemaStmt;
|
|
|
|
@@ -1335,6 +1337,7 @@ typedef struct CreateStmt
|
|
List *options; /* options from WITH clause */
|
|
OnCommitAction oncommit; /* what do we do at COMMIT? */
|
|
char *tablespacename; /* table space to use, or NULL */
|
|
+ List *secLabel; /* explicitly specified security label */
|
|
} CreateStmt;
|
|
|
|
/* ----------
|
|
@@ -1639,6 +1642,7 @@ typedef struct CreateSeqStmt
|
|
NodeTag type;
|
|
RangeVar *sequence; /* the sequence to create */
|
|
List *options;
|
|
+ Node *secLabel;
|
|
} CreateSeqStmt;
|
|
|
|
typedef struct AlterSeqStmt
|
|
@@ -1993,6 +1997,20 @@ typedef struct AlterOwnerStmt
|
|
char *newowner; /* the new owner */
|
|
} AlterOwnerStmt;
|
|
|
|
+/* ----------------------
|
|
+ * Alter Object Security Label Statement
|
|
+ * ----------------------
|
|
+ */
|
|
+typedef struct AlterSecLabelStmt
|
|
+{
|
|
+ NodeTag type;
|
|
+ ObjectType objectType; /* OBJECT_TABLE, OBJECT_COLUMN, 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 *subname; /* column name, if needed */
|
|
+ Node *secLabel; /* the new security label */
|
|
+} AlterSecLabelStmt;
|
|
|
|
/* ----------------------
|
|
* Create Rule Statement
|
|
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
|
|
index 23a5117..2525e22 100644
|
|
--- a/src/include/nodes/plannodes.h
|
|
+++ b/src/include/nodes/plannodes.h
|
|
@@ -16,6 +16,7 @@
|
|
|
|
#include "access/sdir.h"
|
|
#include "nodes/bitmapset.h"
|
|
+#include "nodes/parsenodes.h"
|
|
#include "nodes/primnodes.h"
|
|
#include "storage/itemptr.h"
|
|
|
|
@@ -239,6 +240,12 @@ typedef struct Scan
|
|
{
|
|
Plan plan;
|
|
Index scanrelid; /* relid is index into the range table */
|
|
+
|
|
+ /*
|
|
+ * Row-level access control stuff. Zero means we don't need
|
|
+ * to apply row-level access control on the Scan.
|
|
+ */
|
|
+ uint32 rowlvPerms;
|
|
} Scan;
|
|
|
|
/* ----------------
|
|
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
|
|
index ea48889..6133c38 100644
|
|
--- a/src/include/nodes/relation.h
|
|
+++ b/src/include/nodes/relation.h
|
|
@@ -383,6 +383,15 @@ typedef struct RelOptInfo
|
|
* list just to avoid recomputing the best inner indexscan repeatedly for
|
|
* similar outer relations. See comments for InnerIndexscanInfo.
|
|
*/
|
|
+
|
|
+ /*
|
|
+ * Permissions used in Row-level access control features both of DAC
|
|
+ * and MAC. The lower 16bit is used for DAC, and rest of upper bits
|
|
+ * are used for MAC. When rowlvPerms is zero, so it means we don't need
|
|
+ * to apply the row-level stuff on the relation in both of levels.
|
|
+ * It can be used as a hint for optimization stuff.
|
|
+ */
|
|
+ uint32 rowlvPerms;
|
|
} RelOptInfo;
|
|
|
|
/*
|
|
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
|
|
index 67e9cb4..df9bb5b 100644
|
|
--- a/src/include/parser/kwlist.h
|
|
+++ b/src/include/parser/kwlist.h
|
|
@@ -88,6 +88,7 @@ PG_KEYWORD("connection", CONNECTION, UNRESERVED_KEYWORD)
|
|
PG_KEYWORD("constraint", CONSTRAINT, RESERVED_KEYWORD)
|
|
PG_KEYWORD("constraints", CONSTRAINTS, UNRESERVED_KEYWORD)
|
|
PG_KEYWORD("content", CONTENT_P, UNRESERVED_KEYWORD)
|
|
+PG_KEYWORD("context", CONTEXT_P, UNRESERVED_KEYWORD)
|
|
PG_KEYWORD("continue", CONTINUE_P, UNRESERVED_KEYWORD)
|
|
PG_KEYWORD("conversion", CONVERSION_P, UNRESERVED_KEYWORD)
|
|
PG_KEYWORD("copy", COPY, UNRESERVED_KEYWORD)
|
|
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
|
|
index 3473227..28301d0 100644
|
|
--- a/src/include/pg_config.h.in
|
|
+++ b/src/include/pg_config.h.in
|
|
@@ -263,6 +263,9 @@
|
|
/* Define to 1 if you have the <ldap.h> header file. */
|
|
#undef HAVE_LDAP_H
|
|
|
|
+/* Define to 1 if you have the `audit' library (-laudit). */
|
|
+#undef HAVE_LIBAUDIT
|
|
+
|
|
/* Define to 1 if you have the `crypto' library (-lcrypto). */
|
|
#undef HAVE_LIBCRYPTO
|
|
|
|
@@ -391,6 +394,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/security/rowlevel.h b/src/include/security/rowlevel.h
|
|
new file mode 100644
|
|
index 0000000..a737a0d
|
|
--- /dev/null
|
|
+++ b/src/include/security/rowlevel.h
|
|
@@ -0,0 +1,44 @@
|
|
+/*
|
|
+ * src/include/security/rowlevel.h
|
|
+ * Definition of the facility of row-level access controls
|
|
+ *
|
|
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
|
+ * Portions Copyright (c) 1994, Regents of the University of California
|
|
+ */
|
|
+#ifndef ROWLEVEL_H
|
|
+#define ROWLEVEL_H
|
|
+
|
|
+#include "access/htup.h"
|
|
+#include "executor/tuptable.h"
|
|
+#include "nodes/plannodes.h"
|
|
+#include "utils/relcache.h"
|
|
+
|
|
+#define ROWLV_BYPASS_MODE 1
|
|
+#define ROWLV_FILTER_MODE 2
|
|
+#define ROWLV_ABORT_MODE 3
|
|
+
|
|
+extern int
|
|
+rowlvGetPerformingMode(void);
|
|
+
|
|
+extern int
|
|
+rowlvSetPerformingMode(int mode);
|
|
+
|
|
+extern uint32
|
|
+rowlvSetupPermissions(RangeTblEntry *rte);
|
|
+
|
|
+extern bool
|
|
+rowlvExecScanFilter(Scan *scan, Relation rel, TupleTableSlot *slot);
|
|
+
|
|
+extern void
|
|
+rowlvExecScanAbort(Scan *scan, Relation rel, TupleTableSlot *slot);
|
|
+
|
|
+extern void
|
|
+rowlvHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal);
|
|
+
|
|
+extern void
|
|
+rowlvHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup);
|
|
+
|
|
+extern bool
|
|
+rowlvCopyToTuple(Relation rel, HeapTuple tuple);
|
|
+
|
|
+#endif /* ROWLEVEL_H */
|
|
diff --git a/src/include/security/sepgsql.h b/src/include/security/sepgsql.h
|
|
new file mode 100644
|
|
index 0000000..d5ac80b
|
|
--- /dev/null
|
|
+++ b/src/include/security/sepgsql.h
|
|
@@ -0,0 +1,725 @@
|
|
+/*
|
|
+ * src/include/security/sepgsql.h
|
|
+ * Headers of SE-PostgreSQL
|
|
+ *
|
|
+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
|
+ * Portions Copyright (c) 1994, Regents of the University of California
|
|
+ */
|
|
+#ifndef SEPGSQL_H
|
|
+#define SEPGSQL_H
|
|
+
|
|
+#include "access/htup.h"
|
|
+#include "catalog/dependency.h"
|
|
+#include "executor/execdesc.h"
|
|
+#include "fmgr.h"
|
|
+#include "nodes/parsenodes.h"
|
|
+#include "storage/large_object.h"
|
|
+#include "utils/relcache.h"
|
|
+
|
|
+#ifdef HAVE_SELINUX
|
|
+
|
|
+#include <selinux/selinux.h>
|
|
+
|
|
+/* GUC parameter to turn on/off SE-PostgreSQL */
|
|
+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 parameter to turn on/off Row-level controls */
|
|
+extern bool sepostgresql_row_level;
|
|
+
|
|
+/* GUC parameter to turn on/off mcstrans */
|
|
+extern bool sepostgresql_mcstrans;
|
|
+
|
|
+/* 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_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_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_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_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_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_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_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_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__REFERENCE (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__USAGE (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_COLUMN__REFERENCE (1<<9)
|
|
+
|
|
+#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)
|
|
+
|
|
+/*
|
|
+ * sepgsql_sid_t : alternative representation of security context
|
|
+ */
|
|
+typedef struct {
|
|
+ Oid relid;
|
|
+ Oid secid;
|
|
+} sepgsql_sid_t;
|
|
+
|
|
+#define SidIsValid(sid) (OidIsValid((sid).relid) && OidIsValid((sid).secid))
|
|
+
|
|
+/*
|
|
+ * selinux.c : communication to in-kernel SELinux
|
|
+ */
|
|
+extern void sepgsqlInitialize(void);
|
|
+extern Size sepgsqlShmemSize(void);
|
|
+extern bool sepgsqlIsEnabled(void);
|
|
+extern bool sepgsqlIsEnabledBootstrap(void);
|
|
+extern bool sepgsqlGetEnforce(void);
|
|
+extern char *sepgsqlShowMode(void);
|
|
+extern char *sepgsqlGetServerLabel(void);
|
|
+extern char *sepgsqlGetClientLabel(void);
|
|
+extern char *sepgsqlSetClientLabel(char *new_label);
|
|
+extern bool
|
|
+sepgsqlComputePerms(char *scontext, char *tcontext,
|
|
+ uint16 tclass, uint32 required,
|
|
+ const char *audit_name, bool abort);
|
|
+extern char *
|
|
+sepgsqlComputeCreate(char *scontext, char *tcontext, uint16 tclass);
|
|
+extern bool
|
|
+sepgsqlClientHasPerms(sepgsql_sid_t tsid, uint16 tclass, uint32 required,
|
|
+ const char *audit_name, bool abort);
|
|
+extern sepgsql_sid_t
|
|
+sepgsqlClientCreateSecid(sepgsql_sid_t tsid, uint16 tclass, Oid nrelid);
|
|
+extern char *
|
|
+sepgsqlClientCreateLabel(sepgsql_sid_t tsid, uint16 tclass);
|
|
+
|
|
+extern bool sepgsqlReceiverStart(void);
|
|
+extern void sepgsqlReceiverMain(void);
|
|
+
|
|
+/*
|
|
+ * bridge.c : new style security hooks
|
|
+ */
|
|
+
|
|
+/* pg_attribute */
|
|
+extern Oid
|
|
+sepgsql_attribute_create(Oid relOid, ColumnDef *cdef);
|
|
+extern void
|
|
+sepgsql_attribute_alter(Oid relOid, const char *attname);
|
|
+extern void
|
|
+sepgsql_attribute_drop(Oid relOid, AttrNumber attnum);
|
|
+extern void
|
|
+sepgsql_attribute_grant(Oid relOid, AttrNumber attnum);
|
|
+extern Oid
|
|
+sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, DefElem *newLabel);
|
|
+
|
|
+/* pg_cast */
|
|
+extern Oid
|
|
+sepgsql_cast_create(Oid sourceTypOid, Oid targetTypOid, Oid funcOid);
|
|
+extern void
|
|
+sepgsql_cast_drop(Oid castOid);
|
|
+
|
|
+/* pg_class */
|
|
+extern Oid *
|
|
+sepgsql_relation_create(const char *relName,
|
|
+ char relkind,
|
|
+ TupleDesc tupDesc,
|
|
+ Oid nspOid,
|
|
+ DefElem *relLabel,
|
|
+ List *colList,
|
|
+ bool createAs,
|
|
+ bool permission);
|
|
+extern Oid *
|
|
+sepgsql_relation_copy(Relation src);
|
|
+extern void
|
|
+sepgsql_relation_alter(Oid relOid, const char *newName, Oid newNsp);
|
|
+extern void
|
|
+sepgsql_relation_drop(Oid relOid);
|
|
+extern void
|
|
+sepgsql_relation_grant(Oid relOid);
|
|
+extern Oid
|
|
+sepgsql_relation_relabel(Oid relOid, DefElem *newLabel);
|
|
+extern void
|
|
+sepgsql_relation_get_transaction_id(Oid relOid);
|
|
+extern void
|
|
+sepgsql_relation_copy_definition(Oid relOid);
|
|
+extern void
|
|
+sepgsql_relation_truncate(Relation rel);
|
|
+extern void
|
|
+sepgsql_relation_references(Relation rel, int16 *attnums, int natts);
|
|
+extern void
|
|
+sepgsql_relation_lock(Oid relOid);
|
|
+extern void
|
|
+sepgsql_view_replace(Oid viewOid);
|
|
+extern void
|
|
+sepgsql_index_create(Oid relOid, Oid nspOid);
|
|
+extern void
|
|
+sepgsql_sequence_get_value(Oid seqOid);
|
|
+extern void
|
|
+sepgsql_sequence_next_value(Oid seqOid);
|
|
+extern void
|
|
+sepgsql_sequence_set_value(Oid seqOid);
|
|
+
|
|
+/* pg_conversion */
|
|
+extern Oid
|
|
+sepgsql_conversion_create(const char *convName, Oid nspOid, Oid procOid);
|
|
+extern void
|
|
+sepgsql_conversion_alter(Oid convOid, const char *newName);
|
|
+extern void
|
|
+sepgsql_conversion_drop(Oid convOid);
|
|
+
|
|
+/* pg_database */
|
|
+extern Oid
|
|
+sepgsql_database_create(const char *datName, Oid srcDatOid, DefElem *newLabel);
|
|
+extern void
|
|
+sepgsql_database_alter(Oid datOid);
|
|
+extern void
|
|
+sepgsql_database_drop(Oid datOid);
|
|
+extern Oid
|
|
+sepgsql_database_relabel(Oid datOid, DefElem *newLabel);
|
|
+extern void
|
|
+sepgsql_database_grant(Oid datOid);
|
|
+extern void
|
|
+sepgsql_database_access(Oid datOid);
|
|
+extern bool
|
|
+sepgsql_database_superuser(Oid datOid);
|
|
+extern void
|
|
+sepgsql_database_load_module(Oid datOid, const char *filename);
|
|
+
|
|
+/* pg_foreign_data_wrapper */
|
|
+extern Oid
|
|
+sepgsql_fdw_create(const char *fdwName, Oid fdwValidator);
|
|
+extern void
|
|
+sepgsql_fdw_alter(Oid fdwOid, Oid newValidator);
|
|
+extern void
|
|
+sepgsql_fdw_drop(Oid fdwOid);
|
|
+extern void
|
|
+sepgsql_fdw_grant(Oid fdwOid);
|
|
+
|
|
+/* pg_foreign_server */
|
|
+extern Oid
|
|
+sepgsql_foreign_server_create(const char *fsrvName);
|
|
+extern void
|
|
+sepgsql_foreign_server_alter(Oid fsrvOid);
|
|
+extern void
|
|
+sepgsql_foreign_server_drop(Oid fsrvOid);
|
|
+extern void
|
|
+sepgsql_foreign_server_grant(Oid fsrvOid);
|
|
+
|
|
+/* pg_language */
|
|
+extern Oid
|
|
+sepgsql_language_create(const char *langName, Oid handlerOid, Oid validatorOid);
|
|
+extern void
|
|
+sepgsql_language_alter(Oid langOid);
|
|
+extern void
|
|
+sepgsql_language_drop(Oid langOid);
|
|
+extern void
|
|
+sepgsql_language_grant(Oid langOid);
|
|
+
|
|
+/* pg_largeobject */
|
|
+extern Oid
|
|
+sepgsql_largeobject_create(Oid loid, Value *secLabel);
|
|
+extern void
|
|
+sepgsql_largeobject_alter(Oid loid);
|
|
+extern void
|
|
+sepgsql_largeobject_relabel(Oid loid, Value *secLabel);
|
|
+extern void
|
|
+sepgsql_largeobject_drop(Oid loid);
|
|
+extern void
|
|
+sepgsql_largeobject_read(Oid loid, Snapshot snapshot);
|
|
+extern void
|
|
+sepgsql_largeobject_write(Oid loid, Snapshot snapshot);
|
|
+extern void
|
|
+sepgsql_largeobject_export(Oid loid, const char *filename);
|
|
+extern Oid
|
|
+sepgsql_largeobject_import(Oid loid, const char *filename);
|
|
+
|
|
+/* pg_namespace */
|
|
+extern Oid
|
|
+sepgsql_schema_create(const char *nspName, bool isTemp, DefElem *newLabel);
|
|
+extern void
|
|
+sepgsql_schema_alter(Oid nspOid);
|
|
+extern void
|
|
+sepgsql_schema_drop(Oid nspOid);
|
|
+extern Oid
|
|
+sepgsql_schema_relabel(Oid nspOid, DefElem *newLabel);
|
|
+extern void
|
|
+sepgsql_schema_grant(Oid nspOid);
|
|
+extern bool
|
|
+sepgsql_schema_search(Oid nspOid, bool abort);
|
|
+
|
|
+/* pg_opclass */
|
|
+extern Oid
|
|
+sepgsql_opclass_create(const char *opcName, Oid nspOid);
|
|
+extern void
|
|
+sepgsql_opclass_alter(Oid opcOid, const char *newName);
|
|
+extern void
|
|
+sepgsql_opclass_drop(Oid opcOid);
|
|
+
|
|
+/* pg_opfamily */
|
|
+extern Oid
|
|
+sepgsql_opfamily_create(const char *opfName, Oid nspOid);
|
|
+extern void
|
|
+sepgsql_opfamily_alter(Oid opfOid, const char *newName);
|
|
+extern void
|
|
+sepgsql_opfamily_drop(Oid opfOid);
|
|
+extern void
|
|
+sepgsql_opfamily_add_operator(Oid opfOid, Oid operOid);
|
|
+extern void
|
|
+sepgsql_opfamily_add_procedure(Oid opfOid, Oid procOid);
|
|
+
|
|
+/* pg_operator */
|
|
+extern Oid
|
|
+sepgsql_operator_create(const char *oprName, Oid oprOid, Oid nspOid,
|
|
+ Oid codeFn, Oid restFn, Oid joinFn);
|
|
+extern void
|
|
+sepgsql_operator_alter(Oid oprOid);
|
|
+extern void
|
|
+sepgsql_operator_drop(Oid oprOid);
|
|
+
|
|
+/* pg_proc */
|
|
+extern Oid
|
|
+sepgsql_proc_create(const char *procName, HeapTuple oldTup,
|
|
+ Oid nspOid, Oid langOid, DefElem *newLabel);
|
|
+extern void
|
|
+sepgsql_proc_alter(Oid procOid, const char *newName, Oid newNsp);
|
|
+extern void
|
|
+sepgsql_proc_drop(Oid procOid);
|
|
+extern Oid
|
|
+sepgsql_proc_relabel(Oid procOid, DefElem *newLabel);
|
|
+extern void
|
|
+sepgsql_proc_grant(Oid procOid);
|
|
+extern void
|
|
+sepgsql_proc_execute(Oid procOid);
|
|
+extern bool
|
|
+sepgsql_proc_hint_inlined(HeapTuple protup);
|
|
+extern bool
|
|
+sepgsql_proc_entrypoint(HeapTuple protup);
|
|
+extern char *
|
|
+sepgsql_proc_trusted(HeapTuple protup, MemoryContext mcxt);
|
|
+
|
|
+/* pg_rewrite */
|
|
+extern void
|
|
+sepgsql_rule_create(Oid relOid, const char *ruleName);
|
|
+extern void
|
|
+sepgsql_rule_drop(Oid relOid, const char *ruleName);
|
|
+
|
|
+/* pg_trigger */
|
|
+extern void
|
|
+sepgsql_trigger_create(Oid relOid, const char *trigName, Oid procOid);
|
|
+extern void
|
|
+sepgsql_trigger_alter(Oid relOid, const char *trigName);
|
|
+extern void
|
|
+sepgsql_trigger_drop(Oid relOid, const char *trigName);
|
|
+
|
|
+/* pg_ts_config */
|
|
+extern Oid
|
|
+sepgsql_ts_config_create(const char *cfgName, Oid nspOid);
|
|
+extern void
|
|
+sepgsql_ts_config_alter(Oid cfgOid, const char *newName);
|
|
+extern void
|
|
+sepgsql_ts_config_drop(Oid cfgOid);
|
|
+
|
|
+/* pg_ts_dict */
|
|
+extern Oid
|
|
+sepgsql_ts_dict_create(const char *dictName, Oid nspOid);
|
|
+extern void
|
|
+sepgsql_ts_dict_alter(Oid dictOid, const char *newName);
|
|
+extern void
|
|
+sepgsql_ts_dict_drop(Oid dictOid);
|
|
+
|
|
+/* pg_ts_parser */
|
|
+extern Oid
|
|
+sepgsql_ts_parser_create(const char *prsName, Oid nspOid,
|
|
+ Oid startFn, Oid tokenFn, Oid sendFn,
|
|
+ Oid headlineFn, Oid lextypeFn);
|
|
+extern void
|
|
+sepgsql_ts_parser_alter(Oid prsOid, const char *newName);
|
|
+extern void
|
|
+sepgsql_ts_parser_drop(Oid prsOid);
|
|
+
|
|
+/* pg_ts_templace */
|
|
+extern Oid
|
|
+sepgsql_ts_template_create(const char *tmplName, Oid nspOid,
|
|
+ Oid initFn, Oid lexizeFn);
|
|
+extern void
|
|
+sepgsql_ts_template_alter(Oid tmplOid, const char *newName);
|
|
+extern void
|
|
+sepgsql_ts_template_drop(Oid tmplOid);
|
|
+
|
|
+/* pg_type */
|
|
+extern Oid
|
|
+sepgsql_type_create(const char *typName, HeapTuple oldTup, Oid nspOid,
|
|
+ Oid inputProc, Oid outputProc, Oid recvProc, Oid sendProc,
|
|
+ Oid modinProc, Oid modoutProc, Oid analyzeProc);
|
|
+extern void
|
|
+sepgsql_type_alter(Oid typOid, const char *newName, Oid newNsp);
|
|
+extern void
|
|
+sepgsql_type_drop(Oid typOid);
|
|
+
|
|
+/* misc objects */
|
|
+extern void
|
|
+sepgsql_sysobj_drop(const ObjectAddress *object);
|
|
+
|
|
+/* filesystem objects */
|
|
+void
|
|
+sepgsql_file_stat(const char *filename);
|
|
+void
|
|
+sepgsql_file_read(const char *filename);
|
|
+void
|
|
+sepgsql_file_write(const char *filename);
|
|
+
|
|
+/*
|
|
+ * checker.c : check permission on given queries
|
|
+ */
|
|
+extern void
|
|
+sepgsqlCheckRTEPerms(RangeTblEntry *rte);
|
|
+
|
|
+extern void
|
|
+sepgsqlCheckCopyTable(Relation rel, List *attnumlist, bool is_from);
|
|
+
|
|
+extern void
|
|
+sepgsqlCheckSelectInto(Oid relaionId);
|
|
+
|
|
+extern bool
|
|
+sepgsqlExecScan(Relation rel, HeapTuple tuple, uint32 required, bool abort);
|
|
+
|
|
+extern uint32
|
|
+sepgsqlSetupTuplePerms(RangeTblEntry *rte);
|
|
+
|
|
+extern void
|
|
+sepgsqlHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal);
|
|
+
|
|
+extern void
|
|
+sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup);
|
|
+
|
|
+/*
|
|
+ * label.c : security label management
|
|
+ */
|
|
+extern bool sepgsqlTupleDescHasSecid(Oid relid, char relkind);
|
|
+
|
|
+extern void sepgsqlPostBootstrapingMode(void);
|
|
+
|
|
+extern void sepgsqlSetDefaultSecid(Relation rel, HeapTuple tuple);
|
|
+extern sepgsql_sid_t sepgsqlGetDefaultDatabaseSecid(Oid src_database_oid);
|
|
+extern sepgsql_sid_t sepgsqlGetDefaultSchemaSecid(Oid database_oid);
|
|
+extern sepgsql_sid_t sepgsqlGetDefaultSchemaTempSecid(Oid database_oid);
|
|
+extern sepgsql_sid_t sepgsqlGetDefaultTableSecid(Oid namespace_oid);
|
|
+extern sepgsql_sid_t sepgsqlGetDefaultSequenceSecid(Oid namespace_oid);
|
|
+extern sepgsql_sid_t sepgsqlGetDefaultProcedureSecid(Oid namespace_oid);
|
|
+extern sepgsql_sid_t sepgsqlGetDefaultColumnSecid(Oid table_oid);
|
|
+extern sepgsql_sid_t sepgsqlGetDefaultTupleSecid(Oid table_oid);
|
|
+extern sepgsql_sid_t sepgsqlGetDefaultBlobSecid(Oid database_oid);
|
|
+
|
|
+extern Oid *sepgsqlCreateTableColumns(CreateStmt *stmt,
|
|
+ const char *relname, Oid namespace_oid,
|
|
+ TupleDesc tupdesc, char relkind);
|
|
+extern Oid *sepgsqlCopyTableColumns(Relation source);
|
|
+
|
|
+extern sepgsql_sid_t
|
|
+sepgsqlGetTupleSecid(Oid tableOid, HeapTuple tuple, uint16 *tclass);
|
|
+extern sepgsql_sid_t
|
|
+sepgsqlGetSysobjSecid(Oid tableOid, Oid objectId, int32 objsubId, uint16 *tclass);
|
|
+
|
|
+extern char *sepgsqlTransSecLabelIn(char *seclabel);
|
|
+extern char *sepgsqlTransSecLabelOut(char *seclabel);
|
|
+extern char *sepgsqlRawSecLabelIn(char *seclabel);
|
|
+extern char *sepgsqlRawSecLabelOut(char *seclabel);
|
|
+extern char *sepgsqlSysattSecLabelOut(Oid relid, HeapTuple tuple);
|
|
+
|
|
+#else /* HAVE_SELINUX */
|
|
+
|
|
+/* avc.c */
|
|
+#define sepgsqlShmemSize() (0)
|
|
+
|
|
+/* checker.c */
|
|
+#define sepgsqlCheckRTEPerms(a) do {} while(0)
|
|
+#define sepgsqlCheckCopyTable(a,b,c) do {} while(0)
|
|
+#define sepgsqlCheckSelectInto(a) do {} while(0)
|
|
+#define sepgsqlExecScan(a,b,c) (true)
|
|
+#define sepgsqlSetupTuplePerms(a) (0)
|
|
+#define sepgsqlHeapTupleInsert(a,b,c) do {} while(0)
|
|
+#define sepgsqlHeapTupleUpdate(a,b,c) do {} while(0)
|
|
+
|
|
+/* core.c */
|
|
+#define sepgsqlIsEnabled() (false)
|
|
+#define sepgsqlInitialize() do {} while(0)
|
|
+
|
|
+/* bridge.c */
|
|
+#define sepgsql_attribute_create(a,b) (InvalidOid)
|
|
+#define sepgsql_attribute_alter(a,b) do {} while(0)
|
|
+#define sepgsql_attribute_drop(a,b) do {} while(0)
|
|
+#define sepgsql_attribute_grant(a,b) do {} while(0)
|
|
+#define sepgsql_attribute_relabel(a,b,c) (InvalidOid)
|
|
+
|
|
+#define sepgsql_cast_create(a,b,c) (InvalidOid)
|
|
+#define sepgsql_cast_drop(a) (InvalidOid)
|
|
+
|
|
+#define sepgsql_relation_create(a,b,c,d,e,f) (NULL)
|
|
+#define sepgsql_relation_copy(a) (NULL)
|
|
+#define sepgsql_relation_alter(a,b,c) do {} while(0)
|
|
+#define sepgsql_relation_drop(a) do {} while(0)
|
|
+#define sepgsql_relation_grant(a) do {} while(0)
|
|
+#define sepgsql_relation_relabel(a,b) do {} while(0)
|
|
+#define sepgsql_relation_get_transaction_id(a) do {} while(0)
|
|
+#define sepgsql_relation_copy_definition(a) do {} while(0)
|
|
+#define sepgsql_relation_truncate(a) do {} while(0)
|
|
+#define sepgsql_relation_references(a,b,c) do {} while(0)
|
|
+#define sepgsql_relation_lock(a) do {} while(0)
|
|
+#define sepgsql_view_replace(a) do {} while(0)
|
|
+#define sepgsql_index_create(a,b,c) do {} while(0)
|
|
+#define sepgsql_sequence_get_value(a) do {} while(0)
|
|
+#define sepgsql_sequence_next_value(a) do {} while(0)
|
|
+#define sepgsql_sequence_set_value(a) do {} while(0)
|
|
+
|
|
+#define sepgsql_conversion_create(a,b,c) do {} while(0)
|
|
+#define sepgsql_conversion_alter(a,b) do {} while(0)
|
|
+#define sepgsql_conversion_drop(a) do {} while(0)
|
|
+
|
|
+#define sepgsql_database_create(a,b) (InvalidOid)
|
|
+#define sepgsql_database_alter(a) do {} while(0)
|
|
+#define sepgsql_database_drop(a) do {} while(0)
|
|
+#define sepgsql_database_relabel(a,b) (InvalidOid)
|
|
+#define sepgsql_database_grant(a) do {} while(0)
|
|
+#define sepgsql_database_access(a) do {} while(0)
|
|
+#define sepgsql_database_superuser(a) (true)
|
|
+#define sepgsql_database_load_module(a,b) do {} while(0)
|
|
+
|
|
+#define sepgsql_fdw_create(a,b) (InvalidOid)
|
|
+#define sepgsql_fdw_alter(a,b) do {} while(0)
|
|
+#define sepgsql_fdw_drop(a) do {} while(0)
|
|
+#define sepgsql_fdw_grant(a) do {} while(0)
|
|
+
|
|
+#define sepgsql_foreign_server_create(a) (InvalidOid)
|
|
+#define sepgsql_foreign_server_alter(a) do {} while(0)
|
|
+#define sepgsql_foreign_server_drop(a) do {} while(0)
|
|
+#define sepgsql_foreign_server_grant(a) do {} while(0)
|
|
+
|
|
+#define sepgsql_language_create(a,b,c) (InvalidOid)
|
|
+#define sepgsql_language_alter(a) do {} while(0)
|
|
+#define sepgsql_language_drop(a) do {} while(0)
|
|
+#define sepgsql_language_grant(a) do {} while(0)
|
|
+
|
|
+#define sepgsql_largeobject_create(a,b) (InvalidOid)
|
|
+#define sepgsql_largeobject_alter(a,b) do {} while(0)
|
|
+#define sepgsql_largeobject_drop(a) do {} while(0)
|
|
+#define sepgsql_largeobject_read(a) do {} while(0)
|
|
+#define sepgsql_largeobject_write(a) do {} while(0)
|
|
+#define sepgsql_largeobject_export(a,b) do {} while(0)
|
|
+#define sepgsql_largeobject_import(a,b) (InvalidOid)
|
|
+
|
|
+#define sepgsql_schema_create(a,b,c) (InvalidOid)
|
|
+#define sepgsql_schema_alter(a) do {} while(0)
|
|
+#define sepgsql_schema_drop(a) do {} while(0)
|
|
+#define sepgsql_schema_relabel(a,b) (InvalidOid)
|
|
+#define sepgsql_schema_grant(a) do {} while(0)
|
|
+#define sepgsql_schema_search(a,b) (true)
|
|
+
|
|
+#define sepgsql_opclass_create(a,b) (InvalidOid)
|
|
+#define sepgsql_opclass_alter(a,b) do {} while(0)
|
|
+#define sepgsql_opclass_drop(a) do {} while(0)
|
|
+
|
|
+#define sepgsql_opfamily_create(a,b) (InvalidOid)
|
|
+#define sepgsql_opfamily_alter(a,b) do {} while(0)
|
|
+#define sepgsql_opfamily_drop(a) do {} while(0)
|
|
+#define sepgsql_opfamily_add_operator(a,b) do {} while(0)
|
|
+#define sepgsql_opfamily_add_procedure(a,b) do {} while(0)
|
|
+
|
|
+#define sepgsql_operator_create(a,b,c,d,e,f) (InvalidOid)
|
|
+#define sepgsql_operator_alter(a) do {} while(0)
|
|
+#define sepgsql_operator_drop(a) do {} while(0)
|
|
+
|
|
+#define sepgsql_proc_create(a,b,c,d,e) (InvalidOid)
|
|
+#define sepgsql_proc_alter(a,b,c) do {} while(0)
|
|
+#define sepgsql_proc_drop(a) do {} while(0)
|
|
+#define sepgsql_proc_relabel(a,b) (InvalidOid)
|
|
+#define sepgsql_proc_grant(a) do {} while(0)
|
|
+#define sepgsql_proc_execute(a) do {} while(0)
|
|
+#define sepgsql_proc_hint_inlined(a) (true)
|
|
+#define sepgsql_proc_entrypoint(a,b) do {} while(0)
|
|
+
|
|
+#define sepgsql_rule_create(a,b) do {} while(0)
|
|
+#define sepgsql_rule_drop(a,b) do {} while(0)
|
|
+
|
|
+#define sepgsql_trigger_create(a,b,c) do {} while(0)
|
|
+#define sepgsql_trigger_alter(a,b) do {} while(0)
|
|
+#define sepgsql_trigger_drop(a,b) do {} while(0)
|
|
+
|
|
+#define sepgsql_ts_config_create(a,b) (InvalidOid)
|
|
+#define sepgsql_ts_config_alter(a,b) do {} while(0)
|
|
+#define sepgsql_ts_config_drop(a) do {} while(0)
|
|
+
|
|
+#define sepgsql_ts_config_create(a,b) (InvalidOid)
|
|
+#define sepgsql_ts_config_alter(a,b) do {} while(0)
|
|
+#define sepgsql_ts_config_drop(a) do {} while(0)
|
|
+
|
|
+#define sepgsql_ts_dict_create(a,b) (InvalidOid)
|
|
+#define sepgsql_ts_dict_alter(a,b) do {} while(0)
|
|
+#define sepgsql_ts_dict_drop(a) do {} while(0)
|
|
+
|
|
+#define sepgsql_ts_parser_create(a,b,c,d,e,f,g) (InvalidOid)
|
|
+#define sepgsql_ts_parser_alter(a,b) do {} while(0)
|
|
+#define sepgsql_ts_parser_drop(a) do {} while(0)
|
|
+
|
|
+#define sepgsql_ts_template_create(a,b,c,d) (InvalidOid)
|
|
+#define sepgsql_ts_template_alter(a,b) do {} while(0)
|
|
+#define sepgsql_ts_template_drop(a) do {} while(0)
|
|
+
|
|
+#define sepgsql_type_create(a,b,c,d,e,f,g,h,i,j) (InvalidOid)
|
|
+#define sepgsql_type_alter(a,b,c) do {} while(0)
|
|
+#define sepgsql_type_drop(a) do {} while(0)
|
|
+
|
|
+#define sepgsql_sysobj_drop(a) do {} while(0)
|
|
+
|
|
+#define sepgsql_file_stat(a) do {} while(0)
|
|
+#define sepgsql_file_read(a) do {} while(0)
|
|
+#define sepgsql_file_write(a) do {} while(0)
|
|
+
|
|
+/* label.c */
|
|
+#define sepgsqlTupleDescHasSecLabel(a,b) (false)
|
|
+#define sepgsqlSetDefaultSecLabel(a,b) do {} while(0)
|
|
+#define sepgsqlTransSecLabelIn(a) (a)
|
|
+#define sepgsqlTransSecLabelOut(a) (a)
|
|
+#define sepgsqlRawSecLabelIn(a) (a)
|
|
+#define sepgsqlRawSecLabelOut(a) (a)
|
|
+
|
|
+#endif /* HAVE_SELINUX */
|
|
+
|
|
+extern Datum sepgsql_getcon(PG_FUNCTION_ARGS);
|
|
+extern Datum sepgsql_server_getcon(PG_FUNCTION_ARGS);
|
|
+extern Datum sepgsql_get_user(PG_FUNCTION_ARGS);
|
|
+extern Datum sepgsql_get_role(PG_FUNCTION_ARGS);
|
|
+extern Datum sepgsql_get_type(PG_FUNCTION_ARGS);
|
|
+extern Datum sepgsql_get_range(PG_FUNCTION_ARGS);
|
|
+extern Datum sepgsql_set_user(PG_FUNCTION_ARGS);
|
|
+extern Datum sepgsql_set_role(PG_FUNCTION_ARGS);
|
|
+extern Datum sepgsql_set_type(PG_FUNCTION_ARGS);
|
|
+extern Datum sepgsql_set_range(PG_FUNCTION_ARGS);
|
|
+
|
|
+#endif /* SEPGSQL_H */
|
|
diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h
|
|
index 462f6d1..d2c5e6e 100644
|
|
--- a/src/include/storage/fd.h
|
|
+++ b/src/include/storage/fd.h
|
|
@@ -68,6 +68,7 @@ extern int FileWrite(File file, char *buffer, int amount);
|
|
extern int FileSync(File file);
|
|
extern off_t FileSeek(File file, off_t offset, int whence);
|
|
extern int FileTruncate(File file, off_t offset);
|
|
+extern int FileRawDescriptor(File file);
|
|
|
|
/* Operations that allow use of regular stdio --- USE WITH CAUTION */
|
|
extern FILE *AllocateFile(const char *name, const char *mode);
|
|
diff --git a/src/include/storage/large_object.h b/src/include/storage/large_object.h
|
|
index 818db40..feb066f 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 secid);
|
|
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 e389c61..1ea6c09 100644
|
|
--- a/src/include/storage/lwlock.h
|
|
+++ b/src/include/storage/lwlock.h
|
|
@@ -67,6 +67,7 @@ typedef enum LWLockId
|
|
AutovacuumLock,
|
|
AutovacuumScheduleLock,
|
|
SyncScanLock,
|
|
+ SepgsqlAvcLock,
|
|
/* Individual lock IDs end here */
|
|
FirstBufMappingLock,
|
|
FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS,
|
|
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
|
|
index 48872d9..3383637 100644
|
|
--- a/src/include/storage/proc.h
|
|
+++ b/src/include/storage/proc.h
|
|
@@ -143,8 +143,10 @@ typedef struct PROC_HDR
|
|
* normal operation. Startup process also consumes one slot, but WAL
|
|
* writer and autovacuum launcher are launched only after it has
|
|
* exited.
|
|
+ * In addition, a netlink receiver process may be launched, if SELinux
|
|
+ * support is enabled.
|
|
*/
|
|
-#define NUM_AUXILIARY_PROCS 3
|
|
+#define NUM_AUXILIARY_PROCS 4
|
|
|
|
|
|
/* configurable options */
|
|
diff --git a/src/include/utils/errcodes.h b/src/include/utils/errcodes.h
|
|
index 44018cd..b225468 100644
|
|
--- a/src/include/utils/errcodes.h
|
|
+++ b/src/include/utils/errcodes.h
|
|
@@ -301,6 +301,7 @@
|
|
#define ERRCODE_INVALID_SCHEMA_DEFINITION MAKE_SQLSTATE('4','2', 'P','1','5')
|
|
#define ERRCODE_INVALID_TABLE_DEFINITION MAKE_SQLSTATE('4','2', 'P','1','6')
|
|
#define ERRCODE_INVALID_OBJECT_DEFINITION MAKE_SQLSTATE('4','2', 'P','1','7')
|
|
+#define ERRCODE_INVALID_SECURITY_LABEL MAKE_SQLSTATE('4','2', 'P','9','9')
|
|
|
|
/* Class 44 - WITH CHECK OPTION Violation */
|
|
#define ERRCODE_WITH_CHECK_OPTION_VIOLATION MAKE_SQLSTATE('4','4', '0','0','0')
|
|
diff --git a/src/test/regress/GNUmakefile b/src/test/regress/GNUmakefile
|
|
index 8ce5d25..70d7def 100644
|
|
--- a/src/test/regress/GNUmakefile
|
|
+++ b/src/test/regress/GNUmakefile
|
|
@@ -38,6 +38,12 @@ ifdef NO_LOCALE
|
|
NOLOCALE += --no-locale
|
|
endif
|
|
|
|
+# SELinux support
|
|
+ENABLE_SELINUX =
|
|
+ifdef SELINUX
|
|
+ENABLE_SELINUX += --enable-selinux
|
|
+endif
|
|
+
|
|
# stuff to pass into build of pg_regress
|
|
EXTRADEFS = '-DHOST_TUPLE="$(host_tuple)"' \
|
|
'-DMAKEPROG="$(MAKE)"' \
|
|
@@ -138,7 +144,7 @@ tablespace-setup:
|
|
## Run tests
|
|
##
|
|
|
|
-pg_regress_call = ./pg_regress --inputdir=$(srcdir) --dlpath=. --multibyte=$(MULTIBYTE) --load-language=plpgsql $(NOLOCALE)
|
|
+pg_regress_call = ./pg_regress --inputdir=$(srcdir) --dlpath=. --multibyte=$(MULTIBYTE) --load-language=plpgsql $(NOLOCALE) $(ENABLE_SELINUX)
|
|
|
|
check: all
|
|
$(pg_regress_call) --temp-install=./tmp_check --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(TEMP_CONF)
|
|
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
|
|
index 9a66ba0..e0efefc 100644
|
|
--- a/src/test/regress/expected/sanity_check.out
|
|
+++ b/src/test/regress/expected/sanity_check.out
|
|
@@ -113,6 +113,7 @@ SELECT relname, relhasindex
|
|
pg_pltemplate | t
|
|
pg_proc | t
|
|
pg_rewrite | t
|
|
+ pg_security | t
|
|
pg_shdepend | t
|
|
pg_shdescription | t
|
|
pg_statistic | t
|
|
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
|
|
index 37193ae..a5f05cc 100644
|
|
--- a/src/test/regress/pg_regress.c
|
|
+++ b/src/test/regress/pg_regress.c
|
|
@@ -82,6 +82,7 @@ static _stringlist *schedulelist = NULL;
|
|
static _stringlist *extra_tests = NULL;
|
|
static char *temp_install = NULL;
|
|
static char *temp_config = NULL;
|
|
+static bool enable_selinux = false;
|
|
static char *top_builddir = NULL;
|
|
static bool nolocale = false;
|
|
static char *hostname = NULL;
|
|
@@ -1863,6 +1864,7 @@ help(void)
|
|
printf(_(" --top-builddir=DIR (relative) path to top level build directory\n"));
|
|
printf(_(" --port=PORT start postmaster on PORT\n"));
|
|
printf(_(" --temp-config=PATH append contents of PATH to temporary config\n"));
|
|
+ printf(_(" --enable-selinux enables SELinux support, if available\n"));
|
|
printf(_("\n"));
|
|
printf(_("Options for using an existing installation:\n"));
|
|
printf(_(" --host=HOST use postmaster running on HOST\n"));
|
|
@@ -1907,6 +1909,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
|
|
{"dlpath", required_argument, NULL, 17},
|
|
{"create-role", required_argument, NULL, 18},
|
|
{"temp-config", required_argument, NULL, 19},
|
|
+ {"enable-selinux", optional_argument, NULL, 20},
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
|
|
@@ -1997,6 +2000,9 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
|
|
case 19:
|
|
temp_config = strdup(optarg);
|
|
break;
|
|
+ case 20:
|
|
+ enable_selinux = true;
|
|
+ break;
|
|
default:
|
|
/* getopt_long already emitted a complaint */
|
|
fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
|
|
@@ -2086,10 +2092,11 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
|
|
/* initdb */
|
|
header(_("initializing database system"));
|
|
snprintf(buf, sizeof(buf),
|
|
- SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE,
|
|
+ SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean%s%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE,
|
|
bindir, temp_install, datadir,
|
|
debug ? " --debug" : "",
|
|
nolocale ? " --no-locale" : "",
|
|
+ enable_selinux ? " --enable-selinux" : "",
|
|
outputdir);
|
|
if (system(buf))
|
|
{
|