- HeapInsert/HeapUpdate hooks called ReadBuffer without any locks. - Unexpected behavior in SELECT ... INTO t2 FROM t1 statement.
4542 lines
133 KiB
Diff
4542 lines
133 KiB
Diff
diff -rpNU3 pgace/configure sepgsql/configure
|
|
--- pgace/configure 2008-01-20 04:59:24.000000000 +0900
|
|
+++ sepgsql/configure 2008-01-20 05:06:27.000000000 +0900
|
|
@@ -314,7 +314,7 @@ ac_includes_default="\
|
|
# include <unistd.h>
|
|
#endif"
|
|
|
|
-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS configure_args build build_cpu build_vendor build_os host host_cpu host_vendor host_os PORTNAME docdir enable_nls WANTED_LANGUAGES default_port enable_shared enable_rpath enable_debug enable_profiling DTRACE DTRACEFLAGS enable_dtrace CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP GCC TAS autodepend INCLUDES enable_thread_safety with_tcl with_perl with_python with_gssapi with_krb5 krb_srvtab with_pam with_ldap with_bonjour with_openssl with_ossp_uuid XML2_CONFIG with_libxml with_libxslt with_system_tzdata with_zlib EGREP ELF_SYS LDFLAGS_SL LD with_gnu_ld ld_R_works RANLIB ac_ct_RANLIB STRIP ac_ct_STRIP STRIP_STATIC_LIB STRIP_SHARED_LIB TAR LN_S AWK YACC YFLAGS FLEX FLEXFLAGS PERL perl_archlibexp perl_privlibexp perl_useshrplib perl_embed_ldflags PYTHON python_version python_configdir python_includespec python_libdir python_libspec python_additional_libs OSSP_UUID_LIBS HAVE_IPV6 LIBOBJS acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS LDAP_LIBS_FE LDAP_LIBS_BE HAVE_POSIX_SIGNALS MSGFMT MSGMERGE XGETTEXT localedir TCLSH TCL_CONFIG_SH TCL_INCLUDE_SPEC TCL_LIB_FILE TCL_LIBS TCL_LIB_SPEC TCL_SHARED_BUILD TCL_SHLIB_LD_LIBS NSGMLS JADE have_docbook DOCBOOKSTYLE COLLATEINDEX SGMLSPL vpath_build LTLIBOBJS'
|
|
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS configure_args build build_cpu build_vendor build_os host host_cpu host_vendor host_os PORTNAME docdir enable_nls WANTED_LANGUAGES default_port enable_shared enable_rpath enable_debug enable_profiling DTRACE DTRACEFLAGS enable_dtrace CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP GCC TAS autodepend INCLUDES enable_thread_safety with_tcl with_perl with_python with_gssapi with_krb5 krb_srvtab with_pam with_ldap with_bonjour with_openssl with_ossp_uuid XML2_CONFIG with_libxml with_libxslt with_system_tzdata with_zlib enable_selinux EGREP ELF_SYS LDFLAGS_SL LD with_gnu_ld ld_R_works RANLIB ac_ct_RANLIB STRIP ac_ct_STRIP STRIP_STATIC_LIB STRIP_SHARED_LIB TAR LN_S AWK YACC YFLAGS FLEX FLEXFLAGS PERL perl_archlibexp perl_privlibexp perl_useshrplib perl_embed_ldflags PYTHON python_version python_configdir python_includespec python_libdir python_libspec python_additional_libs OSSP_UUID_LIBS HAVE_IPV6 LIBOBJS acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS LDAP_LIBS_FE LDAP_LIBS_BE HAVE_POSIX_SIGNALS MSGFMT MSGMERGE XGETTEXT localedir TCLSH TCL_CONFIG_SH TCL_INCLUDE_SPEC TCL_LIB_FILE TCL_LIBS TCL_LIB_SPEC TCL_SHARED_BUILD TCL_SHLIB_LD_LIBS NSGMLS JADE have_docbook DOCBOOKSTYLE COLLATEINDEX SGMLSPL vpath_build LTLIBOBJS'
|
|
ac_subst_files=''
|
|
|
|
# Initialize some variables set by options.
|
|
@@ -871,6 +871,7 @@ Optional Features:
|
|
--enable-cassert enable assertion checks (for debugging)
|
|
--enable-thread-safety make client libraries thread-safe
|
|
--enable-thread-safety-force force thread-safety despite thread test failure
|
|
+ --enable-selinux build with NSA SELinux support
|
|
--disable-largefile omit support for large files
|
|
|
|
Optional Packages:
|
|
@@ -4565,6 +4566,118 @@ fi;
|
|
|
|
|
|
#
|
|
+# NSA SELinux support
|
|
+#
|
|
+
|
|
+pgac_args="$pgac_args enable_selinux"
|
|
+
|
|
+# Check whether --enable-selinux or --disable-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
|
|
+ echo "$as_me:$LINENO: checking for getpeercon in -lselinux" >&5
|
|
+echo $ECHO_N "checking for getpeercon in -lselinux... $ECHO_C" >&6
|
|
+if test "${ac_cv_lib_selinux_getpeercon+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 gcc2 internal prototype to avoid an error. */
|
|
+#ifdef __cplusplus
|
|
+extern "C"
|
|
+#endif
|
|
+/* We use char because int might match the return type of a gcc2
|
|
+ builtin and then its argument prototype would still apply. */
|
|
+char getpeercon ();
|
|
+int
|
|
+main ()
|
|
+{
|
|
+getpeercon ();
|
|
+ ;
|
|
+ return 0;
|
|
+}
|
|
+_ACEOF
|
|
+rm -f conftest.$ac_objext conftest$ac_exeext
|
|
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&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); } &&
|
|
+ { ac_try='test -z "$ac_c_werror_flag"
|
|
+ || test ! -s conftest.err'
|
|
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
|
|
+ (eval $ac_try) 2>&5
|
|
+ ac_status=$?
|
|
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
|
|
+ (exit $ac_status); }; } &&
|
|
+ { ac_try='test -s conftest$ac_exeext'
|
|
+ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
|
|
+ (eval $ac_try) 2>&5
|
|
+ ac_status=$?
|
|
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
|
|
+ (exit $ac_status); }; }; then
|
|
+ ac_cv_lib_selinux_getpeercon=yes
|
|
+else
|
|
+ echo "$as_me: failed program was:" >&5
|
|
+sed 's/^/| /' conftest.$ac_ext >&5
|
|
+
|
|
+ac_cv_lib_selinux_getpeercon=no
|
|
+fi
|
|
+rm -f conftest.err conftest.$ac_objext \
|
|
+ conftest$ac_exeext conftest.$ac_ext
|
|
+LIBS=$ac_check_lib_save_LIBS
|
|
+fi
|
|
+echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_getpeercon" >&5
|
|
+echo "${ECHO_T}$ac_cv_lib_selinux_getpeercon" >&6
|
|
+if test $ac_cv_lib_selinux_getpeercon = yes; then
|
|
+ cat >>confdefs.h <<\_ACEOF
|
|
+#define SECURITY_SYSATTR_NAME "security_context"
|
|
+_ACEOF
|
|
+
|
|
+ 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
|
|
+
|
|
+fi
|
|
+
|
|
+#
|
|
# Elf
|
|
#
|
|
|
|
@@ -25933,6 +26046,7 @@ s,@with_libxml@,$with_libxml,;t t
|
|
s,@with_libxslt@,$with_libxslt,;t t
|
|
s,@with_system_tzdata@,$with_system_tzdata,;t t
|
|
s,@with_zlib@,$with_zlib,;t t
|
|
+s,@enable_selinux@,$enable_selinux,;t t
|
|
s,@EGREP@,$EGREP,;t t
|
|
s,@ELF_SYS@,$ELF_SYS,;t t
|
|
s,@LDFLAGS_SL@,$LDFLAGS_SL,;t t
|
|
diff -rpNU3 pgace/configure.in sepgsql/configure.in
|
|
--- pgace/configure.in 2008-01-20 04:59:24.000000000 +0900
|
|
+++ sepgsql/configure.in 2008-01-20 05:06:27.000000000 +0900
|
|
@@ -624,6 +624,19 @@ PGAC_ARG_BOOL(with, zlib, yes,
|
|
AC_SUBST(with_zlib)
|
|
|
|
#
|
|
+# NSA SELinux support
|
|
+#
|
|
+PGAC_ARG_BOOL(enable, selinux, no,
|
|
+ [ --enable-selinux build with NSA SELinux support])
|
|
+if test "$enable_selinux" = yes; then
|
|
+ AC_CHECK_LIB(selinux, getpeercon,
|
|
+ AC_DEFINE(SECURITY_SYSATTR_NAME, "security_context")
|
|
+ AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1)
|
|
+ AC_SUBST(enable_selinux),
|
|
+ AC_MSG_ERROR("--enable-selinux requires libselinux."))
|
|
+fi
|
|
+
|
|
+#
|
|
# Elf
|
|
#
|
|
|
|
diff -rpNU3 pgace/src/Makefile.global.in sepgsql/src/Makefile.global.in
|
|
--- pgace/src/Makefile.global.in 2007-11-18 02:56:38.000000000 +0900
|
|
+++ sepgsql/src/Makefile.global.in 2007-11-22 23:10:13.000000000 +0900
|
|
@@ -165,6 +165,7 @@ enable_rpath = @enable_rpath@
|
|
enable_nls = @enable_nls@
|
|
enable_debug = @enable_debug@
|
|
enable_dtrace = @enable_dtrace@
|
|
+enable_selinux = @enable_selinux@
|
|
enable_thread_safety = @enable_thread_safety@
|
|
|
|
python_includespec = @python_includespec@
|
|
diff -rpNU3 pgace/src/backend/Makefile sepgsql/src/backend/Makefile
|
|
--- pgace/src/backend/Makefile 2008-01-08 01:39:49.000000000 +0900
|
|
+++ sepgsql/src/backend/Makefile 2008-01-08 12:56:27.000000000 +0900
|
|
@@ -32,6 +32,11 @@ LIBS := $(filter-out -lpgport, $(LIBS))
|
|
# The backend doesn't need everything that's in LIBS, however
|
|
LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS))
|
|
|
|
+# SELinux support needs to link libselinux
|
|
+ifeq ($(enable_selinux), yes)
|
|
+LIBS += -lselinux
|
|
+endif
|
|
+
|
|
##########################################################################
|
|
|
|
all: submake-libpgport postgres $(POSTGRES_IMP)
|
|
diff -rpNU3 pgace/src/backend/security/Makefile sepgsql/src/backend/security/Makefile
|
|
--- pgace/src/backend/security/Makefile 2007-10-15 00:14:18.000000000 +0900
|
|
+++ sepgsql/src/backend/security/Makefile 2007-11-24 17:25:06.000000000 +0900
|
|
@@ -10,7 +10,12 @@ include $(top_builddir)/src/Makefile.glo
|
|
|
|
OBJS := pgaceCommon.o
|
|
|
|
+ifeq ($(enable_selinux), yes)
|
|
+OBJS += sepgsql/core.o sepgsql/hooks.o sepgsql/interface.o \
|
|
+ sepgsql/permissions.o sepgsql/proxy.o
|
|
+else
|
|
OBJS += pgaceHooks.o
|
|
+endif
|
|
|
|
all: SUBSYS.o
|
|
|
|
diff -rpNU3 pgace/src/backend/security/sepgsql/core.c sepgsql/src/backend/security/sepgsql/core.c
|
|
--- pgace/src/backend/security/sepgsql/core.c 1970-01-01 09:00:00.000000000 +0900
|
|
+++ sepgsql/src/backend/security/sepgsql/core.c 2007-11-24 18:15:13.000000000 +0900
|
|
@@ -0,0 +1,1013 @@
|
|
+/*
|
|
+ * src/backend/security/sepgsqlCore.c
|
|
+ * SE-PostgreSQL core facilities like userspace AVC, policy state monitoring.
|
|
+ *
|
|
+ * Copyright (c) 2007 KaiGai Kohei <kaigai@kaigai.gr.jp>
|
|
+ */
|
|
+#include "postgres.h"
|
|
+
|
|
+#include "access/heapam.h"
|
|
+#include "access/genam.h"
|
|
+#include "access/tupdesc.h"
|
|
+#include "access/xact.h"
|
|
+#include "libpq/libpq-be.h"
|
|
+#include "libpq/pqsignal.h"
|
|
+#include "miscadmin.h"
|
|
+#include "security/pgace.h"
|
|
+#include "security/sepgsql.h"
|
|
+#include "storage/lwlock.h"
|
|
+#include "utils/builtins.h"
|
|
+#include "utils/fmgroids.h"
|
|
+#include "utils/rel.h"
|
|
+#include "utils/syscache.h"
|
|
+#include <linux/netlink.h>
|
|
+#include <linux/selinux_netlink.h>
|
|
+#include <sched.h>
|
|
+#include <signal.h>
|
|
+#include <sys/file.h>
|
|
+#include <sys/stat.h>
|
|
+#include <sys/wait.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+static struct {
|
|
+ struct {
|
|
+ char *name; /* name of object class */
|
|
+ uint16 inum; /* internal identifier number */
|
|
+ } tclass;
|
|
+ struct {
|
|
+ char *name; /* name of access vector */
|
|
+ uint32 inum; /* internal identifier number */
|
|
+ } av_perms[sizeof(access_vector_t) * 8];
|
|
+} selinux_catalog[] = {
|
|
+ {
|
|
+ { "db_database", SECCLASS_DB_DATABASE },
|
|
+ {
|
|
+ { "create", DB_DATABASE__CREATE },
|
|
+ { "drop", DB_DATABASE__DROP },
|
|
+ { "getattr", DB_DATABASE__GETATTR },
|
|
+ { "setattr", DB_DATABASE__SETATTR },
|
|
+ { "relabelfrom", DB_DATABASE__RELABELFROM },
|
|
+ { "relabelto", DB_DATABASE__RELABELTO },
|
|
+ { "access", DB_DATABASE__ACCESS },
|
|
+ { "install_module", DB_DATABASE__INSTALL_MODULE },
|
|
+ { "load_module", DB_DATABASE__LOAD_MODULE },
|
|
+ { "get_param", DB_DATABASE__GET_PARAM },
|
|
+ { "set_param", DB_DATABASE__SET_PARAM },
|
|
+ { NULL, 0UL },
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ { "db_table", SECCLASS_DB_TABLE },
|
|
+ {
|
|
+ { "create", DB_TABLE__CREATE },
|
|
+ { "drop", DB_TABLE__DROP },
|
|
+ { "getattr", DB_TABLE__GETATTR },
|
|
+ { "setattr", DB_TABLE__SETATTR },
|
|
+ { "relabelfrom", DB_TABLE__RELABELFROM },
|
|
+ { "relabelto", DB_TABLE__RELABELTO },
|
|
+ { "use", DB_TABLE__USE },
|
|
+ { "select", DB_TABLE__SELECT },
|
|
+ { "update", DB_TABLE__UPDATE },
|
|
+ { "insert", DB_TABLE__INSERT },
|
|
+ { "delete", DB_TABLE__DELETE },
|
|
+ { "lock", DB_TABLE__LOCK },
|
|
+ { NULL, 0UL },
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ { "db_procedure", SECCLASS_DB_PROCEDURE },
|
|
+ {
|
|
+ { "create", DB_PROCEDURE__CREATE },
|
|
+ { "drop", DB_PROCEDURE__DROP },
|
|
+ { "getattr", DB_PROCEDURE__GETATTR },
|
|
+ { "setattr", DB_PROCEDURE__SETATTR },
|
|
+ { "relabelfrom", DB_PROCEDURE__RELABELFROM },
|
|
+ { "relabelto", DB_PROCEDURE__RELABELTO },
|
|
+ { "execute", DB_PROCEDURE__EXECUTE },
|
|
+ { "entrypoint", DB_PROCEDURE__ENTRYPOINT },
|
|
+ { NULL, 0UL },
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ { "db_column", SECCLASS_DB_COLUMN },
|
|
+ {
|
|
+ { "create", DB_COLUMN__CREATE },
|
|
+ { "drop", DB_COLUMN__DROP },
|
|
+ { "getattr", DB_COLUMN__GETATTR },
|
|
+ { "setattr", DB_COLUMN__SETATTR },
|
|
+ { "relabelfrom", DB_COLUMN__RELABELFROM },
|
|
+ { "relabelto", DB_COLUMN__RELABELTO },
|
|
+ { "use", DB_COLUMN__USE },
|
|
+ { "select", DB_COLUMN__SELECT },
|
|
+ { "update", DB_COLUMN__UPDATE },
|
|
+ { "insert", DB_COLUMN__INSERT },
|
|
+ { NULL, 0UL },
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ { "db_tuple", SECCLASS_DB_TUPLE },
|
|
+ {
|
|
+ { "relabelfrom", DB_TUPLE__RELABELFROM },
|
|
+ { "relabelto", DB_TUPLE__RELABELTO },
|
|
+ { "use", DB_TUPLE__USE },
|
|
+ { "select", DB_TUPLE__SELECT },
|
|
+ { "update", DB_TUPLE__UPDATE },
|
|
+ { "insert", DB_TUPLE__INSERT },
|
|
+ { "delete", DB_TUPLE__DELETE },
|
|
+ { NULL, 0UL },
|
|
+ }
|
|
+ },
|
|
+ {
|
|
+ { "db_blob", SECCLASS_DB_BLOB },
|
|
+ {
|
|
+ { "create", DB_BLOB__CREATE },
|
|
+ { "drop", DB_BLOB__DROP },
|
|
+ { "getattr", DB_BLOB__GETATTR },
|
|
+ { "setattr", DB_BLOB__SETATTR },
|
|
+ { "relabelfrom", DB_BLOB__RELABELFROM },
|
|
+ { "relabelto", DB_BLOB__RELABELTO },
|
|
+ { "read", DB_BLOB__READ },
|
|
+ { "write", DB_BLOB__WRITE },
|
|
+ { "import", DB_BLOB__IMPORT },
|
|
+ { "export", DB_BLOB__EXPORT },
|
|
+ { NULL, 0UL },
|
|
+ }
|
|
+ },
|
|
+};
|
|
+#define NUM_SELINUX_CATALOG (sizeof(selinux_catalog) / sizeof(selinux_catalog[0]))
|
|
+
|
|
+static const char *sepgsql_class_to_string(uint16 tclass)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i=0; i < NUM_SELINUX_CATALOG; i++) {
|
|
+ if (selinux_catalog[i].tclass.inum == tclass)
|
|
+ return selinux_catalog[i].tclass.name;
|
|
+ }
|
|
+#ifdef SEPGSQLOPT_LIBSELINUX_1_33
|
|
+ /* for legacy libselinux (Fedora core 6) */
|
|
+ /* This code will be replaced near future */
|
|
+ if (tclass == SECCLASS_PROCESS)
|
|
+ return "process";
|
|
+ return "unknown";
|
|
+#else
|
|
+ /* because tclass didn't match with userspace object classes,
|
|
+ * its external representation is always same as internal one */
|
|
+ return security_class_to_string((security_class_t) tclass);
|
|
+#endif
|
|
+}
|
|
+
|
|
+static const char *sepgsql_av_perm_to_string(uint16 tclass, uint32 perm)
|
|
+{
|
|
+ int i, j;
|
|
+
|
|
+ for (i=0; i < NUM_SELINUX_CATALOG; i++) {
|
|
+ if (selinux_catalog[i].tclass.inum == tclass) {
|
|
+ char *perm_name;
|
|
+
|
|
+ for (j=0; (perm_name = selinux_catalog[i].av_perms[j].name); j++) {
|
|
+ if (selinux_catalog[i].av_perms[j].inum == perm)
|
|
+ return perm_name;
|
|
+ }
|
|
+ return "unknown";
|
|
+ }
|
|
+ }
|
|
+#ifdef SEPGSQLOPT_LIBSELINUX_1_33
|
|
+ /* for legacy libselinux (Fedora core 6) */
|
|
+ /* This code will be replaced near future */
|
|
+ if (tclass == SECCLASS_PROCESS && perm == PROCESS__TRANSITION)
|
|
+ return "transition";
|
|
+ return "unknown";
|
|
+#else
|
|
+ /* because tclass/perm didn't match with userspace object classes,
|
|
+ * its external representation is always same as internal one */
|
|
+ return security_av_perm_to_string((security_class_t) tclass, (access_vector_t) perm);
|
|
+#endif
|
|
+}
|
|
+
|
|
+/*
|
|
+ * SE-PostgreSQL Internal AVC(Access Vector Cache) implementation.
|
|
+ *
|
|
+ */
|
|
+struct avc_datum {
|
|
+ SHMEM_OFFSET next;
|
|
+
|
|
+ Oid ssid; /* subject context */
|
|
+ Oid tsid; /* object context */
|
|
+ uint16 tclass; /* object class */
|
|
+
|
|
+ uint32 allowed;
|
|
+ uint32 decided;
|
|
+ uint32 auditallow;
|
|
+ uint32 auditdeny;
|
|
+
|
|
+ Oid create; /* newly created context */
|
|
+ bool is_hot;
|
|
+};
|
|
+
|
|
+#define AVC_DATUM_CACHE_SLOTS 512
|
|
+#define AVC_DATUM_CACHE_MAXNODES 800
|
|
+static struct {
|
|
+ LWLockId lock;
|
|
+ SHMEM_OFFSET slot[AVC_DATUM_CACHE_SLOTS];
|
|
+ SHMEM_OFFSET freelist;
|
|
+ int lru_hint;
|
|
+ int enforcing;
|
|
+ struct avc_datum entry[AVC_DATUM_CACHE_MAXNODES];
|
|
+
|
|
+ /* dynamic object class/av permission mapping */
|
|
+ struct {
|
|
+ struct {
|
|
+ uint16 internal;
|
|
+ security_class_t external;
|
|
+ } tclass;
|
|
+ struct {
|
|
+ uint32 internal;
|
|
+ access_vector_t external;
|
|
+ } av_perms[sizeof(access_vector_t) * 8];
|
|
+ } catalog[NUM_SELINUX_CATALOG];
|
|
+} *avc_shmem = NULL;
|
|
+
|
|
+Size sepgsqlShmemSize(void)
|
|
+{
|
|
+ return sizeof(*avc_shmem);
|
|
+}
|
|
+
|
|
+static void sepgsql_load_class_av_mapping()
|
|
+{
|
|
+ extern char *selinux_mnt;
|
|
+ char buffer[PATH_MAX];
|
|
+ struct stat st_buf;
|
|
+ int i, j, fd, len;
|
|
+
|
|
+ if (!selinux_mnt)
|
|
+ goto legacy_mapping;
|
|
+
|
|
+ /* Does '/selinux/class' exist? */
|
|
+ snprintf(buffer, sizeof(buffer), "%s/class", selinux_mnt);
|
|
+ if (lstat(buffer, &st_buf) || !S_ISDIR(st_buf.st_mode))
|
|
+ goto legacy_mapping;
|
|
+
|
|
+ for (i=0; i < NUM_SELINUX_CATALOG; i++) {
|
|
+ /* obtain external object class number */
|
|
+ snprintf(buffer, sizeof(buffer), "%s/class/%s/index",
|
|
+ selinux_mnt, selinux_catalog[i].tclass.name);
|
|
+ fd = open(buffer, O_RDONLY);
|
|
+ if (fd < 0)
|
|
+ goto legacy_mapping;
|
|
+
|
|
+ len = read(fd, buffer, sizeof(buffer));
|
|
+ close(fd);
|
|
+ if (len < 1)
|
|
+ goto legacy_mapping;
|
|
+ buffer[len] = '\0';
|
|
+
|
|
+ avc_shmem->catalog[i].tclass.internal
|
|
+ = selinux_catalog[i].tclass.inum;
|
|
+ avc_shmem->catalog[i].tclass.external
|
|
+ = atoi(buffer);
|
|
+
|
|
+ /* obtain external access vector number */
|
|
+ for (j=0; selinux_catalog[i].av_perms[j].name; j++) {
|
|
+ snprintf(buffer, sizeof(buffer), "%s/class/%s/perms/%s",
|
|
+ selinux_mnt,
|
|
+ selinux_catalog[i].tclass.name,
|
|
+ selinux_catalog[i].av_perms[j].name);
|
|
+ fd = open(buffer, O_RDONLY);
|
|
+ if (fd < 0)
|
|
+ goto legacy_mapping;
|
|
+
|
|
+ len = read(fd, buffer, sizeof(buffer));
|
|
+ close(fd);
|
|
+ if (len < 1)
|
|
+ goto legacy_mapping;
|
|
+ buffer[len] = '\0';
|
|
+
|
|
+ avc_shmem->catalog[i].av_perms[j].internal
|
|
+ = selinux_catalog[i].av_perms[j].inum;
|
|
+ avc_shmem->catalog[i].av_perms[j].external
|
|
+ = (0x0001UL << (atoi(buffer) - 1));
|
|
+ }
|
|
+ }
|
|
+ return;
|
|
+
|
|
+legacy_mapping:
|
|
+ for (i=0; i < NUM_SELINUX_CATALOG; i++) {
|
|
+ uint16 tclass = selinux_catalog[i].tclass.inum;
|
|
+
|
|
+ avc_shmem->catalog[i].tclass.internal = tclass;
|
|
+ avc_shmem->catalog[i].tclass.external = tclass;
|
|
+
|
|
+ for (j=0; selinux_catalog[i].av_perms[j].name; j++) {
|
|
+ uint32 av_perm = selinux_catalog[i].av_perms[j].inum;
|
|
+
|
|
+ avc_shmem->catalog[i].av_perms[j].internal = av_perm;
|
|
+ avc_shmem->catalog[i].av_perms[j].external = av_perm;
|
|
+ }
|
|
+ }
|
|
+ return;
|
|
+}
|
|
+
|
|
+static void sepgsql_avc_reset()
|
|
+{
|
|
+ int i, enforcing;
|
|
+
|
|
+ enforcing = security_getenforce();
|
|
+ Assert(enforcing==0 || enforcing==1);
|
|
+
|
|
+ LWLockAcquire(avc_shmem->lock, LW_EXCLUSIVE);
|
|
+
|
|
+ for (i=0; i < AVC_DATUM_CACHE_SLOTS; i++)
|
|
+ avc_shmem->slot[i] = INVALID_OFFSET;
|
|
+ avc_shmem->freelist = INVALID_OFFSET;
|
|
+ for (i=0; i < AVC_DATUM_CACHE_MAXNODES; i++) {
|
|
+ struct avc_datum *avd = avc_shmem->entry + i;
|
|
+
|
|
+ memset(avd, 0, sizeof(struct avc_datum));
|
|
+ avd->next = avc_shmem->freelist;
|
|
+ avc_shmem->freelist = MAKE_OFFSET(avd);
|
|
+ }
|
|
+ sepgsql_load_class_av_mapping();
|
|
+ avc_shmem->enforcing = enforcing;
|
|
+
|
|
+ LWLockRelease(avc_shmem->lock);
|
|
+}
|
|
+
|
|
+static void sepgsql_avc_init()
|
|
+{
|
|
+ bool found_avc;
|
|
+
|
|
+ avc_shmem = ShmemInitStruct("SELinux userspace AVC",
|
|
+ sepgsqlShmemSize(), &found_avc);
|
|
+ if (!found_avc) {
|
|
+ avc_shmem->lock = LWLockAssign();
|
|
+ sepgsql_avc_reset();
|
|
+ }
|
|
+}
|
|
+
|
|
+static uint32 sepgsql_validate_av_perms(security_class_t tclass, access_vector_t perms)
|
|
+{
|
|
+ /* we have to hold LW_SHARED lock at least */
|
|
+ int i, j;
|
|
+
|
|
+ for (i=0; i < NUM_SELINUX_CATALOG; i++) {
|
|
+ if (avc_shmem->catalog[i].tclass.external == tclass) {
|
|
+ uint32 __perms = 0;
|
|
+
|
|
+ for (j=0; j < sizeof(access_vector_t) * 8; j++) {
|
|
+ if (avc_shmem->catalog[i].av_perms[j].external & perms)
|
|
+ __perms |= avc_shmem->catalog[i].av_perms[j].internal;
|
|
+ }
|
|
+ //selnotice("tclass(ext:%d -> int:%d) av_perms(ext:%08x -> int:%08x) validated",
|
|
+ // tclass, avc_shmem->catalog[i].tclass.internal,
|
|
+ // perms, __perms);
|
|
+ return __perms;
|
|
+ }
|
|
+ }
|
|
+ //selnotice("tclass = %d is not user tclass, perms (%08x) is used as is", tclass, perms);
|
|
+
|
|
+ return (uint32) perms;
|
|
+}
|
|
+
|
|
+static void sepgsql_compute_avc_datum(Oid ssid, Oid tsid, uint16 tclass,
|
|
+ struct avc_datum *avd)
|
|
+{
|
|
+ security_class_t tclass_external = tclass;
|
|
+ security_context_t scon, tcon, ncon;
|
|
+ struct av_decision x;
|
|
+ Datum tmp;
|
|
+ int i;
|
|
+
|
|
+ memset(avd, 0, sizeof(struct avc_datum));
|
|
+ tmp = DirectFunctionCall1(security_label_raw_out,
|
|
+ ObjectIdGetDatum(ssid));
|
|
+ scon = DatumGetCString(tmp);
|
|
+ tmp = DirectFunctionCall1(security_label_raw_out,
|
|
+ ObjectIdGetDatum(tsid));
|
|
+ tcon = DatumGetCString(tmp);
|
|
+
|
|
+ LWLockAcquire(avc_shmem->lock, LW_SHARED);
|
|
+ /* translate internal tclass into external one, to query the kernel */
|
|
+ for (i=0; i < NUM_SELINUX_CATALOG; i++) {
|
|
+ if (avc_shmem->catalog[i].tclass.internal == tclass) {
|
|
+ tclass_external = avc_shmem->catalog[i].tclass.external;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (security_compute_av_raw(scon, tcon, tclass_external, 0, &x))
|
|
+ selerror("could not obtain access vector decision "
|
|
+ " scon='%s' tcon='%s' tclass=%u", scon, tcon, tclass);
|
|
+ if (security_compute_create_raw(scon, tcon, tclass_external, &ncon) != 0)
|
|
+ selerror("could not obtain a newly created security context "
|
|
+ "scon='%s' tcon='%s' tclass=%u", scon, tcon, tclass);
|
|
+
|
|
+ avd->ssid = ssid;
|
|
+ avd->tsid = tsid;
|
|
+ avd->tclass = tclass;
|
|
+
|
|
+ avd->allowed = sepgsql_validate_av_perms(tclass_external, x.allowed);
|
|
+ avd->decided = sepgsql_validate_av_perms(tclass_external, x.decided);
|
|
+ avd->auditallow = sepgsql_validate_av_perms(tclass_external, x.auditallow);
|
|
+ avd->auditdeny = sepgsql_validate_av_perms(tclass_external, x.auditdeny);
|
|
+ LWLockRelease(avc_shmem->lock);
|
|
+
|
|
+ PG_TRY();
|
|
+ {
|
|
+ tmp = DirectFunctionCall1(security_label_raw_in,
|
|
+ CStringGetDatum(ncon));
|
|
+ avd->create = DatumGetObjectId(tmp);
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(ncon);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+
|
|
+ pfree(scon);
|
|
+ pfree(tcon);
|
|
+ freecon(ncon);
|
|
+}
|
|
+
|
|
+static Oid sepgsql_compute_relabel(Oid ssid, Oid tsid, uint16 tclass)
|
|
+{
|
|
+ security_context_t scon, tcon, ncon;
|
|
+ Oid nsid;
|
|
+ Datum tmp;
|
|
+
|
|
+ tmp = DirectFunctionCall1(security_label_raw_out,
|
|
+ ObjectIdGetDatum(ssid));
|
|
+ scon = DatumGetCString(tmp);
|
|
+ tmp = DirectFunctionCall1(security_label_raw_out,
|
|
+ ObjectIdGetDatum(tsid));
|
|
+ tcon = DatumGetCString(tmp);
|
|
+
|
|
+ if (security_compute_relabel_raw(scon, tcon, tclass, &ncon) != 0)
|
|
+ selerror("could not obtain a newly relabeled security context "
|
|
+ "scon='%s' tcon='%s' tclass=%u", scon, tcon, tclass);
|
|
+
|
|
+ PG_TRY();
|
|
+ {
|
|
+ tmp = DirectFunctionCall1(security_label_raw_in,
|
|
+ CStringGetDatum(ncon));
|
|
+ nsid = DatumGetObjectId(tmp);
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(ncon);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+
|
|
+ freecon(ncon);
|
|
+ pfree(scon);
|
|
+ pfree(tcon);
|
|
+
|
|
+ return nsid;
|
|
+}
|
|
+
|
|
+static char *sepgsql_avc_audit(uint32 perms, struct avc_datum *avd, char *objname)
|
|
+{
|
|
+ /* we have to hold LW_SHARED lock at least */
|
|
+ uint32 denied, audited, mask;
|
|
+ char buffer[4096];
|
|
+ char *context;
|
|
+ int len;
|
|
+
|
|
+ denied = perms & ~avd->allowed;
|
|
+ audited = denied ? (denied & avd->auditdeny) : (perms & avd->auditallow);
|
|
+ if (!audited)
|
|
+ return NULL;
|
|
+
|
|
+ len = snprintf(buffer, sizeof(buffer), "%s {", denied ? "denied" : "granted");
|
|
+ for (mask=1; mask; mask <<= 1) {
|
|
+ if (audited & mask) {
|
|
+ len += snprintf(buffer + len, sizeof(buffer) - len, " %s",
|
|
+ sepgsql_av_perm_to_string(avd->tclass, mask));
|
|
+ }
|
|
+ }
|
|
+ len += snprintf(buffer + len, sizeof(buffer) - len, " }");
|
|
+
|
|
+ context = DatumGetCString(DirectFunctionCall1(security_label_out,
|
|
+ ObjectIdGetDatum(avd->ssid)));
|
|
+ len += snprintf(buffer + len, sizeof(buffer) - len, " scontext=%s", context);
|
|
+ pfree(context);
|
|
+
|
|
+ context = DatumGetCString(DirectFunctionCall1(security_label_out,
|
|
+ ObjectIdGetDatum(avd->tsid)));
|
|
+ len += snprintf(buffer + len, sizeof(buffer) - len, " tcontext=%s", context);
|
|
+ pfree(context);
|
|
+
|
|
+ len += snprintf(buffer + len, sizeof(buffer) - len, " tclass=%s",
|
|
+ sepgsql_class_to_string(avd->tclass));
|
|
+ if (objname)
|
|
+ len += snprintf(buffer + len, sizeof(buffer) - len, " name=%s", objname);
|
|
+
|
|
+ return pstrdup(buffer);
|
|
+}
|
|
+
|
|
+static inline int sepgsql_avc_hash(Oid ssid, Oid tsid, uint16 tclass)
|
|
+{
|
|
+ return ((uint32)ssid ^ ((uint32)tsid << 2) ^ tclass) % AVC_DATUM_CACHE_SLOTS;
|
|
+}
|
|
+
|
|
+static struct avc_datum *
|
|
+sepgsql_avc_lookup(Oid ssid, Oid tsid, uint16 tclass, uint32 perms)
|
|
+{
|
|
+ /* we have to hold LW_SHARED lock at least */
|
|
+ struct avc_datum *avd;
|
|
+ SHMEM_OFFSET curr;
|
|
+ int hashkey = sepgsql_avc_hash(ssid, tsid, tclass);
|
|
+
|
|
+ for (curr = avc_shmem->slot[hashkey];
|
|
+ SHM_OFFSET_VALID(curr);
|
|
+ curr = avd->next) {
|
|
+ avd = (void *)MAKE_PTR(curr);
|
|
+ if (avd->ssid==ssid && avd->tsid==tsid && avd->tclass==tclass
|
|
+ && (perms & avd->decided)==perms)
|
|
+ return avd;
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void sepgsql_avc_reclaim() {
|
|
+ /* we have to hold LW_EXCLUSIVE lock */
|
|
+ SHMEM_OFFSET *prev, next;
|
|
+ struct avc_datum *avd;
|
|
+
|
|
+ while (!SHM_OFFSET_VALID(avc_shmem->freelist)) {
|
|
+ prev = avc_shmem->slot + avc_shmem->lru_hint;
|
|
+ next = *prev;
|
|
+ while (!SHM_OFFSET_VALID(next)) {
|
|
+ avd = (void *)MAKE_PTR(next);
|
|
+ next = avd->next;
|
|
+ if (avd->is_hot) {
|
|
+ avd->is_hot = false;
|
|
+ } else {
|
|
+ *prev = avd->next;
|
|
+ avd->next = avc_shmem->freelist;
|
|
+ avc_shmem->freelist = MAKE_OFFSET(avd);
|
|
+ }
|
|
+ avd = (void *)MAKE_PTR(next);
|
|
+ }
|
|
+ avc_shmem->lru_hint = (avc_shmem->lru_hint + 1) % AVC_DATUM_CACHE_SLOTS;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void sepgsql_avc_insert(struct avc_datum *tmp)
|
|
+{
|
|
+ /* we have to hold LW_EXCLUSIVE lock */
|
|
+ struct avc_datum *avd;
|
|
+ int hashkey;
|
|
+
|
|
+ avd = sepgsql_avc_lookup(tmp->ssid, tmp->tsid, tmp->tclass, tmp->decided);
|
|
+ if (avd)
|
|
+ return;
|
|
+
|
|
+ if (!SHM_OFFSET_VALID(avc_shmem->freelist))
|
|
+ sepgsql_avc_reclaim();
|
|
+ Assert(SHM_OFFSET_VALID(avc_shmem->freelist));
|
|
+
|
|
+ avd = (void *)MAKE_PTR(avc_shmem->freelist);
|
|
+ avc_shmem->freelist = avd->next;
|
|
+
|
|
+ memcpy(avd, tmp, sizeof(struct avc_datum));
|
|
+ avd->is_hot = true;
|
|
+
|
|
+ hashkey = sepgsql_avc_hash(avd->ssid, avd->tsid, avd->tclass);
|
|
+ avd->next = avc_shmem->slot[hashkey];
|
|
+ avc_shmem->slot[hashkey] = MAKE_OFFSET(avd);
|
|
+
|
|
+ return;
|
|
+}
|
|
+
|
|
+bool sepgsql_avc_permission_noaudit(Oid ssid, Oid tsid, uint16 tclass, uint32 perms,
|
|
+ char **audit, char *objname)
|
|
+{
|
|
+ struct avc_datum *avd, lavd;
|
|
+ uint32 denied;
|
|
+ bool rc = true;
|
|
+ bool wlock = false;
|
|
+
|
|
+ LWLockAcquire(avc_shmem->lock, LW_SHARED);
|
|
+retry:
|
|
+ avd = sepgsql_avc_lookup(ssid, tsid, tclass, perms);
|
|
+ if (!avd) {
|
|
+ LWLockRelease(avc_shmem->lock);
|
|
+
|
|
+ sepgsql_compute_avc_datum(ssid, tsid, tclass, &lavd);
|
|
+
|
|
+ LWLockAcquire(avc_shmem->lock, LW_EXCLUSIVE);
|
|
+ wlock = true;
|
|
+ sepgsql_avc_insert(&lavd);
|
|
+ } else {
|
|
+ memcpy(&lavd, avd, sizeof(struct avc_datum));
|
|
+ }
|
|
+ denied = perms & ~lavd.allowed;
|
|
+ if (!perms || denied) {
|
|
+ if (avc_shmem->enforcing) {
|
|
+ errno = EACCES;
|
|
+ rc = false;
|
|
+ } else {
|
|
+ if (!wlock) {
|
|
+ /* update avd need LW_EXCLUSIVE lock onto shmem */
|
|
+ LWLockRelease(avc_shmem->lock);
|
|
+ LWLockAcquire(avc_shmem->lock, LW_EXCLUSIVE);
|
|
+ wlock = true;
|
|
+ goto retry;
|
|
+ }
|
|
+ /* grant permission to avoid flood of access denied log */
|
|
+ if (!avd)
|
|
+ avd = sepgsql_avc_lookup(ssid, tsid, tclass, perms);
|
|
+ if (avd)
|
|
+ avd->allowed |= denied;
|
|
+ }
|
|
+ }
|
|
+ LWLockRelease(avc_shmem->lock);
|
|
+ if (audit)
|
|
+ *audit = sepgsql_avc_audit(perms, &lavd, objname);
|
|
+
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+void sepgsql_avc_permission(Oid ssid, Oid tsid, uint16 tclass, uint32 perms, char *objname)
|
|
+{
|
|
+ char *audit;
|
|
+ bool rc;
|
|
+
|
|
+ rc = sepgsql_avc_permission_noaudit(ssid, tsid, tclass, perms, &audit, objname);
|
|
+ sepgsql_audit(rc, audit);
|
|
+
|
|
+ if (audit)
|
|
+ pfree(audit);
|
|
+}
|
|
+
|
|
+void sepgsql_audit(bool result, char *message)
|
|
+{
|
|
+ if (message) {
|
|
+ ereport((result ? NOTICE : ERROR),
|
|
+ (errcode(ERRCODE_INTERNAL_ERROR),
|
|
+ errmsg("SELinux: %s", message)));
|
|
+ } else if (!result) {
|
|
+ ereport(ERROR,
|
|
+ (errcode(ERRCODE_INTERNAL_ERROR),
|
|
+ errmsg("Transaction aborted due to SELinux access denied.")));
|
|
+ }
|
|
+}
|
|
+
|
|
+Oid sepgsql_avc_createcon(Oid ssid, Oid tsid, uint16 tclass)
|
|
+{
|
|
+ struct avc_datum *avd, lavd;
|
|
+ Oid nsid;
|
|
+
|
|
+ LWLockAcquire(avc_shmem->lock, LW_SHARED);
|
|
+ avd = sepgsql_avc_lookup(ssid, tsid, tclass, 0);
|
|
+ if (!avd) {
|
|
+ LWLockRelease(avc_shmem->lock);
|
|
+
|
|
+ sepgsql_compute_avc_datum(ssid, tsid, tclass, &lavd);
|
|
+
|
|
+ LWLockAcquire(avc_shmem->lock, LW_EXCLUSIVE);
|
|
+ sepgsql_avc_insert(&lavd);
|
|
+ nsid = lavd.create;
|
|
+ } else {
|
|
+ nsid = avd->create;
|
|
+ }
|
|
+ LWLockRelease(avc_shmem->lock);
|
|
+
|
|
+ return nsid;
|
|
+}
|
|
+
|
|
+Oid sepgsql_avc_relabelcon(Oid ssid, Oid tsid, uint16 tclass)
|
|
+{
|
|
+ /* currently no avc support on relabeling */
|
|
+ return sepgsql_compute_relabel(ssid, tsid, tclass);
|
|
+}
|
|
+
|
|
+/* sepgsql_getcon() -- returns a security context of client */
|
|
+Datum
|
|
+sepgsql_getcon(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ PG_RETURN_OID(sepgsqlGetClientContext());
|
|
+}
|
|
+
|
|
+/* sepgsql_system_getcon() -- obtain the server's context */
|
|
+static Oid sepgsql_system_getcon()
|
|
+{
|
|
+ security_context_t context;
|
|
+ Oid ssid;
|
|
+
|
|
+ if (getcon_raw(&context) != 0)
|
|
+ selerror("could not obtain security context of server process");
|
|
+
|
|
+ PG_TRY();
|
|
+ {
|
|
+ ssid = DatumGetObjectId(DirectFunctionCall1(security_label_raw_in,
|
|
+ CStringGetDatum(context)));
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(context);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+ freecon(context);
|
|
+ return ssid;
|
|
+}
|
|
+
|
|
+/* sepgsql_system_getpeercon() -- obtain the client's context */
|
|
+static Oid sepgsql_system_getpeercon(int sockfd)
|
|
+{
|
|
+ security_context_t context, __context;
|
|
+ Oid ssid;
|
|
+
|
|
+ if (getpeercon_raw(sockfd, &context)) {
|
|
+ /* we can set finally fallbacked context */
|
|
+ __context = getenv("SEPGSQL_FALLBACK_CONTEXT");
|
|
+ if (!__context)
|
|
+ selerror("could not obtain security context of database client");
|
|
+ if (security_check_context(__context) ||
|
|
+ selinux_trans_to_raw_context(__context, &context))
|
|
+ selerror("'%s' is not a valid context", __context);
|
|
+ }
|
|
+
|
|
+ PG_TRY();
|
|
+ {
|
|
+ ssid = DatumGetObjectId(DirectFunctionCall1(security_label_raw_in,
|
|
+ CStringGetDatum(context)));
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(context);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+ freecon(context);
|
|
+ return ssid;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * SE-PostgreSQL core functions
|
|
+ *
|
|
+ * sepgsqlGetServerContext() -- obtains server's context
|
|
+ * sepgsqlGetClientContext() -- obtains client's context via getpeercon()
|
|
+ * sepgsqlSetClientContext() -- changes client's context for trusted procedure
|
|
+ * sepgsqlInitialize() -- called when initializing 'postgres' includes bootstraping
|
|
+ * sepgsqlInitializePostmaster() -- called when initializing 'postmaster'
|
|
+ * sepgsqlFinalizePostmaster() -- called when finalizing 'postmaster' to kill
|
|
+ * policy state monitoring process.
|
|
+ * sepgsqlMonitoringPolicyState() -- is implementation of policy state monitoring
|
|
+ * process.
|
|
+ *
|
|
+ */
|
|
+static Oid sepgsqlServerContext = InvalidOid;
|
|
+static Oid sepgsqlClientContext = InvalidOid;
|
|
+
|
|
+Oid sepgsqlGetServerContext()
|
|
+{
|
|
+ return sepgsqlServerContext;
|
|
+}
|
|
+
|
|
+Oid sepgsqlGetClientContext()
|
|
+{
|
|
+ return sepgsqlClientContext;
|
|
+}
|
|
+
|
|
+void sepgsqlSetClientContext(Oid new_context)
|
|
+{
|
|
+ sepgsqlClientContext = new_context;
|
|
+}
|
|
+
|
|
+Oid sepgsqlGetDatabaseContext()
|
|
+{
|
|
+ HeapTuple tuple;
|
|
+ Oid datcon;
|
|
+
|
|
+ if (IsBootstrapProcessingMode()) {
|
|
+ return sepgsql_avc_createcon(sepgsqlGetClientContext(),
|
|
+ sepgsqlGetServerContext(),
|
|
+ SECCLASS_DB_DATABASE);
|
|
+ }
|
|
+
|
|
+ tuple = SearchSysCache(DATABASEOID,
|
|
+ ObjectIdGetDatum(MyDatabaseId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ selerror("cache lookup failed for Database %u", MyDatabaseId);
|
|
+ datcon = HeapTupleGetSecurity(tuple);
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ return datcon;
|
|
+}
|
|
+
|
|
+char *sepgsqlGetDatabaseName()
|
|
+{
|
|
+ Form_pg_database dat_form;
|
|
+ HeapTuple tuple;
|
|
+ char *datname;
|
|
+
|
|
+ if (IsBootstrapProcessingMode())
|
|
+ return NULL;
|
|
+
|
|
+ tuple = SearchSysCache(DATABASEOID,
|
|
+ ObjectIdGetDatum(MyDatabaseId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ selerror("cache lookup failed for Database %u", MyDatabaseId);
|
|
+ dat_form = (Form_pg_database) GETSTRUCT(tuple);
|
|
+ datname = pstrdup(NameStr(dat_form->datname));
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ return datname;
|
|
+}
|
|
+
|
|
+void sepgsqlInitialize(bool is_bootstrap)
|
|
+{
|
|
+ sepgsql_avc_init();
|
|
+
|
|
+ if (IsBootstrapProcessingMode()) {
|
|
+ sepgsqlServerContext = sepgsql_system_getcon();
|
|
+ sepgsqlClientContext = sepgsql_system_getcon();
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ sepgsqlGetDatabaseContext(),
|
|
+ SECCLASS_DB_DATABASE,
|
|
+ DB_DATABASE__ACCESS,
|
|
+ NULL);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* obtain security context of server process */
|
|
+ sepgsqlServerContext = sepgsql_system_getcon();
|
|
+
|
|
+ /* obtain security context of client process */
|
|
+ if (MyProcPort != NULL) {
|
|
+ sepgsqlClientContext = sepgsql_system_getpeercon(MyProcPort->sock);
|
|
+ } else {
|
|
+ sepgsqlClientContext = sepgsql_system_getcon();
|
|
+ }
|
|
+
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ sepgsqlGetDatabaseContext(),
|
|
+ SECCLASS_DB_DATABASE,
|
|
+ DB_DATABASE__ACCESS,
|
|
+ sepgsqlGetDatabaseName());
|
|
+}
|
|
+
|
|
+/* sepgsqlMonitoringPolicyState() is worker process to monitor
|
|
+ * the status of SELinux policy. When it is changed, light after the worker
|
|
+ * thread receive a notification via netlink socket. The notification is
|
|
+ * delivered into any PostgreSQL instance by reseting shared avc.
|
|
+ */
|
|
+static void sepgsqlMonitoringPolicyState_SIGHUP(int signum)
|
|
+{
|
|
+ selnotice("selinux userspace AVC reset, by receiving SIGHUP");
|
|
+ sepgsql_avc_reset();
|
|
+}
|
|
+
|
|
+static int sepgsqlMonitoringPolicyState()
|
|
+{
|
|
+ char buffer[2048];
|
|
+ struct sockaddr_nl addr;
|
|
+ socklen_t addrlen;
|
|
+ struct nlmsghdr *nlh;
|
|
+ int i, rc, nl_sockfd;
|
|
+
|
|
+ seldebug("%s pid=%u", __FUNCTION__, getpid());
|
|
+
|
|
+ /* close listen port */
|
|
+ for (i=3; !close(i); i++);
|
|
+
|
|
+ /* map shared memory segment */
|
|
+ sepgsql_avc_init();
|
|
+
|
|
+ /* setup the signal handler */
|
|
+ pqinitmask();
|
|
+ pqsignal(SIGHUP, sepgsqlMonitoringPolicyState_SIGHUP);
|
|
+ pqsignal(SIGINT, SIG_DFL);
|
|
+ pqsignal(SIGQUIT, SIG_DFL);
|
|
+ pqsignal(SIGTERM, SIG_DFL);
|
|
+ pqsignal(SIGUSR1, SIG_DFL);
|
|
+ pqsignal(SIGUSR2, SIG_DFL);
|
|
+ pqsignal(SIGCHLD, SIG_DFL);
|
|
+ PG_SETMASK(&UnBlockSig);
|
|
+
|
|
+ /* open netlink socket */
|
|
+ nl_sockfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_SELINUX);
|
|
+ if (nl_sockfd < 0) {
|
|
+ selnotice("could not create netlink socket");
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ memset(&addr, 0, sizeof(addr));
|
|
+ addr.nl_family = AF_NETLINK;
|
|
+ addr.nl_groups = SELNL_GRP_AVC;
|
|
+ if (bind(nl_sockfd, (struct sockaddr *)&addr, sizeof(addr))) {
|
|
+ selnotice("could not bind netlink socket");
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ /* waiting loop */
|
|
+ while (true) {
|
|
+ addrlen = sizeof(addr);
|
|
+ rc = recvfrom(nl_sockfd, buffer, sizeof(buffer), 0,
|
|
+ (struct sockaddr *)&addr, &addrlen);
|
|
+ if (rc < 0) {
|
|
+ if (errno == EINTR)
|
|
+ continue;
|
|
+ selnotice("selinux netlink: recvfrom() error=%d, %s",
|
|
+ errno, strerror(errno));
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ if (addrlen != sizeof(addr)) {
|
|
+ selnotice("selinux netlink: netlink address truncated (len = %d)", addrlen);
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ if (addr.nl_pid) {
|
|
+ selnotice("selinux netlink: received spoofed packet from: %u", addr.nl_pid);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (rc == 0) {
|
|
+ selnotice("selinux netlink: received EOF on socket");
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ nlh = (struct nlmsghdr *)buffer;
|
|
+
|
|
+ if (nlh->nlmsg_flags & MSG_TRUNC
|
|
+ || nlh->nlmsg_len > (unsigned int)rc) {
|
|
+ selnotice("selinux netlink: incomplete netlink message");
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ switch (nlh->nlmsg_type) {
|
|
+ case NLMSG_ERROR: {
|
|
+ struct nlmsgerr *err = NLMSG_DATA(nlh);
|
|
+ if (err->error == 0)
|
|
+ break;
|
|
+ selnotice("selinux netlink: error message %d", -err->error);
|
|
+ return 1;
|
|
+ }
|
|
+ case SELNL_MSG_SETENFORCE: {
|
|
+ struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh);
|
|
+ selnotice("selinux netlink: received setenforce notice (enforcing=%d)", msg->val);
|
|
+ sepgsql_avc_reset();
|
|
+ break;
|
|
+ }
|
|
+ case SELNL_MSG_POLICYLOAD: {
|
|
+ struct selnl_msg_policyload *msg = NLMSG_DATA(nlh);
|
|
+ selnotice("selinux netlink: received policyload notice (seqno=%d)", msg->seqno);
|
|
+ sepgsql_avc_reset();
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ selnotice("selinux netlink: unknown message type (%d)", nlh->nlmsg_type);
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static pid_t MonitoringPolicyStatePid = -1;
|
|
+
|
|
+int sepgsqlInitializePostmaster()
|
|
+{
|
|
+ MonitoringPolicyStatePid = fork();
|
|
+ if (MonitoringPolicyStatePid == 0) {
|
|
+ exit(sepgsqlMonitoringPolicyState());
|
|
+ } else if (MonitoringPolicyStatePid < 0) {
|
|
+ selnotice("could not create a child process to monitor the policy state");
|
|
+ return false;
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
+void sepgsqlFinalizePostmaster()
|
|
+{
|
|
+ int status;
|
|
+
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ return;
|
|
+
|
|
+ if (MonitoringPolicyStatePid > 0) {
|
|
+ if (kill(MonitoringPolicyStatePid, SIGTERM) < 0) {
|
|
+ selnotice("could not kill(%u, SIGTERM), errno=%d (%s)",
|
|
+ MonitoringPolicyStatePid, errno, strerror(errno));
|
|
+ return;
|
|
+ }
|
|
+ waitpid(MonitoringPolicyStatePid, &status, 0);
|
|
+ }
|
|
+}
|
|
+
|
|
+bool sepgsqlIsEnabled()
|
|
+{
|
|
+ static int enabled = -1;
|
|
+
|
|
+ if (enabled < 0)
|
|
+ enabled = is_selinux_enabled();
|
|
+
|
|
+ return enabled > 0 ? true : false;
|
|
+}
|
|
diff -rpNU3 pgace/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/security/sepgsql/hooks.c
|
|
--- pgace/src/backend/security/sepgsql/hooks.c 1970-01-01 09:00:00.000000000 +0900
|
|
+++ sepgsql/src/backend/security/sepgsql/hooks.c 2008-01-22 13:52:55.000000000 +0900
|
|
@@ -0,0 +1,658 @@
|
|
+/*
|
|
+ * src/backend/sepgsqlHooks.c
|
|
+ * SE-PostgreSQL hooks
|
|
+ *
|
|
+ * Copyright 2007 KaiGai Kohei <kaigai@kaigai.gr.jp>
|
|
+ */
|
|
+#include "postgres.h"
|
|
+
|
|
+#include "access/heapam.h"
|
|
+#include "access/genam.h"
|
|
+#include "access/skey.h"
|
|
+#include "miscadmin.h"
|
|
+#include "nodes/makefuncs.h"
|
|
+#include "security/pgace.h"
|
|
+#include "security/sepgsql.h"
|
|
+#include "utils/fmgroids.h"
|
|
+#include "utils/syscache.h"
|
|
+#include <fcntl.h>
|
|
+#include <unistd.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+
|
|
+static HeapTuple __getHeapTupleFromItemPointer(Relation rel, ItemPointer tid)
|
|
+{
|
|
+ /* obtain an old tuple */
|
|
+ Buffer buffer;
|
|
+ PageHeader dp;
|
|
+ ItemId lp;
|
|
+ HeapTupleData tuple;
|
|
+ HeapTuple oldtup;
|
|
+
|
|
+ buffer = ReadBuffer(rel, ItemPointerGetBlockNumber(tid));
|
|
+ LockBuffer(buffer, BUFFER_LOCK_SHARE);
|
|
+
|
|
+ dp = (PageHeader) BufferGetPage(buffer);
|
|
+ lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid));
|
|
+
|
|
+ Assert(ItemIdIsUsed(lp));
|
|
+
|
|
+ tuple.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
|
|
+ tuple.t_len = ItemIdGetLength(lp);
|
|
+ tuple.t_self = *tid;
|
|
+ tuple.t_tableOid = RelationGetRelid(rel);
|
|
+ oldtup = heap_copytuple(&tuple);
|
|
+
|
|
+ LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
|
|
+ ReleaseBuffer(buffer);
|
|
+
|
|
+ return oldtup;
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * Extended SQL statement hooks
|
|
+ *******************************************************************************/
|
|
+DefElem *sepgsqlGramSecurityItem(char *defname, char *value)
|
|
+{
|
|
+ DefElem *n = NULL;
|
|
+ if (!strcmp(defname, "context"))
|
|
+ n = makeDefElem(pstrdup(defname), (Node *) makeString(value));
|
|
+ return n;
|
|
+}
|
|
+
|
|
+bool sepgsqlIsGramSecurityItem(DefElem *defel)
|
|
+{
|
|
+ Assert(IsA(defel, DefElem));
|
|
+ if (defel->defname && !strcmp(defel->defname, "context"))
|
|
+ return true;
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static void __put_gram_context(HeapTuple tuple, DefElem *defel)
|
|
+{
|
|
+ if (defel) {
|
|
+ Oid newcon = DirectFunctionCall1(security_label_in,
|
|
+ CStringGetDatum(strVal(defel->arg)));
|
|
+ HeapTupleSetSecurity(tuple, newcon);
|
|
+ }
|
|
+}
|
|
+
|
|
+void sepgsqlGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel)
|
|
+{
|
|
+ __put_gram_context(tuple, defel);
|
|
+}
|
|
+
|
|
+void sepgsqlGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel)
|
|
+{
|
|
+ __put_gram_context(tuple, defel);
|
|
+}
|
|
+
|
|
+void sepgsqlGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel)
|
|
+{
|
|
+ __put_gram_context(tuple, defel);
|
|
+}
|
|
+
|
|
+void sepgsqlGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel)
|
|
+{
|
|
+ __put_gram_context(tuple, defel);
|
|
+}
|
|
+
|
|
+void sepgsqlGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel)
|
|
+{
|
|
+ __put_gram_context(tuple, defel);
|
|
+}
|
|
+
|
|
+void sepgsqlGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel)
|
|
+{
|
|
+ __put_gram_context(tuple, defel);
|
|
+}
|
|
+
|
|
+void sepgsqlGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel)
|
|
+{
|
|
+ __put_gram_context(tuple, defel);
|
|
+}
|
|
+
|
|
+void sepgsqlGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel)
|
|
+{
|
|
+ __put_gram_context(tuple, defel);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * DATABASE object related hooks
|
|
+ *******************************************************************************/
|
|
+
|
|
+void sepgsqlGetDatabaseParam(const char *name)
|
|
+{
|
|
+ HeapTuple tuple;
|
|
+
|
|
+ tuple = SearchSysCache(DATABASEOID,
|
|
+ ObjectIdGetDatum(MyDatabaseId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ selerror("cache lookup failed for database %u", MyDatabaseId);
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ HeapTupleGetSecurity(tuple),
|
|
+ SECCLASS_DB_DATABASE,
|
|
+ DB_DATABASE__GET_PARAM,
|
|
+ sepgsqlGetTupleName(DatabaseRelationId, tuple));
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+void sepgsqlSetDatabaseParam(const char *name, char *argstring)
|
|
+{
|
|
+ HeapTuple tuple;
|
|
+
|
|
+ tuple = SearchSysCache(DATABASEOID,
|
|
+ ObjectIdGetDatum(MyDatabaseId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ selerror("cache lookup failed for database %u", MyDatabaseId);
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ HeapTupleGetSecurity(tuple),
|
|
+ SECCLASS_DB_DATABASE,
|
|
+ DB_DATABASE__SET_PARAM,
|
|
+ sepgsqlGetTupleName(DatabaseRelationId, tuple));
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * RELATION(Table)/ATTRIBTUE(column) object related hooks
|
|
+ *******************************************************************************/
|
|
+void sepgsqlLockTable(Oid relid)
|
|
+{
|
|
+ HeapTuple tuple;
|
|
+ Form_pg_class classForm;
|
|
+
|
|
+ tuple = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(relid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ selerror("cache lookup failed for relation %u", relid);
|
|
+ classForm = (Form_pg_class) GETSTRUCT(tuple);
|
|
+
|
|
+ if (classForm->relkind == RELKIND_RELATION)
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ HeapTupleGetSecurity(tuple),
|
|
+ SECCLASS_DB_TABLE,
|
|
+ DB_TABLE__LOCK,
|
|
+ sepgsqlGetTupleName(RelationRelationId, tuple));
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * PROCEDURE related hooks
|
|
+ *******************************************************************************/
|
|
+
|
|
+static Datum __callTrustedProcedure(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ Oid orig_client_con;
|
|
+ Datum retval;
|
|
+
|
|
+ /* save original security context */
|
|
+ orig_client_con = sepgsqlGetClientContext();
|
|
+ /* set exec context */
|
|
+ sepgsqlSetClientContext(DatumGetObjectId(fcinfo->flinfo->fn_pgace_data));
|
|
+ PG_TRY();
|
|
+ {
|
|
+ retval = fcinfo->flinfo->fn_pgace_addr(fcinfo);
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ sepgsqlSetClientContext(orig_client_con);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+ sepgsqlSetClientContext(orig_client_con);
|
|
+
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+void sepgsqlCallFunction(FmgrInfo *finfo, bool with_perm_check)
|
|
+{
|
|
+ HeapTuple tuple;
|
|
+ Oid execcon;
|
|
+ uint32 perms = DB_PROCEDURE__EXECUTE;
|
|
+
|
|
+ tuple = SearchSysCache(PROCOID,
|
|
+ ObjectIdGetDatum(finfo->fn_oid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ selerror("cache lookup failed for procedure %u", finfo->fn_oid);
|
|
+
|
|
+ /* check trusted procedure */
|
|
+ execcon = sepgsql_avc_createcon(sepgsqlGetClientContext(),
|
|
+ HeapTupleGetSecurity(tuple),
|
|
+ SECCLASS_PROCESS);
|
|
+ if (sepgsqlGetClientContext() != execcon) {
|
|
+ finfo->fn_pgace_addr = finfo->fn_addr;
|
|
+ finfo->fn_pgace_data = ObjectIdGetDatum(execcon);
|
|
+ finfo->fn_addr = __callTrustedProcedure;
|
|
+
|
|
+ perms |= DB_PROCEDURE__ENTRYPOINT;
|
|
+ }
|
|
+
|
|
+ if (with_perm_check) {
|
|
+ /* check procedure:{execute entrypoint} permission */
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ HeapTupleGetSecurity(tuple),
|
|
+ SECCLASS_DB_PROCEDURE,
|
|
+ perms,
|
|
+ sepgsqlGetTupleName(ProcedureRelationId, tuple));
|
|
+ }
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+bool sepgsqlCallFunctionTrigger(FmgrInfo *finfo, TriggerData *tgdata)
|
|
+{
|
|
+ Relation rel = tgdata->tg_relation;
|
|
+ HeapTuple newtup = NULL;
|
|
+ HeapTuple oldtup = NULL;
|
|
+
|
|
+ if (TRIGGER_FIRED_FOR_STATEMENT(tgdata->tg_event))
|
|
+ return true; /* statement trigger does not contain any tuple */
|
|
+ if (TRIGGER_FIRED_BY_INSERT(tgdata->tg_event)) {
|
|
+ if (TRIGGER_FIRED_AFTER(tgdata->tg_event))
|
|
+ newtup = tgdata->tg_trigtuple;
|
|
+ } else if (TRIGGER_FIRED_BY_UPDATE(tgdata->tg_event)) {
|
|
+ oldtup = tgdata->tg_trigtuple;
|
|
+ if (TRIGGER_FIRED_AFTER(tgdata->tg_event)
|
|
+ && HeapTupleGetSecurity(oldtup) != HeapTupleGetSecurity(tgdata->tg_newtuple))
|
|
+ newtup = tgdata->tg_newtuple;
|
|
+ } else if (TRIGGER_FIRED_BY_DELETE(tgdata->tg_event)) {
|
|
+ if (TRIGGER_FIRED_AFTER(tgdata->tg_event))
|
|
+ oldtup = tgdata->tg_trigtuple;
|
|
+ } else {
|
|
+ selerror("unknown trigger event type (%u)", tgdata->tg_event);
|
|
+ }
|
|
+ if (oldtup && !sepgsqlCheckTuplePerms(rel, oldtup, NULL, DB_TUPLE__SELECT, false))
|
|
+ return false;
|
|
+ if (newtup && !sepgsqlCheckTuplePerms(rel, newtup, NULL, DB_TUPLE__SELECT, false))
|
|
+ return false;
|
|
+
|
|
+ sepgsqlCallFunction(finfo, false);
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * LOAD shared library module hook
|
|
+ *******************************************************************************/
|
|
+void sepgsqlLoadSharedModule(const char *filename)
|
|
+{
|
|
+ security_context_t filecon;
|
|
+ Datum filecon_sid;
|
|
+
|
|
+ if (getfilecon_raw(filename, &filecon) < 1)
|
|
+ selerror("could not obtain security context of %s", filename);
|
|
+ PG_TRY();
|
|
+ {
|
|
+ filecon_sid = DirectFunctionCall1(security_label_raw_in,
|
|
+ CStringGetDatum(filecon));
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(filecon);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+ freecon(filecon);
|
|
+
|
|
+ sepgsql_avc_permission(sepgsqlGetDatabaseContext(),
|
|
+ DatumGetObjectId(filecon_sid),
|
|
+ SECCLASS_DB_DATABASE,
|
|
+ DB_DATABASE__LOAD_MODULE,
|
|
+ (char *) filename);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * Binary Large Object hooks
|
|
+ *******************************************************************************/
|
|
+Oid sepgsqlLargeObjectGetSecurity(HeapTuple tuple) {
|
|
+ Oid lo_security = HeapTupleGetSecurity(tuple);
|
|
+
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ lo_security,
|
|
+ SECCLASS_DB_BLOB,
|
|
+ DB_BLOB__GETATTR,
|
|
+ sepgsqlGetTupleName(LargeObjectRelationId, tuple));
|
|
+ return lo_security;
|
|
+}
|
|
+
|
|
+void sepgsqlLargeObjectSetSecurity(HeapTuple tuple, Oid lo_security, bool is_first)
|
|
+{
|
|
+ if (is_first) {
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ HeapTupleGetSecurity(tuple),
|
|
+ SECCLASS_DB_BLOB,
|
|
+ DB_BLOB__SETATTR | DB_BLOB__RELABELFROM,
|
|
+ sepgsqlGetTupleName(LargeObjectRelationId, tuple));
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ lo_security,
|
|
+ SECCLASS_DB_BLOB,
|
|
+ DB_BLOB__RELABELTO,
|
|
+ sepgsqlGetTupleName(LargeObjectRelationId, tuple));
|
|
+ }
|
|
+ HeapTupleSetSecurity(tuple, lo_security);
|
|
+}
|
|
+
|
|
+void sepgsqlLargeObjectCreate(Relation rel, HeapTuple tuple)
|
|
+{
|
|
+ Oid newcon = sepgsqlComputeImplicitContext(rel, tuple);
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ newcon,
|
|
+ SECCLASS_DB_BLOB,
|
|
+ DB_BLOB__CREATE,
|
|
+ sepgsqlGetTupleName(LargeObjectRelationId, tuple));
|
|
+ HeapTupleSetSecurity(tuple, newcon);
|
|
+}
|
|
+
|
|
+void sepgsqlLargeObjectDrop(Relation rel, HeapTuple tuple)
|
|
+{
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ HeapTupleGetSecurity(tuple),
|
|
+ SECCLASS_DB_BLOB,
|
|
+ DB_BLOB__DROP,
|
|
+ sepgsqlGetTupleName(LargeObjectRelationId, tuple));
|
|
+}
|
|
+
|
|
+void sepgsqlLargeObjectOpen(Relation rel, HeapTuple tuple, bool read_only)
|
|
+{
|
|
+ sepgsqlCheckTuplePerms(rel, tuple, NULL, DB_TUPLE__SELECT, true);
|
|
+}
|
|
+
|
|
+void sepgsqlLargeObjectRead(Relation rel, HeapTuple tuple)
|
|
+{
|
|
+ sepgsqlCheckTuplePerms(rel, tuple, NULL, DB_TUPLE__SELECT | DB_BLOB__READ, true);
|
|
+}
|
|
+
|
|
+void sepgsqlLargeObjectWrite(Relation rel, HeapTuple newtup, HeapTuple oldtup)
|
|
+{
|
|
+ Oid lo_security;
|
|
+
|
|
+ if (HeapTupleIsValid(oldtup)) {
|
|
+ lo_security = HeapTupleGetSecurity(oldtup);
|
|
+ } else {
|
|
+ Form_pg_largeobject lobj_form
|
|
+ = (Form_pg_largeobject) GETSTRUCT(newtup);
|
|
+ ScanKeyData skey;
|
|
+ SysScanDesc sd;
|
|
+ HeapTuple tuple;
|
|
+
|
|
+ ScanKeyInit(&skey,
|
|
+ Anum_pg_largeobject_loid,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(lobj_form->loid));
|
|
+ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true,
|
|
+ SnapshotNow, 1, &skey);
|
|
+ tuple = systable_getnext(sd);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ selerror("large object %u does not exist", lobj_form->loid);
|
|
+ lo_security = HeapTupleGetSecurity(tuple);
|
|
+ systable_endscan(sd);
|
|
+ }
|
|
+ HeapTupleSetSecurity(newtup, lo_security);
|
|
+ sepgsqlCheckTuplePerms(rel, newtup, NULL, DB_TUPLE__UPDATE | DB_BLOB__WRITE, true);
|
|
+}
|
|
+
|
|
+void sepgsqlLargeObjectTruncate(Relation rel, Oid loid) {
|
|
+ ScanKeyData skey;
|
|
+ SysScanDesc sd;
|
|
+ HeapTuple tuple;
|
|
+
|
|
+ ScanKeyInit(&skey,
|
|
+ Anum_pg_largeobject_loid,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(loid));
|
|
+ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true,
|
|
+ SnapshotNow, 1, &skey);
|
|
+ tuple = systable_getnext(sd);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ selerror("large object %u does not exist", loid);
|
|
+ sepgsqlCheckTuplePerms(rel, tuple, NULL, DB_TUPLE__UPDATE | DB_BLOB__WRITE, true);
|
|
+ systable_endscan(sd);
|
|
+}
|
|
+
|
|
+void sepgsqlLargeObjectImport()
|
|
+{
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ sepgsqlGetServerContext(),
|
|
+ SECCLASS_DB_BLOB,
|
|
+ DB_BLOB__IMPORT,
|
|
+ NULL);
|
|
+}
|
|
+
|
|
+void sepgsqlLargeObjectExport()
|
|
+{
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ sepgsqlGetServerContext(),
|
|
+ SECCLASS_DB_BLOB,
|
|
+ DB_BLOB__EXPORT,
|
|
+ NULL);
|
|
+}
|
|
+
|
|
+/*******************************************************************************
|
|
+ * security_label hooks
|
|
+ *******************************************************************************/
|
|
+char *sepgsqlSecurityLabelIn(char *context) {
|
|
+ security_context_t raw_context, canonical_context;
|
|
+ char *result;
|
|
+ int rc;
|
|
+
|
|
+ rc = selinux_trans_to_raw_context(context, &raw_context);
|
|
+ if (rc)
|
|
+ selerror("could not translate MLS label");
|
|
+
|
|
+ rc = security_canonicalize_context_raw(raw_context, &canonical_context);
|
|
+ freecon(raw_context);
|
|
+ if (rc)
|
|
+ selerror("could not canonicalize the context");
|
|
+
|
|
+ PG_TRY();
|
|
+ {
|
|
+ result = pstrdup(canonical_context);
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(canonical_context);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+ freecon(canonical_context);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+char *sepgsqlSecurityLabelOut(char *raw_context) {
|
|
+ security_context_t context;
|
|
+ char *result;
|
|
+
|
|
+ if (selinux_raw_to_trans_context(raw_context, &context))
|
|
+ selerror("could not translate MLS label");
|
|
+ PG_TRY();
|
|
+ {
|
|
+ result = pstrdup(context);
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(context);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+ freecon(context);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+bool sepgsqlSecurityLabelIsValid(char *context) {
|
|
+ if (!security_check_context_raw(context))
|
|
+ return true;
|
|
+ return false;
|
|
+}
|
|
+
|
|
+char *sepgsqlSecurityLabelOfLabel(char *context) {
|
|
+ HeapTuple tuple;
|
|
+ security_context_t scon, tcon, ncon, _ncon;
|
|
+ int rc;
|
|
+
|
|
+ /* obtain the security context of pg_security */
|
|
+ tuple = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(SecurityRelationId),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ selerror("pg_security (relid=%u) not found", SecurityRelationId);
|
|
+ tcon = DatumGetCString(DirectFunctionCall1(security_label_raw_out,
|
|
+ ObjectIdGetDatum(HeapTupleGetSecurity(tuple))));
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ /* obtain server's context */
|
|
+ rc = getcon_raw(&scon);
|
|
+ if (rc)
|
|
+ selerror("could not obtain server's context");
|
|
+
|
|
+ /* compute pg_selinux tuple context */
|
|
+ rc = security_compute_create_raw(scon, tcon, SECCLASS_DB_TUPLE, &ncon);
|
|
+ pfree(tcon);
|
|
+ freecon(scon);
|
|
+ if (rc)
|
|
+ selerror("could not compute a newly created security context");
|
|
+
|
|
+ /* copy tuple's context */
|
|
+ PG_TRY();
|
|
+ {
|
|
+ _ncon = pstrdup(ncon);
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(ncon);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+
|
|
+ freecon(ncon);
|
|
+
|
|
+ return _ncon;
|
|
+}
|
|
+
|
|
+extern char *selinux_mnt;
|
|
+
|
|
+char *sepgsqlSecurityLabelNotFound(Oid sid) {
|
|
+ security_context_t unlabeled_con;
|
|
+ char *result = NULL;
|
|
+
|
|
+ if (!security_get_initial_context_raw("unlabeled", &unlabeled_con)) {
|
|
+ PG_TRY();
|
|
+ {
|
|
+ result = pstrdup(unlabeled_con);
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(unlabeled_con);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+ freecon(unlabeled_con);
|
|
+ }
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/******************************************************************
|
|
+ * HeapTuple modification hooks
|
|
+ ******************************************************************/
|
|
+static bool __HeapTupleInternalCheckRelation(Relation rel)
|
|
+{
|
|
+ switch (RelationGetRelid(rel)) {
|
|
+ case AggregateRelationId:
|
|
+ case AttributeRelationId:
|
|
+ case AuthIdRelationId:
|
|
+ case CastRelationId:
|
|
+ case ConversionRelationId:
|
|
+ case DatabaseRelationId:
|
|
+ case LanguageRelationId:
|
|
+ case NamespaceRelationId:
|
|
+ case OperatorRelationId:
|
|
+ case OperatorClassRelationId:
|
|
+ case ProcedureRelationId:
|
|
+ case RelationRelationId:
|
|
+ case RewriteRelationId:
|
|
+ case TableSpaceRelationId:
|
|
+ case TriggerRelationId:
|
|
+ case TypeRelationId:
|
|
+ return true;
|
|
+ break;
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+bool sepgsqlHeapTupleInsert(Relation rel, HeapTuple tuple,
|
|
+ bool is_internal, bool with_returning)
|
|
+{
|
|
+ uint32 perms;
|
|
+
|
|
+ /* default context for no explicit labeled tuple */
|
|
+ if (HeapTupleGetSecurity(tuple) == InvalidOid) {
|
|
+ Oid newcon = sepgsqlComputeImplicitContext(rel, tuple);
|
|
+ HeapTupleSetSecurity(tuple, newcon);
|
|
+ }
|
|
+ if (is_internal && !__HeapTupleInternalCheckRelation(rel))
|
|
+ return true;
|
|
+
|
|
+ perms = DB_TUPLE__INSERT;
|
|
+ if (with_returning)
|
|
+ perms |= DB_TUPLE__SELECT;
|
|
+
|
|
+ return sepgsqlCheckTuplePerms(rel, tuple, NULL, perms, is_internal);
|
|
+}
|
|
+
|
|
+bool sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup,
|
|
+ bool is_internal, bool with_returning)
|
|
+{
|
|
+ HeapTuple oldtup;
|
|
+ uint32 perms;
|
|
+ bool rc = true;
|
|
+
|
|
+ oldtup = __getHeapTupleFromItemPointer(rel, otid);
|
|
+
|
|
+ if (HeapTupleGetSecurity(newtup) == InvalidOid) {
|
|
+ /* keep old context for no explicit labeled tuple */
|
|
+ HeapTupleSetSecurity(newtup, HeapTupleGetSecurity(oldtup));
|
|
+ }
|
|
+
|
|
+ if (is_internal && !__HeapTupleInternalCheckRelation(rel))
|
|
+ goto out;
|
|
+
|
|
+ if (is_internal) {
|
|
+ perms = DB_TUPLE__UPDATE;
|
|
+ if (HeapTupleGetSecurity(newtup) != HeapTupleGetSecurity(oldtup))
|
|
+ perms |= DB_TUPLE__RELABELFROM;
|
|
+ rc = sepgsqlCheckTuplePerms(rel, oldtup, NULL, perms, is_internal);
|
|
+ if (!rc)
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ if (HeapTupleGetSecurity(newtup) != HeapTupleGetSecurity(oldtup)) {
|
|
+ perms = DB_TUPLE__RELABELTO;
|
|
+ if (with_returning)
|
|
+ perms |= DB_TUPLE__SELECT;
|
|
+ rc = sepgsqlCheckTuplePerms(rel, newtup, oldtup, perms, is_internal);
|
|
+ }
|
|
+out:
|
|
+ heap_freetuple(oldtup);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+bool sepgsqlHeapTupleDelete(Relation rel, ItemPointer otid,
|
|
+ bool is_internal, bool with_returning)
|
|
+{
|
|
+ HeapTuple oldtup;
|
|
+ bool rc = true;
|
|
+
|
|
+ if (is_internal) {
|
|
+ if (!__HeapTupleInternalCheckRelation(rel))
|
|
+ return true;
|
|
+
|
|
+ oldtup = __getHeapTupleFromItemPointer(rel, otid);
|
|
+ rc = sepgsqlCheckTuplePerms(rel, oldtup, NULL, DB_TUPLE__DELETE, is_internal);
|
|
+ heap_freetuple(oldtup);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
diff -rpNU3 pgace/src/backend/security/sepgsql/interface.c sepgsql/src/backend/security/sepgsql/interface.c
|
|
--- pgace/src/backend/security/sepgsql/interface.c 1970-01-01 09:00:00.000000000 +0900
|
|
+++ sepgsql/src/backend/security/sepgsql/interface.c 2008-01-10 10:15:10.000000000 +0900
|
|
@@ -0,0 +1,366 @@
|
|
+/*
|
|
+ * src/backend/security/sepgsql/interface.c
|
|
+ * SE-PostgreSQL/PGACE Interfaces
|
|
+ *
|
|
+ * Copyright (c) 2007 KaiGai Kohei <kaigai@kaigai.gr.jp>
|
|
+ */
|
|
+#include "postgres.h"
|
|
+
|
|
+#include "executor/executor.h"
|
|
+#include "security/pgace.h"
|
|
+#include "security/sepgsql.h"
|
|
+
|
|
+/******************************************************************
|
|
+ * Initialize / Finalize related hooks
|
|
+ ******************************************************************/
|
|
+Size pgaceShmemSize(void)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ return sepgsqlShmemSize();
|
|
+ return (Size) 0;
|
|
+}
|
|
+
|
|
+void pgaceInitialize(bool is_bootstrap)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlInitialize(is_bootstrap);
|
|
+}
|
|
+
|
|
+bool pgaceInitializePostmaster(void)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ return sepgsqlInitializePostmaster();
|
|
+ return true;
|
|
+}
|
|
+
|
|
+void pgaceFinalizePostmaster(void)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlFinalizePostmaster();
|
|
+}
|
|
+
|
|
+/******************************************************************
|
|
+ * SQL proxy hooks
|
|
+ ******************************************************************/
|
|
+List *pgaceProxyQuery(List *queryList)
|
|
+{
|
|
+ List *newList = NIL;
|
|
+ ListCell *l;
|
|
+
|
|
+ if (sepgsqlIsEnabled()) {
|
|
+ foreach (l, queryList) {
|
|
+ Query *q = (Query *) lfirst(l);
|
|
+
|
|
+ newList = list_concat(newList, sepgsqlProxyQuery(q));
|
|
+ }
|
|
+ queryList = newList;
|
|
+ }
|
|
+ return queryList;
|
|
+}
|
|
+
|
|
+void pgacePortalStart(Portal portal)
|
|
+{
|
|
+ /* do nothing */
|
|
+}
|
|
+
|
|
+void pgaceExecutorStart(QueryDesc *queryDesc, int eflags)
|
|
+{
|
|
+ if (sepgsqlIsEnabled() && !(eflags & EXEC_FLAG_EXPLAIN_ONLY)) {
|
|
+ Assert(queryDesc->plannedstmt != NULL);
|
|
+ sepgsqlVerifyQuery(queryDesc->plannedstmt);
|
|
+ }
|
|
+}
|
|
+
|
|
+/******************************************************************
|
|
+ * HeapTuple modification hooks
|
|
+ ******************************************************************/
|
|
+bool pgaceHeapTupleInsert(Relation rel, HeapTuple tuple,
|
|
+ bool is_internal, bool with_returning)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ return sepgsqlHeapTupleInsert(rel, tuple, is_internal, with_returning);
|
|
+ return true;
|
|
+}
|
|
+
|
|
+bool pgaceHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup,
|
|
+ bool is_internal, bool with_returning)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ return sepgsqlHeapTupleUpdate(rel, otid, newtup, is_internal, with_returning);
|
|
+ return true;
|
|
+}
|
|
+
|
|
+bool pgaceHeapTupleDelete(Relation rel, ItemPointer otid,
|
|
+ bool is_internal, bool with_returning)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ return sepgsqlHeapTupleDelete(rel, otid, is_internal, with_returning);
|
|
+ return true;
|
|
+}
|
|
+
|
|
+/******************************************************************
|
|
+ * Extended SQL statement hooks
|
|
+ ******************************************************************/
|
|
+DefElem *pgaceGramSecurityItem(char *defname, char *value)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ return sepgsqlGramSecurityItem(defname, value);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+bool pgaceIsGramSecurityItem(DefElem *defel)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ return sepgsqlIsGramSecurityItem(defel);
|
|
+ return false;
|
|
+}
|
|
+
|
|
+void pgaceGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ return sepgsqlGramCreateRelation(rel, tuple, defel);
|
|
+}
|
|
+
|
|
+void pgaceGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ return sepgsqlGramCreateAttribute(rel, tuple, defel);
|
|
+}
|
|
+
|
|
+void pgaceGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ return sepgsqlGramAlterRelation(rel, tuple, defel);
|
|
+}
|
|
+
|
|
+void pgaceGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ return sepgsqlGramAlterAttribute(rel, tuple, defel);
|
|
+}
|
|
+
|
|
+void pgaceGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlGramCreateDatabase(rel, tuple, defel);
|
|
+}
|
|
+
|
|
+void pgaceGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlGramAlterDatabase(rel, tuple, defel);
|
|
+}
|
|
+
|
|
+void pgaceGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlGramCreateFunction(rel, tuple, defel);
|
|
+}
|
|
+
|
|
+void pgaceGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlGramAlterFunction(rel, tuple, defel);
|
|
+}
|
|
+
|
|
+/******************************************************************
|
|
+ * DATABASE related hooks
|
|
+ ******************************************************************/
|
|
+void pgaceSetDatabaseParam(const char *name, char *argstring)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlSetDatabaseParam(name, argstring);
|
|
+}
|
|
+
|
|
+void pgaceGetDatabaseParam(const char *name)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlGetDatabaseParam(name);
|
|
+}
|
|
+
|
|
+/******************************************************************
|
|
+ * FUNCTION related hooks
|
|
+ ******************************************************************/
|
|
+void pgaceCallFunction(FmgrInfo *finfo)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlCallFunction(finfo, false);
|
|
+}
|
|
+
|
|
+bool pgaceCallFunctionTrigger(FmgrInfo *finfo, TriggerData *tgdata)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ return sepgsqlCallFunctionTrigger(finfo, tgdata);
|
|
+ return true;
|
|
+}
|
|
+
|
|
+void pgaceCallFunctionFastPath(FmgrInfo *finfo)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlCallFunction(finfo, true);
|
|
+}
|
|
+
|
|
+Datum pgacePreparePlanCheck(Relation rel)
|
|
+{
|
|
+ Oid saved = InvalidOid;
|
|
+
|
|
+ if (sepgsqlIsEnabled())
|
|
+ saved = sepgsqlPreparePlanCheck(rel);
|
|
+ return ObjectIdGetDatum(saved);
|
|
+}
|
|
+
|
|
+void pgaceRestorePlanCheck(Relation rel, Datum pgace_saved)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlRestorePlanCheck(rel, DatumGetObjectId(pgace_saved));
|
|
+}
|
|
+
|
|
+/******************************************************************
|
|
+ * TABLE related hooks
|
|
+ ******************************************************************/
|
|
+void pgaceLockTable(Oid relid)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlLockTable(relid);
|
|
+}
|
|
+
|
|
+/******************************************************************
|
|
+ * COPY TO/COPY FROM statement hooks
|
|
+ ******************************************************************/
|
|
+void pgaceCopyTable(Relation rel, List *attNumList, bool isFrom) {
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlCopyTable(rel, attNumList, isFrom);
|
|
+}
|
|
+
|
|
+bool pgaceCopyToTuple(Relation rel, HeapTuple tuple) {
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlCopyToTuple(rel, tuple);
|
|
+ return true;
|
|
+}
|
|
+
|
|
+/******************************************************************
|
|
+ * Loadable shared library module hooks
|
|
+ ******************************************************************/
|
|
+void pgaceLoadSharedModule(const char *filename) {
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlLoadSharedModule(filename);
|
|
+}
|
|
+
|
|
+/******************************************************************
|
|
+ * Binary Large Object (BLOB) hooks
|
|
+ ******************************************************************/
|
|
+
|
|
+Oid pgaceLargeObjectGetSecurity(HeapTuple tuple) {
|
|
+ if (!sepgsqlIsEnabled())
|
|
+ selerror("SELinux is disabled");
|
|
+ return sepgsqlLargeObjectGetSecurity(tuple);
|
|
+}
|
|
+
|
|
+void pgaceLargeObjectSetSecurity(HeapTuple tuple, Oid lo_security, bool is_first) {
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlLargeObjectSetSecurity(tuple, lo_security, is_first);
|
|
+}
|
|
+
|
|
+void pgaceLargeObjectCreate(Relation rel, HeapTuple tuple) {
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlLargeObjectCreate(rel, tuple);
|
|
+}
|
|
+
|
|
+void pgaceLargeObjectDrop(Relation rel, HeapTuple tuple) {
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlLargeObjectDrop(rel, tuple);
|
|
+}
|
|
+
|
|
+void pgaceLargeObjectOpen(Relation rel, HeapTuple tuple, bool read_only) {
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlLargeObjectOpen(rel, tuple, read_only);
|
|
+}
|
|
+
|
|
+void pgaceLargeObjectRead(Relation rel, HeapTuple tuple, bool is_first) {
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlLargeObjectRead(rel, tuple);
|
|
+}
|
|
+
|
|
+void pgaceLargeObjectWrite(Relation rel, HeapTuple newtup, HeapTuple oldtup, bool is_first) {
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlLargeObjectWrite(rel, newtup, oldtup);
|
|
+}
|
|
+
|
|
+void pgaceLargeObjectTruncate(Relation rel, Oid loid) {
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlLargeObjectTruncate(rel, loid);
|
|
+}
|
|
+
|
|
+void pgaceLargeObjectImport(int fd) {
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlLargeObjectImport();
|
|
+}
|
|
+
|
|
+void pgaceLargeObjectExport(int fd, Oid loid) {
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlLargeObjectExport();
|
|
+}
|
|
+
|
|
+/******************************************************************
|
|
+ * Security Label hooks
|
|
+ ******************************************************************/
|
|
+char *pgaceSecurityLabelIn(char *seclabel)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ seclabel = sepgsqlSecurityLabelIn(seclabel);
|
|
+ return seclabel;
|
|
+}
|
|
+
|
|
+char *pgaceSecurityLabelOut(char *seclabel)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ seclabel = sepgsqlSecurityLabelOut(seclabel);
|
|
+ return seclabel;
|
|
+}
|
|
+
|
|
+bool pgaceSecurityLabelIsValid(char *seclabel)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ return sepgsqlSecurityLabelIsValid(seclabel);
|
|
+ return true;
|
|
+}
|
|
+
|
|
+char *pgaceSecurityLabelOfLabel(char *new_label)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ return sepgsqlSecurityLabelOfLabel(new_label);
|
|
+ return pstrdup("unlabeled");
|
|
+}
|
|
+
|
|
+char *pgaceSecurityLabelNotFound(Oid sid)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ return sepgsqlSecurityLabelNotFound(sid);
|
|
+ return pstrdup("unlabeled");
|
|
+}
|
|
+
|
|
+/******************************************************************
|
|
+ * Extended node type hooks
|
|
+ ******************************************************************/
|
|
+Node *pgaceCopyObject(Node *orig)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ return sepgsqlCopyObject(orig);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+bool pgaceOutObject(StringInfo str, Node *node)
|
|
+{
|
|
+ if (sepgsqlIsEnabled())
|
|
+ sepgsqlOutObject(str, node);
|
|
+ return false;
|
|
+}
|
|
+
|
|
+void *pgaceReadObject(char *token)
|
|
+{
|
|
+ void *result = NULL;
|
|
+
|
|
+ if (sepgsqlIsEnabled())
|
|
+ result = sepgsqlReadObject(token);
|
|
+ return NULL;
|
|
+}
|
|
diff -rpNU3 pgace/src/backend/security/sepgsql/permissions.c sepgsql/src/backend/security/sepgsql/permissions.c
|
|
--- pgace/src/backend/security/sepgsql/permissions.c 1970-01-01 09:00:00.000000000 +0900
|
|
+++ sepgsql/src/backend/security/sepgsql/permissions.c 2007-11-24 18:15:13.000000000 +0900
|
|
@@ -0,0 +1,490 @@
|
|
+/*
|
|
+ * src/backend/security/sepgsqlPerms.c
|
|
+ * SE-PostgreSQL permission checking functions
|
|
+ *
|
|
+ * Copyright (c) 2007 KaiGai Kohei <kaigai@kaigai.gr.jp>
|
|
+ */
|
|
+#include "postgres.h"
|
|
+
|
|
+#include "access/genam.h"
|
|
+#include "access/heapam.h"
|
|
+#include "miscadmin.h"
|
|
+#include "security/pgace.h"
|
|
+#include "security/sepgsql.h"
|
|
+#include "utils/builtins.h"
|
|
+#include "utils/fmgroids.h"
|
|
+#include "utils/syscache.h"
|
|
+#include "utils/typcache.h"
|
|
+
|
|
+/*
|
|
+ * If we have to refere a object which is newly inserted or updated
|
|
+ * in the same command, SearchSysCache() returns NULL because it use
|
|
+ * SnapshowNow internally. The followings are fallback routine to
|
|
+ * avoid a failed cache lookup.
|
|
+ */
|
|
+static Oid __lookupRelationForm(Oid relid, Form_pg_class classForm) {
|
|
+ Relation rel;
|
|
+ SysScanDesc scan;
|
|
+ ScanKeyData skey;
|
|
+ HeapTuple tuple;
|
|
+ Oid t_security;
|
|
+
|
|
+ tuple = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(relid),
|
|
+ 0, 0, 0);
|
|
+ if (HeapTupleIsValid(tuple)) {
|
|
+ if (classForm)
|
|
+ memcpy(classForm, GETSTRUCT(tuple), sizeof(FormData_pg_class));
|
|
+ t_security = HeapTupleGetSecurity(tuple);
|
|
+ ReleaseSysCache(tuple);
|
|
+ return t_security;
|
|
+ }
|
|
+
|
|
+ rel = heap_open(RelationRelationId, AccessShareLock);
|
|
+ ScanKeyInit(&skey,
|
|
+ ObjectIdAttributeNumber,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(relid));
|
|
+ scan = systable_beginscan(rel, ClassOidIndexId,
|
|
+ true, SnapshotSelf, 1, &skey);
|
|
+ tuple = systable_getnext(scan);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ selerror("relation %u is not exist", relid);
|
|
+
|
|
+ if (classForm)
|
|
+ memcpy(classForm, GETSTRUCT(tuple), sizeof(FormData_pg_class));
|
|
+ t_security = HeapTupleGetSecurity(tuple);
|
|
+
|
|
+ systable_endscan(scan);
|
|
+ heap_close(rel, AccessShareLock);
|
|
+
|
|
+ return t_security;
|
|
+}
|
|
+
|
|
+static uint32 __tuple_perms_to_common_perms(uint32 perms) {
|
|
+ uint32 __perms = 0;
|
|
+ __perms |= (perms & DB_TUPLE__RELABELFROM ? COMMON_DATABASE__RELABELFROM : 0);
|
|
+ __perms |= (perms & DB_TUPLE__RELABELTO ? COMMON_DATABASE__RELABELTO : 0);
|
|
+ __perms |= (perms & DB_TUPLE__SELECT ? COMMON_DATABASE__GETATTR : 0);
|
|
+ __perms |= (perms & DB_TUPLE__UPDATE ? COMMON_DATABASE__SETATTR : 0);
|
|
+ __perms |= (perms & DB_TUPLE__INSERT ? COMMON_DATABASE__CREATE : 0);
|
|
+ __perms |= (perms & DB_TUPLE__DELETE ? COMMON_DATABASE__DROP : 0);
|
|
+ return __perms;
|
|
+}
|
|
+
|
|
+char *sepgsqlGetTupleName(Oid relid, HeapTuple tuple)
|
|
+{
|
|
+ char buffer[NAMEDATALEN * 2 + 32];
|
|
+
|
|
+ switch (relid) {
|
|
+ case AccessMethodRelationId:
|
|
+ return NameStr(((Form_pg_am) GETSTRUCT(tuple))->amname);
|
|
+
|
|
+ case AttributeRelationId: {
|
|
+ Form_pg_attribute attrForm = (Form_pg_attribute) GETSTRUCT(tuple);
|
|
+ Form_pg_class classForm;
|
|
+ HeapTuple reltup;
|
|
+
|
|
+ if (IsBootstrapProcessingMode())
|
|
+ return NameStr(attrForm->attname);
|
|
+
|
|
+ reltup = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(attrForm->attrelid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(reltup))
|
|
+ return NameStr(attrForm->attname);
|
|
+
|
|
+ classForm = (Form_pg_class) GETSTRUCT(reltup);
|
|
+ snprintf(buffer, sizeof(buffer), "%s.%s",
|
|
+ NameStr(classForm->relname),
|
|
+ NameStr(attrForm->attname));
|
|
+ ReleaseSysCache(reltup);
|
|
+ return pstrdup(buffer);
|
|
+ }
|
|
+ case AuthIdRelationId:
|
|
+ return NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname);
|
|
+
|
|
+ case RelationRelationId:
|
|
+ return NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname);
|
|
+
|
|
+ case ConstraintRelationId:
|
|
+ return NameStr(((Form_pg_constraint) GETSTRUCT(tuple))->conname);
|
|
+
|
|
+ case ConversionRelationId:
|
|
+ return NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname);
|
|
+
|
|
+ case DatabaseRelationId:
|
|
+ return NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname);
|
|
+
|
|
+ case LanguageRelationId:
|
|
+ return NameStr(((Form_pg_language) GETSTRUCT(tuple))->lanname);
|
|
+
|
|
+ case LargeObjectRelationId:
|
|
+ snprintf(buffer, sizeof(buffer), "loid:%u",
|
|
+ ((Form_pg_largeobject) GETSTRUCT(tuple))->loid);
|
|
+ return pstrdup(buffer);
|
|
+
|
|
+ case ListenerRelationId:
|
|
+ return NameStr(((Form_pg_listener) GETSTRUCT(tuple))->relname);
|
|
+
|
|
+ case NamespaceRelationId:
|
|
+ return NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname);
|
|
+
|
|
+ case OperatorClassRelationId:
|
|
+ return NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname);
|
|
+
|
|
+ case OperatorRelationId:
|
|
+ return NameStr(((Form_pg_operator) GETSTRUCT(tuple))->oprname);
|
|
+
|
|
+ case PLTemplateRelationId:
|
|
+ return NameStr(((Form_pg_pltemplate) GETSTRUCT(tuple))->tmplname);
|
|
+
|
|
+ case ProcedureRelationId:
|
|
+ return NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname);
|
|
+
|
|
+ case RewriteRelationId:
|
|
+ return NameStr(((Form_pg_rewrite) GETSTRUCT(tuple))->rulename);
|
|
+
|
|
+ case TableSpaceRelationId:
|
|
+ return NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname);
|
|
+
|
|
+ case TriggerRelationId:
|
|
+ return NameStr(((Form_pg_trigger) GETSTRUCT(tuple))->tgname);
|
|
+
|
|
+ case TypeRelationId:
|
|
+ snprintf(buffer, sizeof(buffer), "pg_type.%s",
|
|
+ NameStr(((Form_pg_type) GETSTRUCT(tuple))->typname));
|
|
+ return pstrdup(buffer);
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void __check_pg_attribute(HeapTuple tuple, HeapTuple oldtup,
|
|
+ uint32 *p_perms, uint16 *p_tclass)
|
|
+{
|
|
+ Form_pg_attribute attrForm = (Form_pg_attribute) GETSTRUCT(tuple);
|
|
+ FormData_pg_class classForm;
|
|
+
|
|
+ switch (attrForm->attrelid) {
|
|
+ case TypeRelationId:
|
|
+ case ProcedureRelationId:
|
|
+ case AttributeRelationId:
|
|
+ case RelationRelationId:
|
|
+ /* those are pure relation */
|
|
+ break;
|
|
+ default:
|
|
+ __lookupRelationForm(attrForm->attrelid, &classForm);
|
|
+ if (classForm.relkind != RELKIND_RELATION) {
|
|
+ *p_tclass = SECCLASS_DB_TUPLE;
|
|
+ return;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ *p_tclass = SECCLASS_DB_COLUMN;
|
|
+ *p_perms = __tuple_perms_to_common_perms(*p_perms);
|
|
+ if (HeapTupleIsValid(oldtup)) {
|
|
+ Form_pg_attribute oldForm = (Form_pg_attribute) GETSTRUCT(oldtup);
|
|
+
|
|
+ if (oldForm->attisdropped != true && attrForm->attisdropped == true)
|
|
+ *p_perms |= DB_COLUMN__DROP;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void __check_pg_largeobject(HeapTuple tuple, HeapTuple oldtup,
|
|
+ uint32 *p_perms, uint16 *p_tclass)
|
|
+{
|
|
+ Form_pg_largeobject blobForm
|
|
+ = (Form_pg_largeobject) GETSTRUCT(tuple);
|
|
+ Relation rel;
|
|
+ ScanKeyData skey;
|
|
+ SysScanDesc sd;
|
|
+ uint32 perms = 0;
|
|
+
|
|
+ perms |= (*p_perms & DB_TUPLE__SELECT ? DB_BLOB__GETATTR : 0);
|
|
+ perms |= (*p_perms & DB_TUPLE__UPDATE ? DB_BLOB__SETATTR : 0);
|
|
+ perms |= (*p_perms & DB_BLOB__READ ? DB_BLOB__READ : 0);
|
|
+ perms |= (*p_perms & DB_BLOB__WRITE ? DB_BLOB__WRITE : 0);
|
|
+
|
|
+ if (*p_perms & DB_TUPLE__INSERT) {
|
|
+ bool found = false;
|
|
+
|
|
+ ScanKeyInit(&skey,
|
|
+ Anum_pg_largeobject_loid,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(blobForm->loid));
|
|
+ rel = heap_open(LargeObjectRelationId, AccessShareLock);
|
|
+ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true,
|
|
+ SnapshotSelf, 1, &skey);
|
|
+ if (HeapTupleIsValid(systable_getnext(sd)))
|
|
+ found = true;
|
|
+ systable_endscan(sd);
|
|
+ heap_close(rel, AccessShareLock);
|
|
+ perms |= (!found ? DB_BLOB__CREATE : DB_BLOB__SETATTR);
|
|
+ }
|
|
+
|
|
+ if (*p_perms & DB_TUPLE__DELETE) {
|
|
+ HeapTuple exttup;
|
|
+ bool found = false;
|
|
+
|
|
+ ScanKeyInit(&skey,
|
|
+ Anum_pg_largeobject_loid,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(blobForm->loid));
|
|
+ rel = heap_open(LargeObjectRelationId, AccessShareLock);
|
|
+ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true,
|
|
+ SnapshotSelf, 1, &skey);
|
|
+ while ((exttup = systable_getnext(sd))) {
|
|
+ int __pageno = ((Form_pg_largeobject) GETSTRUCT(exttup))->pageno;
|
|
+
|
|
+ if (blobForm->pageno != __pageno) {
|
|
+ found = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ systable_endscan(sd);
|
|
+ heap_close(rel, AccessShareLock);
|
|
+ perms |= (!found ? DB_BLOB__DROP : DB_BLOB__SETATTR);
|
|
+ }
|
|
+ *p_tclass = SECCLASS_DB_BLOB;
|
|
+ *p_perms = perms;
|
|
+}
|
|
+
|
|
+static void __check_pg_proc(HeapTuple tuple, HeapTuple oldtup,
|
|
+ uint32 *p_perms, uint16 *p_tclass)
|
|
+{
|
|
+ uint32 perms = __tuple_perms_to_common_perms(*p_perms);
|
|
+ Form_pg_proc procForm = (Form_pg_proc) GETSTRUCT(tuple);
|
|
+
|
|
+ if (procForm->prolang == ClanguageId) {
|
|
+ Datum oldbin, newbin;
|
|
+ bool isnull, verify = false;
|
|
+
|
|
+ newbin = SysCacheGetAttr(PROCOID, tuple,
|
|
+ Anum_pg_proc_probin, &isnull);
|
|
+ if (!isnull) {
|
|
+ if (perms & DB_PROCEDURE__CREATE) {
|
|
+ verify = true;
|
|
+ } else if (HeapTupleIsValid(oldtup)) {
|
|
+ oldbin = SysCacheGetAttr(PROCOID, oldtup,
|
|
+ Anum_pg_proc_probin, &isnull);
|
|
+ if (isnull || DatumGetBool(DirectFunctionCall2(textne, oldbin, newbin)))
|
|
+ verify = true;
|
|
+ }
|
|
+
|
|
+ if (verify) {
|
|
+ char *filename;
|
|
+ security_context_t filecon;
|
|
+ Datum filesid;
|
|
+
|
|
+ /* <client type> <-- database:module_install --> <database type> */
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ sepgsqlGetDatabaseContext(),
|
|
+ SECCLASS_DB_DATABASE,
|
|
+ DB_DATABASE__INSTALL_MODULE,
|
|
+ NULL);
|
|
+
|
|
+ /* <client type> <-- database:module_install --> <file type> */
|
|
+ filename = DatumGetCString(DirectFunctionCall1(textout, newbin));
|
|
+ filename = expand_dynamic_library_name(filename);
|
|
+ if (getfilecon_raw(filename, &filecon) < 1)
|
|
+ selerror("could not obtain the security context of '%s'", filename);
|
|
+ PG_TRY();
|
|
+ {
|
|
+ filesid = DirectFunctionCall1(security_label_raw_in,
|
|
+ CStringGetDatum(filecon));
|
|
+ }
|
|
+ PG_CATCH();
|
|
+ {
|
|
+ freecon(filecon);
|
|
+ PG_RE_THROW();
|
|
+ }
|
|
+ PG_END_TRY();
|
|
+ freecon(filecon);
|
|
+
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ DatumGetObjectId(filesid),
|
|
+ SECCLASS_DB_DATABASE,
|
|
+ DB_DATABASE__INSTALL_MODULE,
|
|
+ filename);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ *p_perms = perms;
|
|
+ *p_tclass = SECCLASS_DB_PROCEDURE;
|
|
+}
|
|
+
|
|
+static void __check_pg_relation(HeapTuple tuple, HeapTuple oldtup,
|
|
+ uint32 *p_perms, uint16 *p_tclass)
|
|
+{
|
|
+ Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
|
|
+ if (classForm->relkind == RELKIND_RELATION) {
|
|
+ *p_tclass = SECCLASS_DB_TABLE;
|
|
+ *p_perms = __tuple_perms_to_common_perms(*p_perms);
|
|
+ } else {
|
|
+ *p_tclass = SECCLASS_DB_TUPLE;
|
|
+ }
|
|
+}
|
|
+
|
|
+static bool __check_tuple_perms(Oid tableoid, Oid tcontext, uint32 perms,
|
|
+ HeapTuple tuple, HeapTuple oldtup, bool abort)
|
|
+{
|
|
+ uint16 tclass;
|
|
+ bool rc = true;
|
|
+
|
|
+ Assert(tuple != NULL);
|
|
+
|
|
+ switch (tableoid) {
|
|
+ case DatabaseRelationId: /* pg_database */
|
|
+ perms = __tuple_perms_to_common_perms(perms);
|
|
+ tclass = SECCLASS_DB_DATABASE;
|
|
+ break;
|
|
+
|
|
+ case RelationRelationId: /* pg_class */
|
|
+ __check_pg_relation(tuple, oldtup, &perms, &tclass);
|
|
+ break;
|
|
+
|
|
+ case AttributeRelationId: /* pg_attribute */
|
|
+ __check_pg_attribute(tuple, oldtup, &perms, &tclass);
|
|
+ break;
|
|
+
|
|
+ case ProcedureRelationId: /* pg_proc */
|
|
+ __check_pg_proc(tuple, oldtup, &perms, &tclass);
|
|
+ break;
|
|
+
|
|
+ case LargeObjectRelationId: /* pg_largeobject */
|
|
+ __check_pg_largeobject(tuple, oldtup, &perms, &tclass);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ tclass = SECCLASS_DB_TUPLE;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (perms) {
|
|
+ char *audit;
|
|
+ rc = sepgsql_avc_permission_noaudit(sepgsqlGetClientContext(),
|
|
+ tcontext,
|
|
+ tclass,
|
|
+ perms,
|
|
+ &audit,
|
|
+ sepgsqlGetTupleName(tableoid, tuple));
|
|
+ sepgsql_audit(abort ? rc : true, audit);
|
|
+ }
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * MEMO: we cannot obtain system column from RECORD datatype.
|
|
+ * If those are necesasry, they should be separately delivered.
|
|
+ */
|
|
+Datum sepgsql_tuple_perms(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ Oid tableoid = PG_GETARG_OID(0);
|
|
+ Oid tcontext = PG_GETARG_OID(1);
|
|
+ uint32 perms = PG_GETARG_UINT32(2);
|
|
+ HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(3);
|
|
+ HeapTupleData tuple;
|
|
+
|
|
+ tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
|
|
+ ItemPointerSetInvalid(&tuple.t_self);
|
|
+ tuple.t_tableOid = tableoid;
|
|
+ tuple.t_data = rec;
|
|
+
|
|
+ PG_RETURN_BOOL(__check_tuple_perms(tableoid, tcontext, perms, &tuple, NULL, false));
|
|
+}
|
|
+
|
|
+Datum sepgsql_tuple_perms_abort(PG_FUNCTION_ARGS)
|
|
+{
|
|
+ Oid tableoid = PG_GETARG_OID(0);
|
|
+ Oid tcontext = PG_GETARG_OID(1);
|
|
+ uint32 perms = PG_GETARG_UINT32(2);
|
|
+ HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(3);
|
|
+ HeapTupleData tuple;
|
|
+
|
|
+ tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
|
|
+ ItemPointerSetInvalid(&tuple.t_self);
|
|
+ tuple.t_tableOid = tableoid;
|
|
+ tuple.t_data = rec;
|
|
+
|
|
+ PG_RETURN_BOOL(__check_tuple_perms(tableoid, tcontext, perms, &tuple, NULL, true));
|
|
+}
|
|
+
|
|
+bool sepgsqlCheckTuplePerms(Relation rel, HeapTuple tuple, HeapTuple oldtup, uint32 perms, bool abort)
|
|
+{
|
|
+ return __check_tuple_perms(RelationGetRelid(rel),
|
|
+ HeapTupleGetSecurity(tuple),
|
|
+ perms,
|
|
+ tuple,
|
|
+ oldtup,
|
|
+ abort);
|
|
+}
|
|
+
|
|
+Oid sepgsqlComputeImplicitContext(Relation rel, HeapTuple tuple) {
|
|
+ uint16 tclass;
|
|
+ Oid tcon;
|
|
+
|
|
+ switch (RelationGetRelid(rel)) {
|
|
+ case DatabaseRelationId: /* pg_database */
|
|
+ tclass = SECCLASS_DB_DATABASE;
|
|
+ tcon = sepgsqlGetServerContext();
|
|
+ break;
|
|
+
|
|
+ case RelationRelationId: { /* pg_class */
|
|
+ Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
|
|
+ if (classForm->relkind == RELKIND_RELATION) {
|
|
+ tclass = SECCLASS_DB_TABLE;
|
|
+ tcon = sepgsqlGetDatabaseContext();
|
|
+ break;
|
|
+ }
|
|
+ tcon = __lookupRelationForm(RelationRelationId, NULL);
|
|
+ tclass = SECCLASS_DB_TUPLE;
|
|
+ break;
|
|
+ }
|
|
+ case AttributeRelationId: { /* pg_attribute */
|
|
+ Form_pg_attribute attrForm = (Form_pg_attribute) GETSTRUCT(tuple);
|
|
+ FormData_pg_class classForm;
|
|
+
|
|
+ /* special case in bootstraping mode */
|
|
+ if (IsBootstrapProcessingMode()
|
|
+ && (attrForm->attrelid == TypeRelationId ||
|
|
+ attrForm->attrelid == ProcedureRelationId ||
|
|
+ attrForm->attrelid == AttributeRelationId ||
|
|
+ attrForm->attrelid == RelationRelationId)) {
|
|
+ tcon = sepgsql_avc_createcon(sepgsqlGetClientContext(),
|
|
+ sepgsqlGetDatabaseContext(),
|
|
+ SECCLASS_DB_TABLE);
|
|
+ tclass = SECCLASS_DB_COLUMN;
|
|
+ break;
|
|
+ }
|
|
+ tcon = __lookupRelationForm(attrForm->attrelid, &classForm);
|
|
+ tclass = (classForm.relkind == RELKIND_RELATION
|
|
+ ? SECCLASS_DB_COLUMN
|
|
+ : SECCLASS_DB_TUPLE);
|
|
+ break;
|
|
+ }
|
|
+ case ProcedureRelationId:
|
|
+ tclass = SECCLASS_DB_PROCEDURE;
|
|
+ tcon = sepgsqlGetDatabaseContext();
|
|
+ break;
|
|
+
|
|
+ case LargeObjectRelationId:
|
|
+ tclass = SECCLASS_DB_BLOB;
|
|
+ tcon = sepgsqlGetDatabaseContext();
|
|
+ break;
|
|
+
|
|
+ case TypeRelationId: /* pg_type */
|
|
+ if (IsBootstrapProcessingMode()) {
|
|
+ /* special case in early phase */
|
|
+ tcon = sepgsql_avc_createcon(sepgsqlGetClientContext(),
|
|
+ sepgsqlGetDatabaseContext(),
|
|
+ SECCLASS_DB_TABLE);
|
|
+ tclass = SECCLASS_DB_TUPLE;
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ tclass = SECCLASS_DB_TUPLE;
|
|
+ tcon = __lookupRelationForm(RelationGetRelid(rel), NULL);
|
|
+ break;
|
|
+ }
|
|
+ return sepgsql_avc_createcon(sepgsqlGetClientContext(), tcon, tclass);
|
|
+}
|
|
diff -rpNU3 pgace/src/backend/security/sepgsql/proxy.c sepgsql/src/backend/security/sepgsql/proxy.c
|
|
--- pgace/src/backend/security/sepgsql/proxy.c 1970-01-01 09:00:00.000000000 +0900
|
|
+++ sepgsql/src/backend/security/sepgsql/proxy.c 2008-01-11 15:22:55.000000000 +0900
|
|
@@ -0,0 +1,1589 @@
|
|
+/*
|
|
+ * src/backend/security/sepgsqlProxy.c
|
|
+ * SE-PostgreSQL Query Proxy function to walk on query node tree
|
|
+ * and append tuple filter.
|
|
+ *
|
|
+ * Copyright KaiGai Kohei <kaigai@kaigai.gr.jp>
|
|
+ */
|
|
+#include "postgres.h"
|
|
+
|
|
+#include "access/genam.h"
|
|
+#include "access/heapam.h"
|
|
+#include "catalog/heap.h"
|
|
+#include "catalog/pg_attribute.h"
|
|
+#include "catalog/pg_class.h"
|
|
+#include "catalog/pg_database.h"
|
|
+#include "catalog/pg_largeobject.h"
|
|
+#include "catalog/pg_operator.h"
|
|
+#include "catalog/pg_proc.h"
|
|
+#include "catalog/pg_type.h"
|
|
+#include "executor/spi.h"
|
|
+#include "nodes/makefuncs.h"
|
|
+#include "nodes/readfuncs.h"
|
|
+#include "optimizer/plancat.h"
|
|
+#include "parser/parse_relation.h"
|
|
+#include "parser/parse_target.h"
|
|
+#include "parser/parsetree.h"
|
|
+#include "security/pgace.h"
|
|
+#include "security/sepgsql.h"
|
|
+#include "storage/lock.h"
|
|
+#include "utils/fmgroids.h"
|
|
+#include "utils/syscache.h"
|
|
+
|
|
+#define RTEMARK_USE (1<<(N_ACL_RIGHTS))
|
|
+#define RTEMARK_SELECT (1<<(N_ACL_RIGHTS + 1))
|
|
+#define RTEMARK_INSERT (1<<(N_ACL_RIGHTS + 2))
|
|
+#define RTEMARK_UPDATE (1<<(N_ACL_RIGHTS + 3))
|
|
+#define RTEMARK_DELETE (1<<(N_ACL_RIGHTS + 4))
|
|
+#define RTEMARK_RELABELFROM (1<<(N_ACL_RIGHTS + 5))
|
|
+#define RTEMARK_RELABELTO (1<<(N_ACL_RIGHTS + 6))
|
|
+#define RTEMARK_BLOB_READ (1<<(N_ACL_RIGHTS + 7))
|
|
+#define RTEMARK_BLOB_WRITE (1<<(N_ACL_RIGHTS + 8))
|
|
+
|
|
+/* SE-PostgreSQL Evaluation Item */
|
|
+#define T_SEvalItem (T_TIDBitmap + 1) /* must be unique identifier */
|
|
+
|
|
+typedef struct SEvalItem {
|
|
+ NodeTag type;
|
|
+ uint16 tclass;
|
|
+ uint32 perms;
|
|
+ union {
|
|
+ struct {
|
|
+ Oid relid;
|
|
+ bool inh;
|
|
+ } c; /* for pg_class */
|
|
+ struct {
|
|
+ Oid relid;
|
|
+ bool inh;
|
|
+ AttrNumber attno;
|
|
+ } a; /* for pg_attribute */
|
|
+ struct {
|
|
+ Oid funcid;
|
|
+ } p; /* for pg_proc */
|
|
+ };
|
|
+} SEvalItem;
|
|
+
|
|
+/* query stack definition for outer references */
|
|
+typedef struct queryChain {
|
|
+ struct queryChain *parent;
|
|
+ Query *tail;
|
|
+} queryChain;
|
|
+
|
|
+static inline queryChain *upperQueryChain(queryChain *qc, int lvup) {
|
|
+ while (lvup > 0) {
|
|
+ Assert(!!qc->parent);
|
|
+ qc = qc->parent;
|
|
+ lvup--;
|
|
+ }
|
|
+ return qc;
|
|
+}
|
|
+
|
|
+static inline Query *getQueryFromChain(queryChain *qc) {
|
|
+ return qc->tail;
|
|
+}
|
|
+
|
|
+/* static definitions for proxy functions */
|
|
+static List *proxyRteRelation(List *selist, queryChain *qc, int rtindex, Node **quals);
|
|
+static List *proxyRteSubQuery(List *selist, queryChain *qc, Query *query);
|
|
+static List *proxyJoinTree(List *selist, queryChain *qc, Node *n, Node **quals);
|
|
+static List *proxySetOperations(List *selist, queryChain *qc, Node *n);
|
|
+
|
|
+/* static */
|
|
+static List *sepgsqlWalkExpr(List *selist, queryChain *qc, Node *n, int flags);
|
|
+#define WKFLAG_INTERNAL_USE (0x0001)
|
|
+
|
|
+/* -----------------------------------------------------------
|
|
+ * addEvalXXXX -- add evaluation items into Query->SEvalItemList.
|
|
+ * Those are used for execution phase.
|
|
+ * ----------------------------------------------------------- */
|
|
+static List *__addEvalPgClass(List *selist, Oid relid, bool inh, uint32 perms)
|
|
+{
|
|
+ SEvalItem *se;
|
|
+ ListCell *l;
|
|
+
|
|
+ foreach (l, selist) {
|
|
+ se = (SEvalItem *) lfirst(l);
|
|
+ if (se->tclass == SECCLASS_DB_TABLE
|
|
+ && se->c.relid == relid
|
|
+ && se->c.inh == inh) {
|
|
+ se->perms |= perms;
|
|
+ return selist;
|
|
+ }
|
|
+ }
|
|
+ /* not found */
|
|
+ se = makeNode(SEvalItem);
|
|
+ se->tclass = SECCLASS_DB_TABLE;
|
|
+ se->perms = perms;
|
|
+ se->c.relid = relid;
|
|
+ se->c.inh = inh;
|
|
+ return lappend(selist, se);
|
|
+}
|
|
+
|
|
+static List *addEvalPgClass(List *selist, RangeTblEntry *rte, uint32 perms)
|
|
+{
|
|
+ rte->requiredPerms |= (perms & DB_TABLE__USE ? RTEMARK_USE : 0);
|
|
+ rte->requiredPerms |= (perms & DB_TABLE__SELECT ? RTEMARK_SELECT : 0);
|
|
+ rte->requiredPerms |= (perms & DB_TABLE__INSERT ? RTEMARK_INSERT : 0);
|
|
+ rte->requiredPerms |= (perms & DB_TABLE__UPDATE ? RTEMARK_UPDATE : 0);
|
|
+ rte->requiredPerms |= (perms & DB_TABLE__DELETE ? RTEMARK_DELETE : 0);
|
|
+
|
|
+ return __addEvalPgClass(selist, rte->relid, rte->inh, perms);
|
|
+}
|
|
+
|
|
+static List *__addEvalPgAttribute(List *selist, Oid relid, bool inh, AttrNumber attno, uint32 perms)
|
|
+{
|
|
+ ListCell *l;
|
|
+ SEvalItem *se;
|
|
+
|
|
+ foreach (l, selist) {
|
|
+ se = (SEvalItem *) lfirst(l);
|
|
+ if (se->tclass == SECCLASS_DB_COLUMN
|
|
+ && se->a.relid == relid
|
|
+ && se->a.inh == inh
|
|
+ && se->a.attno == attno) {
|
|
+ se->perms |= perms;
|
|
+ return selist;
|
|
+ }
|
|
+ }
|
|
+ /* not found */
|
|
+ se = makeNode(SEvalItem);
|
|
+ se->tclass = SECCLASS_DB_COLUMN;
|
|
+ se->perms = perms;
|
|
+ se->a.relid = relid;
|
|
+ se->a.inh = inh;
|
|
+ se->a.attno = attno;
|
|
+
|
|
+ return lappend(selist, se);
|
|
+}
|
|
+
|
|
+static List *addEvalPgAttribute(List *selist, RangeTblEntry *rte, AttrNumber attno, uint32 perms)
|
|
+{
|
|
+ /* for 'security_context' */
|
|
+ if (attno == SecurityAttributeNumber
|
|
+ && (perms & (DB_COLUMN__UPDATE | DB_COLUMN__INSERT)))
|
|
+ rte->requiredPerms |= RTEMARK_RELABELFROM;
|
|
+
|
|
+ /* for 'pg_largeobject' */
|
|
+ if (rte->relid == LargeObjectRelationId
|
|
+ && attno == Anum_pg_largeobject_data) {
|
|
+ if (perms & DB_COLUMN__SELECT)
|
|
+ rte->requiredPerms |= RTEMARK_BLOB_READ;
|
|
+ if (perms & (DB_COLUMN__UPDATE | DB_COLUMN__INSERT))
|
|
+ rte->requiredPerms |= RTEMARK_BLOB_WRITE;
|
|
+ }
|
|
+
|
|
+ return __addEvalPgAttribute(selist, rte->relid, rte->inh, attno, perms);
|
|
+}
|
|
+
|
|
+static List *addEvalPgProc(List *selist, Oid funcid, uint32 perms)
|
|
+{
|
|
+ ListCell *l;
|
|
+ SEvalItem *se;
|
|
+
|
|
+ foreach (l, selist) {
|
|
+ se = (SEvalItem *) lfirst(l);
|
|
+ if (se->tclass == SECCLASS_DB_PROCEDURE
|
|
+ && se->p.funcid == funcid) {
|
|
+ se->perms |= perms;
|
|
+ return selist;
|
|
+ }
|
|
+ }
|
|
+ se = makeNode(SEvalItem);
|
|
+ se->tclass = SECCLASS_DB_PROCEDURE;
|
|
+ se->perms = perms;
|
|
+ se->p.funcid = funcid;
|
|
+
|
|
+ return lappend(selist, se);
|
|
+}
|
|
+
|
|
+static List *addEvalTriggerAccess(List *selist, Oid relid, bool is_inh, int cmdType)
|
|
+{
|
|
+ Relation rel;
|
|
+ SysScanDesc scan;
|
|
+ ScanKeyData skey;
|
|
+ HeapTuple tuple;
|
|
+ bool checked = false;
|
|
+
|
|
+ Assert(cmdType == CMD_INSERT || cmdType == CMD_UPDATE || cmdType == CMD_DELETE);
|
|
+
|
|
+ rel = heap_open(TriggerRelationId, AccessShareLock);
|
|
+ ScanKeyInit(&skey,
|
|
+ Anum_pg_trigger_tgrelid,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(relid));
|
|
+ scan = systable_beginscan(rel, TriggerRelidNameIndexId,
|
|
+ true, SnapshotNow, 1, &skey);
|
|
+ while (HeapTupleIsValid((tuple = systable_getnext(scan)))) {
|
|
+ Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
|
|
+
|
|
+ if (!trigForm->tgenabled)
|
|
+ continue;
|
|
+
|
|
+ if ((cmdType == CMD_INSERT && !TRIGGER_FOR_INSERT(trigForm->tgtype))
|
|
+ || (cmdType == CMD_UPDATE && !TRIGGER_FOR_UPDATE(trigForm->tgtype))
|
|
+ || (cmdType == CMD_DELETE && !TRIGGER_FOR_DELETE(trigForm->tgtype)))
|
|
+ continue;
|
|
+
|
|
+ /* per STATEMENT trigger cannot refer whole of a tuple */
|
|
+ if (!TRIGGER_FOR_ROW(trigForm->tgtype))
|
|
+ continue;
|
|
+
|
|
+ /* BEFORE-ROW-INSERT trigger cannot refer whole of a tuple */
|
|
+ if (TRIGGER_FOR_BEFORE(trigForm->tgtype) && TRIGGER_FOR_INSERT(trigForm->tgtype))
|
|
+ continue;
|
|
+
|
|
+ selist = addEvalPgProc(selist, trigForm->tgfoid, DB_PROCEDURE__EXECUTE);
|
|
+ if (!checked) {
|
|
+ HeapTuple reltup;
|
|
+ Form_pg_class classForm;
|
|
+ AttrNumber attnum;
|
|
+
|
|
+ reltup = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(relid),
|
|
+ 0, 0, 0);
|
|
+ classForm = (Form_pg_class) GETSTRUCT(reltup);
|
|
+
|
|
+ selist = __addEvalPgClass(selist, relid, false, DB_TABLE__SELECT);
|
|
+ for (attnum = FirstLowInvalidHeapAttributeNumber + 1; attnum <= 0; attnum++) {
|
|
+ if (attnum == ObjectIdAttributeNumber && !classForm->relhasoids)
|
|
+ continue;
|
|
+ selist = __addEvalPgAttribute(selist, relid, false, attnum, DB_COLUMN__SELECT);
|
|
+ }
|
|
+ ReleaseSysCache(reltup);
|
|
+
|
|
+ checked = true;
|
|
+ }
|
|
+ }
|
|
+ systable_endscan(scan);
|
|
+ heap_close(rel, AccessShareLock);
|
|
+
|
|
+ if (is_inh) {
|
|
+ List *child_list = find_inheritance_children(relid);
|
|
+ ListCell *l;
|
|
+
|
|
+ foreach(l, child_list)
|
|
+ selist = addEvalTriggerAccess(selist, lfirst_oid(l), is_inh, cmdType);
|
|
+ }
|
|
+
|
|
+ return selist;
|
|
+}
|
|
+
|
|
+/* *******************************************************************************
|
|
+ * walkExpr() -- walk on expression tree recursively to pick up and to construct
|
|
+ * a SEvalItem list related to expression node.
|
|
+ * It is evaluated at later phase.
|
|
+ * *******************************************************************************/
|
|
+static List *walkVarHelper(List *selist, queryChain *qc, Var *var, int flags)
|
|
+{
|
|
+ RangeTblEntry *rte;
|
|
+ Query *query;
|
|
+ Node *n;
|
|
+
|
|
+ Assert(IsA(var, Var));
|
|
+ if (!qc)
|
|
+ selerror("we could not use Var node in parameter list");
|
|
+
|
|
+ qc = upperQueryChain(qc, var->varlevelsup);
|
|
+ query = getQueryFromChain(qc);
|
|
+ rte = list_nth(query->rtable, var->varno - 1);
|
|
+ Assert(IsA(rte, RangeTblEntry));
|
|
+
|
|
+ switch (rte->rtekind) {
|
|
+ case RTE_RELATION:
|
|
+ /* table:{select} */
|
|
+ selist = addEvalPgClass(selist, rte,
|
|
+ (flags & WKFLAG_INTERNAL_USE)
|
|
+ ? DB_TABLE__USE : DB_TABLE__SELECT);
|
|
+ /* column:{select} */
|
|
+ selist = addEvalPgAttribute(selist, rte, var->varattno,
|
|
+ (flags & WKFLAG_INTERNAL_USE)
|
|
+ ? DB_COLUMN__USE : DB_COLUMN__SELECT);
|
|
+ break;
|
|
+ case RTE_JOIN:
|
|
+ n = list_nth(rte->joinaliasvars, var->varattno - 1);
|
|
+ selist = sepgsqlWalkExpr(selist, qc, n, flags);
|
|
+ break;
|
|
+ case RTE_SUBQUERY:
|
|
+ /* In normal cases, rte->relid equals zero for subquery.
|
|
+ * If rte->relid has none-zero value, it's rewritten subquery
|
|
+ * for outer join handling.
|
|
+ */
|
|
+ if (rte->relid) {
|
|
+ Query *sqry = rte->subquery;
|
|
+ RangeTblEntry *srte;
|
|
+ TargetEntry *tle;
|
|
+ Var *svar;
|
|
+
|
|
+ Assert(sqry->commandType == CMD_SELECT);
|
|
+ Assert(list_length(sqry->rtable) == 1);
|
|
+
|
|
+ srte = (RangeTblEntry *) list_nth(sqry->rtable, 0);
|
|
+ Assert(srte->rtekind == RTE_RELATION);
|
|
+ Assert(srte->relid == rte->relid);
|
|
+
|
|
+ if (var->varattno < 1) {
|
|
+ ListCell *l;
|
|
+ bool found = false;
|
|
+
|
|
+ foreach(l, sqry->targetList) {
|
|
+ TargetEntry *tle = lfirst(l);
|
|
+
|
|
+ Assert(IsA(tle, TargetEntry));
|
|
+ if (IsA(tle->expr, Const))
|
|
+ continue;
|
|
+
|
|
+ svar = (Var *) tle->expr;
|
|
+ Assert(IsA(svar, Var));
|
|
+ if (svar->varattno == var->varattno) {
|
|
+ var->varattno = tle->resno;
|
|
+ found = true;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (!found) {
|
|
+ AttrNumber resno = list_length(sqry->targetList) + 1;
|
|
+ svar = makeVar(1,
|
|
+ var->varattno,
|
|
+ var->vartype,
|
|
+ var->vartypmod,
|
|
+ 0);
|
|
+ tle = makeTargetEntry((Expr *) svar, resno, NULL, false);
|
|
+ var->varattno = resno;
|
|
+ sqry->targetList = lappend(sqry->targetList, tle);
|
|
+ }
|
|
+ } else {
|
|
+ tle = list_nth(sqry->targetList, var->varattno - 1);
|
|
+ Assert(IsA(tle, TargetEntry));
|
|
+ if (!IsA(tle->expr, Var))
|
|
+ selerror("dropped column is accessed (relid=%u, attno=%d)",
|
|
+ rte->relid, var->varattno);
|
|
+ svar = (Var *) tle->expr;
|
|
+ }
|
|
+ /* table:{select} or [use} */
|
|
+ selist = addEvalPgClass(selist, srte,
|
|
+ (flags & WKFLAG_INTERNAL_USE)
|
|
+ ? DB_TABLE__USE : DB_TABLE__SELECT);
|
|
+ /* column:{select} or {use}*/
|
|
+ selist = addEvalPgAttribute(selist, srte, svar->varattno,
|
|
+ (flags & WKFLAG_INTERNAL_USE)
|
|
+ ? DB_COLUMN__USE : DB_COLUMN__SELECT);
|
|
+ }
|
|
+ break;
|
|
+ case RTE_SPECIAL:
|
|
+ case RTE_FUNCTION:
|
|
+ case RTE_VALUES:
|
|
+ break;
|
|
+ default:
|
|
+ selerror("unrecognized rtekind (%d)", rte->rtekind);
|
|
+ break;
|
|
+ }
|
|
+ return selist;
|
|
+}
|
|
+
|
|
+static List *walkOpExprHelper(List *selist, Oid opid)
|
|
+{
|
|
+ HeapTuple tuple;
|
|
+ Form_pg_operator oprform;
|
|
+
|
|
+ tuple = SearchSysCache(OPEROID,
|
|
+ ObjectIdGetDatum(opid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ selerror("cache lookup failed for OPEROID = %u", opid);
|
|
+ oprform = (Form_pg_operator) GETSTRUCT(tuple);
|
|
+
|
|
+ selist = addEvalPgProc(selist, oprform->oprcode, DB_PROCEDURE__EXECUTE);
|
|
+ /* NOTE: opr->oprrest and opr->oprjoin are internal use only
|
|
+ * and have no effect onto the data references, so we don't
|
|
+ * apply any checkings for them.
|
|
+ */
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ return selist;
|
|
+}
|
|
+
|
|
+static List *sepgsqlWalkExpr(List *selist, queryChain *qc, Node *node, int flags)
|
|
+{
|
|
+ if (node == NULL)
|
|
+ return selist;
|
|
+
|
|
+ switch (nodeTag(node)) {
|
|
+ case T_Const:
|
|
+ case T_Param:
|
|
+ case T_CaseTestExpr:
|
|
+ case T_CoerceToDomainValue:
|
|
+ case T_SetToDefault:
|
|
+ case T_CurrentOfExpr:
|
|
+ /* do nothing */
|
|
+ break;
|
|
+ case T_List: {
|
|
+ ListCell *l;
|
|
+
|
|
+ foreach (l, (List *) node)
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) lfirst(l), flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_Var: {
|
|
+ selist = walkVarHelper(selist, qc, (Var *) node, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_FuncExpr: {
|
|
+ FuncExpr *func = (FuncExpr *) node;
|
|
+
|
|
+ selist = addEvalPgProc(selist, func->funcid, DB_PROCEDURE__EXECUTE);
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) func->args, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_Aggref: {
|
|
+ Aggref *aggref = (Aggref *) node;
|
|
+
|
|
+ selist = addEvalPgProc(selist, aggref->aggfnoid, DB_PROCEDURE__EXECUTE);
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) aggref->args, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_OpExpr:
|
|
+ case T_DistinctExpr: /* typedef of OpExpr */
|
|
+ case T_NullIfExpr: /* typedef of OpExpr */
|
|
+ {
|
|
+ OpExpr *op = (OpExpr *) node;
|
|
+
|
|
+ selist = walkOpExprHelper(selist, op->opno);
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) op->args, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_ScalarArrayOpExpr: {
|
|
+ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node;
|
|
+
|
|
+ selist = walkOpExprHelper(selist, saop->opno);
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) saop->args, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_BoolExpr: {
|
|
+ selist = sepgsqlWalkExpr(selist, qc,
|
|
+ (Node *) ((BoolExpr *) node)->args, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_ArrayRef: {
|
|
+ ArrayRef *aref = (ArrayRef *) node;
|
|
+
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) aref->refupperindexpr, flags);
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) aref->reflowerindexpr, flags);
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) aref->refexpr, flags);
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) aref->refassgnexpr, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_SubLink: {
|
|
+ SubLink *slink = (SubLink *) node;
|
|
+
|
|
+ Assert(IsA(slink->subselect, Query));
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) slink->testexpr, flags);
|
|
+ selist = proxyRteSubQuery(selist, qc, (Query *) slink->subselect);
|
|
+ break;
|
|
+ }
|
|
+ case T_SortClause:
|
|
+ case T_GroupClause: /* typedef of SortClause */
|
|
+ {
|
|
+ SortClause *sort = (SortClause *) node;
|
|
+ Query *query = getQueryFromChain(qc);
|
|
+ ListCell *l;
|
|
+
|
|
+ foreach (l, query->targetList) {
|
|
+ TargetEntry *tle = (TargetEntry *) lfirst(l);
|
|
+ Assert(IsA(tle, TargetEntry));
|
|
+ if (tle->ressortgroupref == sort->tleSortGroupRef) {
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) tle->expr, flags);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ case T_CoerceToDomain: {
|
|
+ selist = sepgsqlWalkExpr(selist, qc,
|
|
+ (Node *) ((CoerceToDomain *) node)->arg, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_CaseExpr: {
|
|
+ CaseExpr *ce = (CaseExpr *) node;
|
|
+
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) ce->arg, flags);
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) ce->args, flags);
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) ce->defresult, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_CaseWhen: {
|
|
+ CaseWhen *casewhen = (CaseWhen *) node;
|
|
+
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) casewhen->expr, flags);
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) casewhen->result, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_RelabelType: {
|
|
+ selist = sepgsqlWalkExpr(selist, qc,
|
|
+ (Node *) ((RelabelType *) node)->arg, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_CoerceViaIO: {
|
|
+ selist = sepgsqlWalkExpr(selist, qc,
|
|
+ (Node *) ((CoerceViaIO *) node)->arg, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_ArrayCoerceExpr: {
|
|
+ ArrayCoerceExpr *ace = (ArrayCoerceExpr *) node;
|
|
+
|
|
+ if (ace->elemfuncid != InvalidOid)
|
|
+ selist = addEvalPgProc(selist, ace->elemfuncid, DB_PROCEDURE__EXECUTE);
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) ace->arg, flags);
|
|
+
|
|
+ break;
|
|
+ }
|
|
+ case T_CoalesceExpr: {
|
|
+ selist = sepgsqlWalkExpr(selist, qc,
|
|
+ (Node *) ((CoalesceExpr *) node)->args, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_MinMaxExpr: {
|
|
+ selist = sepgsqlWalkExpr(selist, qc,
|
|
+ (Node *) ((MinMaxExpr *) node)->args, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_XmlExpr: {
|
|
+ XmlExpr *xe = (XmlExpr *) node;
|
|
+
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) xe->named_args, flags);
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) xe->args, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_NullTest: {
|
|
+ selist = sepgsqlWalkExpr(selist, qc,
|
|
+ (Node *) ((NullTest *) node)->arg, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_BooleanTest: {
|
|
+ selist = sepgsqlWalkExpr(selist, qc,
|
|
+ (Node *) ((BooleanTest *) node)->arg, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_FieldSelect: {
|
|
+ selist = sepgsqlWalkExpr(selist, qc,
|
|
+ (Node *) ((FieldSelect *) node)->arg, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_FieldStore: {
|
|
+ FieldStore *fstore = (FieldStore *) node;
|
|
+
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) fstore->arg, flags);
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) fstore->newvals, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_ArrayExpr: {
|
|
+ selist = sepgsqlWalkExpr(selist, qc,
|
|
+ (Node *) ((ArrayExpr *) node)->elements, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_RowExpr: {
|
|
+ selist = sepgsqlWalkExpr(selist, qc,
|
|
+ (Node *) ((RowExpr *) node)->args, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_RowCompareExpr: {
|
|
+ RowCompareExpr *rce = (RowCompareExpr *) node;
|
|
+ ListCell *l;
|
|
+
|
|
+ foreach (l, rce->opnos)
|
|
+ selist = walkOpExprHelper(selist, lfirst_oid(l));
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) rce->largs, flags);
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) rce->rargs, flags);
|
|
+ break;
|
|
+ }
|
|
+ case T_ConvertRowtypeExpr: {
|
|
+ selist = sepgsqlWalkExpr(selist, qc,
|
|
+ (Node *) ((ConvertRowtypeExpr *) node)->arg, flags);
|
|
+ break;
|
|
+ }
|
|
+ default:
|
|
+ selnotice("node(%d) is ignored => %s", nodeTag(node), nodeToString(node));
|
|
+ break;
|
|
+ }
|
|
+ return selist;
|
|
+}
|
|
+
|
|
+/* *******************************************************************************
|
|
+ * proxyRteXXXX() -- check any relation type objects in the required query,
|
|
+ * including general relation, outer|inner|cross join and subquery.
|
|
+ *
|
|
+ * sepgsqlProxyQuery() is called just after query rewriting phase to constract
|
|
+ * a list of SEvalItems. It is attached into Query->pgaceList and evaluated by
|
|
+ * sepgsqlVerifyQuery() at later phase.
|
|
+ * *******************************************************************************/
|
|
+
|
|
+static Oid fnoid_sepgsql_tuple_perm = F_SEPGSQL_TUPLE_PERMS;
|
|
+
|
|
+/*
|
|
+ * When we use LEFT OUTER JOIN, any condition defined at ON clause are not
|
|
+ * considered to filter tuples, so left-hand relation have to be re-written
|
|
+ * as a subquery to filter violated tuples.
|
|
+ */
|
|
+static List *makePseudoTargetList(Oid relid) {
|
|
+ HeapTuple reltup, atttup;
|
|
+ Form_pg_class classForm;
|
|
+ Form_pg_attribute attrForm;
|
|
+ AttrNumber attno, relnatts;
|
|
+ TargetEntry *tle;
|
|
+ Expr *expr;
|
|
+ List *targetList = NIL;
|
|
+
|
|
+ reltup = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(relid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(reltup))
|
|
+ selerror("relation (oid: %u) does not exist", relid);
|
|
+
|
|
+ classForm = (Form_pg_class) GETSTRUCT(reltup);
|
|
+ relnatts = classForm->relnatts;
|
|
+ for (attno = 1; attno <= relnatts; attno++) {
|
|
+ atttup = SearchSysCache(ATTNUM,
|
|
+ ObjectIdGetDatum(relid),
|
|
+ Int16GetDatum(attno),
|
|
+ 0, 0);
|
|
+ if (!HeapTupleIsValid(atttup))
|
|
+ selerror("attribute %u of relation '%s' does not exist",
|
|
+ attno, NameStr(classForm->relname));
|
|
+ attrForm = (Form_pg_attribute) GETSTRUCT(atttup);
|
|
+ if (attrForm->attisdropped) {
|
|
+ expr = (Expr *) makeNullConst(INT4OID, -1);
|
|
+ } else {
|
|
+ expr = (Expr *) makeVar(1,
|
|
+ attno,
|
|
+ attrForm->atttypid,
|
|
+ attrForm->atttypmod,
|
|
+ 0);
|
|
+ }
|
|
+ tle = makeTargetEntry(expr, attno, NULL, false);
|
|
+ targetList = lappend(targetList, tle);
|
|
+ ReleaseSysCache(atttup);
|
|
+
|
|
+ Assert(list_length(targetList) == attno);
|
|
+ }
|
|
+ ReleaseSysCache(reltup);
|
|
+
|
|
+ return targetList;
|
|
+}
|
|
+
|
|
+static void rewriteOuterJoinTree(Node *n, Query *query, bool is_outer_join)
|
|
+{
|
|
+ RangeTblRef *rtr, *srtr;
|
|
+ RangeTblEntry *rte, *srte;
|
|
+ Query *sqry;
|
|
+ FromExpr *sfrm;
|
|
+
|
|
+ if (IsA(n, RangeTblRef)) {
|
|
+ if (!is_outer_join)
|
|
+ return;
|
|
+
|
|
+ rtr = (RangeTblRef *) n;
|
|
+ rte = list_nth(query->rtable, rtr->rtindex - 1);
|
|
+ Assert(IsA(rte, RangeTblEntry));
|
|
+ if (rte->rtekind != RTE_RELATION)
|
|
+ return;
|
|
+
|
|
+ /* setup alternative query */
|
|
+ sqry = makeNode(Query);
|
|
+ sqry->commandType = CMD_SELECT;
|
|
+ sqry->targetList = makePseudoTargetList(rte->relid);
|
|
+
|
|
+ srte = copyObject(rte);
|
|
+ sqry->rtable = list_make1(srte);
|
|
+
|
|
+ srtr = makeNode(RangeTblRef);
|
|
+ srtr->rtindex = 1;
|
|
+
|
|
+ sfrm = makeNode(FromExpr);
|
|
+ sfrm->fromlist = list_make1(srtr);
|
|
+ sfrm->quals = NULL;
|
|
+
|
|
+ sqry->jointree = sfrm;
|
|
+ sqry->hasSubLinks = false;
|
|
+ sqry->hasAggs = false;
|
|
+
|
|
+ rte->rtekind = RTE_SUBQUERY;
|
|
+ rte->subquery = sqry;
|
|
+ } else if (IsA(n, FromExpr)) {
|
|
+ FromExpr *f = (FromExpr *)n;
|
|
+ ListCell *l;
|
|
+
|
|
+ foreach (l, f->fromlist)
|
|
+ rewriteOuterJoinTree(lfirst(l), query, false);
|
|
+ } else if (IsA(n, JoinExpr)) {
|
|
+ JoinExpr *j = (JoinExpr *) n;
|
|
+
|
|
+ rewriteOuterJoinTree(j->larg, query,
|
|
+ (j->jointype == JOIN_LEFT || j->jointype == JOIN_FULL));
|
|
+ rewriteOuterJoinTree(j->rarg, query,
|
|
+ (j->jointype == JOIN_RIGHT || j->jointype == JOIN_FULL));
|
|
+ } else {
|
|
+ selerror("unrecognized node type (%d) in query->jointree", nodeTag(n));
|
|
+ }
|
|
+}
|
|
+
|
|
+static List *proxyRteRelation(List *selist, queryChain *qc, int rtindex, Node **quals)
|
|
+{
|
|
+ Query *query;
|
|
+ RangeTblEntry *rte;
|
|
+ Relation rel;
|
|
+ TupleDesc tdesc;
|
|
+ uint32 perms = 0;
|
|
+
|
|
+ query = getQueryFromChain(qc);
|
|
+ rte = list_nth(query->rtable, rtindex - 1);
|
|
+ rel = relation_open(rte->relid, AccessShareLock);
|
|
+ tdesc = RelationGetDescr(rel);
|
|
+
|
|
+ /* setup tclass and access vector */
|
|
+ perms = 0;
|
|
+ if (rte->requiredPerms & RTEMARK_USE)
|
|
+ perms |= DB_TUPLE__USE;
|
|
+ if (rte->requiredPerms & RTEMARK_SELECT)
|
|
+ perms |= DB_TUPLE__SELECT;
|
|
+ if (rte->requiredPerms & RTEMARK_INSERT)
|
|
+ perms |= DB_TUPLE__INSERT;
|
|
+ if (rte->requiredPerms & RTEMARK_UPDATE)
|
|
+ perms |= DB_TUPLE__UPDATE;
|
|
+ if (rte->requiredPerms & RTEMARK_DELETE)
|
|
+ perms |= DB_TUPLE__DELETE;
|
|
+ if (rte->requiredPerms & RTEMARK_RELABELFROM)
|
|
+ perms |= DB_TUPLE__RELABELFROM;
|
|
+ if (rte->requiredPerms & RTEMARK_RELABELTO)
|
|
+ perms |= DB_TUPLE__RELABELTO;
|
|
+ if (rte->requiredPerms & RTEMARK_BLOB_READ)
|
|
+ perms |= DB_BLOB__READ;
|
|
+ if (rte->requiredPerms & RTEMARK_BLOB_WRITE)
|
|
+ perms |= DB_BLOB__WRITE;
|
|
+
|
|
+ /* append sepgsql_tuple_perm(relid, record, perms) */
|
|
+ if (perms) {
|
|
+ Var *v1, *v2, *v4;
|
|
+ Const *c3;
|
|
+ FuncExpr *func;
|
|
+
|
|
+ /* 1st arg : Oid of the target relation */
|
|
+ v1 = makeVar(rtindex, TableOidAttributeNumber, OIDOID, -1, 0);
|
|
+
|
|
+ /* 2nd arg : Security Attribute of tuple */
|
|
+ v2 = makeVar(rtindex, SecurityAttributeNumber, OIDOID, -1, 0);
|
|
+
|
|
+ /* 3rd arg : permission set */
|
|
+ c3 = makeConst(INT4OID, -1, sizeof(int32), Int32GetDatum(perms), false, true);
|
|
+
|
|
+ /* 4th arg : RECORD of the target relation */
|
|
+ v4 = makeVar(rtindex, 0, RelationGetForm(rel)->reltype, -1, 0);
|
|
+
|
|
+ /* append sepgsql_tuple_perm */
|
|
+ func = makeFuncExpr(fnoid_sepgsql_tuple_perm, BOOLOID,
|
|
+ list_make4(v1, v2, c3, v4), COERCE_DONTCARE);
|
|
+ if (*quals == NULL) {
|
|
+ *quals = (Node *) func;
|
|
+ } else {
|
|
+ *quals = (Node *) makeBoolExpr(AND_EXPR, list_make2(func, *quals));
|
|
+ }
|
|
+ }
|
|
+ relation_close(rel, NoLock);
|
|
+
|
|
+ return selist;
|
|
+}
|
|
+
|
|
+static List *proxyRteOuterJoin(List *selist, queryChain *qc, Query *query)
|
|
+{
|
|
+ queryChain qcData;
|
|
+ ListCell *l;
|
|
+
|
|
+ qcData.parent = qc;
|
|
+ qcData.tail = query;
|
|
+ qc = &qcData;
|
|
+
|
|
+ selist = proxyRteRelation(selist, qc, 1, &query->jointree->quals);
|
|
+
|
|
+ /* clean-up polluted RangeTblEntry */
|
|
+ foreach (l, query->rtable) {
|
|
+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
|
|
+ rte->requiredPerms &= ((1<<N_ACL_RIGHTS) - 1);
|
|
+ }
|
|
+
|
|
+ return selist;
|
|
+}
|
|
+
|
|
+static List *proxyRteSubQuery(List *selist, queryChain *qc, Query *query)
|
|
+{
|
|
+ CmdType cmdType = query->commandType;
|
|
+ RangeTblEntry *rte = NULL;
|
|
+ queryChain qcData;
|
|
+ ListCell *l;
|
|
+
|
|
+ /* query chain setup */
|
|
+ qcData.parent = qc;
|
|
+ qcData.tail = query;
|
|
+ qc = &qcData;
|
|
+
|
|
+ /* rewrite outer join */
|
|
+ rewriteOuterJoinTree((Node *) query->jointree, query, false);
|
|
+
|
|
+ if (cmdType != CMD_SELECT) {
|
|
+ rte = list_nth(query->rtable, query->resultRelation - 1);
|
|
+ Assert(IsA(rte, RangeTblEntry) && rte->rtekind==RTE_RELATION);
|
|
+ switch (cmdType) {
|
|
+ case CMD_INSERT:
|
|
+ selist = addEvalPgClass(selist, rte, DB_TABLE__INSERT);
|
|
+ break;
|
|
+ case CMD_UPDATE:
|
|
+ selist = addEvalPgClass(selist, rte, DB_TABLE__UPDATE);
|
|
+ break;
|
|
+ case CMD_DELETE:
|
|
+ selist = addEvalPgClass(selist, rte, DB_TABLE__DELETE);
|
|
+ break;
|
|
+ default:
|
|
+ selerror("commandType = %d should not be found here", cmdType);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* permission mark on the target columns */
|
|
+ if (cmdType != CMD_DELETE) {
|
|
+ foreach (l, query->targetList) {
|
|
+ TargetEntry *tle = lfirst(l);
|
|
+ Assert(IsA(tle, TargetEntry));
|
|
+
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) tle->expr,
|
|
+ tle->resjunk ? WKFLAG_INTERNAL_USE : 0);
|
|
+ /* mark insert/update target */
|
|
+ if (cmdType==CMD_UPDATE || cmdType==CMD_INSERT) {
|
|
+ uint32 perms = (cmdType == CMD_UPDATE
|
|
+ ? DB_COLUMN__UPDATE : DB_COLUMN__INSERT);
|
|
+ if (tle->resjunk) {
|
|
+ if (!strcmp(tle->resname, SECURITY_SYSATTR_NAME))
|
|
+ selist = addEvalPgAttribute(selist,
|
|
+ rte,
|
|
+ SecurityAttributeNumber,
|
|
+ perms);
|
|
+ continue;
|
|
+ }
|
|
+ selist = addEvalPgAttribute(selist, rte, tle->resno, perms);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* permission mark on RETURNING clause, if necessary */
|
|
+ foreach (l, query->returningList) {
|
|
+ TargetEntry *te = lfirst(l);
|
|
+ Assert(IsA(te, TargetEntry));
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) te->expr, 0);
|
|
+ }
|
|
+
|
|
+ /* permission mark on the WHERE/HAVING clause */
|
|
+ selist = sepgsqlWalkExpr(selist, qc, query->jointree->quals,
|
|
+ WKFLAG_INTERNAL_USE);
|
|
+ selist = sepgsqlWalkExpr(selist, qc, query->havingQual,
|
|
+ WKFLAG_INTERNAL_USE);
|
|
+
|
|
+ /* permission mark on the ORDER BY clause */
|
|
+ //selist = sepgsqlWalkExpr(selist, qc, (Node *) query->sortClause, WKFLAG_INTERNAL_USE);
|
|
+
|
|
+ /* permission mark on the GROUP BY/HAVING clause */
|
|
+ //selist = sepgsqlWalkExpr(selist, qc, (Node *) query->groupClause, WKFLAG_INTERNAL_USE);
|
|
+
|
|
+ /* permission mark on the UNION/INTERSECT/EXCEPT */
|
|
+ selist = proxySetOperations(selist, qc, query->setOperations);
|
|
+
|
|
+ /* append sepgsql_permission() on the FROM clause/USING clause
|
|
+ * for SELECT/UPDATE/DELETE statement.
|
|
+ * The target Relation of INSERT is noe necessary to append it
|
|
+ */
|
|
+ selist = proxyJoinTree(selist, qc, (Node *) query->jointree,
|
|
+ &query->jointree->quals);
|
|
+
|
|
+ /* clean-up polluted RangeTblEntry */
|
|
+ foreach (l, query->rtable) {
|
|
+ rte = (RangeTblEntry *) lfirst(l);
|
|
+ rte->requiredPerms &= ((1<<N_ACL_RIGHTS) - 1);
|
|
+ }
|
|
+
|
|
+ return selist;
|
|
+}
|
|
+
|
|
+static List *proxyJoinTree(List *selist, queryChain *qc, Node *n, Node **quals)
|
|
+{
|
|
+ Query *query = getQueryFromChain(qc);
|
|
+
|
|
+ if (n == NULL)
|
|
+ return selist;
|
|
+
|
|
+ if (IsA(n, RangeTblRef)) {
|
|
+ RangeTblRef *rtr = (RangeTblRef *) n;
|
|
+ RangeTblEntry *rte = list_nth(query->rtable, rtr->rtindex - 1);
|
|
+ Assert(IsA(rte, RangeTblEntry));
|
|
+
|
|
+ switch (rte->rtekind) {
|
|
+ case RTE_RELATION:
|
|
+ selist = proxyRteRelation(selist, qc, rtr->rtindex, quals);
|
|
+ break;
|
|
+ case RTE_SUBQUERY:
|
|
+ selist = (rte->relid
|
|
+ ? proxyRteOuterJoin(selist, qc, rte->subquery)
|
|
+ : proxyRteSubQuery(selist, qc, rte->subquery));
|
|
+ break;
|
|
+ case RTE_FUNCTION: {
|
|
+ FuncExpr *f = (FuncExpr *) rte->funcexpr;
|
|
+
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) f, 0);
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) f->args, 0);
|
|
+ break;
|
|
+ }
|
|
+ case RTE_VALUES:
|
|
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) rte->values_lists, 0);
|
|
+ break;
|
|
+ default:
|
|
+ selerror("rtekind = %d should not be found fromList", rte->rtekind);
|
|
+ break;
|
|
+ }
|
|
+ } else if (IsA(n, FromExpr)) {
|
|
+ FromExpr *f = (FromExpr *)n;
|
|
+ ListCell *l;
|
|
+
|
|
+ selist = sepgsqlWalkExpr(selist, qc, f->quals, WKFLAG_INTERNAL_USE);
|
|
+ foreach (l, f->fromlist)
|
|
+ selist = proxyJoinTree(selist, qc, lfirst(l), quals);
|
|
+ } else if (IsA(n, JoinExpr)) {
|
|
+ JoinExpr *j = (JoinExpr *) n;
|
|
+
|
|
+ selist = sepgsqlWalkExpr(selist, qc, j->quals, WKFLAG_INTERNAL_USE);
|
|
+ selist = proxyJoinTree(selist, qc, j->larg, &j->quals);
|
|
+ selist = proxyJoinTree(selist, qc, j->rarg, &j->quals);
|
|
+ } else {
|
|
+ selerror("unrecognized node type (%d) in query->jointree", nodeTag(n));
|
|
+ }
|
|
+ return selist;
|
|
+}
|
|
+
|
|
+static List *proxySetOperations(List *selist, queryChain *qc, Node *n)
|
|
+{
|
|
+ Query *query = getQueryFromChain(qc);
|
|
+
|
|
+ if (n == NULL)
|
|
+ return selist;
|
|
+
|
|
+ if (IsA(n, RangeTblRef)) {
|
|
+ RangeTblRef *rtr = (RangeTblRef *) n;
|
|
+ RangeTblEntry *rte = list_nth(query->rtable, rtr->rtindex - 1);
|
|
+
|
|
+ Assert(IsA(rte, RangeTblEntry) && rte->rtekind == RTE_SUBQUERY);
|
|
+
|
|
+ selist = proxyRteSubQuery(selist, qc, rte->subquery);
|
|
+ } else if (IsA(n, SetOperationStmt)) {
|
|
+ SetOperationStmt *op = (SetOperationStmt *) n;
|
|
+
|
|
+ selist = proxySetOperations(selist, qc, (Node *) op->larg);
|
|
+ selist = proxySetOperations(selist, qc, (Node *) op->rarg);
|
|
+ } else {
|
|
+ selerror("setOperationsTree contains => %s", nodeToString(n));
|
|
+ }
|
|
+
|
|
+ return selist;
|
|
+}
|
|
+
|
|
+static List *proxyGeneralQuery(Query *query)
|
|
+{
|
|
+ List *selist = NIL;
|
|
+
|
|
+ selist = proxyRteSubQuery(selist, NULL, query);
|
|
+ query->pgaceItem = (Node *) selist;
|
|
+
|
|
+ return list_make1(query);
|
|
+}
|
|
+
|
|
+static List *proxyExecuteStmt(Query *query)
|
|
+{
|
|
+ List *selist = NIL;
|
|
+ ExecuteStmt *estmt = (ExecuteStmt *) query->utilityStmt;
|
|
+ queryChain qcData;
|
|
+
|
|
+ Assert(nodeTag(query->utilityStmt) == T_ExecuteStmt);
|
|
+
|
|
+ qcData.parent = NULL;
|
|
+ qcData.tail = query;
|
|
+ selist = sepgsqlWalkExpr(selist, &qcData, (Node *) estmt->params, 0);
|
|
+ query->pgaceItem = (Node *) selist;
|
|
+
|
|
+ return list_make1(query);
|
|
+}
|
|
+
|
|
+static Query *convertTruncateToDelete(Relation rel)
|
|
+{
|
|
+ Query *query = makeNode(Query);
|
|
+ RangeTblEntry *rte;
|
|
+ RangeTblRef *rtr;
|
|
+
|
|
+ rte = addRangeTableEntryForRelation(NULL, rel, NULL, false, false);
|
|
+ rte->requiredPerms = ACL_DELETE;
|
|
+ rtr = makeNode(RangeTblRef);
|
|
+ rtr->rtindex = 1;
|
|
+
|
|
+ query->commandType = CMD_DELETE;
|
|
+ query->rtable = list_make1(rte);
|
|
+ query->jointree = makeNode(FromExpr);
|
|
+ query->jointree->fromlist = list_make1(rtr);
|
|
+ query->jointree->quals = NULL;
|
|
+ query->resultRelation = rtr->rtindex;
|
|
+ query->hasSubLinks = false;
|
|
+ query->hasAggs = false;
|
|
+
|
|
+ sepgsqlProxyQuery(query);
|
|
+
|
|
+ return query;
|
|
+}
|
|
+
|
|
+static List *proxyTruncateStmt(Query *query)
|
|
+{
|
|
+ TruncateStmt *stmt = (TruncateStmt *) query->utilityStmt;
|
|
+ Relation rel;
|
|
+ Query *subqry;
|
|
+ ListCell *l;
|
|
+ List *subquery_list = NIL, *subquery_lids = NIL;
|
|
+
|
|
+ /* resolve the relation names */
|
|
+ foreach (l, stmt->relations) {
|
|
+ RangeVar *rv = lfirst(l);
|
|
+
|
|
+ rel = heap_openrv(rv, AccessShareLock);
|
|
+ subqry = convertTruncateToDelete(rel);
|
|
+ subquery_list = lappend(subquery_list, subqry);
|
|
+ subquery_lids = lappend_oid(subquery_lids, RelationGetRelid(rel));
|
|
+ heap_close(rel, NoLock);
|
|
+
|
|
+ selnotice("virtual TRUNCATE %s", RelationGetRelationName(rel));
|
|
+ }
|
|
+
|
|
+ if (stmt->behavior == DROP_CASCADE) {
|
|
+ subquery_lids = heap_truncate_find_FKs(subquery_lids);
|
|
+ foreach (l, subquery_lids) {
|
|
+ Oid relid = lfirst_oid(l);
|
|
+
|
|
+ rel = heap_open(relid, AccessShareLock);
|
|
+ subqry = convertTruncateToDelete(rel);
|
|
+ subquery_list = lappend(subquery_list, subqry);
|
|
+ heap_close(rel, NoLock);
|
|
+ }
|
|
+ }
|
|
+ return subquery_list;
|
|
+}
|
|
+
|
|
+List *sepgsqlProxyQuery(Query *query)
|
|
+{
|
|
+ List *new_list = NIL;
|
|
+
|
|
+ switch (query->commandType) {
|
|
+ case CMD_SELECT:
|
|
+ case CMD_UPDATE:
|
|
+ case CMD_INSERT:
|
|
+ case CMD_DELETE:
|
|
+ new_list = proxyGeneralQuery(query);
|
|
+ break;
|
|
+ case CMD_UTILITY:
|
|
+ switch (nodeTag(query->utilityStmt)) {
|
|
+ case T_TruncateStmt:
|
|
+ new_list = proxyTruncateStmt(query);
|
|
+ break;
|
|
+ case T_ExecuteStmt:
|
|
+ new_list = proxyExecuteStmt(query);
|
|
+ break;
|
|
+ default:
|
|
+ new_list = list_make1(query);
|
|
+ /* do nothing now */
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ selerror("unknown command type (=%d) found",
|
|
+ query->commandType);
|
|
+ break;
|
|
+ }
|
|
+ return new_list;
|
|
+}
|
|
+
|
|
+/* *******************************************************************************
|
|
+ * verifyXXXX() -- checks any SEvalItem attached with Query->pgaceList.
|
|
+ * Those are generated in proxyXXXX() phase, and this evaluation is done
|
|
+ * just before PortalStart().
|
|
+ * The reason why the checks are delayed is to handle cases when parse
|
|
+ * and execute are separated like PREPARE/EXECUTE statement.
|
|
+ * *******************************************************************************/
|
|
+static void verifyPgClassPerms(Oid relid, bool inh, uint32 perms)
|
|
+{
|
|
+ Form_pg_class pgclass;
|
|
+ HeapTuple tuple;
|
|
+
|
|
+ /* check untouchable tables */
|
|
+ if (perms & (DB_TABLE__UPDATE | DB_TABLE__INSERT | DB_TABLE__DELETE)) {
|
|
+ if (relid == SecurityRelationId)
|
|
+ selerror("user cannot modify pg_security directly, for security reason");
|
|
+ }
|
|
+
|
|
+ /* check table:{required permissions} */
|
|
+ tuple = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(relid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ selerror("RELOID cache lookup failed (relid=%u)", relid);
|
|
+ pgclass = (Form_pg_class) GETSTRUCT(tuple);
|
|
+
|
|
+ if (pgclass->relkind != RELKIND_RELATION) {
|
|
+ //selnotice("%s is not a general relation", NameStr(pgclass->relname));
|
|
+ ReleaseSysCache(tuple);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ HeapTupleGetSecurity(tuple),
|
|
+ SECCLASS_DB_TABLE,
|
|
+ perms,
|
|
+ sepgsqlGetTupleName(RelationRelationId, tuple));
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+static void verifyPgAttributePerms(Oid relid, bool inh, AttrNumber attno, uint32 perms)
|
|
+{
|
|
+ HeapTuple tuple;
|
|
+ Form_pg_class classForm;
|
|
+ Form_pg_attribute attrForm;
|
|
+
|
|
+ tuple = SearchSysCache(RELOID,
|
|
+ ObjectIdGetDatum(relid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ selerror("RELOID cache lookup failed (relid=%u)", relid);
|
|
+ classForm = (Form_pg_class) GETSTRUCT(tuple);
|
|
+ if (classForm->relkind != RELKIND_RELATION) {
|
|
+ /* column:{ xxx } checks are applied only column within tables */
|
|
+ ReleaseSysCache(tuple);
|
|
+ return;
|
|
+ }
|
|
+ ReleaseSysCache(tuple);
|
|
+
|
|
+ /* 2. verify column perms */
|
|
+ if (attno == 0) {
|
|
+ /* RECORD type permission check */
|
|
+ Relation rel;
|
|
+ ScanKeyData skey;
|
|
+ SysScanDesc scan;
|
|
+
|
|
+ ScanKeyInit(&skey,
|
|
+ Anum_pg_attribute_attrelid,
|
|
+ BTEqualStrategyNumber, F_OIDEQ,
|
|
+ ObjectIdGetDatum(relid));
|
|
+
|
|
+ rel = heap_open(AttributeRelationId, AccessShareLock);
|
|
+ scan = systable_beginscan(rel, AttributeRelidNumIndexId,
|
|
+ true, SnapshotNow, 1, &skey);
|
|
+ while ((tuple = systable_getnext(scan)) != NULL) {
|
|
+ attrForm = (Form_pg_attribute) GETSTRUCT(tuple);
|
|
+ if (attrForm->attisdropped || attrForm->attnum < 1)
|
|
+ continue;
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ HeapTupleGetSecurity(tuple),
|
|
+ SECCLASS_DB_COLUMN,
|
|
+ perms,
|
|
+ sepgsqlGetTupleName(AttributeRelationId, tuple));
|
|
+ }
|
|
+ systable_endscan(scan);
|
|
+ heap_close(rel, AccessShareLock);
|
|
+
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ tuple = SearchSysCache(ATTNUM,
|
|
+ ObjectIdGetDatum(relid),
|
|
+ Int16GetDatum(attno),
|
|
+ 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ selerror("ATTNUM cache lookup failed (relid=%u, attno=%d)", relid, attno);
|
|
+
|
|
+ /* check column:{required permissions} */
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ HeapTupleGetSecurity(tuple),
|
|
+ SECCLASS_DB_COLUMN,
|
|
+ perms,
|
|
+ sepgsqlGetTupleName(AttributeRelationId, tuple));
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+static void verifyPgProcPerms(Oid funcid, uint32 perms)
|
|
+{
|
|
+ HeapTuple tuple;
|
|
+ Oid newcon;
|
|
+
|
|
+ tuple = SearchSysCache(PROCOID,
|
|
+ ObjectIdGetDatum(funcid),
|
|
+ 0, 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ selerror("cache lookup failed for procedure %d", funcid);
|
|
+
|
|
+ /* compute domain transition */
|
|
+ newcon = sepgsql_avc_createcon(sepgsqlGetClientContext(),
|
|
+ HeapTupleGetSecurity(tuple),
|
|
+ SECCLASS_PROCESS);
|
|
+ if (newcon != sepgsqlGetClientContext())
|
|
+ perms |= DB_PROCEDURE__ENTRYPOINT;
|
|
+
|
|
+ /* check procedure executiong permission */
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ HeapTupleGetSecurity(tuple),
|
|
+ SECCLASS_DB_PROCEDURE,
|
|
+ perms,
|
|
+ sepgsqlGetTupleName(ProcedureRelationId, tuple));
|
|
+
|
|
+ /* check domain transition, if necessary */
|
|
+ if (newcon != sepgsqlGetClientContext()) {
|
|
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
|
|
+ newcon,
|
|
+ SECCLASS_PROCESS,
|
|
+ PROCESS__TRANSITION,
|
|
+ NULL);
|
|
+ }
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+}
|
|
+
|
|
+static List *__expandPgClassInheritance(List *selist, Oid relid, uint32 perms)
|
|
+{
|
|
+ List *child_list = find_inheritance_children(relid);
|
|
+ ListCell *l;
|
|
+
|
|
+ foreach (l, child_list) {
|
|
+ selist = __addEvalPgClass(selist, lfirst_oid(l), false, perms);
|
|
+ selist = __expandPgClassInheritance(selist, lfirst_oid(l), perms);
|
|
+ }
|
|
+ return selist;
|
|
+}
|
|
+
|
|
+static List *__expandPgAttributeInheritance(List *selist, Oid relid, char *attname, uint32 perms)
|
|
+{
|
|
+ List *child_list = find_inheritance_children(relid);
|
|
+ ListCell *l;
|
|
+
|
|
+ foreach (l, child_list) {
|
|
+ Form_pg_attribute attrForm;
|
|
+ HeapTuple tuple;
|
|
+
|
|
+ if (!attname) {
|
|
+ /* attname == NULL means RECORD reference */
|
|
+ selist = __addEvalPgAttribute(selist, lfirst_oid(l), false, 0, perms);
|
|
+ selist = __expandPgAttributeInheritance(selist, lfirst_oid(l), NULL, perms);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ tuple = SearchSysCacheAttName(lfirst_oid(l), attname);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ selerror("relation %u does not have attribute %s",
|
|
+ lfirst_oid(l), attname);
|
|
+ attrForm = (Form_pg_attribute) GETSTRUCT(tuple);
|
|
+ selist = __addEvalPgAttribute(selist, lfirst_oid(l), false, attrForm->attnum, perms);
|
|
+ selist = __expandPgAttributeInheritance(selist, lfirst_oid(l), attname, perms);
|
|
+
|
|
+ ReleaseSysCache(tuple);
|
|
+ }
|
|
+
|
|
+ return selist;
|
|
+}
|
|
+
|
|
+static List *expandSEvalListInheritance(List *selist) {
|
|
+ List *result = NIL;
|
|
+ ListCell *l;
|
|
+
|
|
+ foreach (l, selist) {
|
|
+ SEvalItem *se = (SEvalItem *) lfirst(l);
|
|
+
|
|
+ result = lappend(result, se);
|
|
+ switch (se->tclass) {
|
|
+ case SECCLASS_DB_TABLE:
|
|
+ if (se->c.inh) {
|
|
+ se->c.inh = false;
|
|
+ result = __expandPgClassInheritance(result,
|
|
+ se->c.relid,
|
|
+ se->perms);
|
|
+ }
|
|
+ break;
|
|
+ case SECCLASS_DB_COLUMN:
|
|
+ if (se->a.inh) {
|
|
+ Form_pg_attribute attrForm;
|
|
+ HeapTuple tuple;
|
|
+
|
|
+ se->a.inh = false;
|
|
+ if (se->a.attno == 0) {
|
|
+ result = __expandPgAttributeInheritance(result,
|
|
+ se->a.relid,
|
|
+ NULL,
|
|
+ se->perms);
|
|
+ break;
|
|
+ }
|
|
+ tuple = SearchSysCache(ATTNUM,
|
|
+ ObjectIdGetDatum(se->a.relid),
|
|
+ Int16GetDatum(se->a.attno),
|
|
+ 0, 0);
|
|
+ if (!HeapTupleIsValid(tuple))
|
|
+ selerror("relation %u attribute %d not found", se->a.relid, se->a.attno);
|
|
+ attrForm = (Form_pg_attribute) GETSTRUCT(tuple);
|
|
+
|
|
+ result = __expandPgAttributeInheritance(result,
|
|
+ se->a.relid,
|
|
+ NameStr(attrForm->attname),
|
|
+ se->perms);
|
|
+ ReleaseSysCache(tuple);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return result;
|
|
+}
|
|
+
|
|
+static void execVerifyQuery(List *selist)
|
|
+{
|
|
+ ListCell *l;
|
|
+
|
|
+ foreach (l, selist) {
|
|
+ SEvalItem *se = lfirst(l);
|
|
+
|
|
+ switch (se->tclass) {
|
|
+ case SECCLASS_DB_TABLE:
|
|
+ verifyPgClassPerms(se->c.relid, se->c.inh, se->perms);
|
|
+ break;
|
|
+ case SECCLASS_DB_COLUMN:
|
|
+ verifyPgAttributePerms(se->a.relid, se->a.inh, se->a.attno, se->perms);
|
|
+ break;
|
|
+ case SECCLASS_DB_PROCEDURE:
|
|
+ verifyPgProcPerms(se->p.funcid, se->perms);
|
|
+ break;
|
|
+ default:
|
|
+ selerror("unknown SEvalItem (tclass=%u)", se->tclass);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+void sepgsqlVerifyQuery(PlannedStmt *pstmt)
|
|
+{
|
|
+ RangeTblEntry *rte;
|
|
+ List *selist;
|
|
+ ListCell *l;
|
|
+
|
|
+ if (!pstmt->pgaceItem)
|
|
+ return;
|
|
+ Assert(IsA(pstmt->pgaceItem, List));
|
|
+ selist = (List *) pstmt->pgaceItem;
|
|
+
|
|
+ /* expand table inheritances */
|
|
+ selist = expandSEvalListInheritance(selist);
|
|
+
|
|
+ /* add checks for access via trigger function */
|
|
+ foreach(l, pstmt->resultRelations) {
|
|
+ Index rindex = lfirst_int(l);
|
|
+
|
|
+ rte = rt_fetch(rindex, pstmt->rtable);
|
|
+ Assert(IsA(rte, RangeTblEntry));
|
|
+
|
|
+ selist = addEvalTriggerAccess(selist, rte->relid, rte->inh, pstmt->commandType);
|
|
+ }
|
|
+ execVerifyQuery(selist);
|
|
+}
|
|
+
|
|
+/* *******************************************************************************
|
|
+ * PGACE hooks: we cannon the following hooks in sepgsqlHooks.c because they
|
|
+ * refers static defined variables in sepgsqlProxy.c
|
|
+ * *******************************************************************************/
|
|
+
|
|
+/* ----------------------------------------------------------
|
|
+ * COPY TO/COPY FROM statement hooks
|
|
+ * ---------------------------------------------------------- */
|
|
+void sepgsqlCopyTable(Relation rel, List *attNumList, bool isFrom)
|
|
+{
|
|
+ List *selist = NIL;
|
|
+ ListCell *l;
|
|
+
|
|
+ /* on 'COPY FROM SELECT ...' cases, any checkings are done in select.c */
|
|
+ if (rel == NULL)
|
|
+ return;
|
|
+
|
|
+ /* no need to check non-table relation */
|
|
+ if (RelationGetForm(rel)->relkind != RELKIND_RELATION)
|
|
+ return;
|
|
+
|
|
+ selist = __addEvalPgClass(selist, RelationGetRelid(rel), false,
|
|
+ isFrom ? DB_TABLE__INSERT : DB_TABLE__SELECT);
|
|
+ foreach (l, attNumList) {
|
|
+ AttrNumber attnum = lfirst_int(l);
|
|
+
|
|
+ selist = __addEvalPgAttribute(selist, RelationGetRelid(rel), false, attnum,
|
|
+ isFrom ? DB_COLUMN__INSERT : DB_COLUMN__SELECT);
|
|
+ }
|
|
+
|
|
+ /* check call trigger function */
|
|
+ if (isFrom)
|
|
+ selist = addEvalTriggerAccess(selist, RelationGetRelid(rel), false, CMD_INSERT);
|
|
+
|
|
+ execVerifyQuery(selist);
|
|
+}
|
|
+
|
|
+bool sepgsqlCopyToTuple(Relation rel, HeapTuple tuple)
|
|
+{
|
|
+ return sepgsqlCheckTuplePerms(rel, tuple, NULL, DB_TUPLE__SELECT, false);
|
|
+}
|
|
+
|
|
+#if 0
|
|
+// sepgsqlCopyFromTuple is replaced by sepgsqlHeapTupleInsert()
|
|
+bool sepgsqlCopyFromTuple(Relation rel, HeapTuple tuple)
|
|
+{
|
|
+ Oid tcontext = HeapTupleGetSecurity(tuple);
|
|
+
|
|
+ if (tcontext == InvalidOid) {
|
|
+ /* implicit labeling */
|
|
+ tcontext = sepgsqlComputeImplicitContext(rel, tuple);
|
|
+ HeapTupleSetSecurity(tuple, tcontext);
|
|
+ }
|
|
+ return sepgsqlCheckTuplePerms(rel, tuple, NULL, DB_TUPLE__INSERT, false);
|
|
+}
|
|
+#endif
|
|
+
|
|
+/* ----------------------------------------------------------
|
|
+ * node copy/print hooks
|
|
+ * ---------------------------------------------------------- */
|
|
+Node *sepgsqlCopyObject(Node *__oldnode) {
|
|
+ SEvalItem *oldnode, *newnode;
|
|
+
|
|
+ if (nodeTag(__oldnode) != T_SEvalItem)
|
|
+ return NULL;
|
|
+ oldnode = (SEvalItem *) __oldnode;
|
|
+
|
|
+ newnode = makeNode(SEvalItem);
|
|
+ newnode->tclass = oldnode->tclass;
|
|
+ newnode->perms = oldnode->perms;
|
|
+ switch (oldnode->tclass) {
|
|
+ case SECCLASS_DB_TABLE:
|
|
+ newnode->c.relid = oldnode->c.relid;
|
|
+ newnode->c.inh = oldnode->c.inh;
|
|
+ break;
|
|
+ case SECCLASS_DB_COLUMN:
|
|
+ newnode->a.relid = oldnode->a.relid;
|
|
+ newnode->a.attno = oldnode->a.attno;
|
|
+ newnode->a.inh = oldnode->a.inh;
|
|
+ break;
|
|
+ case SECCLASS_DB_PROCEDURE:
|
|
+ newnode->p.funcid = oldnode->p.funcid;
|
|
+ break;
|
|
+ default:
|
|
+ selerror("unrecognized SEvalItem node (tclass: %d)", oldnode->tclass);
|
|
+ break;
|
|
+ }
|
|
+ return (Node *) newnode;
|
|
+}
|
|
+
|
|
+bool sepgsqlOutObject(StringInfo str, Node *node) {
|
|
+ SEvalItem *seitem = (SEvalItem *) node;
|
|
+
|
|
+ if (nodeTag(node) != T_SEvalItem)
|
|
+ return false;
|
|
+
|
|
+ appendStringInfoString(str, "SEVALITEM");
|
|
+ appendStringInfo(str, ":tclass %u", seitem->tclass);
|
|
+ appendStringInfo(str, ":perms %u", seitem->perms);
|
|
+ switch(seitem->tclass) {
|
|
+ case SECCLASS_DB_TABLE:
|
|
+ appendStringInfo(str, ":c.relid %u", seitem->c.relid);
|
|
+ appendStringInfo(str, ":c.inh %s", seitem->c.inh ? "true" : "false");
|
|
+ break;
|
|
+ case SECCLASS_DB_COLUMN:
|
|
+ appendStringInfo(str, ":a.relid %u", seitem->a.relid);
|
|
+ appendStringInfo(str, ":a.inh %s", seitem->a.inh ? "true" : "false");
|
|
+ appendStringInfo(str, ":a.attno %u", seitem->a.attno);
|
|
+ break;
|
|
+ case SECCLASS_DB_PROCEDURE:
|
|
+ appendStringInfo(str, ":p.funcid %u", seitem->p.funcid);
|
|
+ break;
|
|
+ default:
|
|
+ selerror("unrecognized SEvalItem node (tclass: %d)", seitem->tclass);
|
|
+ break;
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
+void *sepgsqlReadObject(char *token)
|
|
+{
|
|
+ SEvalItem *seitem;
|
|
+ int length;
|
|
+
|
|
+ if (strcmp(token, "SEVALITEM"))
|
|
+ return NULL;
|
|
+
|
|
+ seitem = makeNode(SEvalItem);
|
|
+
|
|
+ /* :tclass */
|
|
+ token = pg_strtok(&length);
|
|
+ token = pg_strtok(&length);
|
|
+ seitem->tclass = atoi(token);
|
|
+
|
|
+ /* :perms */
|
|
+ token = pg_strtok(&length);
|
|
+ token = pg_strtok(&length);
|
|
+ seitem->perms = (unsigned int) strtoul(token, NULL, 10);
|
|
+
|
|
+ switch (seitem->tclass) {
|
|
+ case SECCLASS_DB_TABLE:
|
|
+ /* :c.relid */
|
|
+ token = pg_strtok(&length);
|
|
+ token = pg_strtok(&length);
|
|
+ seitem->c.relid = (unsigned int) strtoul(token, NULL, 10);
|
|
+
|
|
+ /* :c.inh */
|
|
+ token = pg_strtok(&length);
|
|
+ token = pg_strtok(&length);
|
|
+ seitem->c.inh = (strcmp(token, "true") == 0 ? true : false);
|
|
+ break;
|
|
+
|
|
+ case SECCLASS_DB_COLUMN:
|
|
+ /* :a.relid */
|
|
+ token = pg_strtok(&length);
|
|
+ token = pg_strtok(&length);
|
|
+ seitem->a.relid = (unsigned int) strtoul(token, NULL, 10);
|
|
+
|
|
+ /* :a.inh */
|
|
+ token = pg_strtok(&length);
|
|
+ token = pg_strtok(&length);
|
|
+ seitem->a.inh = (strcmp(token, "true") == 0 ? true : false);
|
|
+
|
|
+ /* :a.attno */
|
|
+ token = pg_strtok(&length);
|
|
+ token = pg_strtok(&length);
|
|
+ seitem->a.attno = atoi(token);
|
|
+ break;
|
|
+
|
|
+ case SECCLASS_DB_PROCEDURE:
|
|
+ /* :p.funcid */
|
|
+ token = pg_strtok(&length);
|
|
+ token = pg_strtok(&length);
|
|
+ seitem->p.funcid = (unsigned int) strtoul(token, NULL, 10);
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ selerror("unrecognized SEvalItem node (tclass: %d)", seitem->tclass);
|
|
+ break;
|
|
+ }
|
|
+ return (void *) seitem;
|
|
+}
|
|
+
|
|
+/* ----------------------------------------------------------
|
|
+ * special cases in foreign key constraint
|
|
+ * ---------------------------------------------------------- */
|
|
+Oid sepgsqlPreparePlanCheck(Relation rel) {
|
|
+ Oid pgace_saved = fnoid_sepgsql_tuple_perm;
|
|
+ fnoid_sepgsql_tuple_perm = F_SEPGSQL_TUPLE_PERMS_ABORT;
|
|
+ return pgace_saved;
|
|
+}
|
|
+
|
|
+void sepgsqlRestorePlanCheck(Relation rel, Oid pgace_saved) {
|
|
+ fnoid_sepgsql_tuple_perm = pgace_saved;
|
|
+}
|
|
diff -rpNU3 pgace/src/include/catalog/pg_proc.h sepgsql/src/include/catalog/pg_proc.h
|
|
--- pgace/src/include/catalog/pg_proc.h 2008-01-08 01:39:49.000000000 +0900
|
|
+++ sepgsql/src/include/catalog/pg_proc.h 2008-01-08 12:56:27.000000000 +0900
|
|
@@ -4123,6 +4123,11 @@ DATA(insert OID = 3409 ( security_label_
|
|
DATA(insert OID = 3410 ( lo_get_security PGNSP PGUID 12 1 0 f f t f v 1 3403 "26" _null_ _null_ _null_ lo_get_security - _null_ _null_ ));
|
|
DATA(insert OID = 3411 ( lo_set_security PGNSP PGUID 12 1 0 f f t f v 2 16 "26 3403" _null_ _null_ _null_ lo_set_security - _null_ _null_ ));
|
|
|
|
+/* SE-PostgreSQL related function */
|
|
+DATA(insert OID = 3420 ( sepgsql_getcon PGNSP PGUID 12 1 0 f f t f v 0 3403 "" _null_ _null_ _null_ sepgsql_getcon - _null_ _null_ ));
|
|
+DATA(insert OID = 3421 ( sepgsql_tuple_perms PGNSP PGUID 12 1 0 f f t f v 4 16 "26 3403 23 2249" _null_ _null_ _null_ sepgsql_tuple_perms - _null_ _null_ ));
|
|
+DATA(insert OID = 3422 ( sepgsql_tuple_perms_abort PGNSP PGUID 12 1 0 f f t f v 4 16 "26 3403 23 2249" _null_ _null_ _null_ sepgsql_tuple_perms_abort - _null_ _null_ ));
|
|
+
|
|
/* enum related procs */
|
|
DATA(insert OID = 3504 ( anyenum_in PGNSP PGUID 12 1 0 f f t f i 1 3500 "2275" _null_ _null_ _null_ anyenum_in - _null_ _null_ ));
|
|
DESCR("I/O");
|
|
diff -rpNU3 pgace/src/include/pg_config.h.in sepgsql/src/include/pg_config.h.in
|
|
--- pgace/src/include/pg_config.h.in 2007-11-13 12:03:06.000000000 +0900
|
|
+++ sepgsql/src/include/pg_config.h.in 2007-11-22 23:10:13.000000000 +0900
|
|
@@ -366,6 +366,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 NSA SELinux support */
|
|
+#undef HAVE_SELINUX
|
|
+
|
|
/* Define to 1 if you have the `setproctitle' function. */
|
|
#undef HAVE_SETPROCTITLE
|
|
|
|
diff -rpNU3 pgace/src/include/security/sepgsql.h sepgsql/src/include/security/sepgsql.h
|
|
--- pgace/src/include/security/sepgsql.h 1970-01-01 09:00:00.000000000 +0900
|
|
+++ sepgsql/src/include/security/sepgsql.h 2008-01-08 16:14:52.000000000 +0900
|
|
@@ -0,0 +1,162 @@
|
|
+#ifndef SEPGSQL_H
|
|
+#define SEPGSQL_H
|
|
+
|
|
+/* system catalogs */
|
|
+#include "catalog/catalog.h"
|
|
+#include "catalog/indexing.h"
|
|
+#include "catalog/pg_aggregate.h"
|
|
+#include "catalog/pg_am.h"
|
|
+#include "catalog/pg_attribute.h"
|
|
+#include "catalog/pg_authid.h"
|
|
+#include "catalog/pg_cast.h"
|
|
+#include "catalog/pg_class.h"
|
|
+#include "catalog/pg_constraint.h"
|
|
+#include "catalog/pg_conversion.h"
|
|
+#include "catalog/pg_database.h"
|
|
+#include "catalog/pg_language.h"
|
|
+#include "catalog/pg_largeobject.h"
|
|
+#include "catalog/pg_listener.h"
|
|
+#include "catalog/pg_namespace.h"
|
|
+#include "catalog/pg_opclass.h"
|
|
+#include "catalog/pg_operator.h"
|
|
+#include "catalog/pg_pltemplate.h"
|
|
+#include "catalog/pg_proc.h"
|
|
+#include "catalog/pg_rewrite.h"
|
|
+#include "catalog/pg_security.h"
|
|
+#include "catalog/pg_tablespace.h"
|
|
+#include "catalog/pg_trigger.h"
|
|
+#include "catalog/pg_type.h"
|
|
+#include "lib/stringinfo.h"
|
|
+#include "nodes/nodes.h"
|
|
+#include "storage/large_object.h"
|
|
+
|
|
+#include <selinux/selinux.h>
|
|
+#include <selinux/flask.h>
|
|
+#include <selinux/av_permissions.h>
|
|
+
|
|
+#define selerror(fmt, ...) \
|
|
+ ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), \
|
|
+ errmsg("%s(%d): " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)))
|
|
+#define selnotice(fmt, ...) \
|
|
+ ereport(NOTICE, (errcode(ERRCODE_WARNING), \
|
|
+ errmsg("%s(%d): " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)))
|
|
+#define seldebug(fmt, ...) \
|
|
+ ereport(NOTICE, (errcode(ERRCODE_WARNING), \
|
|
+ errmsg("%s(%d): " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)))
|
|
+#define selbugon(x) do { if (x)((char *)NULL)[0] = 'a'; }while(0)
|
|
+
|
|
+/*
|
|
+ * The implementation of PGACE/SE-PostgreSQL hooks
|
|
+ */
|
|
+
|
|
+/* Initialize / Finalize related hooks */
|
|
+extern Size sepgsqlShmemSize(void);
|
|
+extern void sepgsqlInitialize(bool is_bootstrap);
|
|
+extern int sepgsqlInitializePostmaster(void);
|
|
+extern void sepgsqlFinalizePostmaster(void);
|
|
+
|
|
+/* SQL proxy hooks */
|
|
+extern List *sepgsqlProxyQuery(Query *query);
|
|
+extern void sepgsqlVerifyQuery(PlannedStmt *pstmt);
|
|
+
|
|
+/* HeapTuple modification hooks */
|
|
+extern bool sepgsqlHeapTupleInsert(Relation rel, HeapTuple tuple,
|
|
+ bool is_internal, bool with_returning);
|
|
+extern bool sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup,
|
|
+ bool is_internal, bool with_returning);
|
|
+extern bool sepgsqlHeapTupleDelete(Relation rel, ItemPointer otid,
|
|
+ bool is_internal, bool with_returning);
|
|
+
|
|
+/* Extended SQL statement hooks */
|
|
+extern DefElem *sepgsqlGramSecurityItem(char *defname, char *value);
|
|
+extern bool sepgsqlIsGramSecurityItem(DefElem *defel);
|
|
+extern void sepgsqlGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel);
|
|
+extern void sepgsqlGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel);
|
|
+extern void sepgsqlGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel);
|
|
+extern void sepgsqlGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel);
|
|
+extern void sepgsqlGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel);
|
|
+extern void sepgsqlGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel);
|
|
+extern void sepgsqlGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel);
|
|
+extern void sepgsqlGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel);
|
|
+
|
|
+/* DATABASE related hooks */
|
|
+extern void sepgsqlSetDatabaseParam(const char *name, char *argstring);
|
|
+extern void sepgsqlGetDatabaseParam(const char *name);
|
|
+
|
|
+/* FUNCTION related hooks */
|
|
+extern void sepgsqlCallFunction(FmgrInfo *finfo, bool with_perm_check);
|
|
+extern bool sepgsqlCallFunctionTrigger(FmgrInfo *finfo, TriggerData *tgdata);
|
|
+extern Oid sepgsqlPreparePlanCheck(Relation rel);
|
|
+extern void sepgsqlRestorePlanCheck(Relation rel, Oid pgace_saved);
|
|
+
|
|
+/* TABLE related hooks */
|
|
+extern void sepgsqlLockTable(Oid relid);
|
|
+extern bool sepgsqlAlterTable(Relation rel, AlterTableCmd *cmd);
|
|
+
|
|
+/* COPY TO/COPY FROM statement hooks */
|
|
+extern void sepgsqlCopyTable(Relation rel, List *attnumlist, bool is_from);
|
|
+extern bool sepgsqlCopyToTuple(Relation rel, HeapTuple tuple);
|
|
+
|
|
+/* Loadable shared library module hooks */
|
|
+extern void sepgsqlLoadSharedModule(const char *filename);
|
|
+
|
|
+/* Binary Large Object (BLOB) hooks */
|
|
+extern Oid sepgsqlLargeObjectGetSecurity(HeapTuple tuple);
|
|
+extern void sepgsqlLargeObjectSetSecurity(HeapTuple tuple, Oid lo_security, bool is_first);
|
|
+extern void sepgsqlLargeObjectCreate(Relation rel, HeapTuple tuple);
|
|
+extern void sepgsqlLargeObjectDrop(Relation rel, HeapTuple tuple);
|
|
+extern void sepgsqlLargeObjectOpen(Relation rel, HeapTuple tuple, bool read_only);
|
|
+extern void sepgsqlLargeObjectRead(Relation rel, HeapTuple tuple);
|
|
+extern void sepgsqlLargeObjectWrite(Relation rel, HeapTuple newtup, HeapTuple oldtup);
|
|
+extern void sepgsqlLargeObjectTruncate(Relation rel, Oid loid);
|
|
+extern void sepgsqlLargeObjectImport(void);
|
|
+extern void sepgsqlLargeObjectExport(void);
|
|
+
|
|
+/* Security Label hooks */
|
|
+extern char *sepgsqlSecurityLabelIn(char *context);
|
|
+extern char *sepgsqlSecurityLabelOut(char *context);
|
|
+extern bool sepgsqlSecurityLabelIsValid(char *context);
|
|
+extern char *sepgsqlSecurityLabelOfLabel(char *context);
|
|
+extern char *sepgsqlSecurityLabelNotFound(Oid sid);
|
|
+
|
|
+/* Extended node type hooks */
|
|
+extern Node *sepgsqlCopyObject(Node *node);
|
|
+extern bool sepgsqlOutObject(StringInfo str, Node *node);
|
|
+extern void *sepgsqlReadObject(char *token);
|
|
+
|
|
+/*
|
|
+ * SE-PostgreSQL core functions
|
|
+ * src/backend/security/sepgsql/core.c
|
|
+ */
|
|
+extern bool sepgsqlIsEnabled(void);
|
|
+extern Oid sepgsqlGetServerContext(void);
|
|
+extern Oid sepgsqlGetClientContext(void);
|
|
+extern void sepgsqlSetClientContext(Oid new_ctx);
|
|
+extern Oid sepgsqlGetDatabaseContext(void);
|
|
+extern char *sepgsqlGetDatabaseName(void);
|
|
+
|
|
+/* userspace access vector cache related */
|
|
+extern void sepgsql_avc_permission(Oid ssid, Oid tsid, uint16 tclass,
|
|
+ uint32 perms, char *objname);
|
|
+extern bool sepgsql_avc_permission_noaudit(Oid ssid, Oid tsid, uint16 tclass,
|
|
+ uint32 perms, char **audit, char *objname);
|
|
+extern void sepgsql_audit(bool result, char *message);
|
|
+extern Oid sepgsql_avc_createcon(Oid ssid, Oid tsid, uint16 tclass);
|
|
+extern Oid sepgsql_avc_relabelcon(Oid ssid, Oid tsid, uint16 tclass);
|
|
+
|
|
+/*
|
|
+ * SE-PostgreSQL permission evaluation related
|
|
+ * src/backend/security/sepgsql/permission.c
|
|
+ */
|
|
+extern char *sepgsqlGetTupleName(Oid relid, HeapTuple tuple);
|
|
+extern Oid sepgsqlComputeImplicitContext(Relation rel, HeapTuple tuple);
|
|
+extern bool sepgsqlCheckTuplePerms(Relation rel, HeapTuple tuple, HeapTuple oldtup,
|
|
+ uint32 perms, bool abort);
|
|
+/*
|
|
+ * SE-PostgreSQL SQL FUNCTIONS
|
|
+ */
|
|
+extern Datum sepgsql_getcon(PG_FUNCTION_ARGS);
|
|
+extern Datum sepgsql_tuple_perms(PG_FUNCTION_ARGS);
|
|
+extern Datum sepgsql_tuple_perms_abort(PG_FUNCTION_ARGS);
|
|
+
|
|
+#endif /* SEPGSQL_H */
|