sepostgresql/sepostgresql-sepgsql-8.3.0-2.patch

4609 lines
136 KiB
Diff

diff -rpNU3 pgace/configure sepgsql/configure
--- pgace/configure 2008-02-03 01:18:48.000000000 +0900
+++ sepgsql/configure 2008-02-03 01:26:35.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-02-03 01:18:48.000000000 +0900
+++ sepgsql/configure.in 2008-02-03 01:26:35.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 2008-02-04 17:40:05.000000000 +0900
@@ -0,0 +1,994 @@
+/*
+ * 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 "catalog/pg_database.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;
+ }
+ /* 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);
+}
+
+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";
+ }
+ }
+ /* 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);
+}
+
+/*
+ * 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;
+ }
+ return __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))
+ elog(ERROR, "SELinux: could not compute an access vector decision"
+ " scon='%s' tcon='%s' tclass=%u", scon, tcon, tclass);
+ if (security_compute_create_raw(scon, tcon, tclass_external, &ncon) != 0)
+ elog(ERROR, "SELinux: could not compute an implicit 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)
+ elog(ERROR, "SELinux: could not compute a 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 bool __avc_audit(uint32 perms, struct avc_datum *avd, char *objname,
+ char *audit_buf, int buflen)
+{
+ /* we have to hold LW_SHARED lock at least */
+ uint32 denied, audited, mask;
+ char *context;
+ int ofs = 0;
+
+ denied = perms & ~avd->allowed;
+ audited = denied ? (denied & avd->auditdeny) : (perms & avd->auditallow);
+ if (!audited)
+ return false;
+
+ ofs += snprintf(audit_buf + ofs, buflen - ofs, "%s {",
+ denied ? "denied" : "granted");
+ for (mask=1; mask; mask <<= 1) {
+ if (audited & mask) {
+ ofs += snprintf(audit_buf + ofs, buflen - ofs, " %s",
+ sepgsql_av_perm_to_string(avd->tclass, mask));
+ }
+ }
+ ofs += snprintf(audit_buf + ofs, buflen - ofs, " }");
+
+ context = DatumGetCString(DirectFunctionCall1(security_label_out,
+ ObjectIdGetDatum(avd->ssid)));
+ ofs += snprintf(audit_buf + ofs, buflen - ofs, " scontext=%s", context);
+ pfree(context);
+
+ context = DatumGetCString(DirectFunctionCall1(security_label_out,
+ ObjectIdGetDatum(avd->tsid)));
+ ofs += snprintf(audit_buf + ofs, buflen - ofs, " tcontext=%s", context);
+ pfree(context);
+
+ ofs += snprintf(audit_buf + ofs, buflen - ofs, " tclass=%s",
+ sepgsql_class_to_string(avd->tclass));
+ if (objname)
+ ofs += snprintf(audit_buf + ofs, buflen - ofs, " name=%s", objname);
+
+ return true;
+}
+
+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;
+}
+
+static bool __avc_permission(Oid ssid, Oid tsid, uint16 tclass, uint32 perms,
+ char *objname, struct avc_datum *local_avd)
+{
+ struct avc_datum *avd;
+ 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, local_avd);
+
+ LWLockAcquire(avc_shmem->lock, LW_EXCLUSIVE);
+ wlock = true;
+ sepgsql_avc_insert(local_avd);
+ } else {
+ memcpy(local_avd, avd, sizeof(struct avc_datum));
+ }
+ denied = perms & ~local_avd->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);
+
+ return rc;
+}
+
+void sepgsql_avc_permission(Oid ssid, Oid tsid, uint16 tclass, uint32 perms, char *objname)
+{
+ struct avc_datum local_avd;
+ char audit_buf[4096];
+ bool rc;
+
+ rc = __avc_permission(ssid, tsid, tclass, perms, objname, &local_avd);
+ if (__avc_audit(perms, &local_avd, objname,
+ audit_buf, sizeof(audit_buf))) {
+ elog(rc ? NOTICE : ERROR, "SELinux: %s", audit_buf);
+ } else if (rc != true) {
+ elog(ERROR, "SELinux: security policy violation.");
+ }
+}
+
+bool sepgsql_avc_permission_noabort(Oid ssid, Oid tsid, uint16 tclass, uint32 perms, char *objname)
+{
+ struct avc_datum local_avd;
+ char audit_buf[4096];
+ bool rc;
+
+ rc = __avc_permission(ssid, tsid, tclass, perms, objname, &local_avd);
+ if (__avc_audit(perms, &local_avd, objname,
+ audit_buf, sizeof(audit_buf))) {
+ elog(NOTICE, "SELinux: %s", audit_buf);
+ }
+ return rc;
+}
+
+Oid sepgsql_avc_createcon(Oid ssid, Oid tsid, uint16 tclass)
+{
+ struct avc_datum *avd, local_avd;
+ 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, &local_avd);
+
+ LWLockAcquire(avc_shmem->lock, LW_EXCLUSIVE);
+ sepgsql_avc_insert(&local_avd);
+ nsid = local_avd.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)
+ elog(ERROR, "SELinux: 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)
+ elog(ERROR, "SELinux: could not obtain security context of database client");
+ if (security_check_context(__context) ||
+ selinux_trans_to_raw_context(__context, &context))
+ elog(ERROR, "SELinux: '%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))
+ elog(ERROR, "SELinux: 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))
+ elog(ERROR, "SELinux: 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)
+{
+ elog(NOTICE, "SELinux: userspace AVC reset");
+ sepgsql_avc_reset();
+}
+
+static int sepgsqlMonitoringPolicyState()
+{
+ char buffer[2048];
+ struct sockaddr_nl addr;
+ socklen_t addrlen;
+ struct nlmsghdr *nlh;
+ int i, rc, nl_sockfd;
+
+ /* 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) {
+ elog(NOTICE, "SELinux: could not open 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))) {
+ elog(NOTICE, "SELinux: could not bint 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;
+ elog(NOTICE, "SELinux: netlink recvfrom() errno=%d (%s)",
+ errno, strerror(errno));
+ return 1;
+ }
+
+ if (addrlen != sizeof(addr)) {
+ elog(NOTICE, "SELinux: netlink address truncated (len=%d)", addrlen);
+ return 1;
+ }
+
+ if (addr.nl_pid) {
+ elog(NOTICE, "SELinux: netlink received spoofed packet from: %u", addr.nl_pid);
+ continue;
+ }
+
+ if (rc == 0) {
+ elog(NOTICE, "SELinux: netlink received EOF on socket");
+ return 1;
+ }
+
+ nlh = (struct nlmsghdr *)buffer;
+
+ if (nlh->nlmsg_flags & MSG_TRUNC
+ || nlh->nlmsg_len > (unsigned int)rc) {
+ elog(NOTICE, "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;
+ elog(NOTICE, "SELinux: netlink error message %d", -err->error);
+ return 1;
+ }
+ case SELNL_MSG_SETENFORCE: {
+ struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh);
+ elog(NOTICE, "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);
+ elog(NOTICE, "SELinux: netlink received policyload notice (seqno=%d)", msg->seqno);
+ sepgsql_avc_reset();
+ break;
+ }
+ default:
+ elog(NOTICE, "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) {
+ elog(NOTICE, "SELinux: could not create a policy state monitoring process.");
+ return false;
+ }
+ return true;
+}
+
+void sepgsqlFinalizePostmaster()
+{
+ int status;
+
+ if (!sepgsqlIsEnabled())
+ return;
+
+ if (MonitoringPolicyStatePid > 0) {
+ if (kill(MonitoringPolicyStatePid, SIGTERM) < 0) {
+ elog(NOTICE, "SELinux: could not kill(%u, SIGTERM), (%s)",
+ MonitoringPolicyStatePid, 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-02-04 17:40:05.000000000 +0900
@@ -0,0 +1,667 @@
+/*
+ * 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 "catalog/indexing.h"
+#include "catalog/pg_database.h"
+#include "catalog/pg_largeobject.h"
+#include "catalog/pg_proc.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;
+ NameData audit_name;
+
+ tuple = SearchSysCache(DATABASEOID,
+ ObjectIdGetDatum(MyDatabaseId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
+
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_DATABASE,
+ DB_DATABASE__GET_PARAM,
+ sepgsqlGetTupleName(DatabaseRelationId, tuple, &audit_name));
+ ReleaseSysCache(tuple);
+}
+
+void sepgsqlSetDatabaseParam(const char *name, char *argstring)
+{
+ HeapTuple tuple;
+ NameData audit_name;
+
+ tuple = SearchSysCache(DATABASEOID,
+ ObjectIdGetDatum(MyDatabaseId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for database %u", MyDatabaseId);
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_DATABASE,
+ DB_DATABASE__SET_PARAM,
+ sepgsqlGetTupleName(DatabaseRelationId, tuple, &audit_name));
+ ReleaseSysCache(tuple);
+}
+
+/*******************************************************************************
+ * RELATION(Table)/ATTRIBTUE(column) object related hooks
+ *******************************************************************************/
+void sepgsqlLockTable(Oid relid)
+{
+ HeapTuple tuple;
+ Form_pg_class classForm;
+ NameData name;
+
+ tuple = SearchSysCache(RELOID,
+ ObjectIdGetDatum(relid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "SELinux: 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, &name));
+ 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;
+ NameData name;
+ Oid execcon;
+ uint32 perms = DB_PROCEDURE__EXECUTE;
+
+ tuple = SearchSysCache(PROCOID,
+ ObjectIdGetDatum(finfo->fn_oid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "SELinux: 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, &name));
+ }
+ 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 {
+ elog(ERROR, "SELinux: unexpected trigger event type (%u)", tgdata->tg_event);
+ }
+ if (oldtup && !sepgsqlCheckTuplePerms(rel, oldtup, NULL, SEPGSQL_PERMS_SELECT, false))
+ return false;
+ if (newtup && !sepgsqlCheckTuplePerms(rel, newtup, NULL, SEPGSQL_PERMS_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)
+ elog(ERROR, "SELinux: 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
+ *******************************************************************************/
+void sepgsqlLargeObjectGetSecurity(HeapTuple tuple) {
+ Oid lo_security = HeapTupleGetSecurity(tuple);
+ NameData name;
+
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
+ lo_security,
+ SECCLASS_DB_BLOB,
+ DB_BLOB__GETATTR,
+ sepgsqlGetTupleName(LargeObjectRelationId, tuple, &name));
+}
+
+void sepgsqlLargeObjectSetSecurity(HeapTuple tuple, Oid lo_security)
+{
+ NameData name;
+
+ /* check db_blob:{setattr relabelfrom} */
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_BLOB,
+ DB_BLOB__SETATTR | DB_BLOB__RELABELFROM,
+ sepgsqlGetTupleName(LargeObjectRelationId, tuple, &name));
+
+ /* check db_blob:{relabelto} */
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
+ lo_security,
+ SECCLASS_DB_BLOB,
+ DB_BLOB__RELABELTO,
+ sepgsqlGetTupleName(LargeObjectRelationId, tuple, &name));
+}
+
+void sepgsqlLargeObjectCreate(Relation rel, HeapTuple tuple)
+{
+ Oid newcon = sepgsqlComputeImplicitContext(rel, tuple);
+ NameData name;
+
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
+ newcon,
+ SECCLASS_DB_BLOB,
+ DB_BLOB__CREATE,
+ sepgsqlGetTupleName(LargeObjectRelationId, tuple, &name));
+ HeapTupleSetSecurity(tuple, newcon);
+}
+
+void sepgsqlLargeObjectDrop(Relation rel, HeapTuple tuple)
+{
+ NameData name;
+
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_BLOB,
+ DB_BLOB__DROP,
+ sepgsqlGetTupleName(LargeObjectRelationId, tuple, &name));
+}
+
+void sepgsqlLargeObjectRead(Relation rel, HeapTuple tuple)
+{
+ sepgsqlCheckTuplePerms(rel, tuple, NULL,
+ SEPGSQL_PERMS_SELECT | SEPGSQL_PERMS_READ, true);
+}
+
+void sepgsqlLargeObjectWrite(Relation rel, HeapTuple newtup, HeapTuple oldtup)
+{
+ ScanKeyData skey;
+ SysScanDesc sd;
+ HeapTuple tuple;
+ Oid loid;
+
+ /* update existing region */
+ if (HeapTupleIsValid(oldtup)) {
+ HeapTupleSetSecurity(newtup, HeapTupleGetSecurity(oldtup));
+ sepgsqlCheckTuplePerms(rel, newtup, NULL, SEPGSQL_PERMS_UPDATE, true);
+ return;
+ }
+
+ /* insert a new large object page */
+ loid = ((Form_pg_largeobject) GETSTRUCT(newtup))->loid;
+ ScanKeyInit(&skey,
+ Anum_pg_largeobject_loid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(loid));
+ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true,
+ SnapshotSelf, 1, &skey);
+ tuple = systable_getnext(sd);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "SELinux: large object %u does not exist", loid);
+ HeapTupleSetSecurity(newtup, HeapTupleGetSecurity(tuple));
+ sepgsqlCheckTuplePerms(rel, newtup, NULL, SEPGSQL_PERMS_UPDATE, true);
+ systable_endscan(sd);
+}
+
+void sepgsqlLargeObjectTruncate(Relation rel, Oid loid, HeapTuple headtup) {
+ ScanKeyData skey;
+ SysScanDesc sd;
+ HeapTuple tuple;
+
+ /* simple truncating case */
+ if (HeapTupleIsValid(headtup)) {
+ sepgsqlCheckTuplePerms(rel, headtup, NULL, SEPGSQL_PERMS_UPDATE, true);
+ return;
+ }
+
+ /* terminated in a hole */
+ 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))
+ elog(ERROR, "SELinux: large object %u does not exist", loid);
+ sepgsqlCheckTuplePerms(rel, tuple, NULL, SEPGSQL_PERMS_UPDATE, 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)
+ elog(ERROR, "SELinux: could not translate MLS label");
+
+ rc = security_canonicalize_context_raw(raw_context, &canonical_context);
+ freecon(raw_context);
+ if (rc)
+ elog(ERROR, "SELinux: could not formalize security 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))
+ elog(ERROR, "could not translate MLS label");
+ PG_TRY();
+ {
+ result = pstrdup(context);
+ }
+ PG_CATCH();
+ {
+ freecon(context);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(context);
+
+ return result;
+}
+
+char *sepgsqlSecurityLabelCheckValid(char *context) {
+ security_context_t unlbl_con;
+ char *unlbl_result = NULL;
+
+ if (context && !security_check_context_raw(context))
+ return context;
+
+ /* context is invalid one */
+ if (security_get_initial_context_raw("unlabeled", &unlbl_con))
+ elog(ERROR, "SELinux: could not assign an alternative security context");
+ PG_TRY();
+ {
+ unlbl_result = pstrdup(unlbl_con);
+ }
+ PG_CATCH();
+ {
+ freecon(unlbl_con);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(unlbl_con);
+
+ return unlbl_result;
+}
+
+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))
+ elog(ERROR, "SELinux: cache lookup failed for pg_security");
+ tcon = DatumGetCString(DirectFunctionCall1(security_label_raw_out,
+ ObjectIdGetDatum(HeapTupleGetSecurity(tuple))));
+ ReleaseSysCache(tuple);
+
+ /* obtain server's context */
+ rc = getcon_raw(&scon);
+ if (rc)
+ elog(ERROR, "SELinux: 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)
+ elog(ERROR, "SELinux: could not compute label of pg_security");
+
+ /* copy tuple's context */
+ PG_TRY();
+ {
+ _ncon = pstrdup(ncon);
+ }
+ PG_CATCH();
+ {
+ freecon(ncon);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ freecon(ncon);
+
+ return _ncon;
+}
+
+/******************************************************************
+ * HeapTuple modification hooks
+ ******************************************************************/
+static bool __TrustedRelationForInternal(Relation rel)
+{
+ if (RelationGetForm(rel)->relkind != RELKIND_RELATION)
+ return true;
+
+ switch (RelationGetRelid(rel)) {
+ case LargeObjectRelationId:
+ case SecurityRelationId:
+ 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 && __TrustedRelationForInternal(rel))
+ return true;
+
+ perms = SEPGSQL_PERMS_INSERT;
+ if (with_returning)
+ perms |= SEPGSQL_PERMS_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 && __TrustedRelationForInternal(rel))
+ goto out;
+
+ if (is_internal) {
+ perms = SEPGSQL_PERMS_UPDATE;
+ if (HeapTupleGetSecurity(newtup) != HeapTupleGetSecurity(oldtup))
+ perms |= SEPGSQL_PERMS_RELABELFROM;
+ rc = sepgsqlCheckTuplePerms(rel, oldtup, NULL, perms, is_internal);
+ if (!rc)
+ goto out;
+ }
+
+ if (HeapTupleGetSecurity(newtup) != HeapTupleGetSecurity(oldtup)) {
+ perms = SEPGSQL_PERMS_RELABELTO;
+ if (with_returning)
+ perms |= SEPGSQL_PERMS_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;
+ uint32 perms;
+ bool rc = true;
+
+ if (is_internal) {
+ if (__TrustedRelationForInternal(rel))
+ return true;
+
+ oldtup = __getHeapTupleFromItemPointer(rel, otid);
+ perms = SEPGSQL_PERMS_DELETE;
+ if (with_returning)
+ perms |= SEPGSQL_PERMS_SELECT;
+ rc = sepgsqlCheckTuplePerms(rel, oldtup, NULL, perms, 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-02-01 20:23:10.000000000 +0900
@@ -0,0 +1,353 @@
+/*
+ * 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, List *attNumList, HeapTuple tuple) {
+ if (sepgsqlIsEnabled())
+ sepgsqlCopyToTuple(rel, attNumList, tuple);
+ return true;
+}
+
+/******************************************************************
+ * Loadable shared library module hooks
+ ******************************************************************/
+void pgaceLoadSharedModule(const char *filename) {
+ if (sepgsqlIsEnabled())
+ sepgsqlLoadSharedModule(filename);
+}
+
+/******************************************************************
+ * Binary Large Object (BLOB) hooks
+ ******************************************************************/
+
+void pgaceLargeObjectGetSecurity(HeapTuple tuple) {
+ if (sepgsqlIsEnabled())
+ sepgsqlLargeObjectGetSecurity(tuple);
+}
+
+void pgaceLargeObjectSetSecurity(HeapTuple tuple, Oid lo_security) {
+ if (sepgsqlIsEnabled())
+ sepgsqlLargeObjectSetSecurity(tuple, lo_security);
+}
+
+void pgaceLargeObjectCreate(Relation rel, HeapTuple tuple) {
+ if (sepgsqlIsEnabled())
+ sepgsqlLargeObjectCreate(rel, tuple);
+}
+
+void pgaceLargeObjectDrop(Relation rel, HeapTuple tuple) {
+ if (sepgsqlIsEnabled())
+ sepgsqlLargeObjectDrop(rel, tuple);
+}
+
+void pgaceLargeObjectRead(Relation rel, HeapTuple tuple) {
+ if (sepgsqlIsEnabled())
+ sepgsqlLargeObjectRead(rel, tuple);
+}
+
+void pgaceLargeObjectWrite(Relation rel, HeapTuple newtup, HeapTuple oldtup) {
+ if (sepgsqlIsEnabled())
+ sepgsqlLargeObjectWrite(rel, newtup, oldtup);
+}
+
+void pgaceLargeObjectTruncate(Relation rel, Oid loid, HeapTuple headtup) {
+ if (sepgsqlIsEnabled())
+ sepgsqlLargeObjectTruncate(rel, loid, headtup);
+}
+
+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;
+}
+
+char *pgaceSecurityLabelCheckValid(char *seclabel)
+{
+ if (sepgsqlIsEnabled())
+ return sepgsqlSecurityLabelCheckValid(seclabel);
+ return seclabel;
+}
+
+char *pgaceSecurityLabelOfLabel(char *new_label)
+{
+ if (sepgsqlIsEnabled())
+ return sepgsqlSecurityLabelOfLabel(new_label);
+ 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 2008-02-04 17:40:05.000000000 +0900
@@ -0,0 +1,587 @@
+/*
+ * 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 "catalog/catalog.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_authid.h"
+#include "catalog/pg_class.h"
+#include "catalog/pg_database.h"
+#include "catalog/pg_language.h"
+#include "catalog/pg_largeobject.h"
+#include "catalog/pg_proc.h"
+#include "catalog/pg_security.h"
+#include "catalog/pg_trigger.h"
+#include "catalog/pg_type.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))
+ elog(ERROR, "SELinux: cache lookup failed for relation %u", 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 __sepgsql_perms_to_common_perms(uint32 perms) {
+ uint32 __perms = 0;
+
+ Assert((perms & ~SEPGSQL_PERMS_ALL) == 0);
+ __perms |= (perms & SEPGSQL_PERMS_USE ? COMMON_DATABASE__GETATTR : 0);
+ __perms |= (perms & SEPGSQL_PERMS_SELECT ? COMMON_DATABASE__GETATTR : 0);
+ __perms |= (perms & SEPGSQL_PERMS_UPDATE ? COMMON_DATABASE__SETATTR : 0);
+ __perms |= (perms & SEPGSQL_PERMS_INSERT ? COMMON_DATABASE__CREATE : 0);
+ __perms |= (perms & SEPGSQL_PERMS_DELETE ? COMMON_DATABASE__DROP : 0);
+ __perms |= (perms & SEPGSQL_PERMS_RELABELFROM ? COMMON_DATABASE__RELABELFROM : 0);
+ __perms |= (perms & SEPGSQL_PERMS_RELABELTO ? COMMON_DATABASE__RELABELTO : 0);
+
+ return __perms;
+}
+
+static uint32 __sepgsql_perms_to_tuple_perms(uint32 perms) {
+ uint32 __perms = 0;
+
+ Assert((perms & ~SEPGSQL_PERMS_ALL) == 0);
+ __perms |= (perms & SEPGSQL_PERMS_USE ? DB_TUPLE__USE : 0);
+ __perms |= (perms & SEPGSQL_PERMS_SELECT ? DB_TUPLE__SELECT : 0);
+ __perms |= (perms & SEPGSQL_PERMS_UPDATE ? DB_TUPLE__UPDATE : 0);
+ __perms |= (perms & SEPGSQL_PERMS_INSERT ? DB_TUPLE__INSERT : 0);
+ __perms |= (perms & SEPGSQL_PERMS_DELETE ? DB_TUPLE__DELETE : 0);
+ __perms |= (perms & SEPGSQL_PERMS_RELABELFROM ? DB_TUPLE__RELABELFROM : 0);
+ __perms |= (perms & SEPGSQL_PERMS_RELABELTO ? DB_TUPLE__RELABELTO : 0);
+
+ return __perms;
+}
+
+char *sepgsqlGetTupleName(Oid relid, HeapTuple tuple, NameData *name)
+{
+ switch (relid) {
+ case AttributeRelationId: {
+ Form_pg_attribute attr = (Form_pg_attribute) GETSTRUCT(tuple);
+ HeapTuple reltup;
+
+ if (IsBootstrapProcessingMode()) {
+ strncpy(NameStr(*name),
+ NameStr(attr->attname),
+ NAMEDATALEN);
+ return NameStr(*name);
+ }
+ reltup = SearchSysCache(RELOID,
+ ObjectIdGetDatum(attr->attrelid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(reltup)) {
+ strncpy(NameStr(*name),
+ NameStr(attr->attname),
+ NAMEDATALEN);
+ return NameStr(*name);
+ }
+ snprintf(NameStr(*name), NAMEDATALEN, "%s.%s",
+ NameStr(((Form_pg_class) GETSTRUCT(reltup))->relname),
+ NameStr(attr->attname));
+ ReleaseSysCache(reltup);
+ return NameStr(*name);
+ }
+ case AuthIdRelationId: {
+ strncpy(NameStr(*name),
+ NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname),
+ NAMEDATALEN);
+ return NameStr(*name);
+ }
+ case RelationRelationId: {
+ strncpy(NameStr(*name),
+ NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname),
+ NAMEDATALEN);
+ return NameStr(*name);
+ }
+ case DatabaseRelationId: {
+ strncpy(NameStr(*name),
+ NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname),
+ NAMEDATALEN);
+ return NameStr(*name);
+ }
+ case LargeObjectRelationId: {
+ snprintf(NameStr(*name), NAMEDATALEN, "loid:%u",
+ ((Form_pg_largeobject) GETSTRUCT(tuple))->loid);
+ return NameStr(*name);
+ }
+ case ProcedureRelationId: {
+ strncpy(NameStr(*name),
+ NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname),
+ NAMEDATALEN);
+ return NameStr(*name);
+ }
+ case TriggerRelationId: {
+ strncpy(NameStr(*name),
+ NameStr(((Form_pg_trigger) GETSTRUCT(tuple))->tgname),
+ NAMEDATALEN);
+ return NameStr(*name);
+ }
+ case TypeRelationId: {
+ snprintf(NameStr(*name), NAMEDATALEN, "pg_type::%s",
+ NameStr(((Form_pg_type) GETSTRUCT(tuple))->typname));
+ return NameStr(*name);
+ }
+ default:
+ if (HeapTupleGetOid(tuple) != InvalidOid) {
+ snprintf(NameStr(*name), NAMEDATALEN, "relid:%u,oid:%u",
+ relid, HeapTupleGetOid(tuple));
+ return NameStr(*name);
+ }
+ break;
+ }
+ 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;
+ *p_perms = __sepgsql_perms_to_tuple_perms(*p_perms);
+ return;
+ }
+ break;
+ }
+ *p_tclass = SECCLASS_DB_COLUMN;
+ *p_perms = __sepgsql_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 loForm = (Form_pg_largeobject) GETSTRUCT(tuple);
+ Relation rel;
+ ScanKeyData skey;
+ SysScanDesc sd;
+ HeapTuple exttup;
+ uint32 perms = 0;
+
+ perms |= (*p_perms & SEPGSQL_PERMS_USE ? DB_BLOB__GETATTR : 0);
+ perms |= (*p_perms & SEPGSQL_PERMS_SELECT ? DB_BLOB__GETATTR : 0);
+ perms |= (*p_perms & SEPGSQL_PERMS_UPDATE ? DB_BLOB__SETATTR | DB_BLOB__WRITE : 0);
+ perms |= (*p_perms & SEPGSQL_PERMS_RELABELFROM ? DB_BLOB__RELABELFROM : 0);
+ perms |= (*p_perms & SEPGSQL_PERMS_READ ? DB_BLOB__READ : 0);
+ perms |= (*p_perms & SEPGSQL_PERMS_WRITE ? DB_BLOB__WRITE : 0);
+
+ if (*p_perms & SEPGSQL_PERMS_INSERT) {
+ perms |= DB_BLOB__SETATTR | DB_BLOB__WRITE;
+ ScanKeyInit(&skey,
+ Anum_pg_largeobject_loid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(loForm->loid));
+ rel = heap_open(LargeObjectRelationId, AccessShareLock);
+ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true,
+ SnapshotSelf, 1, &skey);
+ /* INSERT the first one means create a largeobject */
+ exttup = systable_getnext(sd);
+ if (!HeapTupleIsValid(exttup)) {
+ perms |= DB_BLOB__CREATE;
+ } else if (HeapTupleGetSecurity(tuple) != HeapTupleGetSecurity(exttup)) {
+ elog(ERROR, "SELinux: inconsistent security context specified");
+ }
+ systable_endscan(sd);
+ heap_close(rel, AccessShareLock);
+ }
+
+ if (*p_perms & SEPGSQL_PERMS_DELETE) {
+ bool found = false;
+
+ perms |= DB_BLOB__SETATTR | DB_BLOB__WRITE;
+ ScanKeyInit(&skey,
+ Anum_pg_largeobject_loid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(loForm->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 (loForm->pageno != __pageno) {
+ found = true;
+ break;
+ }
+ }
+ systable_endscan(sd);
+ heap_close(rel, AccessShareLock);
+
+ /*
+ * If this tuple is the last one with given large object,
+ * it means to drop the whole of large object.
+ */
+ if (!found)
+ perms |= DB_BLOB__DROP;
+ }
+
+ /*
+ * SE-PostgreSQL does not allow different security contexts are
+ * held in a single large object.
+ */
+ if (*p_perms & SEPGSQL_PERMS_RELABELTO) {
+ bool found = false;
+
+ perms |= DB_BLOB__RELABELTO;
+ ScanKeyInit(&skey,
+ Anum_pg_largeobject_loid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(loForm->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 (loForm->pageno != __pageno) {
+ found = true;
+ break;
+ }
+ }
+ systable_endscan(sd);
+ heap_close(rel, AccessShareLock);
+
+ if (found)
+ elog(ERROR,
+ "SELinux: It's not possible a part of tuples within"
+ " a single large object to have different security context."
+ " You can use lo_set_security() instead.");
+ }
+ *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 = __sepgsql_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)
+ elog(ERROR, "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 = __sepgsql_perms_to_common_perms(*p_perms);
+ } else {
+ *p_tclass = SECCLASS_DB_TUPLE;
+ *p_perms = __sepgsql_perms_to_tuple_perms(*p_perms);
+ }
+}
+
+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 = __sepgsql_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:
+ perms = __sepgsql_perms_to_tuple_perms(perms);
+ tclass = SECCLASS_DB_TUPLE;
+ break;
+ }
+
+ if (perms) {
+ NameData name;
+
+ if (abort) {
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
+ tcontext,
+ tclass,
+ perms,
+ sepgsqlGetTupleName(tableoid, tuple, &name));
+ } else {
+ rc = sepgsql_avc_permission_noabort(sepgsqlGetClientContext(),
+ tcontext,
+ tclass,
+ perms,
+ sepgsqlGetTupleName(tableoid, tuple, &name));
+ }
+ }
+ 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: { /* pg_largeobject */
+ ScanKeyData skey;
+ SysScanDesc sd;
+ HeapTuple lotup;
+ Oid loid, lo_security = InvalidOid;
+
+ loid = ((Form_pg_largeobject) GETSTRUCT(tuple))->loid;
+ ScanKeyInit(&skey,
+ Anum_pg_largeobject_loid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(loid));
+ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true,
+ SnapshotSelf, 1, &skey);
+ lotup = systable_getnext(sd);
+ if (HeapTupleIsValid(lotup))
+ lo_security = HeapTupleGetSecurity(lotup);
+ systable_endscan(sd);
+ /* Inherit previous page's security context */
+ if (lo_security != InvalidOid)
+ return lo_security;
+ /* compute newly created one */
+ 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-03-06 20:29:42.000000000 +0900
@@ -0,0 +1,1604 @@
+/*
+ * 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/indexing.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_trigger.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"
+
+/* 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 ? SEPGSQL_PERMS_USE : 0);
+ rte->requiredPerms |= (perms & DB_TABLE__SELECT ? SEPGSQL_PERMS_SELECT : 0);
+ rte->requiredPerms |= (perms & DB_TABLE__INSERT ? SEPGSQL_PERMS_INSERT : 0);
+ rte->requiredPerms |= (perms & DB_TABLE__UPDATE ? SEPGSQL_PERMS_UPDATE : 0);
+ rte->requiredPerms |= (perms & DB_TABLE__DELETE ? SEPGSQL_PERMS_DELETE : 0);
+
+ /* for 'pg_largeobject' */
+ if (rte->relid == LargeObjectRelationId && (perms & DB_TABLE__DELETE))
+ rte->requiredPerms |= SEPGSQL_PERMS_WRITE;
+
+ 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)
+{
+ uint32 t_perms = 0;
+
+ /* for table:{ ... } permission */
+ t_perms |= (perms & DB_COLUMN__USE ? DB_TABLE__USE : 0);
+ t_perms |= (perms & DB_COLUMN__SELECT ? DB_TABLE__SELECT : 0);
+ t_perms |= (perms & DB_COLUMN__INSERT ? DB_TABLE__INSERT : 0);
+ t_perms |= (perms & DB_COLUMN__UPDATE ? DB_TABLE__UPDATE : 0);
+ selist = addEvalPgClass(selist, rte, t_perms);
+
+ /* for 'security_context' */
+ if (attno == SecurityAttributeNumber
+ && (perms & (DB_COLUMN__UPDATE | DB_COLUMN__INSERT)))
+ rte->requiredPerms |= SEPGSQL_PERMS_RELABELFROM;
+
+ /* for 'pg_largeobject' */
+ if (rte->relid == LargeObjectRelationId) {
+ if ((perms & DB_COLUMN__SELECT) && attno == Anum_pg_largeobject_data)
+ rte->requiredPerms |= SEPGSQL_PERMS_READ;
+ if ((perms & (DB_COLUMN__UPDATE | DB_COLUMN__INSERT)) && attno > 0)
+ rte->requiredPerms |= SEPGSQL_PERMS_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)
+ elog(ERROR, "SELinux: Var node should not appear 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/use} and column:{select/use} */
+ 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))
+ elog(ERROR, "SELinux: refering to dropped column (relid=%u, attno=%d)",
+ rte->relid, var->varattno);
+ svar = (Var *) tle->expr;
+ }
+ /* table:{select/use} and column:{select/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:
+ elog(ERROR, "SELinux: unexpected 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))
+ elog(ERROR, "SELinux: cache lookup failed for operator %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:
+ elog(NOTICE, "SELinux: node with tag %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))
+ elog(ERROR, "SELinux: cache lookup failed for relation %u", 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))
+ elog(ERROR, "SELinux: cache lookup failed for attribute %d of relation %s",
+ 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 {
+ elog(ERROR, "SELinux: unexpected 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;
+
+ 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 = rte->requiredPerms & SEPGSQL_PERMS_ALL;
+
+ /* 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 &= ~SEPGSQL_PERMS_ALL;
+ }
+
+ return selist;
+}
+
+static List *__checkSelectTargets(List *selist, Query *query, Node *node)
+{
+ if (node == NULL)
+ return selist;
+
+ if (IsA(node, RangeTblRef)) {
+ RangeTblRef *rtr = (RangeTblRef *) node;
+ RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
+
+ if (rte->rtekind == RTE_RELATION)
+ selist = addEvalPgClass(selist, rte, DB_TABLE__SELECT);
+ } else if (IsA(node, JoinExpr)) {
+ JoinExpr *j = (JoinExpr *) node;
+
+ selist = __checkSelectTargets(selist, query, j->larg);
+ selist = __checkSelectTargets(selist, query, j->rarg);
+ } else if (IsA(node, FromExpr)) {
+ FromExpr *fm = (FromExpr *)node;
+ ListCell *l;
+
+ foreach (l, fm->fromlist)
+ selist = __checkSelectTargets(selist, query, lfirst(l));
+ } else {
+ elog(ERROR, "SELinux: unexpected node type (%d) at Query->fromlist", nodeTag(node));
+ }
+ 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);
+
+ switch (cmdType) {
+ case CMD_SELECT:
+ selist = __checkSelectTargets(selist, query, (Node *)query->jointree);
+
+ case CMD_UPDATE:
+ case CMD_INSERT:
+ foreach (l, query->targetList) {
+ TargetEntry *tle = lfirst(l);
+ bool is_security_attr = false;
+ uint32 perms;
+ Assert(IsA(tle, TargetEntry));
+
+ if (tle->resjunk && tle->resname
+ && !strcmp(tle->resname, SECURITY_SYSATTR_NAME))
+ is_security_attr = true;
+
+ /* pure junk target entries */
+ if (tle->resjunk && !is_security_attr) {
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) tle->expr, WKFLAG_INTERNAL_USE);
+ continue;
+ }
+
+ selist = sepgsqlWalkExpr(selist, qc, (Node *) tle->expr, 0);
+
+ if (cmdType == CMD_SELECT)
+ continue;
+
+ rte = list_nth(query->rtable, query->resultRelation - 1);
+ Assert(IsA(rte, RangeTblEntry) && rte->rtekind==RTE_RELATION);
+ perms = (cmdType == CMD_UPDATE ? DB_COLUMN__UPDATE : DB_COLUMN__INSERT);
+
+ selist = addEvalPgAttribute(selist,
+ rte,
+ is_security_attr ? SecurityAttributeNumber : tle->resno,
+ perms);
+ }
+ break;
+
+ case CMD_DELETE:
+ rte = list_nth(query->rtable, query->resultRelation - 1);
+ Assert(IsA(rte, RangeTblEntry) && rte->rtekind==RTE_RELATION);
+ selist = addEvalPgClass(selist, rte, DB_TABLE__DELETE);
+ break;
+
+ default:
+ elog(ERROR, "SELinux: unexpected cmdType = %d", cmdType);
+ break;
+ }
+
+ /* 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 */
+ // MEMO: no need to walk it again, it is checked as junk entries
+ //selist = sepgsqlWalkExpr(selist, qc, (Node *) query->sortClause, WKFLAG_INTERNAL_USE);
+
+ /* permission mark on the GROUP BY/HAVING clause */
+ // MEMO: no need to walk it again, it is checked as junk entries
+ //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 &= ~SEPGSQL_PERMS_ALL;
+ }
+
+ 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:
+ elog(ERROR, "SELinux: unexpected rtekinf = %d at 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 {
+ elog(ERROR, "SELinux: unexpected node type (%d) at 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 {
+ elog(ERROR, "SELinux: 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);
+
+ elog(NOTICE, "SELinux: TRUNCATE %s is replaced unconditional DELETE",
+ 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:
+ elog(ERROR, "SELinux: unexpected command type (%d)", 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;
+ NameData name;
+
+ /* prevent to modify pg_security directly */
+ if (relid == SecurityRelationId
+ && (perms & (DB_TABLE__UPDATE | DB_TABLE__INSERT | DB_TABLE__DELETE)) != 0)
+ elog(ERROR, "SELinux: user cannot modify pg_security directly");
+
+ /* check table:{required permissions} */
+ tuple = SearchSysCache(RELOID,
+ ObjectIdGetDatum(relid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "SELinux: relation (oid=%u) does not exist", relid);
+ pgclass = (Form_pg_class) GETSTRUCT(tuple);
+
+ if (pgclass->relkind != RELKIND_RELATION) {
+ ReleaseSysCache(tuple);
+ return;
+ }
+
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_TABLE,
+ perms,
+ sepgsqlGetTupleName(RelationRelationId, tuple, &name));
+ ReleaseSysCache(tuple);
+}
+
+static void verifyPgAttributePerms(Oid relid, bool inh, AttrNumber attno, uint32 perms)
+{
+ HeapTuple tuple;
+ Form_pg_class classForm;
+ Form_pg_attribute attrForm;
+ NameData name;
+
+ tuple = SearchSysCache(RELOID,
+ ObjectIdGetDatum(relid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "SELinux: relation (oid=%u) does not exist", 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, &name));
+ }
+ systable_endscan(scan);
+ heap_close(rel, AccessShareLock);
+
+ return;
+ }
+
+ tuple = SearchSysCache(ATTNUM,
+ ObjectIdGetDatum(relid),
+ Int16GetDatum(attno),
+ 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "SELinux: cache lookup failed for attribute %d of relation %u", attno, relid);
+
+ /* check column:{required permissions} */
+ sepgsql_avc_permission(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_COLUMN,
+ perms,
+ sepgsqlGetTupleName(AttributeRelationId, tuple, &name));
+ ReleaseSysCache(tuple);
+}
+
+static void verifyPgProcPerms(Oid funcid, uint32 perms)
+{
+ HeapTuple tuple;
+ NameData name;
+ Oid newcon;
+
+ tuple = SearchSysCache(PROCOID,
+ ObjectIdGetDatum(funcid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "SELinux: 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, &name));
+
+ /* 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))
+ elog(ERROR, "SELinux: cache lookup failed for attribute %s of relation %u",
+ attname, lfirst_oid(l));
+ 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))
+ elog(ERROR, "SELinux: cache lookup failed for attribute %d of relation %u",
+ se->a.attno, se->a.relid);
+ 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:
+ elog(ERROR, "SELinux: unexpected SEvalItem (tclass: %d)", 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, List *attNumList, HeapTuple tuple)
+{
+ uint32 perms = SEPGSQL_PERMS_SELECT;
+
+ /* for 'pg_largeobject' */
+ if (RelationGetRelid(rel) == LargeObjectRelationId) {
+ ListCell *l;
+
+ foreach (l, attNumList) {
+ AttrNumber attnum = lfirst_int(l);
+ if (attnum == Anum_pg_largeobject_data) {
+ perms |= SEPGSQL_PERMS_READ;
+ break;
+ }
+ }
+ }
+ return sepgsqlCheckTuplePerms(rel, tuple, NULL, perms, false);
+}
+
+/* ----------------------------------------------------------
+ * 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:
+ elog(ERROR, "SELinux: unexpected 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:
+ elog(ERROR, "SELinux: unexpected 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:
+ elog(ERROR, "SELinux: unexpected 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 2008-01-28 16:14:33.000000000 +0900
+++ sepgsql/src/include/pg_config.h.in 2008-01-28 16:19:11.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-02-04 17:40:05.000000000 +0900
@@ -0,0 +1,140 @@
+#ifndef SEPGSQL_H
+#define SEPGSQL_H
+
+/* system catalogs */
+#include "catalog/pg_security.h"
+#include "lib/stringinfo.h"
+#include "nodes/nodes.h"
+#include "nodes/parsenodes.h"
+#include "storage/large_object.h"
+
+#include <selinux/selinux.h>
+#include <selinux/flask.h>
+#include <selinux/av_permissions.h>
+
+/*
+ * Permission codes of internal representation
+ */
+#define SEPGSQL_PERMS_USE (1UL << (N_ACL_RIGHTS + 0))
+#define SEPGSQL_PERMS_SELECT (1UL << (N_ACL_RIGHTS + 1))
+#define SEPGSQL_PERMS_UPDATE (1UL << (N_ACL_RIGHTS + 2))
+#define SEPGSQL_PERMS_INSERT (1UL << (N_ACL_RIGHTS + 3))
+#define SEPGSQL_PERMS_DELETE (1UL << (N_ACL_RIGHTS + 4))
+#define SEPGSQL_PERMS_RELABELFROM (1UL << (N_ACL_RIGHTS + 5))
+#define SEPGSQL_PERMS_RELABELTO (1UL << (N_ACL_RIGHTS + 6))
+#define SEPGSQL_PERMS_READ (1UL << (N_ACL_RIGHTS + 7))
+#define SEPGSQL_PERMS_WRITE (1UL << (N_ACL_RIGHTS + 8))
+#define SEPGSQL_PERMS_ALL ((SEPGSQL_PERMS_WRITE << 1) - SEPGSQL_PERMS_USE)
+
+/*
+ * 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, List *attnumlist, HeapTuple tuple);
+
+/* Loadable shared library module hooks */
+extern void sepgsqlLoadSharedModule(const char *filename);
+
+/* Binary Large Object (BLOB) hooks */
+extern void sepgsqlLargeObjectGetSecurity(HeapTuple tuple);
+extern void sepgsqlLargeObjectSetSecurity(HeapTuple tuple, Oid lo_security);
+extern void sepgsqlLargeObjectCreate(Relation rel, HeapTuple tuple);
+extern void sepgsqlLargeObjectDrop(Relation rel, HeapTuple tuple);
+extern void sepgsqlLargeObjectRead(Relation rel, HeapTuple tuple);
+extern void sepgsqlLargeObjectWrite(Relation rel, HeapTuple newtup, HeapTuple oldtup);
+extern void sepgsqlLargeObjectTruncate(Relation rel, Oid loid, HeapTuple headtup);
+extern void sepgsqlLargeObjectImport(void);
+extern void sepgsqlLargeObjectExport(void);
+
+/* Security Label hooks */
+extern char *sepgsqlSecurityLabelIn(char *context);
+extern char *sepgsqlSecurityLabelOut(char *context);
+extern char *sepgsqlSecurityLabelCheckValid(char *context);
+extern char *sepgsqlSecurityLabelOfLabel(char *context);
+
+/* 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_noabort(Oid ssid, Oid tsid, uint16 tclass,
+ uint32 perms, char *objname);
+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, NameData *name);
+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 */