sepostgresql/sepostgresql-sepgsql-8.3.3-2.patch
2008-09-23 02:04:13 +00:00

11475 lines
325 KiB
Diff

diff -rpNU3 base/configure sepgsql/configure
--- base/configure 2008-06-12 23:42:03.000000000 +0900
+++ sepgsql/configure 2008-06-14 02:36:58.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:
@@ -4619,6 +4620,120 @@ 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
#
@@ -26006,6 +26121,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 base/configure.in sepgsql/configure.in
--- base/configure.in 2008-06-12 23:42:03.000000000 +0900
+++ sepgsql/configure.in 2008-06-14 02:36:58.000000000 +0900
@@ -626,6 +626,21 @@ 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",
+ [Enables system column for security attribute support])
+ AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1,
+ [Enables SE-PostgreSQL feature])
+ AC_SUBST(enable_selinux),
+ AC_MSG_ERROR("--enable-selinux requires libselinux."))
+fi
+
+#
# Elf
#
diff -rpNU3 base/contrib/sepgsql_policy/Makefile sepgsql/contrib/sepgsql_policy/Makefile
--- base/contrib/sepgsql_policy/Makefile 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/contrib/sepgsql_policy/Makefile 2008-06-26 11:44:44.000000000 +0900
@@ -0,0 +1,40 @@
+#
+# contrib/sepgsql_policy/Makefile
+# Makefile of security policy module for SE-PostgreSQL
+#
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+
+policy_basedir := /usr/share/selinux
+policy_makefile := $(policy_basedir)/devel/Makefile
+policy_types := targeted mls
+policy := $(strip $(shell $(AWK) -F= '/^SELINUXTYPE/{ print $$2 }' /etc/selinux/config))
+package_names := sepostgresql sepostgresql-devel
+prefix_ptn := "s/%%__prefix__%%/$(shell echo $(prefix)|sed 's/\//\\\//g')/g"
+bindir_ptn := "s/%%__bindir__%%/$(shell echo $(bindir)|sed 's/\//\\\//g')/g"
+libdir_ptn := "s/%%__libdir__%%/$(shell echo $(pkglibdir)|sed 's/\//\\\//g')/g"
+
+all:
+ $(foreach pkg, $(package_names), $(foreach p, $(policy_types), $(MAKE) $(MAKEOVERRIDES) policy=$(p) $(pkg).pp;))
+ $(foreach pkg, $(package_names), test -e $(pkg).pp.$(policy) && ln -sf $(pkg).pp.$(policy) $(pkg).pp;)
+
+.install-policy:
+ test -d $(DESTDIR)$(policy_basedir)/$(policy) || install -d $(DESTDIR)$(policy_basedir)/$(policy)
+ $(foreach pkg, $(package_names), install -p -m 644 $(pkg).pp.$(policy) $(DESTDIR)$(policy_basedir)/$(policy)/$(pkg).pp;)
+
+install: all
+ $(foreach p, $(policy_types), $(MAKE) $(MAKEOVERRIDES) policy=$(p) .install-policy;)
+
+%.pp: %.te %.if %.fc
+ $(MAKE) NAME=$(policy) -f $(policy_makefile) $@
+ mv $@ $@.$(policy)
+
+sepostgresql-devel.fc: sepostgresql.fc.template
+ cat $< | grep -v ^/var | sed -e $(prefix_ptn) -e $(bindir_ptn) -e $(libdir_ptn) > $@
+
+sepostgresql.fc: sepostgresql.fc.template
+ cat $< | sed -e $(prefix_ptn) -e $(bindir_ptn) -e $(libdir_ptn) > $@
+
+clean:
+ $(MAKE) -f $(policy_makefile) clean
+ rm -f *.pp.* *.fc
diff -rpNU3 base/contrib/sepgsql_policy/README sepgsql/contrib/sepgsql_policy/README
--- base/contrib/sepgsql_policy/README 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/contrib/sepgsql_policy/README 2008-06-19 13:12:15.000000000 +0900
@@ -0,0 +1,50 @@
+The security policy module of SE-PostgreSQL
+-------------------------------------------
+
+o Introduction
+
+ We provide two kind of security policy modules.
+
+ One is "sepostgresql.pp" which contains full-set of security policy
+ and suitable for legacy base policy (selinux-policy-3.4.1, or prior).
+
+ The other is "sepostgresql-devel.pp" which provides several booleans
+ for developers, and suitable for newer base policy (selinux-policy-3.4.2,
+ or later).
+
+ In the selinux-policy-3.4.2, most part of the policy got upstreamed.
+ So, we don't need to install "sepostgresql.pp" explicitly on the newer
+ base security policy.
+
+ If you need to run regression test, or (don't) want to generate access
+ logs, install "sepostgresql-devel.pp" and turn on/off booleans.
+
+o Build & Installation
+
+ $ cd contrib/sepgsql_policy
+ $ make
+ $ su
+ # /usr/sbin/semodule -i sepostgresql-devel.pp
+ or
+ # /usr/sbin/semodule -i sepostgresql.pp
+
+o Booleans
+
+- sepgsql_enable_users_ddl (default: on)
+ This boolean enables to control to execute DDL statement come from
+ confined users.
+
+- sepgsql_enable_auditallow (default: off)
+ This boolean enables to generate access allow logs except for tuple
+ level.
+
+- sepgsql_enable_auditdeny (default: on)
+ This boolean enables to generata access denied logs except for tuple
+ level.
+
+- sepgsql_regression_test_mode (default: off)
+ This boolean provides several permission to run regression test on
+ your home directory. It enables to load shared library files deployed
+ on home directory.
+ However, we don't recommend it to turn on in the operation phase.
+
diff -rpNU3 base/contrib/sepgsql_policy/sepostgresql-devel.if sepgsql/contrib/sepgsql_policy/sepostgresql-devel.if
--- base/contrib/sepgsql_policy/sepostgresql-devel.if 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/contrib/sepgsql_policy/sepostgresql-devel.if 2008-06-19 13:12:15.000000000 +0900
@@ -0,0 +1 @@
+## There are no interface declaration
diff -rpNU3 base/contrib/sepgsql_policy/sepostgresql-devel.te sepgsql/contrib/sepgsql_policy/sepostgresql-devel.te
--- base/contrib/sepgsql_policy/sepostgresql-devel.te 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/contrib/sepgsql_policy/sepostgresql-devel.te 2008-06-26 11:44:44.000000000 +0900
@@ -0,0 +1,82 @@
+policy_module(sepostgresql-devel, 3.11)
+
+gen_require(`
+ class db_database all_db_database_perms;
+ class db_table all_db_table_perms;
+ class db_procedure all_db_procedure_perms;
+ class db_column all_db_column_perms;
+ class db_tuple all_db_tuple_perms;
+ class db_blob all_db_blob_perms;
+
+ attribute domain, home_type;
+ attribute sepgsql_client_type;
+ attribute sepgsql_unconfined_type;
+
+ attribute sepgsql_database_type;
+ attribute sepgsql_table_type;
+ attribute sepgsql_sysobj_table_type;
+ attribute sepgsql_procedure_type;
+ attribute sepgsql_blob_type;
+ attribute sepgsql_module_type;
+')
+
+#################################
+#
+# SE-PostgreSQL Declarations
+#
+
+## <desc>
+## <p>
+## Allow to generate auditallow logs
+## </p>
+## </desc>
+gen_tunable(sepgsql_enable_auditallow, false)
+
+## <desc>
+## <p>
+## Allow to generate auditdeny logs
+## </p>
+## </desc>
+gen_tunable(sepgsql_enable_auditdeny, true)
+
+## <desc>
+## <p>
+## Allow widespread permissions for regression test
+## Don't set TRUE on operation phase
+## </p>
+## </desc>
+gen_tunable(sepgsql_regression_test_mode, false)
+
+########################################
+#
+# SE-PostgreSQL audit switch for debugging
+#
+tunable_policy(`sepgsql_enable_auditallow',`
+ auditallow domain sepgsql_database_type : db_database *;
+ auditallow domain sepgsql_table_type : db_table *;
+ auditallow domain sepgsql_table_type : db_column *;
+ auditallow domain sepgsql_procedure_type : db_procedure *;
+ auditallow domain sepgsql_blob_type : db_blob *;
+ auditallow domain sepgsql_module_type : db_database { install_module };
+ auditallow sepgsql_database_type sepgsql_module_type : db_database { load_module };
+')
+
+tunable_policy(`! sepgsql_enable_auditdeny',`
+ dontaudit domain sepgsql_database_type : db_database *;
+ dontaudit domain sepgsql_table_type : db_table *;
+ dontaudit domain sepgsql_table_type : db_column *;
+ dontaudit domain sepgsql_procedure_type : db_procedure *;
+ dontaudit domain sepgsql_blob_type : db_blob *;
+ dontaudit domain sepgsql_module_type : db_database { install_module };
+ dontaudit sepgsql_database_type sepgsql_module_type : db_database { load_module };
+')
+
+########################################
+#
+# SE-PostgreSQL regression test mode switch
+#
+tunable_policy(`sepgsql_regression_test_mode',`
+ allow sepgsql_client_type home_type : db_database { install_module };
+ allow sepgsql_unconfined_type home_type : db_database { install_module };
+ allow sepgsql_database_type home_type : db_database { load_module };
+')
diff -rpNU3 base/contrib/sepgsql_policy/sepostgresql.fc.template sepgsql/contrib/sepgsql_policy/sepostgresql.fc.template
--- base/contrib/sepgsql_policy/sepostgresql.fc.template 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/contrib/sepgsql_policy/sepostgresql.fc.template 2008-06-14 19:20:56.000000000 +0900
@@ -0,0 +1,15 @@
+#
+# SE-PostgreSQL install path
+#
+%%__prefix__%%(/.*)? -- gen_context(system_u:object_r:usr_t,s0)
+
+%%__bindir__%%/(se)?postgres -- gen_context(system_u:object_r:postgresql_exec_t,s0)
+%%__bindir__%%/(se)?pg_ctl -- gen_context(system_u:object_r:initrc_exec_t,s0)
+%%__bindir__%%/initdb(\.sepgsql)? -- gen_context(system_u:object_r:postgresql_exec_t,s0)
+%%__bindir__%%(/.*)? -- gen_context(system_u:object_r:bin_t,s0)
+
+%%__libdir__%%(/.*)? -- gen_context(system_u:object_r:lib_t,s0)
+
+/var/lib/sepgsql(/.*)? gen_context(system_u:object_r:postgresql_db_t,s0)
+/var/lib/sepgsql/pgstartup\.log gen_context(system_u:object_r:postgresql_log_t,s0)
+/var/log/sepostgresql\.log.* -- gen_context(system_u:object_r:postgresql_log_t,s0)
diff -rpNU3 base/contrib/sepgsql_policy/sepostgresql.if sepgsql/contrib/sepgsql_policy/sepostgresql.if
--- base/contrib/sepgsql_policy/sepostgresql.if 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/contrib/sepgsql_policy/sepostgresql.if 2008-06-26 11:44:44.000000000 +0900
@@ -0,0 +1,259 @@
+#######################################
+## <summary>
+## The userdomain template for the SE-PostgreSQL.
+## </summary>
+## <desc>
+## This template creates a delivered types which are used
+## for given userdomains.
+## </desc>
+## <param name="userdomain_prefix">
+## <summary>
+## The prefix of the user domain (e.g., user
+## is the prefix for user_t).
+## </summary>
+## </param>
+## <param name="user_domain">
+## <summary>
+## The type of the user domain.
+## </summary>
+## </param>
+## <param name="user_role">
+## <summary>
+## The role associated with the user domain.
+## </summary>
+## </param>
+#
+template(`sepostgresql_userdom_template',`
+ gen_require(`
+ class db_database all_db_database_perms;
+ class db_table all_db_table_perms;
+ class db_procedure all_db_procedure_perms;
+ class db_column all_db_column_perms;
+ class db_tuple all_db_tuple_perms;
+ class db_blob all_db_blob_perms;
+
+ attribute sepgsql_client_type;
+ attribute sepgsql_database_type;
+ attribute sepgsql_sysobj_table_type;
+
+ type sepgsql_trusted_proc_t;
+ type sepgsql_trusted_proc_exec_t;
+ ')
+
+ ########################################
+ #
+ # Declarations
+ #
+
+ typeattribute $2 sepgsql_client_type;
+
+ type $1_sepgsql_blob_t;
+ postgresql_blob_object($1_sepgsql_blob_t)
+
+ type $1_sepgsql_proc_exec_t;
+ postgresql_procedure_object($1_sepgsql_proc_exec_t)
+
+ type $1_sepgsql_sysobj_t;
+ postgresql_system_table_object($1_sepgsql_sysobj_t)
+
+ type $1_sepgsql_table_t;
+ postgresql_table_object($1_sepgsql_table_t)
+
+ role $3 types sepgsql_trusted_proc_t;
+
+ ##############################
+ #
+ # Client local policy
+ #
+
+ tunable_policy(`sepgsql_enable_users_ddl',`
+ allow $2 $1_sepgsql_table_t : db_table { create drop };
+ type_transition $2 sepgsql_database_type:db_table $1_sepgsql_table_t;
+
+ allow $2 $1_sepgsql_table_t : db_column { create drop };
+
+ allow $2 $1_sepgsql_sysobj_t : db_tuple { update insert delete };
+ type_transition $2 sepgsql_sysobj_table_type:db_tuple $1_sepgsql_sysobj_t;
+ ')
+
+ allow $2 $1_sepgsql_table_t : db_table { getattr setattr use select update insert delete };
+ allow $2 $1_sepgsql_table_t : db_column { getattr setattr use select update insert };
+ allow $2 $1_sepgsql_table_t : db_tuple { use select update insert delete };
+ allow $2 $1_sepgsql_sysobj_t : db_tuple { use select };
+
+ allow $2 $1_sepgsql_proc_exec_t : db_procedure { create drop getattr setattr execute };
+ type_transition $2 sepgsql_database_type:db_procedure $1_sepgsql_proc_exec_t;
+
+ allow $2 $1_sepgsql_blob_t : db_blob { create drop getattr setattr read write };
+ type_transition $2 sepgsql_database_type:db_blob $1_sepgsql_blob_t;
+
+ allow $2 sepgsql_trusted_proc_t:process transition;
+ type_transition $2 sepgsql_trusted_proc_exec_t:process sepgsql_trusted_proc_t;
+')
+
+########################################
+## <summary>
+## Marks as a SE-PostgreSQL loadable shared library module
+## </summary>
+## <param name="type">
+## <summary>
+## Type marked as a database object type.
+## </summary>
+## </param>
+#
+interface(`sepostgresql_loadable_module',`
+ gen_require(`
+ attribute sepgsql_module_type;
+ ')
+
+ typeattribute $1 sepgsql_module_type;
+')
+
+########################################
+## <summary>
+## Marks as a SE-PostgreSQL database object type
+## </summary>
+## <param name="type">
+## <summary>
+## Type marked as a database object type.
+## </summary>
+## </param>
+#
+interface(`sepostgresql_database_object',`
+ gen_require(`
+ attribute sepgsql_database_type;
+ ')
+
+ typeattribute $1 sepgsql_database_type;
+')
+
+########################################
+## <summary>
+## Marks as a SE-PostgreSQL table/column/tuple object type
+## </summary>
+## <param name="type">
+## <summary>
+## Type marked as a table/column/tuple object type.
+## </summary>
+## </param>
+#
+interface(`sepostgresql_table_object',`
+ gen_require(`
+ attribute sepgsql_table_type;
+ ')
+
+ typeattribute $1 sepgsql_table_type;
+')
+
+########################################
+## <summary>
+## Marks as a SE-PostgreSQL system table/column/tuple object type
+## </summary>
+## <param name="type">
+## <summary>
+## Type marked as a table/column/tuple object type.
+## </summary>
+## </param>
+#
+interface(`sepostgresql_system_table_object',`
+ gen_require(`
+ attribute sepgsql_table_type;
+ attribute sepgsql_sysobj_table_type;
+ ')
+
+ typeattribute $1 sepgsql_table_type;
+ typeattribute $1 sepgsql_sysobj_table_type;
+')
+
+########################################
+## <summary>
+## Marks as a SE-PostgreSQL procedure object type
+## </summary>
+## <param name="type">
+## <summary>
+## Type marked as a database object type.
+## </summary>
+## </param>
+#
+interface(`sepostgresql_procedure_object',`
+ gen_require(`
+ attribute sepgsql_procedure_type;
+ ')
+
+ typeattribute $1 sepgsql_procedure_type;
+')
+
+########################################
+## <summary>
+## Marks as a SE-PostgreSQL binary large object type
+## </summary>
+## <param name="type">
+## <summary>
+## Type marked as a database binary large object type.
+## </summary>
+## </param>
+#
+interface(`sepostgresql_blob_object',`
+ gen_require(`
+ attribute sepgsql_blob_type;
+ ')
+
+ typeattribute $1 sepgsql_blob_type;
+')
+
+########################################
+## <summary>
+## Allow the specified domain unprivileged accesses to unifined database objects
+## managed by SE-PostgreSQL,
+## </summary>
+## <param name="domain">
+## <summary>
+## Domain allowed access.
+## </summary>
+## </param>
+#
+interface(`sepostgresql_unpriv_client',`
+ gen_require(`
+ class db_table all_db_table_perms;
+ class db_procedure all_db_procedure_perms;
+ class db_blob all_db_blob_perms;
+
+ attribute sepgsql_client_type;
+ attribute sepgsql_database_type;
+
+ type sepgsql_table_t;
+ type sepgsql_proc_t;
+ type sepgsql_blob_t;
+
+ type sepgsql_trusted_proc_t;
+ type sepgsql_trusted_proc_exec_t;
+ ')
+
+ typeattribute $1 sepgsql_client_type;
+
+ type_transition $1 sepgsql_database_type:db_table sepgsql_table_t;
+ type_transition $1 sepgsql_database_type:db_procedure sepgsql_proc_t;
+ type_transition $1 sepgsql_database_type:db_blob sepgsql_blob_t;
+
+ type_transition $1 sepgsql_trusted_proc_exec_t:process sepgsql_trusted_proc_t;
+ allow $1 sepgsql_trusted_proc_t:process transition;
+')
+
+########################################
+## <summary>
+## Allow the specified domain unconfined accesses to any database objects
+## managed by SE-PostgreSQL,
+## </summary>
+## <param name="domain">
+## <summary>
+## Domain allowed access.
+## </summary>
+## </param>
+#
+interface(`sepostgresql_unconfined',`
+ gen_require(`
+ attribute sepgsql_unconfined_type;
+ ')
+
+ typeattribute $1 sepgsql_unconfined_type;
+')
diff -rpNU3 base/contrib/sepgsql_policy/sepostgresql.te sepgsql/contrib/sepgsql_policy/sepostgresql.te
--- base/contrib/sepgsql_policy/sepostgresql.te 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/contrib/sepgsql_policy/sepostgresql.te 2008-06-26 13:45:05.000000000 +0900
@@ -0,0 +1,308 @@
+policy_module(sepostgresql, 3.11)
+
+gen_require(`
+ class db_database all_db_database_perms;
+ class db_table all_db_table_perms;
+ class db_procedure all_db_procedure_perms;
+ class db_column all_db_column_perms;
+ class db_tuple all_db_tuple_perms;
+ class db_blob all_db_blob_perms;
+
+ attribute domain, home_type;
+ type postgresql_t, unlabeled_t;
+
+ role system_r;
+')
+
+#################################
+#
+# SE-PostgreSQL Declarations
+#
+
+## <desc>
+## <p>
+## Allow to generate auditallow logs
+## </p>
+## </desc>
+gen_tunable(sepgsql_enable_auditallow, false)
+
+## <desc>
+## <p>
+## Allow to generate auditdeny logs
+## </p>
+## </desc>
+gen_tunable(sepgsql_enable_auditdeny, true)
+
+## <desc>
+## <p>
+## Allow unprivileged users to execute DDL statement
+## </p>
+## </desc>
+gen_tunable(sepgsql_enable_users_ddl, true)
+
+## <desc>
+## <p>
+## Allow widespread permissions for regression test
+## Don't set TRUE on operation phase
+## </p>
+## </desc>
+gen_tunable(sepgsql_regression_test_mode, false)
+
+# database clients attribute
+attribute sepgsql_client_type;
+attribute sepgsql_unconfined_type;
+
+# database objects attribute
+attribute sepgsql_database_type;
+attribute sepgsql_table_type;
+attribute sepgsql_sysobj_table_type;
+attribute sepgsql_procedure_type;
+attribute sepgsql_blob_type;
+attribute sepgsql_module_type;
+
+# database object types
+type sepgsql_blob_t;
+sepostgresql_blob_object(sepgsql_blob_t)
+
+type sepgsql_db_t;
+sepostgresql_database_object(sepgsql_db_t)
+
+type sepgsql_fixed_table_t;
+sepostgresql_table_object(sepgsql_fixed_table_t)
+
+type sepgsql_proc_t;
+sepostgresql_procedure_object(sepgsql_proc_t)
+
+type sepgsql_ro_blob_t;
+sepostgresql_blob_object(sepgsql_ro_blob_t)
+
+type sepgsql_ro_table_t;
+sepostgresql_table_object(sepgsql_ro_table_t)
+
+type sepgsql_secret_blob_t;
+sepostgresql_blob_object(sepgsql_secret_blob_t)
+
+type sepgsql_secret_table_t;
+sepostgresql_table_object(sepgsql_secret_table_t)
+
+type sepgsql_sysobj_t;
+sepostgresql_system_table_object(sepgsql_sysobj_t)
+
+type sepgsql_table_t;
+sepostgresql_table_object(sepgsql_table_t)
+
+type sepgsql_trusted_proc_exec_t;
+sepostgresql_procedure_object(sepgsql_trusted_proc_exec_t)
+
+# Trusted Procedure Domain
+type sepgsql_trusted_proc_t;
+domain_type(sepgsql_trusted_proc_t)
+sepostgresql_unconfined(sepgsql_trusted_proc_t)
+role system_r types sepgsql_trusted_proc_t;
+
+########################################
+#
+# SE-PostgreSQL Local Policy
+#
+allow postgresql_t self:netlink_selinux_socket create_socket_perms;
+selinux_get_enforce_mode(postgresql_t)
+selinux_validate_context(postgresql_t)
+selinux_compute_access_vector(postgresql_t)
+selinux_compute_create_context(postgresql_t)
+selinux_compute_relabel_context(postgresql_t)
+seutil_libselinux_linked(postgresql_t)
+
+allow postgresql_t sepgsql_database_type:db_database *;
+type_transition postgresql_t postgresql_t:db_database sepgsql_db_t;
+
+allow postgresql_t sepgsql_module_type:db_database install_module;
+allow postgresql_t sepgsql_table_type:{ db_table db_column db_tuple } *;
+allow postgresql_t sepgsql_procedure_type:db_procedure *;
+allow postgresql_t sepgsql_blob_type:db_blob *;
+
+# server specific type transitions
+type_transition postgresql_t sepgsql_database_type:db_table sepgsql_sysobj_t;
+type_transition postgresql_t sepgsql_database_type:db_procedure sepgsql_proc_t;
+type_transition postgresql_t sepgsql_database_type:db_blob sepgsql_blob_t;
+
+# Database/Loadable module
+allow sepgsql_database_type sepgsql_module_type:db_database load_module;
+
+########################################
+#
+# Rules common to all clients
+#
+
+# Client domain constraint
+allow sepgsql_client_type sepgsql_db_t:db_database { getattr access get_param set_param };
+type_transition sepgsql_client_type sepgsql_client_type:db_database sepgsql_db_t;
+
+allow sepgsql_client_type sepgsql_fixed_table_t:db_table { getattr use select insert };
+allow sepgsql_client_type sepgsql_fixed_table_t:db_column { getattr use select insert };
+allow sepgsql_client_type sepgsql_fixed_table_t:db_tuple { use select insert };
+
+allow sepgsql_client_type sepgsql_table_t:db_table { getattr use select update insert delete };
+allow sepgsql_client_type sepgsql_table_t:db_column { getattr use select update insert };
+allow sepgsql_client_type sepgsql_table_t:db_tuple { use select update insert delete };
+
+allow sepgsql_client_type sepgsql_ro_table_t:db_table { getattr use select };
+allow sepgsql_client_type sepgsql_ro_table_t:db_column { getattr use select };
+allow sepgsql_client_type sepgsql_ro_table_t:db_tuple { use select };
+
+allow sepgsql_client_type sepgsql_secret_table_t:db_table getattr;
+allow sepgsql_client_type sepgsql_secret_table_t:db_column getattr;
+
+allow sepgsql_client_type sepgsql_sysobj_t:db_table { getattr use select };
+allow sepgsql_client_type sepgsql_sysobj_t:db_column { getattr use select };
+allow sepgsql_client_type sepgsql_sysobj_t:db_tuple { use select };
+
+allow sepgsql_client_type sepgsql_proc_t:db_procedure { getattr execute };
+allow sepgsql_client_type sepgsql_trusted_proc_t:db_procedure { getattr execute entrypoint };
+
+allow sepgsql_client_type sepgsql_blob_t:db_blob { create drop getattr setattr read write };
+allow sepgsql_client_type sepgsql_ro_blob_t:db_blob { getattr read };
+allow sepgsql_client_type sepgsql_secret_blob_t:db_blob getattr;
+
+tunable_policy(`sepgsql_enable_users_ddl',`
+ allow sepgsql_client_type sepgsql_table_t:db_table { create drop setattr };
+ allow sepgsql_client_type sepgsql_table_t:db_column { create drop setattr };
+ allow sepgsql_client_type sepgsql_sysobj_t:db_tuple { update insert delete };
+')
+
+########################################
+#
+# Unconfined access to this module
+#
+
+allow sepgsql_unconfined_type sepgsql_database_type:db_database *;
+allow sepgsql_unconfined_type sepgsql_table_type:{ db_table db_column db_tuple } *;
+allow sepgsql_unconfined_type sepgsql_blob_type:db_blob *;
+allow sepgsql_unconfined_type { sepgsql_proc_t sepgsql_trusted_proc_t }:db_procedure *;
+allow sepgsql_unconfined_type sepgsql_procedure_type:db_procedure { create drop getattr setattr relabelfrom relabelto };
+allow sepgsql_unconfined_type sepgsql_module_type:db_database install_module;
+
+type_transition sepgsql_unconfined_type sepgsql_unconfined_type:db_database sepgsql_db_t;
+type_transition sepgsql_unconfined_type sepgsql_database_type:db_table sepgsql_table_t;
+type_transition sepgsql_unconfined_type sepgsql_database_type:db_procedure sepgsql_proc_t;
+type_transition sepgsql_unconfined_type sepgsql_database_type:db_blob sepgsql_blob_t;
+
+
+########################################
+#
+# Allow permission to external domains
+#
+
+# relabelfrom for invalid security context
+allow sepgsql_unconfined_type unlabeled_t:db_database { setattr relabelfrom };
+allow sepgsql_unconfined_type unlabeled_t:db_table { setattr relabelfrom };
+allow sepgsql_unconfined_type unlabeled_t:db_procedure { setattr relabelfrom };
+allow sepgsql_unconfined_type unlabeled_t:db_column { setattr relabelfrom };
+allow sepgsql_unconfined_type unlabeled_t:db_tuple { update relabelfrom };
+allow sepgsql_unconfined_type unlabeled_t:db_blob { setattr relabelfrom };
+
+# administrative client domain
+optional_policy(`
+ gen_require(`
+ type unconfined_t;
+ ')
+ sepostgresql_unconfined(unconfined_t)
+')
+
+optional_policy(`
+ gen_require(`
+ type sysadm_t;
+ ')
+ sepostgresql_unconfined(sysadm_t)
+')
+
+# unprivilleged client domain
+optional_policy(`
+ gen_require(`
+ type user_t;
+ role user_r;
+ ')
+ sepostgresql_userdom_template(user,user_t,user_r)
+')
+
+optional_policy(`
+ gen_require(`
+ type staff_t;
+ role staff_r;
+ ')
+ sepostgresql_userdom_template(staff,staff_t,staff_r)
+')
+
+optional_policy(`
+ gen_require(`
+ type guest_t;
+ role guest_r;
+ ')
+ sepostgresql_userdom_template(guest,guest_t,guest_r)
+')
+
+optional_policy(`
+ gen_require(`
+ type xguest_t;
+ role xguest_r;
+ ')
+ sepostgresql_userdom_template(xguest,xguest_t,xguest_r)
+')
+
+optional_policy(`
+ gen_require(`
+ type httpd_t;
+ ')
+ sepostgresql_unpriv_client(httpd_t)
+')
+
+optional_policy(`
+ gen_require(`
+ type httpd_sys_script_t;
+ ')
+ sepostgresql_unpriv_client(httpd_sys_script_t)
+')
+
+# SE-PostgreSQL loadable modules
+optional_policy(`
+ gen_require(`
+ type lib_t, textrel_shlib_t;
+ ')
+ sepostgresql_loadable_module(lib_t)
+ sepostgresql_loadable_module(textrel_shlib_t)
+')
+
+########################################
+#
+# SE-PostgreSQL audit switch for debugging
+#
+tunable_policy(`sepgsql_enable_auditallow',`
+ auditallow domain sepgsql_database_type : db_database *;
+ auditallow domain sepgsql_table_type : db_table *;
+ auditallow domain sepgsql_table_type : db_column *;
+ auditallow domain sepgsql_procedure_type : db_procedure *;
+ auditallow domain sepgsql_blob_type : db_blob *;
+ auditallow domain sepgsql_module_type : db_database { install_module };
+ auditallow sepgsql_database_type sepgsql_module_type : db_database { load_module };
+')
+
+tunable_policy(`! sepgsql_enable_auditdeny',`
+ dontaudit domain sepgsql_database_type : db_database *;
+ dontaudit domain sepgsql_table_type : db_table *;
+ dontaudit domain sepgsql_table_type : db_column *;
+ dontaudit domain sepgsql_procedure_type : db_procedure *;
+ dontaudit domain sepgsql_blob_type : db_blob *;
+ dontaudit domain sepgsql_module_type : db_database { install_module };
+ dontaudit sepgsql_database_type sepgsql_module_type : db_database { load_module };
+')
+
+dontaudit domain { sepgsql_table_type - sepgsql_sysobj_table_type } : db_tuple { use select update insert delete };
+
+########################################
+#
+# SE-PostgreSQL regression test mode switch
+#
+tunable_policy(`sepgsql_regression_test_mode',`
+ allow sepgsql_client_type home_type : db_database { install_module };
+ allow sepgsql_unconfined_type home_type : db_database { install_module };
+ allow sepgsql_database_type home_type : db_database { load_module };
+')
diff -rpNU3 base/src/Makefile.global.in sepgsql/src/Makefile.global.in
--- base/src/Makefile.global.in 2007-11-17 20:15:40.000000000 +0900
+++ sepgsql/src/Makefile.global.in 2008-06-14 02:36:58.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 base/src/backend/Makefile sepgsql/src/backend/Makefile
--- base/src/backend/Makefile 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/Makefile 2008-06-14 02:36:58.000000000 +0900
@@ -16,7 +16,7 @@ include $(top_builddir)/src/Makefile.glo
DIRS = access bootstrap catalog parser commands executor lib libpq \
main nodes optimizer port postmaster regex rewrite \
- storage tcop tsearch utils $(top_builddir)/src/timezone
+ security storage tcop tsearch utils $(top_builddir)/src/timezone
SUBSYSOBJS = $(DIRS:%=%/SUBSYS.o)
@@ -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 base/src/backend/access/common/heaptuple.c sepgsql/src/backend/access/common/heaptuple.c
--- base/src/backend/access/common/heaptuple.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/access/common/heaptuple.c 2008-06-14 02:36:58.000000000 +0900
@@ -67,6 +67,7 @@
#include "access/heapam.h"
#include "access/tuptoaster.h"
#include "executor/tuptable.h"
+#include "security/pgace.h"
/* Does att's datatype allow packing into the 1-byte-header varlena format? */
@@ -473,6 +474,9 @@ heap_attisnull(HeapTuple tup, int attnum
case MinCommandIdAttributeNumber:
case MaxTransactionIdAttributeNumber:
case MaxCommandIdAttributeNumber:
+#ifdef SECURITY_SYSATTR_NAME
+ case SecurityAttributeNumber:
+#endif
/* these are never null */
break;
@@ -785,6 +789,16 @@ heap_getsysattr(HeapTuple tup, int attnu
case TableOidAttributeNumber:
result = ObjectIdGetDatum(tup->t_tableOid);
break;
+#ifdef SECURITY_SYSATTR_NAME
+ case SecurityAttributeNumber: {
+ Oid security_id = HeapTupleGetSecurity(tup);
+ char *sec_label = pgaceSidToSecurityLabel(security_id);
+
+ result = CStringGetTextDatum(sec_label);
+ pfree(sec_label);
+ break;
+ }
+#endif
default:
elog(ERROR, "invalid attnum: %d", attnum);
result = 0; /* keep compiler quiet */
@@ -909,6 +923,9 @@ heap_form_tuple(TupleDesc tupleDescripto
if (tupleDescriptor->tdhasoid)
len += sizeof(Oid);
+ if (pgaceSecurityAttributeNecessary())
+ len += sizeof(Oid);
+
hoff = len = MAXALIGN(len); /* align user data safely */
data_len = heap_compute_data_size(tupleDescriptor, values, isnull);
@@ -940,6 +957,9 @@ heap_form_tuple(TupleDesc tupleDescripto
if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */
td->t_infomask = HEAP_HASOID;
+ if (pgaceSecurityAttributeNecessary())
+ td->t_infomask |= HEAP_HASSECURITY;
+
heap_fill_tuple(tupleDescriptor,
values,
isnull,
@@ -1020,6 +1040,9 @@ heap_formtuple(TupleDesc tupleDescriptor
if (tupleDescriptor->tdhasoid)
len += sizeof(Oid);
+ if (pgaceSecurityAttributeNecessary())
+ len += sizeof(Oid);
+
hoff = len = MAXALIGN(len); /* align user data safely */
data_len = ComputeDataSize(tupleDescriptor, values, nulls);
@@ -1051,6 +1074,9 @@ heap_formtuple(TupleDesc tupleDescriptor
if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */
td->t_infomask = HEAP_HASOID;
+ if (pgaceSecurityAttributeNecessary())
+ td->t_infomask |= HEAP_HASSECURITY;
+
DataFill(tupleDescriptor,
values,
nulls,
@@ -1129,6 +1155,8 @@ heap_modify_tuple(HeapTuple tuple,
newTuple->t_tableOid = tuple->t_tableOid;
if (tupleDesc->tdhasoid)
HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple));
+ if (HeapTupleHasSecurity(newTuple))
+ HeapTupleSetSecurity(newTuple, HeapTupleGetSecurity(tuple));
return newTuple;
}
@@ -1201,6 +1229,8 @@ heap_modifytuple(HeapTuple tuple,
newTuple->t_tableOid = tuple->t_tableOid;
if (tupleDesc->tdhasoid)
HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple));
+ if (HeapTupleHasSecurity(newTuple))
+ HeapTupleSetSecurity(newTuple, HeapTupleGetSecurity(tuple));
return newTuple;
}
@@ -1847,6 +1877,9 @@ heap_form_minimal_tuple(TupleDesc tupleD
if (tupleDescriptor->tdhasoid)
len += sizeof(Oid);
+ if (pgaceSecurityAttributeNecessary())
+ len += sizeof(Oid);
+
hoff = len = MAXALIGN(len); /* align user data safely */
data_len = heap_compute_data_size(tupleDescriptor, values, isnull);
@@ -1868,6 +1901,9 @@ heap_form_minimal_tuple(TupleDesc tupleD
if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */
tuple->t_infomask = HEAP_HASOID;
+ if (pgaceSecurityAttributeNecessary())
+ tuple->t_infomask |= HEAP_HASSECURITY;
+
heap_fill_tuple(tupleDescriptor,
values,
isnull,
@@ -1979,6 +2015,10 @@ heap_addheader(int natts, /* max domain
hoff = offsetof(HeapTupleHeaderData, t_bits);
if (withoid)
hoff += sizeof(Oid);
+
+ if (pgaceSecurityAttributeNecessary())
+ hoff += sizeof(Oid);
+
hoff = MAXALIGN(hoff);
len = hoff + structlen;
@@ -1997,6 +2037,9 @@ heap_addheader(int natts, /* max domain
if (withoid) /* else leave infomask = 0 */
td->t_infomask = HEAP_HASOID;
+ if (pgaceSecurityAttributeNecessary())
+ td->t_infomask |= HEAP_HASSECURITY;
+
memcpy((char *) td + hoff, structure, structlen);
return tuple;
diff -rpNU3 base/src/backend/access/heap/heapam.c sepgsql/src/backend/access/heap/heapam.c
--- base/src/backend/access/heap/heapam.c 2008-03-19 09:48:23.000000000 +0900
+++ sepgsql/src/backend/access/heap/heapam.c 2008-06-14 02:36:58.000000000 +0900
@@ -50,6 +50,7 @@
#include "catalog/namespace.h"
#include "miscadmin.h"
#include "pgstat.h"
+#include "security/pgace.h"
#include "storage/procarray.h"
#include "storage/smgr.h"
#include "utils/datum.h"
@@ -1946,6 +1947,9 @@ heap_insert(Relation relation, HeapTuple
Oid
simple_heap_insert(Relation relation, HeapTuple tup)
{
+ if (!pgaceHeapTupleInsert(relation, tup, true, false))
+ elog(ERROR, "simple_heap_insert on %s failed due to security reason",
+ RelationGetRelationName(relation));
return heap_insert(relation, tup, GetCurrentCommandId(true), true, true);
}
@@ -2227,6 +2231,9 @@ simple_heap_delete(Relation relation, It
ItemPointerData update_ctid;
TransactionId update_xmax;
+ if (!pgaceHeapTupleDelete(relation, tid, true, false))
+ elog(ERROR, "simple_heap_delete on %s failed due to security reason",
+ RelationGetRelationName(relation));
result = heap_delete(relation, tid,
&update_ctid, &update_xmax,
GetCurrentCommandId(true), InvalidSnapshot,
@@ -2870,6 +2877,9 @@ simple_heap_update(Relation relation, It
ItemPointerData update_ctid;
TransactionId update_xmax;
+ if (!pgaceHeapTupleUpdate(relation, otid, tup, true, false))
+ elog(ERROR, "simple_heap_update on %s failed due to security reason",
+ RelationGetRelationName(relation));
result = heap_update(relation, otid, tup,
&update_ctid, &update_xmax,
GetCurrentCommandId(true), InvalidSnapshot,
diff -rpNU3 base/src/backend/access/heap/tuptoaster.c sepgsql/src/backend/access/heap/tuptoaster.c
--- base/src/backend/access/heap/tuptoaster.c 2008-03-19 09:48:23.000000000 +0900
+++ sepgsql/src/backend/access/heap/tuptoaster.c 2008-06-14 02:36:58.000000000 +0900
@@ -35,6 +35,7 @@
#include "access/tuptoaster.h"
#include "access/xact.h"
#include "catalog/catalog.h"
+#include "security/pgace.h"
#include "utils/fmgroids.h"
#include "utils/pg_lzcompress.h"
#include "utils/typcache.h"
@@ -589,6 +590,8 @@ toast_insert_or_update(Relation rel, Hea
hoff += BITMAPLEN(numAttrs);
if (newtup->t_data->t_infomask & HEAP_HASOID)
hoff += sizeof(Oid);
+ if (pgaceSecurityAttributeNecessary())
+ hoff += sizeof(Oid);
hoff = MAXALIGN(hoff);
Assert(hoff == newtup->t_data->t_hoff);
/* now convert to a limit on the tuple data size */
@@ -838,6 +841,8 @@ toast_insert_or_update(Relation rel, Hea
new_len += BITMAPLEN(numAttrs);
if (olddata->t_infomask & HEAP_HASOID)
new_len += sizeof(Oid);
+ if (pgaceSecurityAttributeNecessary())
+ new_len += sizeof(Oid);
new_len = MAXALIGN(new_len);
Assert(new_len == olddata->t_hoff);
new_data_len = heap_compute_data_size(tupleDesc,
@@ -989,6 +994,8 @@ toast_flatten_tuple_attribute(Datum valu
new_len += BITMAPLEN(numAttrs);
if (olddata->t_infomask & HEAP_HASOID)
new_len += sizeof(Oid);
+ if (pgaceSecurityAttributeNecessary())
+ new_len += sizeof(Oid);
new_len = MAXALIGN(new_len);
Assert(new_len == olddata->t_hoff);
new_data_len = heap_compute_data_size(tupleDesc,
@@ -1175,6 +1182,8 @@ toast_save_datum(Relation rel, Datum val
if (!HeapTupleIsValid(toasttup))
elog(ERROR, "failed to build TOAST tuple");
+ if (!pgaceHeapTupleInsert(toastrel, toasttup, true, false))
+ elog(ERROR, "failed to insert TOAST tuple due to security reason");
heap_insert(toastrel, toasttup, mycid, use_wal, use_fsm);
/*
diff -rpNU3 base/src/backend/bootstrap/bootparse.y sepgsql/src/backend/bootstrap/bootparse.y
--- base/src/backend/bootstrap/bootparse.y 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/bootstrap/bootparse.y 2008-06-14 02:36:58.000000000 +0900
@@ -212,7 +212,8 @@ Boot_CreateStmt:
0,
ONCOMMIT_NOOP,
(Datum) 0,
- true);
+ true,
+ NIL);
elog(DEBUG4, "relation created with oid %u", id);
}
do_end();
diff -rpNU3 base/src/backend/bootstrap/bootstrap.c sepgsql/src/backend/bootstrap/bootstrap.c
--- base/src/backend/bootstrap/bootstrap.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/bootstrap/bootstrap.c 2008-06-14 02:36:58.000000000 +0900
@@ -31,6 +31,7 @@
#include "nodes/makefuncs.h"
#include "postmaster/bgwriter.h"
#include "postmaster/walwriter.h"
+#include "security/pgace.h"
#include "storage/freespace.h"
#include "storage/ipc.h"
#include "storage/proc.h"
@@ -499,6 +500,8 @@ BootstrapModeMain(void)
*/
boot_yyparse();
+ pgacePostBootstrapingMode();
+
/* Perform a checkpoint to ensure everything's down to disk */
SetProcessingMode(NormalProcessing);
CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE);
diff -rpNU3 base/src/backend/catalog/Makefile sepgsql/src/backend/catalog/Makefile
--- base/src/backend/catalog/Makefile 2007-09-11 10:53:53.000000000 +0900
+++ sepgsql/src/backend/catalog/Makefile 2008-06-14 02:36:58.000000000 +0900
@@ -35,6 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_sr
pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \
pg_database.h pg_tablespace.h pg_pltemplate.h \
pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+ pg_security.h \
pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
pg_ts_parser.h pg_ts_template.h \
toasting.h indexing.h \
diff -rpNU3 base/src/backend/catalog/catalog.c sepgsql/src/backend/catalog/catalog.c
--- base/src/backend/catalog/catalog.c 2008-03-19 09:48:23.000000000 +0900
+++ sepgsql/src/backend/catalog/catalog.c 2008-06-14 02:36:58.000000000 +0900
@@ -30,6 +30,7 @@
#include "catalog/pg_database.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_pltemplate.h"
+#include "catalog/pg_security.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_shdescription.h"
#include "catalog/pg_tablespace.h"
@@ -257,6 +258,7 @@ IsSharedRelation(Oid relationId)
relationId == AuthMemRelationId ||
relationId == DatabaseRelationId ||
relationId == PLTemplateRelationId ||
+ relationId == SecurityRelationId ||
relationId == SharedDescriptionRelationId ||
relationId == SharedDependRelationId ||
relationId == TableSpaceRelationId)
@@ -269,6 +271,8 @@ IsSharedRelation(Oid relationId)
relationId == DatabaseNameIndexId ||
relationId == DatabaseOidIndexId ||
relationId == PLTemplateNameIndexId ||
+ relationId == SecurityOidIndexId ||
+ relationId == SecuritySeclabelIndexId ||
relationId == SharedDescriptionObjIndexId ||
relationId == SharedDependDependerIndexId ||
relationId == SharedDependReferenceIndexId ||
diff -rpNU3 base/src/backend/catalog/genbki.sh sepgsql/src/backend/catalog/genbki.sh
--- base/src/backend/catalog/genbki.sh 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/catalog/genbki.sh 2008-06-14 02:36:58.000000000 +0900
@@ -130,6 +130,16 @@ for dir in $INCLUDE_DIRS; do
fi
done
+# Get SECURITY_SYSATTR_NAME from pg_config.h
+SECURITY_SYSATTR_NAME_FILTERING="SECURITY_SYSATTR_NAME"
+for dir in $INCLUDE_DIRS; do
+ if [ -f "$dir/pg_config.h" ]; then
+ SECURITY_SYSATTR_NAME=`grep '#define[ ]*SECURITY_SYSATTR_NAME' $dir/pg_config.h | $AWK '{ print $3 }' | sed 's/\"//g'`
+ test -n "$SECURITY_SYSATTR_NAME" && SECURITY_SYSATTR_NAME_FILTERING="^__invalid__pattern__$"
+ break
+ fi
+done
+
touch ${OUTPUT_PREFIX}.description.$$
touch ${OUTPUT_PREFIX}.shdescription.$$
@@ -144,6 +154,7 @@ touch ${OUTPUT_PREFIX}.shdescription.$$
# ----------------
#
cat $INFILES | \
+grep -v $SECURITY_SYSATTR_NAME_FILTERING | \
sed -e 's;/\*.*\*/;;g' \
-e 's;/\*;\
/*\
@@ -165,6 +176,7 @@ sed -e "s/;[ ]*$//g" \
-e "s/PGUID/$BOOTSTRAP_SUPERUSERID/g" \
-e "s/NAMEDATALEN/$NAMEDATALEN/g" \
-e "s/PGNSP/$PG_CATALOG_NAMESPACE/g" \
+ -e "s/SECURITY_SYSATTR_NAME/$SECURITY_SYSATTR_NAME/g" \
| $AWK '
# ----------------
# now use awk to process remaining .h file..
diff -rpNU3 base/src/backend/catalog/heap.c sepgsql/src/backend/catalog/heap.c
--- base/src/backend/catalog/heap.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/catalog/heap.c 2008-06-18 17:25:44.000000000 +0900
@@ -53,6 +53,7 @@
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
+#include "security/pgace.h"
#include "storage/smgr.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@@ -67,7 +68,8 @@ static void AddNewRelationTuple(Relation
Oid new_rel_oid, Oid new_type_oid,
Oid relowner,
char relkind,
- Datum reloptions);
+ Datum reloptions,
+ List *pgace_attr_list);
static Oid AddNewRelationType(const char *typeName,
Oid typeNamespace,
Oid new_rel_oid,
@@ -144,7 +146,21 @@ static FormData_pg_attribute a7 = {
true, 'p', 'i', true, false, false, true, 0
};
+#ifdef SECURITY_SYSATTR_NAME
+/*
+ * SECURITY_SYSATTR_NAME is defined at PGACE header file.
+ * If SELinux is enabled, it is defined as "security_context"
+ */
+static FormData_pg_attribute a8 = {
+ 0, {SECURITY_SYSATTR_NAME}, TEXTOID, 0, -1,
+ SecurityAttributeNumber, 0, -1, -1,
+ false, 'x', 'i', true, false, false, true, 0
+};
+
+static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8};
+#else
static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7};
+#endif
/*
* This function returns a Form_pg_attribute pointer for a system attribute.
@@ -184,6 +200,20 @@ SystemAttributeByName(const char *attnam
return NULL;
}
+/*
+ * This function returns true, if the given attribute number is writable
+ * system column. If not, returns false.
+ */
+bool
+SystemAttributeIsWritable(AttrNumber attno, bool relhasoids)
+{
+#ifdef SECURITY_SYSATTR_NAME
+ if (pgaceSecurityAttributeNecessary()
+ && attno == SecurityAttributeNumber)
+ return true;
+#endif
+ return false;
+}
/* ----------------------------------------------------------------
* XXX END OF UGLY HARD CODED BADNESS XXX
@@ -467,7 +497,8 @@ AddNewAttributeTuples(Oid new_rel_oid,
TupleDesc tupdesc,
char relkind,
bool oidislocal,
- int oidinhcount)
+ int oidinhcount,
+ List *pgace_attr_list)
{
const Form_pg_attribute *dpp;
int i;
@@ -502,6 +533,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
false,
ATTRIBUTE_TUPLE_SIZE,
(void *) *dpp);
+ pgaceCreateAttributeCommon(rel, tup, pgace_attr_list);
simple_heap_insert(rel, tup);
@@ -592,7 +624,8 @@ void
InsertPgClassTuple(Relation pg_class_desc,
Relation new_rel_desc,
Oid new_rel_oid,
- Datum reloptions)
+ Datum reloptions,
+ List *pgace_attr_list)
{
Form_pg_class rd_rel = new_rel_desc->rd_rel;
Datum values[Natts_pg_class];
@@ -642,12 +675,16 @@ InsertPgClassTuple(Relation pg_class_des
* be embarrassing to do this sort of thing in polite company.
*/
HeapTupleSetOid(tup, new_rel_oid);
+ pgaceCreateRelationCommon(pg_class_desc, tup, pgace_attr_list);
/* finally insert the new tuple, update the indexes, and clean up */
simple_heap_insert(pg_class_desc, tup);
CatalogUpdateIndexes(pg_class_desc, tup);
+ /* temporary use for this tuple */
+ InsertSysCache(RelationGetRelid(pg_class_desc), tup);
+
heap_freetuple(tup);
}
@@ -665,7 +702,8 @@ AddNewRelationTuple(Relation pg_class_de
Oid new_type_oid,
Oid relowner,
char relkind,
- Datum reloptions)
+ Datum reloptions,
+ List *pgace_attr_list)
{
Form_pg_class new_rel_reltup;
@@ -725,7 +763,7 @@ AddNewRelationTuple(Relation pg_class_de
new_rel_desc->rd_att->tdtypeid = new_type_oid;
/* Now build and insert the tuple */
- InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions);
+ InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions, pgace_attr_list);
}
@@ -791,7 +829,8 @@ heap_create_with_catalog(const char *rel
int oidinhcount,
OnCommitAction oncommit,
Datum reloptions,
- bool allow_system_table_mods)
+ bool allow_system_table_mods,
+ List *pgace_attr_list)
{
Relation pg_class_desc;
Relation new_rel_desc;
@@ -963,13 +1002,14 @@ heap_create_with_catalog(const char *rel
new_type_oid,
ownerid,
relkind,
- reloptions);
+ reloptions,
+ pgace_attr_list);
/*
* now add tuples to pg_attribute for the attributes in our new relation.
*/
AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind,
- oidislocal, oidinhcount);
+ oidislocal, oidinhcount, pgace_attr_list);
/*
* Make a dependency link to force the relation to be deleted if its
diff -rpNU3 base/src/backend/catalog/index.c sepgsql/src/backend/catalog/index.c
--- base/src/backend/catalog/index.c 2008-02-03 01:11:28.000000000 +0900
+++ sepgsql/src/backend/catalog/index.c 2008-06-14 02:36:58.000000000 +0900
@@ -624,7 +624,7 @@ index_create(Oid heapRelationId,
*/
InsertPgClassTuple(pg_class, indexRelation,
RelationGetRelid(indexRelation),
- reloptions);
+ reloptions, NIL);
/* done with pg_class */
heap_close(pg_class, RowExclusiveLock);
diff -rpNU3 base/src/backend/catalog/pg_aggregate.c sepgsql/src/backend/catalog/pg_aggregate.c
--- base/src/backend/catalog/pg_aggregate.c 2008-01-14 22:59:48.000000000 +0900
+++ sepgsql/src/backend/catalog/pg_aggregate.c 2008-06-14 02:36:58.000000000 +0900
@@ -213,8 +213,9 @@ AggregateCreate(const char *aggName,
PointerGetDatum(NULL), /* parameterModes */
PointerGetDatum(NULL), /* parameterNames */
PointerGetDatum(NULL), /* proconfig */
- 1, /* procost */
- 0); /* prorows */
+ 1, /* procost */
+ 0, /* prorows */
+ NULL); /* PGACE opaque */
/*
* Okay to create the pg_aggregate entry.
diff -rpNU3 base/src/backend/catalog/pg_largeobject.c sepgsql/src/backend/catalog/pg_largeobject.c
--- base/src/backend/catalog/pg_largeobject.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/catalog/pg_largeobject.c 2008-06-14 02:36:58.000000000 +0900
@@ -18,6 +18,7 @@
#include "access/heapam.h"
#include "catalog/indexing.h"
#include "catalog/pg_largeobject.h"
+#include "security/pgace.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@@ -57,6 +58,8 @@ LargeObjectCreate(Oid loid)
ntup = heap_formtuple(pg_largeobject->rd_att, values, nulls);
+ pgaceLargeObjectCreate(pg_largeobject, ntup);
+
/*
* Insert it
*/
@@ -78,6 +81,7 @@ LargeObjectDrop(Oid loid)
ScanKeyData skey[1];
SysScanDesc sd;
HeapTuple tuple;
+ void *pgaceItem = NULL;
ScanKeyInit(&skey[0],
Anum_pg_largeobject_loid,
@@ -91,6 +95,7 @@ LargeObjectDrop(Oid loid)
while ((tuple = systable_getnext(sd)) != NULL)
{
+ pgaceLargeObjectDrop(pg_largeobject, tuple, &pgaceItem);
simple_heap_delete(pg_largeobject, &tuple->t_self);
found = true;
}
diff -rpNU3 base/src/backend/catalog/pg_proc.c sepgsql/src/backend/catalog/pg_proc.c
--- base/src/backend/catalog/pg_proc.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/catalog/pg_proc.c 2008-06-14 02:36:58.000000000 +0900
@@ -27,6 +27,7 @@
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/parse_type.h"
+#include "security/pgace.h"
#include "tcop/pquery.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
@@ -74,7 +75,8 @@ ProcedureCreate(const char *procedureNam
Datum parameterNames,
Datum proconfig,
float4 procost,
- float4 prorows)
+ float4 prorows,
+ void *pgaceItem)
{
Oid retval;
int parameterCount;
@@ -339,6 +341,7 @@ ProcedureCreate(const char *procedureNam
/* Okay, do it... */
tup = heap_modifytuple(oldtup, tupDesc, values, nulls, replaces);
+ pgaceGramCreateFunction(rel, tup, (DefElem *)pgaceItem);
simple_heap_update(rel, &tup->t_self, tup);
ReleaseSysCache(oldtup);
@@ -348,6 +351,7 @@ ProcedureCreate(const char *procedureNam
{
/* Creating a new procedure */
tup = heap_formtuple(tupDesc, values, nulls);
+ pgaceGramCreateFunction(rel, tup, (DefElem *)pgaceItem);
simple_heap_insert(rel, tup);
is_update = false;
}
diff -rpNU3 base/src/backend/catalog/toasting.c sepgsql/src/backend/catalog/toasting.c
--- base/src/backend/catalog/toasting.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/catalog/toasting.c 2008-06-14 02:36:58.000000000 +0900
@@ -199,7 +199,8 @@ create_toast_table(Relation rel, Oid toa
0,
ONCOMMIT_NOOP,
(Datum) 0,
- true);
+ true,
+ NIL);
/* make the toast relation visible, else index creation will fail */
CommandCounterIncrement();
diff -rpNU3 base/src/backend/commands/cluster.c sepgsql/src/backend/commands/cluster.c
--- base/src/backend/commands/cluster.c 2008-02-03 01:11:28.000000000 +0900
+++ sepgsql/src/backend/commands/cluster.c 2008-06-14 02:36:58.000000000 +0900
@@ -666,7 +666,8 @@ make_new_heap(Oid OIDOldHeap, const char
0,
ONCOMMIT_NOOP,
reloptions,
- allowSystemTableMods);
+ allowSystemTableMods,
+ NIL);
ReleaseSysCache(tuple);
@@ -857,6 +858,10 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOl
if (NewHeap->rd_rel->relhasoids)
HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple));
+ /* Preserve SID, if any */
+ if (HeapTupleHasSecurity(tuple))
+ HeapTupleSetSecurity(copiedTuple, HeapTupleGetSecurity(tuple));
+
/* The heap rewrite module does the rest */
rewrite_heap_tuple(rwstate, tuple, copiedTuple);
diff -rpNU3 base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c
--- base/src/backend/commands/copy.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/commands/copy.c 2008-06-18 17:25:44.000000000 +0900
@@ -22,6 +22,7 @@
#include "access/heapam.h"
#include "access/xact.h"
+#include "catalog/heap.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "commands/copy.h"
@@ -34,6 +35,7 @@
#include "optimizer/planner.h"
#include "parser/parse_relation.h"
#include "rewrite/rewriteHandler.h"
+#include "security/pgace.h"
#include "storage/fd.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
@@ -159,6 +161,10 @@ typedef struct CopyStateData
char *raw_buf;
int raw_buf_index; /* next byte to process */
int raw_buf_len; /* total # of bytes stored */
+
+ /* security attribute dump/restore support */
+ FmgrInfo security_out_function;
+ bool security_force_quot;
} CopyStateData;
typedef CopyStateData *CopyState;
@@ -242,7 +248,7 @@ static const char BinarySignature[11] =
/* non-export function prototypes */
static void DoCopyTo(CopyState cstate);
static void CopyTo(CopyState cstate);
-static void CopyOneRowTo(CopyState cstate, Oid tupleOid,
+static void CopyOneRowTo(CopyState cstate, Oid tupleOid, Oid securityOid,
Datum *values, bool *nulls);
static void CopyFrom(CopyState cstate);
static bool CopyReadLine(CopyState cstate);
@@ -277,6 +283,19 @@ static bool CopyGetInt32(CopyState cstat
static void CopySendInt16(CopyState cstate, int16 val);
static bool CopyGetInt16(CopyState cstate, int16 *val);
+/*
+ * IsWritableSecurityAttribute
+ *
+ * It returns false, if the given attribute is not
+ * a writable security system attribute.
+ */
+#ifdef SECURITY_SYSATTR_NAME
+#define IsWritableSecurityAttribute(attnum) \
+ ((attnum) == SecurityAttributeNumber \
+ && pgaceSecurityAttributeNecessary())
+#else
+#define IsWritableSecurityAttribute(attnum) (false)
+#endif
/*
* Send copy start/stop messages for frontend copies. These have changed
@@ -1073,6 +1092,8 @@ DoCopy(const CopyStmt *stmt, const char
/* Generate or convert list of attributes to process */
cstate->attnumlist = CopyGetAttnums(tupDesc, cstate->rel, attnamelist);
+ pgaceCopyTable(cstate->rel, cstate->attnumlist, is_from);
+
num_phys_attrs = tupDesc->natts;
/* Convert FORCE QUOTE name list to per-column flags, check validity */
@@ -1093,6 +1114,11 @@ DoCopy(const CopyStmt *stmt, const char
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
errmsg("FORCE QUOTE column \"%s\" not referenced by COPY",
NameStr(tupDesc->attrs[attnum - 1]->attname))));
+ if (IsWritableSecurityAttribute(attnum))
+ {
+ cstate->security_force_quot = true;
+ continue;
+ }
cstate->force_quote_flags[attnum - 1] = true;
}
}
@@ -1115,6 +1141,9 @@ DoCopy(const CopyStmt *stmt, const char
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY",
NameStr(tupDesc->attrs[attnum - 1]->attname))));
+ if (IsWritableSecurityAttribute(attnum))
+ continue; /* ignore, if specified */
+
cstate->force_notnull_flags[attnum - 1] = true;
}
}
@@ -1305,16 +1334,29 @@ CopyTo(CopyState cstate)
int attnum = lfirst_int(cur);
Oid out_func_oid;
bool isvarlena;
+ FmgrInfo *out_fmgr;
+ Form_pg_attribute attForm;
+
+ if (IsWritableSecurityAttribute(attnum))
+ {
+ attForm = SystemAttributeDefinition(attnum, false);
+ out_fmgr = &cstate->security_out_function;
+ }
+ else
+ {
+ attForm = attr[attnum - 1];
+ out_fmgr = &cstate->out_functions[attnum - 1];
+ }
if (cstate->binary)
- getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid,
+ getTypeBinaryOutputInfo(attForm->atttypid,
&out_func_oid,
&isvarlena);
else
- getTypeOutputInfo(attr[attnum - 1]->atttypid,
+ getTypeOutputInfo(attForm->atttypid,
&out_func_oid,
&isvarlena);
- fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]);
+ fmgr_info(out_func_oid, out_fmgr);
}
/*
@@ -1369,7 +1411,14 @@ CopyTo(CopyState cstate)
CopySendChar(cstate, cstate->delim[0]);
hdr_delim = true;
- colname = NameStr(attr[attnum - 1]->attname);
+ if (IsWritableSecurityAttribute(attnum))
+ {
+ Form_pg_attribute attForm
+ = SystemAttributeDefinition(attnum, false);
+ colname = NameStr(attForm->attname);
+ }
+ else
+ colname = NameStr(attr[attnum - 1]->attname);
CopyAttributeOutCSV(cstate, colname, false,
list_length(cstate->attnumlist) == 1);
@@ -1395,11 +1444,17 @@ CopyTo(CopyState cstate)
{
CHECK_FOR_INTERRUPTS();
+ if (!pgaceCopyToTuple(cstate->rel, cstate->attnumlist, tuple))
+ continue;
+
/* Deconstruct the tuple ... faster than repeated heap_getattr */
heap_deform_tuple(tuple, tupDesc, values, nulls);
/* Format and send the data */
- CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls);
+ CopyOneRowTo(cstate,
+ HeapTupleGetOid(tuple),
+ HeapTupleGetSecurity(tuple),
+ values, nulls);
}
heap_endscan(scandesc);
@@ -1425,7 +1480,7 @@ CopyTo(CopyState cstate)
* Emit one row during CopyTo().
*/
static void
-CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls)
+CopyOneRowTo(CopyState cstate, Oid tupleOid, Oid tupleSecurity, Datum *values, bool *nulls)
{
bool need_delim = false;
FmgrInfo *out_functions = cstate->out_functions;
@@ -1464,8 +1519,10 @@ CopyOneRowTo(CopyState cstate, Oid tuple
foreach(cur, cstate->attnumlist)
{
int attnum = lfirst_int(cur);
- Datum value = values[attnum - 1];
- bool isnull = nulls[attnum - 1];
+ Datum value;
+ bool isnull;
+ bool force_quot;
+ FmgrInfo *out_fmgr;
if (!cstate->binary)
{
@@ -1474,6 +1531,22 @@ CopyOneRowTo(CopyState cstate, Oid tuple
need_delim = true;
}
+ if (IsWritableSecurityAttribute(attnum))
+ {
+ char *tmp = pgaceSidToSecurityLabel(tupleSecurity);
+ value = CStringGetTextDatum(tmp);
+ isnull = false;
+ force_quot = cstate->security_force_quot;
+ out_fmgr = &cstate->security_out_function;
+ }
+ else
+ {
+ value = values[attnum - 1];
+ isnull = nulls[attnum - 1];
+ force_quot = cstate->force_quote_flags[attnum - 1];
+ out_fmgr = &out_functions[attnum - 1];
+ }
+
if (isnull)
{
if (!cstate->binary)
@@ -1485,11 +1558,9 @@ CopyOneRowTo(CopyState cstate, Oid tuple
{
if (!cstate->binary)
{
- string = OutputFunctionCall(&out_functions[attnum - 1],
- value);
+ string = OutputFunctionCall(out_fmgr, value);
if (cstate->csv_mode)
- CopyAttributeOutCSV(cstate, string,
- cstate->force_quote_flags[attnum - 1],
+ CopyAttributeOutCSV(cstate, string, force_quot,
list_length(cstate->attnumlist) == 1);
else
CopyAttributeOutText(cstate, string);
@@ -1498,8 +1569,7 @@ CopyOneRowTo(CopyState cstate, Oid tuple
{
bytea *outputbytes;
- outputbytes = SendFunctionCall(&out_functions[attnum - 1],
- value);
+ outputbytes = SendFunctionCall(out_fmgr, value);
CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ);
CopySendData(cstate, VARDATA(outputbytes),
VARSIZE(outputbytes) - VARHDRSZ);
@@ -1633,10 +1703,13 @@ CopyFrom(CopyState cstate)
num_defaults;
FmgrInfo *in_functions;
FmgrInfo oid_in_function;
+ FmgrInfo security_in_function;
Oid *typioparams;
Oid oid_typioparam;
+ Oid security_typioparam;
int attnum;
int i;
+ ListCell *l;
Oid in_func_oid;
Datum *values;
char *nulls;
@@ -1872,6 +1945,25 @@ CopyFrom(CopyState cstate)
fmgr_info(in_func_oid, &oid_in_function);
}
+ foreach (l, cstate->attnumlist)
+ {
+ attnum = lfirst_int(l);
+
+ if (IsWritableSecurityAttribute(attnum))
+ {
+ if (!cstate->binary)
+ getTypeInputInfo(TEXTOID,
+ &in_func_oid,
+ &security_typioparam);
+ else
+ getTypeBinaryInputInfo(TEXTOID,
+ &in_func_oid,
+ &security_typioparam);
+ fmgr_info(in_func_oid, &security_in_function);
+ break;
+ }
+ }
+
values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
nulls = (char *) palloc(num_phys_attrs * sizeof(char));
@@ -1904,6 +1996,7 @@ CopyFrom(CopyState cstate)
{
bool skip_tuple;
Oid loaded_oid = InvalidOid;
+ Datum loaded_security = PointerGetDatum(NULL);
CHECK_FOR_INTERRUPTS();
@@ -1978,6 +2071,32 @@ CopyFrom(CopyState cstate)
int attnum = lfirst_int(cur);
int m = attnum - 1;
+ if (IsWritableSecurityAttribute(attnum))
+ {
+ Form_pg_attribute attForm
+ = SystemAttributeDefinition(attnum, false);
+
+ if (fieldno >= fldct)
+ ereport(ERROR,
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
+ errmsg("missing data for column \"%s\"",
+ NameStr(attForm->attname))));
+ string = field_strings[fieldno++];
+ cstate->cur_attname = NameStr(attForm->attname);
+ cstate->cur_attval = string;
+ if (string)
+ {
+ loaded_security
+ = InputFunctionCall(&security_in_function,
+ string,
+ security_typioparam,
+ attForm->atttypmod);
+ }
+ cstate->cur_attname = NULL;
+ cstate->cur_attval = NULL;
+ continue;
+ }
+
if (fieldno >= fldct)
ereport(ERROR,
(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
@@ -2048,6 +2167,27 @@ CopyFrom(CopyState cstate)
int attnum = lfirst_int(cur);
int m = attnum - 1;
+ if (IsWritableSecurityAttribute(attnum))
+ {
+ Form_pg_attribute attForm
+ = SystemAttributeDefinition(attnum, false);
+ Datum tmp;
+
+ cstate->cur_attname = NameStr(attForm->attname);
+ i++;
+
+ tmp = CopyReadBinaryAttribute(cstate,
+ i,
+ &security_in_function,
+ security_typioparam,
+ attForm->atttypmod,
+ &isnull);
+ if (!isnull)
+ loaded_security = tmp;
+ cstate->cur_attname = NULL;
+ continue;
+ }
+
cstate->cur_attname = NameStr(attr[m]->attname);
i++;
values[m] = CopyReadBinaryAttribute(cstate,
@@ -2079,6 +2219,12 @@ CopyFrom(CopyState cstate)
if (cstate->oids && file_has_oids)
HeapTupleSetOid(tuple, loaded_oid);
+ if (loaded_security != PointerGetDatum(NULL))
+ {
+ Oid security_id
+ = pgaceSecurityLabelToSid(TextDatumGetCString(loaded_security));
+ HeapTupleSetSecurity(tuple, security_id);
+ }
/* Triggers and stuff need to be invoked in query context. */
MemoryContextSwitchTo(oldcontext);
@@ -2102,6 +2248,9 @@ CopyFrom(CopyState cstate)
}
}
+ if (!skip_tuple && !pgaceHeapTupleInsert(cstate->rel, tuple, false, false))
+ skip_tuple = true;
+
if (!skip_tuple)
{
/* Place tuple in tuple slot */
@@ -3364,6 +3513,18 @@ CopyGetAttnums(TupleDesc tupDesc, Relati
break;
}
}
+
+ /* Is it writable system column? */
+ if (attnum == InvalidAttrNumber)
+ {
+ Form_pg_attribute attForm;
+ bool relhasoids = RelationGetForm(rel)->relhasoids;
+
+ attForm = SystemAttributeByName(name, relhasoids);
+ if (attForm && IsWritableSecurityAttribute(attForm->attnum))
+ attnum = attForm->attnum;
+ }
+
if (attnum == InvalidAttrNumber)
{
if (rel != NULL)
@@ -3413,7 +3574,9 @@ copy_dest_receive(TupleTableSlot *slot,
slot_getallattrs(slot);
/* And send the data */
- CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull);
+ CopyOneRowTo(cstate,
+ InvalidOid, InvalidOid,
+ slot->tts_values, slot->tts_isnull);
}
/*
diff -rpNU3 base/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/dbcommands.c
--- base/src/backend/commands/dbcommands.c 2008-06-12 22:34:19.000000000 +0900
+++ sepgsql/src/backend/commands/dbcommands.c 2008-06-14 02:36:58.000000000 +0900
@@ -40,6 +40,7 @@
#include "miscadmin.h"
#include "pgstat.h"
#include "postmaster/bgwriter.h"
+#include "security/pgace.h"
#include "storage/freespace.h"
#include "storage/ipc.h"
#include "storage/procarray.h"
@@ -100,6 +101,7 @@ createdb(const CreatedbStmt *stmt)
DefElem *dtemplate = NULL;
DefElem *dencoding = NULL;
DefElem *dconnlimit = NULL;
+ DefElem *dpgace_item = NULL;
char *dbname = stmt->dbname;
char *dbowner = NULL;
const char *dbtemplate = NULL;
@@ -160,6 +162,13 @@ createdb(const CreatedbStmt *stmt)
errmsg("LOCATION is not supported anymore"),
errhint("Consider using tablespaces instead.")));
}
+ else if (pgaceIsGramSecurityItem(defel)) {
+ if (dpgace_item)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ dpgace_item = defel;
+ }
else
elog(ERROR, "option \"%s\" not recognized",
defel->defname);
@@ -433,6 +442,7 @@ createdb(const CreatedbStmt *stmt)
new_record, new_record_nulls);
HeapTupleSetOid(tuple, dboid);
+ pgaceGramCreateDatabase(pg_database_rel, tuple, dpgace_item);
simple_heap_insert(pg_database_rel, tuple);
@@ -853,6 +863,7 @@ AlterDatabase(AlterDatabaseStmt *stmt)
ListCell *option;
int connlimit = -1;
DefElem *dconnlimit = NULL;
+ DefElem *dpgace_item = NULL;
Datum new_record[Natts_pg_database];
char new_record_nulls[Natts_pg_database];
char new_record_repl[Natts_pg_database];
@@ -870,6 +881,13 @@ AlterDatabase(AlterDatabaseStmt *stmt)
errmsg("conflicting or redundant options")));
dconnlimit = defel;
}
+ else if (pgaceIsGramSecurityItem(defel)) {
+ if (dpgace_item)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ dpgace_item = defel;
+ }
else
elog(ERROR, "option \"%s\" not recognized",
defel->defname);
@@ -915,6 +933,7 @@ AlterDatabase(AlterDatabaseStmt *stmt)
newtuple = heap_modifytuple(tuple, RelationGetDescr(rel), new_record,
new_record_nulls, new_record_repl);
+ pgaceGramAlterDatabase(rel, newtuple, dpgace_item);
simple_heap_update(rel, &tuple->t_self, newtuple);
/* Update indexes */
diff -rpNU3 base/src/backend/commands/functioncmds.c sepgsql/src/backend/commands/functioncmds.c
--- base/src/backend/commands/functioncmds.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/commands/functioncmds.c 2008-06-14 02:36:58.000000000 +0900
@@ -47,6 +47,7 @@
#include "miscadmin.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
+#include "security/pgace.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@@ -412,7 +413,8 @@ compute_attributes_sql_style(List *optio
bool *security_definer,
ArrayType **proconfig,
float4 *procost,
- float4 *prorows)
+ float4 *prorows,
+ DefElem **pgaceItem)
{
ListCell *option;
DefElem *as_item = NULL;
@@ -444,6 +446,14 @@ compute_attributes_sql_style(List *optio
errmsg("conflicting or redundant options")));
language_item = defel;
}
+ else if (pgaceIsGramSecurityItem(defel))
+ {
+ if (*pgaceItem)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ *pgaceItem = defel;
+ }
else if (compute_common_attribute(defel,
&volatility_item,
&strict_item,
@@ -624,6 +634,7 @@ CreateFunction(CreateFunctionStmt *stmt)
HeapTuple languageTuple;
Form_pg_language languageStruct;
List *as_clause;
+ DefElem *pgaceItem = NULL;
/* Convert list of names to a name and namespace */
namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname,
@@ -647,7 +658,7 @@ CreateFunction(CreateFunctionStmt *stmt)
compute_attributes_sql_style(stmt->options,
&as_clause, &language,
&volatility, &isStrict, &security,
- &proconfig, &procost, &prorows);
+ &proconfig, &procost, &prorows, &pgaceItem);
/* Convert language name to canonical case */
languageName = case_translate_language_name(language);
@@ -801,7 +812,8 @@ CreateFunction(CreateFunctionStmt *stmt)
PointerGetDatum(parameterNames),
PointerGetDatum(proconfig),
procost,
- prorows);
+ prorows,
+ pgaceItem);
}
@@ -1151,6 +1163,7 @@ AlterFunction(AlterFunctionStmt *stmt)
List *set_items = NIL;
DefElem *cost_item = NULL;
DefElem *rows_item = NULL;
+ DefElem *pgaceItem = NULL;
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
@@ -1182,6 +1195,15 @@ AlterFunction(AlterFunctionStmt *stmt)
{
DefElem *defel = (DefElem *) lfirst(l);
+ if (pgaceIsGramSecurityItem(defel)) {
+ if (pgaceItem)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ pgaceItem = defel;
+ continue;
+ }
+
if (compute_common_attribute(defel,
&volatility_item,
&strict_item,
@@ -1252,6 +1274,7 @@ AlterFunction(AlterFunctionStmt *stmt)
tup = heap_modifytuple(tup, RelationGetDescr(rel),
repl_val, repl_null, repl_repl);
}
+ pgaceGramAlterFunction(rel, tup, pgaceItem);
/* Do the update */
simple_heap_update(rel, &tup->t_self, tup);
diff -rpNU3 base/src/backend/commands/lockcmds.c sepgsql/src/backend/commands/lockcmds.c
--- base/src/backend/commands/lockcmds.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/commands/lockcmds.c 2008-06-14 02:36:58.000000000 +0900
@@ -18,6 +18,7 @@
#include "catalog/namespace.h"
#include "commands/lockcmds.h"
#include "miscadmin.h"
+#include "security/pgace.h"
#include "utils/acl.h"
#include "utils/lsyscache.h"
@@ -59,6 +60,8 @@ LockTableCommand(LockStmt *lockstmt)
aclcheck_error(aclresult, ACL_KIND_CLASS,
get_rel_name(reloid));
+ pgaceLockTable(reloid);
+
if (lockstmt->nowait)
rel = relation_open_nowait(reloid, lockstmt->mode);
else
diff -rpNU3 base/src/backend/commands/prepare.c sepgsql/src/backend/commands/prepare.c
--- base/src/backend/commands/prepare.c 2008-06-12 22:34:19.000000000 +0900
+++ sepgsql/src/backend/commands/prepare.c 2008-06-14 02:36:58.000000000 +0900
@@ -27,6 +27,7 @@
#include "parser/parse_expr.h"
#include "parser/parse_type.h"
#include "rewrite/rewriteHandler.h"
+#include "security/pgace.h"
#include "tcop/pquery.h"
#include "tcop/tcopprot.h"
#include "tcop/utility.h"
@@ -359,6 +360,8 @@ EvaluateParams(PreparedStatement *pstmt,
lfirst(l) = expr;
i++;
}
+ /* Evaluate permissions to use parameters */
+ pgaceEvaluateParams(params);
/* Prepare the expressions for execution */
exprstates = (List *) ExecPrepareExpr((Expr *) params, estate);
diff -rpNU3 base/src/backend/commands/proclang.c sepgsql/src/backend/commands/proclang.c
--- base/src/backend/commands/proclang.c 2008-06-12 22:34:19.000000000 +0900
+++ sepgsql/src/backend/commands/proclang.c 2008-06-14 02:36:58.000000000 +0900
@@ -146,7 +146,8 @@ CreateProceduralLanguage(CreatePLangStmt
PointerGetDatum(NULL),
PointerGetDatum(NULL),
1,
- 0);
+ 0,
+ NULL);
}
/*
@@ -179,7 +180,8 @@ CreateProceduralLanguage(CreatePLangStmt
PointerGetDatum(NULL),
PointerGetDatum(NULL),
1,
- 0);
+ 0,
+ NULL);
}
}
else
diff -rpNU3 base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/tablecmds.c
--- base/src/backend/commands/tablecmds.c 2008-06-12 22:34:19.000000000 +0900
+++ sepgsql/src/backend/commands/tablecmds.c 2008-06-14 02:36:58.000000000 +0900
@@ -57,6 +57,7 @@
#include "parser/parser.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteHandler.h"
+#include "security/pgace.h"
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
@@ -434,7 +435,8 @@ DefineRelation(CreateStmt *stmt, char re
parentOidCount,
stmt->oncommit,
reloptions,
- allowSystemTableMods);
+ allowSystemTableMods,
+ pgaceRelationAttrList(stmt));
StoreCatalogInheritance(relationId, inheritOids);
@@ -2031,6 +2033,7 @@ ATPrepCmd(List **wqueue, Relation rel, A
case AT_DisableRule:
case AT_AddInherit: /* INHERIT / NO INHERIT */
case AT_DropInherit:
+ case AT_SetSecurityLabel:
ATSimplePermissions(rel, false);
/* These commands never recurse */
/* No command-specific prep needed */
@@ -2253,6 +2256,9 @@ ATExecCmd(AlteredTableInfo *tab, Relatio
case AT_DropInherit:
ATExecDropInherit(rel, (RangeVar *) cmd->def);
break;
+ case AT_SetSecurityLabel:
+ pgaceAlterRelationCommon(rel, cmd);
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -2591,11 +2597,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oi
if (newrel)
{
Oid tupOid = InvalidOid;
+ Oid tupSid = InvalidOid;
/* Extract data from old tuple */
heap_deform_tuple(tuple, oldTupDesc, values, isnull);
if (oldTupDesc->tdhasoid)
tupOid = HeapTupleGetOid(tuple);
+ if (HeapTupleHasSecurity(tuple))
+ tupSid = HeapTupleGetSecurity(tuple);
/* Set dropped attributes to null in new tuple */
foreach(lc, dropped_attrs)
@@ -2627,6 +2636,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oi
/* Preserve OID, if any */
if (newTupDesc->tdhasoid)
HeapTupleSetOid(tuple, tupOid);
+ /* Preserve Security ID, if any */
+ if (tupSid != InvalidOid)
+ HeapTupleSetSecurity(tuple, tupSid);
}
/* Now check any constraints on the possibly-changed tuple */
diff -rpNU3 base/src/backend/commands/trigger.c sepgsql/src/backend/commands/trigger.c
--- base/src/backend/commands/trigger.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/commands/trigger.c 2008-06-14 02:36:58.000000000 +0900
@@ -31,6 +31,7 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/parse_func.h"
+#include "security/pgace.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
@@ -1574,6 +1575,12 @@ ExecCallTriggerFunc(TriggerData *trigdat
*/
InitFunctionCallInfoData(fcinfo, finfo, 0, (Node *) trigdata, NULL);
+ if (!pgaceCallFunctionTrigger(finfo, trigdata))
+ {
+ MemoryContextSwitchTo(oldContext);
+ return (HeapTuple) DatumGetPointer(NULL);
+ }
+
result = FunctionCallInvoke(&fcinfo);
MemoryContextSwitchTo(oldContext);
diff -rpNU3 base/src/backend/executor/execMain.c sepgsql/src/backend/executor/execMain.c
--- base/src/backend/executor/execMain.c 2008-06-12 22:34:19.000000000 +0900
+++ sepgsql/src/backend/executor/execMain.c 2008-06-26 10:27:05.000000000 +0900
@@ -48,6 +48,7 @@
#include "optimizer/clauses.h"
#include "parser/parse_clause.h"
#include "parser/parsetree.h"
+#include "security/pgace.h"
#include "storage/smgr.h"
#include "utils/acl.h"
#include "utils/lsyscache.h"
@@ -136,6 +137,8 @@ ExecutorStart(QueryDesc *queryDesc, int
Assert(queryDesc != NULL);
Assert(queryDesc->estate == NULL);
+ pgaceExecutorStart(queryDesc, eflags);
+
/*
* If the transaction is read-only, we need to check if any writes are
* planned to non-temporary tables. EXPLAIN is considered read-only.
@@ -1164,6 +1167,50 @@ ExecEndPlan(PlanState *planstate, EState
}
}
+/*
+ * fetchWritableSystemAttribute() fetches writable system column data
+ * using Junkfilter, and saves them at TupleTableSlot temporary.
+ *
+ * storeWritableSystemAttribute() copies these fetched data into
+ * header structure of HeapTuple.
+ */
+static void
+fetchWritableSystemAttribute(JunkFilter *junkfilter, TupleTableSlot *slot,
+ Datum *tts_security)
+{
+ AttrNumber attno;
+ Datum datum;
+ bool isnull;
+
+#ifdef SECURITY_SYSATTR_NAME
+ attno = ExecFindJunkAttribute(junkfilter, SECURITY_SYSATTR_NAME);
+ if (attno != InvalidAttrNumber)
+ {
+ datum = ExecGetJunkAttribute(slot, attno, &isnull);
+ if (!isnull)
+ *tts_security = datum;
+ }
+#endif
+}
+
+static void
+storeWritableSystemAttribute(Relation rel, TupleTableSlot *slot, HeapTuple tuple)
+{
+ /* for security attribute */
+ if (HeapTupleHasSecurity(tuple))
+ {
+ if (!DatumGetPointer(slot->tts_security))
+ HeapTupleSetSecurity(tuple, InvalidOid);
+ else
+ {
+ char *label = TextDatumGetCString(slot->tts_security);
+
+ HeapTupleSetSecurity(tuple,
+ pgaceSecurityLabelToSid(label));
+ }
+ }
+}
+
/* ----------------------------------------------------------------
* ExecutePlan
*
@@ -1231,6 +1278,8 @@ ExecutePlan(EState *estate,
for (;;)
{
+ Datum tts_security = PointerGetDatum(NULL);
+
/* Reset the per-output-tuple exprcontext */
ResetPerTupleExprContext(estate);
@@ -1355,6 +1404,11 @@ lnext: ;
}
/*
+ * extract writable system attribute
+ */
+ fetchWritableSystemAttribute(junkfilter, slot, &tts_security);
+
+ /*
* extract the 'ctid' junk attribute.
*/
if (operation == CMD_UPDATE || operation == CMD_DELETE)
@@ -1381,6 +1435,7 @@ lnext: ;
if (operation != CMD_DELETE)
slot = ExecFilterJunk(junkfilter, slot);
}
+ slot->tts_security = tts_security;
/*
* now that we have a tuple, do the appropriate thing with it.. either
@@ -1501,6 +1556,8 @@ ExecInsert(TupleTableSlot *slot,
resultRelInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelInfo->ri_RelationDesc;
+ storeWritableSystemAttribute(resultRelationDesc, slot, tuple);
+
/* BEFORE ROW INSERT Triggers */
if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0)
@@ -1537,6 +1594,13 @@ ExecInsert(TupleTableSlot *slot,
ExecConstraints(resultRelInfo, slot, estate);
/*
+ * Mandatory access controls of the tuple
+ */
+ if (!pgaceHeapTupleInsert(resultRelationDesc, tuple,
+ false, !!resultRelInfo->ri_projectReturning))
+ return;
+
+ /*
* insert the tuple
*
* Note: heap_insert returns the tid (location) of the new tuple in the
@@ -1603,6 +1667,10 @@ ExecDelete(ItemPointer tupleid,
return;
}
+ if (!pgaceHeapTupleDelete(resultRelationDesc, tupleid,
+ false, !!resultRelInfo->ri_projectReturning))
+ return;
+
/*
* delete the tuple
*
@@ -1739,6 +1807,8 @@ ExecUpdate(TupleTableSlot *slot,
resultRelInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelInfo->ri_RelationDesc;
+ storeWritableSystemAttribute(resultRelationDesc, slot, tuple);
+
/* BEFORE ROW UPDATE Triggers */
if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0)
@@ -1783,6 +1853,13 @@ lreplace:;
ExecConstraints(resultRelInfo, slot, estate);
/*
+ * Mandatory access controls of the tuple
+ */
+ if (!pgaceHeapTupleUpdate(resultRelationDesc, tupleid, tuple,
+ false, !!resultRelInfo->ri_projectReturning))
+ return;
+
+ /*
* replace the heap tuple
*
* Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check that
@@ -2646,7 +2723,8 @@ OpenIntoRel(QueryDesc *queryDesc)
0,
into->onCommit,
reloptions,
- allowSystemTableMods);
+ allowSystemTableMods,
+ NIL);
FreeTupleDesc(tupdesc);
@@ -2752,6 +2830,12 @@ intorel_receive(TupleTableSlot *slot, De
tuple = ExecCopySlotTuple(slot);
+ storeWritableSystemAttribute(estate->es_into_relation_descriptor, slot, tuple);
+ if (!pgaceHeapTupleInsert(estate->es_into_relation_descriptor, tuple, false, false)) {
+ heap_freetuple(tuple);
+ return;
+ }
+
heap_insert(estate->es_into_relation_descriptor,
tuple,
estate->es_output_cid,
diff -rpNU3 base/src/backend/executor/execQual.c sepgsql/src/backend/executor/execQual.c
--- base/src/backend/executor/execQual.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/executor/execQual.c 2008-08-15 11:22:38.000000000 +0900
@@ -47,6 +47,7 @@
#include "nodes/makefuncs.h"
#include "optimizer/planmain.h"
#include "parser/parse_expr.h"
+#include "security/pgace.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
@@ -1015,6 +1016,9 @@ init_fcache(Oid foid, FuncExprState *fca
fcache->setArgsValid = false;
fcache->shutdown_reg = false;
fcache->func.fn_expr = (Node *) fcache->xprstate.expr;
+
+ /* Check permission to call function by security subsystem */
+ pgaceCallFunction(&fcache->func);
}
/*
diff -rpNU3 base/src/backend/executor/execScan.c sepgsql/src/backend/executor/execScan.c
--- base/src/backend/executor/execScan.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/executor/execScan.c 2008-06-14 02:36:58.000000000 +0900
@@ -20,6 +20,7 @@
#include "executor/executor.h"
#include "miscadmin.h"
+#include "security/pgace.h"
#include "utils/memutils.h"
@@ -48,7 +49,7 @@ TupleTableSlot *
ExecScan(ScanState *node,
ExecScanAccessMtd accessMtd) /* function returning a tuple */
{
- ExprContext *econtext;
+ ExprContext *econtext = node->ps.ps_ExprContext;
List *qual;
ProjectionInfo *projInfo;
ExprDoneCond isDone;
@@ -65,7 +66,22 @@ ExecScan(ScanState *node,
* all the overhead and return the raw scan tuple.
*/
if (!qual && !projInfo)
- return (*accessMtd) (node);
+ {
+ while (true)
+ {
+ resultSlot = (*accessMtd) (node);
+
+ if (TupIsNull(resultSlot))
+ break;
+
+ if (pgaceExecScan((Scan *)node->ps.plan,
+ node->ss_currentRelation, resultSlot))
+ break;
+
+ ResetExprContext(econtext);
+ }
+ return resultSlot;
+ }
/*
* Check to see if we're still projecting out tuples from a previous scan
@@ -87,7 +103,6 @@ ExecScan(ScanState *node,
* storage allocated in the previous tuple cycle. Note this can't happen
* until we're done projecting out tuples from a scan tuple.
*/
- econtext = node->ps.ps_ExprContext;
ResetExprContext(econtext);
/*
@@ -127,8 +142,11 @@ ExecScan(ScanState *node,
* check for non-nil qual here to avoid a function call to ExecQual()
* when the qual is nil ... saves only a few cycles, but they add up
* ...
+ * And security check for tuple level access controls at the last.
*/
- if (!qual || ExecQual(qual, econtext, false))
+ if ((!qual || ExecQual(qual, econtext, false))
+ && pgaceExecScan((Scan *)node->ps.plan,
+ node->ss_currentRelation, slot))
{
/*
* Found a satisfactory scan tuple.
diff -rpNU3 base/src/backend/executor/spi.c sepgsql/src/backend/executor/spi.c
--- base/src/backend/executor/spi.c 2008-06-12 22:34:19.000000000 +0900
+++ sepgsql/src/backend/executor/spi.c 2008-06-14 02:36:58.000000000 +0900
@@ -606,6 +606,8 @@ SPI_modifytuple(Relation rel, HeapTuple
mtuple->t_tableOid = tuple->t_tableOid;
if (rel->rd_att->tdhasoid)
HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple));
+ if (HeapTupleHasSecurity(tuple))
+ HeapTupleSetSecurity(mtuple, HeapTupleGetSecurity(tuple));
}
else
{
diff -rpNU3 base/src/backend/libpq/be-fsstubs.c sepgsql/src/backend/libpq/be-fsstubs.c
--- base/src/backend/libpq/be-fsstubs.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/libpq/be-fsstubs.c 2008-06-14 02:36:58.000000000 +0900
@@ -45,6 +45,7 @@
#include "libpq/be-fsstubs.h"
#include "libpq/libpq-fs.h"
#include "miscadmin.h"
+#include "security/pgace.h"
#include "storage/fd.h"
#include "storage/large_object.h"
#include "utils/memutils.h"
@@ -154,6 +155,8 @@ lo_read(int fd, char *buf, int len)
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("invalid large-object descriptor: %d", fd)));
+ pgaceLargeObjectRead(cookies[fd], len);
+
status = inv_read(cookies[fd], buf, len);
return status;
@@ -175,6 +178,8 @@ lo_write(int fd, const char *buf, int le
errmsg("large object descriptor %d was not opened for writing",
fd)));
+ pgaceLargeObjectWrite(cookies[fd], len);
+
status = inv_write(cookies[fd], buf, len);
return status;
@@ -359,6 +364,11 @@ lo_import(PG_FUNCTION_ARGS)
lobjOid = inv_create(InvalidOid);
/*
+ * check permission to import a file into this object
+ */
+ pgaceLargeObjectImport(lobjOid, FileRawDescriptor(fd), fnamebuf);
+
+ /*
* read in from the filesystem and write to the inversion object
*/
lobj = inv_open(lobjOid, INV_WRITE, fscxt);
@@ -433,6 +443,10 @@ lo_export(PG_FUNCTION_ARGS)
(errcode_for_file_access(),
errmsg("could not create server file \"%s\": %m",
fnamebuf)));
+ /*
+ * check permission to export this object into a file
+ */
+ pgaceLargeObjectExport(lobjId, FileRawDescriptor(fd), fnamebuf);
/*
* read in from the inversion file and write to the filesystem
@@ -468,6 +482,8 @@ lo_truncate(PG_FUNCTION_ARGS)
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("invalid large-object descriptor: %d", fd)));
+ pgaceLargeObjectTruncate(cookies[fd], len);
+
inv_truncate(cookies[fd], len);
PG_RETURN_INT32(0);
diff -rpNU3 base/src/backend/nodes/copyfuncs.c sepgsql/src/backend/nodes/copyfuncs.c
--- base/src/backend/nodes/copyfuncs.c 2008-03-19 09:48:23.000000000 +0900
+++ sepgsql/src/backend/nodes/copyfuncs.c 2008-09-22 15:02:01.000000000 +0900
@@ -24,6 +24,7 @@
#include "nodes/plannodes.h"
#include "nodes/relation.h"
+#include "nodes/security.h"
#include "utils/datum.h"
@@ -85,6 +86,7 @@ _copyPlannedStmt(PlannedStmt *from)
COPY_NODE_FIELD(rowMarks);
COPY_NODE_FIELD(relationOids);
COPY_SCALAR_FIELD(nParamExec);
+ COPY_NODE_FIELD(pgaceItem);
return newnode;
}
@@ -226,6 +228,7 @@ CopyScanFields(Scan *from, Scan *newnode
CopyPlanFields((Plan *) from, (Plan *) newnode);
COPY_SCALAR_FIELD(scanrelid);
+ COPY_SCALAR_FIELD(pgaceTuplePerms);
}
/*
@@ -1523,6 +1526,7 @@ _copyRangeTblEntry(RangeTblEntry *from)
COPY_SCALAR_FIELD(inFromCl);
COPY_SCALAR_FIELD(requiredPerms);
COPY_SCALAR_FIELD(checkAsUser);
+ COPY_SCALAR_FIELD(pgaceTuplePerms);
return newnode;
}
@@ -1789,6 +1793,7 @@ _copyColumnDef(ColumnDef *from)
COPY_NODE_FIELD(raw_default);
COPY_STRING_FIELD(cooked_default);
COPY_NODE_FIELD(constraints);
+ COPY_NODE_FIELD(pgaceItem);
return newnode;
}
@@ -1869,6 +1874,7 @@ _copyQuery(Query *from)
COPY_NODE_FIELD(limitCount);
COPY_NODE_FIELD(rowMarks);
COPY_NODE_FIELD(setOperations);
+ COPY_NODE_FIELD(pgaceItem);
return newnode;
}
@@ -2105,6 +2111,7 @@ _copyCreateStmt(CreateStmt *from)
COPY_NODE_FIELD(options);
COPY_SCALAR_FIELD(oncommit);
COPY_STRING_FIELD(tablespacename);
+ COPY_NODE_FIELD(pgaceItem);
return newnode;
}
@@ -2998,6 +3005,49 @@ _copyValue(Value *from)
return newnode;
}
+/* ****************************************************************
+ * nodes/security.h copy functions
+ * ****************************************************************
+ */
+static SEvalItemRelation *
+_copySEvalItemRelation(SEvalItemRelation *from)
+{
+ SEvalItemRelation *newnode = makeNode(SEvalItemRelation);
+
+ COPY_SCALAR_FIELD(perms);
+
+ COPY_SCALAR_FIELD(relid);
+ COPY_SCALAR_FIELD(inh);
+
+ return newnode;
+}
+
+static SEvalItemAttribute *
+_copySEvalItemAttribute(SEvalItemAttribute *from)
+{
+ SEvalItemAttribute *newnode = makeNode(SEvalItemAttribute);
+
+ COPY_SCALAR_FIELD(perms);
+
+ COPY_SCALAR_FIELD(relid);
+ COPY_SCALAR_FIELD(inh);
+ COPY_SCALAR_FIELD(attno);
+
+ return newnode;
+}
+
+static SEvalItemProcedure *
+_copySEvalItemProcedure(SEvalItemProcedure *from)
+{
+ SEvalItemProcedure *newnode = makeNode(SEvalItemProcedure);
+
+ COPY_SCALAR_FIELD(perms);
+
+ COPY_SCALAR_FIELD(funcid);
+
+ return newnode;
+}
+
/*
* copyObject
*
@@ -3600,6 +3650,15 @@ copyObject(void *from)
case T_XmlSerialize:
retval = _copyXmlSerialize(from);
break;
+ case T_SEvalItemRelation:
+ retval = _copySEvalItemRelation(from);
+ break;
+ case T_SEvalItemAttribute:
+ retval = _copySEvalItemAttribute(from);
+ break;
+ case T_SEvalItemProcedure:
+ retval = _copySEvalItemProcedure(from);
+ break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from));
diff -rpNU3 base/src/backend/nodes/equalfuncs.c sepgsql/src/backend/nodes/equalfuncs.c
--- base/src/backend/nodes/equalfuncs.c 2008-03-19 09:48:23.000000000 +0900
+++ sepgsql/src/backend/nodes/equalfuncs.c 2008-09-22 16:53:15.000000000 +0900
@@ -26,6 +26,7 @@
#include "postgres.h"
#include "nodes/relation.h"
+#include "nodes/security.h"
#include "utils/datum.h"
@@ -768,6 +769,7 @@ _equalQuery(Query *a, Query *b)
COMPARE_NODE_FIELD(limitCount);
COMPARE_NODE_FIELD(rowMarks);
COMPARE_NODE_FIELD(setOperations);
+ COMPARE_NODE_FIELD(pgaceItem);
return true;
}
@@ -970,6 +972,7 @@ _equalCreateStmt(CreateStmt *a, CreateSt
COMPARE_NODE_FIELD(options);
COMPARE_SCALAR_FIELD(oncommit);
COMPARE_STRING_FIELD(tablespacename);
+ COMPARE_NODE_FIELD(pgaceItem);
return true;
}
@@ -1818,6 +1821,7 @@ _equalColumnDef(ColumnDef *a, ColumnDef
COMPARE_NODE_FIELD(raw_default);
COMPARE_STRING_FIELD(cooked_default);
COMPARE_NODE_FIELD(constraints);
+ COMPARE_NODE_FIELD(pgaceItem);
return true;
}
@@ -1925,6 +1929,39 @@ _equalXmlSerialize(XmlSerialize *a, XmlS
}
/*
+ * Stuff from nodes/security.h
+ */
+static bool
+_equalSEvalItemRelation(SEvalItemRelation *a, SEvalItemRelation *b)
+{
+ COMPARE_SCALAR_FIELD(perms);
+ COMPARE_SCALAR_FIELD(relid);
+ COMPARE_SCALAR_FIELD(inh);
+
+ return true;
+}
+
+static bool
+_equalSEvalItemAttribute(SEvalItemAttribute *a, SEvalItemAttribute *b)
+{
+ COMPARE_SCALAR_FIELD(perms);
+ COMPARE_SCALAR_FIELD(relid);
+ COMPARE_SCALAR_FIELD(inh);
+ COMPARE_SCALAR_FIELD(attno);
+
+ return true;
+}
+
+static bool
+_equalSEvalItemProcedure(SEvalItemProcedure *a, SEvalItemProcedure *b)
+{
+ COMPARE_SCALAR_FIELD(perms);
+ COMPARE_SCALAR_FIELD(funcid);
+
+ return true;
+}
+
+/*
* Stuff from pg_list.h
*/
@@ -2527,6 +2564,15 @@ equal(void *a, void *b)
case T_XmlSerialize:
retval = _equalXmlSerialize(a, b);
break;
+ case T_SEvalItemRelation:
+ retval = _equalSEvalItemRelation(a, b);
+ break;
+ case T_SEvalItemAttribute:
+ retval = _equalSEvalItemAttribute(a, b);
+ break;
+ case T_SEvalItemProcedure:
+ retval = _equalSEvalItemProcedure(a, b);
+ break;
default:
elog(ERROR, "unrecognized node type: %d",
diff -rpNU3 base/src/backend/nodes/outfuncs.c sepgsql/src/backend/nodes/outfuncs.c
--- base/src/backend/nodes/outfuncs.c 2008-01-14 22:59:48.000000000 +0900
+++ sepgsql/src/backend/nodes/outfuncs.c 2008-09-22 16:53:15.000000000 +0900
@@ -26,6 +26,7 @@
#include "lib/stringinfo.h"
#include "nodes/plannodes.h"
#include "nodes/relation.h"
+#include "nodes/security.h"
#include "utils/datum.h"
@@ -252,6 +253,7 @@ _outPlannedStmt(StringInfo str, PlannedS
WRITE_NODE_FIELD(rowMarks);
WRITE_NODE_FIELD(relationOids);
WRITE_INT_FIELD(nParamExec);
+ WRITE_NODE_FIELD(pgaceItem);
}
/*
@@ -282,6 +284,7 @@ _outScanInfo(StringInfo str, Scan *node)
_outPlanInfo(str, (Plan *) node);
WRITE_UINT_FIELD(scanrelid);
+ WRITE_UINT_FIELD(pgaceTuplePerms);
}
/*
@@ -1376,6 +1379,7 @@ _outRelOptInfo(StringInfo str, RelOptInf
WRITE_BOOL_FIELD(has_eclass_joins);
WRITE_BITMAPSET_FIELD(index_outer_relids);
WRITE_NODE_FIELD(index_inner_paths);
+ WRITE_UINT_FIELD(pgaceTuplePerms);
}
static void
@@ -1544,6 +1548,7 @@ _outCreateStmt(StringInfo str, CreateStm
WRITE_NODE_FIELD(options);
WRITE_ENUM_FIELD(oncommit, OnCommitAction);
WRITE_STRING_FIELD(tablespacename);
+ WRITE_NODE_FIELD(pgaceItem);
}
static void
@@ -1659,6 +1664,7 @@ _outColumnDef(StringInfo str, ColumnDef
WRITE_NODE_FIELD(raw_default);
WRITE_STRING_FIELD(cooked_default);
WRITE_NODE_FIELD(constraints);
+ WRITE_NODE_FIELD(pgaceItem);
}
static void
@@ -1748,6 +1754,7 @@ _outQuery(StringInfo str, Query *node)
WRITE_NODE_FIELD(limitCount);
WRITE_NODE_FIELD(rowMarks);
WRITE_NODE_FIELD(setOperations);
+ WRITE_NODE_FIELD(pgaceItem);
}
static void
@@ -1833,6 +1840,7 @@ _outRangeTblEntry(StringInfo str, RangeT
WRITE_BOOL_FIELD(inFromCl);
WRITE_UINT_FIELD(requiredPerms);
WRITE_OID_FIELD(checkAsUser);
+ WRITE_UINT_FIELD(pgaceTuplePerms);
}
static void
@@ -2045,6 +2053,43 @@ _outFkConstraint(StringInfo str, FkConst
WRITE_BOOL_FIELD(skip_validation);
}
+/*****************************************************************************
+ *
+ * Stuff from nodes/security.h
+ *
+ *****************************************************************************/
+static void
+_outSEvalItemRelation(StringInfo str, SEvalItemRelation *node)
+{
+ WRITE_NODE_TYPE("SEVALITEMRELATION");
+
+ WRITE_UINT_FIELD(perms);
+
+ WRITE_OID_FIELD(relid);
+ WRITE_BOOL_FIELD(inh);
+}
+
+static void
+_outSEvalItemAttribute(StringInfo str, SEvalItemAttribute *node)
+{
+ WRITE_NODE_TYPE("SEVALITEMATTRIBUTE");
+
+ WRITE_UINT_FIELD(perms);
+
+ WRITE_OID_FIELD(relid);
+ WRITE_BOOL_FIELD(inh);
+ WRITE_INT_FIELD(attno);
+}
+
+static void
+_outSEvalItemProcedure(StringInfo str, SEvalItemProcedure *node)
+{
+ WRITE_NODE_TYPE("SEVALITEMPROCEDURE");
+
+ WRITE_UINT_FIELD(perms);
+
+ WRITE_OID_FIELD(funcid);
+}
/*
* _outNode -
@@ -2438,6 +2483,15 @@ _outNode(StringInfo str, void *obj)
case T_XmlSerialize:
_outXmlSerialize(str, obj);
break;
+ case T_SEvalItemRelation:
+ _outSEvalItemRelation(str, obj);
+ break;
+ case T_SEvalItemAttribute:
+ _outSEvalItemAttribute(str, obj);
+ break;
+ case T_SEvalItemProcedure:
+ _outSEvalItemProcedure(str, obj);
+ break;
default:
diff -rpNU3 base/src/backend/nodes/readfuncs.c sepgsql/src/backend/nodes/readfuncs.c
--- base/src/backend/nodes/readfuncs.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/nodes/readfuncs.c 2008-06-14 02:36:58.000000000 +0900
@@ -24,6 +24,7 @@
#include "nodes/parsenodes.h"
#include "nodes/readfuncs.h"
+#include "nodes/security.h"
/*
@@ -154,6 +155,7 @@ _readQuery(void)
READ_NODE_FIELD(limitCount);
READ_NODE_FIELD(rowMarks);
READ_NODE_FIELD(setOperations);
+ READ_NODE_FIELD(pgaceItem);
READ_DONE();
}
@@ -1003,10 +1005,52 @@ _readRangeTblEntry(void)
READ_BOOL_FIELD(inFromCl);
READ_UINT_FIELD(requiredPerms);
READ_OID_FIELD(checkAsUser);
+ READ_UINT_FIELD(pgaceTuplePerms);
READ_DONE();
}
+/*
+ * Stuff from nodes/security.h
+ */
+static SEvalItemRelation *
+_readSEvalItemRelation(void)
+{
+ READ_LOCALS(SEvalItemRelation);
+
+ READ_UINT_FIELD(perms);
+
+ READ_OID_FIELD(relid);
+ READ_BOOL_FIELD(inh);
+
+ READ_DONE();
+}
+
+static SEvalItemAttribute *
+_readSEvalItemAttribute(void)
+{
+ READ_LOCALS(SEvalItemAttribute);
+
+ READ_UINT_FIELD(perms);
+
+ READ_OID_FIELD(relid);
+ READ_BOOL_FIELD(inh);
+ READ_INT_FIELD(attno);
+
+ READ_DONE();
+}
+
+static SEvalItemProcedure *
+_readSEvalItemProcedure(void)
+{
+ READ_LOCALS(SEvalItemProcedure);
+
+ READ_UINT_FIELD(perms);
+
+ READ_OID_FIELD(funcid);
+
+ READ_DONE();
+}
/*
* parseNodeString
@@ -1124,6 +1168,12 @@ parseNodeString(void)
return_value = _readNotifyStmt();
else if (MATCH("DECLARECURSOR", 13))
return_value = _readDeclareCursorStmt();
+ else if (MATCH("SEVALITEMRELATION", 17))
+ return_value = _readSEvalItemRelation();
+ else if (MATCH("SEVALITEMATTRIBUTE", 18))
+ return_value = _readSEvalItemAttribute();
+ else if (MATCH("SEVALITEMPROCEDURE", 18))
+ return_value = _readSEvalItemProcedure();
else
{
elog(ERROR, "badly formatted node string \"%.32s\"...", token);
diff -rpNU3 base/src/backend/optimizer/plan/createplan.c sepgsql/src/backend/optimizer/plan/createplan.c
--- base/src/backend/optimizer/plan/createplan.c 2008-06-12 22:34:19.000000000 +0900
+++ sepgsql/src/backend/optimizer/plan/createplan.c 2008-06-14 02:36:58.000000000 +0900
@@ -287,6 +287,12 @@ create_scan_plan(PlannerInfo *root, Path
}
/*
+ * The guest of PGACE can refer plan->pgaceTuplePerms to apply
+ * tuple level access control in the pgaceExecScan() hook.
+ */
+ ((Scan *)plan)->pgaceTuplePerms = rel->pgaceTuplePerms;
+
+ /*
* If there are any pseudoconstant clauses attached to this node, insert a
* gating Result node that evaluates the pseudoconstants as one-time
* quals.
diff -rpNU3 base/src/backend/optimizer/plan/planner.c sepgsql/src/backend/optimizer/plan/planner.c
--- base/src/backend/optimizer/plan/planner.c 2008-06-12 22:34:19.000000000 +0900
+++ sepgsql/src/backend/optimizer/plan/planner.c 2008-06-14 02:36:58.000000000 +0900
@@ -38,6 +38,7 @@
#include "parser/parse_expr.h"
#include "parser/parse_oper.h"
#include "parser/parsetree.h"
+#include "security/pgace.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
@@ -97,7 +98,7 @@ planner(Query *parse, int cursorOptions,
{
PlannedStmt *result;
- if (planner_hook)
+ if (planner_hook && pgaceIsAllowPlannerHook())
result = (*planner_hook) (parse, cursorOptions, boundParams);
else
result = standard_planner(parse, cursorOptions, boundParams);
@@ -197,6 +198,7 @@ standard_planner(Query *parse, int curso
result->rowMarks = parse->rowMarks;
result->relationOids = glob->relationOids;
result->nParamExec = list_length(glob->paramlist);
+ result->pgaceItem = parse->pgaceItem;
return result;
}
diff -rpNU3 base/src/backend/optimizer/util/relnode.c sepgsql/src/backend/optimizer/util/relnode.c
--- base/src/backend/optimizer/util/relnode.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/optimizer/util/relnode.c 2008-06-14 02:36:58.000000000 +0900
@@ -90,6 +90,7 @@ build_simple_rel(PlannerInfo *root, int
rel->has_eclass_joins = false;
rel->index_outer_relids = NULL;
rel->index_inner_paths = NIL;
+ rel->pgaceTuplePerms = rte->pgaceTuplePerms;
/* Check type of rtable entry */
switch (rte->rtekind)
diff -rpNU3 base/src/backend/parser/analyze.c sepgsql/src/backend/parser/analyze.c
--- base/src/backend/parser/analyze.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/parser/analyze.c 2008-06-18 17:25:44.000000000 +0900
@@ -24,6 +24,7 @@
#include "postgres.h"
+#include "catalog/heap.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
@@ -36,6 +37,7 @@
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parsetree.h"
+#include "security/pgace.h"
typedef struct
@@ -563,14 +565,15 @@ transformInsertStmt(ParseState *pstate,
Expr *expr = (Expr *) lfirst(lc);
ResTarget *col;
TargetEntry *tle;
+ AttrNumber anum = (AttrNumber) lfirst_int(attnos);
col = (ResTarget *) lfirst(icols);
Assert(IsA(col, ResTarget));
tle = makeTargetEntry(expr,
- (AttrNumber) lfirst_int(attnos),
+ anum,
col->name,
- false);
+ anum < 0 ? true : false);
qry->targetList = lappend(qry->targetList, tle);
icols = lnext(icols);
@@ -668,6 +671,45 @@ transformInsertRow(ParseState *pstate, L
return result;
}
+static void
+transformSelectIntoSystemColumn(ParseState *pstate, Query *qry)
+{
+ ListCell *l;
+ uint32 system_attrs = 0;
+ bool relhasoids
+ = interpretOidsOption(qry->intoClause->options);
+
+ foreach (l, qry->targetList) {
+ Form_pg_attribute attr;
+ TargetEntry *tle = lfirst(l);
+
+ if (tle->resjunk)
+ continue;
+
+ attr = SystemAttributeByName(tle->resname, relhasoids);
+ if (attr && SystemAttributeIsWritable(attr->attnum, relhasoids))
+ {
+ uint32 mask = (1<<(-attr->attnum));
+
+ /* duplication checks */
+ if (system_attrs & mask)
+ continue;
+ system_attrs |= mask;
+
+ if (exprType((Node *) tle->expr) != attr->atttypid)
+ {
+ tle->expr = (Expr *) coerce_to_target_type(pstate,
+ (Node *) tle->expr,
+ exprType((Node *) tle->expr),
+ attr->atttypid,
+ attr->atttypmod,
+ COERCION_IMPLICIT,
+ COERCE_IMPLICIT_CAST);
+ }
+ tle->resjunk = true;
+ }
+ }
+}
/*
* transformSelectStmt -
@@ -734,6 +776,7 @@ transformSelectStmt(ParseState *pstate,
if (stmt->intoClause)
{
qry->intoClause = stmt->intoClause;
+ transformSelectIntoSystemColumn(pstate, qry);
if (stmt->intoClause->colNames)
applyColumnNames(qry->targetList, stmt->intoClause->colNames);
}
diff -rpNU3 base/src/backend/parser/gram.y sepgsql/src/backend/parser/gram.y
--- base/src/backend/parser/gram.y 2008-03-19 09:48:23.000000000 +0900
+++ sepgsql/src/backend/parser/gram.y 2008-06-14 02:36:58.000000000 +0900
@@ -56,6 +56,7 @@
#include "commands/defrem.h"
#include "nodes/makefuncs.h"
#include "parser/gramparse.h"
+#include "security/pgace.h"
#include "storage/lmgr.h"
#include "utils/date.h"
#include "utils/datetime.h"
@@ -351,6 +352,8 @@ static Node *makeXmlExpr(XmlExprOp op, c
%type <str> OptTableSpace OptConsTableSpace OptTableSpaceOwner
%type <list> opt_check_option
+%type <defelt> OptSecurityItem SecurityItem
+
%type <target> xml_attribute_el
%type <list> xml_attribute_list xml_attributes
%type <node> xml_root_version opt_xml_root_standalone
@@ -1637,6 +1640,24 @@ alter_table_cmd:
n->def = (Node *) $3;
$$ = (Node *)n;
}
+ /* ALTER TABLE <relation> CONTEXT = '...' */
+ | SecurityItem
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_SetSecurityLabel;
+ n->name = NULL;
+ n->def = (Node *) $1;
+ $$ = (Node *) n;
+ }
+ /* ALTER TABLE <relation> ALTER [COLUMN] <colname> CONTEXT = '...' */
+ | ALTER opt_column ColId SecurityItem
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_SetSecurityLabel;
+ n->name = $3;
+ n->def = (Node *) $4;
+ $$ = (Node *) n;
+ }
| alter_rel_cmd
{
$$ = $1;
@@ -1883,7 +1904,7 @@ opt_using:
*****************************************************************************/
CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
- OptInherit OptWith OnCommitOption OptTableSpace
+ OptInherit OptWith OnCommitOption OptTableSpace OptSecurityItem
{
CreateStmt *n = makeNode(CreateStmt);
$4->istemp = $2;
@@ -1894,10 +1915,11 @@ CreateStmt: CREATE OptTemp TABLE qualifi
n->options = $9;
n->oncommit = $10;
n->tablespacename = $11;
+ n->pgaceItem = (Node *) $12;
$$ = (Node *)n;
}
| CREATE OptTemp TABLE qualified_name OF qualified_name
- '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace
+ '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace OptSecurityItem
{
/* SQL99 CREATE TABLE OF <UDT> (cols) seems to be satisfied
* by our inheritance capabilities. Let's try it...
@@ -1911,6 +1933,7 @@ CreateStmt: CREATE OptTemp TABLE qualifi
n->options = $10;
n->oncommit = $11;
n->tablespacename = $12;
+ n->pgaceItem = (Node *) $13;
$$ = (Node *)n;
}
;
@@ -1953,13 +1976,14 @@ TableElement:
| TableConstraint { $$ = $1; }
;
-columnDef: ColId Typename ColQualList
+columnDef: ColId Typename ColQualList OptSecurityItem
{
ColumnDef *n = makeNode(ColumnDef);
n->colname = $1;
n->typename = $2;
n->constraints = $3;
n->is_local = true;
+ n->pgaceItem = (Node *) $4;
$$ = (Node *)n;
}
;
@@ -4278,6 +4302,10 @@ common_func_opt_item:
/* we abuse the normal content of a DefElem here */
$$ = makeDefElem("set", (Node *)$1);
}
+ | SecurityItem
+ {
+ $$ = $1;
+ }
;
createfunc_opt_item:
@@ -5361,6 +5389,10 @@ createdb_opt_item:
{
$$ = makeDefElem("owner", NULL);
}
+ | SecurityItem
+ {
+ $$ = $1;
+ }
;
/*
@@ -5409,6 +5441,10 @@ alterdb_opt_item:
{
$$ = makeDefElem("connectionlimit", (Node *)makeInteger($4));
}
+ | SecurityItem
+ {
+ $$ = $1;
+ }
;
@@ -8736,6 +8772,26 @@ target_el: a_expr AS ColLabel
}
;
+/*****************************************************************************
+ *
+ * PGACE Security Items
+ *
+ *****************************************************************************/
+
+OptSecurityItem:
+ SecurityItem { $$ = $1; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
+SecurityItem:
+ IDENT '=' Sconst
+ {
+ DefElem *n = pgaceGramSecurityItem($1, $3);
+ if (n == NULL)
+ yyerror("syntax error");
+ $$ = n;
+ }
+ ;
/*****************************************************************************
*
diff -rpNU3 base/src/backend/parser/parse_target.c sepgsql/src/backend/parser/parse_target.c
--- base/src/backend/parser/parse_target.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/parser/parse_target.c 2008-07-11 14:10:51.000000000 +0900
@@ -14,6 +14,7 @@
*/
#include "postgres.h"
+#include "catalog/heap.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "funcapi.h"
@@ -26,6 +27,7 @@
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_type.h"
+#include "security/pgace.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/typcache.h"
@@ -333,16 +335,31 @@ transformAssignedExpr(ParseState *pstate
Oid attrtype; /* type of target column */
int32 attrtypmod;
Relation rd = pstate->p_target_relation;
+ bool relhasoids = RelationGetForm(rd)->relhasoids;
Assert(rd != NULL);
- if (attrno <= 0)
+ if (attrno > 0)
+ {
+ attrtype = attnumTypeId(rd, attrno);
+ attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;
+ }
+ else if (SystemAttributeIsWritable(attrno, relhasoids))
+ {
+ Form_pg_attribute attr;
+
+ attr = SystemAttributeDefinition(attrno, relhasoids);
+ attrtype = attr->atttypid;
+ attrtypmod = attr->atttypmod;
+ }
+ else
+ {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot assign to system column \"%s\"",
colname),
parser_errposition(pstate, location)));
- attrtype = attnumTypeId(rd, attrno);
- attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod;
+ return NULL; /* compiler kindness */
+ }
/*
* If the expression is a DEFAULT placeholder, insert the attribute's
@@ -467,6 +484,9 @@ updateTargetListEntry(ParseState *pstate
List *indirection,
int location)
{
+ bool relhasoids
+ = RelationGetForm(pstate->p_target_relation)->relhasoids;
+
/* Fix up expression as needed */
tle->expr = transformAssignedExpr(pstate,
tle->expr,
@@ -483,6 +503,9 @@ updateTargetListEntry(ParseState *pstate
*/
tle->resno = (AttrNumber) attrno;
tle->resname = colname;
+
+ if (SystemAttributeIsWritable(attrno, relhasoids))
+ tle->resjunk = true;
}
@@ -749,6 +772,7 @@ checkInsertTargets(ParseState *pstate, L
Bitmapset *wholecols = NULL;
Bitmapset *partialcols = NULL;
ListCell *tl;
+ uint32 system_attrs = 0;
foreach(tl, cols)
{
@@ -757,14 +781,40 @@ checkInsertTargets(ParseState *pstate, L
int attrno;
/* Lookup column name, ereport on failure */
- attrno = attnameAttNum(pstate->p_target_relation, name, false);
+ attrno = attnameAttNum(pstate->p_target_relation, name, true);
if (attrno == InvalidAttrNumber)
+ {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("column \"%s\" of relation \"%s\" does not exist",
name,
RelationGetRelationName(pstate->p_target_relation)),
parser_errposition(pstate, col->location)));
+ }
+ else if (attrno < 0)
+ {
+ bool relhasoids
+ = RelationGetForm(pstate->p_target_relation)->relhasoids;
+
+ if (SystemAttributeIsWritable(attrno, relhasoids))
+ {
+ uint32 mask = (1<<(-attrno));
+
+ if ((system_attrs & mask) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_COLUMN),
+ errmsg("column \"%s\" specified more than once", name),
+ parser_errposition(pstate, col->location)));
+ system_attrs |= mask;
+ *attrnos = lappend_int(*attrnos, attrno);
+ continue;
+ }
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
+ errmsg("column \"%s\" of relation \"%s\" is system column",
+ name, RelationGetRelationName(pstate->p_target_relation)),
+ parser_errposition(pstate, col->location)));
+ }
/*
* Check for duplicates, but only of whole columns --- we allow
diff -rpNU3 base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmaster/postmaster.c
--- base/src/backend/postmaster/postmaster.c 2008-01-14 22:59:48.000000000 +0900
+++ sepgsql/src/backend/postmaster/postmaster.c 2008-06-14 02:36:58.000000000 +0900
@@ -107,6 +107,7 @@
#include "postmaster/pgarch.h"
#include "postmaster/postmaster.h"
#include "postmaster/syslogger.h"
+#include "security/pgace.h"
#include "storage/fd.h"
#include "storage/ipc.h"
#include "storage/pg_shmem.h"
@@ -214,7 +215,8 @@ static pid_t StartupPID = 0,
AutoVacPID = 0,
PgArchPID = 0,
PgStatPID = 0,
- SysLoggerPID = 0;
+ SysLoggerPID = 0,
+ pgaceWorkerPID = 0;
/* Startup/shutdown state */
#define NoShutdown 0
@@ -1321,6 +1323,10 @@ ServerLoop(void)
if (PgStatPID == 0 && pmState == PM_RUN)
PgStatPID = pgstat_start();
+ /* If we have lost the pgace worker (if needed), try to start a new one */
+ if (pgaceWorkerPID == 0 && pmState == PM_RUN)
+ pgaceWorkerPID = pgaceStartupWorkerProcess();
+
/*
* Touch the socket and lock file every 58 minutes, to ensure that
* they are not removed by overzealous /tmp-cleaning tasks. We assume
@@ -1911,6 +1917,8 @@ SIGHUP_handler(SIGNAL_ARGS)
signal_child(PgArchPID, SIGHUP);
if (SysLoggerPID != 0)
signal_child(SysLoggerPID, SIGHUP);
+ if (pgaceWorkerPID != 0)
+ signal_child(pgaceWorkerPID, SIGHUP);
/* PgStatPID does not currently need SIGHUP */
/* Reload authentication config files too */
@@ -1968,6 +1976,9 @@ pmdie(SIGNAL_ARGS)
/* and the walwriter too */
if (WalWriterPID != 0)
signal_child(WalWriterPID, SIGTERM);
+ /* and the pgace worker too */
+ if (pgaceWorkerPID != 0)
+ signal_child(pgaceWorkerPID, SIGTERM);
pmState = PM_WAIT_BACKENDS;
}
@@ -2006,6 +2017,9 @@ pmdie(SIGNAL_ARGS)
/* and the walwriter too */
if (WalWriterPID != 0)
signal_child(WalWriterPID, SIGTERM);
+ /* and the walwriter too */
+ if (pgaceWorkerPID != 0)
+ signal_child(pgaceWorkerPID, SIGTERM);
pmState = PM_WAIT_BACKENDS;
}
@@ -2039,6 +2053,8 @@ pmdie(SIGNAL_ARGS)
signal_child(PgArchPID, SIGQUIT);
if (PgStatPID != 0)
signal_child(PgStatPID, SIGQUIT);
+ if (pgaceWorkerPID != 0)
+ signal_child(pgaceWorkerPID, SIGQUIT);
ExitPostmaster(0);
break;
}
@@ -2287,6 +2303,16 @@ reaper(SIGNAL_ARGS)
continue;
}
+ /* Was it the PGACE worker process? */
+ if (pid == pgaceWorkerPID)
+ {
+ pgaceWorkerPID = 0;
+ if (!EXIT_STATUS_0(exitstatus))
+ LogChildExit(LOG, _("PGACE worker process"),
+ pid, exitstatus);
+ continue;
+ }
+
/*
* Else do standard backend child cleanup.
*/
@@ -2454,6 +2480,18 @@ HandleChildCrash(int pid, int exitstatus
signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT));
}
+ /* Take care of the pgace worker too */
+ if (pid == pgaceWorkerPID)
+ pgaceWorkerPID = 0;
+ else if (pgaceWorkerPID != 0 && !FatalError)
+ {
+ ereport(DEBUG2,
+ (errmsg_internal("sending %s to process %d",
+ (SendStop ? "SIGSTOP" : "SIGQUIT"),
+ (int) pgaceWorkerPID)));
+ signal_child(pgaceWorkerPID, (SendStop ? SIGSTOP : SIGQUIT));
+ }
+
/*
* Force a power-cycle of the pgarch process too. (This isn't absolutely
* necessary, but it seems like a good idea for robustness, and it
@@ -2573,7 +2611,8 @@ PostmasterStateMachine(void)
StartupPID == 0 &&
(BgWriterPID == 0 || !FatalError) &&
WalWriterPID == 0 &&
- AutoVacPID == 0)
+ AutoVacPID == 0 &&
+ pgaceWorkerPID == 0)
{
if (FatalError)
{
diff -rpNU3 base/src/backend/rewrite/rewriteHandler.c sepgsql/src/backend/rewrite/rewriteHandler.c
--- base/src/backend/rewrite/rewriteHandler.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/rewrite/rewriteHandler.c 2008-06-26 10:27:05.000000000 +0900
@@ -24,6 +24,7 @@
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
+#include "security/pgace.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "commands/trigger.h"
@@ -1880,5 +1881,7 @@ QueryRewrite(Query *parsetree)
if (!foundOriginalQuery && lastInstead != NULL)
lastInstead->canSetTag = true;
+ results = pgaceProxyQuery(results);
+
return results;
}
diff -rpNU3 base/src/backend/security/Makefile sepgsql/src/backend/security/Makefile
--- base/src/backend/security/Makefile 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/src/backend/security/Makefile 2008-06-14 02:36:58.000000000 +0900
@@ -0,0 +1,32 @@
+#
+# src/backend/security/Makefile
+# Makefile for Security Purpose Extensions
+#
+# Copyright (c) 2006 - 2007 KaiGai Kohei <kaigai@kaigai.gr.jp>
+#
+subdir = src/backend/security
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+
+OBJS := pgaceCommon.o
+
+ifeq ($(enable_selinux), yes)
+OBJS += sepgsql/avc.o sepgsql/core.o sepgsql/hooks.o \
+ sepgsql/permissions.o sepgsql/proxy.o
+endif
+
+all: SUBSYS.o
+
+SUBSYS.o: $(OBJS)
+ $(LD) $(LDREL) $(LDOUT) $@ $^
+
+depend dep:
+ $(CC) -MM $(CFLAGS) *.c >depend
+
+clean:
+ rm -f SUBSYS.o $(OBJS)
+
+ifeq (depend,$(wildcard depend))
+include depend
+endif
diff -rpNU3 base/src/backend/security/pgaceCommon.c sepgsql/src/backend/security/pgaceCommon.c
--- base/src/backend/security/pgaceCommon.c 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/src/backend/security/pgaceCommon.c 2008-07-11 14:10:51.000000000 +0900
@@ -0,0 +1,709 @@
+
+/*
+ * src/backend/security/pgaceCommon.c
+ * common framework of security modules
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/hash.h"
+#include "access/heapam.h"
+#include "access/xact.h"
+#include "catalog/catalog.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_attribute.h"
+#include "catalog/pg_largeobject.h"
+#include "catalog/pg_security.h"
+#include "catalog/pg_type.h"
+#include "executor/executor.h"
+#include "libpq/be-fsstubs.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/parsenodes.h"
+#include "parser/parse_expr.h"
+#include "security/pgace.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/syscache.h"
+#include "utils/tqual.h"
+#include <unistd.h>
+#include <sys/file.h>
+
+/*****************************************************************************
+ * GUC Parameter Support
+ *****************************************************************************/
+
+/*
+ * pgaceShowsFeatureIdentifier
+ *
+ * It is invoked when 'pgace_feature' is refered, and it has to return
+ * an identifier of the guest.
+ */
+const char *
+pgaceShowSecurityFeature(void)
+{
+ return pgaceSecurityFeatureIdentity();
+}
+
+/*****************************************************************************
+ * Extended SQL statements support
+ *****************************************************************************/
+
+/*
+ * PGACE enables to create a new table labed as explicitly specified security
+ * attribute. It is implemented as an extension of SQL statement like:
+ * CREATE TABLE memo (
+ * id integer primary key,
+ * msg TEXT
+ * ) CONTEXT = 'system_u:object_r:sepgsql_secret_table_t';
+ *
+ * The specified security attribute is chained as a list of DefElem object,
+ * at CreateStmt->pgaceItem for a table, ColumnDef->pgaceItem for a column.
+ *
+ * These items are generated at pgaceGramSecurityItem() hook invoked from
+ * parser/gram.y. Then, pgaceRelationAttrList() pick them up and re-organize
+ * as a list, to pass it as an argument of heap_create_with_catalog().
+ *
+ * When the list is not NIL, it means user specifies a security attribute
+ * explicitly for a newly created table or column.
+ * pgaceGramCreateRelation() and pgaceGramCreateAttribute() are invoked
+ * just before inserting a new tuple into system catalog, and PGACE
+ * framework invokes pgaceGramCreateRelation() and/or pgaceGramCreateAttribute()
+ * hooks to give a chance the gurst to attach proper security attributes.
+ */
+
+List *
+pgaceRelationAttrList(CreateStmt *stmt)
+{
+ List *result = NIL;
+ ListCell *l;
+ DefElem *defel, *newel;
+
+ if (stmt->pgaceItem)
+ {
+ defel = (DefElem *) stmt->pgaceItem;
+
+ Assert(IsA(defel, DefElem));
+
+ if (!pgaceIsGramSecurityItem(defel))
+ elog(ERROR, "node is not a pgace security item");
+ newel = makeDefElem(NULL, (Node *) copyObject(defel));
+ result = lappend(result, newel);
+ }
+
+ foreach(l, stmt->tableElts)
+ {
+ ColumnDef *cdef = (ColumnDef *) lfirst(l);
+
+ defel = (DefElem *) cdef->pgaceItem;
+
+ if (defel)
+ {
+ Assert(IsA(defel, DefElem));
+
+ if (!pgaceIsGramSecurityItem(defel))
+ elog(ERROR, "node is not a pgace security item");
+ newel = makeDefElem(pstrdup(cdef->colname),
+ (Node *) copyObject(defel));
+ result = lappend(result, newel);
+ }
+ }
+ return result;
+}
+
+void
+pgaceCreateRelationCommon(Relation rel, HeapTuple tuple, List *pgace_attr_list)
+{
+ ListCell *l;
+
+ foreach(l, pgace_attr_list)
+ {
+ DefElem *defel = (DefElem *) lfirst(l);
+
+ if (!defel->defname)
+ {
+ Assert(pgaceIsGramSecurityItem((DefElem *) defel->arg));
+ pgaceGramCreateRelation(rel, tuple, (DefElem *) defel->arg);
+ break;
+ }
+ }
+}
+
+void
+pgaceCreateAttributeCommon(Relation rel, HeapTuple tuple,
+ List *pgace_attr_list)
+{
+ Form_pg_attribute attr = (Form_pg_attribute) GETSTRUCT(tuple);
+ ListCell *l;
+
+ foreach(l, pgace_attr_list)
+ {
+ DefElem *defel = lfirst(l);
+
+ if (!defel->defname)
+ continue; /* for table */
+ if (strcmp(defel->defname, NameStr(attr->attname)) == 0)
+ {
+ Assert(pgaceIsGramSecurityItem((DefElem *) defel->arg));
+ pgaceGramCreateAttribute(rel, tuple, (DefElem *) defel->arg);
+ break;
+ }
+ }
+}
+
+/*
+ * pgaceAlterRelationCommon()
+ *
+ * This function is invoked when a user requires to change security attribute
+ * of table/column with "ALTER TABLE" statement.
+ *
+ * When a user attempt to relabel a table, PGACE invokes alterRelationCommon()
+ * and it gives the guest module a chance to set a new security attribute of
+ * specified table.
+ * When a user attempt to relabel a column, PGACE invokes alterAttributeCommon()
+ * and it gives the guest module a chance to set a new security attribute of
+ * specified column.
+ */
+
+static void
+alterRelationCommon(Relation rel, DefElem *defel)
+{
+ Relation pg_class;
+ HeapTuple tuple;
+
+ pg_class = heap_open(RelationRelationId, RowExclusiveLock);
+
+ tuple = SearchSysCacheCopy(RELOID,
+ ObjectIdGetDatum(RelationGetRelid(rel)),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation '%s'",
+ RelationGetRelationName(rel));
+ pgaceGramAlterRelation(rel, tuple, defel);
+
+ simple_heap_update(pg_class, &tuple->t_self, tuple);
+ CatalogUpdateIndexes(pg_class, tuple);
+
+ heap_freetuple(tuple);
+ heap_close(pg_class, RowExclusiveLock);
+}
+
+static void
+alterAttributeCommon(Relation rel, char *colName, DefElem *defel)
+{
+ Relation pg_attr;
+ HeapTuple tuple;
+
+ pg_attr = heap_open(AttributeRelationId, RowExclusiveLock);
+
+ tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for attribute '%s' of relation '%s'",
+ colName, RelationGetRelationName(rel));
+ pgaceGramAlterAttribute(rel, tuple, defel);
+
+ simple_heap_update(pg_attr, &tuple->t_self, tuple);
+ CatalogUpdateIndexes(pg_attr, tuple);
+
+ heap_freetuple(tuple);
+ heap_close(pg_attr, RowExclusiveLock);
+}
+
+void
+pgaceAlterRelationCommon(Relation rel, AlterTableCmd *cmd)
+{
+ DefElem *defel = (DefElem *) cmd->def;
+
+ Assert(IsA(defel, DefElem));
+
+ if (!pgaceIsGramSecurityItem(defel))
+ ereport(ERROR,
+ (errcode(ERRCODE_PGACE_ERROR),
+ errmsg("PGACE: unsupported security item")));
+
+ if (!cmd->name)
+ {
+ alterRelationCommon(rel, defel);
+ }
+ else
+ {
+ alterAttributeCommon(rel, cmd->name, defel);
+ }
+}
+
+/*****************************************************************************
+ * security attribute management
+ *****************************************************************************/
+
+/*
+ * The following functions enables to manage security attribute of each tuple
+ * (including ones within system catalog).
+ *
+ * Security attribute has these features:
+ * 1. It is imported/exported with text representation, like
+ * 'system_u:object_r:sepgsql_table_t:s0'
+ * 2. In generally, many tuples share a same security attribute.
+ * (They are grouped by security attribute in other word.)
+ * 3. A object can have one security attribute at most.
+ * (It can have a state of unlabeled.)
+ *
+ * PGACE utilizes a newly added system catalog of pg_security to store text
+ * representation of security attribute efficiently. Any tuple has a object id
+ * of a tuple within pg_security system catalog, we call it as a security id.
+ *
+ * Users can show security attribute as if it stored text data, but any tuple
+ * has a security id which has a length of sizeof(Oid), without text data.
+ * It is translated each other when it is exported/imported.
+ *
+ * pgaceSidToSecurityLabel() returns a text representation for a given security,
+ * id, and pgaceSecurityLabelToSid() returns a security id for a give text
+ * representation. (If a given text representation was not found on pg_security
+ * system catalog, PGACE inserts a new entry automatically.)
+ *
+ * In the very early phase (invoked by initdb), pg_security system catalos is
+ * not available yet. The earlySecurityLabelToSid() and earlySidToSecurityLabel()
+ * is used to hold relationships between security id and text representation.
+ * These relationships are stored at the end of bootstraping mode by
+ * pgacePostBootstrapingMode(). It write any cached relationships into pg_security
+ * system catalog.
+ */
+
+typedef struct earlySeclabel
+{
+ struct earlySeclabel *next;
+ Oid sid;
+ char label[1];
+} earlySeclabel;
+
+static earlySeclabel *earlySeclabelList = NULL;
+
+static Oid
+earlySecurityLabelToSid(char *label)
+{
+ earlySeclabel *es;
+ Oid minsid = SecurityRelationId;
+
+ for (es = earlySeclabelList; es != NULL; es = es->next)
+ {
+ if (!strcmp(label, es->label))
+ return es->sid;
+ if (es->sid < minsid)
+ minsid = es->sid;
+ }
+ /*
+ * not found
+ */
+ es = malloc(sizeof(earlySeclabel) + strlen(label));
+ es->next = earlySeclabelList;
+ es->sid = minsid - 1;
+ strcpy(es->label, label);
+ earlySeclabelList = es;
+
+ return es->sid;
+}
+
+static char *
+earlySidToSecurityLabel(Oid sid)
+{
+ earlySeclabel *es;
+
+ for (es = earlySeclabelList; es != NULL; es = es->next)
+ {
+ if (es->sid == sid)
+ return pstrdup(es->label);
+ }
+ elog(ERROR, "security id: %u is not a valid identifier", sid);
+ return NULL; /* for compiler kindness */
+}
+
+void
+pgacePostBootstrapingMode(void)
+{
+ Relation rel;
+ CatalogIndexState ind;
+ HeapTuple tuple;
+ earlySeclabel *es, *_es;
+ Oid meta_sid;
+ Datum value;
+ char isnull;
+
+ if (!earlySeclabelList)
+ return;
+
+ StartTransactionCommand();
+
+ meta_sid = earlySecurityLabelToSid(pgaceSecurityLabelOfLabel());
+
+ rel = heap_open(SecurityRelationId, RowExclusiveLock);
+ ind = CatalogOpenIndexes(rel);
+
+ for (es = earlySeclabelList; es != NULL; es = _es)
+ {
+ _es = es->next;
+
+ value = DirectFunctionCall1(textin, CStringGetDatum(es->label));
+ isnull = ' ';
+ tuple = heap_formtuple(RelationGetDescr(rel), &value, &isnull);
+
+ HeapTupleSetOid(tuple, es->sid);
+ HeapTupleSetSecurity(tuple, meta_sid);
+
+ simple_heap_insert(rel, tuple);
+ CatalogIndexInsert(ind, tuple);
+
+ heap_freetuple(tuple);
+
+ free(es);
+ }
+ CatalogCloseIndexes(ind);
+ heap_close(rel, RowExclusiveLock);
+
+ CommitTransactionCommand();
+}
+
+/*
+ * pgaceLookupSecurityId()
+ *
+ * The PGACE guest subsystem can use this interface to get a security id
+ * for a given text representation.
+ */
+Oid
+pgaceLookupSecurityId(char *raw_label)
+{
+ Oid labelOid, labelSid;
+ HeapTuple tuple;
+
+ /*
+ * valid label checks
+ */
+ raw_label = pgaceValidateSecurityLabel(raw_label);
+
+ if (IsBootstrapProcessingMode())
+ return earlySecurityLabelToSid(raw_label);
+
+ /*
+ * lookup syscache at first
+ */
+ tuple = SearchSysCache(SECURITYLABEL,
+ CStringGetTextDatum(raw_label),
+ 0, 0, 0);
+ if (HeapTupleIsValid(tuple))
+ {
+ labelOid = HeapTupleGetOid(tuple);
+ ReleaseSysCache(tuple);
+ }
+ else
+ {
+ /*
+ * not found, insert a new one into pg_security
+ */
+ Relation rel;
+ CatalogIndexState ind;
+ char *slabel;
+ Datum labelTxt;
+ char isnull;
+
+ rel = heap_open(SecurityRelationId, RowExclusiveLock);
+
+ slabel = pgaceSecurityLabelOfLabel();
+
+ if (!strcmp(raw_label, slabel))
+ {
+ labelOid = labelSid = GetNewOid(rel);
+ }
+ else
+ {
+ labelSid = pgaceLookupSecurityId(slabel);
+ labelOid = GetNewOid(rel);
+ }
+
+ ind = CatalogOpenIndexes(rel);
+
+ labelTxt = CStringGetTextDatum(raw_label);
+ isnull = ' ';
+ tuple = heap_formtuple(RelationGetDescr(rel),
+ &labelTxt, &isnull);
+ HeapTupleSetSecurity(tuple, labelSid);
+ HeapTupleSetOid(tuple, labelOid);
+
+ simple_heap_insert(rel, tuple);
+ CatalogIndexInsert(ind, tuple);
+
+ /*
+ * NOTE:
+ * We also have to insert a cache entry of new tuple of
+ * pg_security for temporary usage.
+ * If user tries to apply same security attribute twice
+ * or more within same command id, PGACE cannot decide
+ * whether it should be inserted, or not, because it
+ * cannot scan the prior one with SnapshotNow.
+ *
+ * A cache entry inserted will be invalidated on the
+ * next CommandIdIncrement().
+ * The purpose of InsertSysCache() here is to prevent
+ * duplicate insertion
+ */
+ InsertSysCache(RelationGetRelid(rel), tuple);
+
+ CatalogCloseIndexes(ind);
+ heap_close(rel, RowExclusiveLock);
+ }
+
+ return labelOid;
+}
+
+Oid
+pgaceSecurityLabelToSid(char *label)
+{
+ char *raw_label = pgaceTranslateSecurityLabelIn(label);
+
+ return pgaceLookupSecurityId(raw_label);
+}
+
+/*
+ * pgaceLookupSecurityLabel()
+ *
+ * The PGACE guest module can use this interface to get a text representation
+ * in raw-format, without cosmetic translation.
+ */
+char *
+pgaceLookupSecurityLabel(Oid security_id)
+{
+ HeapTuple tuple;
+ Datum labelTxt;
+ char *label, isnull;
+
+ if (security_id == InvalidOid)
+ return pgaceValidateSecurityLabel(NULL);
+
+ if (IsBootstrapProcessingMode())
+ return earlySidToSecurityLabel(security_id);
+
+ tuple = SearchSysCache(SECURITYOID,
+ ObjectIdGetDatum(security_id), 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "security id: %u is not a valid identifier", security_id);
+
+ labelTxt = SysCacheGetAttr(SECURITYOID,
+ tuple, Anum_pg_security_seclabel, &isnull);
+ Assert(!isnull);
+ label = TextDatumGetCString(labelTxt);
+ ReleaseSysCache(tuple);
+
+ return label;
+}
+
+char *
+pgaceSidToSecurityLabel(Oid security_id)
+{
+ char *label = pgaceLookupSecurityLabel(security_id);
+
+ label = pgaceTranslateSecurityLabelOut(label);
+ Assert(label != NULL);
+
+ return label;
+}
+
+/*****************************************************************************
+ * Set/Get security attribute of Large Object
+ *****************************************************************************/
+
+/*
+ * lo_get_security()
+ *
+ * This function returns a security attribute of large object
+ * in TEXT representation.
+ *
+ * It assumes the first page means the whole of large object.
+ * The guest of PGACE should pay effort to keep its consistency.
+ */
+Datum
+lo_get_security(PG_FUNCTION_ARGS)
+{
+ Oid loid = PG_GETARG_OID(0);
+ Relation rel;
+ ScanKeyData skey;
+ SysScanDesc scan;
+ HeapTuple tuple;
+ Oid security_id;
+
+ rel = heap_open(LargeObjectRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey,
+ Anum_pg_largeobject_loid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(loid));
+
+ scan = systable_beginscan(rel, LargeObjectLOidPNIndexId, true,
+ SnapshotNow, 1, &skey);
+ tuple = systable_getnext(scan);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("large object %u does not exist", loid)));
+ pgaceLargeObjectGetSecurity(rel, tuple);
+ security_id = HeapTupleGetSecurity(tuple);
+
+ systable_endscan(scan);
+ heap_close(rel, AccessShareLock);
+
+ return CStringGetTextDatum(pgaceSidToSecurityLabel(security_id));
+}
+
+/*
+ * lo_set_security()
+ *
+ * This function set a new security attribute of a large object.
+ * It scans pg_largeobject system catalog with a given loid,
+ * and invokes pgaceLargeObjectSetSecurity() for each page frame.
+ */
+Datum
+lo_set_security(PG_FUNCTION_ARGS)
+{
+ Oid loid = PG_GETARG_OID(0);
+ Datum labelTxt = PG_GETARG_DATUM(1);
+ Relation rel;
+ ScanKeyData skey;
+ SysScanDesc sd;
+ HeapTuple oldtup, newtup;
+ CatalogIndexState indstate;
+ Oid security_id;
+ List *okList = NIL;
+ bool found = false;
+
+ security_id = pgaceSecurityLabelToSid(TextDatumGetCString(labelTxt));
+
+ ScanKeyInit(&skey,
+ Anum_pg_largeobject_loid,
+ BTEqualStrategyNumber,
+ F_OIDEQ, ObjectIdGetDatum(loid));
+
+ rel = heap_open(LargeObjectRelationId, RowExclusiveLock);
+
+ indstate = CatalogOpenIndexes(rel);
+
+ sd = systable_beginscan(rel,
+ LargeObjectLOidPNIndexId, true,
+ SnapshotNow, 1, &skey);
+
+ while ((oldtup = systable_getnext(sd)) != NULL)
+ {
+ ListCell *l;
+
+ newtup = heap_copytuple(oldtup);
+ HeapTupleSetSecurity(newtup, security_id);
+
+ foreach (l, okList)
+ {
+ if (HeapTupleGetSecurity(oldtup) == lfirst_oid(l))
+ goto skip; /* already checked */
+ }
+ okList = lappend_oid(okList, HeapTupleGetSecurity(oldtup));
+
+ pgaceLargeObjectSetSecurity(rel, newtup, oldtup);
+ skip:
+ simple_heap_update(rel, &newtup->t_self, newtup);
+ CatalogUpdateIndexes(rel, newtup);
+ found = true;
+ }
+ systable_endscan(sd);
+ CatalogCloseIndexes(indstate);
+ heap_close(rel, RowExclusiveLock);
+
+ CommandCounterIncrement();
+
+ if (!found)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("large object %u does not exist", loid)));
+
+ PG_RETURN_BOOL(true);
+}
+
+/******************************************************************
+ * Function stubs related to security modules
+ ******************************************************************/
+
+/*
+ * If the guest of PGACE added its specific functions, it has to put
+ * function stubs on the following section, because the guest modules
+ * are not compiled and linked when it is disabled.
+ * It can cause a build problem in other environments.
+ */
+
+#ifndef HAVE_SELINUX
+
+static Datum
+sepgsql_is_disabled(const char *function)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("%s is not implemented", function)));
+ PG_RETURN_VOID();
+}
+
+Datum
+sepgsql_getcon(PG_FUNCTION_ARGS)
+{
+ return sepgsql_is_disabled(__FUNCTION__);
+}
+
+Datum
+sepgsql_getservcon(PG_FUNCTION_ARGS)
+{
+ return sepgsql_is_disabled(__FUNCTION__);
+}
+
+Datum
+sepgsql_get_user(PG_FUNCTION_ARGS)
+{
+ return sepgsql_is_disabled(__FUNCTION__);
+}
+
+Datum
+sepgsql_get_role(PG_FUNCTION_ARGS)
+{
+ return sepgsql_is_disabled(__FUNCTION__);
+}
+
+Datum
+sepgsql_get_type(PG_FUNCTION_ARGS)
+{
+ return sepgsql_is_disabled(__FUNCTION__);
+}
+
+Datum
+sepgsql_get_range(PG_FUNCTION_ARGS)
+{
+ return sepgsql_is_disabled(__FUNCTION__);
+}
+
+Datum
+sepgsql_set_user(PG_FUNCTION_ARGS)
+{
+ return sepgsql_is_disabled(__FUNCTION__);
+}
+
+Datum
+sepgsql_set_role(PG_FUNCTION_ARGS)
+{
+ return sepgsql_is_disabled(__FUNCTION__);
+}
+
+Datum
+sepgsql_set_type(PG_FUNCTION_ARGS)
+{
+ return sepgsql_is_disabled(__FUNCTION__);
+}
+
+Datum
+sepgsql_set_range(PG_FUNCTION_ARGS)
+{
+ return sepgsql_is_disabled(__FUNCTION__);
+}
+
+#endif /* HAVE_SELINUX */
diff -rpNU3 base/src/backend/security/sepgsql/avc.c sepgsql/src/backend/security/sepgsql/avc.c
--- base/src/backend/security/sepgsql/avc.c 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/src/backend/security/sepgsql/avc.c 2008-07-11 14:14:24.000000000 +0900
@@ -0,0 +1,1123 @@
+
+/*
+ * src/backend/security/sepgsql/avc.c
+ * SE-PostgreSQL userspace access vector cache
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ */
+#include "postgres.h"
+
+#include "access/hash.h"
+#include "libpq/pqsignal.h"
+#include "postmaster/postmaster.h"
+#include "security/pgace.h"
+#include "storage/ipc.h"
+#include "storage/lwlock.h"
+#include "utils/memutils.h"
+#include "utils/syscache.h"
+#include <linux/netlink.h>
+#include <linux/selinux_netlink.h>
+#include <signal.h>
+#include <unistd.h>
+
+/*
+ * uAVC: userspace Access Vector Cache
+ *
+ * SE-PostgreSQL makes inqueries for SELinux to check whether the security
+ * policy allows the required action, or not. However, it need to invoke
+ * system call because SELinux is a kernel feature and it hold its security
+ * policy in the kernel memory.
+ *
+ * uAVC enables to reduce the number of kernel invocation, with caching
+ * the result of inquiries. When we have to make a decision based on the
+ * security policy of SELinux, it tries to find up an appropriate cache
+ * entry on the uAVC. If exist, we don't need to invoke a system call
+ * and can reduce unnecessary overhead.
+ *
+ * If not exist, SE-PostgreSQL makes a new cache entry based on the
+ * result of inquiries, and chains it on uAVC to prepare the following
+ * decision makings.
+ *
+ * uAVC has a version number to check whether it is now valid, or not.
+ * Not need to say, uAVC cache entry has to be invalid just after
+ * policy reloaded or state change.
+ * If it is not match the latest one, updated by the policy state
+ * monitoring process, uAVC has to be reseted.
+ */
+
+/*
+ * Dynamic object class/access vector mapping
+ *
+ * SELinux exports the list of object classes (it means kind of object, like
+ * file or table) and access vectors (it means permission set, like read,
+ * select, ...) under /selinux/class.
+ * It enables to provide userspace object managers a interface to get what
+ * codes should be used to ask SELinux.
+ *
+ * libselinux provides an API to translate a string expression and a code
+ * used by the loaded security policy. These correspondences are not assured
+ * over the bound of policy loading, so we have to reload the mapping after
+ * in-kernel policy is reloaded, or its state is changed.
+ */
+static struct
+{
+ struct
+ {
+ const char *name;
+ security_class_t internal;
+ } tclass;
+ struct
+ {
+ char *name;
+ access_vector_t internal;
+ } 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 MemoryContext AvcMemCtx;
+
+#define AVC_HASH_NUM_SLOTS 256
+#define AVC_HASH_NUM_NODES 600
+
+typedef struct
+{
+ uint32 hash_key;
+
+ security_context_t scon; /* source security context */
+ security_context_t tcon; /* target security context */
+ Oid tsid; /* target security id, if exist */
+ security_class_t tclass; /* object class number */
+
+ security_context_t ncon; /* newly created security context */
+ Oid nsid; /* security id of ncon, if exist */
+
+ access_vector_t allowed;
+ access_vector_t decided;
+ access_vector_t auditallow;
+ access_vector_t auditdeny;
+
+ bool hot_cache;
+} avc_datum;
+static sig_atomic_t avc_version;
+static bool avc_enforcing;
+static List *avc_slot[AVC_HASH_NUM_SLOTS];
+static uint32 avc_datum_count = 0;
+static uint32 avc_lru_hint = 0;
+
+/*
+ * selinux_state
+ *
+ * This structure shows the global state of SELinux and its security
+ * policy, and it is assigned on shared memory region.
+ *
+ * The most significant variable is selinux_state->version.
+ * Any instance can refer this variable to confirm current sequence
+ * number of policy state, without locking.
+ *
+ * The only process able to update this variable is policy state
+ * monitoring process forked by postmaster. It can receive notifications
+ * from the kernel via netlink socket, and it update selinux_state->version
+ * to encourage any instance to reflush its uAVC.
+ *
+ * When we read rest of variable, we have to hold SepgsqlAvcLock LWlock
+ * as a reader. enforceing shows the current SELinux working mode.
+ * catalog shows the mapping set of security classes and access vectors.
+ */
+struct
+{
+ /*
+ * only state monitoring process can update version.
+ * any other process can read it without locks.
+ */
+ volatile sig_atomic_t version;
+
+ bool enforcing;
+
+ struct
+ {
+ struct
+ {
+ security_class_t internal;
+ security_class_t external;
+ } tclass;
+ struct
+ {
+ access_vector_t internal;
+ access_vector_t external;
+ } av_perms[sizeof(access_vector_t) * 8];
+ } catalog[NUM_SELINUX_CATALOG];
+} *selinux_state = NULL;
+
+Size
+sepgsqlShmemSize(void)
+{
+ return sizeof(*selinux_state);
+}
+
+/*
+ * load_class_av_mapping
+ *
+ * This function rebuild the mapping set of security classes and access
+ * vectors on selinux_state. It has to be invoked by the policy state
+ * monitoring process with SepgsqlAvcLock in LW_EXCLUSIVE.
+ */
+static void
+load_class_av_mapping(void)
+{
+ security_class_t tclass;
+ access_vector_t av_perms;
+ int i, j;
+
+ for (i = 0; i < NUM_SELINUX_CATALOG; i++)
+ {
+ tclass = string_to_security_class(selinux_catalog[i].tclass.name);
+ if (!tclass)
+ tclass = selinux_catalog[i].tclass.internal;
+
+ selinux_state->catalog[i].tclass.internal
+ = selinux_catalog[i].tclass.internal;
+ selinux_state->catalog[i].tclass.external = tclass;
+
+ for (j = 0; selinux_catalog[i].av_perms[j].name; j++)
+ {
+ av_perms =
+ string_to_av_perm(tclass, selinux_catalog[i].av_perms[j].name);
+ if (!av_perms)
+ av_perms = selinux_catalog[i].av_perms[j].internal;
+
+ selinux_state->catalog[i].av_perms[j].internal
+ = selinux_catalog[i].av_perms[j].internal;
+ selinux_state->catalog[i].av_perms[j].external = av_perms;
+ }
+ }
+}
+
+static security_class_t
+trans_to_external_tclass(security_class_t i_tclass)
+{
+ /* have to hold SepgsqlAvcLock with LW_SHARED */
+ int i;
+
+ for (i = 0; i < NUM_SELINUX_CATALOG; i++)
+ {
+ if (selinux_state->catalog[i].tclass.internal == i_tclass)
+ return selinux_state->catalog[i].tclass.external;
+ }
+ return i_tclass; /* use it as is for kernel classes */
+}
+
+static access_vector_t
+trans_to_internal_perms(security_class_t e_tclass, access_vector_t e_perms)
+{
+ /* have to hold SepgsqlAvcLock with LW_SHARED */
+ access_vector_t i_perms = 0UL;
+ int i, j;
+
+ for (i = 0; i < NUM_SELINUX_CATALOG; i++)
+ {
+ if (selinux_state->catalog[i].tclass.external != e_tclass)
+ continue;
+ for (j = 0; j < sizeof(access_vector_t) * 8; j++)
+ {
+ if ((selinux_state->catalog[i].av_perms[j].external & e_perms) != 0)
+ i_perms |= selinux_state->catalog[i].av_perms[j].internal;
+ }
+ return i_perms;
+ }
+ return e_perms; /* use it as is for kernel classes */
+}
+
+static const char *
+sepgsql_class_to_string(security_class_t tclass)
+{
+ int i;
+
+ for (i = 0; i < NUM_SELINUX_CATALOG; i++)
+ {
+ if (selinux_catalog[i].tclass.internal == tclass)
+ return selinux_catalog[i].tclass.name;
+ }
+ /*
+ * tclass is always same as external one, for kernel object classes
+ */
+ return security_class_to_string(tclass);
+}
+
+static const char *
+sepgsql_av_perm_to_string(security_class_t tclass, access_vector_t perm)
+{
+ int i, j;
+
+ for (i = 0; i < NUM_SELINUX_CATALOG; i++)
+ {
+ if (selinux_catalog[i].tclass.internal == tclass)
+ {
+ char *perm_name;
+
+ for (j = 0; (perm_name = selinux_catalog[i].av_perms[j].name); j++)
+ {
+ if (selinux_catalog[i].av_perms[j].internal == perm)
+ return perm_name;
+ }
+ return "unknown";
+ }
+ }
+ /*
+ * tclass is always same as external one, for kernel object classes
+ */
+ return security_av_perm_to_string(tclass, perm);
+}
+
+/*
+ * sepgsql_avc_reset
+ *
+ * This function clears all current avc entries, and update its version.
+ */
+
+static void
+sepgsql_avc_reset(void)
+{
+ int i;
+
+ MemoryContextReset(AvcMemCtx);
+
+ LWLockAcquire(SepgsqlAvcLock, LW_SHARED);
+
+ avc_version = selinux_state->version;
+ if (!strcmp(sepostgresql_mode, "default"))
+ avc_enforcing = selinux_state->enforcing;
+ else if (!strcmp(sepostgresql_mode, "permissive"))
+ avc_enforcing = false;
+ else if (!strcmp(sepostgresql_mode, "enforcing"))
+ avc_enforcing = true;
+ else
+ elog(FATAL, "SELinux: undefined state (sepostgresql = %s)", sepostgresql_mode);
+
+ for (i = 0; i < AVC_HASH_NUM_SLOTS; i++)
+ avc_slot[i] = NIL;
+ avc_datum_count = 0;
+
+ LWLockRelease(SepgsqlAvcLock);
+}
+
+/*
+ * sepgsql_avc_reclaim
+ *
+ * This function reclaims recently not-used avc entries,
+ * when the number of caches overs AVC_HASH_NUM_NODES
+ */
+static void
+sepgsql_avc_reclaim(void)
+{
+ List *slot;
+ ListCell *l;
+ avc_datum *cache;
+
+ while (avc_datum_count > AVC_HASH_NUM_NODES)
+ {
+ avc_lru_hint = (avc_lru_hint + 1) % AVC_HASH_NUM_SLOTS;
+ slot = avc_slot[avc_lru_hint];
+ foreach(l, slot)
+ {
+ cache = lfirst(l);
+
+ if (cache->hot_cache)
+ {
+ cache->hot_cache = false;
+ continue;
+ }
+ list_delete_ptr(slot, cache);
+ pfree(cache);
+ avc_datum_count--;
+ }
+ }
+}
+
+static void
+sepgsql_avc_insert(avc_datum *cache, uint32 hash_key)
+{
+ uint32 index;
+
+ cache->hash_key = hash_key;
+ index = hash_key % AVC_HASH_NUM_SLOTS;
+
+ /*
+ * reclaim avc, if needed
+ */
+ sepgsql_avc_reclaim();
+
+ avc_slot[index] = lcons(cache, avc_slot[index]);
+
+ avc_datum_count++;
+}
+
+/*
+ * sepgsql_avc_compute
+ *
+ * This function compute an avc cache for the given subject/target
+ * context and object class, based on results of inquiries to SELinux.
+ */
+static void
+sepgsql_avc_compute(const security_context_t scon,
+ const security_context_t tcon,
+ security_class_t tclass,
+ avc_datum *cache)
+{
+ security_class_t e_tclass;
+ security_context_t svcon, tvcon, ncon;
+ struct av_decision avd;
+
+ svcon = (!security_check_context_raw(scon)
+ ? scon : sepgsqlGetUnlabeledContext());
+ tvcon = (!security_check_context_raw(tcon)
+ ? tcon : sepgsqlGetUnlabeledContext());
+
+ LWLockAcquire(SepgsqlAvcLock, LW_SHARED);
+
+ e_tclass = trans_to_external_tclass(tclass);
+
+ if (security_compute_av_raw(svcon, tvcon, e_tclass, 0, &avd) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not compute a new avc entry"
+ " scon=%s tcon=%s tclass=%u", svcon, tvcon, tclass)));
+ if (security_compute_create_raw(svcon, tvcon, e_tclass, &ncon) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not compute a new avc entry"
+ " scon=%s tcon=%s tclass=%u", svcon, tvcon, tclass)));
+
+ cache->allowed = trans_to_internal_perms(e_tclass, avd.allowed);
+ cache->decided = trans_to_internal_perms(e_tclass, avd.decided);
+ cache->auditallow = trans_to_internal_perms(e_tclass, avd.auditallow);
+ cache->auditdeny = trans_to_internal_perms(e_tclass, avd.auditdeny);
+ cache->hot_cache = true;
+
+ LWLockRelease(SepgsqlAvcLock);
+
+ PG_TRY();
+ {
+ cache->scon = pstrdup(scon);
+ cache->tcon = pstrdup(tcon);
+ cache->ncon = pstrdup(ncon);
+ cache->tclass = tclass;
+ }
+ PG_CATCH();
+ {
+ freecon(ncon);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ freecon(ncon);
+}
+
+/*
+ * avc_lookup_entry
+ * avc_lookup_entry_sid
+ *
+ * These function lookup the required avc_datum on AVC.
+ * It returns avc_datum object which has required tag.
+ * If not found, NULL will be returned.
+ */
+static avc_datum *
+avc_lookup_entry(const security_context_t scon,
+ const security_context_t tcon,
+ security_class_t tclass, uint32 hash)
+{
+ uint32 index = hash % AVC_HASH_NUM_SLOTS;
+ avc_datum *cache;
+ ListCell *l;
+
+ foreach (l, avc_slot[index])
+ {
+ cache = lfirst(l);
+
+ if (cache->hash_key == hash
+ && cache->tclass == tclass
+ && strcmp(cache->scon, scon) == 0
+ && strcmp(cache->tcon, tcon) == 0)
+ {
+ cache->hot_cache = true;
+ return cache;
+ }
+ }
+ return NULL;
+}
+
+
+
+static avc_datum *
+avc_lookup_entry_sid(const security_context_t scon,
+ Oid tsid, security_class_t tclass, uint32 hash)
+{
+ uint32 index = hash % AVC_HASH_NUM_SLOTS;
+ avc_datum *cache;
+ ListCell *l;
+
+ foreach (l, avc_slot[index])
+ {
+ cache = lfirst(l);
+
+ if (cache->hash_key == hash
+ && cache->tclass == tclass
+ && cache->tsid == tsid
+ && strcmp(cache->scon, scon) == 0)
+ {
+ cache->hot_cache = true;
+ return cache;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * avc_audit_common
+ *
+ * This function makes an audit message on the given Cstring buffer,
+ * based on the given av_decision (which is the result of permission
+ * checks).
+ */
+static bool
+avc_audit_common(char *buffer, uint32 buflen, avc_datum *cache,
+ access_vector_t perms, const char *objname)
+{
+ access_vector_t denied, audited, mask;
+ security_context_t svcon, tvcon;
+ uint32 ofs = 0;
+
+ denied = perms & ~cache->allowed;
+ audited = denied ? (denied & cache->auditdeny) : (perms & cache->auditallow);
+
+ if (audited == 0)
+ return false;
+
+ ofs += snprintf(buffer + ofs, buflen - ofs, "%s {",
+ denied ? "denied" : "granted");
+ for (mask = 1; mask != 0; mask <<= 1)
+ {
+ if ((audited & mask) != 0)
+ ofs += snprintf(buffer + ofs, buflen - ofs, " %s",
+ sepgsql_av_perm_to_string(cache->tclass, mask));
+ }
+ ofs += snprintf(buffer + ofs, buflen - ofs, " } ");
+
+ svcon = sepgsqlTranslateSecurityLabelOut(cache->scon);
+ tvcon = sepgsqlTranslateSecurityLabelOut(cache->tcon);
+ ofs += snprintf(buffer + ofs, buflen - ofs,
+ "scontext=%s tcontext=%s tclass=%s",
+ svcon, tvcon, sepgsql_class_to_string(cache->tclass));
+ pfree(svcon);
+ pfree(tvcon);
+ if (objname)
+ ofs += snprintf(buffer + ofs, buflen - ofs, " name=%s", objname);
+
+ return true;
+}
+
+/*
+ * sepgsqlAvcPermission
+ * sepgsqlAvcPermissionSid
+ *
+ * These functions make a dicision for the given action, and an audit
+ * record if necessary. When the required action is not allowed by
+ * the policy and "abort" is true, these functions aborts current
+ * transaction. Elsewhere, it returns the result simply.
+ *
+ * They tries to lookup an cached entry on uAVC. If it does not found
+ * on uAVC, it create a new entry and insert it for the future usage.
+ * In most cases, this feature enables to reduce the number of kernel
+ * invocation.
+ *
+ * The only difference between two API is we can use security id as
+ * second argument of sepgsqlAvcPermissionSid(). In most cases to
+ * invoke AVC functions, we have to evaluate permissions onto required
+ * tuple holding security id. It enables to reduce overhead to translate
+ * security id and text representation.
+ */
+static inline uint32
+sepgsql_avc_hash(const security_context_t scon, const security_context_t tcon,
+ Oid security_id, security_class_t tclass)
+{
+ uint32 hash = 0;
+
+ hash ^= (scon ? DatumGetUInt32(hash_any((unsigned char *) scon, strlen(scon))) : 0);
+ hash ^= (tcon ? DatumGetUInt32(hash_any((unsigned char *) tcon, strlen(tcon))) : 0);
+ hash ^= DatumGetUInt32(hash_any((unsigned char *) &security_id, sizeof(Oid)));
+ hash ^= (tclass << 2);
+
+ return hash;
+}
+
+static bool
+avc_permission_common(avc_datum *cache, access_vector_t perms,
+ const char *objname, bool abort)
+{
+ char audit_buffer[2048];
+ access_vector_t denied;
+ bool audit;
+ bool rc = true;
+
+ audit = avc_audit_common(audit_buffer, sizeof(audit_buffer),
+ cache, perms, objname);
+
+ denied = perms & ~cache->allowed;
+ if (!perms || denied)
+ {
+ if (avc_enforcing)
+ rc = false;
+ else
+ {
+ /*
+ * In permissive mode, once denied permissions are
+ * allowed to avoid a flood of denied logs.
+ */
+ cache->allowed |= perms;
+ }
+ }
+
+ if (audit)
+ {
+ ereport((!rc && abort) ? ERROR : NOTICE,
+ (errcode(ERRCODE_SELINUX_AUDIT),
+ errmsg("SELinux: %s", audit_buffer)));
+ }
+ else if (!rc && abort)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_AUDIT),
+ errmsg("SELinux: security policy violation")));
+
+ return rc;
+}
+
+bool
+sepgsqlAvcPermission(const security_context_t scon,
+ const security_context_t tcon,
+ security_class_t tclass,
+ access_vector_t perms,
+ const char *objname, bool abort)
+{
+ avc_datum *cache;
+ uint32 hash;
+
+ /*
+ * check avc invalidation
+ */
+ if (avc_version != selinux_state->version)
+ sepgsql_avc_reset();
+
+ /*
+ * lookup avc entry
+ */
+ hash = sepgsql_avc_hash(scon, tcon, InvalidOid, tclass);
+ cache = avc_lookup_entry(scon, tcon, tclass, hash);
+ if (!cache)
+ {
+ /*
+ * not found, make a new avc entry
+ */
+ MemoryContext oldctx
+ = MemoryContextSwitchTo(AvcMemCtx);
+
+ cache = palloc0(sizeof(avc_datum));
+ sepgsql_avc_compute(scon, tcon, tclass, cache);
+
+ sepgsql_avc_insert(cache, hash);
+
+ MemoryContextSwitchTo(oldctx);
+ }
+
+ return avc_permission_common(cache, perms, objname, abort);
+}
+
+bool
+sepgsqlAvcPermissionSid(const security_context_t scon, Oid tsid,
+ security_class_t tclass,
+ access_vector_t perms,
+ const char *objname, bool abort)
+{
+ avc_datum *cache;
+ uint32 hash;
+
+ /*
+ * check avc invalidation
+ */
+ if (avc_version != selinux_state->version)
+ sepgsql_avc_reset();
+
+ /*
+ * lookup avc entry
+ */
+ hash = sepgsql_avc_hash(scon, NULL, tsid, tclass);
+ cache = avc_lookup_entry_sid(scon, tsid, tclass, hash);
+ if (!cache)
+ {
+ /*
+ * not found, make a new avc entry
+ */
+ MemoryContext oldctx = MemoryContextSwitchTo(AvcMemCtx);
+ security_context_t tcon;
+
+ cache = palloc0(sizeof(avc_datum));
+
+ tcon = pgaceLookupSecurityLabel(tsid);
+ sepgsql_avc_compute(scon, tcon, tclass, cache);
+ pfree(tcon);
+
+ cache->tsid = tsid;
+
+ sepgsql_avc_insert(cache, hash);
+
+ MemoryContextSwitchTo(oldctx);
+ }
+
+ return avc_permission_common(cache, perms, objname, abort);
+}
+
+/*
+ * sepgsqlAvcCreateCon
+ * sepgsqlAvcCreateConSid
+ *
+ * These functions returns a security context or security id of newly
+ * created object based on the security policy.
+ */
+security_context_t
+sepgsqlAvcCreateCon(const security_context_t scon,
+ const security_context_t tcon, security_class_t tclass)
+{
+ avc_datum *cache;
+ uint32 hash;
+
+ /*
+ * check avc invalidation
+ */
+ if (avc_version != selinux_state->version)
+ sepgsql_avc_reset();
+
+ /*
+ * lookup avc entry
+ */
+ hash = sepgsql_avc_hash(scon, tcon, InvalidOid, tclass);
+ cache = avc_lookup_entry(scon, tcon, tclass, hash);
+ if (!cache)
+ {
+ /*
+ * not found, make a new avc entry
+ */
+ MemoryContext oldctx
+ = MemoryContextSwitchTo(AvcMemCtx);
+
+ cache = palloc0(sizeof(avc_datum));
+ sepgsql_avc_compute(scon, tcon, tclass, cache);
+
+ sepgsql_avc_insert(cache, hash);
+
+ MemoryContextSwitchTo(oldctx);
+ }
+ return pstrdup(cache->ncon);
+}
+
+Oid
+sepgsqlAvcCreateConSid(const security_context_t scon, Oid tsid,
+ security_class_t tclass)
+{
+ avc_datum *cache;
+ uint32 hash;
+
+ /*
+ * check avc invalidation
+ */
+ if (avc_version != selinux_state->version)
+ sepgsql_avc_reset();
+
+ /*
+ * lookup avc entry
+ */
+ hash = sepgsql_avc_hash(scon, NULL, tsid, tclass);
+ cache = avc_lookup_entry_sid(scon, tsid, tclass, hash);
+ if (!cache)
+ {
+ /*
+ * not found, make a new avc entry
+ */
+ MemoryContext oldctx
+ = MemoryContextSwitchTo(AvcMemCtx);
+ security_context_t tcon;
+
+ cache = palloc0(sizeof(avc_datum));
+
+ tcon = pgaceLookupSecurityLabel(tsid);
+ sepgsql_avc_compute(scon, tcon, tclass, cache);
+ pfree(tcon);
+
+ cache->tsid = tsid;
+
+ sepgsql_avc_insert(cache, hash);
+
+ MemoryContextSwitchTo(oldctx);
+ }
+ if (!cache->nsid)
+ {
+ Oid nsid = pgaceSecurityLabelToSid(cache->ncon);
+
+ cache->nsid = nsid;
+ }
+ return cache->nsid;
+}
+
+/*
+ * sepgsqlAvcInit
+ *
+ * Initialize local memory context and assign shared memory segment
+ */
+void
+sepgsqlAvcInit(void)
+{
+ bool found;
+
+ /*
+ * local memory
+ */
+ AvcMemCtx = AllocSetContextCreate(TopMemoryContext,
+ "SE-PostgreSQL userspace avc",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+
+ /*
+ * shared memory
+ */
+ selinux_state = ShmemInitStruct("SELinux policy state",
+ sepgsqlShmemSize(), &found);
+ if (!found)
+ {
+ int enforcing = security_getenforce();
+
+ Assert(enforcing == 0 || enforcing == 1);
+
+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE);
+ selinux_state->version = 0;
+ selinux_state->enforcing = enforcing;
+ load_class_av_mapping();
+
+ LWLockRelease(SepgsqlAvcLock);
+ }
+
+ /*
+ * reset local avc
+ */
+ sepgsql_avc_reset();
+}
+
+/*
+ * SELinux state monitoring process
+ *
+ * This process is forked from postmaster to monitor the state of SELinux.
+ * SELinux can make a notifier message to userspace object manager via
+ * netlink socket. When it receives the message, it updates selinux_state
+ * structure assigned on shared memory region to make any instance reset
+ * its AVC soon.
+ */
+
+static bool sepgsqlStateMonitorAlive = true;
+
+static void
+sepgsqlStateMonitorSIGHUP(SIGNAL_ARGS)
+{
+ ereport(NOTICE,
+ (errcode(ERRCODE_SELINUX_INFO),
+ errmsg("SELinux: reset userspace avc")));
+ sepgsql_avc_reset();
+}
+
+static int
+sepgsqlStateMonitorMain()
+{
+ char buffer[2048];
+ struct sockaddr_nl addr;
+ socklen_t addrlen;
+ struct nlmsghdr *nlh;
+ int rc, nl_sockfd;
+
+ /*
+ * map shared memory segment
+ */
+ sepgsqlAvcInit();
+
+ /*
+ * setup the signal handler
+ */
+ pqinitmask();
+ pqsignal(SIGHUP, sepgsqlStateMonitorSIGHUP);
+ pqsignal(SIGINT, SIG_IGN);
+ pqsignal(SIGTERM, exit);
+ pqsignal(SIGQUIT, exit);
+ pqsignal(SIGUSR1, SIG_IGN);
+ pqsignal(SIGUSR2, SIG_IGN);
+ pqsignal(SIGCHLD, SIG_DFL);
+ PG_SETMASK(&UnBlockSig);
+
+ ereport(NOTICE,
+ (errcode(ERRCODE_SELINUX_INFO),
+ errmsg("SELinux: policy state monitor process (pid: %u)",
+ getpid())));
+ /*
+ * open netlink socket
+ */
+ nl_sockfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_SELINUX);
+ if (nl_sockfd < 0)
+ {
+ ereport(NOTICE,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("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)))
+ {
+ ereport(NOTICE,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not bind netlink socket")));
+ return 1;
+ }
+
+ /*
+ * waiting loop
+ */
+ while (sepgsqlStateMonitorAlive)
+ {
+ addrlen = sizeof(addr);
+ rc = recvfrom(nl_sockfd, buffer, sizeof(buffer), 0,
+ (struct sockaddr *) &addr, &addrlen);
+ if (rc < 0)
+ {
+ if (errno == EINTR)
+ continue;
+
+ ereport(NOTICE,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: error on netlink recvfrom(): %s",
+ strerror(errno))));
+ return 1;
+ }
+
+ if (addrlen != sizeof(addr))
+ {
+ ereport(NOTICE,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: netlink address truncated (len=%d)",
+ addrlen)));
+ return 1;
+ }
+
+ if (addr.nl_pid)
+ {
+ ereport(NOTICE,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: netlink received spoofed packet from: %u",
+ addr.nl_pid)));
+ continue;
+ }
+
+ if (rc == 0)
+ {
+ ereport(NOTICE,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: netlink received EOF")));
+ return 1;
+ }
+
+ nlh = (struct nlmsghdr *) buffer;
+ if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > (unsigned int) rc)
+ {
+ ereport(NOTICE,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: netlink incomplete message")));
+ return 1;
+ }
+
+ switch (nlh->nlmsg_type)
+ {
+ case SELNL_MSG_SETENFORCE:
+ {
+ struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh);
+
+ ereport(NOTICE,
+ (errcode(ERRCODE_SELINUX_INFO),
+ errmsg("SELinux: setenforce notifier"
+ " (enforcing=%d)", msg->val)));
+
+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE);
+ load_class_av_mapping();
+
+ /*
+ * userspace avc invalidation
+ */
+ selinux_state->version = selinux_state->version + 1;
+ selinux_state->enforcing = msg->val ? true : false;
+
+ LWLockRelease(SepgsqlAvcLock);
+ break;
+ }
+ case SELNL_MSG_POLICYLOAD:
+ {
+ struct selnl_msg_policyload *msg = NLMSG_DATA(nlh);
+
+ ereport(NOTICE,
+ (errcode(ERRCODE_SELINUX_INFO),
+ errmsg("policyload notifier (seqno=%d)",
+ msg->seqno)));
+
+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE);
+ load_class_av_mapping();
+ /*
+ * userspace avc invalidation
+ */
+ selinux_state->version = selinux_state->version + 1;
+
+ LWLockRelease(SepgsqlAvcLock);
+ break;
+ }
+ case NLMSG_ERROR:
+ {
+ struct nlmsgerr *err = NLMSG_DATA(nlh);
+
+ if (err->error == 0)
+ break;
+
+ ereport(NOTICE,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: netlink error: %s",
+ strerror(-err->error))));
+ return 1;
+ }
+ default:
+ ereport(NOTICE,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("netlink unknown message type (%d)",
+ nlh->nlmsg_type)));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+pid_t
+sepgsqlStartupWorkerProcess(void)
+{
+ pid_t chld;
+
+ chld = fork();
+ if (chld == 0)
+ {
+ ClosePostmasterPorts(false);
+
+ on_exit_reset();
+
+ exit(sepgsqlStateMonitorMain());
+ }
+ else if (chld > 0)
+ return chld;
+
+ return (pid_t) 0;
+}
diff -rpNU3 base/src/backend/security/sepgsql/core.c sepgsql/src/backend/security/sepgsql/core.c
--- base/src/backend/security/sepgsql/core.c 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/src/backend/security/sepgsql/core.c 2008-07-11 14:10:51.000000000 +0900
@@ -0,0 +1,638 @@
+
+/*
+ * src/backend/security/sepgsqlCore.c
+ * SE-PostgreSQL core facilities
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ */
+#include "postgres.h"
+
+#include "catalog/pg_database.h"
+#include "catalog/pg_security.h"
+#include "libpq/libpq.h"
+#include "miscadmin.h"
+#include "security/pgace.h"
+#include "utils/syscache.h"
+#include <selinux/context.h>
+
+static security_context_t serverContext = NULL;
+static security_context_t clientContext = NULL;
+static security_context_t unlabeledContext = NULL;
+
+const security_context_t
+sepgsqlGetServerContext(void)
+{
+ Assert(serverContext != NULL);
+ return serverContext;
+}
+
+const security_context_t
+sepgsqlGetClientContext(void)
+{
+ Assert(clientContext != NULL);
+ return clientContext;
+}
+
+const security_context_t
+sepgsqlGetDatabaseContext(void)
+{
+ security_context_t dcontext;
+
+ if (IsBootstrapProcessingMode())
+ {
+ security_context_t tmp;
+
+ if (security_compute_create_raw(sepgsqlGetClientContext(),
+ sepgsqlGetClientContext(),
+ SECCLASS_DB_DATABASE, &tmp) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not get database context")));
+ PG_TRY();
+ {
+ dcontext = pstrdup(tmp);
+ }
+ PG_CATCH();
+ {
+ freecon(tmp);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(tmp);
+ }
+ else
+ {
+ HeapTuple tuple;
+
+ tuple = SearchSysCache(DATABASEOID,
+ ObjectIdGetDatum(MyDatabaseId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "SELinux: cache lookup failed for database: %u", MyDatabaseId);
+
+ dcontext = pgaceLookupSecurityLabel(HeapTupleGetSecurity(tuple));
+
+ ReleaseSysCache(tuple);
+ }
+
+ return dcontext;
+}
+
+Oid
+sepgsqlGetDatabaseSecurityId(void)
+{
+ Oid security_id;
+
+ if (IsBootstrapProcessingMode())
+ {
+ security_context_t dcontext
+ = sepgsqlGetDatabaseContext();
+
+ security_id = pgaceSecurityLabelToSid(dcontext);
+ }
+ else
+ {
+ HeapTuple tuple;
+
+ tuple = SearchSysCache(DATABASEOID,
+ ObjectIdGetDatum(MyDatabaseId),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "SELinux: cache lookup failed for database: %u", MyDatabaseId);
+
+ security_id = HeapTupleGetSecurity(tuple);
+
+ ReleaseSysCache(tuple);
+ }
+
+ return security_id;
+}
+
+const security_context_t
+sepgsqlGetUnlabeledContext(void)
+{
+ if (unlabeledContext)
+ return unlabeledContext;
+
+ if (security_get_initial_context_raw("unlabeled", &unlabeledContext) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not get unlabeled context")));
+
+ return unlabeledContext;
+}
+
+const security_context_t
+sepgsqlSwitchClientContext(security_context_t new_context)
+{
+ security_context_t original_context = clientContext;
+
+ clientContext = new_context;
+
+ return original_context;
+}
+
+static void
+initContexts(void)
+{
+ /*
+ * server context
+ */
+ if (getcon_raw(&serverContext) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not get server process context")));
+
+ /*
+ * client context
+ */
+ if (!MyProcPort)
+ {
+ /*
+ * When the proces is not invoked as a backend of clietnt,
+ * it works as a server process and as a client process
+ * in same time.
+ */
+ clientContext = serverContext;
+ }
+ else
+ {
+ if (getpeercon_raw(MyProcPort->sock, &clientContext) < 0)
+ {
+ /*
+ * fallbacked security context
+ *
+ * When getpeercon() API does not obtain the context of
+ * peer process, SEPGSQL_FALLBACK_CONTEXT environment
+ * variable is used as an alternative security context
+ * of the peer.
+ *
+ * getpeercon() needs the following condition to fail:
+ * - Connection come from remote host,
+ * - and, there is no labeled ipsec configuration between
+ * localhost and remote host.
+ * - and, there is no static fallbacked context configuration
+ * for the remote host.
+ */
+ char *fallback = getenv("SEPGSQL_FALLBACK_CONTEXT");
+
+ if (!fallback)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg
+ ("SELinux: could not get client process context")));
+
+ if (security_check_context(fallback) < 0
+ || selinux_trans_to_raw_context(fallback, &clientContext) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: %s is not a valid context",
+ fallback)));
+ }
+ }
+}
+
+/*
+ * sepgsqlInitialize
+ *
+ * It initializes SE-PostgreSQL itself including assignment of shared
+ * memory segment, reset of AVC, obtaining the client/server security
+ * context and checks whether the client can access the required database,
+ * or not.
+ */
+void
+sepgsqlInitialize(bool bootstrap)
+{
+ char *dbname;
+
+ sepgsqlAvcInit();
+
+ initContexts();
+
+ /*
+ * check db_database:{ access }
+ */
+ if (IsBootstrapProcessingMode())
+ dbname = "template1";
+ else
+ {
+ Form_pg_database dbForm;
+ HeapTuple tuple;
+
+ tuple = SearchSysCache(DATABASEOID,
+ ObjectIdGetDatum(MyDatabaseId), 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "SELinux: cache lookup failed for database %u",
+ MyDatabaseId);
+ dbForm = (Form_pg_database) GETSTRUCT(tuple);
+
+ dbname = pstrdup(NameStr(dbForm->datname));
+
+ ReleaseSysCache(tuple);
+ }
+
+ sepgsqlAvcPermission(sepgsqlGetClientContext(),
+ sepgsqlGetDatabaseContext(),
+ SECCLASS_DB_DATABASE,
+ DB_DATABASE__ACCESS,
+ dbname,
+ true);
+}
+
+/*
+ * sepgsqlIsEnabled
+ *
+ * This function returns the state of SE-PostgreSQL when PGACE hooks
+ * are invoked, to prevent to call sepgsqlXXXX() functions when
+ * SE-PostgreSQL is disabled.
+ *
+ * We can config the state of SE-PostgreSQL in $PGDATA/postgresql.conf.
+ * The GUC option "sepostgresql" can have the following four parameter.
+ *
+ * - default : It always follows the in-kernel SELinux state. When it
+ * works in Enforcing mode, SE-PostgreSQL also works in
+ * Enforcing mode. Changes of in-kernel state are delivered
+ * to userspace SE-PostgreSQL soon, and SELinux state
+ * monitoring process updates it rapidly.
+ * - enforcing : It always works in Enforcing mode. In-kernel SELinux
+ * has to be enabled.
+ * - permissive : It always works in Permissive mode. In-kernel SELinux
+ * has to be enabled.
+ * - disabled : It disables SE-PostgreSQL feature. It works as if
+ * original PostgreSQL
+ */
+
+bool
+sepgsqlIsEnabled(void)
+{
+ static int enabled = -1;
+
+ if (enabled < 0)
+ {
+ if (strcmp(sepostgresql_mode, "disabled") == 0)
+ enabled = 0;
+ else
+ {
+ int rc = is_selinux_enabled();
+
+ if (strcmp(sepostgresql_mode, "default") == 0)
+ enabled = rc;
+ else if (strcmp(sepostgresql_mode, "permissice") == 0
+ || strcmp(sepostgresql_mode, "enforcing") == 0)
+ {
+ if (rc == 0)
+ ereport(FATAL,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: disabled in kernel, but sepostgresql = %s",
+ sepostgresql_mode)));
+ enabled = 1;
+ }
+ else
+ {
+ ereport(FATAL,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: unknown state sepostgresql = %s",
+ sepostgresql_mode)));
+ }
+ }
+ }
+
+ return enabled > 0 ? true : false;
+}
+
+/*
+ * sepgsql_getcon(void)
+ *
+ * It returns security context of client
+ */
+Datum
+sepgsql_getcon(PG_FUNCTION_ARGS)
+{
+ security_context_t context;
+ Datum labelTxt;
+
+ if (!sepgsqlIsEnabled())
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: disabled now")));
+
+ if (selinux_raw_to_trans_context(clientContext, &context) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not translate mls label")));
+ PG_TRY();
+ {
+ labelTxt = CStringGetTextDatum(context);
+ }
+ PG_CATCH();
+ {
+ freecon(context);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(context);
+
+ PG_RETURN_DATUM(labelTxt);
+}
+
+/*
+ * sepgsql_getcon(void)
+ *
+ * It returns security context of server process
+ */
+Datum
+sepgsql_getservcon(PG_FUNCTION_ARGS)
+{
+ security_context_t context;
+ Datum labelTxt;
+
+ if (!sepgsqlIsEnabled())
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: disabled now")));
+
+ if (selinux_raw_to_trans_context(serverContext, &context) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not translate mls label")));
+ PG_TRY();
+ {
+ labelTxt = CStringGetTextDatum(context);
+ }
+ PG_CATCH();
+ {
+ freecon(context);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(context);
+
+ PG_RETURN_DATUM(labelTxt);
+}
+
+static void
+parse_to_context(security_context_t context,
+ char **user, char **role, char **type, char **range)
+{
+ security_context_t raw_context;
+
+ if (!sepgsqlIsEnabled())
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: disabled now")));
+
+ if (selinux_trans_to_raw_context(context, &raw_context) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not translate mls label")));
+ PG_TRY();
+ {
+ char *tmp;
+
+ tmp = pstrdup(strtok(raw_context, ":"));
+ if (user)
+ *user = tmp;
+ tmp = pstrdup(strtok(NULL, ":"));
+ if (role)
+ *role = tmp;
+ tmp = pstrdup(strtok(NULL, ":"));
+ if (type)
+ *type = tmp;
+ if (is_selinux_mls_enabled())
+ {
+ tmp = pstrdup(strtok(NULL, "\0"));
+ if (range)
+ *range = tmp;
+ }
+ else if (range)
+ *range = NULL;
+ }
+ PG_CATCH();
+ {
+ freecon(raw_context);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(raw_context);
+}
+
+/*
+ * text sepgsql_get_user(text)
+ *
+ * It picks up the USER field of given security context.
+ */
+Datum
+sepgsql_get_user(PG_FUNCTION_ARGS)
+{
+ char *user;
+
+ parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)),
+ &user, NULL, NULL, NULL);
+ PG_RETURN_TEXT_P(CStringGetTextDatum(user));
+}
+
+/*
+ * text sepgsql_set_user(text, text)
+ *
+ * It replaces the USER field of given security context by the second argument.
+ */
+Datum
+sepgsql_set_user(PG_FUNCTION_ARGS)
+{
+ char *user, *role, *type, *range;
+ char buffer[1024];
+ security_context_t newcon;
+ Datum result;
+
+ parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)),
+ &user, &role, &type, &range);
+ if (range)
+ snprintf(buffer, sizeof(buffer), "%s:%s:%s:%s",
+ TextDatumGetCString(PG_GETARG_TEXT_P(1)), role, type, range);
+ else
+ snprintf(buffer, sizeof(buffer), "%s:%s:%s",
+ TextDatumGetCString(PG_GETARG_TEXT_P(1)), role, type);
+ if (selinux_raw_to_trans_context((security_context_t) buffer, &newcon) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not set a new user")));
+ PG_TRY();
+ {
+ result = CStringGetTextDatum(newcon);
+ }
+ PG_CATCH();
+ {
+ freecon(newcon);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(newcon);
+
+ PG_RETURN_DATUM(result);
+}
+
+/*
+ * text sepgsql_get_role(text)
+ *
+ * It picks up the ROLE field of given security context.
+ */
+Datum
+sepgsql_get_role(PG_FUNCTION_ARGS)
+{
+ char *role;
+
+ parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)),
+ NULL, &role, NULL, NULL);
+ PG_RETURN_TEXT_P(CStringGetTextDatum(role));
+}
+
+/*
+ * text sepgsql_set_user(text, text)
+ *
+ * It replaces the ROLE field of given security context by the second argument.
+ */
+Datum
+sepgsql_set_role(PG_FUNCTION_ARGS)
+{
+ char *user, *role, *type, *range;
+ char buffer[1024];
+ security_context_t newcon;
+ Datum result;
+
+ parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)),
+ &user, &role, &type, &range);
+ if (range)
+ snprintf(buffer, sizeof(buffer), "%s:%s:%s:%s",
+ user, TextDatumGetCString(PG_GETARG_TEXT_P(1)), type, range);
+ else
+ snprintf(buffer, sizeof(buffer), "%s:%s:%s",
+ user, TextDatumGetCString(PG_GETARG_TEXT_P(1)), type);
+ if (selinux_raw_to_trans_context((security_context_t) buffer, &newcon) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not set a new role")));
+ PG_TRY();
+ {
+ result = CStringGetTextDatum(newcon);
+ }
+ PG_CATCH();
+ {
+ freecon(newcon);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(newcon);
+
+ PG_RETURN_DATUM(result);
+}
+
+/*
+ * text sepgsql_get_type(text)
+ *
+ * It picks up the TYPE field of given security context.
+ */
+Datum
+sepgsql_get_type(PG_FUNCTION_ARGS)
+{
+ char *type;
+
+ parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)),
+ NULL, NULL, &type, NULL);
+ PG_RETURN_TEXT_P(CStringGetTextDatum(type));
+}
+
+/*
+ * text sepgsql_set_user(text, text)
+ *
+ * It replaces the TYPE field of given security context by the second argument.
+ */
+Datum
+sepgsql_set_type(PG_FUNCTION_ARGS)
+{
+ char *user, *role, *type, *range;
+ char buffer[1024];
+ security_context_t newcon;
+ Datum result;
+
+ parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)),
+ &user, &role, &type, &range);
+ if (range)
+ snprintf(buffer, sizeof(buffer), "%s:%s:%s:%s",
+ user, role, TextDatumGetCString(PG_GETARG_TEXT_P(1)), range);
+ else
+ snprintf(buffer, sizeof(buffer), "%s:%s:%s",
+ user, role, TextDatumGetCString(PG_GETARG_TEXT_P(1)));
+ if (selinux_raw_to_trans_context((security_context_t) buffer, &newcon) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not set a new type")));
+ PG_TRY();
+ {
+ result = CStringGetTextDatum(newcon);
+ }
+ PG_CATCH();
+ {
+ freecon(newcon);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(newcon);
+
+ PG_RETURN_DATUM(result);
+}
+
+/*
+ * text sepgsql_get_range(text)
+ *
+ * It picks up the RANGE field of given security context.
+ */
+Datum
+sepgsql_get_range(PG_FUNCTION_ARGS)
+{
+ char *range;
+
+ parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)),
+ NULL, NULL, NULL, &range);
+ PG_RETURN_TEXT_P(CStringGetTextDatum(range));
+}
+
+/*
+ * text sepgsql_set_user(text, text)
+ *
+ * It replaces the RANGE field of given security context by the second argument.
+ */
+Datum
+sepgsql_set_range(PG_FUNCTION_ARGS)
+{
+ char *user, *role, *type, *range;
+ char buffer[1024];
+ security_context_t newcon;
+ Datum result;
+
+ parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)),
+ &user, &role, &type, &range);
+ if (range)
+ snprintf(buffer, sizeof(buffer), "%s:%s:%s:%s",
+ user, role, type, TextDatumGetCString(PG_GETARG_TEXT_P(1)));
+ else
+ snprintf(buffer, sizeof(buffer), "%s:%s:%s", user, role, type);
+ if (selinux_raw_to_trans_context((security_context_t) buffer, &newcon) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not set a new range")));
+ PG_TRY();
+ {
+ result = CStringGetTextDatum(newcon);
+ }
+ PG_CATCH();
+ {
+ freecon(newcon);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(newcon);
+
+ PG_RETURN_DATUM(result);
+}
diff -rpNU3 base/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/security/sepgsql/hooks.c
--- base/src/backend/security/sepgsql/hooks.c 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/src/backend/security/sepgsql/hooks.c 2008-09-22 17:51:21.000000000 +0900
@@ -0,0 +1,892 @@
+/*
+ * src/backend/security/sepgsql/hooks.c
+ * implementations of PGACE framework
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ */
+#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 "utils/tqual.h"
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/*******************************************************************************
+ * Extended SQL statement hooks
+ *******************************************************************************/
+DefElem *
+sepgsqlGramSecurityItem(char *defname, char *value)
+{
+ DefElem *n = NULL;
+
+ if (strcmp(defname, "security_context") == 0)
+ n = makeDefElem(pstrdup(defname), (Node *) makeString(value));
+ return n;
+}
+
+bool
+sepgsqlIsGramSecurityItem(DefElem *defel)
+{
+ Assert(IsA(defel, DefElem));
+
+ if (defel->defname && strcmp(defel->defname, "security_context") == 0)
+ return true;
+ return false;
+}
+
+static void
+putExplicitContext(HeapTuple tuple, DefElem *defel)
+{
+ if (defel)
+ {
+ Oid security_id = pgaceSecurityLabelToSid(strVal(defel->arg));
+
+ HeapTupleSetSecurity(tuple, security_id);
+ }
+}
+
+void
+sepgsqlGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel)
+{
+ putExplicitContext(tuple, defel);
+}
+
+void
+sepgsqlGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel)
+{
+ putExplicitContext(tuple, defel);
+}
+
+void
+sepgsqlGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel)
+{
+ putExplicitContext(tuple, defel);
+}
+
+void
+sepgsqlGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel)
+{
+ putExplicitContext(tuple, defel);
+}
+
+void
+sepgsqlGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel)
+{
+ putExplicitContext(tuple, defel);
+}
+
+void
+sepgsqlGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel)
+{
+ putExplicitContext(tuple, defel);
+}
+
+void
+sepgsqlGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel)
+{
+ putExplicitContext(tuple, defel);
+}
+
+void
+sepgsqlGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel)
+{
+ putExplicitContext(tuple, defel);
+}
+
+/*******************************************************************************
+ * DATABASE object related hooks
+ *******************************************************************************/
+
+void
+sepgsqlGetDatabaseParam(const char *name)
+{
+ HeapTuple tuple;
+
+ tuple = SearchSysCache(DATABASEOID,
+ ObjectIdGetDatum(MyDatabaseId), 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "SELinux: cache lookup failed for database %u",
+ MyDatabaseId);
+
+ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_DATABASE,
+ DB_DATABASE__GET_PARAM,
+ sepgsqlTupleName(DatabaseRelationId, tuple),
+ true);
+ ReleaseSysCache(tuple);
+}
+
+void
+sepgsqlSetDatabaseParam(const char *name, char *argstring)
+{
+ HeapTuple tuple;
+
+ tuple = SearchSysCache(DATABASEOID,
+ ObjectIdGetDatum(MyDatabaseId), 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "SELinux: cache lookup failed for database %u",
+ MyDatabaseId);
+
+ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_DATABASE,
+ DB_DATABASE__SET_PARAM,
+ sepgsqlTupleName(DatabaseRelationId, tuple),
+ true);
+ ReleaseSysCache(tuple);
+}
+
+/*******************************************************************************
+ * RELATION(Table)/ATTRIBTUE(column) object related hooks
+ *******************************************************************************/
+void
+sepgsqlLockTable(Oid relid)
+{
+ HeapTuple tuple;
+
+ tuple = SearchSysCache(RELOID, ObjectIdGetDatum(relid), 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "SELinux: cache lookup failed for relation %u", relid);
+
+ if (((Form_pg_class) GETSTRUCT(tuple))->relkind == RELKIND_RELATION)
+ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_TABLE,
+ DB_TABLE__LOCK,
+ sepgsqlTupleName(RelationRelationId, tuple),
+ true);
+ ReleaseSysCache(tuple);
+}
+
+/*******************************************************************************
+ * PROCEDURE related hooks
+ *******************************************************************************/
+
+typedef struct
+{
+ PGFunction fn_addr;
+ security_context_t fn_con;
+} sepgsql_fn_info;
+
+static Datum
+invokeTrustedProcedure(PG_FUNCTION_ARGS)
+{
+ sepgsql_fn_info *sefinfo = fcinfo->flinfo->fn_pgaceItem;
+ security_context_t orig_context;
+ Datum retval;
+
+ /*
+ * set new domain
+ */
+ orig_context = sepgsqlSwitchClientContext(sefinfo->fn_con);
+
+ PG_TRY();
+ {
+ retval = sefinfo->fn_addr(fcinfo);
+ }
+ PG_CATCH();
+ {
+ sepgsqlSwitchClientContext(orig_context);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ sepgsqlSwitchClientContext(orig_context);
+
+ return retval;
+}
+
+void
+sepgsqlCallFunction(FmgrInfo *finfo, bool with_perm_check)
+{
+ MemoryContext oldctx;
+ HeapTuple tuple;
+ security_context_t procon, newcon;
+ access_vector_t 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);
+
+ oldctx = MemoryContextSwitchTo(finfo->fn_mcxt);
+ /*
+ * check trusted procedure
+ */
+ procon = pgaceLookupSecurityLabel(HeapTupleGetSecurity(tuple));
+
+ newcon = sepgsqlAvcCreateCon(sepgsqlGetClientContext(),
+ procon,
+ SECCLASS_PROCESS);
+ if (strcmp(newcon, sepgsqlGetClientContext()) != 0)
+ {
+ sepgsql_fn_info *sefinfo
+ = palloc0(sizeof(sepgsql_fn_info));
+
+ sefinfo->fn_addr = finfo->fn_addr;
+ sefinfo->fn_con = newcon;
+ finfo->fn_addr = invokeTrustedProcedure;
+ finfo->fn_pgaceItem = sefinfo;
+
+ perms |= DB_PROCEDURE__ENTRYPOINT;
+ }
+ else
+ pfree(newcon);
+
+ pfree(procon);
+
+ MemoryContextSwitchTo(oldctx);
+
+ if (with_perm_check)
+ {
+ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_PROCEDURE,
+ perms,
+ sepgsqlTupleName(ProcedureRelationId, tuple),
+ true);
+ }
+
+ 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;
+
+ if (getfilecon_raw(filename, &filecon) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not get context of %s", filename)));
+ PG_TRY();
+ {
+ sepgsqlAvcPermission(sepgsqlGetDatabaseContext(),
+ filecon,
+ SECCLASS_DB_DATABASE,
+ DB_DATABASE__LOAD_MODULE,
+ filename,
+ true);
+ }
+ PG_CATCH();
+ {
+ freecon(filecon);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(filecon);
+}
+
+/*******************************************************************************
+ * Binary Large Object hooks
+ *******************************************************************************/
+
+void
+sepgsqlLargeObjectCreate(Relation rel, HeapTuple tuple)
+{
+ sepgsqlSetDefaultContext(rel, tuple);
+
+ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_BLOB,
+ DB_BLOB__CREATE,
+ sepgsqlTupleName(RelationGetRelid(rel), tuple),
+ true);
+}
+
+void
+sepgsqlLargeObjectDrop(Relation rel, HeapTuple tuple, void **pgaceItem)
+{
+ Oid security_id = HeapTupleGetSecurity(tuple);
+ List *okList = (List *) (*pgaceItem);
+ ListCell *l;
+
+ foreach (l, okList)
+ {
+ if (security_id == lfirst_oid(l))
+ return; /* already allowed */
+ }
+
+ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(),
+ security_id,
+ SECCLASS_DB_BLOB,
+ DB_BLOB__DROP,
+ sepgsqlTupleName(RelationGetRelid(rel), tuple),
+ true);
+
+ *pgaceItem = lappend_oid(okList, security_id);
+}
+
+static void
+checkLargeObjectPages(Oid loid, Snapshot snapshot,
+ int32 start_pageno, int32 end_pageno,
+ access_vector_t perms)
+{
+ Relation rel;
+ HeapTuple tuple;
+ SysScanDesc sd;
+ ScanKeyData skey[2];
+ List *okList = NIL;
+
+ rel = heap_open(LargeObjectRelationId, AccessShareLock);
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_largeobject_loid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(loid));
+
+ if (start_pageno <= 0)
+ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId,
+ true, snapshot, 1, skey);
+ else
+ {
+ ScanKeyInit(&skey[1],
+ Anum_pg_largeobject_pageno,
+ BTGreaterEqualStrategyNumber, F_INT4GE,
+ Int32GetDatum(start_pageno));
+
+ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId,
+ true, snapshot, 2, skey);
+ }
+
+ while ((tuple = systable_getnext(sd)) != NULL)
+ {
+ Form_pg_largeobject loForm
+ = (Form_pg_largeobject) GETSTRUCT(tuple);
+ Oid security_id;
+ ListCell *l;
+
+ if (end_pageno >= 0 && loForm->pageno > end_pageno)
+ break;
+
+ security_id = HeapTupleGetSecurity(tuple);
+
+ foreach (l, okList)
+ {
+ if (security_id == lfirst_oid(l))
+ goto skip;
+ }
+ okList = lappend_oid(okList, security_id);
+
+ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(),
+ security_id,
+ SECCLASS_DB_BLOB,
+ perms,
+ sepgsqlTupleName(RelationGetRelid(rel), tuple),
+ true);
+ skip:
+ ;
+ }
+ systable_endscan(sd);
+
+ list_free(okList);
+
+ heap_close(rel, NoLock);
+}
+
+void
+sepgsqlLargeObjectRead(LargeObjectDesc *lodesc, int32 length)
+{
+ int32 start_pageno = lodesc->offset / LOBLKSIZE;
+ int32 end_pageno = (lodesc->offset + length + LOBLKSIZE - 1) / LOBLKSIZE;
+
+ checkLargeObjectPages(lodesc->id, lodesc->snapshot,
+ start_pageno, end_pageno, DB_BLOB__READ);
+}
+
+void
+sepgsqlLargeObjectWrite(LargeObjectDesc *lodesc, int32 length)
+{
+ int32 start_pageno = lodesc->offset / LOBLKSIZE;
+ int32 end_pageno = (lodesc->offset + length + LOBLKSIZE - 1) / LOBLKSIZE;
+
+ checkLargeObjectPages(lodesc->id, lodesc->snapshot,
+ start_pageno, end_pageno, DB_BLOB__WRITE);
+}
+
+void
+sepgsqlLargeObjectTruncate(LargeObjectDesc *lodesc, int32 offset)
+{
+ int32 start_pageno = lodesc->offset / LOBLKSIZE;
+
+ checkLargeObjectPages(lodesc->id, lodesc->snapshot,
+ start_pageno, -1, DB_BLOB__WRITE);
+}
+
+void
+sepgsqlLargeObjectImport(Oid loid, int fdesc, const char *filename)
+{
+ security_context_t tcontext;
+
+ if (fgetfilecon_raw(fdesc, &tcontext) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not get context of %s", filename)));
+ PG_TRY();
+ {
+ sepgsqlAvcPermission(sepgsqlGetClientContext(),
+ tcontext,
+ SECCLASS_FILE,
+ FILE__READ,
+ filename,
+ true);
+ }
+ PG_CATCH();
+ {
+ freecon(tcontext);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(tcontext);
+
+ checkLargeObjectPages(loid, SnapshotNow, -1, -1,
+ DB_BLOB__WRITE | DB_BLOB__IMPORT);
+}
+
+void
+sepgsqlLargeObjectExport(Oid loid, int fdesc, const char *filename)
+{
+ security_context_t tcontext;
+
+ if (fgetfilecon_raw(fdesc, &tcontext) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not get context of %s", filename)));
+ PG_TRY();
+ {
+ sepgsqlAvcPermission(sepgsqlGetClientContext(),
+ tcontext,
+ SECCLASS_FILE,
+ FILE__WRITE,
+ filename,
+ true);
+ }
+ PG_CATCH();
+ {
+ freecon(tcontext);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(tcontext);
+
+ checkLargeObjectPages(loid, SnapshotNow, -1, -1,
+ DB_BLOB__READ | DB_BLOB__EXPORT);
+}
+
+void
+sepgsqlLargeObjectGetSecurity(Relation rel, HeapTuple tuple)
+{
+ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_BLOB,
+ DB_BLOB__GETATTR,
+ sepgsqlTupleName(RelationGetRelid(rel), tuple),
+ true);
+}
+
+void
+sepgsqlLargeObjectSetSecurity(Relation rel, HeapTuple newtup, HeapTuple oldtup)
+{
+ if (HeapTupleGetSecurity(newtup) == HeapTupleGetSecurity(oldtup))
+ return;
+
+ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(oldtup),
+ SECCLASS_DB_BLOB,
+ DB_BLOB__SETATTR | DB_BLOB__RELABELFROM,
+ sepgsqlTupleName(RelationGetRelid(rel), oldtup),
+ true);
+
+ /*
+ * check db_blob:{setattr relabelto}
+ */
+ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(newtup),
+ SECCLASS_DB_BLOB,
+ DB_BLOB__RELABELTO,
+ sepgsqlTupleName(RelationGetRelid(rel), newtup),
+ true);
+}
+
+/*******************************************************************************
+ * ExecScan hooks
+ *******************************************************************************/
+static bool abort_on_violated_tuple = false;
+
+bool
+sepgsqlExecScan(Scan *scan, Relation rel, TupleTableSlot *slot)
+{
+ HeapTuple tuple;
+ uint32 perms = scan->pgaceTuplePerms;
+
+ if (perms == 0)
+ return true;
+
+ tuple = ExecMaterializeSlot(slot);
+
+ return sepgsqlCheckTuplePerms(rel, tuple, NULL, perms,
+ abort_on_violated_tuple);
+}
+
+/* ----------------------------------------------------------
+ * special cases for Foreign Key constraint
+ * ---------------------------------------------------------- */
+void
+sepgsqlBeginPerformCheckFK(Relation rel, bool is_primary, Datum *save_pgace)
+{
+ /*
+ * NOTE: when a tuple is inserted/updated on FK side, all we should do
+ * is simply filtering violated tuples on PK size, as normal row-level
+ * access control doin.
+ * In the result, INSERT/UPDATE with invisible tuple is failed.
+ */
+ if (is_primary)
+ return;
+
+ *save_pgace = BoolGetDatum(abort_on_violated_tuple);
+ abort_on_violated_tuple = true;
+}
+
+void
+sepgsqlEndPerformCheckFK(Relation rel, bool is_primary, Datum save_pgace)
+{
+ if (is_primary)
+ return;
+
+ abort_on_violated_tuple = DatumGetBool(save_pgace);
+}
+
+/*******************************************************************************
+ * security_label hooks
+ *******************************************************************************/
+char *
+sepgsqlTranslateSecurityLabelIn(char *context)
+{
+ security_context_t i_context;
+ char *result;
+
+ if (selinux_trans_to_raw_context((security_context_t) context, &i_context) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not translate mls label")));
+ PG_TRY();
+ {
+ result = pstrdup(i_context);
+ }
+ PG_CATCH();
+ {
+ freecon(i_context);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(i_context);
+
+ return result;
+}
+
+char *
+sepgsqlTranslateSecurityLabelOut(char *context)
+{
+ security_context_t o_context;
+ char *result;
+
+ if (selinux_raw_to_trans_context((security_context_t) context, &o_context) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not translate mls label")));
+ PG_TRY();
+ {
+ result = pstrdup(o_context);
+ }
+ PG_CATCH();
+ {
+ freecon(o_context);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(o_context);
+
+ return result;
+}
+
+/*
+ * sepgsqlValidateSecurityLabel() checks whether the given context
+ * is valid for the current policy, or not.
+ * If not valid, it returns alternative context.
+ */
+char *
+sepgsqlValidateSecurityLabel(char *context)
+{
+ security_context_t unlabeled;
+ char *result;
+
+ if (context != NULL)
+ {
+ if (security_check_context_raw((security_context_t) context) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: %s is invalid security context",
+ context)));
+ return context;
+ }
+
+ if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not get unlabeled context")));
+ PG_TRY();
+ {
+ result = pstrdup(unlabeled);
+ }
+ PG_CATCH();
+ {
+ freecon(unlabeled);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(unlabeled);
+
+ return result;
+}
+
+char *
+sepgsqlSecurityLabelOfLabel(void)
+{
+ security_context_t table_context, tuple_context;
+ HeapTuple tuple;
+
+ /*
+ * obtain security context of pg_security
+ */
+ tuple = SearchSysCache(RELOID,
+ ObjectIdGetDatum(SecurityRelationId), 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "SELinux: cache lookup failed for relation %u",
+ SecurityRelationId);
+
+ table_context = pgaceLookupSecurityLabel(HeapTupleGetSecurity(tuple));
+
+ tuple_context = sepgsqlAvcCreateCon(sepgsqlGetServerContext(),
+ table_context, SECCLASS_DB_TUPLE);
+ pfree(table_context);
+
+ ReleaseSysCache(tuple);
+
+ return tuple_context;
+}
+
+/******************************************************************
+ * HeapTuple modification hooks
+ ******************************************************************/
+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(ItemIdIsNormal(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;
+}
+
+static bool
+isTrustedRelation(Relation rel, bool is_internal)
+{
+ if (!is_internal)
+ return false;
+
+ if (RelationGetForm(rel)->relkind != RELKIND_RELATION)
+ return true;
+
+ switch (RelationGetRelid(rel))
+ {
+ case LargeObjectRelationId:
+ case SecurityRelationId:
+ return true;
+ }
+ 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)
+ sepgsqlSetDefaultContext(rel, tuple);
+ else if (!is_internal && RelationGetRelid(rel) == LargeObjectRelationId)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: not allowed to insert into "
+ "pg_largeobject.security_context")));
+
+ if (isTrustedRelation(rel, is_internal))
+ 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)
+ {
+ Oid security_id = HeapTupleGetSecurity(oldtup);
+
+ HeapTupleSetSecurity(newtup, security_id);
+ }
+ else if (!is_internal && RelationGetRelid(rel) == LargeObjectRelationId)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: not allowed to update "
+ "pg_largeobject.security_context")));
+
+ if (isTrustedRelation(rel, is_internal))
+ return true;
+
+ if (is_internal)
+ {
+ perms = SEPGSQL_PERMS_UPDATE;
+ if (HeapTupleGetSecurity(newtup) != HeapTupleGetSecurity(oldtup))
+ perms |= SEPGSQL_PERMS_RELABELFROM;
+ else if (with_returning)
+ perms |= SEPGSQL_PERMS_SELECT;
+ 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 = SEPGSQL_PERMS_DELETE;
+ bool rc;
+
+ if (!is_internal)
+ return true;
+
+ if (isTrustedRelation(rel, is_internal))
+ return true;
+
+ oldtup = getHeapTupleFromItemPointer(rel, otid);
+ if (with_returning)
+ perms |= SEPGSQL_PERMS_SELECT;
+ rc = sepgsqlCheckTuplePerms(rel, oldtup, NULL, perms, is_internal);
+ heap_freetuple(oldtup);
+
+ return rc;
+}
diff -rpNU3 base/src/backend/security/sepgsql/permissions.c sepgsql/src/backend/security/sepgsql/permissions.c
--- base/src/backend/security/sepgsql/permissions.c 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/src/backend/security/sepgsql/permissions.c 2008-07-11 14:10:51.000000000 +0900
@@ -0,0 +1,597 @@
+
+/*
+ * src/backend/security/sepgsql/permissions.c
+ * applies SE-PostgreSQL permission checks
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "access/genam.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 "utils/fmgroids.h"
+#include "utils/syscache.h"
+#include "utils/tqual.h"
+
+static access_vector_t
+sepgsql_perms_to_common_perms(uint32 perms)
+{
+ access_vector_t result = 0;
+
+ result |= (perms & SEPGSQL_PERMS_USE ? COMMON_DATABASE__GETATTR : 0);
+ result |= (perms & SEPGSQL_PERMS_SELECT ? COMMON_DATABASE__GETATTR : 0);
+ result |= (perms & SEPGSQL_PERMS_UPDATE ? COMMON_DATABASE__SETATTR : 0);
+ result |= (perms & SEPGSQL_PERMS_INSERT ? COMMON_DATABASE__CREATE : 0);
+ result |= (perms & SEPGSQL_PERMS_DELETE ? COMMON_DATABASE__DROP : 0);
+ result |= (perms & SEPGSQL_PERMS_RELABELFROM ? COMMON_DATABASE__RELABELFROM : 0);
+ result |= (perms & SEPGSQL_PERMS_RELABELTO ? COMMON_DATABASE__RELABELTO : 0);
+
+ return result;
+}
+
+static access_vector_t
+sepgsql_perms_to_tuple_perms(uint32 perms)
+{
+ access_vector_t result = 0;
+
+ result |= (perms & SEPGSQL_PERMS_USE ? DB_TUPLE__USE : 0);
+ result |= (perms & SEPGSQL_PERMS_SELECT ? DB_TUPLE__SELECT : 0);
+ result |= (perms & SEPGSQL_PERMS_UPDATE ? DB_TUPLE__UPDATE : 0);
+ result |= (perms & SEPGSQL_PERMS_INSERT ? DB_TUPLE__INSERT : 0);
+ result |= (perms & SEPGSQL_PERMS_DELETE ? DB_TUPLE__DELETE : 0);
+ result |= (perms & SEPGSQL_PERMS_RELABELFROM ? DB_TUPLE__RELABELFROM : 0);
+ result |= (perms & SEPGSQL_PERMS_RELABELTO ? DB_TUPLE__RELABELTO : 0);
+
+ return result;
+}
+
+const char *
+sepgsqlTupleName(Oid relid, HeapTuple tuple)
+{
+ static char buffer[NAMEDATALEN * 3];
+
+ switch (relid)
+ {
+ case AttributeRelationId:
+ {
+ Form_pg_attribute attForm
+ = (Form_pg_attribute) GETSTRUCT(tuple);
+
+ if (!IsBootstrapProcessingMode())
+ {
+ HeapTuple exttup = SearchSysCache(RELOID,
+ ObjectIdGetDatum
+ (attForm->attrelid),
+ 0, 0, 0);
+
+ if (HeapTupleIsValid(exttup))
+ {
+ snprintf(buffer, sizeof(buffer), "%s.%s",
+ NameStr(((Form_pg_class) GETSTRUCT(exttup))->relname),
+ NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname));
+ ReleaseSysCache(exttup);
+ break;
+ }
+ }
+ snprintf(buffer, sizeof(buffer), "%s",
+ NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname));
+ break;
+ }
+ case AuthIdRelationId:
+ snprintf(buffer, sizeof(buffer), "%s",
+ NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname));
+ break;
+
+ case RelationRelationId:
+ snprintf(buffer, sizeof(buffer), "%s",
+ NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname));
+ break;
+
+ case DatabaseRelationId:
+ snprintf(buffer, sizeof(buffer), "%s",
+ NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname));
+ break;
+
+ case LargeObjectRelationId:
+ snprintf(buffer, sizeof(buffer), "loid:%u",
+ ((Form_pg_largeobject) GETSTRUCT(tuple))->loid);
+ break;
+
+ case ProcedureRelationId:
+ snprintf(buffer, sizeof(buffer), "%s",
+ NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname));
+ break;
+
+ case TriggerRelationId:
+ snprintf(buffer, sizeof(buffer), "%s",
+ NameStr(((Form_pg_trigger) GETSTRUCT(tuple))->tgname));
+ break;
+
+ case TypeRelationId:
+ snprintf(buffer, sizeof(buffer), "pg_type::%s",
+ NameStr(((Form_pg_type) GETSTRUCT(tuple))->typname));
+ break;
+ default:
+ /*
+ * this tuple has no name
+ */
+ return NULL;
+ }
+ return buffer;
+}
+
+/*
+ * sepgsqlCheckTuplePerms
+ *
+ * This function evaluates given permission set (SEPGSQL_PERMS_*) onto the
+ * given tuple, with translating them into proper SELinux permission.
+ *
+ * Accesses to some of system catalog has special meanings. DELETE a tuple
+ * within pg_class also means DROP TABLE for instance. In this case,
+ * SE-PostgreSQL translate given SEPGSQL_PERMS_DELETE into DB_TABLE__DROP
+ * to keep consistency of user operation. To delete a tuple within pg_class
+ * always means dropping a table independent from what SQL statement is
+ * used.
+ *
+ * Thus, checks for some of system catalog need to modify given permission
+ * set at checkTuplePermsXXXX() functions.
+ */
+
+static void
+checkTuplePermsAttribute(HeapTuple tuple, HeapTuple oldtup,
+ access_vector_t *p_perms,
+ security_class_t *p_tclass)
+{
+ Form_pg_attribute attForm, oldForm;
+ HeapTuple reltup;
+
+ attForm = (Form_pg_attribute) GETSTRUCT(tuple);
+ switch (attForm->attrelid)
+ {
+ case TypeRelationId:
+ case ProcedureRelationId:
+ case AttributeRelationId:
+ case RelationRelationId:
+ /*
+ * those are pure relation
+ */
+ break;
+ default:
+ reltup = SearchSysCache(RELOID,
+ ObjectIdGetDatum(attForm->attrelid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(reltup))
+ elog(ERROR, "SELinux: cache lookup failed for relation %u",
+ attForm->attrelid);
+ if (RELKIND_RELATION !=
+ ((Form_pg_class) GETSTRUCT(reltup))->relkind)
+ {
+ *p_tclass = SECCLASS_DB_TUPLE;
+ *p_perms = sepgsql_perms_to_tuple_perms(*p_perms);
+ ReleaseSysCache(reltup);
+ return;
+ }
+ ReleaseSysCache(reltup);
+ break;
+ }
+ *p_tclass = SECCLASS_DB_COLUMN;
+ *p_perms = sepgsql_perms_to_common_perms(*p_perms);
+ if (HeapTupleIsValid(oldtup))
+ {
+ oldForm = (Form_pg_attribute) GETSTRUCT(oldtup);
+
+ if (oldForm->attisdropped != true && attForm->attisdropped == true)
+ *p_perms |= DB_COLUMN__DROP;
+ }
+}
+
+static void
+checkTuplePermsLargeObject(HeapTuple tuple, HeapTuple oldtup,
+ access_vector_t *p_perms,
+ security_class_t *p_tclass)
+{
+ access_vector_t perms;
+
+ /*
+ * NOTE: INSERT tuples into pg_largeobject has a possibility to create
+ * a new largeobject, if the given loid is not exist on the current
+ * pg_largeobject. Ditto for DELETE statement, it also has a possibility
+ * to drop a largeobject, if it removes all tuples within a large object.
+ *
+ * db_blob:{create} and db_blob:{drop} should be evaluated for
+ * creation/deletion of largeobject, but we have to check pg_largeobject
+ * with SnapshotSelf whether there is one or more tuple having same loid,
+ * or not, on each tuple insertion or deletion.
+ *
+ * So, we assume any INSERT means db_blob:{create}, any DELETE means
+ * db_blob:{drop}.
+ */
+ perms = sepgsql_perms_to_common_perms(*p_perms);
+ perms |= (*p_perms & SEPGSQL_PERMS_INSERT ? DB_BLOB__WRITE : 0);
+ perms |= (*p_perms & SEPGSQL_PERMS_DELETE ? DB_BLOB__WRITE : 0);
+ perms |= (*p_perms & SEPGSQL_PERMS_READ ? DB_BLOB__READ : 0);
+ perms |= (*p_perms & SEPGSQL_PERMS_WRITE ? DB_BLOB__WRITE : 0);
+
+ *p_tclass = SECCLASS_DB_BLOB;
+ *p_perms = perms;
+}
+
+static void
+checkTuplePermsProcedure(HeapTuple tuple, HeapTuple oldtup,
+ access_vector_t *p_perms,
+ security_class_t *p_tclass)
+{
+ access_vector_t 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 *file_name;
+ security_context_t file_context;
+
+ /*
+ * <client type> <-- database:module_install --> <database type>
+ */
+ sepgsqlAvcPermission(sepgsqlGetClientContext(),
+ sepgsqlGetDatabaseContext(),
+ SECCLASS_DB_DATABASE,
+ DB_DATABASE__INSTALL_MODULE,
+ NULL, true);
+
+ /*
+ * <client type> <-- database:module_install --> <file type>
+ */
+ file_name = DatumGetCString(DirectFunctionCall1(textout, newbin));
+ file_name = expand_dynamic_library_name(file_name);
+ if (getfilecon_raw(file_name, &file_context) < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("SELinux: could not get context of %s",
+ file_name)));
+ PG_TRY();
+ {
+ sepgsqlAvcPermission(sepgsqlGetClientContext(),
+ file_context,
+ SECCLASS_DB_DATABASE,
+ DB_DATABASE__INSTALL_MODULE,
+ file_name, true);
+ }
+ PG_CATCH();
+ {
+ freecon(file_context);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ freecon(file_context);
+ }
+ }
+ }
+ *p_perms = perms;
+ *p_tclass = SECCLASS_DB_PROCEDURE;
+}
+
+static void
+checkTuplePermsRelation(HeapTuple tuple, HeapTuple oldtup,
+ access_vector_t *p_perms,
+ security_class_t *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);
+ }
+}
+
+bool
+sepgsqlCheckTuplePerms(Relation rel, HeapTuple tuple, HeapTuple oldtup,
+ uint32 perms, bool abort)
+{
+ security_class_t tclass;
+ bool rc = true;
+
+ Assert(tuple != NULL);
+
+ switch (RelationGetRelid(rel))
+ {
+ case DatabaseRelationId: /* pg_datbase */
+ perms = sepgsql_perms_to_common_perms(perms);
+ tclass = SECCLASS_DB_DATABASE;
+ break;
+
+ case RelationRelationId: /* pg_class */
+ checkTuplePermsRelation(tuple, oldtup, &perms, &tclass);
+ break;
+
+ case AttributeRelationId: /* pg_attribute */
+ checkTuplePermsAttribute(tuple, oldtup, &perms, &tclass);
+ break;
+
+ case ProcedureRelationId: /* pg_proc */
+ checkTuplePermsProcedure(tuple, oldtup, &perms, &tclass);
+ break;
+
+ case LargeObjectRelationId: /* pg_largeobject */
+ checkTuplePermsLargeObject(tuple, oldtup, &perms, &tclass);
+ break;
+
+ default:
+ perms = sepgsql_perms_to_tuple_perms(perms);
+ tclass = SECCLASS_DB_TUPLE;
+ break;
+ }
+
+ if (perms)
+ {
+ const char *objname = sepgsqlTupleName(RelationGetRelid(rel), tuple);
+
+ rc = sepgsqlAvcPermissionSid(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ tclass, perms, objname, abort);
+ }
+ return rc;
+}
+
+/*
+ * sepgsqlSetDefaultContext
+ *
+ * This function attach a proper security context for a newly inserted tuple,
+ * refering the security policy.
+ * In the default, any tuple inherits the security context of its table.
+ * However, we have several exception for some of system catalog. It come from
+ * TYPE_TRANSITION rules in the security policy.
+ */
+
+static void
+setDefaultContextDatabase(Relation rel, HeapTuple tuple)
+{
+ security_context_t ncontext;
+ Oid security_id;
+
+ ncontext = sepgsqlAvcCreateCon(sepgsqlGetClientContext(),
+ sepgsqlGetClientContext(),
+ SECCLASS_DB_DATABASE);
+ security_id = pgaceSecurityLabelToSid(ncontext);
+ HeapTupleSetSecurity(tuple, security_id);
+}
+
+static void
+setDefaultContextRelation(Relation rel, HeapTuple tuple)
+{
+ Oid dbsid, tblsid;
+
+ dbsid = sepgsqlGetDatabaseSecurityId();
+ tblsid = sepgsqlAvcCreateConSid(sepgsqlGetClientContext(),
+ dbsid,
+ SECCLASS_DB_TABLE);
+ HeapTupleSetSecurity(tuple, tblsid);
+}
+
+static void
+setDefaultContextAttribute(Relation rel, HeapTuple tuple)
+{
+ HeapTuple reltup;
+ Oid security_id;
+ Form_pg_class clsForm;
+ Form_pg_attribute attForm
+ = (Form_pg_attribute) GETSTRUCT(tuple);
+
+ switch (attForm->attrelid)
+ {
+ case TypeRelationId:
+ case ProcedureRelationId:
+ case AttributeRelationId:
+ case RelationRelationId:
+ /*
+ * we cannot touch these relations at very early phase in bootstrap
+ */
+ if (IsBootstrapProcessingMode())
+ {
+ Oid security_id;
+
+ security_id = sepgsqlAvcCreateConSid(sepgsqlGetClientContext(),
+ sepgsqlGetDatabaseSecurityId(),
+ SECCLASS_DB_TABLE);
+ HeapTupleSetSecurity(tuple, security_id);
+ break;
+ }
+ default:
+ reltup = SearchSysCache(RELOID,
+ ObjectIdGetDatum(attForm->attrelid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(reltup))
+ elog(ERROR, "SELinux: cache lookup failed for relation %u",
+ attForm->attrelid);
+ clsForm = (Form_pg_class) GETSTRUCT(reltup);
+
+ security_id
+ = sepgsqlAvcCreateConSid(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(reltup),
+ (clsForm->relkind == RELKIND_RELATION
+ ? SECCLASS_DB_COLUMN
+ : SECCLASS_DB_TUPLE));
+ HeapTupleSetSecurity(tuple, security_id);
+
+ ReleaseSysCache(reltup);
+ break;
+ }
+ return;
+}
+
+static void
+setDefaultContextProcedure(Relation rel, HeapTuple tuple)
+{
+ Oid security_id;
+
+ security_id = sepgsqlAvcCreateConSid(sepgsqlGetClientContext(),
+ sepgsqlGetDatabaseSecurityId(),
+ SECCLASS_DB_PROCEDURE);
+ HeapTupleSetSecurity(tuple, security_id);
+}
+
+static void
+setDefaultContextLargeObject(Relation rel, HeapTuple tuple)
+{
+ /*
+ * NOTE:
+ * A new tuple to be inserted into pg_largeobject inheris
+ * security context of tuple with same large object id.
+ * We can scan it with SnapshotNow because lo_create invokes
+ * CommandCounterIncrement() just after create a new large
+ * object.
+ *
+ * If no page found, it means this action is to insert the
+ * first page, or user run INSERT INTO ... statement with
+ * multiple tuples with same loid.
+ * However, these newly inserted tuples are labeled by
+ * TYPE_TRANSITION rules in both cases. So, there are
+ * no differences.
+ */
+ Form_pg_largeobject loForm
+ = (Form_pg_largeobject) GETSTRUCT(tuple);
+ ScanKeyData skey;
+ SysScanDesc scan;
+ HeapTuple lotup;
+ Oid security_id = InvalidOid;
+
+ ScanKeyInit(&skey,
+ Anum_pg_largeobject_loid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(loForm->loid));
+ scan = systable_beginscan(rel,
+ LargeObjectLOidPNIndexId, true,
+ SnapshotNow, 1, &skey);
+ while ((lotup = systable_getnext(scan)) != NULL)
+ {
+ security_id = HeapTupleGetSecurity(lotup);
+ if (security_id != InvalidOid)
+ break;
+ }
+ systable_endscan(scan);
+
+ if (security_id == InvalidOid)
+ {
+ security_id = sepgsqlAvcCreateConSid(sepgsqlGetClientContext(),
+ sepgsqlGetDatabaseSecurityId(),
+ SECCLASS_DB_BLOB);
+ }
+ HeapTupleSetSecurity(tuple, security_id);
+}
+
+void
+sepgsqlSetDefaultContext(Relation rel, HeapTuple tuple)
+{
+ security_context_t ncontext;
+ HeapTuple reltup;
+ Oid security_id;
+
+ switch (RelationGetRelid(rel))
+ {
+ case DatabaseRelationId:
+ setDefaultContextDatabase(rel, tuple);
+ return;
+
+ case RelationRelationId:
+ {
+ Form_pg_class clsForm
+ = (Form_pg_class) GETSTRUCT(tuple);
+
+ if (clsForm->relkind == RELKIND_RELATION)
+ {
+ setDefaultContextRelation(rel, tuple);
+ return;
+ }
+ }
+ break;
+
+ case AttributeRelationId:
+ setDefaultContextAttribute(rel, tuple);
+ return;
+
+ case ProcedureRelationId:
+ setDefaultContextProcedure(rel, tuple);
+ return;
+
+ case LargeObjectRelationId:
+ setDefaultContextLargeObject(rel, tuple);
+ return;
+
+ case TypeRelationId:
+ if (IsBootstrapProcessingMode())
+ {
+ /*
+ * we cannot touch system cache in very early phase
+ */
+ security_context_t tcontext
+ = sepgsqlAvcCreateCon(sepgsqlGetClientContext(),
+ sepgsqlGetDatabaseContext(),
+ SECCLASS_DB_TABLE);
+ ncontext = sepgsqlAvcCreateCon(sepgsqlGetClientContext(),
+ tcontext,
+ SECCLASS_DB_TUPLE);
+ security_id = pgaceSecurityLabelToSid(ncontext);
+ HeapTupleSetSecurity(tuple, security_id);
+
+ return;
+ }
+ break;
+ }
+ /*
+ * normal or user defined relation
+ */
+ reltup = SearchSysCache(RELOID,
+ ObjectIdGetDatum(RelationGetRelid(rel)),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(reltup))
+ elog(ERROR, "SELinux: cache lookup failed for relation %u",
+ RelationGetRelid(rel));
+
+ security_id = sepgsqlAvcCreateConSid(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(reltup),
+ SECCLASS_DB_TUPLE);
+ HeapTupleSetSecurity(tuple, security_id);
+
+ ReleaseSysCache(reltup);
+}
diff -rpNU3 base/src/backend/security/sepgsql/proxy.c sepgsql/src/backend/security/sepgsql/proxy.c
--- base/src/backend/security/sepgsql/proxy.c 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/src/backend/security/sepgsql/proxy.c 2008-07-11 14:10:51.000000000 +0900
@@ -0,0 +1,1461 @@
+
+/*
+ * src/backend/security/sepgsql/proxy.c
+ * proxy routines to pick up all appeared columns, functions, ...
+ * within given queries, and apply mandatory access controls.
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "catalog/heap.h"
+#include "catalog/indexing.h"
+#include "catalog/namespace.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 "executor/executor.h"
+#include "nodes/security.h"
+#include "optimizer/clauses.h"
+#include "optimizer/plancat.h"
+#include "optimizer/prep.h"
+#include "optimizer/tlist.h"
+#include "parser/parsetree.h"
+#include "security/pgace.h"
+#include "storage/lock.h"
+#include "utils/fmgroids.h"
+#include "utils/syscache.h"
+#include "utils/tqual.h"
+
+/*
+ * queryStack
+ *
+ * This structure represents a hierarchical relationshipt
+ * between subqueries. When a Var node has positive varlevelsup,
+ * it refers upper level Query structure using the chain of
+ * queryStack.
+ */
+typedef struct queryStack
+{
+ struct queryStack *parent;
+ Query *query;
+} queryStack;
+
+/*
+ * sepgsqlWalkerContext
+ *
+ * This structure holds a context during analyzing a given query.
+ * selist is a list of SEvalItemXXX objects to enumerate appared
+ * tables, columns and functions. It is evaluated later, just
+ * before executing query.
+ * is_internal_use shows the current state whether the current
+ * Node is chained with target list, or conditional clause.
+ */
+typedef struct sepgsqlWalkerContext
+{
+ List *selist; /* List of SEvalItem */
+
+ struct queryStack *qstack;
+
+ bool is_internal_use;
+} sepgsqlWalkerContext;
+
+/* static definitions for proxy functions */
+static void proxyRteSubQuery(sepgsqlWalkerContext *swc, Query *query);
+
+static bool sepgsqlExprWalker(Node *node, sepgsqlWalkerContext *swc);
+
+static void execVerifyQuery(List *selist);
+
+/*
+ * addEvalRelation
+ * addEvalRelationRTE
+ *
+ * These functions add a given relation into selist, if it is not
+ * contained yet. In addition, addEvalRelationRTE also marks required
+ * permissions on rte->pgaceTuplePerms. It is delivered to Scan object
+ * and we can use it on ExecScan hook to apply tuple-level access
+ * controls.
+ */
+static List *
+addEvalRelation(List *selist, Oid relid, bool inh, uint32 perms)
+{
+ SEvalItemRelation *ser;
+
+ ListCell *l;
+
+ foreach(l, selist)
+ {
+ ser = (SEvalItemRelation *) lfirst(l);
+ if (IsA(ser, SEvalItemRelation)
+ && ser->relid == relid
+ && ser->inh == inh)
+ {
+ ser->perms |= perms;
+ return selist;
+ }
+ }
+ /*
+ * not found
+ */
+ ser = makeNode(SEvalItemRelation);
+ ser->perms = perms;
+ ser->relid = relid;
+ ser->inh = inh;
+
+ return lappend(selist, ser);
+}
+
+static List *
+addEvalRelationRTE(List *selist, RangeTblEntry *rte, uint32 perms)
+{
+ rte->pgaceTuplePerms |= (perms & DB_TABLE__USE ? SEPGSQL_PERMS_USE : 0);
+ rte->pgaceTuplePerms |=
+ (perms & DB_TABLE__SELECT ? SEPGSQL_PERMS_SELECT : 0);
+ rte->pgaceTuplePerms |=
+ (perms & DB_TABLE__INSERT ? SEPGSQL_PERMS_INSERT : 0);
+ rte->pgaceTuplePerms |=
+ (perms & DB_TABLE__UPDATE ? SEPGSQL_PERMS_UPDATE : 0);
+ rte->pgaceTuplePerms |=
+ (perms & DB_TABLE__DELETE ? SEPGSQL_PERMS_DELETE : 0);
+
+ /*
+ * for 'pg_largeobject'
+ */
+ if (rte->relid == LargeObjectRelationId && (perms & DB_TABLE__DELETE))
+ rte->pgaceTuplePerms |= SEPGSQL_PERMS_WRITE;
+
+ return addEvalRelation(selist, rte->relid, rte->inh, perms);
+}
+
+/*
+ * addEvalAttribute
+ * addEvalAttributeRTE
+ *
+ * These functions add a given attribute into selist, if it is not
+ * contained yet. In addition, addEvalAttributeRTE also marks required
+ * permissions on rte->pgaceTuplePerms. It is delivered to Scan object
+ * and we can use it on ExecScan hook to apply tuple-level access
+ * controls.
+ */
+static List *
+addEvalAttribute(List *selist, Oid relid, bool inh, AttrNumber attno, uint32 perms)
+{
+ SEvalItemAttribute *sea;
+
+ ListCell *l;
+
+ foreach(l, selist)
+ {
+ sea = (SEvalItemAttribute *) lfirst(l);
+ if (IsA(sea, SEvalItemAttribute)
+ && sea->relid == relid
+ && sea->inh == inh
+ && sea->attno == attno)
+ {
+ sea->perms |= perms;
+ return selist;
+ }
+ }
+ /*
+ * not found
+ */
+ sea = makeNode(SEvalItemAttribute);
+ sea->perms = perms;
+ sea->relid = relid;
+ sea->inh = inh;
+ sea->attno = attno;
+
+ return lappend(selist, sea);
+}
+
+static List *
+addEvalAttributeRTE(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 = addEvalRelationRTE(selist, rte, t_perms);
+
+ /*
+ * for 'security_context'
+ */
+ if (attno == SecurityAttributeNumber
+ && (perms & (DB_COLUMN__UPDATE | DB_COLUMN__INSERT)))
+ rte->pgaceTuplePerms |= SEPGSQL_PERMS_RELABELFROM;
+
+ /*
+ * for 'pg_largeobject'
+ */
+ if (rte->relid == LargeObjectRelationId)
+ {
+ if ((perms & DB_COLUMN__SELECT) && attno == Anum_pg_largeobject_data)
+ rte->pgaceTuplePerms |= SEPGSQL_PERMS_READ;
+ if ((perms & DB_COLUMN__UPDATE) && attno == Anum_pg_largeobject_data)
+ rte->pgaceTuplePerms |= SEPGSQL_PERMS_WRITE;
+ }
+
+ return addEvalAttribute(selist, rte->relid, rte->inh, attno, perms);
+}
+
+/*
+ * addEvalPgProc
+ *
+ * This function adds a given procedure into selist, if it is not
+ * contained yet.
+ */
+static List *
+addEvalPgProc(List *selist, Oid funcid, uint32 perms)
+{
+ SEvalItemProcedure *sep;
+
+ ListCell *l;
+
+ foreach(l, selist)
+ {
+ sep = (SEvalItemProcedure *) lfirst(l);
+ if (IsA(sep, SEvalItemProcedure)
+ && sep->funcid == funcid)
+ {
+ sep->perms |= perms;
+ return selist;
+ }
+ }
+ /*
+ * not found
+ */
+ sep = makeNode(SEvalItemProcedure);
+ sep->perms = perms;
+ sep->funcid = funcid;
+
+ return lappend(selist, sep);
+}
+
+/*
+ * addEvalTriggerAccess
+ *
+ * This function adds needed items into selist, to execute a trigger
+ * function. At least, it requires permission set to execute a function
+ * configured as a trigger, to select a table and whole of columns
+ * because whole of a tuple is delivered to trigger functions.
+ */
+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 = addEvalRelation(selist, relid, false, DB_TABLE__SELECT);
+ for (attnum = FirstLowInvalidHeapAttributeNumber + 1; attnum <= 0; attnum++)
+ {
+ if (attnum == ObjectIdAttributeNumber
+ && !classForm->relhasoids)
+ continue;
+ selist = addEvalAttribute(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;
+}
+
+/*
+ * sepgsqlExprWalker
+ *
+ * This function walks on the given node tree recursively, to pick up
+ * all appeared tables, columns and functions. Their identifiers are
+ * chained swc->selist, and evaluated later.
+ *
+ * walkVarHelper and walkOpExprHelper are used to simplify its
+ * implementation. If swx->is_internal_use is true, it add a "use"
+ * permission to be evaluate, or a "select" permission otherwise.
+ */
+static void
+walkVarHelper(sepgsqlWalkerContext *swc, Var *var)
+{
+ RangeTblEntry *rte;
+ queryStack *qstack;
+ Query *query;
+ int lv;
+
+ Assert(IsA(var, Var));
+
+ /*
+ * resolve external Var reference
+ */
+ qstack = swc->qstack;
+ lv = var->varlevelsup;
+ while (lv > 0)
+ {
+ Assert(!!qstack->parent);
+ qstack = qstack->parent;
+ lv--;
+ }
+ query = qstack->query;
+ if (!query)
+ elog(ERROR, "SELinux: could not walk T_Var node in this context");
+
+ rte = rt_fetch(var->varno, query->rtable);
+ Assert(IsA(rte, RangeTblEntry));
+
+ if (rte->rtekind == RTE_RELATION)
+ {
+ /*
+ * table:{select/use} and column:{select/use}
+ */
+ swc->selist = addEvalAttributeRTE(swc->selist, rte, var->varattno,
+ swc->is_internal_use
+ ? DB_COLUMN__USE : DB_COLUMN__SELECT);
+
+ }
+ else if (rte->rtekind == RTE_JOIN)
+ {
+ Node *node = list_nth(rte->joinaliasvars,
+ var->varattno - 1);
+
+ sepgsqlExprWalker(node, swc);
+ }
+}
+
+static void
+walkOpExprHelper(sepgsqlWalkerContext *swc, 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);
+
+ swc->selist = addEvalPgProc(swc->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);
+}
+
+static bool
+sepgsqlExprWalker(Node *node, sepgsqlWalkerContext *swc)
+{
+ if (node == NULL)
+ return false;
+
+ switch (nodeTag(node))
+ {
+ case T_Var:
+ walkVarHelper(swc, (Var *) node);
+ break;
+
+ case T_FuncExpr:
+ swc->selist = addEvalPgProc(swc->selist,
+ ((FuncExpr *) node)->funcid,
+ DB_PROCEDURE__EXECUTE);
+ break;
+
+ case T_Aggref:
+ swc->selist = addEvalPgProc(swc->selist,
+ ((Aggref *) node)->aggfnoid,
+ DB_PROCEDURE__EXECUTE);
+ break;
+
+ case T_OpExpr:
+ case T_DistinctExpr: /* typedef of OpExpr */
+ case T_NullIfExpr: /* typedef of OpExpr */
+ walkOpExprHelper(swc, ((OpExpr *) node)->opno);
+ break;
+
+ case T_ScalarArrayOpExpr:
+ walkOpExprHelper(swc, ((ScalarArrayOpExpr *) node)->opno);
+ break;
+
+ case T_SubLink:
+ {
+ SubLink *slink = (SubLink *) node;
+
+ Assert(IsA(slink->subselect, Query));
+
+ proxyRteSubQuery(swc, (Query *) slink->subselect);
+ break;
+ }
+ case T_ArrayCoerceExpr:
+ {
+ ArrayCoerceExpr *ace = (ArrayCoerceExpr *) node;
+
+ if (ace->elemfuncid != InvalidOid)
+ swc->selist = addEvalPgProc(swc->selist,
+ ace->elemfuncid,
+ DB_PROCEDURE__EXECUTE);
+ break;
+ }
+ case T_RowCompareExpr:
+ {
+ RowCompareExpr *rce = (RowCompareExpr *) node;
+ ListCell *l;
+
+ foreach(l, rce->opnos) walkOpExprHelper(swc, lfirst_oid(l));
+ break;
+ }
+ case T_SortClause:
+ case T_GroupClause:
+ {
+ SortClause *sc = (SortClause *) node;
+ Query *q = swc->qstack->query;
+ TargetEntry *tle
+ = get_sortgroupref_tle(sc->tleSortGroupRef, q->targetList);
+
+ Assert(IsA(tle, TargetEntry));
+
+ walkOpExprHelper(swc, sc->sortop);
+ sepgsqlExprWalker((Node *)tle->expr, swc);
+ }
+ return false; /* expression_tree_walker does not suppor them */
+
+ default:
+ break;
+ }
+
+ return expression_tree_walker(node, sepgsqlExprWalker, (void *) swc);
+}
+
+static bool
+sepgsqlExprWalkerFlags(Node *node, sepgsqlWalkerContext *swc, bool is_internal_use)
+{
+ bool saved_is_internal_use = swc->is_internal_use;
+ bool rc;
+
+ swc->is_internal_use = is_internal_use;
+ rc = sepgsqlExprWalker(node, swc);
+ swc->is_internal_use = saved_is_internal_use;
+
+ return rc;
+}
+
+/*
+ * checkSelectFromExpr
+ *
+ * It appends SEvalItem of any relation within FROM clause into
+ * selist recursively.
+ *
+ */
+static void
+checkSelectFromExpr(sepgsqlWalkerContext *swc, Query *query, Node *node)
+{
+ if (node == NULL)
+ return;
+
+ switch (nodeTag(node))
+ {
+ case T_RangeTblRef:
+ {
+ RangeTblRef *rtr = (RangeTblRef *) node;
+
+ RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
+
+ if (rte->rtekind == RTE_RELATION)
+ swc->selist =
+ addEvalRelationRTE(swc->selist, rte, DB_TABLE__SELECT);
+ break;
+ }
+ case T_JoinExpr:
+ {
+ JoinExpr *j = (JoinExpr *) node;
+
+ checkSelectFromExpr(swc, query, j->larg);
+ checkSelectFromExpr(swc, query, j->rarg);
+ break;
+ }
+ case T_FromExpr:
+ {
+ FromExpr *f = (FromExpr *) node;
+ ListCell *l;
+
+ foreach(l, f->fromlist)
+ checkSelectFromExpr(swc, query, lfirst(l));
+ break;
+ }
+ default:
+ elog(ERROR, "SELinux: unexpected node type (%d) on fromlist",
+ nodeTag(node));
+ }
+}
+
+/*
+ * proxyJoinTree
+ *
+ * It appends SEvalItem of WHERE/JOIN ON clause, nodes in VALUE
+ * clause or function which returns a relation, or invokes
+ * proxyRteSubQuery recursively.
+ */
+static void
+proxyJoinTree(sepgsqlWalkerContext *swc, Node *node)
+{
+ Query *query = swc->qstack->query;
+
+ if (node == NULL)
+ return;
+
+ switch (nodeTag(node))
+ {
+ case T_RangeTblRef:
+ {
+ RangeTblRef *rtr = (RangeTblRef *) node;
+ RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
+
+ Assert(IsA(rte, RangeTblEntry));
+
+ switch (rte->rtekind)
+ {
+ case RTE_SUBQUERY:
+ proxyRteSubQuery(swc, rte->subquery);
+ break;
+
+ case RTE_FUNCTION:
+ sepgsqlExprWalkerFlags(rte->funcexpr, swc, false);
+ break;
+
+ case RTE_VALUES:
+ sepgsqlExprWalkerFlags((Node *) rte->values_lists, swc,
+ false);
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+ case T_FromExpr:
+ {
+ FromExpr *f = (FromExpr *) node;
+ ListCell *l;
+
+ sepgsqlExprWalkerFlags(f->quals, swc, true);
+ foreach(l, f->fromlist) proxyJoinTree(swc, lfirst(l));
+ break;
+ }
+ case T_JoinExpr:
+ {
+ JoinExpr *j = (JoinExpr *) node;
+
+ sepgsqlExprWalkerFlags(j->quals, swc, true);
+ proxyJoinTree(swc, j->larg);
+ proxyJoinTree(swc, j->rarg);
+
+ break;
+ }
+ default:
+ elog(ERROR, "SELinux: unexpected node type (%d) at jointree",
+ nodeTag(node));
+ break;
+ }
+}
+
+/*
+ * proxySetOperations
+ *
+ * It walks on a query tree recursively when set operations
+ * (UNION, INTERSECT, EXCEPT) are used.
+ *
+ */
+static void
+proxySetOperations(sepgsqlWalkerContext *swc, Node *node)
+{
+ Query *query = swc->qstack->query;
+
+ if (node == NULL)
+ return;
+
+ switch (nodeTag(node))
+ {
+ case T_RangeTblRef:
+ {
+ RangeTblRef *rtr = (RangeTblRef *) node;
+ RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
+
+ Assert(IsA(rte, RangeTblEntry)
+ && rte->rtekind == RTE_SUBQUERY);
+ proxyRteSubQuery(swc, rte->subquery);
+
+ break;
+ }
+ case T_SetOperationStmt:
+ {
+ SetOperationStmt *sop = (SetOperationStmt *) node;
+
+ proxySetOperations(swc, sop->larg);
+ proxySetOperations(swc, sop->rarg);
+ break;
+ }
+ default:
+ elog(ERROR, "SELinux enexpected node (%d) in setOperations tree",
+ nodeTag(node));
+ break;
+ }
+}
+
+/*
+ * proxyRteSubQuery
+ *
+ * It walks on the given DML Query to enumerate all appeared tables,
+ * columns and functions which include implementations of operator.
+ * While its walking, it generates a list of SEvalItemXXXX object
+ * to be evaluated later, and marks required permission on
+ * RangeTblEntry->pgaceTuplePerms. The swc->selist is copied to
+ * PlannedStmt->pgaceItem and evaluated on the hook invoked from
+ * the executor. RangeTblEntry->pgaceTuplePerms is copied to
+ * Scan->pgaceTuplePerms and it can be refered at sepgsqlExecScan()
+ * hook to apply tuple-level access controls.
+ */
+static void
+proxyRteSubQuery(sepgsqlWalkerContext *swc, Query *query)
+{
+ CmdType cmdType = query->commandType;
+ RangeTblEntry *rte = NULL;
+ struct queryStack qsData;
+ ListCell *l;
+
+ /*
+ * push a query to queryStack
+ */
+ qsData.parent = swc->qstack;
+ qsData.query = query;
+ swc->qstack = &qsData;
+
+ switch (cmdType)
+ {
+ case CMD_SELECT:
+ /*
+ * add db_table:{select} for any relation in FROM clause
+ */
+ checkSelectFromExpr(swc, query, (Node *) query->jointree);
+
+ case CMD_UPDATE:
+ case CMD_INSERT:
+ foreach(l, query->targetList)
+ {
+ TargetEntry *tle = lfirst(l);
+ bool is_security_attr = false;
+
+ Assert(IsA(tle, TargetEntry));
+
+ if (tle->resjunk && tle->resname &&
+ strcmp(tle->resname, SECURITY_SYSATTR_NAME) == 0)
+ is_security_attr = true;
+
+ /*
+ * Result set of junk target entries are not shown
+ * to users, so it is evaluated with "use" permission.
+ */
+ if (tle->resjunk && !is_security_attr)
+ {
+ sepgsqlExprWalkerFlags((Node *) tle->expr, swc, true);
+ continue;
+ }
+
+ sepgsqlExprWalkerFlags((Node *) tle->expr, swc, false);
+
+ if (cmdType == CMD_UPDATE || cmdType == CMD_INSERT)
+ {
+ /*
+ * Add SEvalItem for the target of INSERT/UPDATE
+ */
+ AttrNumber attno
+ = is_security_attr
+ ? SecurityAttributeNumber : tle->resno;
+ uint32 perms
+ = cmdType == CMD_UPDATE
+ ? DB_COLUMN__UPDATE : DB_COLUMN__INSERT;
+
+ rte = rt_fetch(query->resultRelation, query->rtable);
+ Assert(IsA(rte, RangeTblEntry)
+ && rte->rtekind == RTE_RELATION);
+
+ swc->selist
+ = addEvalAttributeRTE(swc->selist, rte, attno, perms);
+ }
+ }
+ break;
+
+ case CMD_DELETE:
+ /*
+ * NOTE:
+ * column level checks are not applied on DELETE.
+ */
+ rte = rt_fetch(query->resultRelation, query->rtable);
+ Assert(IsA(rte, RangeTblEntry) && rte->rtekind == RTE_RELATION);
+
+ swc->selist = addEvalRelationRTE(swc->selist, rte,
+ DB_TABLE__DELETE);
+ break;
+
+ default:
+ elog(ERROR, "SELinux: unexpected cmdType = %d", cmdType);
+ break;
+ }
+
+ /*
+ * RETURNING clause requires "select" permission
+ */
+ foreach(l, query->returningList)
+ {
+ TargetEntry *te = lfirst(l);
+
+ Assert(IsA(te, TargetEntry));
+
+ sepgsqlExprWalkerFlags((Node *) te->expr, swc, false);
+ }
+ /*
+ * WHERE/JOIN ... ON/HAVING/ORDER BY/GROUP BY ... clause
+ * to apply "use" permission
+ */
+ proxyJoinTree(swc, (Node *) query->jointree);
+ sepgsqlExprWalkerFlags(query->havingQual, swc, true);
+ sepgsqlExprWalkerFlags((Node *) query->sortClause, swc, true);
+ sepgsqlExprWalkerFlags((Node *) query->groupClause, swc, true);
+
+ /*
+ * permission mark on the UNION/INTERSECT/EXCEPT
+ */
+ proxySetOperations(swc, query->setOperations);
+
+ /*
+ * pop a query to queryStack
+ */
+ swc->qstack = qsData.parent;
+}
+
+/*
+ * sepgsqlProxyQuery
+ *
+ * This function is invoked just after the given queries rewritten
+ * by the query rewriter. It invokes proxyRteSubQuery() for any
+ * DML queries to pick up all appeared database object and stores
+ * the list of them into Query->pgaceItem to evaluate later.
+ *
+ * It does not do anything for DDL queries because it is processed
+ * on sepgsqlProcessUtility() hook.
+ */
+List *
+sepgsqlProxyQuery(List *queryList)
+{
+ List *newList = NIL;
+ ListCell *l;
+
+ foreach (l, queryList)
+ {
+ Query *query = (Query *) lfirst(l);
+
+ Assert(IsA(query, Query));
+
+ switch (query->commandType)
+ {
+ case CMD_SELECT:
+ case CMD_UPDATE:
+ case CMD_INSERT:
+ case CMD_DELETE:
+ {
+ sepgsqlWalkerContext swcData;
+
+ memset(&swcData, 0, sizeof(swcData));
+
+ proxyRteSubQuery(&swcData, query);
+ query->pgaceItem = (Node *) swcData.selist;
+
+ newList = lappend(newList, query);
+ }
+ break;
+ default:
+ newList = lappend(newList, query);
+ break;
+ }
+ }
+
+ return newList;
+}
+
+/*
+ * sepgsqlEvaluateParams
+ *
+ * It checks permissions to execute functions just before
+ * parameter list is generated.
+ */
+void
+sepgsqlEvaluateParams(List *params)
+{
+ sepgsqlWalkerContext swcData;
+
+ queryStack qsData;
+
+ memset(&qsData, 0, sizeof(queryStack));
+ memset(&swcData, 0, sizeof(sepgsqlWalkerContext));
+ swcData.qstack = &qsData;
+
+ sepgsqlExprWalkerFlags((Node *) params, &swcData, false);
+
+ execVerifyQuery(swcData.selist);
+}
+
+/*
+ * verityXXXX()
+ *
+ * These functions are invoked from execVerifyQuery, to evaluate
+ * SEvalItemXXXX objects generated at sepgsqlProxyQuery().
+ */
+
+/*
+ * verifyPgClassPerms
+ *
+ * It evaluates SEvalItemRelation object to access tables.
+ */
+static void
+verifyPgClassPerms(Oid relid, bool inh, uint32 perms)
+{
+ HeapTuple tuple;
+
+ /*
+ * prevent to modify pg_security directly
+ */
+ if (relid == SecurityRelationId
+ && (perms & (DB_TABLE__UPDATE | DB_TABLE__INSERT | DB_TABLE__DELETE)))
+ ereport(ERROR,
+ (errcode(ERRCODE_SELINUX_ERROR),
+ errmsg("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: cache lookup failed for relation: %u", relid);
+
+ if (((Form_pg_class) GETSTRUCT(tuple))->relkind == RELKIND_RELATION)
+ {
+ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_TABLE,
+ (access_vector_t) perms,
+ sepgsqlTupleName(RelationRelationId, tuple),
+ true);
+ }
+ ReleaseSysCache(tuple);
+}
+
+/*
+ * verifyPgAttributePerms
+ *
+ * It evaluates SEvalItemAttribute to access columns.
+ */
+static void
+verifyPgAttributePerms(Oid relid, bool inh, AttrNumber attno, uint32 perms)
+{
+ Form_pg_class clsForm;
+ HeapTuple tuple;
+
+ tuple = SearchSysCache(RELOID, ObjectIdGetDatum(relid), 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "SELinux: cache lookup failed for relation: %u", relid);
+
+ clsForm = (Form_pg_class) GETSTRUCT(tuple);
+ if (clsForm->relkind != RELKIND_RELATION)
+ {
+ 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)
+ {
+ Form_pg_attribute attForm = (Form_pg_attribute) GETSTRUCT(tuple);
+
+ if (attForm->attisdropped || attForm->attnum < 1)
+ continue;
+
+ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_COLUMN,
+ perms,
+ sepgsqlTupleName(AttributeRelationId, tuple),
+ true);
+ }
+ systable_endscan(scan);
+ heap_close(rel, AccessShareLock);
+
+ return;
+ }
+ /*
+ * check required column's permission
+ */
+ 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);
+
+ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_COLUMN,
+ perms,
+ sepgsqlTupleName(AttributeRelationId, tuple),
+ true);
+ ReleaseSysCache(tuple);
+}
+
+/*
+ * verifyPgProcedurePerms
+ *
+ * It evaluates SEvalItemProcedure object to access tables.
+ */
+static void
+verifyPgProcPerms(Oid funcid, uint32 perms)
+{
+ HeapTuple tuple;
+ security_context_t tcon, ncon;
+
+ tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(funcid), 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "SELinux: cache lookup failed for procedure %d", funcid);
+ /*
+ * check domain transition
+ */
+ tcon = pgaceLookupSecurityLabel(HeapTupleGetSecurity(tuple));
+
+ ncon = sepgsqlAvcCreateCon(sepgsqlGetClientContext(),
+ tcon,
+ SECCLASS_PROCESS);
+ if (strcmp(sepgsqlGetClientContext(), ncon))
+ {
+ perms |= DB_PROCEDURE__ENTRYPOINT;
+
+ sepgsqlAvcPermission(sepgsqlGetClientContext(),
+ ncon,
+ SECCLASS_PROCESS,
+ PROCESS__TRANSITION,
+ NULL, true);
+ }
+ pfree(ncon);
+ pfree(tcon);
+
+ /*
+ * check procedure executiong permission
+ */
+ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_PROCEDURE,
+ perms,
+ sepgsqlTupleName(ProcedureRelationId, tuple),
+ true);
+
+ ReleaseSysCache(tuple);
+}
+
+/*
+ * expandSEvalItemInheritance
+ *
+ * When a request to table/column is inheritable, we have to expand
+ * the target to child relations, because accessing a column within
+ * parent table also means accessing a column within child relation
+ * in same time.
+ *
+ * For example, when t2 and t3 inherits t1, we have to check permission
+ * on t2.x and t3.x for the request to t1.x.
+ * It is impossible to be done before, because we have a chance to
+ * change inheritance relationships between PREPARE and EXECUTE.
+ * So, we have to check it in execution phase.
+ */
+static List *
+expandRelationInheritance(List *selist, Oid relid, uint32 perms)
+{
+ List *inherits = find_all_inheritors(relid);
+ ListCell *l;
+
+ foreach(l, inherits)
+ selist = addEvalRelation(selist, lfirst_oid(l), false, perms);
+
+ return selist;
+}
+
+static List *
+expandAttributeInheritance(List *selist, Oid relid, char *attname,
+ uint32 perms)
+{
+ List *inherits = find_all_inheritors(relid);
+ ListCell *l;
+
+ foreach(l, inherits)
+ {
+ Form_pg_attribute attr;
+
+ HeapTuple tuple;
+
+ if (!attname)
+ {
+ selist = addEvalAttribute(selist, lfirst_oid(l), false, 0, 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));
+
+ attr = (Form_pg_attribute) GETSTRUCT(tuple);
+ selist = addEvalAttribute(selist, lfirst_oid(l), false,
+ attr->attnum, perms);
+
+ ReleaseSysCache(tuple);
+ }
+
+ return selist;
+}
+
+static List *
+expandSEvalItemInheritance(List *selist)
+{
+ SEvalItemRelation *ser;
+ SEvalItemAttribute *sea;
+ List *result = NIL;
+ ListCell *l;
+
+ foreach(l, selist)
+ {
+ Node *node = lfirst(l);
+
+ result = lappend(result, node);
+ switch (nodeTag(node))
+ {
+ case T_SEvalItemRelation:
+ ser = (SEvalItemRelation *) node;
+ if (ser->inh)
+ {
+ ser->inh = false;
+ result = expandRelationInheritance(result,
+ ser->relid, ser->perms);
+ }
+ break;
+
+ case T_SEvalItemAttribute:
+ sea = (SEvalItemAttribute *) node;
+ if (sea->inh)
+ {
+ Form_pg_attribute attr;
+ HeapTuple tuple;
+
+ sea->inh = false;
+ if (sea->attno == 0)
+ {
+ result = expandAttributeInheritance(result,
+ sea->relid,
+ NULL, sea->perms);
+ break;
+ }
+
+ tuple = SearchSysCache(ATTNUM,
+ ObjectIdGetDatum(sea->relid),
+ Int16GetDatum(sea->attno), 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR,
+ "SELinux: cache lookup failed for attribute %d of relation %u",
+ sea->attno, sea->relid);
+ attr = (Form_pg_attribute) GETSTRUCT(tuple);
+
+ result = expandAttributeInheritance(result,
+ sea->relid,
+ NameStr(attr->attname),
+ sea->perms);
+ ReleaseSysCache(tuple);
+ }
+ break;
+
+ case T_SEvalItemProcedure:
+ /*
+ * do nothing
+ */
+ break;
+
+ default:
+ elog(ERROR, "SELinux: Invalid node type (%d) in SEvalItemList",
+ nodeTag(node));
+ break;
+ }
+ }
+ return result;
+}
+
+/*
+ * execVerifyQuery
+ *
+ * This function scans the given list, and invokes proper function
+ * to evaluate it.
+ */
+static void
+execVerifyQuery(List *selist)
+{
+ SEvalItemRelation *ser;
+ SEvalItemAttribute *sea;
+ SEvalItemProcedure *sep;
+ ListCell *l;
+
+ foreach(l, selist)
+ {
+ Node *node = lfirst(l);
+
+ switch (nodeTag(node))
+ {
+ case T_SEvalItemRelation:
+ ser = (SEvalItemRelation *) node;
+ verifyPgClassPerms(ser->relid, ser->inh, ser->perms);
+ break;
+
+ case T_SEvalItemAttribute:
+ sea = (SEvalItemAttribute *) node;
+ verifyPgAttributePerms(sea->relid, sea->inh, sea->attno,
+ sea->perms);
+ break;
+
+ case T_SEvalItemProcedure:
+ sep = (SEvalItemProcedure *) node;
+ verifyPgProcPerms(sep->funcid, sep->perms);
+ break;
+
+ default:
+ elog(ERROR, "SELinux: Invalid node type (%d) in SEvalItemList",
+ nodeTag(node));
+ break;
+ }
+ }
+}
+
+/*
+ * sepgsqlVerifyQuery
+ *
+ * This function is invoked at the head of ExecutorStart, to evaluate
+ * permissions to access appeared object within the given query.
+ * Query->pgaceItem is a list of SEvalItemXXXX objects generated in
+ * previous phase, and it is copied to PlannedStmt->pgaceItem in the
+ * optimizer.
+ * sepgsqlVerifyQuery expand relations/columns and append permissions
+ * to execute trigger functions, if necessary.
+ */
+void
+sepgsqlVerifyQuery(PlannedStmt *pstmt, int eflags)
+{
+ RangeTblEntry *rte;
+ List *selist;
+ ListCell *l;
+
+ /*
+ * EXPLAIN statement does not access any object.
+ */
+ if ((eflags & EXEC_FLAG_EXPLAIN_ONLY) != 0)
+ return;
+ if (!pstmt->pgaceItem)
+ return;
+
+ Assert(IsA(pstmt->pgaceItem, List));
+ selist = copyObject(pstmt->pgaceItem);
+
+ /*
+ * expand table inheritances
+ */
+ selist = expandSEvalItemInheritance(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);
+}
+
+/*
+ * --------------------------------------------------------------
+ * Process Utility hooks
+ * --------------------------------------------------------------
+ */
+
+/*
+ * checkTruncateStmt
+ *
+ * This function checks permissions of tuples within the given
+ * tables before TRUNCATE them. Because its meanings are same
+ * as unconditional DELETE logically, SE-PostgreSQL attempt to
+ * apply same permission for them operation.
+ * If there is a violated tuple at most, it stops to execute
+ * TRUNCATE and abort current trunsaction.
+ */
+static void
+checkTruncateStmt(TruncateStmt *stmt)
+{
+ Relation rel;
+ HeapScanDesc scan;
+ HeapTuple tuple;
+ List *relidList = NIL;
+ ListCell *l;
+
+ foreach(l, stmt->relations)
+ {
+ RangeVar *rv = lfirst(l);
+
+ relidList = lappend_oid(relidList, RangeVarGetRelid(rv, false));
+ }
+
+ if (stmt->behavior == DROP_CASCADE)
+ {
+ relidList = list_concat(relidList, heap_truncate_find_FKs(relidList));
+ }
+
+ foreach(l, relidList)
+ {
+ Oid relid = lfirst_oid(l);
+
+ /*
+ * 1. db_table:{delete}
+ */
+ tuple = SearchSysCache(RELOID, ObjectIdGetDatum(relid), 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "SELinux: cache lookup failed for relation %u", relid);
+ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_TABLE,
+ DB_TABLE__DELETE,
+ sepgsqlTupleName(RelationRelationId, tuple),
+ true);
+ ReleaseSysCache(tuple);
+
+ /*
+ * 2. db_tuple:{delete}
+ */
+ rel = heap_open(relid, AccessShareLock);
+ scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+
+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
+ {
+ sepgsqlAvcPermissionSid(sepgsqlGetClientContext(),
+ HeapTupleGetSecurity(tuple),
+ SECCLASS_DB_TUPLE,
+ DB_TUPLE__DELETE,
+ sepgsqlTupleName(RelationRelationId, tuple),
+ true);
+ }
+ heap_endscan(scan);
+ heap_close(rel, AccessShareLock);
+ }
+}
+
+/*
+ * sepgsqlProcessUtility
+ *
+ * This function is invoked from the head of ProcessUtility(), and
+ * checks given DDL queries.
+ * SE-PostgreSQL catch most of DDL actions on HeapTuple hooks, but
+ * an exception is TRUNCATE statement.
+ */
+void
+sepgsqlProcessUtility(Node *parsetree, ParamListInfo params, bool isTopLevel)
+{
+ switch (nodeTag(parsetree))
+ {
+ case T_TruncateStmt:
+ checkTruncateStmt((TruncateStmt *) parsetree);
+ break;
+ default:
+ /*
+ * do nothing
+ */
+ break;
+ }
+}
+
+/* ----------------------------------------------------------
+ * COPY TO/COPY FROM statement hooks
+ * ---------------------------------------------------------- */
+
+/*
+ * sepgsqlCopyTable
+ *
+ * This function checks permission on the target table and columns
+ * of COPY statement. We don't place it at sepgsql/hooks.c because
+ * it internally uses addEvalXXXX() interface statically declared.
+ */
+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 = addEvalRelation(selist, RelationGetRelid(rel), false,
+ isFrom ? DB_TABLE__INSERT : DB_TABLE__SELECT);
+ foreach(l, attNumList)
+ {
+ AttrNumber attnum = lfirst_int(l);
+
+ selist = addEvalAttribute(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);
+}
+
+/*
+ * sepgsqlCopyToTuple
+ *
+ * This function check permission to read the given tuple.
+ * If not allowed to read, it returns false to skip COPY TO
+ * this tuple. In the result, any violated tuples are filtered
+ * from the result of COPY TO, as if these are not exist.
+ */
+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);
+}
diff -rpNU3 base/src/backend/storage/file/fd.c sepgsql/src/backend/storage/file/fd.c
--- base/src/backend/storage/file/fd.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/storage/file/fd.c 2008-06-14 02:36:58.000000000 +0900
@@ -1241,6 +1241,13 @@ FileTruncate(File file, long offset)
return returnCode;
}
+int
+FileRawDescriptor(File file)
+{
+ Assert(FileIsValid(file));
+
+ return VfdCache[file].fd;
+}
/*
* Routines that want to use stdio (ie, FILE*) should use AllocateFile
diff -rpNU3 base/src/backend/storage/ipc/ipci.c sepgsql/src/backend/storage/ipc/ipci.c
--- base/src/backend/storage/ipc/ipci.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/storage/ipc/ipci.c 2008-06-14 02:36:58.000000000 +0900
@@ -25,6 +25,7 @@
#include "postmaster/autovacuum.h"
#include "postmaster/bgwriter.h"
#include "postmaster/postmaster.h"
+#include "security/pgace.h"
#include "storage/freespace.h"
#include "storage/ipc.h"
#include "storage/pg_shmem.h"
@@ -117,6 +118,7 @@ CreateSharedMemoryAndSemaphores(bool mak
#ifdef EXEC_BACKEND
size = add_size(size, ShmemBackendArraySize());
#endif
+ size = add_size(size, pgaceShmemSize());
/* freeze the addin request size and include it */
addin_request_allowed = false;
diff -rpNU3 base/src/backend/tcop/fastpath.c sepgsql/src/backend/tcop/fastpath.c
--- base/src/backend/tcop/fastpath.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/tcop/fastpath.c 2008-06-14 02:36:58.000000000 +0900
@@ -26,6 +26,7 @@
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
+#include "security/pgace.h"
#include "tcop/fastpath.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
@@ -353,6 +354,8 @@ HandleFunctionRequest(StringInfo msgBuf)
*/
InitFunctionCallInfoData(fcinfo, &fip->flinfo, 0, NULL, NULL);
+ pgaceCallFunctionFastPath(fcinfo.flinfo);
+
if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3)
rformat = parse_fcall_arguments(msgBuf, fip, &fcinfo);
else
diff -rpNU3 base/src/backend/tcop/postgres.c sepgsql/src/backend/tcop/postgres.c
--- base/src/backend/tcop/postgres.c 2008-06-12 22:34:19.000000000 +0900
+++ sepgsql/src/backend/tcop/postgres.c 2008-06-14 02:36:58.000000000 +0900
@@ -53,6 +53,7 @@
#include "parser/parser.h"
#include "postmaster/autovacuum.h"
#include "rewrite/rewriteHandler.h"
+#include "security/pgace.h"
#include "storage/freespace.h"
#include "storage/ipc.h"
#include "storage/proc.h"
@@ -629,6 +630,9 @@ pg_rewrite_query(Query *query)
{
/* don't rewrite utilities, just dump 'em into result list */
querytree_list = list_make1(query);
+
+ /* PGACE rewrite utility query, if necessary */
+ querytree_list = pgaceProxyQuery(querytree_list);
}
else
{
diff -rpNU3 base/src/backend/tcop/utility.c sepgsql/src/backend/tcop/utility.c
--- base/src/backend/tcop/utility.c 2008-03-19 09:48:23.000000000 +0900
+++ sepgsql/src/backend/tcop/utility.c 2008-06-14 02:36:58.000000000 +0900
@@ -49,6 +49,7 @@
#include "postmaster/bgwriter.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteRemove.h"
+#include "security/pgace.h"
#include "storage/fd.h"
#include "tcop/pquery.h"
#include "tcop/utility.h"
@@ -397,6 +398,8 @@ ProcessUtility(Node *parsetree,
if (completionTag)
completionTag[0] = '\0';
+ pgaceProcessUtility(parsetree, params, isTopLevel);
+
switch (nodeTag(parsetree))
{
/*
diff -rpNU3 base/src/backend/utils/adt/ri_triggers.c sepgsql/src/backend/utils/adt/ri_triggers.c
--- base/src/backend/utils/adt/ri_triggers.c 2008-06-12 22:34:19.000000000 +0900
+++ sepgsql/src/backend/utils/adt/ri_triggers.c 2008-09-22 17:51:21.000000000 +0900
@@ -37,6 +37,7 @@
#include "parser/parse_coerce.h"
#include "parser/parse_relation.h"
#include "miscadmin.h"
+#include "security/pgace.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
@@ -3256,6 +3257,7 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPl
int spi_result;
Oid save_userid;
bool save_secdefcxt;
+ Datum save_pgace;
Datum vals[RI_MAX_NUMKEYS * 2];
char nulls[RI_MAX_NUMKEYS * 2];
@@ -3336,11 +3338,22 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPl
GetUserIdAndContext(&save_userid, &save_secdefcxt);
SetUserIdAndContext(RelationGetForm(query_rel)->relowner, true);
- /* Finally we can run the query. */
- spi_result = SPI_execute_snapshot(qplan,
- vals, nulls,
- test_snapshot, crosscheck_snapshot,
- false, false, limit);
+ pgaceBeginPerformCheckFK(query_rel, query_rel == pk_rel, &save_pgace);
+ PG_TRY();
+ {
+ /* Finally we can run the query. */
+ spi_result = SPI_execute_snapshot(qplan,
+ vals, nulls,
+ test_snapshot, crosscheck_snapshot,
+ false, false, limit);
+ }
+ PG_CATCH();
+ {
+ pgaceEndPerformCheckFK(query_rel, query_rel == pk_rel, save_pgace);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ pgaceEndPerformCheckFK(query_rel, query_rel == pk_rel, save_pgace);
/* Restore UID */
SetUserIdAndContext(save_userid, save_secdefcxt);
diff -rpNU3 base/src/backend/utils/cache/catcache.c sepgsql/src/backend/utils/cache/catcache.c
--- base/src/backend/utils/cache/catcache.c 2008-03-19 09:48:23.000000000 +0900
+++ sepgsql/src/backend/utils/cache/catcache.c 2008-06-14 02:36:58.000000000 +0900
@@ -1313,6 +1313,38 @@ ReleaseCatCache(HeapTuple tuple)
CatCacheRemoveCTup(ct->my_cache, ct);
}
+/*
+ * InsertCatCache
+ *
+ * This function enables to refer a tuple recently inserted, using catcache
+ * until next CommandCounterIncrement.
+ */
+void InsertCatCache(CatCache *cache, HeapTuple tuple)
+{
+ ScanKeyData skey[4];
+ uint32 hashValue;
+ Index hashIndex;
+ bool isnull;
+ int i;
+
+ /* initialize the search key information */
+ memcpy(skey, cache->cc_skey, sizeof(skey));
+ for (i=0; i < cache->cc_nkeys; i++)
+ {
+ skey[i].sk_argument = heap_getattr(tuple, cache->cc_key[i],
+ cache->cc_tupdesc, &isnull);
+ Assert(!isnull);
+ }
+
+ /* find the hash bucket in which to look for the tuple */
+ if (cache->cc_tupdesc == NULL)
+ CatalogCacheInitializeCache(cache);
+ hashValue = CatalogCacheComputeHashValue(cache, cache->cc_nkeys, skey);
+ hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
+
+ /* Insert a new tuple */
+ CatalogCacheCreateEntry(cache, tuple, hashValue, hashIndex, false);
+}
/*
* SearchCatCacheList
diff -rpNU3 base/src/backend/utils/cache/syscache.c sepgsql/src/backend/utils/cache/syscache.c
--- base/src/backend/utils/cache/syscache.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/utils/cache/syscache.c 2008-06-14 02:36:58.000000000 +0900
@@ -39,6 +39,7 @@
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_rewrite.h"
+#include "catalog/pg_security.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_config_map.h"
@@ -676,7 +677,31 @@ static const struct cachedesc cacheinfo[
0
},
1024
- }
+ },
+ {SecurityRelationId, /*SECURITYOID */
+ SecurityOidIndexId,
+ 0,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 128
+ },
+ {SecurityRelationId, /* SECURITYLABEL */
+ SecuritySeclabelIndexId,
+ 0,
+ 1,
+ {
+ Anum_pg_security_seclabel,
+ 0,
+ 0,
+ 0
+ },
+ 128
+ },
};
static CatCache *SysCache[
@@ -784,6 +809,21 @@ ReleaseSysCache(HeapTuple tuple)
}
/*
+ * InsertSysCache
+ * interts a tuple temporary until next CommandCounterIncrement
+ */
+void InsertSysCache(Oid relid, HeapTuple tuple)
+{
+ int cacheId;
+
+ for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
+ {
+ if (SysCache[cacheId]->cc_reloid == relid)
+ InsertCatCache(SysCache[cacheId], tuple);
+ }
+}
+
+/*
* SearchSysCacheCopy
*
* A convenience routine that does SearchSysCache and (if successful)
diff -rpNU3 base/src/backend/utils/fmgr/dfmgr.c sepgsql/src/backend/utils/fmgr/dfmgr.c
--- base/src/backend/utils/fmgr/dfmgr.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/utils/fmgr/dfmgr.c 2008-06-26 10:27:05.000000000 +0900
@@ -22,6 +22,7 @@
#include "port/dynloader/win32.h"
#endif
#include "miscadmin.h"
+#include "security/pgace.h"
#include "utils/dynamic_loader.h"
#include "utils/hsearch.h"
@@ -73,7 +74,6 @@ char *Dynamic_library_path;
static void *internal_load_library(const char *libname);
static void internal_unload_library(const char *libname);
static bool file_exists(const char *name);
-static char *expand_dynamic_library_name(const char *name);
static void check_restricted_library_name(const char *name);
static char *substitute_libpath_macro(const char *name);
static char *find_in_dynamic_libpath(const char *basename);
@@ -106,6 +106,9 @@ load_external_function(char *filename, c
/* Expand the possibly-abbreviated filename to an exact path name */
fullname = expand_dynamic_library_name(filename);
+ /* Check whether the shared library should be loaded, or not */
+ pgaceLoadSharedModule(fullname);
+
/* Load the shared library, unless we already did */
lib_handle = internal_load_library(fullname);
@@ -146,6 +149,9 @@ load_file(const char *filename, bool res
/* Expand the possibly-abbreviated filename to an exact path name */
fullname = expand_dynamic_library_name(filename);
+ /* Check whether the library should be loaded, or not */
+ pgaceLoadSharedModule(fullname);
+
/* Unload the library if currently loaded */
internal_unload_library(fullname);
@@ -395,7 +401,7 @@ file_exists(const char *name)
*
* The result will always be freshly palloc'd.
*/
-static char *
+char *
expand_dynamic_library_name(const char *name)
{
bool have_slash;
diff -rpNU3 base/src/backend/utils/init/postinit.c sepgsql/src/backend/utils/init/postinit.c
--- base/src/backend/utils/init/postinit.c 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/backend/utils/init/postinit.c 2008-06-26 10:27:05.000000000 +0900
@@ -31,6 +31,7 @@
#include "pgstat.h"
#include "postmaster/autovacuum.h"
#include "postmaster/postmaster.h"
+#include "security/pgace.h"
#include "storage/backendid.h"
#include "storage/fd.h"
#include "storage/ipc.h"
@@ -601,6 +602,9 @@ InitPostgres(const char *in_dbname, Oid
if (!bootstrap)
pgstat_bestart();
+ /* initialize mandatory access control facilities */
+ pgaceInitialize(bootstrap);
+
/* close the transaction we started above */
if (!bootstrap)
CommitTransactionCommand();
diff -rpNU3 base/src/backend/utils/misc/guc.c sepgsql/src/backend/utils/misc/guc.c
--- base/src/backend/utils/misc/guc.c 2008-06-12 22:34:19.000000000 +0900
+++ sepgsql/src/backend/utils/misc/guc.c 2008-07-11 14:10:51.000000000 +0900
@@ -54,6 +54,7 @@
#include "postmaster/postmaster.h"
#include "postmaster/syslogger.h"
#include "postmaster/walwriter.h"
+#include "security/pgace.h"
#include "storage/fd.h"
#include "storage/freespace.h"
#include "tcop/tcopprot.h"
@@ -268,11 +269,12 @@ static int max_index_keys;
static int max_identifier_length;
static int block_size;
static bool integer_datetimes;
+static char *pgace_security_feature;
/* should be static, but commands/variable.c needs to get at these */
char *role_string;
char *session_authorization_string;
-
+char *sepostgresql_mode;
/*
* Displayable names for context types (enum GucContext)
@@ -2460,6 +2462,27 @@ static struct config_string ConfigureNam
},
#endif /* USE_SSL */
+ {
+ {"pgace_security_feature", PGC_INTERNAL, PRESET_OPTIONS,
+ gettext_noop("Shows the guest of PGACE security framework"),
+ NULL,
+ GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
+ },
+ &pgace_security_feature,
+ NULL, NULL, pgaceShowSecurityFeature,
+ },
+#ifdef HAVE_SELINUX
+ {
+ {"sepostgresql", PGC_POSTMASTER, PRESET_OPTIONS,
+ gettext_noop("SE-PostgreSQL working mode"),
+ gettext_noop("Valid values are DEFAULT, PERMISSIVE, ENFORCING, DISABLED"),
+ 0,
+ },
+ &sepostgresql_mode,
+ "default", NULL, NULL,
+ },
+#endif
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL
@@ -3300,6 +3323,8 @@ ResetAllOptions(void)
{
int i;
+ pgaceSetDatabaseParam("all", NULL);
+
for (i = 0; i < num_guc_variables; i++)
{
struct config_generic *gconf = guc_variables[i];
@@ -4972,6 +4997,7 @@ ExecSetVariableStmt(VariableSetStmt *stm
{
case VAR_SET_VALUE:
case VAR_SET_CURRENT:
+ pgaceSetDatabaseParam(stmt->name, ExtractSetVariableArgs(stmt));
set_config_option(stmt->name,
ExtractSetVariableArgs(stmt),
(superuser() ? PGC_SUSET : PGC_USERSET),
@@ -5029,6 +5055,7 @@ ExecSetVariableStmt(VariableSetStmt *stm
break;
case VAR_SET_DEFAULT:
case VAR_RESET:
+ pgaceSetDatabaseParam(stmt->name, NULL);
set_config_option(stmt->name,
NULL,
(superuser() ? PGC_SUSET : PGC_USERSET),
@@ -5357,6 +5384,9 @@ EmitWarningsOnPlaceholders(const char *c
void
GetPGVariable(const char *name, DestReceiver *dest)
{
+ /* Check get param permissions */
+ pgaceGetDatabaseParam(name);
+
if (guc_name_compare(name, "all") == 0)
ShowAllGUCConfig(dest);
else
diff -rpNU3 base/src/include/access/htup.h sepgsql/src/include/access/htup.h
--- base/src/include/access/htup.h 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/include/access/htup.h 2008-06-14 02:36:58.000000000 +0900
@@ -161,7 +161,7 @@ typedef HeapTupleHeaderData *HeapTupleHe
#define HEAP_HASVARWIDTH 0x0002 /* has variable-width attribute(s) */
#define HEAP_HASEXTERNAL 0x0004 /* has external stored attribute(s) */
#define HEAP_HASOID 0x0008 /* has an object-id field */
-/* bit 0x0010 is available */
+#define HEAP_HASSECURITY 0x0010 /* has an security attribute field */
#define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */
#define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */
#define HEAP_XMAX_SHARED_LOCK 0x0080 /* xmax is shared locker */
@@ -347,6 +347,34 @@ do { \
(tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \
)
+#define HeapTupleHeaderHasSecurity(tup) \
+ ((tup)->t_infomask & HEAP_HASSECURITY)
+
+#define HeapTupleHeaderGetSecurity(tup) \
+ ( \
+ HeapTupleHeaderHasSecurity(tup) \
+ ? (*((Oid *)((char *)(tup) + (tup)->t_hoff \
+ - (((tup)->t_infomask & HEAP_HASOID) ? sizeof(Oid) : 0) \
+ - sizeof(Oid)))) \
+ : InvalidOid \
+ )
+
+#define HeapTupleHeaderSetSecurity(tup, security) \
+ do { \
+ Assert(HeapTupleHeaderHasSecurity(tup)); \
+ *((Oid *)((char *)(tup) + (tup)->t_hoff \
+ - (((tup)->t_infomask & HEAP_HASOID) ? sizeof(Oid) : 0) \
+ - sizeof(Oid))) = (security); \
+ } while(0)
+
+#define HeapTupleHasSecurity(tuple) \
+ HeapTupleHeaderHasSecurity((tuple)->t_data)
+
+#define HeapTupleGetSecurity(tuple) \
+ HeapTupleHeaderGetSecurity((tuple)->t_data)
+
+#define HeapTupleSetSecurity(tuple, security) \
+ HeapTupleHeaderSetSecurity((tuple)->t_data, (security))
/*
* BITMAPLEN(NATTS) -
@@ -402,8 +430,12 @@ do { \
#define MaxTransactionIdAttributeNumber (-5)
#define MaxCommandIdAttributeNumber (-6)
#define TableOidAttributeNumber (-7)
+#ifdef SECURITY_SYSATTR_NAME
+#define SecurityAttributeNumber (-8)
+#define FirstLowInvalidHeapAttributeNumber (-9)
+#else
#define FirstLowInvalidHeapAttributeNumber (-8)
-
+#endif
/*
* MinimalTuple is an alternative representation that is used for transient
diff -rpNU3 base/src/include/catalog/heap.h sepgsql/src/include/catalog/heap.h
--- base/src/include/catalog/heap.h 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/include/catalog/heap.h 2008-06-18 17:25:44.000000000 +0900
@@ -52,7 +52,8 @@ extern Oid heap_create_with_catalog(cons
int oidinhcount,
OnCommitAction oncommit,
Datum reloptions,
- bool allow_system_table_mods);
+ bool allow_system_table_mods,
+ List *pgace_attr_list);
extern void heap_drop_with_catalog(Oid relid);
@@ -65,7 +66,8 @@ extern List *heap_truncate_find_FKs(List
extern void InsertPgClassTuple(Relation pg_class_desc,
Relation new_rel_desc,
Oid new_rel_oid,
- Datum reloptions);
+ Datum reloptions,
+ List *pgace_attr_list);
extern List *AddRelationRawConstraints(Relation rel,
List *rawColDefaults,
@@ -96,6 +98,8 @@ extern Form_pg_attribute SystemAttribute
extern Form_pg_attribute SystemAttributeByName(const char *attname,
bool relhasoids);
+extern bool SystemAttributeIsWritable(AttrNumber attno, bool relhasoids);
+
extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind);
extern void CheckAttributeType(const char *attname, Oid atttypid);
diff -rpNU3 base/src/include/catalog/indexing.h sepgsql/src/include/catalog/indexing.h
--- base/src/include/catalog/indexing.h 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/include/catalog/indexing.h 2008-06-14 02:36:58.000000000 +0900
@@ -252,6 +252,11 @@ DECLARE_UNIQUE_INDEX(pg_type_oid_index,
DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typname name_ops, typnamespace oid_ops));
#define TypeNameNspIndexId 2704
+DECLARE_UNIQUE_INDEX(pg_security_oid_index, 3401, on pg_security using btree(oid oid_ops));
+#define SecurityOidIndexId 3401
+DECLARE_UNIQUE_INDEX(pg_security_seclabel_index, 3402, on pg_security using btree(seclabel text_ops));
+#define SecuritySeclabelIndexId 3402
+
/* last step of initialization script: build the indexes declared above */
BUILD_INDICES
diff -rpNU3 base/src/include/catalog/pg_attribute.h sepgsql/src/include/catalog/pg_attribute.h
--- base/src/include/catalog/pg_attribute.h 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/include/catalog/pg_attribute.h 2008-06-14 02:36:58.000000000 +0900
@@ -282,6 +282,7 @@ DATA(insert ( 1247 cmin 29 0 4 -4 0
DATA(insert ( 1247 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1247 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1247 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 SECURITY_SYSATTR_NAME 25 0 -1 -8 0 -1 -1 f x i t f f t 0));
/* ----------------
* pg_proc
@@ -338,6 +339,7 @@ DATA(insert ( 1255 cmin 29 0 4 -4 0
DATA(insert ( 1255 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1255 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1255 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1255 SECURITY_SYSATTR_NAME 25 0 -1 -8 0 -1 -1 f x i t f f t 0));
/* ----------------
* pg_attribute
@@ -386,6 +388,7 @@ DATA(insert ( 1249 cmin 29 0 4 -4 0
DATA(insert ( 1249 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1249 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1249 SECURITY_SYSATTR_NAME 25 0 -1 -8 0 -1 -1 f x i t f f t 0));
/* ----------------
* pg_class
@@ -454,6 +457,7 @@ DATA(insert ( 1259 cmin 29 0 4 -4 0
DATA(insert ( 1259 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1259 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0));
DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1259 SECURITY_SYSATTR_NAME 25 0 -1 -8 0 -1 -1 f x i t f f t 0));
/* ----------------
* pg_index
diff -rpNU3 base/src/include/catalog/pg_proc.h sepgsql/src/include/catalog/pg_proc.h
--- base/src/include/catalog/pg_proc.h 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/include/catalog/pg_proc.h 2008-06-14 02:36:58.000000000 +0900
@@ -4113,6 +4113,22 @@ DESCR("I/O");
DATA(insert OID = 2963 ( uuid_hash PGNSP PGUID 12 1 0 f f t f i 1 23 "2950" _null_ _null_ _null_ uuid_hash - _null_ _null_ ));
DESCR("hash");
+/* PostgreSQL Access Control Extension related functions */
+DATA(insert OID = 3410 ( lo_get_security PGNSP PGUID 12 1 0 f f t f v 1 25 "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 25" _null_ _null_ _null_ lo_set_security - _null_ _null_ ));
+
+/* SE-PostgreSQL related function */
+DATA(insert OID = 3450 ( sepgsql_getcon PGNSP PGUID 12 1 0 f f t f v 0 25 "" _null_ _null_ _null_ sepgsql_getcon - _null_ _null_ ));
+DATA(insert OID = 3451 ( sepgsql_getservcon PGNSP PGUID 12 1 0 f f t f v 0 25 "" _null_ _null_ _null_ sepgsql_getservcon - _null_ _null_ ));
+DATA(insert OID = 3452 ( sepgsql_get_user PGNSP PGUID 12 1 0 f f t f v 1 25 "25" _null_ _null_ _null_ sepgsql_get_user - _null_ _null_ ));
+DATA(insert OID = 3453 ( sepgsql_set_user PGNSP PGUID 12 1 0 f f t f v 2 25 "25 25" _null_ _null_ _null_ sepgsql_set_user - _null_ _null_ ));
+DATA(insert OID = 3454 ( sepgsql_get_role PGNSP PGUID 12 1 0 f f t f v 1 25 "25" _null_ _null_ _null_ sepgsql_get_role - _null_ _null_ ));
+DATA(insert OID = 3455 ( sepgsql_set_role PGNSP PGUID 12 1 0 f f t f v 2 25 "25 25" _null_ _null_ _null_ sepgsql_set_role - _null_ _null_ ));
+DATA(insert OID = 3456 ( sepgsql_get_type PGNSP PGUID 12 1 0 f f t f v 1 25 "25" _null_ _null_ _null_ sepgsql_get_type - _null_ _null_ ));
+DATA(insert OID = 3457 ( sepgsql_set_type PGNSP PGUID 12 1 0 f f t f v 2 25 "25 25" _null_ _null_ _null_ sepgsql_set_type - _null_ _null_ ));
+DATA(insert OID = 3458 ( sepgsql_get_range PGNSP PGUID 12 1 0 f f t f v 1 25 "25" _null_ _null_ _null_ sepgsql_get_range - _null_ _null_ ));
+DATA(insert OID = 3459 ( sepgsql_set_range PGNSP PGUID 12 1 0 f f t f v 2 25 "25 25" _null_ _null_ _null_ sepgsql_set_range - _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");
@@ -4460,7 +4476,8 @@ extern Oid ProcedureCreate(const char *p
Datum parameterNames,
Datum proconfig,
float4 procost,
- float4 prorows);
+ float4 prorows,
+ void *pgaceItem);
extern bool function_parse_error_transpose(const char *prosrc);
diff -rpNU3 base/src/include/catalog/pg_security.h sepgsql/src/include/catalog/pg_security.h
--- base/src/include/catalog/pg_security.h 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/src/include/catalog/pg_security.h 2008-06-15 22:24:53.000000000 +0900
@@ -0,0 +1,31 @@
+/*
+ * src/include/catalog/pg_security.h
+ * Definition of the security label relation (pg_security)
+ *
+ * Copyright (c) 2006 - 2007 KaiGai Kohei <kaigai@kaigai.gr.jp>
+ */
+#ifndef PG_SECURITY_H
+#define PG_SECURITY_H
+
+#define SecurityRelationId 3400
+
+CATALOG(pg_security,3400) BKI_SHARED_RELATION
+{
+ text seclabel; /* text representation of security label */
+} FormData_pg_security;
+
+/* ----------------
+ * Form_pg_security corresponds to a pointer to a tuple with
+ * the format of pg_security relation.
+ * ----------------
+ */
+typedef FormData_pg_security *Form_pg_security;
+
+/* ----------------
+ * compiler constants for pg_selinux
+ * ----------------
+ */
+#define Natts_pg_security 1
+#define Anum_pg_security_seclabel 1
+
+#endif /* PG_SELINUX_H */
diff -rpNU3 base/src/include/executor/tuptable.h sepgsql/src/include/executor/tuptable.h
--- base/src/include/executor/tuptable.h 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/include/executor/tuptable.h 2008-06-14 02:36:58.000000000 +0900
@@ -118,6 +118,9 @@ typedef struct TupleTableSlot
MinimalTuple tts_mintuple; /* set if it's a minimal tuple, else NULL */
HeapTupleData tts_minhdr; /* workspace if it's a minimal tuple */
long tts_off; /* saved state for slot_deform_tuple */
+
+ /* temporary storage variables for writable system column */
+ Datum tts_security; /* for security attribute */
} TupleTableSlot;
/*
diff -rpNU3 base/src/include/fmgr.h sepgsql/src/include/fmgr.h
--- base/src/include/fmgr.h 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/include/fmgr.h 2008-06-14 02:36:58.000000000 +0900
@@ -52,6 +52,8 @@ typedef struct FmgrInfo
void *fn_extra; /* extra space for use by handler */
MemoryContext fn_mcxt; /* memory context to store fn_extra in */
fmNodePtr fn_expr; /* expression parse tree for call, or NULL */
+
+ void *fn_pgaceItem; /* PGACE opaque field */
} FmgrInfo;
/*
@@ -511,6 +513,7 @@ extern Oid get_call_expr_argtype(fmNodeP
*/
extern char *Dynamic_library_path;
+extern char *expand_dynamic_library_name(const char *name);
extern PGFunction load_external_function(char *filename, char *funcname,
bool signalNotFound, void **filehandle);
extern PGFunction lookup_external_function(void *filehandle, char *funcname);
diff -rpNU3 base/src/include/libpq/be-fsstubs.h sepgsql/src/include/libpq/be-fsstubs.h
--- base/src/include/libpq/be-fsstubs.h 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/include/libpq/be-fsstubs.h 2008-06-14 02:36:58.000000000 +0900
@@ -36,6 +36,9 @@ extern Datum lo_tell(PG_FUNCTION_ARGS);
extern Datum lo_unlink(PG_FUNCTION_ARGS);
extern Datum lo_truncate(PG_FUNCTION_ARGS);
+extern Datum lo_get_security(PG_FUNCTION_ARGS);
+extern Datum lo_set_security(PG_FUNCTION_ARGS);
+
/*
* These are not fmgr-callable, but are available to C code.
* Probably these should have had the underscore-free names,
diff -rpNU3 base/src/include/nodes/nodes.h sepgsql/src/include/nodes/nodes.h
--- base/src/include/nodes/nodes.h 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/include/nodes/nodes.h 2008-06-14 02:36:58.000000000 +0900
@@ -358,7 +358,10 @@ typedef enum NodeTag
*/
T_TriggerData = 950, /* in commands/trigger.h */
T_ReturnSetInfo, /* in nodes/execnodes.h */
- T_TIDBitmap /* in nodes/tidbitmap.h */
+ T_TIDBitmap, /* in nodes/tidbitmap.h */
+ T_SEvalItemRelation, /* in nodes/security.h */
+ T_SEvalItemAttribute, /* in nodes/security.h */
+ T_SEvalItemProcedure, /* in nodes/security.h */
} NodeTag;
/*
diff -rpNU3 base/src/include/nodes/parsenodes.h sepgsql/src/include/nodes/parsenodes.h
--- base/src/include/nodes/parsenodes.h 2008-03-19 09:48:23.000000000 +0900
+++ sepgsql/src/include/nodes/parsenodes.h 2008-06-14 02:36:58.000000000 +0900
@@ -131,6 +131,7 @@ typedef struct Query
Node *setOperations; /* set-operation tree if this is top level of
* a UNION/INTERSECT/EXCEPT query */
+ Node *pgaceItem; /* PGACE: an opaque item for security purpose */
} Query;
@@ -391,6 +392,7 @@ typedef struct ColumnDef
Node *raw_default; /* default value (untransformed parse tree) */
char *cooked_default; /* nodeToString representation */
List *constraints; /* other constraints on column */
+ Node *pgaceItem; /* PGACE: security attribute */
} ColumnDef;
/*
@@ -602,6 +604,15 @@ typedef struct RangeTblEntry
bool inFromCl; /* present in FROM clause? */
AclMode requiredPerms; /* bitmask of required access permissions */
Oid checkAsUser; /* if valid, check access as this role */
+
+ /*
+ * The guest of PGACE can use pgaceTuplePerms to mark permission set
+ * of tuple-level access controls. This field is copied to scan node
+ * (like SeqSan), and it can be refered within pgaceExecScan() hook.
+ * If this hook returns false, the given tuple is filtered from the
+ * result set.
+ */
+ uint32 pgaceTuplePerms;
} RangeTblEntry;
/*
@@ -917,7 +928,8 @@ typedef enum AlterTableType
AT_EnableReplicaRule, /* ENABLE REPLICA RULE name */
AT_DisableRule, /* DISABLE RULE name */
AT_AddInherit, /* INHERIT parent */
- AT_DropInherit /* NO INHERIT parent */
+ AT_DropInherit, /* NO INHERIT parent */
+ AT_SetSecurityLabel, /* PGACE: set security label */
} AlterTableType;
typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */
@@ -1108,6 +1120,7 @@ typedef struct CreateStmt
List *options; /* options from WITH clause */
OnCommitAction oncommit; /* what do we do at COMMIT? */
char *tablespacename; /* table space to use, or NULL */
+ Node *pgaceItem; /* PGACE: security attribute */
} CreateStmt;
/* ----------
diff -rpNU3 base/src/include/nodes/plannodes.h sepgsql/src/include/nodes/plannodes.h
--- base/src/include/nodes/plannodes.h 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/include/nodes/plannodes.h 2008-06-14 02:36:58.000000000 +0900
@@ -73,6 +73,8 @@ typedef struct PlannedStmt
List *relationOids; /* OIDs of relations the plan depends on */
int nParamExec; /* number of PARAM_EXEC Params used */
+
+ Node *pgaceItem; /* PGACE: an opaque item for security purpose */
} PlannedStmt;
/* macro for fetching the Plan associated with a SubPlan node */
@@ -216,6 +218,14 @@ typedef struct Scan
{
Plan plan;
Index scanrelid; /* relid is index into the range table */
+
+ /*
+ * pgaceTuplePerms is used to show permission set to be applied to
+ * tuple-leve access controls by security module.
+ * It is copied from related RangeTblEntry's one when Scan structure
+ * is created.
+ */
+ uint32 pgaceTuplePerms;
} Scan;
/* ----------------
diff -rpNU3 base/src/include/nodes/relation.h sepgsql/src/include/nodes/relation.h
--- base/src/include/nodes/relation.h 2008-06-12 22:34:19.000000000 +0900
+++ sepgsql/src/include/nodes/relation.h 2008-06-14 02:36:58.000000000 +0900
@@ -366,6 +366,8 @@ typedef struct RelOptInfo
* list just to avoid recomputing the best inner indexscan repeatedly for
* similar outer relations. See comments for InnerIndexscanInfo.
*/
+
+ uint32 pgaceTuplePerms; /* copied from RangeTblEntry */
} RelOptInfo;
/*
diff -rpNU3 base/src/include/nodes/security.h sepgsql/src/include/nodes/security.h
--- base/src/include/nodes/security.h 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/src/include/nodes/security.h 2008-06-15 22:24:53.000000000 +0900
@@ -0,0 +1,58 @@
+/*-------------------------------------------------------------------------
+ *
+ * src/include/nodes/security.h
+ * definitions for security extention related nodes
+ *
+ * Portions Copyright (c) 2007-2008, PostgreSQL Global Development Group
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODES_SECURITY_H
+#define NODES_SECURITY_H
+
+#include "access/attnum.h"
+#include "nodes/nodes.h"
+
+/*
+ * SEvalItemRelation
+ *
+ * SE-PostgreSQL permission evaluation item for a relation
+ */
+typedef struct SEvalItemRelation {
+ NodeTag type;
+
+ uint32 perms;
+
+ Oid relid;
+ bool inh;
+} SEvalItemRelation;
+
+/*
+ * SEvalItemAttribute
+ *
+ * SE-PostgreSQL permission evaluation item for an attribute
+ */
+typedef struct SEvalItemAttribute {
+ NodeTag type;
+
+ uint32 perms;
+
+ Oid relid;
+ bool inh;
+ AttrNumber attno;
+} SEvalItemAttribute;
+
+/*
+ * SEvalItemProcedure
+ *
+ * SE-PostgreSQL permission evaluation item for a procedure
+ */
+typedef struct SEvalItemProcedure {
+ NodeTag type;
+
+ uint32 perms;
+
+ Oid funcid;
+} SEvalItemProcedure;
+
+#endif /* NODES_SECURITY_H */
diff -rpNU3 base/src/include/pg_config.h.in sepgsql/src/include/pg_config.h.in
--- base/src/include/pg_config.h.in 2008-01-28 16:06:37.000000000 +0900
+++ sepgsql/src/include/pg_config.h.in 2008-06-14 02:36:58.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
@@ -637,6 +640,9 @@
your system. */
#undef PTHREAD_CREATE_JOINABLE
+/* The name of security attribute. */
+#undef SECURITY_SYSATTR_NAME
+
/* The size of a `size_t', as computed by sizeof. */
#undef SIZEOF_SIZE_T
diff -rpNU3 base/src/include/security/pgace.h sepgsql/src/include/security/pgace.h
--- base/src/include/security/pgace.h 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/src/include/security/pgace.h 2008-09-22 17:51:21.000000000 +0900
@@ -0,0 +1,1202 @@
+/*
+ * include/security/pgace.h
+ * headers for PostgreSQL Access Control Extension (PGACE)
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ */
+#ifndef PGACE_H
+#define PGACE_H
+
+#include "access/htup.h"
+#include "commands/trigger.h"
+#include "executor/execdesc.h"
+#include "nodes/parsenodes.h"
+#include "storage/large_object.h"
+#include "utils/builtins.h"
+#include "utils/rel.h"
+
+#ifdef HAVE_SELINUX
+#include "security/sepgsql.h"
+#endif
+
+/*
+ * The definitions of PGACE hooks are follows:
+ *
+ * These are declared as static inline functions which give us no effect
+ * in the default (no security modules are enabled), and independent from
+ * its platform.
+ *
+ * The purpose of PGACE framework is to provide a security subsystems
+ * common hooks to apply its access controls, and minimize the impact
+ * to add a new security subsystem.
+ *
+ * (*) We calls the security subsystem implemented on PGACE framework
+ * as "the guest", in this comment.
+ *
+ * When a security module uses this framework, is has to add a #ifdef
+ * ... #endif block into the needed hooks, as follows:
+ *
+ * ------------
+ * static inline bool
+ * pgaceHeapTupleInsert(Relation rel, HeapTuple tuple,
+ * bool is_internal, bool with_returning)
+ * {
+ * #ifdef HAVE_SELINUX
+ * if (sepgsqlIsEnabled())
+ * return sepgsqlHeapTupleInsert(rel, tuple,
+ * is_internal,
+ * with_returning);
+ * #endif
+ * #ifdef HAVE_FOO_SECURITY
+ * if (fooIsEnabled())
+ * return fooHeapTupleInsert(rel, tuple,
+ * is_internal,
+ * with_returning);
+ * #endif
+ * return true;
+ * }
+ * ____________
+ *
+ * It can invokes specific security subsystem and the callee makes
+ * its decision whether the required access it allowed, or not.
+ * When no security module is available, these hooks have to keep
+ * the default behaivior to keep compatibility.
+ * In this case, pgaceHeapTupleInsert() has to return 'true'.
+ *
+ * Any hook has a comment to show the purpose of itself.
+ * Please look at this one to understand each hooks.
+ */
+
+/******************************************************************
+ * Shows the PGACE guest identifier
+ ******************************************************************/
+
+/*
+ * pgaceSecurityFeatureIdentity
+ *
+ * This hook has to return unique identifier of the PGACE guest.
+ * A GUC parameter of 'pgace_security_feature' shows this value.
+ */
+
+static inline const char *
+pgaceSecurityFeatureIdentity(void)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return "selinux";
+#endif
+ return "nothing";
+}
+
+/******************************************************************
+ * Initialization hooks
+ ******************************************************************/
+
+/*
+ * pgaceShmemSize
+ *
+ * This hook has to return the size of shared memory required
+ * by the guest. If it needs no shared memory region, it should
+ * return 0.
+ */
+static inline Size
+pgaceShmemSize(void)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlShmemSize();
+#endif
+ return (Size) 0;
+}
+
+/*
+ * pgaceInitialize
+ *
+ * This hook is invoked when a new PostgreSQL instance is created.
+ * The guest can use this hook to initialize itself.
+ *
+ * is_bootstrap is true, if bootstraping mode.
+ */
+static inline void
+pgaceInitialize(bool is_bootstrap)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlInitialize(is_bootstrap);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceStartupWorkerProcess
+ *
+ * The guest can create a worker process in this hook, if necessary.
+ * (currently, PGACE does not support multiple worker processes.)
+ *
+ * This hooks has to return the PID of child process. It is managed
+ * by postmaster in the same way to manage the other children.
+ * So, the worker process has to be available to handle signals.
+ *
+ * If unnecessary, it has to return (pid_t) 0.
+ */
+static inline pid_t
+pgaceStartupWorkerProcess(void)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlStartupWorkerProcess();
+#endif
+ return (pid_t) 0;
+}
+
+/******************************************************************
+ * SQL proxy hooks
+ ******************************************************************/
+
+/*
+ * pgaceProxyQuery
+ *
+ * This hook is invoked just after query is rewritten.
+ *
+ * The guest can check/modify/replace given query trees in this
+ * hook, if necessary.
+ * queryList is a list of Query object processes by rewriter.
+ */
+static inline List *
+pgaceProxyQuery(List *queryList)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlProxyQuery(queryList);
+#endif
+ return queryList;
+}
+
+/*
+ * pgaceIsAllowPlannerHook
+ *
+ * The guest can control whether planner_hook is available, or not.
+ * It returns false, if it is not allowed to apply planner_hook.
+ *
+ * The purpose of this hook is to make sure pgace opaque data are delivered
+ * to PlannedStmt::pgaceItem and Scan::pgaceTuplePerms, because they are
+ * copied in standard_planner(). Overriding planner_hook has a possibility
+ * to prevent the guest works correctly.
+ */
+static inline bool
+pgaceIsAllowPlannerHook(void)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return false;
+#endif
+ return true;
+}
+
+/*
+ * pgaceExecutorStart
+ *
+ * This hook is invoked on the head of ExecutorStart().
+ *
+ * The arguments of this hook are come from the ones of ExecutorStart
+ * as is.
+ */
+static inline void
+pgaceExecutorStart(QueryDesc *queryDesc, int eflags)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ Assert(queryDesc->plannedstmt != NULL);
+ sepgsqlVerifyQuery(queryDesc->plannedstmt, eflags);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceExecScan
+ *
+ * This hook is invoked on ExecScan for each tuple fetched.
+ * The guest can check its visibility, and can skip to scan the given
+ * tuple. If this hook returns false, the tuple is filtered from the
+ * result set or the target of updates/deletion.
+ *
+ * Otherwise, it has to return true.
+ *
+ * The guest can refer Scan::pgaceTuplePerms (declared as uint32).
+ * It is a copy come from RangeTblEntry::pgaceTuplePerms set in
+ * the previous phase. It can be used to mark what permissions are
+ * required to scanned tuples.
+ */
+static inline bool
+pgaceExecScan(Scan *scan, Relation rel, TupleTableSlot *slot)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlExecScan(scan, rel, slot);
+#endif
+ return true;
+}
+
+/*
+ * pgaceProcessUtility
+ *
+ * This hooks is invoked on the head of ProcessUtility().
+ */
+static inline void
+pgaceProcessUtility(Node *parsetree, ParamListInfo params, bool isTopLevel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlProcessUtility(parsetree, params, isTopLevel);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceEvaluateParams
+ *
+ * This hook is invoked just before parameter lists are evaluated
+ * at EvaluateParams().
+ */
+static inline void
+pgaceEvaluateParams(List *params)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlEvaluateParams(params);
+ return;
+ }
+#endif
+}
+
+
+/******************************************************************
+ * HeapTuple modification hooks
+ ******************************************************************/
+
+/*
+ * pgaceHeapTupleInsert
+ *
+ * This hooks is invoked just before a new tuple is inserted.
+ * If it returns false, inserting the given tuple is skipped.
+ * (or generates an error, if we cannot skip it simply.)
+ *
+ * The guest has to set a security attribute of a newly inserted
+ * tuple, if necessary and when user does not specify it explicitly.
+ *
+ * arguments:
+ * - rel is the target relation to be inserted.
+ * - tuple is the new tuple to be inserted.
+ * - is_internal is a bool to show whether it directly come from
+ * user's query, or not.
+ * - with_returning is a bool to show whether this INSERT statement
+ * has RETURNING clause, or not.
+ */
+static inline bool
+pgaceHeapTupleInsert(Relation rel, HeapTuple tuple,
+ bool is_internal, bool with_returning)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlHeapTupleInsert(rel, tuple,
+ is_internal,
+ with_returning);
+#endif
+ return true;
+}
+
+/*
+ * pgaceHeapTupleUpdate
+ *
+ * This hook is invoked just before a tuple is updated.
+ * If it returns false, updating the given tuple is skipped.
+ * (or generates an error, if we cannot skip it simply.)
+ *
+ * The guest has to preserve a security attribute of the updated
+ * tuple, if necessary and when user specify its new security
+ * attribute explicitly.
+ *
+ * arguments:
+ * - rel is the target relation to be updated.
+ * - otid is the ItemPointer of the tuple with older version.
+ * - newtup is the tuple to be updated.
+ * - is_internal is a bool to show whether it directly come from
+ * user's query, or not.
+ * - with_returning is a bool to show whether this INSERT statement
+ * has RETURNING clause, or not.
+ */
+static inline bool
+pgaceHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup,
+ bool is_internal, bool with_returning)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlHeapTupleUpdate(rel, otid, newtup,
+ is_internal,
+ with_returning);
+#endif
+ return true;
+}
+
+/*
+ * pgaceHeapTupleDelete
+ *
+ * This hook is invoked just before a tuple is deleted.
+ * If it returns false, deleting the given tuple is skipped.
+ * (or generates an error, if we cannot skip it simply.)
+ *
+ * arguments:
+ * - rel is the target relation to be deleted.
+ * - otid is the ItemPointer of the tuple to be deleted.
+ * - is_internal is a bool to show whether it directly come from
+ * user's query, or not.
+ * - with_returning is a bool to show whether this INSERT statement
+ * has RETURNING clause, or not.
+ */
+static inline bool
+pgaceHeapTupleDelete(Relation rel, ItemPointer otid,
+ bool is_internal, bool with_returning)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlHeapTupleDelete(rel, otid,
+ is_internal,
+ with_returning);
+#endif
+ return true;
+}
+
+/******************************************************************
+ * Extended SQL statement hooks
+ ******************************************************************/
+
+/*
+ * PGACE framework provides its guest facilities to manage security
+ * attribute of database object, using an extended SQL statement.
+ *
+ * For example:
+ * CREATE TABLE tbl (
+ * x integer,
+ * y text
+ * ) CONTEXT = 'system_u:object_r:sepgsql_ro_table_t:Classified',
+ *
+ * In SE-PostgreSQL, this statement enables to create a new table
+ * with explicitly specified security attribute by CONTEXT = 'xxx'
+ * clause. We call the clause as a "security attribute modifier".
+ *
+ * The series of hooks enables the guest to handle the given
+ * security attribute and apply it on the specified database
+ * object.
+ *
+ * The guest can apply this feature on the following statement:
+ *
+ * CREATE DATABASE <database>
+ * ALTER DATABASE <database>
+ * CREATE TABLE <table>
+ * ALTER TABLE <table>
+ * ALTER TABLE <table> ALTER <column>
+ * CREATE FUNCTION <function>
+ * ALTER FUNCTION <function>
+ */
+
+/*
+ * pgaceGramSecurityItem
+ *
+ * This hook is invoked during parsing a give query from parser/gram.y,
+ * and it generates a DefElem object which holds explicitly specified
+ * security attribute. If the guest support the feature of security
+ * attribute modifier, this hook has to check whether the given clause
+ * is appropriate, or not.
+ *
+ * In the following exmaple case:
+ * CREATE TABLE tbl (
+ * x integer,
+ * y text
+ * ) CONTEXT = 'system_u:object_r:sepgsql_ro_table_t:Classified',
+ *
+ * This hook is invoked with "context" as an argument of defname
+ * and "system_u:object_r:sepgsql_ro_table_t:Classified" as an
+ * argument of value, and has to check whether it is appropriate
+ * as a security attribute modifier, or not.
+ * If OK, the hook generates a DefElem object which contains
+ * the given context, and returns it.
+ *
+ * To return NULL means that "This clause is not a security attribute
+ * modifier", then it makes an error.
+ */
+static inline DefElem *
+pgaceGramSecurityItem(char *defname, char *value)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlGramSecurityItem(defname, value);
+#endif
+ return NULL;
+}
+
+/*
+ * pgaceIsGramSecurityItem
+ *
+ * This hook checks whether the given DefElem object means security
+ * attribute modifier generated at pgaceGramSecurityItem(), or not.
+ * If OK, it returns true.
+ */
+static inline bool
+pgaceIsGramSecurityItem(DefElem *defel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlIsGramSecurityItem(defel);
+#endif
+ return false;
+}
+
+/*
+ * The series of following hooks has three arguments.
+ * - rel is an opened relation of the target system catalog.
+ * - tuple is a new tuple to be inserted/updated.
+ * - defel is a security attribute modifier generated at
+ * pgaceGramSecurityItem().
+ */
+
+/*
+ * pgaceGramCreateRelation
+ *
+ * This hook invoked to apply an explicitly specified security attribute
+ * just before inserting a new tuple into pg_class system catalog on
+ * the processing of CREATE TABLE.
+ * The guest can attach the required security attribute for the given
+ * tuple which means a new relation.
+ */
+static inline void
+pgaceGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlGramCreateRelation(rel, tuple, defel);
+#endif
+}
+
+/*
+ * pgaceGramCreateAttribute
+ *
+ * This hook invoked to apply an explicitly specified security attribute
+ * just before inserting a new tuple into pg_attribute system catalog on
+ * the processing of CREATE TABLE.
+ * The guest can attach the required security attribute for the given
+ * tuple which means a new column.
+ */
+static inline void
+pgaceGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlGramCreateAttribute(rel, tuple, defel);
+#endif
+}
+
+/*
+ * pgaceGramAlterRelation
+ *
+ * This hook invoked to apply an explicitly specified security attribute
+ * just before updating an older tuple of pg_class system catalog on
+ * the processing of ALTER TABLE.
+ * The guest can attach the required security attribute for the given
+ * tuple which means a table.
+ */
+static inline void
+pgaceGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlGramAlterRelation(rel, tuple, defel);
+#endif
+}
+
+/*
+ * pgaceGramAlterAttribute
+ *
+ * This hook invoked to apply an explicitly specified security attribute
+ * just before updating an older tuple of pg_attribute system catalog on
+ * the processing of ALTER TABLE.
+ * The guest can attach the required security attribute for the given
+ * tuple which means a column.
+ */
+static inline void
+pgaceGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlGramAlterAttribute(rel, tuple, defel);
+#endif
+}
+
+/*
+ * pgaceGramCreateDatabase
+ *
+ * This hook invoked to apply an explicitly specified security attribute
+ * just before inserting a new tuple into pg_database system catalog on
+ * the processing of CREATE DATABASE.
+ * The guest can attach the required security attribute for the given
+ * tuple which means a database.
+ */
+static inline void
+pgaceGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlGramCreateDatabase(rel, tuple, defel);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceGramAlterDatabase
+ *
+ * This hook invoked to apply an explicitly specified security attribute
+ * just before updating an older tuple of pg_database system catalog on
+ * the processing of ALTER DATABASE.
+ * The guest can attach the required security attribute for the given
+ * tuple which means a database.
+ */
+static inline void
+pgaceGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlGramAlterDatabase(rel, tuple, defel);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceGramCreateFunction
+ *
+ * This hook invoked to apply an explicitly specified security attribute
+ * just before inserting a new tuple into pg_proc system catalog on
+ * the processing of CREATE FUNCTION.
+ * The guest can attach the required security attribute for the given
+ * tuple which means a function.
+ */
+static inline void
+pgaceGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlGramCreateFunction(rel, tuple, defel);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceGramAlterFunction
+ *
+ * This hook invoked to apply an explicitly specified security attribute
+ * just before updating an older tuple of pg_proc system catalog on
+ * the processing of ALTER FUNCTION.
+ * The guest can attach the required security attribute for the given
+ * tuple which means a function.
+ */
+static inline void
+pgaceGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlGramAlterFunction(rel, tuple, defel);
+ return;
+ }
+#endif
+}
+
+/******************************************************************
+ * DATABASE related hooks
+ ******************************************************************/
+
+/*
+ * pgaceSetDatabaseParam
+ *
+ * This hook is invoked just before putting a new value on a GUC
+ * variable.
+ *
+ * arguments:
+ * - name is a name of GUC variable.
+ * - argstring is its new value. NULL means user tries to reset
+ * the given GUC variable.
+ */
+static inline void
+pgaceSetDatabaseParam(const char *name, char *argstring)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlSetDatabaseParam(name, argstring);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceGetDatabaseParam
+ *
+ * This hook is invoked just before reffering a GUC variable.
+ *
+ * arguments:
+ * - name is a name of GUC variable.
+ */
+static inline void
+pgaceGetDatabaseParam(const char *name)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlGetDatabaseParam(name);
+ return;
+ }
+#endif
+}
+
+/******************************************************************
+ * FUNCTION related hooks
+ ******************************************************************/
+
+/*
+ * pgaceCallFunction
+ *
+ * This hook is invoked just before execute a function as a part
+ * of the query. It provides a FmgrInfo object used to execute
+ * function, and the guest can store an opaque data within
+ * FmgrInfo::fn_pgaceItem.
+ */
+static inline void
+pgaceCallFunction(FmgrInfo *finfo)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlCallFunction(finfo, false);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceCallFunctionTrigger
+ *
+ * This hook is invoked just before executing trigger function.
+ * If it returns false, the trigger function is not invoked and
+ * caller receives a NULL tuple as a result.
+ * (It also means skip to update/delete the tuple in BR-triggers.)
+ *
+ * The guest can refer FmgrInfo and TriggerData object to make
+ * its decision.
+ */
+static inline bool
+pgaceCallFunctionTrigger(FmgrInfo *finfo, TriggerData *tgdata)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlCallFunctionTrigger(finfo, tgdata);
+#endif
+ return true;
+}
+
+/*
+ * pgaceCallFunctionFastPath
+ *
+ * This hook is invoked just before executing a function in
+ * fast path.
+ */
+static inline void
+pgaceCallFunctionFastPath(FmgrInfo *finfo)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlCallFunction(finfo, true);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceBeginPerformCheckFK
+ *
+ * This hook is invoked just before performing FK constraint checks.
+ * The guest can change its internal state during the checks.
+ * The major purpose of this function is to prevent violation of
+ * integrity consistentency violation due to row-level access control.
+ * If the guest requires an opaque data, save_pgace can be used
+ * to store it.
+ */
+static inline void
+pgaceBeginPerformCheckFK(Relation rel, bool rel_is_primary, Datum *save_pgace)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlBeginPerformCheckFK(rel, rel_is_primary, save_pgace);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceEndPerformCheckFK
+ *
+ * This hook is invoked just after performing FK constraint checks.
+ * The guest can restore its internal state using this hook.
+ */
+static inline void
+pgaceEndPerformCheckFK(Relation rel, bool rel_is_primary, Datum save_pgace)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlEndPerformCheckFK(rel, rel_is_primary, save_pgace);
+ return;
+ }
+#endif
+}
+
+/******************************************************************
+ * TABLE related hooks
+ ******************************************************************/
+
+/*
+ * pgaceLockTable
+ *
+ * This hook is invoked when user tries to LOCK a table explicitly.
+ * The argument of relid shows the target relation id.
+ */
+static inline void
+pgaceLockTable(Oid relid)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlLockTable(relid);
+ return;
+ }
+#endif
+}
+
+/******************************************************************
+ * COPY TO/COPY FROM statement hooks
+ ******************************************************************/
+
+/*
+ * pgaceCopyTable
+ *
+ * This hook is invoked before executing COPY TO/COPY FROM statement,
+ * to give the guest a chance to check tables/columns appeared in.
+ *
+ * arguments:
+ * - rel is the target relation of this COPY TO/FROM statement.
+ * It can be NULL, when COPY (SELECT ...) TO ... is given.
+ * - attNumList is a list of attribute number
+ * - isFrom is a bool to show the direction of the COPY
+ */
+static inline void
+pgaceCopyTable(Relation rel, List *attNumList, bool isFrom)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlCopyTable(rel, attNumList, isFrom);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceCopyToTuple
+ *
+ * This hook is invoked just before output of a fetched tuple on
+ * processing COPY TO statement, to give the guest a chance to make
+ * a decision whether the given tuple is visible, or not.
+ * If it returns false, the given tuple is not exported, as if it
+ * does not exist on the target relation.
+ * Elsewhere,
+ *
+ * arguments:
+ * - rel is the target relation of this
+ * - attNumList is a list of attribute number
+ * - tuple is a tuple to be checked
+ */
+static inline bool
+pgaceCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlCopyToTuple(rel, attNumList, tuple);
+#endif
+ return true;
+}
+
+/******************************************************************
+ * Loadable shared library module hooks
+ ******************************************************************/
+
+/*
+ * pgaceLoadSharedModule
+ *
+ * This hook is invoked before loading a shared library module,
+ * to give the guest a change to confirm whether the required
+ * module is safe, or not.
+ *
+ * This hook can be also invoked implicitly when a user tries
+ * to call a function implemented within external modules.
+ */
+static inline void
+pgaceLoadSharedModule(const char *filename)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlLoadSharedModule(filename);
+ return;
+ }
+#endif
+}
+
+/******************************************************************
+ * Binary Large Object (BLOB) hooks
+ ******************************************************************/
+
+/*
+ * pgaceLargeObjectCreate
+ *
+ * This hooks is invoked just before the first tuple of a new large
+ * object is inserted, to give the guest a change to make its
+ * decision and attach proper security context for the tuple.
+ *
+ * The argument of rel is the opened pg_largeobject system catalog.
+ */
+static inline void
+pgaceLargeObjectCreate(Relation rel, HeapTuple tuple)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlLargeObjectCreate(rel, tuple);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceLargeObjectDrop
+ *
+ * This hook is invoked just before each tuple of a large object
+ * are deleted, to give the guest a change to make its decision.
+ *
+ * The argument of pgaceItem is an opaque data, the guest can
+ * use it discreationally.
+ */
+static inline void
+pgaceLargeObjectDrop(Relation rel, HeapTuple tuple, void **pgaceItem)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlLargeObjectDrop(rel, tuple, pgaceItem);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceLargeObjectRead
+ *
+ * This hook is invoked at the head of lo_read().
+ * If the guest allows a large object to have non-uniform security
+ * attributes (not a unique one for each page frame), using HeapTuple
+ * related hooks are more recommendable.
+ */
+static inline void
+pgaceLargeObjectRead(LargeObjectDesc *lodesc, int length)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlLargeObjectRead(lodesc, length);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceLargeObjectWrite
+ *
+ * This hook is invoked at the head of lo_write().
+ */
+static inline void
+pgaceLargeObjectWrite(LargeObjectDesc *lodesc, int length)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlLargeObjectWrite(lodesc, length);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceLargeObjectTruncate
+ *
+ * This hook is invoked at the head of lo_truncate().
+ */
+static inline void
+pgaceLargeObjectTruncate(LargeObjectDesc *lodesc, int offset)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlLargeObjectTruncate(lodesc, offset);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceLargeObjectImport
+ *
+ * This hook is invoked just before importing the given file.
+ */
+static inline void
+pgaceLargeObjectImport(Oid loid, int fdesc, const char *filename)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlLargeObjectImport(loid, fdesc, filename);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceLargeObjectExport
+ *
+ * This hook is invoked just before exporting the given large object.
+ */
+static inline void
+pgaceLargeObjectExport(Oid loid, int fdesc, const char *filename)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlLargeObjectExport(loid, fdesc, filename);
+ return;
+ }
+#endif
+}
+
+/*
+ * pgaceLargeObjectGetSecurity
+ *
+ * This hook is invoked when user requires to run lo_get_security()
+ * Note that PGACE assumes the security attribute of first page frame
+ * of large object represents its security attribute.
+ */
+static inline void
+pgaceLargeObjectGetSecurity(Relation rel, HeapTuple tuple)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlLargeObjectGetSecurity(rel, tuple);
+ return;
+ }
+#endif
+ elog(ERROR, "PGACE: There is no guest module.");
+}
+
+/*
+ * pgaceLargeObjectSetSecurity
+ *
+ * This hook is invoked when user requires to run lo_set_security(),
+ * for each tuple within a given large object, which have unchecked
+ * security attribute. In other word, PGACE does not require the guest
+ * to check permission toward same security attribute twice, or more.
+ */
+static inline void
+pgaceLargeObjectSetSecurity(Relation rel, HeapTuple newtup, HeapTuple oldtup)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ {
+ sepgsqlLargeObjectSetSecurity(rel, newtup, oldtup);
+ return;
+ }
+#endif
+ elog(ERROR, "PGACE: There is no guest module.");
+}
+
+/******************************************************************
+ * Security Label hooks
+ ******************************************************************/
+
+/*
+ * pgaceSecurityAttributeNecessary
+ *
+ * This hook provides a hint to the heap input/output subsystem.
+ * If it returns true, sizeof(Oid) bytes are expanded in HeapTupleHeader
+ * to store security identifier.
+ */
+static inline bool
+pgaceSecurityAttributeNecessary(void)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return true;
+#endif
+ return false;
+}
+
+/*
+ * pgaceTranslateSecurityLabelIn
+ *
+ * This hook enables the guest to translate a text representation
+ * of a given security attribute in external format into internal
+ * raw-format. It is invoked when user specifies security attribute
+ * explicitly in INSERT/UPDATE statement, to translate it into
+ * raw-internal format.
+ *
+ * It has to return a palloc()'ed Cstring, as a raw-internal format.
+ *
+ * In SE-PostgreSQL it supports translation in MLS/MCS labels like:
+ * "system_u:object_r:sepgsql_table_t:SystemHigh"
+ * <--> "system_u:object_r:sepgsql_table_t:s0:c0.c1023"
+ */
+static inline char *
+pgaceTranslateSecurityLabelIn(char *seclabel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlTranslateSecurityLabelIn(seclabel);
+#endif
+ return pstrdup("unlabeled");
+}
+
+/*
+ * pgaceTranslateSecurityLabelOut
+ *
+ * This hook enables the guest to translate a text representation
+ * of a given security attribute in internal format into cosmetic
+ * external format.
+ */
+static inline char *
+pgaceTranslateSecurityLabelOut(char *seclabel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlTranslateSecurityLabelOut(seclabel);
+#endif
+ return pstrdup("unlabeled");
+}
+
+/*
+ * pgaceValidateSecurityLabel
+ *
+ * This hook enables the guest to validate the given security attribute
+ * in raw-internal format. If it is not available, the hook has to
+ * return an alternative security attribute.
+ */
+static inline char *
+pgaceValidateSecurityLabel(char *seclabel)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlValidateSecurityLabel(seclabel);
+#endif
+ return seclabel;
+}
+
+/*
+ * pgaceSecurityLabelOfLabel
+ *
+ * This hook has to return the security attribute of a newly inserted
+ * tuple within pg_security
+ */
+static inline char *
+pgaceSecurityLabelOfLabel(void)
+{
+#ifdef HAVE_SELINUX
+ if (sepgsqlIsEnabled())
+ return sepgsqlSecurityLabelOfLabel();
+#endif
+ return pstrdup("unlabeled");
+}
+
+/******************************************************************
+ * PGACE common facilities (not a hooks)
+ ******************************************************************/
+
+/* GUC parameter support */
+extern const char *pgaceShowSecurityFeature(void);
+
+/* Security Label Management */
+extern void pgacePostBootstrapingMode(void);
+
+extern Oid pgaceSecurityLabelToSid(char *label);
+
+extern char *pgaceSidToSecurityLabel(Oid security_id);
+
+extern Oid pgaceLookupSecurityId(char *label);
+
+extern char *pgaceLookupSecurityLabel(Oid security_id);
+
+/* Extended SQL statements related */
+extern List *pgaceRelationAttrList(CreateStmt *stmt);
+
+extern void pgaceCreateRelationCommon(Relation rel, HeapTuple tuple,
+ List *pgace_attr_list);
+extern void pgaceCreateAttributeCommon(Relation rel, HeapTuple tuple,
+ List *pgace_attr_list);
+extern void pgaceAlterRelationCommon(Relation rel, AlterTableCmd *cmd);
+
+/******************************************************************
+ * Ported utility functions from 8.4devel
+ ******************************************************************/
+#define CStringGetTextDatum(x) \
+ (DirectFunctionCall1(textin, CStringGetDatum(x)))
+#define TextDatumGetCString(x) \
+ (DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(x))))
+
+/******************************************************************
+ * SQL function declaration related to PGACE security framework
+ ******************************************************************/
+
+/*
+ * SE-PostgreSQL SQL FUNCTIONS
+ */
+extern Datum sepgsql_getcon(PG_FUNCTION_ARGS);
+extern Datum sepgsql_getservcon(PG_FUNCTION_ARGS);
+extern Datum sepgsql_get_user(PG_FUNCTION_ARGS);
+extern Datum sepgsql_get_role(PG_FUNCTION_ARGS);
+extern Datum sepgsql_get_type(PG_FUNCTION_ARGS);
+extern Datum sepgsql_get_range(PG_FUNCTION_ARGS);
+extern Datum sepgsql_set_user(PG_FUNCTION_ARGS);
+extern Datum sepgsql_set_role(PG_FUNCTION_ARGS);
+extern Datum sepgsql_set_type(PG_FUNCTION_ARGS);
+extern Datum sepgsql_set_range(PG_FUNCTION_ARGS);
+
+#endif // PGACE_H
diff -rpNU3 base/src/include/security/sepgsql.h sepgsql/src/include/security/sepgsql.h
--- base/src/include/security/sepgsql.h 1970-01-01 09:00:00.000000000 +0900
+++ sepgsql/src/include/security/sepgsql.h 2008-09-22 17:51:21.000000000 +0900
@@ -0,0 +1,208 @@
+/*
+ * src/include/security/sepgsql.h
+ * headers for Security-Enhanced PostgreSQL (SE-PostgreSQL)
+ *
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ */
+#ifndef SEPGSQL_H
+#define SEPGSQL_H
+
+/* system catalogs */
+#include "catalog/pg_security.h"
+#include "lib/stringinfo.h"
+#include "nodes/execnodes.h"
+#include "nodes/nodes.h"
+#include "nodes/params.h"
+#include "nodes/parsenodes.h"
+
+#include <selinux/selinux.h>
+#include <selinux/flask.h>
+#include <selinux/av_permissions.h>
+
+/*
+ * SE-PostgreSQL modes
+ */
+extern char *sepostgresql_mode;
+
+/*
+ * Permission codes of internal representation
+ */
+#define SEPGSQL_PERMS_USE (1UL << 0)
+#define SEPGSQL_PERMS_SELECT (1UL << 1)
+#define SEPGSQL_PERMS_UPDATE (1UL << 2)
+#define SEPGSQL_PERMS_INSERT (1UL << 3)
+#define SEPGSQL_PERMS_DELETE (1UL << 4)
+#define SEPGSQL_PERMS_RELABELFROM (1UL << 5)
+#define SEPGSQL_PERMS_RELABELTO (1UL << 6)
+#define SEPGSQL_PERMS_READ (1UL << 7)
+#define SEPGSQL_PERMS_WRITE (1UL << 8)
+
+/*
+ * The implementation of PGACE/SE-PostgreSQL hooks
+ */
+
+/* Initialize / Finalize related hooks */
+extern Size sepgsqlShmemSize(void);
+
+extern void sepgsqlInitialize(bool is_bootstrap);
+
+extern pid_t sepgsqlStartupWorkerProcess(void);
+
+/* SQL proxy hooks */
+extern List *sepgsqlProxyQuery(List *queryList);
+
+extern void sepgsqlVerifyQuery(PlannedStmt *pstmt, int eflags);
+
+extern void sepgsqlEvaluateParams(List *params);
+
+extern void sepgsqlProcessUtility(Node *parsetree, ParamListInfo params, bool isTopLevel);
+
+/* ExecScan hooks */
+extern bool sepgsqlExecScan(Scan *scan, Relation rel, TupleTableSlot *slot);
+
+/* 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 void sepgsqlBeginPerformCheckFK(Relation rel, bool rel_is_primary, Datum *save_pgace);
+
+extern void sepgsqlEndPerformCheckFK(Relation rel, bool rel_is_primary, Datum save_pgace);
+
+/* 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 sepgsqlLargeObjectCreate(Relation rel, HeapTuple tuple);
+
+extern void sepgsqlLargeObjectDrop(Relation rel, HeapTuple tuple, void **pgaceItem);
+
+extern void sepgsqlLargeObjectRead(LargeObjectDesc *lodesc, int length);
+
+extern void sepgsqlLargeObjectWrite(LargeObjectDesc *lodesc, int length);
+
+extern void sepgsqlLargeObjectTruncate(LargeObjectDesc *lodesc, int offset);
+
+extern void sepgsqlLargeObjectImport(Oid loid, int fdesc, const char *filename);
+
+extern void sepgsqlLargeObjectExport(Oid loid, int fdesc, const char *filename);
+
+extern void sepgsqlLargeObjectGetSecurity(Relation rel, HeapTuple tuple);
+
+extern void sepgsqlLargeObjectSetSecurity(Relation rel, HeapTuple newtup, HeapTuple oldtup);
+
+/* Security Label hooks */
+extern char *sepgsqlTranslateSecurityLabelIn(char *context);
+
+extern char *sepgsqlTranslateSecurityLabelOut(char *context);
+
+extern char *sepgsqlValidateSecurityLabel(char *context);
+
+extern char *sepgsqlSecurityLabelOfLabel(void);
+
+/*
+ * SE-PostgreSQL core functions
+ * src/backend/security/sepgsql/core.c
+ */
+extern bool sepgsqlIsEnabled(void);
+
+extern const security_context_t sepgsqlGetServerContext(void);
+
+extern const security_context_t sepgsqlGetClientContext(void);
+
+extern const security_context_t sepgsqlGetDatabaseContext(void);
+
+extern const security_context_t sepgsqlGetUnlabeledContext(void);
+
+extern const security_context_t sepgsqlSwitchClientContext(security_context_t newcon);
+
+extern Oid sepgsqlGetDatabaseSecurityId(void);
+
+/*
+ * SE-PostgreSQL userspace avc functions
+ * src/backend/security/sepgsql/avc.c
+ */
+extern void sepgsqlAvcInit(void);
+
+extern bool sepgsqlAvcPermission(const security_context_t scon,
+ const security_context_t tcon,
+ security_class_t tclass,
+ access_vector_t perms,
+ const char *objname,
+ bool abort);
+
+extern bool sepgsqlAvcPermissionSid(const security_context_t scon,
+ Oid tsid,
+ security_class_t tclass,
+ access_vector_t perms,
+ const char *objname,
+ bool abort);
+
+extern security_context_t sepgsqlAvcCreateCon(const security_context_t scon,
+ const security_context_t tcon,
+ security_class_t tclass);
+
+extern Oid sepgsqlAvcCreateConSid(const security_context_t scon,
+ Oid tsid,
+ security_class_t tclass);
+
+/*
+ * SE-PostgreSQL permission evaluation related
+ * src/backend/security/sepgsql/permission.c
+ */
+extern const char *sepgsqlTupleName(Oid relid, HeapTuple tuple);
+
+extern void sepgsqlSetDefaultContext(Relation rel, HeapTuple tuple);
+
+extern bool sepgsqlCheckTuplePerms(Relation rel, HeapTuple tuple,
+ HeapTuple oldtup, uint32 perms, bool abort);
+#endif /* SEPGSQL_H */
diff -rpNU3 base/src/include/storage/fd.h sepgsql/src/include/storage/fd.h
--- base/src/include/storage/fd.h 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/include/storage/fd.h 2008-06-14 02:36:58.000000000 +0900
@@ -67,6 +67,7 @@ extern int FileWrite(File file, char *bu
extern int FileSync(File file);
extern long FileSeek(File file, long offset, int whence);
extern int FileTruncate(File file, long offset);
+extern int FileRawDescriptor(File file);
/* Operations that allow use of regular stdio --- USE WITH CAUTION */
extern FILE *AllocateFile(const char *name, const char *mode);
diff -rpNU3 base/src/include/storage/lwlock.h sepgsql/src/include/storage/lwlock.h
--- base/src/include/storage/lwlock.h 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/include/storage/lwlock.h 2008-06-14 02:36:58.000000000 +0900
@@ -63,6 +63,7 @@ typedef enum LWLockId
AutovacuumLock,
AutovacuumScheduleLock,
SyncScanLock,
+ SepgsqlAvcLock,
/* Individual lock IDs end here */
FirstBufMappingLock,
FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS,
diff -rpNU3 base/src/include/utils/catcache.h sepgsql/src/include/utils/catcache.h
--- base/src/include/utils/catcache.h 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/include/utils/catcache.h 2008-06-14 02:36:58.000000000 +0900
@@ -172,6 +172,7 @@ extern HeapTuple SearchCatCache(CatCache
Datum v1, Datum v2,
Datum v3, Datum v4);
extern void ReleaseCatCache(HeapTuple tuple);
+extern void InsertCatCache(CatCache *cache, HeapTuple tuple);
extern CatCList *SearchCatCacheList(CatCache *cache, int nkeys,
Datum v1, Datum v2,
diff -rpNU3 base/src/include/utils/errcodes.h sepgsql/src/include/utils/errcodes.h
--- base/src/include/utils/errcodes.h 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/include/utils/errcodes.h 2008-06-14 02:36:58.000000000 +0900
@@ -339,6 +339,12 @@
#define ERRCODE_NO_DATA_FOUND MAKE_SQLSTATE('P','0', '0','0','2')
#define ERRCODE_TOO_MANY_ROWS MAKE_SQLSTATE('P','0', '0','0','3')
+/* Class SE - Security Error (PGACE/SE-PostgreSQL error class) */
+#define ERRCODE_PGACE_ERROR MAKE_SQLSTATE('S','E', '0','0','0')
+#define ERRCODE_SELINUX_ERROR MAKE_SQLSTATE('S','E', '0','0','1')
+#define ERRCODE_SELINUX_AUDIT MAKE_SQLSTATE('S','E', '0','0','2')
+#define ERRCODE_SELINUX_INFO MAKE_SQLSTATE('S','E', '0','0','3')
+
/* Class XX - Internal Error (PostgreSQL-specific error class) */
/* (this is for "can't-happen" conditions and software bugs) */
#define ERRCODE_INTERNAL_ERROR MAKE_SQLSTATE('X','X', '0','0','0')
diff -rpNU3 base/src/include/utils/syscache.h sepgsql/src/include/utils/syscache.h
--- base/src/include/utils/syscache.h 2008-01-07 23:51:33.000000000 +0900
+++ sepgsql/src/include/utils/syscache.h 2008-06-14 02:36:58.000000000 +0900
@@ -76,6 +76,8 @@
#define TSTEMPLATEOID 45
#define TYPENAMENSP 46
#define TYPEOID 47
+#define SECURITYOID 48
+#define SECURITYLABEL 49
extern void InitCatalogCache(void);
extern void InitCatalogCachePhase2(void);
@@ -84,6 +86,8 @@ extern HeapTuple SearchSysCache(int cach
Datum key1, Datum key2, Datum key3, Datum key4);
extern void ReleaseSysCache(HeapTuple tuple);
+extern void InsertSysCache(Oid relid, HeapTuple tuple);
+
/* convenience routines */
extern HeapTuple SearchSysCacheCopy(int cacheId,
Datum key1, Datum key2, Datum key3, Datum key4);