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 #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 +# + +## +##

+## Allow to generate auditallow logs +##

+##
+gen_tunable(sepgsql_enable_auditallow, false) + +## +##

+## Allow to generate auditdeny logs +##

+##
+gen_tunable(sepgsql_enable_auditdeny, true) + +## +##

+## Allow widespread permissions for regression test +## Don't set TRUE on operation phase +##

+##
+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 @@ +####################################### +## +## The userdomain template for the SE-PostgreSQL. +## +## +## This template creates a delivered types which are used +## for given userdomains. +## +## +## +## The prefix of the user domain (e.g., user +## is the prefix for user_t). +## +## +## +## +## The type of the user domain. +## +## +## +## +## The role associated with the user domain. +## +## +# +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; +') + +######################################## +## +## Marks as a SE-PostgreSQL loadable shared library module +## +## +## +## Type marked as a database object type. +## +## +# +interface(`sepostgresql_loadable_module',` + gen_require(` + attribute sepgsql_module_type; + ') + + typeattribute $1 sepgsql_module_type; +') + +######################################## +## +## Marks as a SE-PostgreSQL database object type +## +## +## +## Type marked as a database object type. +## +## +# +interface(`sepostgresql_database_object',` + gen_require(` + attribute sepgsql_database_type; + ') + + typeattribute $1 sepgsql_database_type; +') + +######################################## +## +## Marks as a SE-PostgreSQL table/column/tuple object type +## +## +## +## Type marked as a table/column/tuple object type. +## +## +# +interface(`sepostgresql_table_object',` + gen_require(` + attribute sepgsql_table_type; + ') + + typeattribute $1 sepgsql_table_type; +') + +######################################## +## +## Marks as a SE-PostgreSQL system table/column/tuple object type +## +## +## +## Type marked as a table/column/tuple object type. +## +## +# +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; +') + +######################################## +## +## Marks as a SE-PostgreSQL procedure object type +## +## +## +## Type marked as a database object type. +## +## +# +interface(`sepostgresql_procedure_object',` + gen_require(` + attribute sepgsql_procedure_type; + ') + + typeattribute $1 sepgsql_procedure_type; +') + +######################################## +## +## Marks as a SE-PostgreSQL binary large object type +## +## +## +## Type marked as a database binary large object type. +## +## +# +interface(`sepostgresql_blob_object',` + gen_require(` + attribute sepgsql_blob_type; + ') + + typeattribute $1 sepgsql_blob_type; +') + +######################################## +## +## Allow the specified domain unprivileged accesses to unifined database objects +## managed by SE-PostgreSQL, +## +## +## +## Domain allowed access. +## +## +# +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; +') + +######################################## +## +## Allow the specified domain unconfined accesses to any database objects +## managed by SE-PostgreSQL, +## +## +## +## Domain allowed access. +## +## +# +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 +# + +## +##

+## Allow to generate auditallow logs +##

+##
+gen_tunable(sepgsql_enable_auditallow, false) + +## +##

+## Allow to generate auditdeny logs +##

+##
+gen_tunable(sepgsql_enable_auditdeny, true) + +## +##

+## Allow unprivileged users to execute DDL statement +##

+##
+gen_tunable(sepgsql_enable_users_ddl, true) + +## +##

+## Allow widespread permissions for regression test +## Don't set TRUE on operation phase +##

+##
+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-06-14 02:36:58.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" @@ -1772,6 +1773,8 @@ ExecEvalOper(FuncExprState *fcache, /* Go directly to ExecMakeFunctionResult on subsequent uses */ fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResult; + pgaceCallFunction(&fcache->func); + return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } 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-06-14 02:36:58.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; } @@ -1523,6 +1525,7 @@ _copyRangeTblEntry(RangeTblEntry *from) COPY_SCALAR_FIELD(inFromCl); COPY_SCALAR_FIELD(requiredPerms); COPY_SCALAR_FIELD(checkAsUser); + COPY_SCALAR_FIELD(pgaceTuplePerms); return newnode; } @@ -1789,6 +1792,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 +1873,7 @@ _copyQuery(Query *from) COPY_NODE_FIELD(limitCount); COPY_NODE_FIELD(rowMarks); COPY_NODE_FIELD(setOperations); + COPY_NODE_FIELD(pgaceItem); return newnode; } @@ -2105,6 +2110,7 @@ _copyCreateStmt(CreateStmt *from) COPY_NODE_FIELD(options); COPY_SCALAR_FIELD(oncommit); COPY_STRING_FIELD(tablespacename); + COPY_NODE_FIELD(pgaceItem); return newnode; } @@ -2998,6 +3004,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 +3649,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/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-06-14 02:36:58.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); } /* @@ -1748,6 +1750,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 +1836,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 +2049,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 +2479,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 OptTableSpace OptConsTableSpace OptTableSpaceOwner %type opt_check_option +%type OptSecurityItem SecurityItem + %type xml_attribute_el %type xml_attribute_list xml_attributes %type xml_root_version opt_xml_root_standalone @@ -1637,6 +1640,24 @@ alter_table_cmd: n->def = (Node *) $3; $$ = (Node *)n; } + /* ALTER TABLE CONTEXT = '...' */ + | SecurityItem + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetSecurityLabel; + n->name = NULL; + n->def = (Node *) $1; + $$ = (Node *) n; + } + /* ALTER TABLE ALTER [COLUMN] 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 (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 +# +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 +#include + +/***************************************************************************** + * 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 +#include +#include +#include + +/* + * 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 + +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-07-11 14:10:51.000000000 +0900 @@ -0,0 +1,882 @@ +/* + * 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 +#include +#include +#include + +/******************************************************************************* + * 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 in foreign key constraint + * ---------------------------------------------------------- */ +void +sepgsqlPreparePlanCheck(Relation rel, Datum *pgace_saved) +{ + /* store the current status */ + *pgace_saved = BoolGetDatum(abort_on_violated_tuple); + + abort_on_violated_tuple = true; +} + +void +sepgsqlRestorePlanCheck(Relation rel, Datum pgace_saved) +{ + abort_on_violated_tuple = DatumGetBool(pgace_saved); +} + +/******************************************************************************* + * 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; + + /* + * <-- database:module_install --> + */ + sepgsqlAvcPermission(sepgsqlGetClientContext(), + sepgsqlGetDatabaseContext(), + SECCLASS_DB_DATABASE, + DB_DATABASE__INSTALL_MODULE, + NULL, true); + + /* + * <-- database:module_install --> + */ + 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-06-14 02:36:58.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" @@ -3202,6 +3203,7 @@ ri_PlanCheck(const char *querystr, int n Relation query_rel; Oid save_userid; bool save_secdefcxt; + Datum save_pgace; /* * The query is always run against the FK table except when this is an @@ -3219,7 +3221,18 @@ ri_PlanCheck(const char *querystr, int n SetUserIdAndContext(RelationGetForm(query_rel)->relowner, true); /* Create the plan */ - qplan = SPI_prepare(querystr, nargs, argtypes); + pgacePreparePlanCheck(query_rel, &save_pgace); + PG_TRY(); + { + qplan = SPI_prepare(querystr, nargs, argtypes); + } + PG_CATCH(); + { + pgaceRestorePlanCheck(query_rel, save_pgace); + PG_RE_THROW(); + } + PG_END_TRY(); + pgaceRestorePlanCheck(query_rel, save_pgace); if (qplan == NULL) elog(ERROR, "SPI_prepare returned %d for %s", SPI_result, querystr); 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 + */ +#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 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-07-11 14:10:51.000000000 +0900 @@ -0,0 +1,1183 @@ +/* + * 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 +} + +/* + * pgaceInitialize + * + * 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 + * ALTER DATABASE + * CREATE TABLE + * ALTER TABLE
+ * ALTER TABLE
ALTER + * CREATE FUNCTION + * ALTER 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 +} + +/* + * pgacePreparePlanCheck + * + * This hook is invoked just before FK/PK constraint checks. + * The guest can change its state during FK/PK constraint checks, + * and restore it on pgaceRestorePlanCheck(). + * If it needs an opaque data, pgace_saved can be used to store + * an opaque data. + */ +static inline void +pgacePreparePlanCheck(Relation rel, Datum *pgace_saved) +{ +#ifdef HAVE_SELINUX + if (sepgsqlIsEnabled()) + { + sepgsqlPreparePlanCheck(rel, pgace_saved); + return; + } +#endif +} + +/* + * pgaceRestorePlanCheck + * + * This hook is invoked just after FK/PK constraint checks. + * When the guest change something, it can be restored in this hook. + */ +static inline void +pgaceRestorePlanCheck(Relation rel, Datum pgace_saved) +{ +#ifdef HAVE_SELINUX + if (sepgsqlIsEnabled()) + { + sepgsqlRestorePlanCheck(rel, pgace_saved); + 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)))) + +#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-07-11 14:10:51.000000000 +0900 @@ -0,0 +1,223 @@ +/* + * 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 +#include +#include + +/* + * 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 sepgsqlPreparePlanCheck(Relation rel, Datum *pgace_saved); + +extern void sepgsqlRestorePlanCheck(Relation rel, Datum pgace_saved); + +/* TABLE related hooks */ +extern void sepgsqlLockTable(Oid relid); + +extern bool sepgsqlAlterTable(Relation rel, AlterTableCmd *cmd); + +/* COPY TO/COPY FROM statement hooks */ +extern void sepgsqlCopyTable(Relation rel, List *attnumlist, bool is_from); + +extern bool sepgsqlCopyToTuple(Relation rel, List *attnumlist, + HeapTuple tuple); + +/* Loadable shared library module hooks */ +extern void sepgsqlLoadSharedModule(const char *filename); + +/* Binary Large Object (BLOB) hooks */ +extern void 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); + +/* + * 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 /* 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);