diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d42fd5a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +postgresql-8.2.11.tar.gz diff --git a/dead.package b/dead.package deleted file mode 100644 index e1c0fac..0000000 --- a/dead.package +++ /dev/null @@ -1 +0,0 @@ -SE-PostgreSQL was merged into mainstream as contrib/sepgsql extension diff --git a/sepostgresql-8.2.11-1.patch b/sepostgresql-8.2.11-1.patch new file mode 100644 index 0000000..67c58f4 --- /dev/null +++ b/sepostgresql-8.2.11-1.patch @@ -0,0 +1,8837 @@ +diff -rpNU3 base/configure sepgsql/configure +--- base/configure 2008-11-05 09:46:39.000000000 +0900 ++++ sepgsql/configure 2008-11-05 09:50:36.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 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_krb5 krb_srvtab with_pam with_ldap with_bonjour with_openssl with_zlib EGREP ELF_SYS LDFLAGS_SL AWK FLEX FLEXFLAGS LN_S LD with_gnu_ld ld_R_works RANLIB ac_ct_RANLIB TAR STRIP ac_ct_STRIP STRIP_STATIC_LIB STRIP_SHARED_LIB YACC YFLAGS PERL perl_archlibexp perl_privlibexp perl_useshrplib perl_embed_ldflags PYTHON python_version python_configdir python_includespec python_libdir python_libspec python_additional_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 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_krb5 krb_srvtab with_pam with_ldap with_bonjour with_openssl with_zlib enable_selinux EGREP ELF_SYS LDFLAGS_SL AWK FLEX FLEXFLAGS LN_S LD with_gnu_ld ld_R_works RANLIB ac_ct_RANLIB TAR STRIP ac_ct_STRIP STRIP_STATIC_LIB STRIP_SHARED_LIB YACC YFLAGS PERL perl_archlibexp perl_privlibexp perl_useshrplib perl_embed_ldflags PYTHON python_version python_configdir python_includespec python_libdir python_libspec python_additional_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. +@@ -870,6 +870,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 in spite of thread test failure ++ --enable-selinux build with NSA SELinux support + --disable-largefile omit support for large files + + Optional Packages: +@@ -4247,6 +4248,118 @@ fi; + + + # ++# NSA SELinux support ++# ++ ++pgac_args="$pgac_args enable_selinux" ++ ++# Check whether --enable-selinux or --disable-selinux was given. ++if test "${enable_selinux+set}" = set; then ++ enableval="$enable_selinux" ++ ++ case $enableval in ++ yes) ++ : ++ ;; ++ no) ++ : ++ ;; ++ *) ++ { { echo "$as_me:$LINENO: error: no argument expected for --enable-selinux option" >&5 ++echo "$as_me: error: no argument expected for --enable-selinux option" >&2;} ++ { (exit 1); exit 1; }; } ++ ;; ++ esac ++ ++else ++ enable_selinux=no ++ ++fi; ++ ++if test "$enable_selinux" = yes; then ++ echo "$as_me:$LINENO: checking for getpeercon in -lselinux" >&5 ++echo $ECHO_N "checking for getpeercon in -lselinux... $ECHO_C" >&6 ++if test "${ac_cv_lib_selinux_getpeercon+set}" = set; then ++ echo $ECHO_N "(cached) $ECHO_C" >&6 ++else ++ ac_check_lib_save_LIBS=$LIBS ++LIBS="-lselinux $LIBS" ++cat >conftest.$ac_ext <<_ACEOF ++/* confdefs.h. */ ++_ACEOF ++cat confdefs.h >>conftest.$ac_ext ++cat >>conftest.$ac_ext <<_ACEOF ++/* end confdefs.h. */ ++ ++/* Override any gcc2 internal prototype to avoid an error. */ ++#ifdef __cplusplus ++extern "C" ++#endif ++/* We use char because int might match the return type of a gcc2 ++ builtin and then its argument prototype would still apply. */ ++char getpeercon (); ++int ++main () ++{ ++getpeercon (); ++ ; ++ return 0; ++} ++_ACEOF ++rm -f conftest.$ac_objext conftest$ac_exeext ++if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 ++ (eval $ac_link) 2>conftest.er1 ++ ac_status=$? ++ grep -v '^ *+' conftest.er1 >conftest.err ++ rm -f conftest.er1 ++ cat conftest.err >&5 ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); } && ++ { ac_try='test -z "$ac_c_werror_flag" ++ || test ! -s conftest.err' ++ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 ++ (eval $ac_try) 2>&5 ++ ac_status=$? ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); }; } && ++ { ac_try='test -s conftest$ac_exeext' ++ { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 ++ (eval $ac_try) 2>&5 ++ ac_status=$? ++ echo "$as_me:$LINENO: \$? = $ac_status" >&5 ++ (exit $ac_status); }; }; then ++ ac_cv_lib_selinux_getpeercon=yes ++else ++ echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ac_cv_lib_selinux_getpeercon=no ++fi ++rm -f conftest.err conftest.$ac_objext \ ++ conftest$ac_exeext conftest.$ac_ext ++LIBS=$ac_check_lib_save_LIBS ++fi ++echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_getpeercon" >&5 ++echo "${ECHO_T}$ac_cv_lib_selinux_getpeercon" >&6 ++if test $ac_cv_lib_selinux_getpeercon = yes; then ++ cat >>confdefs.h <<\_ACEOF ++#define SECURITY_SYSATTR_NAME "security_context" ++_ACEOF ++ ++ cat >>confdefs.h <<_ACEOF ++#define HAVE_SELINUX 1 ++_ACEOF ++ ++ ++else ++ { { echo "$as_me:$LINENO: error: \"libselinux didn't found.\"" >&5 ++echo "$as_me: error: \"libselinux didn't found.\"" >&2;} ++ { (exit 1); exit 1; }; } ++fi ++ ++fi ++ ++# + # Elf + # + +@@ -23823,6 +23936,7 @@ s,@with_ldap@,$with_ldap,;t t + s,@with_bonjour@,$with_bonjour,;t t + s,@with_openssl@,$with_openssl,;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-11-05 09:46:39.000000000 +0900 ++++ sepgsql/configure.in 2008-11-05 09:50:36.000000000 +0900 +@@ -544,6 +544,19 @@ PGAC_ARG_BOOL(with, zlib, yes, + AC_SUBST(with_zlib) + + # ++# NSA SELinux support ++# ++PGAC_ARG_BOOL(enable, selinux, no, ++ [ --enable-selinux build with NSA SELinux support]) ++if test "$enable_selinux" = yes; then ++ AC_CHECK_LIB(selinux, getpeercon, ++ AC_DEFINE(SECURITY_SYSATTR_NAME, "security_context") ++ AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1) ++ AC_SUBST(enable_selinux), ++ AC_MSG_ERROR("libselinux didn't found.")) ++fi ++ ++# + # Elf + # + +diff -rpNU3 base/src/Makefile.global.in sepgsql/src/Makefile.global.in +--- base/src/Makefile.global.in 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/Makefile.global.in 2007-10-25 13:12:52.000000000 +0900 +@@ -159,6 +159,7 @@ enable_nls = @enable_nls@ + enable_debug = @enable_debug@ + enable_dtrace = @enable_dtrace@ + enable_thread_safety = @enable_thread_safety@ ++enable_selinux = @enable_selinux@ + + python_includespec = @python_includespec@ + python_libdir = @python_libdir@ +diff -rpNU3 base/src/backend/Makefile sepgsql/src/backend/Makefile +--- base/src/backend/Makefile 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/Makefile 2007-10-25 13:12:52.000000000 +0900 +@@ -15,7 +15,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 utils $(top_builddir)/src/timezone ++ security storage tcop utils $(top_builddir)/src/timezone + + SUBSYSOBJS := $(DIRS:%=%/SUBSYS.o) + +@@ -31,6 +31,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 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/access/common/heaptuple.c 2007-10-25 13:12:52.000000000 +0900 +@@ -26,6 +26,7 @@ + #include "access/heapam.h" + #include "access/tuptoaster.h" + #include "executor/tuptable.h" ++#include "security/pgace.h" + + + /* ---------------------------------------------------------------- +@@ -314,6 +315,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; + +@@ -593,6 +597,11 @@ heap_getsysattr(HeapTuple tup, int attnu + case TableOidAttributeNumber: + result = ObjectIdGetDatum(tup->t_tableOid); + break; ++#ifdef SECURITY_SYSATTR_NAME ++ case SecurityAttributeNumber: ++ result = ObjectIdGetDatum(HeapTupleGetSecurity(tup)); ++ break; ++#endif + default: + elog(ERROR, "invalid attnum: %d", attnum); + result = 0; /* keep compiler quiet */ +@@ -624,6 +633,7 @@ heap_copytuple(HeapTuple tuple) + newTuple->t_tableOid = tuple->t_tableOid; + newTuple->t_data = (HeapTupleHeader) ((char *) newTuple + HEAPTUPLESIZE); + memcpy((char *) newTuple->t_data, (char *) tuple->t_data, tuple->t_len); ++ HeapTupleSetSecurity(newTuple, HeapTupleGetSecurity(tuple)); + return newTuple; + } + +@@ -650,6 +660,7 @@ heap_copytuple_with_tuple(HeapTuple src, + dest->t_tableOid = src->t_tableOid; + dest->t_data = (HeapTupleHeader) palloc(src->t_len); + memcpy((char *) dest->t_data, (char *) src->t_data, src->t_len); ++ HeapTupleSetSecurity(dest, HeapTupleGetSecurity(src)); + } + + /* +@@ -928,6 +939,7 @@ heap_modify_tuple(HeapTuple tuple, + newTuple->t_tableOid = tuple->t_tableOid; + if (tupleDesc->tdhasoid) + HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); ++ HeapTupleSetSecurity(newTuple, HeapTupleGetSecurity(tuple)); + + return newTuple; + } +@@ -1000,6 +1012,7 @@ heap_modifytuple(HeapTuple tuple, + newTuple->t_tableOid = tuple->t_tableOid; + if (tupleDesc->tdhasoid) + HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); ++ HeapTupleSetSecurity(newTuple, HeapTupleGetSecurity(tuple)); + + return newTuple; + } +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:26:23.000000000 +0900 ++++ sepgsql/src/backend/access/heap/heapam.c 2008-03-19 09:35:55.000000000 +0900 +@@ -49,6 +49,7 @@ + #include "catalog/namespace.h" + #include "miscadmin.h" + #include "pgstat.h" ++#include "security/pgace.h" + #include "storage/procarray.h" + #include "utils/inval.h" + #include "utils/lsyscache.h" +@@ -1420,6 +1421,7 @@ heap_insert(Relation relation, HeapTuple + HeapTupleHeaderSetXmax(tup->t_data, 0); /* zero out Datum fields */ + HeapTupleHeaderSetCmax(tup->t_data, 0); /* for cleanliness */ + tup->t_tableOid = RelationGetRelid(relation); ++ pgaceHeapInsert(relation, tup); + + /* + * If the new tuple is too big for storage or contains already toasted +@@ -1466,6 +1468,7 @@ heap_insert(Relation relation, HeapTuple + rdata[0].buffer = InvalidBuffer; + rdata[0].next = &(rdata[1]); + ++ xlhdr.t_security = HeapTupleGetSecurity(heaptup); + xlhdr.t_natts = heaptup->t_data->t_natts; + xlhdr.t_infomask = heaptup->t_data->t_infomask; + xlhdr.t_hoff = heaptup->t_data->t_hoff; +@@ -1543,6 +1546,7 @@ heap_insert(Relation relation, HeapTuple + Oid + simple_heap_insert(Relation relation, HeapTuple tup) + { ++ pgaceSimpleHeapInsert(relation, tup); + return heap_insert(relation, tup, GetCurrentCommandId(), true, true); + } + +@@ -1595,6 +1599,7 @@ heap_delete(Relation relation, ItemPoint + tp.t_data = (HeapTupleHeader) PageGetItem(dp, lp); + tp.t_len = ItemIdGetLength(lp); + tp.t_self = *tid; ++ pgaceHeapDelete(relation, &tp); + + l1: + result = HeapTupleSatisfiesUpdate(tp.t_data, cid, buffer); +@@ -1817,6 +1822,7 @@ simple_heap_delete(Relation relation, It + ItemPointerData update_ctid; + TransactionId update_xmax; + ++ pgaceSimpleHeapDelete(relation, tid); + result = heap_delete(relation, tid, + &update_ctid, &update_xmax, + GetCurrentCommandId(), InvalidSnapshot, +@@ -2058,6 +2064,7 @@ l2: + HeapTupleHeaderSetCmin(newtup->t_data, cid); + HeapTupleHeaderSetXmax(newtup->t_data, 0); /* zero out Datum fields */ + HeapTupleHeaderSetCmax(newtup->t_data, 0); /* for cleanliness */ ++ pgaceHeapUpdate(relation, newtup, &oldtup); + + /* + * If the toaster needs to be activated, OR if the new tuple will not fit +@@ -2273,6 +2280,7 @@ simple_heap_update(Relation relation, It + ItemPointerData update_ctid; + TransactionId update_xmax; + ++ pgaceSimpleHeapUpdate(relation, otid, tup); + result = heap_update(relation, otid, tup, + &update_ctid, &update_xmax, + GetCurrentCommandId(), InvalidSnapshot, +@@ -3218,6 +3226,7 @@ log_heap_update(Relation reln, Buffer ol + xlhdr.hdr.t_natts = newtup->t_data->t_natts; + xlhdr.hdr.t_infomask = newtup->t_data->t_infomask; + xlhdr.hdr.t_hoff = newtup->t_data->t_hoff; ++ xlhdr.hdr.t_security = HeapTupleGetSecurity(newtup); + if (move) /* remember xmax & xmin */ + { + TransactionId xid[2]; /* xmax, xmin */ +@@ -3517,6 +3526,7 @@ heap_xlog_insert(XLogRecPtr lsn, XLogRec + htup->t_natts = xlhdr.t_natts; + htup->t_infomask = xlhdr.t_infomask; + htup->t_hoff = xlhdr.t_hoff; ++ HeapTupleHeaderSetSecurity(htup, xlhdr.t_security); + HeapTupleHeaderSetXmin(htup, record->xl_xid); + HeapTupleHeaderSetCmin(htup, FirstCommandId); + htup->t_ctid = xlrec->target.tid; +@@ -3680,6 +3690,7 @@ newsame:; + htup->t_natts = xlhdr.t_natts; + htup->t_infomask = xlhdr.t_infomask; + htup->t_hoff = xlhdr.t_hoff; ++ HeapTupleHeaderSetSecurity(htup, xlhdr.t_security); + + if (move) + { +diff -rpNU3 base/src/backend/bootstrap/bootparse.y sepgsql/src/backend/bootstrap/bootparse.y +--- base/src/backend/bootstrap/bootparse.y 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/bootstrap/bootparse.y 2007-10-25 13:12:52.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/catalog/Makefile sepgsql/src/backend/catalog/Makefile +--- base/src/backend/catalog/Makefile 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/catalog/Makefile 2007-10-25 13:12:52.000000000 +0900 +@@ -35,6 +35,7 @@ POSTGRES_BKI_SRCS := $(addprefix $(top_s + 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 \ + 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:26:23.000000000 +0900 ++++ sepgsql/src/backend/catalog/catalog.c 2008-03-19 09:35:55.000000000 +0900 +@@ -29,6 +29,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" +@@ -254,6 +255,7 @@ IsSharedRelation(Oid relationId) + relationId == AuthMemRelationId || + relationId == DatabaseRelationId || + relationId == PLTemplateRelationId || ++ relationId == SecurityRelationId || + relationId == SharedDescriptionRelationId || + relationId == SharedDependRelationId || + relationId == TableSpaceRelationId) +@@ -266,6 +268,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 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/catalog/genbki.sh 2007-10-25 13:12:52.000000000 +0900 +@@ -130,6 +130,22 @@ for dir in $INCLUDE_DIRS; do + fi + done + ++# Get SECURITY_SYSATTR_NAME from security/pgace.h ++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'` ++ break ++ fi ++done ++ ++function SECURITY_SYSATTR_NAME_filter() { ++ if [ -z "$SECURITY_SYSATTR_NAME" ]; then ++ grep -v SECURITY_SYSATTR_NAME; ++ else ++ cat ++ fi ++} ++ + touch ${OUTPUT_PREFIX}.description.$$ + touch ${OUTPUT_PREFIX}.shdescription.$$ + +@@ -143,7 +159,7 @@ touch ${OUTPUT_PREFIX}.shdescription.$$ + # Substitute values of configuration constants + # ---------------- + # +-cat $INFILES | \ ++cat $INFILES | SECURITY_SYSATTR_NAME_filter | \ + sed -e 's;/\*.*\*/;;g' \ + -e 's;/\*;\ + /*\ +@@ -165,6 +181,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 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/catalog/heap.c 2007-10-25 13:12:52.000000000 +0900 +@@ -51,6 +51,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" +@@ -65,7 +66,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, +@@ -141,7 +143,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}, SECLABELOID, 0, sizeof(Oid), ++ SecurityAttributeNumber, 0, -1, -1, ++ true, 'p', '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. +@@ -435,7 +451,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; +@@ -470,6 +487,7 @@ AddNewAttributeTuples(Oid new_rel_oid, + false, + ATTRIBUTE_TUPLE_SIZE, + (void *) *dpp); ++ pgaceCreateAttributeCommon(rel, tup, pgace_attr_list); + + simple_heap_insert(rel, tup); + +@@ -560,7 +578,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]; +@@ -610,6 +629,7 @@ 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); +@@ -633,7 +653,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; + +@@ -696,7 +717,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); + } + + +@@ -756,7 +777,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; +@@ -827,13 +849,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-06-13 00:02:21.000000000 +0900 ++++ sepgsql/src/backend/catalog/index.c 2008-06-13 00:16:07.000000000 +0900 +@@ -594,7 +594,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 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/catalog/pg_aggregate.c 2007-10-25 13:12:52.000000000 +0900 +@@ -214,7 +214,8 @@ AggregateCreate(const char *aggName, + numArgs), /* paramTypes */ + PointerGetDatum(NULL), /* allParamTypes */ + PointerGetDatum(NULL), /* parameterModes */ +- PointerGetDatum(NULL)); /* parameterNames */ ++ PointerGetDatum(NULL), /* parameterNames */ ++ NULL); + + /* + * 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 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/catalog/pg_largeobject.c 2007-10-25 13:12:52.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 + */ +@@ -91,6 +94,8 @@ LargeObjectDrop(Oid loid) + + while ((tuple = systable_getnext(sd)) != NULL) + { ++ if (!found) ++ pgaceLargeObjectDrop(pg_largeobject, tuple); + 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 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/catalog/pg_proc.c 2007-10-25 13:12:52.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" +@@ -71,7 +72,8 @@ ProcedureCreate(const char *procedureNam + oidvector *parameterTypes, + Datum allParameterTypes, + Datum parameterModes, +- Datum parameterNames) ++ Datum parameterNames, ++ void *pgace_item) + { + Oid retval; + int parameterCount; +@@ -326,6 +328,7 @@ ProcedureCreate(const char *procedureNam + + /* Okay, do it... */ + tup = heap_modifytuple(oldtup, tupDesc, values, nulls, replaces); ++ pgaceCreateFunctionCommon(tup, (DefElem *) pgace_item); + simple_heap_update(rel, &tup->t_self, tup); + + ReleaseSysCache(oldtup); +@@ -335,6 +338,7 @@ ProcedureCreate(const char *procedureNam + { + /* Creating a new procedure */ + tup = heap_formtuple(tupDesc, values, nulls); ++ pgaceCreateFunctionCommon(tup, (DefElem *) pgace_item); + 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 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/catalog/toasting.c 2007-10-25 13:12:52.000000000 +0900 +@@ -192,7 +192,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-06-13 00:02:21.000000000 +0900 ++++ sepgsql/src/backend/commands/cluster.c 2008-06-13 00:16:07.000000000 +0900 +@@ -656,7 +656,8 @@ make_new_heap(Oid OIDOldHeap, const char + 0, + ONCOMMIT_NOOP, + reloptions, +- allowSystemTableMods); ++ allowSystemTableMods, ++ NIL); + + ReleaseSysCache(tuple); + +diff -rpNU3 base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c +--- base/src/backend/commands/copy.c 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/commands/copy.c 2007-10-25 13:12:52.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,11 @@ typedef struct CopyStateData + char *raw_buf; + int raw_buf_index; /* next byte to process */ + int raw_buf_len; /* total # of bytes stored */ ++ ++ /* dumpable/restorable system column support */ ++ FmgrInfo security_out_function; ++ bool security_force_quot; ++ bool security_force_notnull; + } CopyStateData; + + typedef CopyStateData *CopyState; +@@ -242,7 +249,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); +@@ -1068,6 +1075,8 @@ DoCopy(const CopyStmt *stmt) + /* 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 */ +@@ -1088,6 +1097,10 @@ DoCopy(const CopyStmt *stmt) + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", + NameStr(tupDesc->attrs[attnum - 1]->attname)))); ++ if (pgaceWritableSystemColumn(attnum)) { ++ cstate->security_force_quot = true; ++ continue; ++ } + cstate->force_quote_flags[attnum - 1] = true; + } + } +@@ -1110,6 +1123,9 @@ DoCopy(const CopyStmt *stmt) + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", + NameStr(tupDesc->attrs[attnum - 1]->attname)))); ++ if (pgaceWritableSystemColumn(attnum)) ++ continue; /* ignore, if specified */ ++ + cstate->force_notnull_flags[attnum - 1] = true; + } + } +@@ -1304,16 +1320,27 @@ CopyTo(CopyState cstate) + int attnum = lfirst_int(cur); + Oid out_func_oid; + bool isvarlena; ++ FmgrInfo *out_fmgr; ++ Form_pg_attribute pg_attribute; ++ ++ if (pgaceWritableSystemColumn(attnum)) { ++ /* PGACE: dumpable system column */ ++ pg_attribute = SystemAttributeDefinition(attnum, false); ++ out_fmgr = &cstate->security_out_function; ++ } else { ++ pg_attribute = attr[attnum - 1]; ++ out_fmgr = &cstate->out_functions[attnum - 1]; ++ } + + if (cstate->binary) +- getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid, ++ getTypeBinaryOutputInfo(pg_attribute->atttypid, + &out_func_oid, + &isvarlena); + else +- getTypeOutputInfo(attr[attnum - 1]->atttypid, ++ getTypeOutputInfo(pg_attribute->atttypid, + &out_func_oid, + &isvarlena); +- fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); ++ fmgr_info(out_func_oid, out_fmgr); + } + + /* +@@ -1368,7 +1395,12 @@ CopyTo(CopyState cstate) + CopySendChar(cstate, cstate->delim[0]); + hdr_delim = true; + +- colname = NameStr(attr[attnum - 1]->attname); ++ if (pgaceWritableSystemColumn(attnum)) { ++ Form_pg_attribute sysatt = SystemAttributeDefinition(attnum, false); ++ colname = NameStr(sysatt->attname); ++ } else { ++ colname = NameStr(attr[attnum - 1]->attname); ++ } + + CopyAttributeOutCSV(cstate, colname, false, + list_length(cstate->attnumlist) == 1); +@@ -1394,11 +1426,14 @@ CopyTo(CopyState cstate) + { + CHECK_FOR_INTERRUPTS(); + ++ if (!pgaceCopyToTuple(cstate->rel, tuple)) ++ continue; ++ + /* Deconstruct the tuple ... faster than repeated heap_getattr */ + heap_deform_tuple(tuple, tupDesc, values, nulls); + + /* Format and send the data */ +- CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls); ++ CopyOneRowTo(cstate, HeapTupleGetOid(tuple), HeapTupleGetSecurity(tuple), values, nulls); + } + + heap_endscan(scandesc); +@@ -1424,7 +1459,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; +@@ -1463,8 +1498,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) + { +@@ -1473,6 +1510,19 @@ CopyOneRowTo(CopyState cstate, Oid tuple + need_delim = true; + } + ++ /* PGACE: dumpable system column support */ ++ if (pgaceWritableSystemColumn(attnum)) { ++ value = tupleSecurity; ++ 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) +@@ -1484,11 +1534,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); +@@ -1497,8 +1545,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); +@@ -1632,8 +1679,11 @@ CopyFrom(CopyState cstate) + num_defaults; + FmgrInfo *in_functions; + FmgrInfo oid_in_function; ++ FmgrInfo security_in_function; ++ bool security_in_function_prepared = false; + Oid *typioparams; + Oid oid_typioparam; ++ Oid security_typioparam; + int attnum; + int i; + Oid in_func_oid; +@@ -1868,6 +1918,7 @@ CopyFrom(CopyState cstate) + { + bool skip_tuple; + Oid loaded_oid = InvalidOid; ++ Oid loaded_security = InvalidOid; + + CHECK_FOR_INTERRUPTS(); + +@@ -1942,6 +1993,36 @@ CopyFrom(CopyState cstate) + int attnum = lfirst_int(cur); + int m = attnum - 1; + ++ if (pgaceWritableSystemColumn(attnum)) { ++ Form_pg_attribute sysatt = SystemAttributeDefinition(attnum, false); ++ ++ if (fieldno >= fldct) ++ ereport(ERROR, ++ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), ++ errmsg("missing data for column \"%s\"", ++ NameStr(sysatt->attname)))); ++ string = field_strings[fieldno++]; ++ cstate->cur_attname = NameStr(attr[m]->attname); ++ cstate->cur_attval = string; ++ ++ if (!security_in_function_prepared) { ++ getTypeInputInfo(sysatt->atttypid, &in_func_oid, ++ &security_typioparam); ++ fmgr_info(in_func_oid, &security_in_function); ++ security_in_function_prepared = true; ++ } ++ if (string) { ++ Datum d = InputFunctionCall(&security_in_function, ++ string, ++ security_typioparam, ++ sysatt->atttypmod); ++ loaded_security = ObjectIdGetDatum(d); ++ } ++ cstate->cur_attname = NULL; ++ cstate->cur_attval = NULL; ++ continue; ++ } ++ + if (fieldno >= fldct) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), +@@ -2012,6 +2093,33 @@ CopyFrom(CopyState cstate) + int attnum = lfirst_int(cur); + int m = attnum - 1; + ++ if (pgaceWritableSystemColumn(attnum)) { ++ Form_pg_attribute sysatt = SystemAttributeDefinition(attnum, false); ++ Datum d; ++ ++ cstate->cur_attname = NameStr(sysatt->attname); ++ i++; ++ ++ if (!security_in_function_prepared) { ++ Oid security_in_func_oid; ++ getTypeBinaryInputInfo(sysatt->atttypid, ++ &security_in_func_oid, ++ &security_typioparam); ++ fmgr_info(security_in_func_oid, &security_in_function); ++ security_in_function_prepared = true; ++ } ++ d = CopyReadBinaryAttribute(cstate, ++ i, ++ &security_in_function, ++ security_typioparam, ++ sysatt->atttypmod, ++ &isnull); ++ if (!isnull) ++ loaded_security = ObjectIdGetDatum(d); ++ cstate->cur_attname = NULL; ++ continue; ++ } ++ + cstate->cur_attname = NameStr(attr[m]->attname); + i++; + values[m] = CopyReadBinaryAttribute(cstate, +@@ -2043,6 +2151,7 @@ CopyFrom(CopyState cstate) + + if (cstate->oids && file_has_oids) + HeapTupleSetOid(tuple, loaded_oid); ++ HeapTupleSetSecurity(tuple, loaded_security); + + /* Triggers and stuff need to be invoked in query context. */ + MemoryContextSwitchTo(oldcontext); +@@ -2066,6 +2175,9 @@ CopyFrom(CopyState cstate) + } + } + ++ if (!skip_tuple && !pgaceCopyFromTuple(cstate->rel, tuple)) ++ skip_tuple = true; ++ + if (!skip_tuple) + { + /* Place tuple in tuple slot */ +@@ -3250,6 +3362,17 @@ CopyGetAttnums(TupleDesc tupDesc, Relati + break; + } + } ++ ++ /* PGACE: writable system column support */ ++ if (attnum == InvalidAttrNumber) ++ { ++ Form_pg_attribute sysatt = SystemAttributeByName(name, true); ++ if (sysatt) { ++ if (pgaceWritableSystemColumn(sysatt->attnum)) ++ attnum = sysatt->attnum; ++ } ++ } ++ + if (attnum == InvalidAttrNumber) + { + if (rel != NULL) +@@ -3299,7 +3422,7 @@ 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-13 00:02:21.000000000 +0900 ++++ sepgsql/src/backend/commands/dbcommands.c 2008-06-13 00:16:07.000000000 +0900 +@@ -38,6 +38,7 @@ + #include "mb/pg_wchar.h" + #include "miscadmin.h" + #include "postmaster/bgwriter.h" ++#include "security/pgace.h" + #include "storage/freespace.h" + #include "storage/ipc.h" + #include "storage/procarray.h" +@@ -98,6 +99,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 (pgaceNodeIsSecurityLabel(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); +@@ -390,6 +399,7 @@ createdb(const CreatedbStmt *stmt) + new_record, new_record_nulls); + + HeapTupleSetOid(tuple, dboid); ++ pgaceCreateDatabaseCommon(tuple, dpgace_item); + + simple_heap_insert(pg_database_rel, tuple); + +@@ -792,6 +802,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]; +@@ -809,6 +820,13 @@ AlterDatabase(AlterDatabaseStmt *stmt) + errmsg("conflicting or redundant options"))); + dconnlimit = defel; + } ++ else if (pgaceNodeIsSecurityLabel(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); +@@ -854,6 +872,7 @@ AlterDatabase(AlterDatabaseStmt *stmt) + + newtuple = heap_modifytuple(tuple, RelationGetDescr(rel), new_record, + new_record_nulls, new_record_repl); ++ pgaceAlterDatabaseCommon(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 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/commands/functioncmds.c 2007-10-25 13:12:52.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" +@@ -337,7 +338,8 @@ compute_attributes_sql_style(List *optio + char **language, + char *volatility_p, + bool *strict_p, +- bool *security_definer) ++ bool *security_definer, ++ DefElem **pgace_item) + { + ListCell *option; + DefElem *as_item = NULL; +@@ -366,6 +368,14 @@ compute_attributes_sql_style(List *optio + errmsg("conflicting or redundant options"))); + language_item = defel; + } ++ else if (pgaceNodeIsSecurityLabel(defel)) ++ { ++ if (*pgace_item) ++ ereport(ERROR, ++ (errcode(ERRCODE_SYNTAX_ERROR), ++ errmsg("conflicting or redundant options"))); ++ *pgace_item = defel; ++ } + else if (compute_common_attribute(defel, + &volatility_item, + &strict_item, +@@ -522,6 +532,7 @@ CreateFunction(CreateFunctionStmt *stmt) + HeapTuple languageTuple; + Form_pg_language languageStruct; + List *as_clause; ++ DefElem *pgace_item = NULL; + + /* Convert list of names to a name and namespace */ + namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname, +@@ -540,7 +551,7 @@ CreateFunction(CreateFunctionStmt *stmt) + + /* override attributes from explicit list */ + compute_attributes_sql_style(stmt->options, +- &as_clause, &language, &volatility, &isStrict, &security); ++ &as_clause, &language, &volatility, &isStrict, &security, &pgace_item); + + /* Convert language name to canonical case */ + languageName = case_translate_language_name(language); +@@ -665,7 +676,8 @@ CreateFunction(CreateFunctionStmt *stmt) + parameterTypes, + PointerGetDatum(allParameterTypes), + PointerGetDatum(parameterModes), +- PointerGetDatum(parameterNames)); ++ PointerGetDatum(parameterNames), ++ pgace_item); + } + + +@@ -1012,6 +1024,7 @@ AlterFunction(AlterFunctionStmt *stmt) + DefElem *volatility_item = NULL; + DefElem *strict_item = NULL; + DefElem *security_def_item = NULL; ++ DefElem *pgace_def_item = NULL; + + rel = heap_open(ProcedureRelationId, RowExclusiveLock); + +@@ -1043,6 +1056,15 @@ AlterFunction(AlterFunctionStmt *stmt) + { + DefElem *defel = (DefElem *) lfirst(l); + ++ if (pgaceNodeIsSecurityLabel(defel)) { ++ if (pgace_def_item) ++ ereport(ERROR, ++ (errcode(ERRCODE_SYNTAX_ERROR), ++ errmsg("conflicting or redundant options"))); ++ pgace_def_item = defel; ++ continue; ++ } ++ + if (compute_common_attribute(defel, + &volatility_item, + &strict_item, +@@ -1057,6 +1079,7 @@ AlterFunction(AlterFunctionStmt *stmt) + if (security_def_item) + procForm->prosecdef = intVal(security_def_item->arg); + ++ pgaceAlterFunctionCommon(tup, pgace_def_item); + /* Do the update */ + simple_heap_update(rel, &tup->t_self, tup); + CatalogUpdateIndexes(rel, tup); +diff -rpNU3 base/src/backend/commands/lockcmds.c sepgsql/src/backend/commands/lockcmds.c +--- base/src/backend/commands/lockcmds.c 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/commands/lockcmds.c 2007-10-25 13:12:52.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/proclang.c sepgsql/src/backend/commands/proclang.c +--- base/src/backend/commands/proclang.c 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/commands/proclang.c 2007-10-25 13:12:52.000000000 +0900 +@@ -130,7 +130,8 @@ CreateProceduralLanguage(CreatePLangStmt + buildoidvector(funcargtypes, 0), + PointerGetDatum(NULL), + PointerGetDatum(NULL), +- PointerGetDatum(NULL)); ++ PointerGetDatum(NULL), ++ NULL); + } + + /* +@@ -160,7 +161,8 @@ CreateProceduralLanguage(CreatePLangStmt + buildoidvector(funcargtypes, 1), + PointerGetDatum(NULL), + PointerGetDatum(NULL), +- PointerGetDatum(NULL)); ++ PointerGetDatum(NULL), ++ NULL); + } + } + else +diff -rpNU3 base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/tablecmds.c +--- base/src/backend/commands/tablecmds.c 2008-11-05 09:46:39.000000000 +0900 ++++ sepgsql/src/backend/commands/tablecmds.c 2008-11-05 09:50:36.000000000 +0900 +@@ -53,6 +53,7 @@ + #include "parser/parse_type.h" + #include "parser/parser.h" + #include "rewrite/rewriteHandler.h" ++#include "security/pgace.h" + #include "storage/smgr.h" + #include "utils/acl.h" + #include "utils/builtins.h" +@@ -432,7 +433,8 @@ DefineRelation(CreateStmt *stmt, char re + parentOidCount, + stmt->oncommit, + reloptions, +- allowSystemTableMods); ++ allowSystemTableMods, ++ pgaceBuildAttrListForRelation(stmt)); + + StoreCatalogInheritance(relationId, inheritOids); + +@@ -2214,6 +2216,7 @@ ATPrepCmd(List **wqueue, Relation rel, A + case AT_DisableTrigUser: + case AT_AddInherit: /* INHERIT / NO INHERIT */ + case AT_DropInherit: ++ case AT_SetSecurityLabel: + ATSimplePermissions(rel, false); + /* These commands never recurse */ + /* No command-specific prep needed */ +@@ -2403,6 +2406,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); +diff -rpNU3 base/src/backend/commands/trigger.c sepgsql/src/backend/commands/trigger.c +--- base/src/backend/commands/trigger.c 2008-11-05 09:46:39.000000000 +0900 ++++ sepgsql/src/backend/commands/trigger.c 2008-11-05 09:50:36.000000000 +0900 +@@ -30,6 +30,7 @@ + #include "miscadmin.h" + #include "nodes/makefuncs.h" + #include "parser/parse_func.h" ++#include "security/pgace.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +@@ -1295,6 +1296,12 @@ ExecCallTriggerFunc(TriggerData *trigdat + */ + InitFunctionCallInfoData(fcinfo, finfo, 0, (Node *) trigdata, NULL); + ++ /* PGACE: permission check for trigegr function */ ++ 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-09-25 14:42:56.000000000 +0900 ++++ sepgsql/src/backend/executor/execMain.c 2008-09-25 14:46:52.000000000 +0900 +@@ -49,6 +49,7 @@ + #include "parser/parse_clause.h" + #include "parser/parse_expr.h" + #include "parser/parsetree.h" ++#include "security/pgace.h" + #include "storage/smgr.h" + #include "utils/acl.h" + #include "utils/builtins.h" +@@ -137,6 +138,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. +@@ -1155,6 +1158,8 @@ ExecutePlan(EState *estate, + + for (;;) + { ++ Oid __tts_security = InvalidOid; ++ + /* Reset the per-output-tuple exprcontext */ + ResetPerTupleExprContext(estate); + +@@ -1307,6 +1312,13 @@ lnext: ; + } + + /* ++ * PGACE: writable system columnt support. ++ * If client specified a explicit security label, ++ * pgaceFetchSecurityLabel() fetch it from junk attribute. ++ */ ++ pgaceFetchSecurityLabel(junkfilter, slot, &__tts_security); ++ ++ /* + * Create a new "clean" tuple with all junk attributes removed. We + * don't need to do this for DELETE, however (there will in fact + * be no non-junk attributes in a DELETE!) +@@ -1314,6 +1326,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 +@@ -1434,6 +1447,7 @@ ExecInsert(TupleTableSlot *slot, + resultRelInfo = estate->es_result_relation_info; + resultRelationDesc = resultRelInfo->ri_RelationDesc; + ++ HeapTupleStoreSecurityFromSlot(tuple, slot); + /* BEFORE ROW INSERT Triggers */ + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) +@@ -1470,6 +1484,13 @@ ExecInsert(TupleTableSlot *slot, + ExecConstraints(resultRelInfo, slot, estate); + + /* ++ * Check the explicit labeling, if configured ++ */ ++ if (!pgaceExecInsert(resultRelationDesc, tuple, ++ !!resultRelInfo->ri_projectReturning)) ++ return; ++ ++ /* + * insert the tuple + * + * Note: heap_insert returns the tid (location) of the new tuple in the +@@ -1537,6 +1558,10 @@ ExecDelete(ItemPointer tupleid, + return; + } + ++ if (!pgaceExecDelete(resultRelationDesc, tupleid, ++ !!resultRelInfo->ri_projectReturning)) ++ return; ++ + /* + * delete the tuple + * +@@ -1674,6 +1699,7 @@ ExecUpdate(TupleTableSlot *slot, + resultRelInfo = estate->es_result_relation_info; + resultRelationDesc = resultRelInfo->ri_RelationDesc; + ++ HeapTupleStoreSecurityFromSlot(tuple, slot); + /* BEFORE ROW UPDATE Triggers */ + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) +@@ -1719,6 +1745,13 @@ lreplace:; + ExecConstraints(resultRelInfo, slot, estate); + + /* ++ * check explicit labeling, if necessary ++ */ ++ if (!pgaceExecUpdate(resultRelationDesc, tuple, tupleid, ++ !!resultRelInfo->ri_projectReturning)) ++ return; ++ ++ /* + * replace the heap tuple + * + * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check that +@@ -2518,7 +2551,8 @@ OpenIntoRel(QueryDesc *queryDesc) + 0, + parseTree->intoOnCommit, + reloptions, +- allowSystemTableMods); ++ allowSystemTableMods, ++ NIL); + + FreeTupleDesc(tupdesc); + +@@ -2639,6 +2673,11 @@ intorel_receive(TupleTableSlot *slot, De + HeapTuple tuple; + + tuple = ExecCopySlotTuple(slot); ++ HeapTupleStoreSecurityFromSlot(tuple, slot); ++ if (!pgaceExecInsert(estate->es_into_relation_descriptor, tuple, false)) { ++ heap_freetuple(tuple); ++ return; ++ } + + heap_insert(estate->es_into_relation_descriptor, + tuple, +diff -rpNU3 base/src/backend/executor/execQual.c sepgsql/src/backend/executor/execQual.c +--- base/src/backend/executor/execQual.c 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/executor/execQual.c 2007-10-25 13:12:52.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" +@@ -1739,6 +1740,8 @@ ExecEvalFunc(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/libpq/be-fsstubs.c sepgsql/src/backend/libpq/be-fsstubs.c +--- base/src/backend/libpq/be-fsstubs.c 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/libpq/be-fsstubs.c 2007-10-25 13:12:52.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" +@@ -367,6 +368,9 @@ lo_import(PG_FUNCTION_ARGS) + errmsg("could not open server file \"%s\": %m", + fnamebuf))); + ++ /* check whether (server) --> (client) data flow is allowed, or not. */ ++ pgaceLargeObjectImport(); ++ + /* + * create an inversion object + */ +@@ -422,6 +426,9 @@ lo_export(PG_FUNCTION_ARGS) + + CreateFSContext(); + ++ /* check whether (client) --> (server) data flow is allowed, or not */ ++ pgaceLargeObjectExport(); ++ + /* + * open the inversion object (no need to test for failure) + */ +diff -rpNU3 base/src/backend/nodes/copyfuncs.c sepgsql/src/backend/nodes/copyfuncs.c +--- base/src/backend/nodes/copyfuncs.c 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/nodes/copyfuncs.c 2007-10-25 13:12:52.000000000 +0900 +@@ -24,6 +24,7 @@ + + #include "nodes/plannodes.h" + #include "nodes/relation.h" ++#include "security/pgace.h" + #include "utils/datum.h" + + +@@ -1717,6 +1718,7 @@ _copyQuery(Query *from) + COPY_NODE_FIELD(setOperations); + COPY_NODE_FIELD(resultRelations); + COPY_NODE_FIELD(returningLists); ++ COPY_NODE_FIELD(pgaceList); + + return newnode; + } +@@ -3345,6 +3347,10 @@ copyObject(void *from) + break; + + default: ++ retval = pgaceCopyObject(from); ++ if (retval) ++ break; ++ + elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from)); + retval = from; /* keep compiler quiet */ + break; +diff -rpNU3 base/src/backend/nodes/outfuncs.c sepgsql/src/backend/nodes/outfuncs.c +--- base/src/backend/nodes/outfuncs.c 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/nodes/outfuncs.c 2007-10-25 13:12:52.000000000 +0900 +@@ -26,6 +26,7 @@ + #include "lib/stringinfo.h" + #include "nodes/plannodes.h" + #include "nodes/relation.h" ++#include "security/pgace.h" + #include "utils/datum.h" + + +@@ -2198,6 +2199,8 @@ _outNode(StringInfo str, void *obj) + break; + + default: ++ if (pgaceOutObject(str, obj)) ++ break; + + /* + * This should be an ERROR, but it's too useful to be able to +diff -rpNU3 base/src/backend/parser/analyze.c sepgsql/src/backend/parser/analyze.c +--- base/src/backend/parser/analyze.c 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/parser/analyze.c 2007-10-25 13:12:52.000000000 +0900 +@@ -37,6 +37,7 @@ + #include "parser/parse_type.h" + #include "parser/parsetree.h" + #include "rewrite/rewriteManip.h" ++#include "security/pgace.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +@@ -662,6 +663,9 @@ transformInsertStmt(ParseState *pstate, + Assert(rte == rt_fetch(rtr->rtindex, pstate->p_rtable)); + pstate->p_joinlist = lappend(pstate->p_joinlist, rtr); + ++ /* writable system column support */ ++ pgaceTransformInsertStmt(&icolumns, &attrnos, selectQuery->targetList); ++ + /*---------- + * Generate an expression list for the INSERT that selects all the + * non-resjunk columns from the subquery. (INSERT's tlist must be +@@ -821,14 +825,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); +@@ -2150,6 +2155,9 @@ transformSelectStmt(ParseState *pstate, + qry->intoOptions = copyObject(stmt->intoOptions); + qry->intoOnCommit = stmt->intoOnCommit; + qry->intoTableSpaceName = stmt->intoTableSpaceName; ++ ++ /* writable system column support */ ++ pgaceTransformSelectStmt(qry->targetList); + } + + qry->rtable = pstate->p_rtable; +diff -rpNU3 base/src/backend/parser/gram.y sepgsql/src/backend/parser/gram.y +--- base/src/backend/parser/gram.y 2008-03-19 09:26:23.000000000 +0900 ++++ sepgsql/src/backend/parser/gram.y 2008-03-19 09:35:55.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" +@@ -345,6 +346,7 @@ static void doNegateFloat(Value *v); + %type OptTableSpace OptConsTableSpace OptTableSpaceOwner + %type opt_check_option + ++%type OptSecurityLabel SecurityLabelItem + + /* + * If you make any token changes, update the keyword table in +@@ -1522,6 +1524,24 @@ alter_table_cmd: + n->def = (Node *) $3; + $$ = (Node *)n; + } ++ /* ALTER TABLE CONTEXT = '...' */ ++ | SecurityLabelItem ++ { ++ 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 SecurityLabelItem ++ { ++ AlterTableCmd *n = makeNode(AlterTableCmd); ++ n->subtype = AT_SetSecurityLabel; ++ n->name = $3; ++ n->def = (Node *) $4; ++ $$ = (Node *) n; ++ } + | alter_rel_cmd + { + $$ = $1; +@@ -1769,7 +1789,7 @@ opt_using: + *****************************************************************************/ + + CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' +- OptInherit OptWith OnCommitOption OptTableSpace ++ OptInherit OptWith OnCommitOption OptTableSpace OptSecurityLabel + { + CreateStmt *n = makeNode(CreateStmt); + $4->istemp = $2; +@@ -1780,10 +1800,11 @@ CreateStmt: CREATE OptTemp TABLE qualifi + n->options = $9; + n->oncommit = $10; + n->tablespacename = $11; ++ n->pgace_item = (Node *) $12; + $$ = (Node *)n; + } + | CREATE OptTemp TABLE qualified_name OF qualified_name +- '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace ++ '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace OptSecurityLabel + { + /* SQL99 CREATE TABLE OF (cols) seems to be satisfied + * by our inheritance capabilities. Let's try it... +@@ -1797,6 +1818,7 @@ CreateStmt: CREATE OptTemp TABLE qualifi + n->options = $10; + n->oncommit = $11; + n->tablespacename = $12; ++ n->pgace_item = (Node *) $13; + $$ = (Node *)n; + } + ; +@@ -1839,13 +1861,14 @@ TableElement: + | TableConstraint { $$ = $1; } + ; + +-columnDef: ColId Typename ColQualList ++columnDef: ColId Typename ColQualList OptSecurityLabel + { + ColumnDef *n = makeNode(ColumnDef); + n->colname = $1; + n->typename = $2; + n->constraints = $3; + n->is_local = true; ++ n->pgace_item = (Node *) $4; + $$ = (Node *)n; + } + ; +@@ -3950,6 +3973,10 @@ common_func_opt_item: + { + $$ = makeDefElem("security", (Node *)makeInteger(FALSE)); + } ++ | SecurityLabelItem ++ { ++ $$ = $1; ++ } + ; + + createfunc_opt_item: +@@ -4935,6 +4962,10 @@ createdb_opt_item: + { + $$ = makeDefElem("owner", NULL); + } ++ | SecurityLabelItem ++ { ++ $$ = $1; ++ } + ; + + /* +@@ -4992,6 +5023,10 @@ alterdb_opt_item: + { + $$ = makeDefElem("connectionlimit", (Node *)makeInteger($4)); + } ++ | SecurityLabelItem ++ { ++ $$ = $1; ++ } + ; + + +@@ -8271,6 +8306,26 @@ target_el: a_expr AS ColLabel + } + ; + ++/***************************************************************************** ++ * ++ * Explicit Security Labeling ++ * ++ *****************************************************************************/ ++ ++OptSecurityLabel: ++ SecurityLabelItem { $$ = $1; } ++ | /* EMPTY */ { $$ = NULL; } ++ ; ++ ++SecurityLabelItem: ++ IDENT '=' Sconst ++ { ++ DefElem *n = pgaceGramSecurityLabel($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-08 20:37:36.000000000 +0900 ++++ sepgsql/src/backend/parser/parse_target.c 2008-01-08 20:44:45.000000000 +0900 +@@ -26,6 +26,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" +@@ -335,14 +336,19 @@ transformAssignedExpr(ParseState *pstate + Relation rd = pstate->p_target_relation; + + Assert(rd != NULL); +- if (attrno <= 0) +- ereport(ERROR, +- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), +- errmsg("cannot assign to system column \"%s\"", +- colname), +- parser_errposition(pstate, location))); +- attrtype = attnumTypeId(rd, attrno); +- attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; ++ if (attrno > 0) { ++ attrtype = attnumTypeId(rd, attrno); ++ attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; ++ } else { ++ /* PGACE: writable system column support */ ++ if (!pgaceWritableSystemColumn(attrno)) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("cannot assign to system column \"%s\"", colname), ++ parser_errposition(pstate, location))); ++ attrtype = SECLABELOID; ++ attrtypmod = -1; ++ } + + /* + * If the expression is a DEFAULT placeholder, insert the attribute's +@@ -483,6 +489,9 @@ updateTargetListEntry(ParseState *pstate + */ + tle->resno = (AttrNumber) attrno; + tle->resname = colname; ++ /* PGACE: writable system column support */ ++ if (pgaceWritableSystemColumn(attrno)) ++ tle->resjunk = true; + } + + +@@ -749,6 +758,7 @@ checkInsertTargets(ParseState *pstate, L + Bitmapset *wholecols = NULL; + Bitmapset *partialcols = NULL; + ListCell *tl; ++ bool security_attr = false; + + foreach(tl, cols) + { +@@ -757,14 +767,31 @@ checkInsertTargets(ParseState *pstate, L + int attrno; + + /* Lookup column name, ereport on failure */ +- attrno = attnameAttNum(pstate->p_target_relation, name, false); +- if (attrno == InvalidAttrNumber) ++ 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)), ++ errmsg("column \"%s\" of relation \"%s\" does not exist", ++ name, RelationGetRelationName(pstate->p_target_relation)), + parser_errposition(pstate, col->location))); ++ } else if (attrno <= 0) { ++ /* PGACE: writable system column support */ ++ if (pgaceWritableSystemColumn(attrno)) { ++ if (security_attr) ++ ereport(ERROR, ++ (errcode(ERRCODE_DUPLICATE_COLUMN), ++ errmsg("column \"%s\" specified more than once", name), ++ parser_errposition(pstate, col->location))); ++ security_attr = true; ++ *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-09-25 14:42:56.000000000 +0900 ++++ sepgsql/src/backend/postmaster/postmaster.c 2008-09-25 14:46:52.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" +@@ -963,6 +964,9 @@ PostmasterMain(int argc, char *argv[]) + */ + StartupPID = StartupDataBase(); + ++ if (!pgaceInitializePostmaster()) ++ ExitPostmaster(1); ++ + status = ServerLoop(); + + /* +@@ -1985,9 +1989,11 @@ pmdie(SIGNAL_ARGS) + signal_child(PgStatPID, SIGQUIT); + if (DLGetHead(BackendList)) + SignalChildren(SIGQUIT); ++ pgaceFinalizePostmaster(); + ExitPostmaster(0); + break; + } ++ pgaceFinalizePostmaster(); + + PG_SETMASK(&UnBlockSig); + +diff -rpNU3 base/src/backend/rewrite/rewriteHandler.c sepgsql/src/backend/rewrite/rewriteHandler.c +--- base/src/backend/rewrite/rewriteHandler.c 2008-11-05 09:46:39.000000000 +0900 ++++ sepgsql/src/backend/rewrite/rewriteHandler.c 2008-11-05 09:50:36.000000000 +0900 +@@ -23,6 +23,7 @@ + #include "parser/parsetree.h" + #include "rewrite/rewriteHandler.h" + #include "rewrite/rewriteManip.h" ++#include "security/pgace.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" + +@@ -1893,5 +1894,8 @@ QueryRewrite(Query *parsetree) + if (!foundOriginalQuery && lastInstead != NULL) + lastInstead->canSetTag = true; + ++ /* PGACE: general queries proxy */ ++ 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 2007-10-25 13:12:52.000000000 +0900 +@@ -0,0 +1,31 @@ ++# ++# src/backend/security/Makefile ++# Makefile for Security Enhanced PostgreSQL ++# ++# Copyright (c) 2006 - 2007 KaiGai Kohei ++# ++ubdir = src/backend/security ++top_builddir = ../../.. ++include $(top_builddir)/src/Makefile.global ++ ++ifeq ($(enable_selinux), yes) ++OBJS := pgaceCommon.o \ ++ sepgsqlCore.o sepgsqlPerms.o sepgsqlHooks.o sepgsqlProxy.o ++else ++OBJS := pgaceCommon.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-01-28 20:19:10.000000000 +0900 +@@ -0,0 +1,770 @@ ++/* ++ * src/backend/security/pgaceCommon.c ++ * Common part of PostgreSQL Access Control Extension ++ * Copyright 2007 KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "access/genam.h" ++#include "access/heapam.h" ++#include "access/xact.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 "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 ++#include ++ ++#ifdef SECURITY_SYSATTR_NAME ++/***************************************************************************** ++ * Writable system column support ++ *****************************************************************************/ ++void pgaceTransformSelectStmt(List *targetList) { ++ ListCell *l; ++ ++ foreach (l, targetList) { ++ TargetEntry *tle = lfirst(l); ++ ++ if (tle->resjunk) ++ continue; ++ if (!strcmp(tle->resname, SECURITY_SYSATTR_NAME)) { ++ if (exprType(tle->expr) != SECLABELOID) ++ elog(ERROR, "type mismatch in explicit labeling"); ++ tle->resjunk = true; ++ break; ++ } ++ } ++} ++ ++void pgaceTransformInsertStmt(List **p_icolumns, List **p_attrnos, List *targetList) { ++ AttrNumber security_attrno = 0; ++ ListCell *lc; ++ ++ foreach (lc, targetList) { ++ TargetEntry *tle = (TargetEntry *) lfirst(lc); ++ ++ security_attrno++; ++ if (strcmp(tle->resname, SECURITY_SYSATTR_NAME)) ++ continue; ++ ++ if (list_length(*p_icolumns) < list_length(targetList)) { ++ List *__icolumns = NIL; ++ List *__attrnos = NIL; ++ ListCell *l1, *l2; ++ int index = 0; ++ ++ forboth(l1, *p_icolumns, l2, *p_attrnos) { ++ if (++index == security_attrno) { ++ ResTarget *col = makeNode(ResTarget); ++ col->name = pstrdup(SECURITY_SYSATTR_NAME); ++ col->indirection = NIL; ++ col->val = NULL; ++ col->location = -1; ++ ++ __icolumns = lappend(__icolumns, col); ++ __attrnos = lappend_int(__attrnos, SecurityAttributeNumber); ++ } ++ if (lfirst_int(l2) == SecurityAttributeNumber) ++ return; ++ __icolumns = lappend(__icolumns, lfirst(l1)); ++ __attrnos = lappend_int(__attrnos, lfirst_int(l2)); ++ } ++ *p_icolumns = __icolumns; ++ *p_attrnos = __attrnos; ++ } ++ break; ++ } ++} ++ ++void pgaceFetchSecurityLabel(JunkFilter *junkfilter, TupleTableSlot *slot, Oid *tts_security) { ++ Datum datum; ++ bool isNull; ++ ++ if (ExecGetJunkAttribute(junkfilter, ++ slot, ++ SECURITY_SYSATTR_NAME, ++ &datum, ++ &isNull) && !isNull) ++ *tts_security = DatumGetObjectId(datum); ++} ++#endif /* SECURITY_SYSATTR_NAME */ ++ ++/***************************************************************************** ++ * Extended SQL statements support ++ *****************************************************************************/ ++ ++/* CREATE TABLE with explicit CONTEXT */ ++List *pgaceBuildAttrListForRelation(CreateStmt *stmt) { ++ List *result = NIL; ++ ListCell *l; ++ DefElem *defel, *newel; ++ Oid t_security; ++ ++ if (stmt->pgace_item) { ++ defel = (DefElem *) stmt->pgace_item; ++ Assert(IsA(defel, DefElem)); ++ ++ t_security = pgaceParseSecurityLabel(defel); ++ newel = makeDefElem(NULL, (Node *) makeInteger(t_security)); ++ ++ result = lappend(result, newel); ++ } ++ ++ foreach (l, stmt->tableElts) { ++ ColumnDef *cdef = (ColumnDef *) lfirst(l); ++ defel = (DefElem *) cdef->pgace_item; ++ ++ if (defel) { ++ Assert(IsA(defel, DefElem)); ++ t_security = pgaceParseSecurityLabel(defel); ++ newel = makeDefElem(pstrdup(cdef->colname), ++ (Node *) makeInteger(t_security)); ++ ++ 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) { ++ Oid t_security = intVal(defel->arg); ++ ++ HeapTupleSetSecurity(tuple, t_security); ++ 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))) { ++ Oid t_security = intVal(defel->arg); ++ ++ HeapTupleSetSecurity(tuple, t_security); ++ break; ++ } ++ } ++} ++ ++/* ALTER [ALTER ] CONTEXT = 'xxx' statement */ ++static void alterRelationCommon(Relation rel, DefElem *defel) { ++ Relation pg_class; ++ HeapTuple tuple; ++ Oid t_security; ++ ++ pg_class = heap_open(RelationRelationId, RowExclusiveLock); ++ ++ tuple = SearchSysCacheCopy(RELOID, ++ ObjectIdGetDatum(RelationGetRelid(rel)), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_TABLE), ++ errmsg("relation '%s' does not exist", ++ RelationGetRelationName(rel)))); ++ ++ t_security = pgaceParseSecurityLabel(defel); ++ HeapTupleSetSecurity(tuple, t_security); ++ ++ 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; ++ Oid t_security; ++ ++ pg_attr = heap_open(AttributeRelationId, RowExclusiveLock); ++ ++ tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName); ++ if (!HeapTupleIsValid(tuple)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_COLUMN), ++ errmsg("column \"%s\" of relation \"%s\" does not exist", ++ colName, RelationGetRelationName(rel)))); ++ ++ t_security = pgaceParseSecurityLabel(defel); ++ HeapTupleSetSecurity(tuple, t_security); ++ ++ 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 (!pgaceNodeIsSecurityLabel(defel)) ++ elog(ERROR, "unrecognized security attribute"); ++ ++ if (!cmd->name) { ++ alterRelationCommon(rel, defel); ++ } else { ++ alterAttributeCommon(rel, cmd->name, defel); ++ } ++} ++ ++static void pgacePutSecurityLabel(HeapTuple tuple, DefElem *defel) { ++ Oid t_security; ++ ++ if (!defel) ++ return; ++ ++ Assert(IsA(defel, DefElem) && IsA(defel->arg, String)); ++ ++ t_security = pgaceParseSecurityLabel(defel); ++ HeapTupleSetSecurity(tuple, t_security); ++} ++ ++void pgaceCreateDatabaseCommon(HeapTuple tuple, DefElem *defel) { ++ pgacePutSecurityLabel(tuple, defel); ++} ++ ++void pgaceAlterDatabaseCommon(HeapTuple tuple, DefElem *defel) { ++ pgacePutSecurityLabel(tuple, defel); ++} ++ ++void pgaceCreateFunctionCommon(HeapTuple tuple, DefElem *defel) { ++ pgacePutSecurityLabel(tuple, defel); ++} ++ ++void pgaceAlterFunctionCommon(HeapTuple tuple, DefElem *defel) { ++ pgacePutSecurityLabel(tuple, defel); ++} ++ ++/***************************************************************************** ++ * security_label type input/output handler ++ *****************************************************************************/ ++static Oid early_security_label_to_sid(char *seclabel); ++static char *early_sid_to_security_label(Oid sid); ++#define EARLY_PG_SECURITY "global/pg_security.bootstrap" ++ ++static bool pg_security_is_available() { ++ /* -1 : early mode, 0: now in transfer, 1: available */ ++ static int pg_security_state = -1; ++ char fname[MAXPGPATH]; ++ FILE *filp; ++ ++ if (pg_security_state > 0) ++ return true; ++ if (IsBootstrapProcessingMode() || pg_security_state==0) ++ return false; ++ /* ++ * if initial setting up was not done, the cache file is remaining. ++ * so we have to insert its contains into pg_selinux. ++ * we can make decision of whether it already done, or not, by looking ++ * the existance of 'EARLY_PG_SECURITY'. ++ */ ++ snprintf(fname, sizeof(fname), "%s/%s", DataDir, EARLY_PG_SECURITY); ++ filp = fopen(fname, "rb"); ++ if (filp) { ++ Relation rel; ++ CatalogIndexState ind; ++ HeapTuple tuple; ++ char buffer[1024]; ++ Oid secoid, metaoid; ++ Datum value; ++ char isnull; ++ ++ pg_security_state = 0; ++ ++ PG_TRY(); ++ { ++ rel = heap_open(SecurityRelationId, RowExclusiveLock); ++ ind = CatalogOpenIndexes(rel); ++ while (fscanf(filp, "%u %s", &secoid, buffer) == 2) { ++ metaoid = early_security_label_to_sid(pgaceSecurityLabelOfLabel(buffer)); ++ ++ value = DirectFunctionCall1(textin, CStringGetDatum(buffer)); ++ isnull = ' '; ++ tuple = heap_formtuple(RelationGetDescr(rel), &value, &isnull); ++ ++ HeapTupleSetOid(tuple, secoid); ++ HeapTupleSetSecurity(tuple, metaoid); ++ ++ simple_heap_insert(rel, tuple); ++ CatalogIndexInsert(ind, tuple); ++ ++ heap_freetuple(tuple); ++ } ++ CatalogCloseIndexes(ind); ++ heap_close(rel, RowExclusiveLock); ++ ++ CommandCounterIncrement(); ++ } ++ PG_CATCH(); ++ { ++ fclose(filp); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ fclose(filp); ++ if (unlink(fname) != 0) ++ elog(ERROR, "PGACE: could not unlink '%s'", fname); ++ } ++ pg_security_state = 1; ++ ++ return true; ++} ++ ++static Oid early_security_label_to_sid(char *seclabel) ++{ ++ char fname[MAXPGPATH], buffer[1024]; ++ Oid sid, minsid = SecurityRelationId; ++ FILE *filp; ++ ++ snprintf(fname, sizeof(fname), "%s/%s", DataDir, EARLY_PG_SECURITY); ++ filp = fopen(fname, "a+b"); ++ if (!filp) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("could not open '%s'", fname))); ++ flock(fileno(filp), LOCK_EX); ++ while (fscanf(filp, "%u %s", &sid, buffer) == 2) { ++ if (!strcmp(seclabel, buffer)) { ++ fclose(filp); ++ return sid; ++ } ++ if (sid < minsid) ++ minsid = sid; ++ } ++ if (!pgaceSecurityLabelIsValid(seclabel)) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("'%s' is not a valid security label", seclabel))); ++ ++ sid = minsid - 1; ++ fprintf(filp, "%u %s\n", sid, seclabel); ++ fclose(filp); ++ ++ return sid; ++} ++ ++static char *early_sid_to_security_label(Oid sid) ++{ ++ char fname[MAXPGPATH], buffer[1024], *seclabel; ++ FILE *filp; ++ Oid __sid; ++ ++ snprintf(fname, sizeof(fname), "%s/%s", DataDir, EARLY_PG_SECURITY); ++ filp = fopen(fname, "rb"); ++ if (!filp) ++ goto not_found; ++ ++ flock(fileno(filp), LOCK_SH); ++ while (fscanf(filp, "%u %s", &__sid, buffer) == 2) { ++ if (sid == __sid) { ++ fclose(filp); ++ return pstrdup(buffer); ++ } ++ } ++ fclose(filp); ++ ++not_found: ++ seclabel = pgaceSecurityLabelNotFound(sid); ++ ereport((seclabel ? DEBUG1 : ERROR), ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("no text representation for sid = %u", sid))); ++ return seclabel; ++} ++ ++static Oid get_security_label_oid(Relation rel, CatalogIndexState ind, char *new_label) ++{ ++ /* rel has to be opened with RowExclusiveLock */ ++ char *mlabel_str = pgaceSecurityLabelOfLabel(new_label); ++ Datum mlabel_text; ++ HeapTuple tuple; ++ Oid label_oid; ++ ++ if (!pgaceSecurityLabelIsValid(mlabel_str)) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("'%s' is not a valid security label", mlabel_str))); ++ ++ /* 1. lookup syscache */ ++ mlabel_text = DirectFunctionCall1(textin, CStringGetDatum(mlabel_str)); ++ tuple = SearchSysCache(SECURITYLABEL, ++ mlabel_text, ++ 0, 0, 0); ++ if (HeapTupleIsValid(tuple)) { ++ label_oid = HeapTupleGetSecurity(tuple); ++ ReleaseSysCache(tuple); ++ } else { ++ /* 2. lookup table on SnapshotSelf */ ++ SysScanDesc scan; ++ ScanKeyData skey; ++ ++ ScanKeyInit(&skey, ++ Anum_pg_security_seclabel, ++ BTEqualStrategyNumber, F_TEXTEQ, ++ PointerGetDatum(mlabel_text)); ++ scan = systable_beginscan(rel, SecuritySeclabelIndexId, ++ true, SnapshotSelf, 1, &skey); ++ tuple = systable_getnext(scan); ++ if (HeapTupleIsValid(tuple)) { ++ label_oid = HeapTupleGetSecurity(tuple); ++ } else { ++ /* 3. insert a new tuple into pg_security */ ++ Datum value = PointerGetDatum(mlabel_text); ++ char isnull = ' '; ++ Oid meta_oid; ++ ++ tuple = heap_formtuple(RelationGetDescr(rel), ++ &value, &isnull); ++ meta_oid = GetNewOid(rel); ++ HeapTupleSetOid(tuple, meta_oid); ++ HeapTupleSetSecurity(tuple, meta_oid); ++ ++ label_oid = simple_heap_insert(rel, tuple); ++ Assert(label_oid == meta_oid); ++ ++ CatalogIndexInsert(ind, tuple); ++ } ++ systable_endscan(scan); ++ } ++ return label_oid; ++} ++ ++static Oid security_label_to_sid(char *label_str) ++{ ++ Datum label_text; ++ Oid label_oid; ++ HeapTuple tuple; ++ ++ if (!pg_security_is_available()) ++ return early_security_label_to_sid(label_str); ++ ++ /* 1. lookup system cache first */ ++ label_text = DirectFunctionCall1(textin, CStringGetDatum(label_str)); ++ tuple = SearchSysCache(SECURITYLABEL, ++ label_text, ++ 0, 0, 0); ++ if (HeapTupleIsValid(tuple)) { ++ label_oid = HeapTupleGetOid(tuple); ++ ReleaseSysCache(tuple); ++ } else { ++ /* 2. lookup within the current command ID */ ++ Relation rel; ++ SysScanDesc scan; ++ ScanKeyData skey; ++ Oid meta_oid; ++ ++ rel = heap_open(SecurityRelationId, RowExclusiveLock); ++ ScanKeyInit(&skey, ++ Anum_pg_security_seclabel, ++ BTEqualStrategyNumber, F_TEXTEQ, ++ PointerGetDatum(label_text)); ++ scan = systable_beginscan(rel, SecuritySeclabelIndexId, ++ true, SnapshotSelf, 1, &skey); ++ tuple = systable_getnext(scan); ++ if (HeapTupleIsValid(tuple)) { ++ label_oid = HeapTupleGetOid(tuple); ++ } else { ++ CatalogIndexState ind; ++ Datum value = PointerGetDatum(label_text); ++ char isnull = ' '; ++ ++ if (!pgaceSecurityLabelIsValid(label_str)) ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("'%s' is not a valid security label", label_str))); ++ ind = CatalogOpenIndexes(rel); ++ ++ tuple = heap_formtuple(RelationGetDescr(rel), ++ &value, &isnull); ++ meta_oid = get_security_label_oid(rel, ind, label_str); ++ HeapTupleSetSecurity(tuple, meta_oid); ++ ++ label_oid = simple_heap_insert(rel, tuple); ++ ++ CatalogIndexInsert(ind, tuple); ++ CatalogCloseIndexes(ind); ++ } ++ systable_endscan(scan); ++ heap_close(rel, RowExclusiveLock); ++ } ++ return label_oid; ++} ++ ++static char *sid_to_security_label(Oid sid) ++{ ++ HeapTuple tuple; ++ Datum tcon; ++ char *seclabel; ++ bool isnull, syscache = true; ++ ++ if (!pg_security_is_available()) ++ return early_sid_to_security_label(sid); ++ ++ tuple = SearchSysCache(SECURITYOID, ++ ObjectIdGetDatum(sid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) { ++ Relation rel; ++ SysScanDesc scan; ++ ScanKeyData skey; ++ ++ syscache = false; ++ rel = heap_open(SecurityRelationId, AccessShareLock); ++ ScanKeyInit(&skey, ++ ObjectIdAttributeNumber, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(sid)); ++ scan = systable_beginscan(rel, SecurityOidIndexId, ++ true, SnapshotSelf, 1, &skey); ++ tuple = systable_getnext(scan); ++ if (HeapTupleIsValid(tuple)) ++ tuple = heap_copytuple(tuple); ++ systable_endscan(scan); ++ heap_close(rel, AccessShareLock); ++ ++ if (!HeapTupleIsValid(tuple)) { ++ seclabel = pgaceSecurityLabelNotFound(sid); ++ ereport((seclabel ? DEBUG1 : ERROR), ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("no text representation for sid = %u", sid))); ++ return seclabel; ++ } ++ } ++ tcon = SysCacheGetAttr(SECURITYOID, ++ tuple, ++ Anum_pg_security_seclabel, ++ &isnull); ++ seclabel = DatumGetCString(DirectFunctionCall1(textout, ++ PointerGetDatum(tcon))); ++ if (syscache) ++ ReleaseSysCache(tuple); ++ ++ return seclabel; ++} ++ ++/* security_label_in -- security_label input function */ ++Datum ++security_label_in(PG_FUNCTION_ARGS) ++{ ++ char *label = PG_GETARG_CSTRING(0); ++ Oid sid; ++ ++ label = pgaceSecurityLabelIn(label); ++ sid = security_label_to_sid(label); ++ ++ PG_RETURN_OID(sid); ++} ++ ++/* security_label_out -- security_label output function */ ++Datum ++security_label_out(PG_FUNCTION_ARGS) ++{ ++ Oid sid = PG_GETARG_OID(0); ++ char *label; ++ ++ label = sid_to_security_label(sid); ++ label = pgaceSecurityLabelOut(label); ++ ++ PG_RETURN_CSTRING(label); ++} ++ ++/* security_label_raw_in -- security_label input function in raw format */ ++Datum ++security_label_raw_in(PG_FUNCTION_ARGS) ++{ ++ char *label = PG_GETARG_CSTRING(0); ++ ++ PG_RETURN_OID(security_label_to_sid(label)); ++} ++ ++/* security_label_raw_out -- security_label output function in raw format */ ++Datum ++security_label_raw_out(PG_FUNCTION_ARGS) ++{ ++ Oid sid = PG_GETARG_OID(0); ++ ++ PG_RETURN_CSTRING(sid_to_security_label(sid)); ++} ++ ++/* text_to_security_label -- security_label cast function */ ++Datum ++text_to_security_label(PG_FUNCTION_ARGS) ++{ ++ text *t = PG_GETARG_TEXT_P(0); ++ char *seclabel; ++ int len; ++ Datum sid; ++ ++ len = VARSIZE(t) - VARHDRSZ; ++ seclabel = palloc0(len + 1); ++ memcpy(seclabel, VARDATA(t), len); ++ sid = DirectFunctionCall1(security_label_in, ++ CStringGetDatum(seclabel)); ++ pfree(seclabel); ++ PG_RETURN_DATUM(sid); ++} ++ ++/* security_label_to_text -- security_label cast function */ ++Datum ++security_label_to_text(PG_FUNCTION_ARGS) ++{ ++ Oid sid = PG_GETARG_OID(0); ++ char *context; ++ text *result; ++ ++ context = DatumGetCString(DirectFunctionCall1(security_label_out, ++ ObjectIdGetDatum(sid))); ++ result = palloc(VARHDRSZ + strlen(context)); ++ VARATT_SIZEP(result) = VARHDRSZ + strlen(context); ++ memcpy(VARDATA(result), context, strlen(context)); ++ ++ PG_RETURN_TEXT_P(result); ++} ++ ++/***************************************************************************** ++ * Set/Get security attribute of Large Object ++ *****************************************************************************/ ++Datum ++lo_get_security(PG_FUNCTION_ARGS) ++{ ++ Oid loid = PG_GETARG_OID(0); ++ Oid lo_security = InvalidOid; ++ Relation rel; ++ ScanKeyData skey; ++ SysScanDesc sd; ++ HeapTuple tuple; ++ bool found = false; ++ ++ ScanKeyInit(&skey, ++ Anum_pg_largeobject_loid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(loid)); ++ ++ rel = heap_open(LargeObjectRelationId, AccessShareLock); ++ ++ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, ++ SnapshotNow, 1, &skey); ++ ++ while ((tuple = systable_getnext(sd)) != NULL) { ++ lo_security = pgaceLargeObjectGetSecurity(tuple); ++ found = true; ++ break; ++ } ++ systable_endscan(sd); ++ ++ heap_close(rel, AccessShareLock); ++ ++ if (!found) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_OBJECT), ++ errmsg("large object %u does not exist", loid))); ++ ++ PG_RETURN_OID(lo_security); ++} ++ ++Datum ++lo_set_security(PG_FUNCTION_ARGS) ++{ ++ Oid loid = PG_GETARG_OID(0); ++ Oid lo_security = PG_GETARG_OID(1); ++ Relation rel; ++ ScanKeyData skey; ++ SysScanDesc sd; ++ HeapTuple tuple, newtup; ++ CatalogIndexState indstate; ++ bool found = false; ++ ++ 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 ((tuple = systable_getnext(sd)) != NULL) { ++ newtup = heap_copytuple(tuple); ++ pgaceLargeObjectSetSecurity(newtup, lo_security, !found); ++ 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); ++} ++ ++#ifndef HAVE_SELINUX ++/* dummy definitions for SE-PostgreSQL */ ++Datum sepgsql_getcon(PG_FUNCTION_ARGS); ++Datum sepgsql_tuple_perms(PG_FUNCTION_ARGS); ++Datum sepgsql_tuple_perms_abort(PG_FUNCTION_ARGS); ++ ++Datum ++sepgsql_getcon(PG_FUNCTION_ARGS) ++{ ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), ++ errmsg("SE-PostgreSQL is not configured"))); ++ PG_RETURN_OID(InvalidOid); ++} ++ ++Datum ++sepgsql_tuple_perms(PG_FUNCTION_ARGS) ++{ ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), ++ errmsg("SE-PostgreSQL is not configured"))); ++ PG_RETURN_BOOL(false); ++} ++ ++Datum ++sepgsql_tuple_perms_abort(PG_FUNCTION_ARGS) ++{ ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), ++ errmsg("SE-PostgreSQL is not configured"))); ++ PG_RETURN_BOOL(false); ++} ++#endif +diff -rpNU3 base/src/backend/security/sepgsqlCore.c sepgsql/src/backend/security/sepgsqlCore.c +--- base/src/backend/security/sepgsqlCore.c 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/sepgsqlCore.c 2007-10-25 13:12:52.000000000 +0900 +@@ -0,0 +1,1019 @@ ++/* ++ * src/backend/security/sepgsqlCore.c ++ * SE-PostgreSQL core facilities like userspace AVC, policy state monitoring. ++ * ++ * Copyright (c) 2007 KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "access/heapam.h" ++#include "access/genam.h" ++#include "access/tupdesc.h" ++#include "access/xact.h" ++#include "libpq/libpq-be.h" ++#include "libpq/pqsignal.h" ++#include "miscadmin.h" ++#include "security/pgace.h" ++#include "storage/lwlock.h" ++#include "utils/builtins.h" ++#include "utils/fmgroids.h" ++#include "utils/rel.h" ++#include "utils/syscache.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct { ++ struct { ++ char *name; /* name of object class */ ++ uint16 inum; /* internal identifier number */ ++ } tclass; ++ struct { ++ char *name; /* name of access vector */ ++ uint32 inum; /* internal identifier number */ ++ } av_perms[sizeof(access_vector_t) * 8]; ++} selinux_catalog[] = { ++ { ++ { "db_database", SECCLASS_DB_DATABASE }, ++ { ++ { "create", DB_DATABASE__CREATE }, ++ { "drop", DB_DATABASE__DROP }, ++ { "getattr", DB_DATABASE__GETATTR }, ++ { "setattr", DB_DATABASE__SETATTR }, ++ { "relabelfrom", DB_DATABASE__RELABELFROM }, ++ { "relabelto", DB_DATABASE__RELABELTO }, ++ { "access", DB_DATABASE__ACCESS }, ++ { "install_module", DB_DATABASE__INSTALL_MODULE }, ++ { "load_module", DB_DATABASE__LOAD_MODULE }, ++ { "get_param", DB_DATABASE__GET_PARAM }, ++ { "set_param", DB_DATABASE__SET_PARAM }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ { "db_table", SECCLASS_DB_TABLE }, ++ { ++ { "create", DB_TABLE__CREATE }, ++ { "drop", DB_TABLE__DROP }, ++ { "getattr", DB_TABLE__GETATTR }, ++ { "setattr", DB_TABLE__SETATTR }, ++ { "relabelfrom", DB_TABLE__RELABELFROM }, ++ { "relabelto", DB_TABLE__RELABELTO }, ++ { "use", DB_TABLE__USE }, ++ { "select", DB_TABLE__SELECT }, ++ { "update", DB_TABLE__UPDATE }, ++ { "insert", DB_TABLE__INSERT }, ++ { "delete", DB_TABLE__DELETE }, ++ { "lock", DB_TABLE__LOCK }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ { "db_procedure", SECCLASS_DB_PROCEDURE }, ++ { ++ { "create", DB_PROCEDURE__CREATE }, ++ { "drop", DB_PROCEDURE__DROP }, ++ { "getattr", DB_PROCEDURE__GETATTR }, ++ { "setattr", DB_PROCEDURE__SETATTR }, ++ { "relabelfrom", DB_PROCEDURE__RELABELFROM }, ++ { "relabelto", DB_PROCEDURE__RELABELTO }, ++ { "execute", DB_PROCEDURE__EXECUTE }, ++ { "entrypoint", DB_PROCEDURE__ENTRYPOINT }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ { "db_column", SECCLASS_DB_COLUMN }, ++ { ++ { "create", DB_COLUMN__CREATE }, ++ { "drop", DB_COLUMN__DROP }, ++ { "getattr", DB_COLUMN__GETATTR }, ++ { "setattr", DB_COLUMN__SETATTR }, ++ { "relabelfrom", DB_COLUMN__RELABELFROM }, ++ { "relabelto", DB_COLUMN__RELABELTO }, ++ { "use", DB_COLUMN__USE }, ++ { "select", DB_COLUMN__SELECT }, ++ { "update", DB_COLUMN__UPDATE }, ++ { "insert", DB_COLUMN__INSERT }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ { "db_tuple", SECCLASS_DB_TUPLE }, ++ { ++ { "relabelfrom", DB_TUPLE__RELABELFROM }, ++ { "relabelto", DB_TUPLE__RELABELTO }, ++ { "use", DB_TUPLE__USE }, ++ { "select", DB_TUPLE__SELECT }, ++ { "update", DB_TUPLE__UPDATE }, ++ { "insert", DB_TUPLE__INSERT }, ++ { "delete", DB_TUPLE__DELETE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ { "db_blob", SECCLASS_DB_BLOB }, ++ { ++ { "create", DB_BLOB__CREATE }, ++ { "drop", DB_BLOB__DROP }, ++ { "getattr", DB_BLOB__GETATTR }, ++ { "setattr", DB_BLOB__SETATTR }, ++ { "relabelfrom", DB_BLOB__RELABELFROM }, ++ { "relabelto", DB_BLOB__RELABELTO }, ++ { "read", DB_BLOB__READ }, ++ { "write", DB_BLOB__WRITE }, ++ { "import", DB_BLOB__IMPORT }, ++ { "export", DB_BLOB__EXPORT }, ++ { NULL, 0UL }, ++ } ++ }, ++}; ++#define NUM_SELINUX_CATALOG (sizeof(selinux_catalog) / sizeof(selinux_catalog[0])) ++ ++static const char *sepgsql_class_to_string(uint16 tclass) ++{ ++ int i; ++ ++ for (i=0; i < NUM_SELINUX_CATALOG; i++) { ++ if (selinux_catalog[i].tclass.inum == tclass) ++ return selinux_catalog[i].tclass.name; ++ } ++#ifdef SEPGSQLOPT_LIBSELINUX_1_33 ++ /* for legacy libselinux (Fedora core 6) */ ++ /* This code will be replaced near future */ ++ if (tclass == SECCLASS_PROCESS) ++ return "process"; ++ return "unknown"; ++#else ++ /* because tclass didn't match with userspace object classes, ++ * its external representation is always same as internal one */ ++ return security_class_to_string((security_class_t) tclass); ++#endif ++} ++ ++static const char *sepgsql_av_perm_to_string(uint16 tclass, uint32 perm) ++{ ++ int i, j; ++ ++ for (i=0; i < NUM_SELINUX_CATALOG; i++) { ++ if (selinux_catalog[i].tclass.inum == tclass) { ++ char *perm_name; ++ ++ for (j=0; (perm_name = selinux_catalog[i].av_perms[j].name); j++) { ++ if (selinux_catalog[i].av_perms[j].inum == perm) ++ return perm_name; ++ } ++ return "unknown"; ++ } ++ } ++#ifdef SEPGSQLOPT_LIBSELINUX_1_33 ++ /* for legacy libselinux (Fedora core 6) */ ++ /* This code will be replaced near future */ ++ if (tclass == SECCLASS_PROCESS && perm == PROCESS__TRANSITION) ++ return "transition"; ++ return "unknown"; ++#else ++ /* because tclass/perm didn't match with userspace object classes, ++ * its external representation is always same as internal one */ ++ return security_av_perm_to_string((security_class_t) tclass, (access_vector_t) perm); ++#endif ++} ++ ++/* ++ * SE-PostgreSQL Internal AVC(Access Vector Cache) implementation. ++ * ++ */ ++struct avc_datum { ++ SHMEM_OFFSET next; ++ ++ Oid ssid; /* subject context */ ++ Oid tsid; /* object context */ ++ uint16 tclass; /* object class */ ++ ++ uint32 allowed; ++ uint32 decided; ++ uint32 auditallow; ++ uint32 auditdeny; ++ ++ Oid create; /* newly created context */ ++ bool is_hot; ++}; ++ ++#define AVC_DATUM_CACHE_SLOTS 512 ++#define AVC_DATUM_CACHE_MAXNODES 800 ++static struct { ++ LWLockId lock; ++ SHMEM_OFFSET slot[AVC_DATUM_CACHE_SLOTS]; ++ SHMEM_OFFSET freelist; ++ int lru_hint; ++ int enabled; ++ int enforcing; ++ struct avc_datum entry[AVC_DATUM_CACHE_MAXNODES]; ++ ++ /* dynamic object class/av permission mapping */ ++ struct { ++ struct { ++ uint16 internal; ++ security_class_t external; ++ } tclass; ++ struct { ++ uint32 internal; ++ access_vector_t external; ++ } av_perms[sizeof(access_vector_t) * 8]; ++ } catalog[NUM_SELINUX_CATALOG]; ++} *avc_shmem = NULL; ++ ++Size sepgsqlShmemSize() ++{ ++ return sizeof(*avc_shmem); ++} ++ ++static void sepgsql_load_class_av_mapping() ++{ ++ extern char *selinux_mnt; ++ char buffer[PATH_MAX]; ++ struct stat st_buf; ++ int i, j, fd, len; ++ ++ if (!selinux_mnt) ++ goto legacy_mapping; ++ ++ /* Does '/selinux/class' exist? */ ++ snprintf(buffer, sizeof(buffer), "%s/class", selinux_mnt); ++ if (lstat(buffer, &st_buf) || !S_ISDIR(st_buf.st_mode)) ++ goto legacy_mapping; ++ ++ for (i=0; i < NUM_SELINUX_CATALOG; i++) { ++ /* obtain external object class number */ ++ snprintf(buffer, sizeof(buffer), "%s/class/%s/index", ++ selinux_mnt, selinux_catalog[i].tclass.name); ++ fd = open(buffer, O_RDONLY); ++ if (fd < 0) ++ goto legacy_mapping; ++ ++ len = read(fd, buffer, sizeof(buffer)); ++ close(fd); ++ if (len < 1) ++ goto legacy_mapping; ++ buffer[len] = '\0'; ++ ++ avc_shmem->catalog[i].tclass.internal ++ = selinux_catalog[i].tclass.inum; ++ avc_shmem->catalog[i].tclass.external ++ = atoi(buffer); ++ ++ /* obtain external access vector number */ ++ for (j=0; selinux_catalog[i].av_perms[j].name; j++) { ++ snprintf(buffer, sizeof(buffer), "%s/class/%s/perms/%s", ++ selinux_mnt, ++ selinux_catalog[i].tclass.name, ++ selinux_catalog[i].av_perms[j].name); ++ fd = open(buffer, O_RDONLY); ++ if (fd < 0) ++ goto legacy_mapping; ++ ++ len = read(fd, buffer, sizeof(buffer)); ++ close(fd); ++ if (len < 1) ++ goto legacy_mapping; ++ buffer[len] = '\0'; ++ ++ avc_shmem->catalog[i].av_perms[j].internal ++ = selinux_catalog[i].av_perms[j].inum; ++ avc_shmem->catalog[i].av_perms[j].external ++ = (0x0001UL << (atoi(buffer) - 1)); ++ } ++ } ++ return; ++ ++legacy_mapping: ++ for (i=0; i < NUM_SELINUX_CATALOG; i++) { ++ uint16 tclass = selinux_catalog[i].tclass.inum; ++ ++ avc_shmem->catalog[i].tclass.internal = tclass; ++ avc_shmem->catalog[i].tclass.external = tclass; ++ ++ for (j=0; selinux_catalog[i].av_perms[j].name; j++) { ++ uint32 av_perm = selinux_catalog[i].av_perms[j].inum; ++ ++ avc_shmem->catalog[i].av_perms[j].internal = av_perm; ++ avc_shmem->catalog[i].av_perms[j].external = av_perm; ++ } ++ } ++ return; ++} ++ ++static void sepgsql_avc_reset() ++{ ++ int i, enforcing, enabled; ++ ++ enabled = is_selinux_enabled(); ++ enforcing = security_getenforce(); ++ Assert(enforcing==0 || enforcing==1); ++ ++ LWLockAcquire(avc_shmem->lock, LW_EXCLUSIVE); ++ ++ for (i=0; i < AVC_DATUM_CACHE_SLOTS; i++) ++ avc_shmem->slot[i] = INVALID_OFFSET; ++ avc_shmem->freelist = INVALID_OFFSET; ++ for (i=0; i < AVC_DATUM_CACHE_MAXNODES; i++) { ++ struct avc_datum *avd = avc_shmem->entry + i; ++ ++ memset(avd, 0, sizeof(struct avc_datum)); ++ avd->next = avc_shmem->freelist; ++ avc_shmem->freelist = MAKE_OFFSET(avd); ++ } ++ sepgsql_load_class_av_mapping(); ++ avc_shmem->enabled = enabled; ++ avc_shmem->enforcing = enforcing; ++ ++ LWLockRelease(avc_shmem->lock); ++} ++ ++static void sepgsql_avc_init() ++{ ++ bool found_avc; ++ ++ avc_shmem = ShmemInitStruct("SELinux userspace AVC", ++ sepgsqlShmemSize(), &found_avc); ++ if (!found_avc) { ++ avc_shmem->lock = LWLockAssign(); ++ sepgsql_avc_reset(); ++ } ++} ++ ++static uint32 sepgsql_validate_av_perms(security_class_t tclass, access_vector_t perms) ++{ ++ /* we have to hold LW_SHARED lock at least */ ++ int i, j; ++ ++ for (i=0; i < NUM_SELINUX_CATALOG; i++) { ++ if (avc_shmem->catalog[i].tclass.external == tclass) { ++ uint32 __perms = 0; ++ ++ for (j=0; j < sizeof(access_vector_t) * 8; j++) { ++ if (avc_shmem->catalog[i].av_perms[j].external & perms) ++ __perms |= avc_shmem->catalog[i].av_perms[j].internal; ++ } ++ //selnotice("tclass(ext:%d -> int:%d) av_perms(ext:%08x -> int:%08x) validated", ++ // tclass, avc_shmem->catalog[i].tclass.internal, ++ // perms, __perms); ++ return __perms; ++ } ++ } ++ //selnotice("tclass = %d is not user tclass, perms (%08x) is used as is", tclass, perms); ++ ++ return (uint32) perms; ++} ++ ++static void sepgsql_compute_avc_datum(Oid ssid, Oid tsid, uint16 tclass, ++ struct avc_datum *avd) ++{ ++ security_class_t tclass_external = tclass; ++ security_context_t scon, tcon, ncon; ++ struct av_decision x; ++ Datum tmp; ++ int i; ++ ++ memset(avd, 0, sizeof(struct avc_datum)); ++ tmp = DirectFunctionCall1(security_label_raw_out, ++ ObjectIdGetDatum(ssid)); ++ scon = DatumGetCString(tmp); ++ tmp = DirectFunctionCall1(security_label_raw_out, ++ ObjectIdGetDatum(tsid)); ++ tcon = DatumGetCString(tmp); ++ ++ LWLockAcquire(avc_shmem->lock, LW_SHARED); ++ /* translate internal tclass into external one, to query the kernel */ ++ for (i=0; i < NUM_SELINUX_CATALOG; i++) { ++ if (avc_shmem->catalog[i].tclass.internal == tclass) { ++ tclass_external = avc_shmem->catalog[i].tclass.external; ++ break; ++ } ++ } ++ ++ if (security_compute_av_raw(scon, tcon, tclass_external, 0, &x)) ++ selerror("could not obtain access vector decision " ++ " scon='%s' tcon='%s' tclass=%u", scon, tcon, tclass); ++ if (security_compute_create_raw(scon, tcon, tclass_external, &ncon) != 0) ++ selerror("could not obtain a newly created security context " ++ "scon='%s' tcon='%s' tclass=%u", scon, tcon, tclass); ++ ++ avd->ssid = ssid; ++ avd->tsid = tsid; ++ avd->tclass = tclass; ++ ++ avd->allowed = sepgsql_validate_av_perms(tclass_external, x.allowed); ++ avd->decided = sepgsql_validate_av_perms(tclass_external, x.decided); ++ avd->auditallow = sepgsql_validate_av_perms(tclass_external, x.auditallow); ++ avd->auditdeny = sepgsql_validate_av_perms(tclass_external, x.auditdeny); ++ LWLockRelease(avc_shmem->lock); ++ ++ PG_TRY(); ++ { ++ tmp = DirectFunctionCall1(security_label_raw_in, ++ CStringGetDatum(ncon)); ++ avd->create = DatumGetObjectId(tmp); ++ } ++ PG_CATCH(); ++ { ++ freecon(ncon); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ ++ pfree(scon); ++ pfree(tcon); ++ freecon(ncon); ++} ++ ++static Oid sepgsql_compute_relabel(Oid ssid, Oid tsid, uint16 tclass) ++{ ++ security_context_t scon, tcon, ncon; ++ Oid nsid; ++ Datum tmp; ++ ++ tmp = DirectFunctionCall1(security_label_raw_out, ++ ObjectIdGetDatum(ssid)); ++ scon = DatumGetCString(tmp); ++ tmp = DirectFunctionCall1(security_label_raw_out, ++ ObjectIdGetDatum(tsid)); ++ tcon = DatumGetCString(tmp); ++ ++ if (security_compute_relabel_raw(scon, tcon, tclass, &ncon) != 0) ++ selerror("could not obtain a newly relabeled security context " ++ "scon='%s' tcon='%s' tclass=%u", scon, tcon, tclass); ++ ++ PG_TRY(); ++ { ++ tmp = DirectFunctionCall1(security_label_raw_in, ++ CStringGetDatum(ncon)); ++ nsid = DatumGetObjectId(tmp); ++ } ++ PG_CATCH(); ++ { ++ freecon(ncon); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ ++ freecon(ncon); ++ pfree(scon); ++ pfree(tcon); ++ ++ return nsid; ++} ++ ++static char *sepgsql_avc_audit(uint32 perms, struct avc_datum *avd, char *objname) ++{ ++ /* we have to hold LW_SHARED lock at least */ ++ uint32 denied, audited, mask; ++ char buffer[4096]; ++ char *context; ++ int len; ++ ++ denied = perms & ~avd->allowed; ++ audited = denied ? (denied & avd->auditdeny) : (perms & avd->auditallow); ++ if (!audited) ++ return NULL; ++ ++ len = snprintf(buffer, sizeof(buffer), "%s {", denied ? "denied" : "granted"); ++ for (mask=1; mask; mask <<= 1) { ++ if (audited & mask) { ++ len += snprintf(buffer + len, sizeof(buffer) - len, " %s", ++ sepgsql_av_perm_to_string(avd->tclass, mask)); ++ } ++ } ++ len += snprintf(buffer + len, sizeof(buffer) - len, " }"); ++ ++ context = DatumGetCString(DirectFunctionCall1(security_label_out, ++ ObjectIdGetDatum(avd->ssid))); ++ len += snprintf(buffer + len, sizeof(buffer) - len, " scontext=%s", context); ++ pfree(context); ++ ++ context = DatumGetCString(DirectFunctionCall1(security_label_out, ++ ObjectIdGetDatum(avd->tsid))); ++ len += snprintf(buffer + len, sizeof(buffer) - len, " tcontext=%s", context); ++ pfree(context); ++ ++ len += snprintf(buffer + len, sizeof(buffer) - len, " tclass=%s", ++ sepgsql_class_to_string(avd->tclass)); ++ if (objname) ++ len += snprintf(buffer + len, sizeof(buffer) - len, " name=%s", objname); ++ ++ return pstrdup(buffer); ++} ++ ++static inline int sepgsql_avc_hash(Oid ssid, Oid tsid, uint16 tclass) ++{ ++ return ((uint32)ssid ^ ((uint32)tsid << 2) ^ tclass) % AVC_DATUM_CACHE_SLOTS; ++} ++ ++static struct avc_datum * ++sepgsql_avc_lookup(Oid ssid, Oid tsid, uint16 tclass, uint32 perms) ++{ ++ /* we have to hold LW_SHARED lock at least */ ++ struct avc_datum *avd; ++ SHMEM_OFFSET curr; ++ int hashkey = sepgsql_avc_hash(ssid, tsid, tclass); ++ ++ for (curr = avc_shmem->slot[hashkey]; ++ SHM_OFFSET_VALID(curr); ++ curr = avd->next) { ++ avd = (void *)MAKE_PTR(curr); ++ if (avd->ssid==ssid && avd->tsid==tsid && avd->tclass==tclass ++ && (perms & avd->decided)==perms) ++ return avd; ++ } ++ return NULL; ++} ++ ++static void sepgsql_avc_reclaim() { ++ /* we have to hold LW_EXCLUSIVE lock */ ++ SHMEM_OFFSET *prev, next; ++ struct avc_datum *avd; ++ ++ while (!SHM_OFFSET_VALID(avc_shmem->freelist)) { ++ prev = avc_shmem->slot + avc_shmem->lru_hint; ++ next = *prev; ++ while (!SHM_OFFSET_VALID(next)) { ++ avd = (void *)MAKE_PTR(next); ++ next = avd->next; ++ if (avd->is_hot) { ++ avd->is_hot = false; ++ } else { ++ *prev = avd->next; ++ avd->next = avc_shmem->freelist; ++ avc_shmem->freelist = MAKE_OFFSET(avd); ++ } ++ avd = (void *)MAKE_PTR(next); ++ } ++ avc_shmem->lru_hint = (avc_shmem->lru_hint + 1) % AVC_DATUM_CACHE_SLOTS; ++ } ++} ++ ++static void sepgsql_avc_insert(struct avc_datum *tmp) ++{ ++ /* we have to hold LW_EXCLUSIVE lock */ ++ struct avc_datum *avd; ++ int hashkey; ++ ++ avd = sepgsql_avc_lookup(tmp->ssid, tmp->tsid, tmp->tclass, tmp->decided); ++ if (avd) ++ return; ++ ++ if (!SHM_OFFSET_VALID(avc_shmem->freelist)) ++ sepgsql_avc_reclaim(); ++ Assert(SHM_OFFSET_VALID(avc_shmem->freelist)); ++ ++ avd = (void *)MAKE_PTR(avc_shmem->freelist); ++ avc_shmem->freelist = avd->next; ++ ++ memcpy(avd, tmp, sizeof(struct avc_datum)); ++ avd->is_hot = true; ++ ++ hashkey = sepgsql_avc_hash(avd->ssid, avd->tsid, avd->tclass); ++ avd->next = avc_shmem->slot[hashkey]; ++ avc_shmem->slot[hashkey] = MAKE_OFFSET(avd); ++ ++ return; ++} ++ ++bool sepgsql_avc_permission_noaudit(Oid ssid, Oid tsid, uint16 tclass, uint32 perms, ++ char **audit, char *objname) ++{ ++ struct avc_datum *avd, lavd; ++ uint32 denied; ++ bool rc = true; ++ bool wlock = false; ++ ++ LWLockAcquire(avc_shmem->lock, LW_SHARED); ++retry: ++ avd = sepgsql_avc_lookup(ssid, tsid, tclass, perms); ++ if (!avd) { ++ LWLockRelease(avc_shmem->lock); ++ ++ sepgsql_compute_avc_datum(ssid, tsid, tclass, &lavd); ++ ++ LWLockAcquire(avc_shmem->lock, LW_EXCLUSIVE); ++ wlock = true; ++ sepgsql_avc_insert(&lavd); ++ } else { ++ memcpy(&lavd, avd, sizeof(struct avc_datum)); ++ } ++ denied = perms & ~lavd.allowed; ++ if (!perms || denied) { ++ if (avc_shmem->enforcing) { ++ errno = EACCES; ++ rc = false; ++ } else { ++ if (!wlock) { ++ /* update avd need LW_EXCLUSIVE lock onto shmem */ ++ LWLockRelease(avc_shmem->lock); ++ LWLockAcquire(avc_shmem->lock, LW_EXCLUSIVE); ++ wlock = true; ++ goto retry; ++ } ++ /* grant permission to avoid flood of access denied log */ ++ if (!avd) ++ avd = sepgsql_avc_lookup(ssid, tsid, tclass, perms); ++ if (avd) ++ avd->allowed |= denied; ++ } ++ } ++ LWLockRelease(avc_shmem->lock); ++ if (audit) ++ *audit = sepgsql_avc_audit(perms, &lavd, objname); ++ ++ return rc; ++} ++ ++void sepgsql_avc_permission(Oid ssid, Oid tsid, uint16 tclass, uint32 perms, char *objname) ++{ ++ char *audit; ++ bool rc; ++ ++ rc = sepgsql_avc_permission_noaudit(ssid, tsid, tclass, perms, &audit, objname); ++ sepgsql_audit(rc, audit); ++ ++ if (audit) ++ pfree(audit); ++} ++ ++void sepgsql_audit(bool result, char *message) ++{ ++ if (message) { ++ ereport((result ? NOTICE : ERROR), ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("SELinux: %s", message))); ++ } else if (!result) { ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("Transaction aborted due to SELinux access denied."))); ++ } ++} ++ ++Oid sepgsql_avc_createcon(Oid ssid, Oid tsid, uint16 tclass) ++{ ++ struct avc_datum *avd, lavd; ++ Oid nsid; ++ ++ LWLockAcquire(avc_shmem->lock, LW_SHARED); ++ avd = sepgsql_avc_lookup(ssid, tsid, tclass, 0); ++ if (!avd) { ++ LWLockRelease(avc_shmem->lock); ++ ++ sepgsql_compute_avc_datum(ssid, tsid, tclass, &lavd); ++ ++ LWLockAcquire(avc_shmem->lock, LW_EXCLUSIVE); ++ sepgsql_avc_insert(&lavd); ++ nsid = lavd.create; ++ } else { ++ nsid = avd->create; ++ } ++ LWLockRelease(avc_shmem->lock); ++ ++ return nsid; ++} ++ ++Oid sepgsql_avc_relabelcon(Oid ssid, Oid tsid, uint16 tclass) ++{ ++ /* currently no avc support on relabeling */ ++ return sepgsql_compute_relabel(ssid, tsid, tclass); ++} ++ ++/* sepgsql_getcon() -- returns a security context of client */ ++Datum ++sepgsql_getcon(PG_FUNCTION_ARGS) ++{ ++ PG_RETURN_OID(sepgsqlGetClientContext()); ++} ++ ++/* sepgsql_system_getcon() -- obtain the server's context */ ++static Oid sepgsql_system_getcon() ++{ ++ security_context_t context; ++ Oid ssid; ++ ++ if (getcon_raw(&context) != 0) ++ selerror("could not obtain security context of server process"); ++ ++ PG_TRY(); ++ { ++ ssid = DatumGetObjectId(DirectFunctionCall1(security_label_raw_in, ++ CStringGetDatum(context))); ++ } ++ PG_CATCH(); ++ { ++ freecon(context); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(context); ++ return ssid; ++} ++ ++/* sepgsql_system_getpeercon() -- obtain the client's context */ ++static Oid sepgsql_system_getpeercon(int sockfd) ++{ ++ security_context_t context, __context; ++ Oid ssid; ++ ++ if (getpeercon_raw(sockfd, &context)) { ++ /* we can set finally fallbacked context */ ++ __context = getenv("SEPGSQL_FALLBACK_CONTEXT"); ++ if (!__context) ++ selerror("could not obtain security context of database client"); ++ if (security_check_context(__context) || ++ selinux_trans_to_raw_context(__context, &context)) ++ selerror("'%s' is not a valid context", __context); ++ } ++ ++ PG_TRY(); ++ { ++ ssid = DatumGetObjectId(DirectFunctionCall1(security_label_raw_in, ++ CStringGetDatum(context))); ++ } ++ PG_CATCH(); ++ { ++ freecon(context); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(context); ++ return ssid; ++} ++ ++/* ++ * SE-PostgreSQL core functions ++ * ++ * sepgsqlGetServerContext() -- obtains server's context ++ * sepgsqlGetClientContext() -- obtains client's context via getpeercon() ++ * sepgsqlSetClientContext() -- changes client's context for trusted procedure ++ * sepgsqlInitialize() -- called when initializing 'postgres' includes bootstraping ++ * sepgsqlInitializePostmaster() -- called when initializing 'postmaster' ++ * sepgsqlFinalizePostmaster() -- called when finalizing 'postmaster' to kill ++ * policy state monitoring process. ++ * sepgsqlMonitoringPolicyState() -- is implementation of policy state monitoring ++ * process. ++ * ++ */ ++static Oid sepgsqlServerContext = InvalidOid; ++static Oid sepgsqlClientContext = InvalidOid; ++ ++Oid sepgsqlGetServerContext() ++{ ++ return sepgsqlServerContext; ++} ++ ++Oid sepgsqlGetClientContext() ++{ ++ return sepgsqlClientContext; ++} ++ ++void sepgsqlSetClientContext(Oid new_context) ++{ ++ sepgsqlClientContext = new_context; ++} ++ ++Oid sepgsqlGetDatabaseContext() ++{ ++ HeapTuple tuple; ++ Oid datcon; ++ ++ if (IsBootstrapProcessingMode()) { ++ return sepgsql_avc_createcon(sepgsqlGetClientContext(), ++ sepgsqlGetServerContext(), ++ SECCLASS_DB_DATABASE); ++ } ++ ++ tuple = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(MyDatabaseId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ selerror("cache lookup failed for Database %u", MyDatabaseId); ++ datcon = HeapTupleGetSecurity(tuple); ++ ReleaseSysCache(tuple); ++ ++ return datcon; ++} ++ ++char *sepgsqlGetDatabaseName() ++{ ++ Form_pg_database dat_form; ++ HeapTuple tuple; ++ char *datname; ++ ++ if (IsBootstrapProcessingMode()) ++ return NULL; ++ ++ tuple = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(MyDatabaseId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ selerror("cache lookup failed for Database %u", MyDatabaseId); ++ dat_form = (Form_pg_database) GETSTRUCT(tuple); ++ datname = pstrdup(NameStr(dat_form->datname)); ++ ReleaseSysCache(tuple); ++ ++ return datname; ++} ++ ++void sepgsqlInitialize() ++{ ++ sepgsql_avc_init(); ++ ++ if (IsBootstrapProcessingMode()) { ++ sepgsqlServerContext = sepgsql_system_getcon(); ++ sepgsqlClientContext = sepgsql_system_getcon(); ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ sepgsqlGetDatabaseContext(), ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__ACCESS, ++ NULL); ++ return; ++ } ++ ++ /* obtain security context of server process */ ++ sepgsqlServerContext = sepgsql_system_getcon(); ++ ++ /* obtain security context of client process */ ++ if (MyProcPort != NULL) { ++ sepgsqlClientContext = sepgsql_system_getpeercon(MyProcPort->sock); ++ } else { ++ sepgsqlClientContext = sepgsql_system_getcon(); ++ } ++ ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ sepgsqlGetDatabaseContext(), ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__ACCESS, ++ sepgsqlGetDatabaseName()); ++} ++ ++/* sepgsqlMonitoringPolicyState() is worker process to monitor ++ * the status of SELinux policy. When it is changed, light after the worker ++ * thread receive a notification via netlink socket. The notification is ++ * delivered into any PostgreSQL instance by reseting shared avc. ++ */ ++static void sepgsqlMonitoringPolicyState_SIGHUP(int signum) ++{ ++ selnotice("selinux userspace AVC reset, by receiving SIGHUP"); ++ sepgsql_avc_reset(); ++} ++ ++static int sepgsqlMonitoringPolicyState() ++{ ++ char buffer[2048]; ++ struct sockaddr_nl addr; ++ socklen_t addrlen; ++ struct nlmsghdr *nlh; ++ int i, rc, nl_sockfd; ++ ++ seldebug("%s pid=%u", __FUNCTION__, getpid()); ++ ++ /* close listen port */ ++ for (i=3; !close(i); i++); ++ ++ /* map shared memory segment */ ++ sepgsql_avc_init(); ++ ++ /* setup the signal handler */ ++ pqinitmask(); ++ pqsignal(SIGHUP, sepgsqlMonitoringPolicyState_SIGHUP); ++ pqsignal(SIGINT, SIG_DFL); ++ pqsignal(SIGQUIT, SIG_DFL); ++ pqsignal(SIGTERM, SIG_DFL); ++ pqsignal(SIGUSR1, SIG_DFL); ++ pqsignal(SIGUSR2, SIG_DFL); ++ pqsignal(SIGCHLD, SIG_DFL); ++ PG_SETMASK(&UnBlockSig); ++ ++ /* open netlink socket */ ++ nl_sockfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_SELINUX); ++ if (nl_sockfd < 0) { ++ selnotice("could not create netlink socket"); ++ return 1; ++ } ++ ++ memset(&addr, 0, sizeof(addr)); ++ addr.nl_family = AF_NETLINK; ++ addr.nl_groups = SELNL_GRP_AVC; ++ if (bind(nl_sockfd, (struct sockaddr *)&addr, sizeof(addr))) { ++ selnotice("could not bind netlink socket"); ++ return 1; ++ } ++ ++ /* waiting loop */ ++ while (true) { ++ addrlen = sizeof(addr); ++ rc = recvfrom(nl_sockfd, buffer, sizeof(buffer), 0, ++ (struct sockaddr *)&addr, &addrlen); ++ if (rc < 0) { ++ if (errno == EINTR) ++ continue; ++ selnotice("selinux netlink: recvfrom() error=%d, %s", ++ errno, strerror(errno)); ++ return 1; ++ } ++ ++ if (addrlen != sizeof(addr)) { ++ selnotice("selinux netlink: netlink address truncated (len = %d)", addrlen); ++ return 1; ++ } ++ ++ if (addr.nl_pid) { ++ selnotice("selinux netlink: received spoofed packet from: %u", addr.nl_pid); ++ continue; ++ } ++ ++ if (rc == 0) { ++ selnotice("selinux netlink: received EOF on socket"); ++ return 1; ++ } ++ ++ nlh = (struct nlmsghdr *)buffer; ++ ++ if (nlh->nlmsg_flags & MSG_TRUNC ++ || nlh->nlmsg_len > (unsigned int)rc) { ++ selnotice("selinux netlink: incomplete netlink message"); ++ return 1; ++ } ++ ++ switch (nlh->nlmsg_type) { ++ case NLMSG_ERROR: { ++ struct nlmsgerr *err = NLMSG_DATA(nlh); ++ if (err->error == 0) ++ break; ++ selnotice("selinux netlink: error message %d", -err->error); ++ return 1; ++ } ++ case SELNL_MSG_SETENFORCE: { ++ struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh); ++ selnotice("selinux netlink: received setenforce notice (enforcing=%d)", msg->val); ++ sepgsql_avc_reset(); ++ break; ++ } ++ case SELNL_MSG_POLICYLOAD: { ++ struct selnl_msg_policyload *msg = NLMSG_DATA(nlh); ++ selnotice("selinux netlink: received policyload notice (seqno=%d)", msg->seqno); ++ sepgsql_avc_reset(); ++ break; ++ } ++ default: ++ selnotice("selinux netlink: unknown message type (%d)", nlh->nlmsg_type); ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++static pid_t MonitoringPolicyStatePid = -1; ++ ++int sepgsqlInitializePostmaster() ++{ ++ MonitoringPolicyStatePid = fork(); ++ if (MonitoringPolicyStatePid == 0) { ++ exit(sepgsqlMonitoringPolicyState()); ++ } else if (MonitoringPolicyStatePid < 0) { ++ selnotice("could not create a child process to monitor the policy state"); ++ return false; ++ } ++ return true; ++} ++ ++void sepgsqlFinalizePostmaster() ++{ ++ int status; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ if (MonitoringPolicyStatePid > 0) { ++ if (kill(MonitoringPolicyStatePid, SIGTERM) < 0) { ++ selnotice("could not kill(%u, SIGTERM), errno=%d (%s)", ++ MonitoringPolicyStatePid, errno, strerror(errno)); ++ return; ++ } ++ waitpid(MonitoringPolicyStatePid, &status, 0); ++ } ++} ++ ++bool sepgsqlIsEnabled() ++{ ++ int enabled; ++ ++ if (avc_shmem) { ++ LWLockAcquire(avc_shmem->lock, LW_SHARED); ++ enabled = avc_shmem->enabled; ++ LWLockRelease(avc_shmem->lock); ++ } else { ++ enabled = is_selinux_enabled(); ++ } ++ return (enabled > 0 ? true : false); ++} +diff -rpNU3 base/src/backend/security/sepgsqlHooks.c sepgsql/src/backend/security/sepgsqlHooks.c +--- base/src/backend/security/sepgsqlHooks.c 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/sepgsqlHooks.c 2008-01-30 17:08:18.000000000 +0900 +@@ -0,0 +1,681 @@ ++/* ++ * src/backend/sepgsqlHooks.c ++ * SE-PostgreSQL hooks ++ * ++ * Copyright 2007 KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "access/heapam.h" ++#include "access/genam.h" ++#include "access/skey.h" ++#include "miscadmin.h" ++#include "nodes/makefuncs.h" ++#include "security/pgace.h" ++#include "utils/fmgroids.h" ++#include "utils/syscache.h" ++#include ++#include ++#include ++#include ++ ++static HeapTuple __getHeapTupleFromItemPointer(Relation rel, ItemPointer tid) ++{ ++ /* obtain an old tuple */ ++ Buffer buffer; ++ PageHeader dp; ++ ItemId lp; ++ HeapTupleData tuple; ++ HeapTuple oldtup; ++ ++ buffer = ReadBuffer(rel, ItemPointerGetBlockNumber(tid)); ++ LockBuffer(buffer, BUFFER_LOCK_SHARE); ++ ++ dp = (PageHeader) BufferGetPage(buffer); ++ lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid)); ++ ++ Assert(ItemIdIsUsed(lp)); ++ ++ tuple.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp); ++ tuple.t_len = ItemIdGetLength(lp); ++ tuple.t_self = *tid; ++ tuple.t_tableOid = RelationGetRelid(rel); ++ oldtup = heap_copytuple(&tuple); ++ ++ LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ++ ReleaseBuffer(buffer); ++ ++ return oldtup; ++} ++ ++/******************************************************************************* ++ * Extended SQL statement hooks ++ *******************************************************************************/ ++/* make context = 'xxx' node */ ++DefElem *sepgsqlGramSecurityLabel(char *defname, char *context) { ++ DefElem *n = NULL; ++ if (!strcmp(defname, "context")) ++ n = makeDefElem(pstrdup(defname), (Node *) makeString(context)); ++ return n; ++} ++ ++/* whether DefElem holds security context, or not */ ++bool sepgsqlNodeIsSecurityLabel(DefElem *defel) { ++ Assert(IsA(defel, DefElem)); ++ if (defel->defname && !strcmp(defel->defname, "context")) ++ return true; ++ return false; ++} ++ ++/* parse explicitly specified security context */ ++Oid sepgsqlParseSecurityLabel(DefElem *defel) { ++ Datum newcon; ++ Assert(IsA(defel, DefElem)); ++ ++ newcon = DirectFunctionCall1(security_label_in, ++ CStringGetDatum(strVal(defel->arg))); ++ return DatumGetObjectId(newcon); ++} ++ ++/******************************************************************************* ++ * DATABASE object related hooks ++ *******************************************************************************/ ++ ++void sepgsqlGetDatabaseParam(const char *name) ++{ ++ HeapTuple tuple; ++ ++ tuple = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(MyDatabaseId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ selerror("cache lookup failed for database %u", MyDatabaseId); ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__GET_PARAM, ++ sepgsqlGetTupleName(DatabaseRelationId, tuple)); ++ ReleaseSysCache(tuple); ++} ++ ++void sepgsqlSetDatabaseParam(const char *name, char *argstring) ++{ ++ HeapTuple tuple; ++ ++ tuple = SearchSysCache(DATABASEOID, ++ ObjectIdGetDatum(MyDatabaseId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ selerror("cache lookup failed for database %u", MyDatabaseId); ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__SET_PARAM, ++ sepgsqlGetTupleName(DatabaseRelationId, tuple)); ++ ReleaseSysCache(tuple); ++} ++ ++/******************************************************************************* ++ * RELATION(Table)/ATTRIBTUE(column) object related hooks ++ *******************************************************************************/ ++void sepgsqlLockTable(Oid relid) ++{ ++ HeapTuple tuple; ++ Form_pg_class classForm; ++ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ selerror("cache lookup failed for relation %u", relid); ++ classForm = (Form_pg_class) GETSTRUCT(tuple); ++ ++ if (classForm->relkind == RELKIND_RELATION) ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_TABLE, ++ DB_TABLE__LOCK, ++ sepgsqlGetTupleName(RelationRelationId, tuple)); ++ ReleaseSysCache(tuple); ++} ++ ++/******************************************************************************* ++ * PROCEDURE related hooks ++ *******************************************************************************/ ++ ++static Datum __callTrustedProcedure(PG_FUNCTION_ARGS) ++{ ++ Oid orig_client_con; ++ Datum retval; ++ ++ /* save original security context */ ++ orig_client_con = sepgsqlGetClientContext(); ++ /* set exec context */ ++ sepgsqlSetClientContext(DatumGetObjectId(fcinfo->flinfo->fn_pgace_data)); ++ PG_TRY(); ++ { ++ retval = fcinfo->flinfo->fn_pgace_addr(fcinfo); ++ } ++ PG_CATCH(); ++ { ++ sepgsqlSetClientContext(orig_client_con); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ sepgsqlSetClientContext(orig_client_con); ++ ++ return retval; ++} ++ ++void sepgsqlCallFunction(FmgrInfo *finfo, bool with_perm_check) ++{ ++ HeapTuple tuple; ++ Oid execcon; ++ uint32 perms = DB_PROCEDURE__EXECUTE; ++ ++ tuple = SearchSysCache(PROCOID, ++ ObjectIdGetDatum(finfo->fn_oid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ selerror("cache lookup failed for procedure %u", finfo->fn_oid); ++ ++ /* check trusted procedure */ ++ execcon = sepgsql_avc_createcon(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_PROCESS); ++ if (sepgsqlGetClientContext() != execcon) { ++ finfo->fn_pgace_addr = finfo->fn_addr; ++ finfo->fn_pgace_data = ObjectIdGetDatum(execcon); ++ finfo->fn_addr = __callTrustedProcedure; ++ ++ perms |= DB_PROCEDURE__ENTRYPOINT; ++ } ++ ++ if (with_perm_check) { ++ /* check procedure:{execute entrypoint} permission */ ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_PROCEDURE, ++ perms, ++ sepgsqlGetTupleName(ProcedureRelationId, tuple)); ++ } ++ ReleaseSysCache(tuple); ++} ++ ++bool sepgsqlCallFunctionTrigger(FmgrInfo *finfo, TriggerData *tgdata) ++{ ++ Relation rel = tgdata->tg_relation; ++ HeapTuple newtup = NULL; ++ HeapTuple oldtup = NULL; ++ ++ if (TRIGGER_FIRED_FOR_STATEMENT(tgdata->tg_event)) ++ return true; /* statement trigger does not contain any tuple */ ++ if (TRIGGER_FIRED_BY_INSERT(tgdata->tg_event)) { ++ if (TRIGGER_FIRED_AFTER(tgdata->tg_event)) ++ newtup = tgdata->tg_trigtuple; ++ } else if (TRIGGER_FIRED_BY_UPDATE(tgdata->tg_event)) { ++ oldtup = tgdata->tg_trigtuple; ++ if (TRIGGER_FIRED_AFTER(tgdata->tg_event) ++ && HeapTupleGetSecurity(oldtup) != HeapTupleGetSecurity(tgdata->tg_newtuple)) ++ newtup = tgdata->tg_newtuple; ++ } else if (TRIGGER_FIRED_BY_DELETE(tgdata->tg_event)) { ++ if (TRIGGER_FIRED_AFTER(tgdata->tg_event)) ++ oldtup = tgdata->tg_trigtuple; ++ } else { ++ selerror("unknown trigger event type (%u)", tgdata->tg_event); ++ } ++ if (oldtup && !sepgsqlCheckTuplePerms(rel, oldtup, NULL, SEPGSQL_PERMS_SELECT, false)) ++ return false; ++ if (newtup && !sepgsqlCheckTuplePerms(rel, newtup, NULL, SEPGSQL_PERMS_SELECT, false)) ++ return false; ++ ++ sepgsqlCallFunction(finfo, false); ++ ++ return true; ++} ++ ++/******************************************************************************* ++ * LOAD shared library module hook ++ *******************************************************************************/ ++void sepgsqlLoadSharedModule(const char *filename) ++{ ++ security_context_t filecon; ++ Datum filecon_sid; ++ ++ if (getfilecon_raw(filename, &filecon) < 1) ++ selerror("could not obtain security context of %s", filename); ++ PG_TRY(); ++ { ++ filecon_sid = DirectFunctionCall1(security_label_raw_in, ++ CStringGetDatum(filecon)); ++ } ++ PG_CATCH(); ++ { ++ freecon(filecon); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(filecon); ++ ++ sepgsql_avc_permission(sepgsqlGetDatabaseContext(), ++ DatumGetObjectId(filecon_sid), ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__LOAD_MODULE, ++ (char *) filename); ++} ++ ++/******************************************************************************* ++ * Binary Large Object hooks ++ *******************************************************************************/ ++Oid sepgsqlLargeObjectGetSecurity(HeapTuple tuple) { ++ Oid lo_security = HeapTupleGetSecurity(tuple); ++ ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ lo_security, ++ SECCLASS_DB_BLOB, ++ DB_BLOB__GETATTR, ++ sepgsqlGetTupleName(LargeObjectRelationId, tuple)); ++ return lo_security; ++} ++ ++void sepgsqlLargeObjectSetSecurity(HeapTuple tuple, Oid lo_security, bool is_first) ++{ ++ if (is_first) { ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_BLOB, ++ DB_BLOB__SETATTR | DB_BLOB__RELABELFROM, ++ sepgsqlGetTupleName(LargeObjectRelationId, tuple)); ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ lo_security, ++ SECCLASS_DB_BLOB, ++ DB_BLOB__RELABELTO, ++ sepgsqlGetTupleName(LargeObjectRelationId, tuple)); ++ } ++ HeapTupleSetSecurity(tuple, lo_security); ++} ++ ++void sepgsqlLargeObjectCreate(Relation rel, HeapTuple tuple) ++{ ++ Oid newcon = sepgsqlComputeImplicitContext(rel, tuple); ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ newcon, ++ SECCLASS_DB_BLOB, ++ DB_BLOB__CREATE, ++ sepgsqlGetTupleName(LargeObjectRelationId, tuple)); ++ HeapTupleSetSecurity(tuple, newcon); ++} ++ ++void sepgsqlLargeObjectDrop(Relation rel, HeapTuple tuple) ++{ ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_BLOB, ++ DB_BLOB__DROP, ++ sepgsqlGetTupleName(LargeObjectRelationId, tuple)); ++} ++ ++void sepgsqlLargeObjectOpen(Relation rel, HeapTuple tuple, bool read_only) ++{ ++ sepgsqlCheckTuplePerms(rel, tuple, NULL, SEPGSQL_PERMS_SELECT, true); ++} ++ ++void sepgsqlLargeObjectRead(Relation rel, HeapTuple tuple) ++{ ++ sepgsqlCheckTuplePerms(rel, tuple, NULL, SEPGSQL_PERMS_SELECT | SEPGSQL_PERMS_READ, true); ++} ++ ++void sepgsqlLargeObjectWrite(Relation rel, HeapTuple newtup, HeapTuple oldtup) ++{ ++ Oid lo_security; ++ ++ if (HeapTupleIsValid(oldtup)) { ++ lo_security = HeapTupleGetSecurity(oldtup); ++ } else { ++ Form_pg_largeobject lobj_form ++ = (Form_pg_largeobject) GETSTRUCT(newtup); ++ ScanKeyData skey; ++ SysScanDesc sd; ++ HeapTuple tuple; ++ ++ ScanKeyInit(&skey, ++ Anum_pg_largeobject_loid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(lobj_form->loid)); ++ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, ++ SnapshotNow, 1, &skey); ++ tuple = systable_getnext(sd); ++ if (!HeapTupleIsValid(tuple)) ++ selerror("large object %u does not exist", lobj_form->loid); ++ lo_security = HeapTupleGetSecurity(tuple); ++ systable_endscan(sd); ++ } ++ HeapTupleSetSecurity(newtup, lo_security); ++ sepgsqlCheckTuplePerms(rel, newtup, NULL, SEPGSQL_PERMS_UPDATE | SEPGSQL_PERMS_WRITE, true); ++} ++ ++void sepgsqlLargeObjectImport() ++{ ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ sepgsqlGetServerContext(), ++ SECCLASS_DB_BLOB, ++ DB_BLOB__IMPORT, ++ NULL); ++} ++ ++void sepgsqlLargeObjectExport() ++{ ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ sepgsqlGetServerContext(), ++ SECCLASS_DB_BLOB, ++ DB_BLOB__EXPORT, ++ NULL); ++} ++ ++/******************************************************************************* ++ * security_label hooks ++ *******************************************************************************/ ++char *sepgsqlSecurityLabelIn(char *context) { ++ security_context_t raw_context, canonical_context; ++ char *result; ++ int rc; ++ ++ rc = selinux_trans_to_raw_context(context, &raw_context); ++ if (rc) ++ selerror("could not translate MLS label"); ++ ++ rc = security_canonicalize_context_raw(raw_context, &canonical_context); ++ freecon(raw_context); ++ if (rc) ++ selerror("could not canonicalize the context"); ++ ++ PG_TRY(); ++ { ++ result = pstrdup(canonical_context); ++ } ++ PG_CATCH(); ++ { ++ freecon(canonical_context); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(canonical_context); ++ ++ return result; ++} ++ ++char *sepgsqlSecurityLabelOut(char *raw_context) { ++ security_context_t context; ++ char *result; ++ ++ if (selinux_raw_to_trans_context(raw_context, &context)) ++ selerror("could not translate MLS label"); ++ PG_TRY(); ++ { ++ result = pstrdup(context); ++ } ++ PG_CATCH(); ++ { ++ freecon(context); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(context); ++ ++ return result; ++} ++ ++bool sepgsqlSecurityLabelIsValid(char *context) { ++ if (!security_check_context_raw(context)) ++ return true; ++ return false; ++} ++ ++char *sepgsqlSecurityLabelOfLabel(char *context) { ++ HeapTuple tuple; ++ security_context_t scon, tcon, ncon, _ncon; ++ int rc; ++ ++ /* obtain the security context of pg_security */ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(SecurityRelationId), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ selerror("pg_security (relid=%u) not found", SecurityRelationId); ++ tcon = DatumGetCString(DirectFunctionCall1(security_label_raw_out, ++ ObjectIdGetDatum(HeapTupleGetSecurity(tuple)))); ++ ReleaseSysCache(tuple); ++ ++ /* obtain server's context */ ++ rc = getcon_raw(&scon); ++ if (rc) ++ selerror("could not obtain server's context"); ++ ++ /* compute pg_selinux tuple context */ ++ rc = security_compute_create_raw(scon, tcon, SECCLASS_DB_TUPLE, &ncon); ++ pfree(tcon); ++ freecon(scon); ++ if (rc) ++ selerror("could not compute a newly created security context"); ++ ++ /* copy tuple's context */ ++ PG_TRY(); ++ { ++ _ncon = pstrdup(ncon); ++ } ++ PG_CATCH(); ++ { ++ freecon(ncon); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ ++ freecon(ncon); ++ ++ return _ncon; ++} ++ ++extern char *selinux_mnt; ++ ++char *sepgsqlSecurityLabelNotFound(Oid sid) { ++ security_context_t unlabeled_con; ++ ++#ifndef SEPGSQLOPT_LIBSELINUX_1_33 ++ if (!security_get_initial_context_raw("unlabeled", &unlabeled_con)) { ++ char *result; ++ ++ PG_TRY(); ++ { ++ result = pstrdup(unlabeled_con); ++ } ++ PG_CATCH(); ++ { ++ freecon(unlabeled_con); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(unlabeled_con); ++ return result; ++ } ++#endif ++ /* FIXME: This fallback code should be eliminated in the near future. ++ * /selinux/init_contexts support will be enabled at 2.6.22 kernel. ++ */ ++ unlabeled_con = "system_u:object_r:unlabeled_t:s0"; ++ if (sepgsqlSecurityLabelIsValid(unlabeled_con)) ++ return pstrdup(unlabeled_con); ++ unlabeled_con = "system_u:object_r:unlabeled_t"; ++ if (sepgsqlSecurityLabelIsValid(unlabeled_con)) ++ return pstrdup(unlabeled_con); ++ return NULL; ++} ++ ++/******************************************************************************* ++ * simple_heap_xxxx hooks ++ *******************************************************************************/ ++static inline bool __is_simple_system_relation(Relation rel) ++{ ++ bool retval = false; ++ switch (RelationGetRelid(rel)) { ++ case AggregateRelationId: ++ case AttributeRelationId: ++ case AuthIdRelationId: ++ case CastRelationId: ++ case ConversionRelationId: ++ case DatabaseRelationId: ++ case LanguageRelationId: ++ case NamespaceRelationId: ++ case OperatorRelationId: ++ case OperatorClassRelationId: ++ case ProcedureRelationId: ++ case RelationRelationId: ++ case RewriteRelationId: ++ case TableSpaceRelationId: ++ case TriggerRelationId: ++ case TypeRelationId: ++ retval = true; ++ break; ++ } ++ return retval; ++} ++ ++void sepgsqlSimpleHeapInsert(Relation rel, HeapTuple tuple) ++{ ++ Oid newcon; ++ ++ if (!__is_simple_system_relation(rel)) ++ return; ++ ++ newcon = HeapTupleGetSecurity(tuple); ++ if (newcon == InvalidOid) { ++ /* no explicit labeling */ ++ newcon = sepgsqlComputeImplicitContext(rel, tuple); ++ HeapTupleSetSecurity(tuple, newcon); ++ } ++ sepgsqlCheckTuplePerms(rel, tuple, NULL, SEPGSQL_PERMS_INSERT, true); ++} ++ ++void sepgsqlSimpleHeapUpdate(Relation rel, ItemPointer tid, HeapTuple newtup) ++{ ++ HeapTuple oldtup; ++ Oid ncon, ocon; ++ uint32 perms = SEPGSQL_PERMS_UPDATE; ++ ++ if (!__is_simple_system_relation(rel)) ++ return; ++ ++ oldtup = __getHeapTupleFromItemPointer(rel, tid); ++ ncon = HeapTupleGetSecurity(newtup); ++ ocon = HeapTupleGetSecurity(oldtup); ++ if (ncon == InvalidOid) { ++ HeapTupleSetSecurity(newtup, ocon); ++ ncon = ocon; ++ } ++ ++ if (ncon == ocon) { ++ sepgsqlCheckTuplePerms(rel, oldtup, NULL, perms, true); ++ } else { ++ perms |= SEPGSQL_PERMS_RELABELFROM; ++ sepgsqlCheckTuplePerms(rel, oldtup, NULL, perms, true); ++ ++ sepgsqlCheckTuplePerms(rel, newtup, oldtup, SEPGSQL_PERMS_RELABELTO, true); ++ } ++ ++ heap_freetuple(oldtup); ++} ++ ++void sepgsqlSimpleHeapDelete(Relation rel, ItemPointer tid) ++{ ++ HeapTuple oldtup; ++ ++ if (!__is_simple_system_relation(rel)) ++ return; ++ ++ oldtup = __getHeapTupleFromItemPointer(rel, tid); ++ sepgsqlCheckTuplePerms(rel, oldtup, NULL, SEPGSQL_PERMS_DELETE, true); ++ heap_freetuple(oldtup); ++} ++ ++/******************************************************************************* ++ * ExecInsert/Delete/Update hooks ++ *******************************************************************************/ ++ ++bool sepgsqlExecInsert(Relation rel, HeapTuple tuple, bool with_returning) ++{ ++ Oid newcon; ++ uint32 perms; ++ ++ if (!sepgsqlIsEnabled()) ++ return true; /* always true, if disabled */ ++ ++ newcon = HeapTupleGetSecurity(tuple); ++ if (newcon == InvalidOid) { ++ /* no explicit labeling */ ++ newcon = sepgsqlComputeImplicitContext(rel, tuple); ++ HeapTupleSetSecurity(tuple, newcon); ++ } ++ perms = SEPGSQL_PERMS_INSERT; ++ if (with_returning) ++ perms |= SEPGSQL_PERMS_SELECT; ++ ++ return sepgsqlCheckTuplePerms(rel, tuple, NULL, perms, false); ++} ++ ++bool sepgsqlExecUpdate(Relation rel, HeapTuple newtup, ItemPointer tid, bool with_returning) ++{ ++ HeapTuple oldtup; ++ Oid newcon, oldcon; ++ uint32 perms = 0; ++ bool rc; ++ ++ oldtup = __getHeapTupleFromItemPointer(rel, tid); ++ newcon = HeapTupleGetSecurity(newtup); ++ oldcon = HeapTupleGetSecurity(oldtup); ++ if (newcon == InvalidOid) { ++ HeapTupleSetSecurity(newtup, oldcon); /* keep old context */ ++ oldcon = newcon; ++ } ++ if (newcon != oldcon) { ++ perms |= SEPGSQL_PERMS_RELABELTO; ++ if (with_returning) ++ perms |= SEPGSQL_PERMS_SELECT; ++ } ++ rc = sepgsqlCheckTuplePerms(rel, newtup, oldtup, perms, false); ++ ++ heap_freetuple(oldtup); ++ ++ return rc; ++} ++ ++bool sepgsqlExecDelete(Relation rel, ItemPointer tid, bool with_returning) ++{ ++ HeapTuple oldtup; ++ bool rc; ++ ++ oldtup = __getHeapTupleFromItemPointer(rel, tid); ++ ++ rc = sepgsqlCheckTuplePerms(rel, oldtup, NULL, 0, false); ++ ++ heap_freetuple(oldtup); ++ ++ return rc; ++} ++ ++/******************************************************************************* ++ * heap_insert/heap_update hooks -- the last gate of implicit labeling ++ *******************************************************************************/ ++void sepgsqlHeapInsert(Relation rel, HeapTuple tuple) ++{ ++ if (HeapTupleGetSecurity(tuple) == InvalidOid) { ++ Oid newcon = sepgsqlComputeImplicitContext(rel, tuple); ++ HeapTupleSetSecurity(tuple, newcon); ++ } ++} ++ ++void sepgsqlHeapUpdate(Relation rel, HeapTuple newtup, HeapTuple oldtup) ++{ ++ if (HeapTupleGetSecurity(newtup) == InvalidOid) { ++ Oid oldcon = HeapTupleGetSecurity(oldtup); ++ HeapTupleSetSecurity(newtup, oldcon); ++ } ++} +diff -rpNU3 base/src/backend/security/sepgsqlPerms.c sepgsql/src/backend/security/sepgsqlPerms.c +--- base/src/backend/security/sepgsqlPerms.c 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/sepgsqlPerms.c 2008-01-30 17:08:18.000000000 +0900 +@@ -0,0 +1,514 @@ ++/* ++ * src/backend/security/sepgsqlPerms.c ++ * SE-PostgreSQL permission checking functions ++ * ++ * Copyright (c) 2007 KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "access/genam.h" ++#include "access/heapam.h" ++#include "miscadmin.h" ++#include "security/pgace.h" ++#include "utils/builtins.h" ++#include "utils/fmgroids.h" ++#include "utils/syscache.h" ++#include "utils/typcache.h" ++ ++/* ++ * If we have to refere a object which is newly inserted or updated ++ * in the same command, SearchSysCache() returns NULL because it use ++ * SnapshowNow internally. The followings are fallback routine to ++ * avoid a failed cache lookup. ++ */ ++static Oid __lookupRelationForm(Oid relid, Form_pg_class classForm) { ++ Relation rel; ++ SysScanDesc scan; ++ ScanKeyData skey; ++ HeapTuple tuple; ++ Oid t_security; ++ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relid), ++ 0, 0, 0); ++ if (HeapTupleIsValid(tuple)) { ++ if (classForm) ++ memcpy(classForm, GETSTRUCT(tuple), sizeof(FormData_pg_class)); ++ t_security = HeapTupleGetSecurity(tuple); ++ ReleaseSysCache(tuple); ++ return t_security; ++ } ++ ++ rel = heap_open(RelationRelationId, AccessShareLock); ++ ScanKeyInit(&skey, ++ ObjectIdAttributeNumber, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(relid)); ++ scan = systable_beginscan(rel, ClassOidIndexId, ++ true, SnapshotSelf, 1, &skey); ++ tuple = systable_getnext(scan); ++ if (!HeapTupleIsValid(tuple)) ++ selerror("relation %u is not exist", relid); ++ ++ if (classForm) ++ memcpy(classForm, GETSTRUCT(tuple), sizeof(FormData_pg_class)); ++ t_security = HeapTupleGetSecurity(tuple); ++ ++ systable_endscan(scan); ++ heap_close(rel, AccessShareLock); ++ ++ return t_security; ++} ++ ++static uint32 __sepgsql_perms_to_common_perms(uint32 perms) { ++ uint32 __perms = 0; ++ ++ Assert((perms & ~SEPGSQL_PERMS_ALL) == 0); ++ __perms |= (perms & SEPGSQL_PERMS_USE ? COMMON_DATABASE__GETATTR : 0); ++ __perms |= (perms & SEPGSQL_PERMS_SELECT ? COMMON_DATABASE__GETATTR : 0); ++ __perms |= (perms & SEPGSQL_PERMS_UPDATE ? COMMON_DATABASE__SETATTR : 0); ++ __perms |= (perms & SEPGSQL_PERMS_INSERT ? COMMON_DATABASE__CREATE : 0); ++ __perms |= (perms & SEPGSQL_PERMS_DELETE ? COMMON_DATABASE__DROP : 0); ++ __perms |= (perms & SEPGSQL_PERMS_RELABELFROM ? COMMON_DATABASE__RELABELFROM : 0); ++ __perms |= (perms & SEPGSQL_PERMS_RELABELTO ? COMMON_DATABASE__RELABELTO : 0); ++ ++ return __perms; ++} ++ ++static uint32 __sepgsql_perms_to_tuple_perms(uint32 perms) { ++ uint32 __perms = 0; ++ ++ //Assert((perms & ~SEPGSQL_PERMS_ALL) == 0); ++ if (perms & ~SEPGSQL_PERMS_ALL) ++ selbugon(1); ++ __perms |= (perms & SEPGSQL_PERMS_USE ? DB_TUPLE__USE : 0); ++ __perms |= (perms & SEPGSQL_PERMS_SELECT ? DB_TUPLE__SELECT : 0); ++ __perms |= (perms & SEPGSQL_PERMS_UPDATE ? DB_TUPLE__UPDATE : 0); ++ __perms |= (perms & SEPGSQL_PERMS_INSERT ? DB_TUPLE__INSERT : 0); ++ __perms |= (perms & SEPGSQL_PERMS_DELETE ? DB_TUPLE__DELETE : 0); ++ __perms |= (perms & SEPGSQL_PERMS_RELABELFROM ? DB_TUPLE__RELABELFROM : 0); ++ __perms |= (perms & SEPGSQL_PERMS_RELABELTO ? DB_TUPLE__RELABELTO : 0); ++ ++ return __perms; ++} ++ ++char *sepgsqlGetTupleName(Oid relid, HeapTuple tuple) ++{ ++ char buffer[NAMEDATALEN * 2 + 32]; ++ ++ switch (relid) { ++ case AccessMethodRelationId: ++ return NameStr(((Form_pg_am) GETSTRUCT(tuple))->amname); ++ ++ case AttributeRelationId: { ++ Form_pg_attribute attrForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ Form_pg_class classForm; ++ HeapTuple reltup; ++ ++ if (IsBootstrapProcessingMode()) ++ return NameStr(attrForm->attname); ++ ++ reltup = SearchSysCache(RELOID, ++ ObjectIdGetDatum(attrForm->attrelid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(reltup)) ++ return NameStr(attrForm->attname); ++ ++ classForm = (Form_pg_class) GETSTRUCT(reltup); ++ snprintf(buffer, sizeof(buffer), "%s.%s", ++ NameStr(classForm->relname), ++ NameStr(attrForm->attname)); ++ ReleaseSysCache(reltup); ++ return pstrdup(buffer); ++ } ++ case AuthIdRelationId: ++ return NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname); ++ ++ case RelationRelationId: ++ return NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname); ++ ++ case ConstraintRelationId: ++ return NameStr(((Form_pg_constraint) GETSTRUCT(tuple))->conname); ++ ++ case ConversionRelationId: ++ return NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname); ++ ++ case DatabaseRelationId: ++ return NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); ++ ++ case LanguageRelationId: ++ return NameStr(((Form_pg_language) GETSTRUCT(tuple))->lanname); ++ ++ case LargeObjectRelationId: ++ snprintf(buffer, sizeof(buffer), "loid:%u", ++ ((Form_pg_largeobject) GETSTRUCT(tuple))->loid); ++ return pstrdup(buffer); ++ ++ case ListenerRelationId: ++ return NameStr(((Form_pg_listener) GETSTRUCT(tuple))->relname); ++ ++ case NamespaceRelationId: ++ return NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); ++ ++ case OperatorClassRelationId: ++ return NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname); ++ ++ case OperatorRelationId: ++ return NameStr(((Form_pg_operator) GETSTRUCT(tuple))->oprname); ++ ++ case PLTemplateRelationId: ++ return NameStr(((Form_pg_pltemplate) GETSTRUCT(tuple))->tmplname); ++ ++ case ProcedureRelationId: ++ return NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname); ++ ++ case RewriteRelationId: ++ return NameStr(((Form_pg_rewrite) GETSTRUCT(tuple))->rulename); ++ ++ case TableSpaceRelationId: ++ return NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname); ++ ++ case TriggerRelationId: ++ return NameStr(((Form_pg_trigger) GETSTRUCT(tuple))->tgname); ++ ++ case TypeRelationId: ++ snprintf(buffer, sizeof(buffer), "pg_type.%s", ++ NameStr(((Form_pg_type) GETSTRUCT(tuple))->typname)); ++ return pstrdup(buffer); ++ } ++ return NULL; ++} ++ ++static void __check_pg_attribute(HeapTuple tuple, HeapTuple oldtup, ++ uint32 *p_perms, uint16 *p_tclass) ++{ ++ Form_pg_attribute attrForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ FormData_pg_class classForm; ++ ++ switch (attrForm->attrelid) { ++ case TypeRelationId: ++ case ProcedureRelationId: ++ case AttributeRelationId: ++ case RelationRelationId: ++ /* those are pure relation */ ++ break; ++ default: ++ __lookupRelationForm(attrForm->attrelid, &classForm); ++ if (classForm.relkind != RELKIND_RELATION) { ++ *p_tclass = SECCLASS_DB_TUPLE; ++ *p_perms = __sepgsql_perms_to_tuple_perms(*p_perms); ++ return; ++ } ++ break; ++ } ++ *p_tclass = SECCLASS_DB_COLUMN; ++ *p_perms = __sepgsql_perms_to_common_perms(*p_perms); ++ if (HeapTupleIsValid(oldtup)) { ++ Form_pg_attribute oldForm = (Form_pg_attribute) GETSTRUCT(oldtup); ++ ++ if (oldForm->attisdropped != true && attrForm->attisdropped == true) ++ *p_perms |= DB_COLUMN__DROP; ++ } ++} ++ ++static void __check_pg_largeobject(HeapTuple tuple, HeapTuple oldtup, ++ uint32 *p_perms, uint16 *p_tclass) ++{ ++ Form_pg_largeobject blobForm ++ = (Form_pg_largeobject) GETSTRUCT(tuple); ++ Relation rel; ++ ScanKeyData skey; ++ SysScanDesc sd; ++ uint32 perms = 0; ++ ++ perms |= (*p_perms & SEPGSQL_PERMS_USE ? DB_BLOB__GETATTR : 0); ++ perms |= (*p_perms & SEPGSQL_PERMS_SELECT ? DB_BLOB__GETATTR : 0); ++ perms |= (*p_perms & SEPGSQL_PERMS_UPDATE ? DB_BLOB__SETATTR : 0); ++ perms |= (*p_perms & SEPGSQL_PERMS_READ ? DB_BLOB__READ : 0); ++ perms |= (*p_perms & SEPGSQL_PERMS_WRITE ? DB_BLOB__WRITE : 0); ++ ++ if (*p_perms & SEPGSQL_PERMS_INSERT) { ++ bool found = false; ++ ++ ScanKeyInit(&skey, ++ Anum_pg_largeobject_loid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(blobForm->loid)); ++ rel = heap_open(LargeObjectRelationId, AccessShareLock); ++ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, ++ SnapshotSelf, 1, &skey); ++ if (HeapTupleIsValid(systable_getnext(sd))) ++ found = true; ++ systable_endscan(sd); ++ heap_close(rel, AccessShareLock); ++ perms |= (!found ? DB_BLOB__CREATE : DB_BLOB__SETATTR | DB_BLOB__WRITE); ++ } ++ ++ if (*p_perms & SEPGSQL_PERMS_DELETE) { ++ HeapTuple exttup; ++ bool found = false; ++ ++ ScanKeyInit(&skey, ++ Anum_pg_largeobject_loid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(blobForm->loid)); ++ rel = heap_open(LargeObjectRelationId, AccessShareLock); ++ sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, ++ SnapshotSelf, 1, &skey); ++ while ((exttup = systable_getnext(sd))) { ++ int __pageno = ((Form_pg_largeobject) GETSTRUCT(exttup))->pageno; ++ ++ if (blobForm->pageno != __pageno) { ++ found = true; ++ break; ++ } ++ } ++ systable_endscan(sd); ++ heap_close(rel, AccessShareLock); ++ perms |= (!found ? DB_BLOB__DROP : DB_BLOB__SETATTR); ++ } ++ *p_tclass = SECCLASS_DB_BLOB; ++ *p_perms = perms; ++} ++ ++static void __check_pg_proc(HeapTuple tuple, HeapTuple oldtup, ++ uint32 *p_perms, uint16 *p_tclass) ++{ ++ uint32 perms = __sepgsql_perms_to_common_perms(*p_perms); ++ Form_pg_proc procForm = (Form_pg_proc) GETSTRUCT(tuple); ++ ++ if (procForm->prolang == ClanguageId) { ++ Datum oldbin, newbin; ++ bool isnull, verify = false; ++ ++ newbin = SysCacheGetAttr(PROCOID, tuple, ++ Anum_pg_proc_probin, &isnull); ++ if (!isnull) { ++ if (perms & DB_PROCEDURE__CREATE) { ++ verify = true; ++ } else if (HeapTupleIsValid(oldtup)) { ++ oldbin = SysCacheGetAttr(PROCOID, oldtup, ++ Anum_pg_proc_probin, &isnull); ++ if (isnull || DatumGetBool(DirectFunctionCall2(textne, oldbin, newbin))) ++ verify = true; ++ } ++ ++ if (verify) { ++ char *filename; ++ security_context_t filecon; ++ Datum filesid; ++ ++ /* <-- database:module_install --> */ ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ sepgsqlGetDatabaseContext(), ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__INSTALL_MODULE, ++ NULL); ++ ++ /* <-- database:module_install --> */ ++ filename = DatumGetCString(DirectFunctionCall1(textout, newbin)); ++ filename = expand_dynamic_library_name(filename); ++ if (getfilecon_raw(filename, &filecon) < 1) ++ selerror("could not obtain the security context of '%s'", filename); ++ PG_TRY(); ++ { ++ filesid = DirectFunctionCall1(security_label_raw_in, ++ CStringGetDatum(filecon)); ++ } ++ PG_CATCH(); ++ { ++ freecon(filecon); ++ PG_RE_THROW(); ++ } ++ PG_END_TRY(); ++ freecon(filecon); ++ ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ DatumGetObjectId(filesid), ++ SECCLASS_DB_DATABASE, ++ DB_DATABASE__INSTALL_MODULE, ++ filename); ++ } ++ } ++ } ++ *p_perms = perms; ++ *p_tclass = SECCLASS_DB_PROCEDURE; ++} ++ ++static void __check_pg_relation(HeapTuple tuple, HeapTuple oldtup, ++ uint32 *p_perms, uint16 *p_tclass) ++{ ++ Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple); ++ if (classForm->relkind == RELKIND_RELATION) { ++ *p_tclass = SECCLASS_DB_TABLE; ++ *p_perms = __sepgsql_perms_to_common_perms(*p_perms); ++ } else { ++ *p_tclass = SECCLASS_DB_TUPLE; ++ *p_perms = __sepgsql_perms_to_tuple_perms(*p_perms); ++ } ++} ++ ++static bool __check_tuple_perms(Oid tableoid, Oid tcontext, uint32 perms, ++ HeapTuple tuple, HeapTuple oldtup, bool abort) ++{ ++ uint16 tclass; ++ bool rc = true; ++ ++ Assert(tuple != NULL); ++ ++ switch (tableoid) { ++ case DatabaseRelationId: /* pg_database */ ++ perms = __sepgsql_perms_to_common_perms(perms); ++ tclass = SECCLASS_DB_DATABASE; ++ break; ++ ++ case RelationRelationId: /* pg_class */ ++ __check_pg_relation(tuple, oldtup, &perms, &tclass); ++ break; ++ ++ case AttributeRelationId: /* pg_attribute */ ++ __check_pg_attribute(tuple, oldtup, &perms, &tclass); ++ break; ++ ++ case ProcedureRelationId: /* pg_proc */ ++ __check_pg_proc(tuple, oldtup, &perms, &tclass); ++ break; ++ ++ case LargeObjectRelationId: /* pg_largeobject */ ++ __check_pg_largeobject(tuple, oldtup, &perms, &tclass); ++ break; ++ ++ default: ++ perms = __sepgsql_perms_to_tuple_perms(perms); ++ tclass = SECCLASS_DB_TUPLE; ++ break; ++ } ++ ++ if (perms) { ++ char *audit; ++ rc = sepgsql_avc_permission_noaudit(sepgsqlGetClientContext(), ++ tcontext, ++ tclass, ++ perms, ++ &audit, ++ sepgsqlGetTupleName(tableoid, tuple)); ++ sepgsql_audit(abort ? rc : true, audit); ++ } ++ return rc; ++} ++ ++/* ++ * MEMO: we cannot obtain system column from RECORD datatype. ++ * If those are necesasry, they should be separately delivered. ++ */ ++Datum sepgsql_tuple_perms(PG_FUNCTION_ARGS) ++{ ++ Oid tableoid = PG_GETARG_OID(0); ++ Oid tcontext = PG_GETARG_OID(1); ++ uint32 perms = PG_GETARG_UINT32(2); ++ HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(3); ++ HeapTupleData tuple; ++ ++ tuple.t_len = HeapTupleHeaderGetDatumLength(rec); ++ ItemPointerSetInvalid(&tuple.t_self); ++ tuple.t_tableOid = tableoid; ++ tuple.t_data = rec; ++ ++ PG_RETURN_BOOL(__check_tuple_perms(tableoid, tcontext, perms, &tuple, NULL, false)); ++} ++ ++Datum sepgsql_tuple_perms_abort(PG_FUNCTION_ARGS) ++{ ++ Oid tableoid = PG_GETARG_OID(0); ++ Oid tcontext = PG_GETARG_OID(1); ++ uint32 perms = PG_GETARG_UINT32(2); ++ HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(3); ++ HeapTupleData tuple; ++ ++ tuple.t_len = HeapTupleHeaderGetDatumLength(rec); ++ ItemPointerSetInvalid(&tuple.t_self); ++ tuple.t_tableOid = tableoid; ++ tuple.t_data = rec; ++ ++ PG_RETURN_BOOL(__check_tuple_perms(tableoid, tcontext, perms, &tuple, NULL, true)); ++} ++ ++bool sepgsqlCheckTuplePerms(Relation rel, HeapTuple tuple, HeapTuple oldtup, uint32 perms, bool abort) ++{ ++ return __check_tuple_perms(RelationGetRelid(rel), ++ HeapTupleGetSecurity(tuple), ++ perms, ++ tuple, ++ oldtup, ++ abort); ++} ++ ++Oid sepgsqlComputeImplicitContext(Relation rel, HeapTuple tuple) { ++ uint16 tclass; ++ Oid tcon; ++ ++ switch (RelationGetRelid(rel)) { ++ case DatabaseRelationId: /* pg_database */ ++ tclass = SECCLASS_DB_DATABASE; ++ tcon = sepgsqlGetServerContext(); ++ break; ++ ++ case RelationRelationId: { /* pg_class */ ++ Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple); ++ if (classForm->relkind == RELKIND_RELATION) { ++ tclass = SECCLASS_DB_TABLE; ++ tcon = sepgsqlGetDatabaseContext(); ++ break; ++ } ++ tcon = __lookupRelationForm(RelationRelationId, NULL); ++ tclass = SECCLASS_DB_TUPLE; ++ break; ++ } ++ case AttributeRelationId: { /* pg_attribute */ ++ Form_pg_attribute attrForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ FormData_pg_class classForm; ++ ++ /* special case in bootstraping mode */ ++ if (IsBootstrapProcessingMode() ++ && (attrForm->attrelid == TypeRelationId || ++ attrForm->attrelid == ProcedureRelationId || ++ attrForm->attrelid == AttributeRelationId || ++ attrForm->attrelid == RelationRelationId)) { ++ tcon = sepgsql_avc_createcon(sepgsqlGetClientContext(), ++ sepgsqlGetDatabaseContext(), ++ SECCLASS_DB_TABLE); ++ tclass = SECCLASS_DB_COLUMN; ++ break; ++ } ++ tcon = __lookupRelationForm(attrForm->attrelid, &classForm); ++ tclass = (classForm.relkind == RELKIND_RELATION ++ ? SECCLASS_DB_COLUMN ++ : SECCLASS_DB_TUPLE); ++ break; ++ } ++ case ProcedureRelationId: ++ tclass = SECCLASS_DB_PROCEDURE; ++ tcon = sepgsqlGetDatabaseContext(); ++ break; ++ ++ case LargeObjectRelationId: ++ tclass = SECCLASS_DB_BLOB; ++ tcon = sepgsqlGetDatabaseContext(); ++ break; ++ ++ case TypeRelationId: /* pg_type */ ++ if (IsBootstrapProcessingMode()) { ++ /* special case in early phase */ ++ tcon = sepgsql_avc_createcon(sepgsqlGetClientContext(), ++ sepgsqlGetDatabaseContext(), ++ SECCLASS_DB_TABLE); ++ tclass = SECCLASS_DB_TUPLE; ++ break; ++ } ++ default: ++ tclass = SECCLASS_DB_TUPLE; ++ tcon = __lookupRelationForm(RelationGetRelid(rel), NULL); ++ break; ++ } ++ return sepgsql_avc_createcon(sepgsqlGetClientContext(), tcon, tclass); ++} +diff -rpNU3 base/src/backend/security/sepgsqlProxy.c sepgsql/src/backend/security/sepgsqlProxy.c +--- base/src/backend/security/sepgsqlProxy.c 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/backend/security/sepgsqlProxy.c 2008-04-30 10:35:11.000000000 +0900 +@@ -0,0 +1,1510 @@ ++/* ++ * src/backend/security/sepgsqlProxy.c ++ * SE-PostgreSQL Query Proxy function to walk on query node tree ++ * and append tuple filter. ++ * ++ * Copyright KaiGai Kohei ++ */ ++#include "postgres.h" ++ ++#include "access/genam.h" ++#include "access/heapam.h" ++#include "catalog/heap.h" ++#include "catalog/pg_attribute.h" ++#include "catalog/pg_class.h" ++#include "catalog/pg_database.h" ++#include "catalog/pg_largeobject.h" ++#include "catalog/pg_operator.h" ++#include "catalog/pg_proc.h" ++#include "catalog/pg_type.h" ++#include "executor/spi.h" ++#include "nodes/makefuncs.h" ++#include "optimizer/plancat.h" ++#include "parser/parse_relation.h" ++#include "parser/parse_target.h" ++#include "security/pgace.h" ++#include "storage/lock.h" ++#include "utils/fmgroids.h" ++#include "utils/syscache.h" ++ ++/* SE-PostgreSQL Evaluation Item */ ++#define T_SEvalItem (T_TIDBitmap + 1) /* must be unique identifier */ ++ ++typedef struct SEvalItem { ++ NodeTag type; ++ uint16 tclass; ++ uint32 perms; ++ union { ++ struct { ++ Oid relid; ++ bool inh; ++ } c; /* for pg_class */ ++ struct { ++ Oid relid; ++ bool inh; ++ AttrNumber attno; ++ } a; /* for pg_attribute */ ++ struct { ++ Oid funcid; ++ } p; /* for pg_proc */ ++ }; ++} SEvalItem; ++ ++/* query stack definition for outer references */ ++typedef struct queryChain { ++ struct queryChain *parent; ++ Query *tail; ++} queryChain; ++ ++static inline queryChain *upperQueryChain(queryChain *qc, int lvup) { ++ while (lvup > 0) { ++ Assert(!!qc->parent); ++ qc = qc->parent; ++ lvup--; ++ } ++ return qc; ++} ++ ++static inline Query *getQueryFromChain(queryChain *qc) { ++ return qc->tail; ++} ++ ++/* static definitions for proxy functions */ ++static List *proxyRteRelation(List *selist, queryChain *qc, int rtindex, Node **quals); ++static List *proxyRteSubQuery(List *selist, queryChain *qc, Query *query); ++static List *proxyJoinTree(List *selist, queryChain *qc, Node *n, Node **quals); ++static List *proxySetOperations(List *selist, queryChain *qc, Node *n); ++ ++/* static */ ++static List *sepgsqlWalkExpr(List *selist, queryChain *qc, Node *n, int flags); ++#define WKFLAG_INTERNAL_USE (0x0001) ++ ++/* ----------------------------------------------------------- ++ * addEvalXXXX -- add evaluation items into Query->SEvalItemList. ++ * Those are used for execution phase. ++ * ----------------------------------------------------------- */ ++static List *__addEvalPgClass(List *selist, Oid relid, bool inh, uint32 perms) ++{ ++ SEvalItem *se; ++ ListCell *l; ++ ++ foreach (l, selist) { ++ se = (SEvalItem *) lfirst(l); ++ if (se->tclass == SECCLASS_DB_TABLE ++ && se->c.relid == relid ++ && se->c.inh == inh) { ++ se->perms |= perms; ++ return selist; ++ } ++ } ++ /* not found */ ++ se = makeNode(SEvalItem); ++ se->tclass = SECCLASS_DB_TABLE; ++ se->perms = perms; ++ se->c.relid = relid; ++ se->c.inh = inh; ++ return lappend(selist, se); ++} ++ ++static List *addEvalPgClass(List *selist, RangeTblEntry *rte, uint32 perms) ++{ ++ rte->requiredPerms |= (perms & DB_TABLE__USE ? SEPGSQL_PERMS_USE : 0); ++ rte->requiredPerms |= (perms & DB_TABLE__SELECT ? SEPGSQL_PERMS_SELECT : 0); ++ rte->requiredPerms |= (perms & DB_TABLE__INSERT ? SEPGSQL_PERMS_INSERT : 0); ++ rte->requiredPerms |= (perms & DB_TABLE__UPDATE ? SEPGSQL_PERMS_UPDATE : 0); ++ rte->requiredPerms |= (perms & DB_TABLE__DELETE ? SEPGSQL_PERMS_DELETE : 0); ++ ++ return __addEvalPgClass(selist, rte->relid, rte->inh, perms); ++} ++ ++static List *__addEvalPgAttribute(List *selist, Oid relid, bool inh, AttrNumber attno, uint32 perms) ++{ ++ ListCell *l; ++ SEvalItem *se; ++ ++ foreach (l, selist) { ++ se = (SEvalItem *) lfirst(l); ++ if (se->tclass == SECCLASS_DB_COLUMN ++ && se->a.relid == relid ++ && se->a.inh == inh ++ && se->a.attno == attno) { ++ se->perms |= perms; ++ return selist; ++ } ++ } ++ /* not found */ ++ se = makeNode(SEvalItem); ++ se->tclass = SECCLASS_DB_COLUMN; ++ se->perms = perms; ++ se->a.relid = relid; ++ se->a.inh = inh; ++ se->a.attno = attno; ++ ++ return lappend(selist, se); ++} ++ ++static List *addEvalPgAttribute(List *selist, RangeTblEntry *rte, AttrNumber attno, uint32 perms) ++{ ++ /* for 'security_context' */ ++ if (attno == SecurityAttributeNumber ++ && (perms & (DB_COLUMN__UPDATE | DB_COLUMN__INSERT))) ++ rte->requiredPerms |= SEPGSQL_PERMS_RELABELFROM; ++ ++ /* for 'pg_largeobject' */ ++ if (rte->relid == LargeObjectRelationId ++ && attno == Anum_pg_largeobject_data) { ++ if (perms & DB_COLUMN__SELECT) ++ rte->requiredPerms |= SEPGSQL_PERMS_READ; ++ if (perms & (DB_COLUMN__UPDATE | DB_COLUMN__INSERT)) ++ rte->requiredPerms |= SEPGSQL_PERMS_WRITE; ++ } ++ ++ return __addEvalPgAttribute(selist, rte->relid, rte->inh, attno, perms); ++} ++ ++static List *addEvalPgProc(List *selist, Oid funcid, uint32 perms) ++{ ++ ListCell *l; ++ SEvalItem *se; ++ ++ foreach (l, selist) { ++ se = (SEvalItem *) lfirst(l); ++ if (se->tclass == SECCLASS_DB_PROCEDURE ++ && se->p.funcid == funcid) { ++ se->perms |= perms; ++ return selist; ++ } ++ } ++ se = makeNode(SEvalItem); ++ se->tclass = SECCLASS_DB_PROCEDURE; ++ se->perms = perms; ++ se->p.funcid = funcid; ++ ++ return lappend(selist, se); ++} ++ ++static List *addEvalTriggerAccess(List *selist, Oid relid, bool is_inh, int cmdType) ++{ ++ Relation rel; ++ SysScanDesc scan; ++ ScanKeyData skey; ++ HeapTuple tuple; ++ bool checked = false; ++ ++ Assert(cmdType == CMD_INSERT || cmdType == CMD_UPDATE || cmdType == CMD_DELETE); ++ ++ rel = heap_open(TriggerRelationId, AccessShareLock); ++ ScanKeyInit(&skey, ++ Anum_pg_trigger_tgrelid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(relid)); ++ scan = systable_beginscan(rel, TriggerRelidNameIndexId, ++ true, SnapshotNow, 1, &skey); ++ while (HeapTupleIsValid((tuple = systable_getnext(scan)))) { ++ Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple); ++ ++ if (!trigForm->tgenabled) ++ continue; ++ ++ if ((cmdType == CMD_INSERT && !TRIGGER_FOR_INSERT(trigForm->tgtype)) ++ || (cmdType == CMD_UPDATE && !TRIGGER_FOR_UPDATE(trigForm->tgtype)) ++ || (cmdType == CMD_DELETE && !TRIGGER_FOR_DELETE(trigForm->tgtype))) ++ continue; ++ ++ /* per STATEMENT trigger cannot refer whole of a tuple */ ++ if (!TRIGGER_FOR_ROW(trigForm->tgtype)) ++ continue; ++ ++ /* BEFORE-ROW-INSERT trigger cannot refer whole of a tuple */ ++ if (TRIGGER_FOR_BEFORE(trigForm->tgtype) && TRIGGER_FOR_INSERT(trigForm->tgtype)) ++ continue; ++ ++ selist = addEvalPgProc(selist, trigForm->tgfoid, DB_PROCEDURE__EXECUTE); ++ if (!checked) { ++ HeapTuple reltup; ++ Form_pg_class classForm; ++ AttrNumber attnum; ++ ++ reltup = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relid), ++ 0, 0, 0); ++ classForm = (Form_pg_class) GETSTRUCT(reltup); ++ ++ selist = __addEvalPgClass(selist, relid, false, DB_TABLE__SELECT); ++ for (attnum = FirstLowInvalidHeapAttributeNumber + 1; attnum <= 0; attnum++) { ++ if (attnum == ObjectIdAttributeNumber && !classForm->relhasoids) ++ continue; ++ selist = __addEvalPgAttribute(selist, relid, false, attnum, DB_COLUMN__SELECT); ++ } ++ ReleaseSysCache(reltup); ++ ++ checked = true; ++ } ++ } ++ systable_endscan(scan); ++ heap_close(rel, AccessShareLock); ++ ++ if (is_inh) { ++ List *child_list = find_inheritance_children(relid); ++ ListCell *l; ++ ++ foreach(l, child_list) ++ selist = addEvalTriggerAccess(selist, lfirst_oid(l), is_inh, cmdType); ++ } ++ ++ return selist; ++} ++ ++/* ******************************************************************************* ++ * walkExpr() -- walk on expression tree recursively to pick up and to construct ++ * a SEvalItem list related to expression node. ++ * It is evaluated at later phase. ++ * *******************************************************************************/ ++static List *walkVarHelper(List *selist, queryChain *qc, Var *var, int flags) ++{ ++ RangeTblEntry *rte; ++ Query *query; ++ Node *n; ++ ++ Assert(IsA(var, Var)); ++ if (!qc) ++ selerror("we could not use Var node in parameter list"); ++ ++ qc = upperQueryChain(qc, var->varlevelsup); ++ query = getQueryFromChain(qc); ++ rte = list_nth(query->rtable, var->varno - 1); ++ Assert(IsA(rte, RangeTblEntry)); ++ ++ switch (rte->rtekind) { ++ case RTE_RELATION: ++ /* table:{select} */ ++ selist = addEvalPgClass(selist, rte, ++ (flags & WKFLAG_INTERNAL_USE) ++ ? DB_TABLE__USE : DB_TABLE__SELECT); ++ /* column:{select} */ ++ selist = addEvalPgAttribute(selist, rte, var->varattno, ++ (flags & WKFLAG_INTERNAL_USE) ++ ? DB_COLUMN__USE : DB_COLUMN__SELECT); ++ break; ++ case RTE_JOIN: ++ n = list_nth(rte->joinaliasvars, var->varattno - 1); ++ selist = sepgsqlWalkExpr(selist, qc, n, flags); ++ break; ++ case RTE_SUBQUERY: ++ /* In normal cases, rte->relid equals zero for subquery. ++ * If rte->relid has none-zero value, it's rewritten subquery ++ * for outer join handling. ++ */ ++ if (rte->relid) { ++ Query *sqry = rte->subquery; ++ RangeTblEntry *srte; ++ TargetEntry *tle; ++ Var *svar; ++ ++ Assert(sqry->commandType == CMD_SELECT); ++ Assert(list_length(sqry->rtable) == 1); ++ ++ srte = (RangeTblEntry *) list_nth(sqry->rtable, 0); ++ Assert(srte->rtekind == RTE_RELATION); ++ Assert(srte->relid == rte->relid); ++ ++ if (var->varattno < 1) { ++ ListCell *l; ++ bool found = false; ++ ++ foreach(l, sqry->targetList) { ++ TargetEntry *tle = lfirst(l); ++ ++ Assert(IsA(tle, TargetEntry)); ++ if (IsA(tle->expr, Const)) ++ continue; ++ ++ svar = (Var *) tle->expr; ++ Assert(IsA(svar, Var)); ++ if (svar->varattno == var->varattno) { ++ var->varattno = tle->resno; ++ found = true; ++ break; ++ } ++ } ++ if (!found) { ++ AttrNumber resno = list_length(sqry->targetList) + 1; ++ svar = makeVar(1, ++ var->varattno, ++ var->vartype, ++ var->vartypmod, ++ 0); ++ tle = makeTargetEntry((Expr *) svar, resno, NULL, false); ++ var->varattno = resno; ++ sqry->targetList = lappend(sqry->targetList, tle); ++ } ++ } else { ++ tle = list_nth(sqry->targetList, var->varattno - 1); ++ Assert(IsA(tle, TargetEntry)); ++ if (!IsA(tle->expr, Var)) ++ selerror("dropped column is accessed (relid=%u, attno=%d)", ++ rte->relid, var->varattno); ++ svar = (Var *) tle->expr; ++ } ++ /* table:{select} or [use} */ ++ selist = addEvalPgClass(selist, srte, ++ (flags & WKFLAG_INTERNAL_USE) ++ ? DB_TABLE__USE : DB_TABLE__SELECT); ++ /* column:{select} or {use}*/ ++ selist = addEvalPgAttribute(selist, srte, svar->varattno, ++ (flags & WKFLAG_INTERNAL_USE) ++ ? DB_COLUMN__USE : DB_COLUMN__SELECT); ++ } ++ break; ++ case RTE_SPECIAL: ++ case RTE_FUNCTION: ++ case RTE_VALUES: ++ break; ++ default: ++ selerror("unrecognized rtekind (%d)", rte->rtekind); ++ break; ++ } ++ return selist; ++} ++ ++static List *walkOpExprHelper(List *selist, Oid opid) ++{ ++ HeapTuple tuple; ++ Form_pg_operator oprform; ++ ++ tuple = SearchSysCache(OPEROID, ++ ObjectIdGetDatum(opid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ selerror("cache lookup failed for OPEROID = %u", opid); ++ oprform = (Form_pg_operator) GETSTRUCT(tuple); ++ ++ selist = addEvalPgProc(selist, oprform->oprcode, DB_PROCEDURE__EXECUTE); ++ /* NOTE: opr->oprrest and opr->oprjoin are internal use only ++ * and have no effect onto the data references, so we don't ++ * apply any checkings for them. ++ */ ++ ReleaseSysCache(tuple); ++ ++ return selist; ++} ++ ++static List *sepgsqlWalkExpr(List *selist, queryChain *qc, Node *node, int flags) ++{ ++ if (node == NULL) ++ return selist; ++ ++ switch (nodeTag(node)) { ++ case T_Const: ++ case T_Param: ++ case T_CaseTestExpr: ++ case T_CoerceToDomainValue: ++ case T_SetToDefault: ++ /* do nothing */ ++ break; ++ case T_List: { ++ ListCell *l; ++ ++ foreach (l, (List *) node) ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) lfirst(l), flags); ++ break; ++ } ++ case T_Var: { ++ selist = walkVarHelper(selist, qc, (Var *) node, flags); ++ break; ++ } ++ case T_FuncExpr: { ++ FuncExpr *func = (FuncExpr *) node; ++ ++ selist = addEvalPgProc(selist, func->funcid, DB_PROCEDURE__EXECUTE); ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) func->args, flags); ++ break; ++ } ++ case T_Aggref: { ++ Aggref *aggref = (Aggref *) node; ++ ++ selist = addEvalPgProc(selist, aggref->aggfnoid, DB_PROCEDURE__EXECUTE); ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) aggref->args, flags); ++ break; ++ } ++ case T_OpExpr: ++ case T_DistinctExpr: /* typedef of OpExpr */ ++ case T_NullIfExpr: /* typedef of OpExpr */ ++ { ++ OpExpr *op = (OpExpr *) node; ++ ++ selist = walkOpExprHelper(selist, op->opno); ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) op->args, flags); ++ break; ++ } ++ case T_ScalarArrayOpExpr: { ++ ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) node; ++ ++ selist = walkOpExprHelper(selist, saop->opno); ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) saop->args, flags); ++ break; ++ } ++ case T_BoolExpr: { ++ selist = sepgsqlWalkExpr(selist, qc, ++ (Node *) ((BoolExpr *) node)->args, flags); ++ break; ++ } ++ case T_ArrayRef: { ++ ArrayRef *aref = (ArrayRef *) node; ++ ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) aref->refupperindexpr, flags); ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) aref->reflowerindexpr, flags); ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) aref->refexpr, flags); ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) aref->refassgnexpr, flags); ++ break; ++ } ++ case T_SubLink: { ++ SubLink *slink = (SubLink *) node; ++ ++ Assert(IsA(slink->subselect, Query)); ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) slink->testexpr, flags); ++ selist = proxyRteSubQuery(selist, qc, (Query *) slink->subselect); ++ break; ++ } ++ case T_SortClause: ++ case T_GroupClause: /* typedef of SortClause */ ++ { ++ SortClause *sort = (SortClause *) node; ++ Query *query = getQueryFromChain(qc); ++ ListCell *l; ++ ++ foreach (l, query->targetList) { ++ TargetEntry *tle = (TargetEntry *) lfirst(l); ++ Assert(IsA(tle, TargetEntry)); ++ if (tle->ressortgroupref == sort->tleSortGroupRef) { ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) tle->expr, flags); ++ break; ++ } ++ } ++ break; ++ } ++ case T_CoerceToDomain: { ++ selist = sepgsqlWalkExpr(selist, qc, ++ (Node *) ((CoerceToDomain *) node)->arg, flags); ++ break; ++ } ++ case T_CaseExpr: { ++ CaseExpr *ce = (CaseExpr *) node; ++ ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) ce->arg, flags); ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) ce->args, flags); ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) ce->defresult, flags); ++ break; ++ } ++ case T_CaseWhen: { ++ CaseWhen *casewhen = (CaseWhen *) node; ++ ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) casewhen->expr, flags); ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) casewhen->result, flags); ++ break; ++ } ++ case T_RelabelType: { ++ selist = sepgsqlWalkExpr(selist, qc, ++ (Node *) ((RelabelType *) node)->arg, flags); ++ break; ++ } ++ case T_CoalesceExpr: { ++ selist = sepgsqlWalkExpr(selist, qc, ++ (Node *) ((CoalesceExpr *) node)->args, flags); ++ break; ++ } ++ case T_MinMaxExpr: { ++ selist = sepgsqlWalkExpr(selist, qc, ++ (Node *) ((MinMaxExpr *) node)->args, flags); ++ break; ++ } ++ case T_NullTest: { ++ selist = sepgsqlWalkExpr(selist, qc, ++ (Node *) ((NullTest *) node)->arg, flags); ++ break; ++ } ++ case T_BooleanTest: { ++ selist = sepgsqlWalkExpr(selist, qc, ++ (Node *) ((BooleanTest *) node)->arg, flags); ++ break; ++ } ++ case T_FieldSelect: { ++ selist = sepgsqlWalkExpr(selist, qc, ++ (Node *) ((FieldSelect *) node)->arg, flags); ++ break; ++ } ++ case T_FieldStore: { ++ FieldStore *fstore = (FieldStore *) node; ++ ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) fstore->arg, flags); ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) fstore->newvals, flags); ++ break; ++ } ++ case T_ArrayExpr: { ++ selist = sepgsqlWalkExpr(selist, qc, ++ (Node *) ((ArrayExpr *) node)->elements, flags); ++ break; ++ } ++ case T_RowExpr: { ++ selist = sepgsqlWalkExpr(selist, qc, ++ (Node *) ((RowExpr *) node)->args, flags); ++ break; ++ } ++ case T_RowCompareExpr: { ++ RowCompareExpr *rce = (RowCompareExpr *) node; ++ ListCell *l; ++ ++ foreach (l, rce->opnos) ++ selist = walkOpExprHelper(selist, lfirst_oid(l)); ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) rce->largs, flags); ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) rce->rargs, flags); ++ break; ++ } ++ case T_ConvertRowtypeExpr: { ++ selist = sepgsqlWalkExpr(selist, qc, ++ (Node *) ((ConvertRowtypeExpr *) node)->arg, flags); ++ break; ++ } ++ default: ++ selnotice("node(%d) is ignored => %s", nodeTag(node), nodeToString(node)); ++ break; ++ } ++ return selist; ++} ++ ++/* ******************************************************************************* ++ * proxyRteXXXX() -- check any relation type objects in the required query, ++ * including general relation, outer|inner|cross join and subquery. ++ * ++ * sepgsqlProxyQuery() is called just after query rewriting phase to constract ++ * a list of SEvalItems. It is attached into Query->pgaceList and evaluated by ++ * sepgsqlVerifyQuery() at later phase. ++ * *******************************************************************************/ ++ ++static Oid fnoid_sepgsql_tuple_perm = F_SEPGSQL_TUPLE_PERMS; ++ ++/* ++ * When we use LEFT OUTER JOIN, any condition defined at ON clause are not ++ * considered to filter tuples, so left-hand relation have to be re-written ++ * as a subquery to filter violated tuples. ++ */ ++static List *makePseudoTargetList(Oid relid) { ++ HeapTuple reltup, atttup; ++ Form_pg_class classForm; ++ Form_pg_attribute attrForm; ++ AttrNumber attno, relnatts; ++ TargetEntry *tle; ++ Expr *expr; ++ List *targetList = NIL; ++ ++ reltup = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(reltup)) ++ selerror("relation (oid: %u) does not exist", relid); ++ ++ classForm = (Form_pg_class) GETSTRUCT(reltup); ++ relnatts = classForm->relnatts; ++ for (attno = 1; attno <= relnatts; attno++) { ++ atttup = SearchSysCache(ATTNUM, ++ ObjectIdGetDatum(relid), ++ Int16GetDatum(attno), ++ 0, 0); ++ if (!HeapTupleIsValid(atttup)) ++ selerror("attribute %u of relation '%s' does not exist", ++ attno, NameStr(classForm->relname)); ++ attrForm = (Form_pg_attribute) GETSTRUCT(atttup); ++ if (attrForm->attisdropped) { ++ expr = (Expr *) makeNullConst(INT4OID); ++ } else { ++ expr = (Expr *) makeVar(1, ++ attno, ++ attrForm->atttypid, ++ attrForm->atttypmod, ++ 0); ++ } ++ tle = makeTargetEntry(expr, attno, NULL, false); ++ targetList = lappend(targetList, tle); ++ ReleaseSysCache(atttup); ++ ++ Assert(list_length(targetList) == attno); ++ } ++ ReleaseSysCache(reltup); ++ ++ return targetList; ++} ++ ++static void rewriteOuterJoinTree(Node *n, Query *query, bool is_outer_join) ++{ ++ RangeTblRef *rtr, *srtr; ++ RangeTblEntry *rte, *srte; ++ Query *sqry; ++ FromExpr *sfrm; ++ ++ if (IsA(n, RangeTblRef)) { ++ if (!is_outer_join) ++ return; ++ ++ rtr = (RangeTblRef *) n; ++ rte = list_nth(query->rtable, rtr->rtindex - 1); ++ Assert(IsA(rte, RangeTblEntry)); ++ if (rte->rtekind != RTE_RELATION) ++ return; ++ ++ /* setup alternative query */ ++ sqry = makeNode(Query); ++ sqry->commandType = CMD_SELECT; ++ sqry->targetList = makePseudoTargetList(rte->relid); ++ ++ srte = copyObject(rte); ++ sqry->rtable = list_make1(srte); ++ ++ srtr = makeNode(RangeTblRef); ++ srtr->rtindex = 1; ++ ++ sfrm = makeNode(FromExpr); ++ sfrm->fromlist = list_make1(srtr); ++ sfrm->quals = NULL; ++ ++ sqry->jointree = sfrm; ++ sqry->hasSubLinks = false; ++ sqry->hasAggs = false; ++ ++ rte->rtekind = RTE_SUBQUERY; ++ rte->subquery = sqry; ++ } else if (IsA(n, FromExpr)) { ++ FromExpr *f = (FromExpr *)n; ++ ListCell *l; ++ ++ foreach (l, f->fromlist) ++ rewriteOuterJoinTree(lfirst(l), query, false); ++ } else if (IsA(n, JoinExpr)) { ++ JoinExpr *j = (JoinExpr *) n; ++ ++ rewriteOuterJoinTree(j->larg, query, ++ (j->jointype == JOIN_LEFT || j->jointype == JOIN_FULL)); ++ rewriteOuterJoinTree(j->rarg, query, ++ (j->jointype == JOIN_RIGHT || j->jointype == JOIN_FULL)); ++ } else { ++ selerror("unrecognized node type (%d) in query->jointree", nodeTag(n)); ++ } ++} ++ ++static List *proxyRteRelation(List *selist, queryChain *qc, int rtindex, Node **quals) ++{ ++ Query *query; ++ RangeTblEntry *rte; ++ Relation rel; ++ TupleDesc tdesc; ++ uint32 perms; ++ ++ query = getQueryFromChain(qc); ++ rte = list_nth(query->rtable, rtindex - 1); ++ rel = relation_open(rte->relid, AccessShareLock); ++ tdesc = RelationGetDescr(rel); ++ ++ /* append sepgsql_tuple_perm(relid, record, perms) */ ++ perms = rte->requiredPerms & SEPGSQL_PERMS_ALL; ++ if (perms) { ++ Var *v1, *v2, *v4; ++ Const *c3; ++ FuncExpr *func; ++ ++ /* 1st arg : Oid of the target relation */ ++ v1 = makeVar(rtindex, TableOidAttributeNumber, OIDOID, -1, 0); ++ ++ /* 2nd arg : Security Attribute of tuple */ ++ v2 = makeVar(rtindex, SecurityAttributeNumber, OIDOID, -1, 0); ++ ++ /* 3rd arg : permission set */ ++ c3 = makeConst(INT4OID, sizeof(int32), Int32GetDatum(perms), false, true); ++ ++ /* 4th arg : RECORD of the target relation */ ++ v4 = makeVar(rtindex, 0, RelationGetForm(rel)->reltype, -1, 0); ++ ++ /* append sepgsql_tuple_perm */ ++ func = makeFuncExpr(fnoid_sepgsql_tuple_perm, BOOLOID, ++ list_make4(v1, v2, c3, v4), COERCE_DONTCARE); ++ if (*quals == NULL) { ++ *quals = (Node *) func; ++ } else { ++ *quals = (Node *) makeBoolExpr(AND_EXPR, list_make2(func, *quals)); ++ } ++ } ++ relation_close(rel, NoLock); ++ ++ return selist; ++} ++ ++static List *proxyRteOuterJoin(List *selist, queryChain *qc, Query *query) ++{ ++ queryChain qcData; ++ ListCell *l; ++ ++ qcData.parent = qc; ++ qcData.tail = query; ++ qc = &qcData; ++ ++ selist = proxyRteRelation(selist, qc, 1, &query->jointree->quals); ++ ++ /* clean-up polluted RangeTblEntry */ ++ foreach (l, query->rtable) { ++ RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); ++ rte->requiredPerms &= ~SEPGSQL_PERMS_ALL; ++ } ++ ++ return selist; ++} ++ ++static List *__checkSelectTargets(List *selist, Query *query, Node *node) ++{ ++ if (node == NULL) ++ return selist; ++ ++ if (IsA(node, RangeTblRef)) { ++ RangeTblRef *rtr = (RangeTblRef *) node; ++ RangeTblEntry *rte = list_nth(query->rtable, rtr->rtindex - 1); ++ ++ switch (rte->rtekind) { ++ case RTE_RELATION: ++ selist = addEvalPgClass(selist, rte, DB_TABLE__SELECT); ++ break; ++ case RTE_SUBQUERY: ++ if (rte->relid) { ++ Query *sqry = rte->subquery; ++ RangeTblEntry *srte = list_nth(sqry->rtable, 0); ++ ++ selist = addEvalPgClass(selist, srte, DB_TABLE__SELECT); ++ } ++ break; ++ default: ++ /* do nothing */ ++ break; ++ } ++ } else if (IsA(node, JoinExpr)) { ++ JoinExpr *j = (JoinExpr *) node; ++ ++ selist = __checkSelectTargets(selist, query, j->larg); ++ selist = __checkSelectTargets(selist, query, j->rarg); ++ } else if (IsA(node, FromExpr)) { ++ FromExpr *fm = (FromExpr *)node; ++ ListCell *l; ++ ++ foreach (l, fm->fromlist) ++ selist = __checkSelectTargets(selist, query, lfirst(l)); ++ } else { ++ elog(ERROR, "SELinux: unexpected node type (%d) at Query->fromlist", nodeTag(node)); ++ } ++ return selist; ++} ++ ++static List *proxyRteSubQuery(List *selist, queryChain *qc, Query *query) ++{ ++ CmdType cmdType = query->commandType; ++ RangeTblEntry *rte = NULL; ++ queryChain qcData; ++ ListCell *l; ++ ++ /* query chain setup */ ++ qcData.parent = qc; ++ qcData.tail = query; ++ qc = &qcData; ++ ++ /* rewrite outer join */ ++ rewriteOuterJoinTree((Node *) query->jointree, query, false); ++ ++ if (cmdType == CMD_SELECT) { ++ selist = __checkSelectTargets(selist, query, (Node *)query->jointree); ++ } else { ++ rte = list_nth(query->rtable, query->resultRelation - 1); ++ Assert(IsA(rte, RangeTblEntry) && rte->rtekind==RTE_RELATION); ++ switch (cmdType) { ++ case CMD_INSERT: ++ selist = addEvalPgClass(selist, rte, DB_TABLE__INSERT); ++ break; ++ case CMD_UPDATE: ++ selist = addEvalPgClass(selist, rte, DB_TABLE__UPDATE); ++ break; ++ case CMD_DELETE: ++ selist = addEvalPgClass(selist, rte, DB_TABLE__DELETE); ++ break; ++ default: ++ selerror("commandType = %d should not be found here", cmdType); ++ break; ++ } ++ } ++ ++ /* permission mark on the target columns */ ++ if (cmdType != CMD_DELETE) { ++ foreach (l, query->targetList) { ++ TargetEntry *tle = lfirst(l); ++ bool is_security_attr = false; ++ Assert(IsA(tle, TargetEntry)); ++ ++ if (tle->resjunk && !strcmp(tle->resname, SECURITY_SYSATTR_NAME)) ++ is_security_attr = true; ++ ++ /* pure junk target entries */ ++ if (tle->resjunk && !is_security_attr) { ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) tle->expr, ++ WKFLAG_INTERNAL_USE); ++ continue; ++ } ++ ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) tle->expr, 0); ++ /* mark insert/update target */ ++ if (cmdType==CMD_UPDATE || cmdType==CMD_INSERT) { ++ uint32 perms = (cmdType == CMD_UPDATE ++ ? DB_COLUMN__UPDATE : DB_COLUMN__INSERT); ++ if (is_security_attr) { ++ selist = addEvalPgAttribute(selist, ++ rte, ++ SecurityAttributeNumber, ++ perms); ++ continue; ++ } ++ selist = addEvalPgAttribute(selist, rte, tle->resno, perms); ++ } ++ } ++ } ++ ++ /* permission mark on RETURNING clause, if necessary */ ++ foreach (l, query->returningList) { ++ TargetEntry *te = lfirst(l); ++ Assert(IsA(te, TargetEntry)); ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) te->expr, 0); ++ } ++ ++ /* permission mark on the WHERE/HAVING clause */ ++ selist = sepgsqlWalkExpr(selist, qc, query->jointree->quals, ++ WKFLAG_INTERNAL_USE); ++ selist = sepgsqlWalkExpr(selist, qc, query->havingQual, ++ WKFLAG_INTERNAL_USE); ++ ++ /* permission mark on the ORDER BY clause */ ++ //selist = sepgsqlWalkExpr(selist, qc, (Node *) query->sortClause, WKFLAG_INTERNAL_USE); ++ ++ /* permission mark on the GROUP BY/HAVING clause */ ++ //selist = sepgsqlWalkExpr(selist, qc, (Node *) query->groupClause, WKFLAG_INTERNAL_USE); ++ ++ /* permission mark on the UNION/INTERSECT/EXCEPT */ ++ selist = proxySetOperations(selist, qc, query->setOperations); ++ ++ /* append sepgsql_permission() on the FROM clause/USING clause ++ * for SELECT/UPDATE/DELETE statement. ++ * The target Relation of INSERT is noe necessary to append it ++ */ ++ selist = proxyJoinTree(selist, qc, (Node *) query->jointree, ++ &query->jointree->quals); ++ ++ /* clean-up polluted RangeTblEntry */ ++ foreach (l, query->rtable) { ++ rte = (RangeTblEntry *) lfirst(l); ++ rte->requiredPerms &= ~SEPGSQL_PERMS_ALL; ++ } ++ ++ return selist; ++} ++ ++static List *proxyJoinTree(List *selist, queryChain *qc, Node *n, Node **quals) ++{ ++ Query *query = getQueryFromChain(qc); ++ ++ if (n == NULL) ++ return selist; ++ ++ if (IsA(n, RangeTblRef)) { ++ RangeTblRef *rtr = (RangeTblRef *) n; ++ RangeTblEntry *rte = list_nth(query->rtable, rtr->rtindex - 1); ++ Assert(IsA(rte, RangeTblEntry)); ++ ++ switch (rte->rtekind) { ++ case RTE_RELATION: ++ selist = proxyRteRelation(selist, qc, rtr->rtindex, quals); ++ break; ++ case RTE_SUBQUERY: ++ selist = (rte->relid ++ ? proxyRteOuterJoin(selist, qc, rte->subquery) ++ : proxyRteSubQuery(selist, qc, rte->subquery)); ++ break; ++ case RTE_FUNCTION: { ++ FuncExpr *f = (FuncExpr *) rte->funcexpr; ++ ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) f, 0); ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) f->args, 0); ++ break; ++ } ++ case RTE_VALUES: ++ selist = sepgsqlWalkExpr(selist, qc, (Node *) rte->values_lists, 0); ++ break; ++ default: ++ selerror("rtekind = %d should not be found fromList", rte->rtekind); ++ break; ++ } ++ } else if (IsA(n, FromExpr)) { ++ FromExpr *f = (FromExpr *)n; ++ ListCell *l; ++ ++ selist = sepgsqlWalkExpr(selist, qc, f->quals, WKFLAG_INTERNAL_USE); ++ foreach (l, f->fromlist) ++ selist = proxyJoinTree(selist, qc, lfirst(l), quals); ++ } else if (IsA(n, JoinExpr)) { ++ JoinExpr *j = (JoinExpr *) n; ++ ++ selist = sepgsqlWalkExpr(selist, qc, j->quals, WKFLAG_INTERNAL_USE); ++ selist = proxyJoinTree(selist, qc, j->larg, &j->quals); ++ selist = proxyJoinTree(selist, qc, j->rarg, &j->quals); ++ } else { ++ selerror("unrecognized node type (%d) in query->jointree", nodeTag(n)); ++ } ++ return selist; ++} ++ ++static List *proxySetOperations(List *selist, queryChain *qc, Node *n) ++{ ++ Query *query = getQueryFromChain(qc); ++ ++ if (n == NULL) ++ return selist; ++ ++ if (IsA(n, RangeTblRef)) { ++ RangeTblRef *rtr = (RangeTblRef *) n; ++ RangeTblEntry *rte = list_nth(query->rtable, rtr->rtindex - 1); ++ ++ Assert(IsA(rte, RangeTblEntry) && rte->rtekind == RTE_SUBQUERY); ++ ++ selist = proxyRteSubQuery(selist, qc, rte->subquery); ++ } else if (IsA(n, SetOperationStmt)) { ++ SetOperationStmt *op = (SetOperationStmt *) n; ++ ++ selist = proxySetOperations(selist, qc, (Node *) op->larg); ++ selist = proxySetOperations(selist, qc, (Node *) op->rarg); ++ } else { ++ selerror("setOperationsTree contains => %s", nodeToString(n)); ++ } ++ ++ return selist; ++} ++ ++static List *proxyGeneralQuery(Query *query) ++{ ++ List *selist = NIL; ++ ++ selist = proxyRteSubQuery(selist, NULL, query); ++ query->pgaceList = selist; ++ ++ return list_make1(query); ++} ++ ++static List *proxyExecuteStmt(Query *query) ++{ ++ List *selist = NIL; ++ ExecuteStmt *estmt = (ExecuteStmt *) query->utilityStmt; ++ queryChain qcData; ++ ++ Assert(nodeTag(query->utilityStmt) == T_ExecuteStmt); ++ ++ qcData.parent = NULL; ++ qcData.tail = query; ++ selist = sepgsqlWalkExpr(selist, &qcData, (Node *) estmt->params, 0); ++ query->pgaceList = selist; ++ ++ return list_make1(query); ++} ++ ++static List *convertTruncateToDelete(Relation rel) ++{ ++ Query *query = makeNode(Query); ++ RangeTblEntry *rte; ++ RangeTblRef *rtr; ++ ++ rte = addRangeTableEntryForRelation(NULL, rel, NULL, false, false); ++ rte->requiredPerms = ACL_DELETE; ++ rtr = makeNode(RangeTblRef); ++ rtr->rtindex = 1; ++ ++ query->commandType = CMD_DELETE; ++ query->rtable = list_make1(rte); ++ query->jointree = makeNode(FromExpr); ++ query->jointree->fromlist = list_make1(rtr); ++ query->jointree->quals = NULL; ++ query->resultRelation = rtr->rtindex; ++ query->hasSubLinks = false; ++ query->hasAggs = false; ++ ++ selnotice("virtual TRUNCATE %s", RelationGetRelationName(rel)); ++ ++ return sepgsqlProxyQuery(query); ++} ++ ++static List *proxyTruncateStmt(Query *query) ++{ ++ TruncateStmt *stmt = (TruncateStmt *) query->utilityStmt; ++ Relation rel; ++ ListCell *l; ++ List *subquery_list = NIL; ++ List *subquery_lids = NIL; ++ ++ /* resolve the relation names */ ++ foreach (l, stmt->relations) { ++ RangeVar *rv = lfirst(l); ++ ++ rel = heap_openrv(rv, AccessShareLock); ++ subquery_list = list_concat(subquery_list, ++ convertTruncateToDelete(rel)); ++ subquery_lids = lappend_oid(subquery_lids, ++ RelationGetRelid(rel)); ++ heap_close(rel, AccessShareLock); ++ } ++ ++ if (stmt->behavior == DROP_CASCADE) { ++ subquery_lids = heap_truncate_find_FKs(subquery_lids); ++ foreach (l, subquery_lids) { ++ Oid relid = lfirst_oid(l); ++ ++ rel = heap_open(relid, AccessShareLock); ++ subquery_list = list_concat(subquery_list, ++ convertTruncateToDelete(rel)); ++ heap_close(rel, AccessShareLock); ++ } ++ } ++ return subquery_list; ++} ++ ++List *sepgsqlProxyQuery(Query *query) ++{ ++ List *new_list = NIL; ++ ++ switch (query->commandType) { ++ case CMD_SELECT: ++ case CMD_UPDATE: ++ case CMD_INSERT: ++ case CMD_DELETE: ++ new_list = proxyGeneralQuery(query); ++ break; ++ case CMD_UTILITY: ++ switch (nodeTag(query->utilityStmt)) { ++ case T_TruncateStmt: ++ new_list = proxyTruncateStmt(query); ++ break; ++ case T_ExecuteStmt: ++ new_list = proxyExecuteStmt(query); ++ break; ++ default: ++ new_list = list_make1(query); ++ /* do nothing now */ ++ break; ++ } ++ break; ++ default: ++ selerror("unknown command type (=%d) found", ++ query->commandType); ++ break; ++ } ++ return new_list; ++} ++ ++/* ******************************************************************************* ++ * verifyXXXX() -- checks any SEvalItem attached with Query->pgaceList. ++ * Those are generated in proxyXXXX() phase, and this evaluation is done ++ * just before PortalStart(). ++ * The reason why the checks are delayed is to handle cases when parse ++ * and execute are separated like PREPARE/EXECUTE statement. ++ * *******************************************************************************/ ++static void verifyPgClassPerms(Oid relid, bool inh, uint32 perms) ++{ ++ Form_pg_class pgclass; ++ HeapTuple tuple; ++ ++ /* prevent to modify pg_security directly */ ++ if (relid == SecurityRelationId ++ && (perms & (DB_TABLE__UPDATE | DB_TABLE__INSERT | DB_TABLE__DELETE)) != 0) ++ selerror("user cannot modify pg_security directly, for security reason"); ++ ++ /* check table:{required permissions} */ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ selerror("RELOID cache lookup failed (relid=%u)", relid); ++ pgclass = (Form_pg_class) GETSTRUCT(tuple); ++ ++ if (pgclass->relkind != RELKIND_RELATION) { ++ //selnotice("%s is not a general relation", NameStr(pgclass->relname)); ++ ReleaseSysCache(tuple); ++ return; ++ } ++ ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_TABLE, ++ perms, ++ sepgsqlGetTupleName(RelationRelationId, tuple)); ++ ReleaseSysCache(tuple); ++} ++ ++static void verifyPgAttributePerms(Oid relid, bool inh, AttrNumber attno, uint32 perms) ++{ ++ HeapTuple tuple; ++ Form_pg_class classForm; ++ Form_pg_attribute attrForm; ++ ++ tuple = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ selerror("RELOID cache lookup failed (relid=%u)", relid); ++ classForm = (Form_pg_class) GETSTRUCT(tuple); ++ if (classForm->relkind != RELKIND_RELATION) { ++ /* column:{ xxx } checks are applied only column within tables */ ++ ReleaseSysCache(tuple); ++ return; ++ } ++ ReleaseSysCache(tuple); ++ ++ /* 2. verify column perms */ ++ if (attno == 0) { ++ /* RECORD type permission check */ ++ Relation rel; ++ ScanKeyData skey; ++ SysScanDesc scan; ++ ++ ScanKeyInit(&skey, ++ Anum_pg_attribute_attrelid, ++ BTEqualStrategyNumber, F_OIDEQ, ++ ObjectIdGetDatum(relid)); ++ ++ rel = heap_open(AttributeRelationId, AccessShareLock); ++ scan = systable_beginscan(rel, AttributeRelidNumIndexId, ++ true, SnapshotNow, 1, &skey); ++ while ((tuple = systable_getnext(scan)) != NULL) { ++ attrForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ if (attrForm->attisdropped || attrForm->attnum < 1) ++ continue; ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_COLUMN, ++ perms, ++ sepgsqlGetTupleName(AttributeRelationId, tuple)); ++ } ++ systable_endscan(scan); ++ heap_close(rel, AccessShareLock); ++ ++ return; ++ } ++ ++ tuple = SearchSysCache(ATTNUM, ++ ObjectIdGetDatum(relid), ++ Int16GetDatum(attno), ++ 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ selerror("ATTNUM cache lookup failed (relid=%u, attno=%d)", relid, attno); ++ ++ /* check column:{required permissions} */ ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_COLUMN, ++ perms, ++ sepgsqlGetTupleName(AttributeRelationId, tuple)); ++ ReleaseSysCache(tuple); ++} ++ ++static void verifyPgProcPerms(Oid funcid, uint32 perms) ++{ ++ HeapTuple tuple; ++ Oid newcon; ++ ++ tuple = SearchSysCache(PROCOID, ++ ObjectIdGetDatum(funcid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ selerror("cache lookup failed for procedure %d", funcid); ++ ++ /* compute domain transition */ ++ newcon = sepgsql_avc_createcon(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_PROCESS); ++ if (newcon != sepgsqlGetClientContext()) ++ perms |= DB_PROCEDURE__ENTRYPOINT; ++ ++ /* check procedure executiong permission */ ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ HeapTupleGetSecurity(tuple), ++ SECCLASS_DB_PROCEDURE, ++ perms, ++ sepgsqlGetTupleName(ProcedureRelationId, tuple)); ++ ++ /* check domain transition, if necessary */ ++ if (newcon != sepgsqlGetClientContext()) { ++ sepgsql_avc_permission(sepgsqlGetClientContext(), ++ newcon, ++ SECCLASS_PROCESS, ++ PROCESS__TRANSITION, ++ NULL); ++ } ++ ++ ReleaseSysCache(tuple); ++} ++ ++static List *__expandPgClassInheritance(List *selist, Oid relid, uint32 perms) ++{ ++ List *child_list = find_inheritance_children(relid); ++ ListCell *l; ++ ++ foreach (l, child_list) { ++ selist = __addEvalPgClass(selist, lfirst_oid(l), false, perms); ++ selist = __expandPgClassInheritance(selist, lfirst_oid(l), perms); ++ } ++ return selist; ++} ++ ++static List *__expandPgAttributeInheritance(List *selist, Oid relid, char *attname, uint32 perms) ++{ ++ List *child_list = find_inheritance_children(relid); ++ ListCell *l; ++ ++ foreach (l, child_list) { ++ Form_pg_attribute attrForm; ++ HeapTuple tuple; ++ ++ if (!attname) { ++ /* attname == NULL means RECORD reference */ ++ selist = __addEvalPgAttribute(selist, lfirst_oid(l), false, 0, perms); ++ selist = __expandPgAttributeInheritance(selist, lfirst_oid(l), NULL, perms); ++ continue; ++ } ++ ++ tuple = SearchSysCacheAttName(lfirst_oid(l), attname); ++ if (!HeapTupleIsValid(tuple)) ++ selerror("relation %u does not have attribute %s", ++ lfirst_oid(l), attname); ++ attrForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ selist = __addEvalPgAttribute(selist, lfirst_oid(l), false, attrForm->attnum, perms); ++ selist = __expandPgAttributeInheritance(selist, lfirst_oid(l), attname, perms); ++ ++ ReleaseSysCache(tuple); ++ } ++ ++ return selist; ++} ++ ++static List *expandSEvalListInheritance(List *selist) { ++ List *result = NIL; ++ ListCell *l; ++ ++ foreach (l, selist) { ++ SEvalItem *se = (SEvalItem *) lfirst(l); ++ ++ result = lappend(result, se); ++ switch (se->tclass) { ++ case SECCLASS_DB_TABLE: ++ if (se->c.inh) { ++ se->c.inh = false; ++ result = __expandPgClassInheritance(result, ++ se->c.relid, ++ se->perms); ++ } ++ break; ++ case SECCLASS_DB_COLUMN: ++ if (se->a.inh) { ++ Form_pg_attribute attrForm; ++ HeapTuple tuple; ++ ++ se->a.inh = false; ++ if (se->a.attno == 0) { ++ result = __expandPgAttributeInheritance(result, ++ se->a.relid, ++ NULL, ++ se->perms); ++ break; ++ } ++ tuple = SearchSysCache(ATTNUM, ++ ObjectIdGetDatum(se->a.relid), ++ Int16GetDatum(se->a.attno), ++ 0, 0); ++ if (!HeapTupleIsValid(tuple)) ++ selerror("relation %u attribute %d not found", se->a.relid, se->a.attno); ++ attrForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ ++ result = __expandPgAttributeInheritance(result, ++ se->a.relid, ++ NameStr(attrForm->attname), ++ se->perms); ++ ReleaseSysCache(tuple); ++ } ++ break; ++ } ++ } ++ return result; ++} ++ ++static void execVerifyQuery(List *selist) ++{ ++ ListCell *l; ++ ++ foreach (l, selist) { ++ SEvalItem *se = lfirst(l); ++ ++ switch (se->tclass) { ++ case SECCLASS_DB_TABLE: ++ verifyPgClassPerms(se->c.relid, se->c.inh, se->perms); ++ break; ++ case SECCLASS_DB_COLUMN: ++ verifyPgAttributePerms(se->a.relid, se->a.inh, se->a.attno, se->perms); ++ break; ++ case SECCLASS_DB_PROCEDURE: ++ verifyPgProcPerms(se->p.funcid, se->perms); ++ break; ++ default: ++ selerror("unknown SEvalItem (tclass=%u)", se->tclass); ++ break; ++ } ++ } ++} ++ ++void sepgsqlVerifyQuery(Query *query) ++{ ++ List *selist = copyObject(query->pgaceList); ++ ++ /* expand table inheritances */ ++ selist = expandSEvalListInheritance(selist); ++ ++ /* add checks for access via trigger function */ ++ if (query->resultRelation > 0) { ++ RangeTblEntry *rte = (RangeTblEntry *) list_nth(query->rtable, ++ query->resultRelation - 1); ++ Assert(IsA(rte, RangeTblEntry)); ++ selist = addEvalTriggerAccess(selist, rte->relid, rte->inh, query->commandType); ++ } ++ ++ execVerifyQuery(selist); ++} ++ ++/* ******************************************************************************* ++ * PGACE hooks: we cannon the following hooks in sepgsqlHooks.c because they ++ * refers static defined variables in sepgsqlProxy.c ++ * *******************************************************************************/ ++ ++/* ---------------------------------------------------------- ++ * COPY TO/COPY FROM statement hooks ++ * ---------------------------------------------------------- */ ++void sepgsqlCopyTable(Relation rel, List *attNumList, bool isFrom) ++{ ++ List *selist = NIL; ++ ListCell *l; ++ ++ /* on 'COPY FROM SELECT ...' cases, any checkings are done in select.c */ ++ if (rel == NULL) ++ return; ++ ++ /* no need to check non-table relation */ ++ if (RelationGetForm(rel)->relkind != RELKIND_RELATION) ++ return; ++ ++ selist = __addEvalPgClass(selist, RelationGetRelid(rel), false, ++ isFrom ? DB_TABLE__INSERT : DB_TABLE__SELECT); ++ foreach (l, attNumList) { ++ AttrNumber attnum = lfirst_int(l); ++ ++ selist = __addEvalPgAttribute(selist, RelationGetRelid(rel), false, attnum, ++ isFrom ? DB_COLUMN__INSERT : DB_COLUMN__SELECT); ++ } ++ ++ /* check call trigger function */ ++ if (isFrom) ++ selist = addEvalTriggerAccess(selist, RelationGetRelid(rel), false, CMD_INSERT); ++ ++ execVerifyQuery(selist); ++} ++ ++bool sepgsqlCopyToTuple(Relation rel, HeapTuple tuple) ++{ ++ return sepgsqlCheckTuplePerms(rel, tuple, NULL, SEPGSQL_PERMS_SELECT, false); ++} ++ ++bool sepgsqlCopyFromTuple(Relation rel, HeapTuple tuple) ++{ ++ Oid tcontext = HeapTupleGetSecurity(tuple); ++ ++ if (tcontext == InvalidOid) { ++ /* implicit labeling */ ++ tcontext = sepgsqlComputeImplicitContext(rel, tuple); ++ HeapTupleSetSecurity(tuple, tcontext); ++ } ++ return sepgsqlCheckTuplePerms(rel, tuple, NULL, SEPGSQL_PERMS_INSERT, false); ++} ++ ++/* ---------------------------------------------------------- ++ * node copy/print hooks ++ * ---------------------------------------------------------- */ ++Node *sepgsqlCopyObject(Node *__oldnode) { ++ SEvalItem *oldnode, *newnode; ++ ++ if (nodeTag(__oldnode) != T_SEvalItem) ++ return NULL; ++ oldnode = (SEvalItem *) __oldnode; ++ ++ newnode = makeNode(SEvalItem); ++ newnode->tclass = oldnode->tclass; ++ newnode->perms = oldnode->perms; ++ switch (oldnode->tclass) { ++ case SECCLASS_DB_TABLE: ++ newnode->c.relid = oldnode->c.relid; ++ newnode->c.inh = oldnode->c.inh; ++ break; ++ case SECCLASS_DB_COLUMN: ++ newnode->a.relid = oldnode->a.relid; ++ newnode->a.attno = oldnode->a.attno; ++ newnode->a.inh = oldnode->a.inh; ++ break; ++ case SECCLASS_DB_PROCEDURE: ++ newnode->p.funcid = oldnode->p.funcid; ++ break; ++ default: ++ selerror("unrecognized SEvalItem node (tclass: %d)", oldnode->tclass); ++ break; ++ } ++ return (Node *) newnode; ++} ++ ++bool sepgsqlOutObject(StringInfo str, Node *node) { ++ SEvalItem *seitem = (SEvalItem *) node; ++ ++ if (nodeTag(node) != T_SEvalItem) ++ return false; ++ ++ appendStringInfoString(str, "SEVALITEM"); ++ appendStringInfo(str, ":tclass %u", seitem->tclass); ++ appendStringInfo(str, ":perms %u", seitem->perms); ++ switch(seitem->tclass) { ++ case SECCLASS_DB_TABLE: ++ appendStringInfo(str, ":c.relid %u", seitem->c.relid); ++ appendStringInfo(str, ":c.inh %s", seitem->c.inh ? "true" : "false"); ++ break; ++ case SECCLASS_DB_COLUMN: ++ appendStringInfo(str, ":a.relid %u", seitem->c.relid); ++ appendStringInfo(str, ":a.inh %s", seitem->c.inh ? "true" : "false"); ++ appendStringInfo(str, ":a.attno %u", seitem->c.inh); ++ break; ++ case SECCLASS_DB_PROCEDURE: ++ appendStringInfo(str, ":p.funcid %u", seitem->p.funcid); ++ break; ++ default: ++ selerror("unrecognized SEvalItem node (tclass: %d)", seitem->tclass); ++ break; ++ } ++ return true; ++} ++ ++/* ---------------------------------------------------------- ++ * special cases in foreign key constraint ++ * ---------------------------------------------------------- */ ++Oid sepgsqlPreparePlanCheck(Relation rel) { ++ Oid pgace_saved = fnoid_sepgsql_tuple_perm; ++ fnoid_sepgsql_tuple_perm = F_SEPGSQL_TUPLE_PERMS_ABORT; ++ return pgace_saved; ++} ++ ++void sepgsqlRestorePlanCheck(Relation rel, Oid pgace_saved) { ++ fnoid_sepgsql_tuple_perm = pgace_saved; ++} +diff -rpNU3 base/src/backend/storage/ipc/ipci.c sepgsql/src/backend/storage/ipc/ipci.c +--- base/src/backend/storage/ipc/ipci.c 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/storage/ipc/ipci.c 2007-10-25 13:12:52.000000000 +0900 +@@ -23,6 +23,7 @@ + #include "pgstat.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" +@@ -113,6 +114,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/storage/large_object/inv_api.c sepgsql/src/backend/storage/large_object/inv_api.c +--- base/src/backend/storage/large_object/inv_api.c 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/storage/large_object/inv_api.c 2007-10-25 13:12:52.000000000 +0900 +@@ -32,6 +32,7 @@ + #include "catalog/pg_largeobject.h" + #include "commands/comment.h" + #include "libpq/libpq-fs.h" ++#include "security/pgace.h" + #include "storage/large_object.h" + #include "utils/fmgroids.h" + #include "utils/resowner.h" +@@ -131,6 +132,7 @@ myLargeObjectExists(Oid loid, Snapshot s + { + bool retval = false; + Relation pg_largeobject; ++ HeapTuple tuple; + ScanKeyData skey[1]; + SysScanDesc sd; + +@@ -147,8 +149,11 @@ myLargeObjectExists(Oid loid, Snapshot s + sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true, + snapshot, 1, skey); + +- if (systable_getnext(sd) != NULL) ++ tuple = systable_getnext(sd); ++ if (HeapTupleIsValid(tuple)) { ++ pgaceLargeObjectOpen(pg_largeobject, tuple, !(snapshot == SnapshotNow)); + retval = true; ++ } + + systable_endscan(sd); + +@@ -434,6 +439,8 @@ inv_read(LargeObjectDesc *obj_desc, char + bytea *datafield; + bool pfreeit; + ++ pgaceLargeObjectRead(lo_heap_r, tuple); ++ + data = (Form_pg_largeobject) GETSTRUCT(tuple); + + /* +@@ -619,6 +626,8 @@ inv_write(LargeObjectDesc *obj_desc, con + replace[Anum_pg_largeobject_data - 1] = 'r'; + newtup = heap_modifytuple(oldtuple, RelationGetDescr(lo_heap_r), + values, nulls, replace); ++ ++ pgaceLargeObjectWrite(lo_heap_r, newtup, oldtuple); + simple_heap_update(lo_heap_r, &newtup->t_self, newtup); + CatalogIndexInsert(indstate, newtup); + heap_freetuple(newtup); +@@ -662,6 +671,7 @@ inv_write(LargeObjectDesc *obj_desc, con + values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno); + values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf); + newtup = heap_formtuple(lo_heap_r->rd_att, values, nulls); ++ pgaceLargeObjectWrite(lo_heap_r, newtup, NULL); + simple_heap_insert(lo_heap_r, newtup); + CatalogIndexInsert(indstate, newtup); + heap_freetuple(newtup); +diff -rpNU3 base/src/backend/tcop/fastpath.c sepgsql/src/backend/tcop/fastpath.c +--- base/src/backend/tcop/fastpath.c 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/tcop/fastpath.c 2007-10-25 13:12:52.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,9 @@ HandleFunctionRequest(StringInfo msgBuf) + */ + InitFunctionCallInfoData(fcinfo, &fip->flinfo, 0, NULL, NULL); + ++ /* PGACE: check procedure permission */ ++ 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 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/tcop/postgres.c 2007-10-25 13:12:52.000000000 +0900 +@@ -52,6 +52,7 @@ + #include "parser/analyze.h" + #include "parser/parser.h" + #include "rewrite/rewriteHandler.h" ++#include "security/pgace.h" + #include "storage/freespace.h" + #include "storage/ipc.h" + #include "storage/proc.h" +@@ -607,8 +608,11 @@ pg_rewrite_queries(List *querytree_list) + + if (querytree->commandType == CMD_UTILITY) + { ++ /* PGACE: utility query proxy */ ++ List *tmp = pgaceProxyQuery(list_make1(querytree)); ++ + /* don't rewrite utilities, just dump 'em into new_list */ +- new_list = lappend(new_list, querytree); ++ new_list = list_concat(new_list, tmp); + } + else + { +diff -rpNU3 base/src/backend/tcop/pquery.c sepgsql/src/backend/tcop/pquery.c +--- base/src/backend/tcop/pquery.c 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/tcop/pquery.c 2007-10-25 13:12:52.000000000 +0900 +@@ -19,6 +19,7 @@ + #include "commands/prepare.h" + #include "commands/trigger.h" + #include "miscadmin.h" ++#include "security/pgace.h" + #include "tcop/pquery.h" + #include "tcop/tcopprot.h" + #include "tcop/utility.h" +@@ -352,6 +353,9 @@ PortalStart(Portal portal, ParamListInfo + AssertState(portal->queryContext != NULL); /* query defined? */ + AssertState(portal->status == PORTAL_NEW); /* else extra PortalStart */ + ++ /* PGACE: verify query via PGACE subsystem */ ++ pgacePortalStart(portal); ++ + /* + * Set up global portal context pointers. (Should we set QueryContext?) + */ +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-01-08 20:37:36.000000000 +0900 ++++ sepgsql/src/backend/utils/adt/ri_triggers.c 2008-01-08 20:44:45.000000000 +0900 +@@ -34,6 +34,7 @@ + + #include "commands/trigger.h" + #include "executor/spi_priv.h" ++#include "security/pgace.h" + #include "utils/acl.h" + #include "utils/fmgroids.h" + #include "utils/lsyscache.h" +@@ -3010,6 +3011,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 +@@ -3027,7 +3029,18 @@ ri_PlanCheck(const char *querystr, int n + SetUserIdAndContext(RelationGetForm(query_rel)->relowner, true); + + /* Create the plan */ +- qplan = SPI_prepare(querystr, nargs, argtypes); ++ save_pgace = pgacePreparePlanCheck(query_rel); ++ 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/syscache.c sepgsql/src/backend/utils/cache/syscache.c +--- base/src/backend/utils/cache/syscache.c 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/utils/cache/syscache.c 2007-10-25 13:12:52.000000000 +0900 +@@ -37,6 +37,7 @@ + #include "catalog/pg_operator.h" + #include "catalog/pg_proc.h" + #include "catalog/pg_rewrite.h" ++#include "catalog/pg_security.h" + #include "catalog/pg_statistic.h" + #include "catalog/pg_type.h" + #include "utils/syscache.h" +@@ -513,7 +514,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[ +diff -rpNU3 base/src/backend/utils/fmgr/dfmgr.c sepgsql/src/backend/utils/fmgr/dfmgr.c +--- base/src/backend/utils/fmgr/dfmgr.c 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/backend/utils/fmgr/dfmgr.c 2007-10-25 13:12:52.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); + ++ /* PGACE: check whether the module can 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); + ++ /* PGACE: check whether the module can 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-09-25 14:42:56.000000000 +0900 ++++ sepgsql/src/backend/utils/init/postinit.c 2008-09-25 14:46:52.000000000 +0900 +@@ -30,6 +30,7 @@ + #include "miscadmin.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" +@@ -530,6 +531,9 @@ InitPostgres(const char *dbname, const c + if (!bootstrap) + pgstat_bestart(); + ++ /* PGACE: initialize access control extension facility */ ++ pgaceInitialize(); ++ + /* 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-09-25 14:42:56.000000000 +0900 ++++ sepgsql/src/backend/utils/misc/guc.c 2008-09-25 14:46:52.000000000 +0900 +@@ -51,6 +51,7 @@ + #include "postmaster/bgwriter.h" + #include "postmaster/postmaster.h" + #include "postmaster/syslogger.h" ++#include "security/pgace.h" + #include "storage/fd.h" + #include "storage/freespace.h" + #include "tcop/tcopprot.h" +@@ -237,6 +238,7 @@ static int max_index_keys; + static int max_identifier_length; + static int block_size; + static bool integer_datetimes; ++static char *security_sysattr_name; + + /* should be static, but commands/variable.c needs to get at these */ + char *role_string; +@@ -2272,6 +2274,20 @@ static struct config_string ConfigureNam + NULL, assign_canonical_path, NULL + }, + ++ { ++ {"security_sysattr_name", PGC_INTERNAL, PRESET_OPTIONS, ++ gettext_noop("Shows the name of security attribute system column"), ++ NULL, ++ GUC_REPORT | GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE ++ }, ++ &security_sysattr_name, ++#ifdef SECURITY_SYSATTR_NAME ++ SECURITY_SYSATTR_NAME, NULL, NULL, ++#else ++ "undefined", NULL, NULL, ++#endif ++ }, ++ + /* End-of-list marker */ + { + {NULL, 0, 0, NULL, NULL}, NULL, NULL, NULL, NULL +@@ -4668,6 +4684,9 @@ SetPGVariable(const char *name, List *ar + { + char *argstring = flatten_set_variable_args(name, args); + ++ /* PGACE: check set param permission */ ++ pgaceSetDatabaseParam(name, argstring); ++ + /* Note SET DEFAULT (argstring == NULL) is equivalent to RESET */ + set_config_option(name, + argstring, +@@ -4930,6 +4949,9 @@ EmitWarningsOnPlaceholders(const char *c + void + GetPGVariable(const char *name, DestReceiver *dest) + { ++ /* PGACE: check get param permission */ ++ pgaceGetDatabaseParam(name); ++ + if (pg_strcasecmp(name, "all") == 0) + ShowAllGUCConfig(dest); + else +@@ -4974,6 +4996,9 @@ GetPGVariableResultDesc(const char *name + void + ResetPGVariable(const char *name) + { ++ /* PGACE: check set param permission */ ++ pgaceSetDatabaseParam(name, NULL); ++ + if (pg_strcasecmp(name, "all") == 0) + ResetAllOptions(); + else +diff -rpNU3 base/src/bin/pg_dump/pg_dump.c sepgsql/src/bin/pg_dump/pg_dump.c +--- base/src/bin/pg_dump/pg_dump.c 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/bin/pg_dump/pg_dump.c 2007-10-25 13:12:52.000000000 +0900 +@@ -119,6 +119,8 @@ static int g_numNamespaces; + /* flag to turn on/off dollar quoting */ + static int disable_dollar_quoting = 0; + ++/* flag to tuen on/off SE-PostgreSQL support */ ++static int enable_selinux = 0; + + static void help(const char *progname); + static void expand_schema_name_patterns(SimpleStringList *patterns, +@@ -261,6 +263,7 @@ main(int argc, char **argv) + {"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1}, + {"disable-triggers", no_argument, &disable_triggers, 1}, + {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ++ {"enable-selinux", no_argument, &enable_selinux, 1}, + + {NULL, 0, NULL, 0} + }; +@@ -418,6 +421,8 @@ main(int argc, char **argv) + disable_triggers = 1; + else if (strcmp(optarg, "use-set-session-authorization") == 0) + use_setsessauth = 1; ++ else if (strcmp(optarg, "enable-selinux") == 0) ++ enable_selinux = 1; + else + { + fprintf(stderr, +@@ -474,6 +479,12 @@ main(int argc, char **argv) + exit(1); + } + ++ /* If TableData dumped with security attribute, INSERT statement has to ++ * use explicit column list. ++ */ ++ if (enable_selinux) ++ attrNames = true; ++ + /* open the output file */ + switch (format[0]) + { +@@ -762,6 +773,7 @@ help(const char *progname) + printf(_(" --use-set-session-authorization\n" + " use SESSION AUTHORIZATION commands instead of\n" + " ALTER OWNER commands to set ownership\n")); ++ printf(_(" --enable-selinux enable to dump security context in SE-PostgreSQL\n")); + + printf(_("\nConnection options:\n")); + printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); +@@ -1144,7 +1156,8 @@ dumpTableData_insert(Archive *fout, void + if (fout->remoteVersion >= 70100) + { + appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " +- "SELECT * FROM ONLY %s", ++ "SELECT * %s FROM ONLY %s", ++ (!enable_selinux ? "" : ", security_context"), + fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, + classname)); + } +@@ -1757,11 +1770,33 @@ dumpBlobComments(Archive *AH, void *arg) + Oid blobOid; + char *comment; + ++ blobOid = atooid(PQgetvalue(res, i, 0)); ++ ++ /* dump security context of binary large object */ ++ if (enable_selinux) { ++ PGresult *__res; ++ char query[256]; ++ ++ snprintf(query, sizeof(query), ++ "SELECT lo_get_security(%u)", blobOid); ++ __res = PQexec(g_conn, query); ++ check_sql_result(__res, g_conn, query, PGRES_TUPLES_OK); ++ ++ if (PQntuples(__res) != 1) { ++ write_msg(NULL, "lo_get_security(%u) returns %d tuples\n", ++ blobOid, PQntuples(__res)); ++ exit_nicely(); ++ } ++ archprintf(AH, "SELECT lo_set_security(%u, '%s');\n", ++ blobOid, PQgetvalue(__res, 0, 0)); ++ ++ PQclear(__res); ++ } ++ + /* ignore blobs without comments */ + if (PQgetisnull(res, i, 1)) + continue; + +- blobOid = atooid(PQgetvalue(res, i, 0)); + comment = PQgetvalue(res, i, 1); + + printfPQExpBuffer(commentcmd, "COMMENT ON LARGE OBJECT %u IS ", +@@ -2759,6 +2794,7 @@ getTables(int *numTables) + int i_owning_col; + int i_reltablespace; + int i_reloptions; ++ int i_selinux; + + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); +@@ -2791,6 +2827,7 @@ getTables(int *numTables) + */ + appendPQExpBuffer(query, + "SELECT c.tableoid, c.oid, relname, " ++ "%s" /* security context, if required */ + "relacl, relkind, relnamespace, " + "(%s relowner) as rolname, " + "relchecks, reltriggers, " +@@ -2807,6 +2844,7 @@ getTables(int *numTables) + "d.refclassid = c.tableoid and d.deptype = 'a') " + "where relkind in ('%c', '%c', '%c', '%c') " + "order by c.oid", ++ (!enable_selinux ? "" : "c." SECURITY_SYSATTR_NAME ", "), + username_subquery, + RELKIND_SEQUENCE, + RELKIND_RELATION, RELKIND_SEQUENCE, +@@ -2974,6 +3012,7 @@ getTables(int *numTables) + i_owning_col = PQfnumber(res, "owning_col"); + i_reltablespace = PQfnumber(res, "reltablespace"); + i_reloptions = PQfnumber(res, "reloptions"); ++ i_selinux = PQfnumber(res, "security_context"); + + for (i = 0; i < ntups; i++) + { +@@ -3004,6 +3043,9 @@ getTables(int *numTables) + } + tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace)); + tblinfo[i].reloptions = strdup(PQgetvalue(res, i, i_reloptions)); ++ tblinfo[i].relsecurity = NULL; ++ if (i_selinux >= 0) ++ tblinfo[i].relsecurity = strdup(PQgetvalue(res, i, i_selinux)); + + /* other fields were zeroed above */ + +@@ -4147,6 +4189,7 @@ getTableAttrs(TableInfo *tblinfo, int nu + int i_atthasdef; + int i_attisdropped; + int i_attislocal; ++ int i_attselinux; + PGresult *res; + int ntups; + bool hasdefaults; +@@ -4188,6 +4231,7 @@ getTableAttrs(TableInfo *tblinfo, int nu + { + /* need left join here to not fail on dropped columns ... */ + appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, a.attstattarget, a.attstorage, t.typstorage, " ++ "%s" /* security context, if required */ + "a.attnotnull, a.atthasdef, a.attisdropped, a.attislocal, " + "pg_catalog.format_type(t.oid,a.atttypmod) as atttypname " + "from pg_catalog.pg_attribute a left join pg_catalog.pg_type t " +@@ -4195,6 +4239,7 @@ getTableAttrs(TableInfo *tblinfo, int nu + "where a.attrelid = '%u'::pg_catalog.oid " + "and a.attnum > 0::pg_catalog.int2 " + "order by a.attrelid, a.attnum", ++ (!enable_selinux ? "" : "a.security_context, "), + tbinfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 70100) +@@ -4243,6 +4288,7 @@ getTableAttrs(TableInfo *tblinfo, int nu + i_atthasdef = PQfnumber(res, "atthasdef"); + i_attisdropped = PQfnumber(res, "attisdropped"); + i_attislocal = PQfnumber(res, "attislocal"); ++ i_attselinux = PQfnumber(res, "security_context"); + + tbinfo->numatts = ntups; + tbinfo->attnames = (char **) malloc(ntups * sizeof(char *)); +@@ -4253,6 +4299,7 @@ getTableAttrs(TableInfo *tblinfo, int nu + tbinfo->typstorage = (char *) malloc(ntups * sizeof(char)); + tbinfo->attisdropped = (bool *) malloc(ntups * sizeof(bool)); + tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool)); ++ tbinfo->attsecurity = (char **) malloc(ntups * sizeof(char *)); + tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool)); + tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *)); + tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool)); +@@ -4284,6 +4331,11 @@ getTableAttrs(TableInfo *tblinfo, int nu + tbinfo->inhAttrs[j] = false; + tbinfo->inhAttrDef[j] = false; + tbinfo->inhNotNull[j] = false; ++ ++ /* security attribute, if defined */ ++ tbinfo->attsecurity[j] = NULL; ++ if (i_attselinux >= 0 && !PQgetisnull(res, j, i_attselinux)) ++ tbinfo->attsecurity[j] = strdup(PQgetvalue(res, j, i_attselinux)); + } + + PQclear(res); +@@ -5796,6 +5848,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + char *proisstrict; + char *prosecdef; + char *lanname; ++ char *proselinux = NULL; + char *rettypename; + int nallargs; + char **allargtypes = NULL; +@@ -5819,11 +5872,13 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + { + appendPQExpBuffer(query, + "SELECT proretset, prosrc, probin, " ++ "%s" /* security context, if required */ + "proallargtypes, proargmodes, proargnames, " + "provolatile, proisstrict, prosecdef, " + "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) as lanname " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", ++ (!enable_selinux ? "" : SECURITY_SYSATTR_NAME ", "), + finfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 80000) +@@ -5905,6 +5960,12 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + proisstrict = PQgetvalue(res, 0, PQfnumber(res, "proisstrict")); + prosecdef = PQgetvalue(res, 0, PQfnumber(res, "prosecdef")); + lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname")); ++ if (enable_selinux) { ++ int i_selinux = PQfnumber(res, "security_context"); ++ ++ if (i_selinux >= 0 && !PQgetisnull(res, 0, i_selinux)) ++ proselinux = PQgetvalue(res, 0, i_selinux); ++ } + + /* + * See backend/commands/define.c for details of how the 'AS' clause is +@@ -6030,6 +6091,9 @@ dumpFunc(Archive *fout, FuncInfo *finfo) + if (prosecdef[0] == 't') + appendPQExpBuffer(q, " SECURITY DEFINER"); + ++ if (proselinux) ++ appendPQExpBuffer(q, " CONTEXT = '%s'", proselinux); ++ + appendPQExpBuffer(q, ";\n"); + + ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId, +@@ -7362,6 +7426,9 @@ dumpTableSchema(Archive *fout, TableInfo + if (tbinfo->notnull[j] && !tbinfo->inhNotNull[j]) + appendPQExpBuffer(q, " NOT NULL"); + ++ if (enable_selinux && tbinfo->attsecurity[j]) ++ appendPQExpBuffer(q, " CONTEXT = '%s'", tbinfo->attsecurity[j]); ++ + actual_atts++; + } + } +@@ -7409,6 +7476,9 @@ dumpTableSchema(Archive *fout, TableInfo + if (tbinfo->reloptions && strlen(tbinfo->reloptions) > 0) + appendPQExpBuffer(q, "\nWITH (%s)", tbinfo->reloptions); + ++ if (enable_selinux && tbinfo->relsecurity) ++ appendPQExpBuffer(q, " CONTEXT = '%s'", tbinfo->relsecurity); ++ + appendPQExpBuffer(q, ";\n"); + + /* Loop dumping statistics and storage statements */ +@@ -8783,6 +8853,12 @@ fmtCopyColumnList(const TableInfo *ti) + + appendPQExpBuffer(q, "("); + needComma = false; ++ ++ if (enable_selinux) { ++ appendPQExpBuffer(q, "security_context"); ++ needComma = true; ++ } ++ + for (i = 0; i < numatts; i++) + { + if (attisdropped[i]) +diff -rpNU3 base/src/bin/pg_dump/pg_dump.h sepgsql/src/bin/pg_dump/pg_dump.h +--- base/src/bin/pg_dump/pg_dump.h 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/bin/pg_dump/pg_dump.h 2007-10-25 13:12:52.000000000 +0900 +@@ -227,6 +227,7 @@ typedef struct _tableInfo + char relkind; + char *reltablespace; /* relation tablespace */ + char *reloptions; /* options specified by WITH (...) */ ++ char *relsecurity; /* security attribute of the relation */ + bool hasindex; /* does it have any indexes? */ + bool hasrules; /* does it have any rules? */ + bool hasoids; /* does it have OIDs? */ +@@ -251,6 +252,7 @@ typedef struct _tableInfo + char *typstorage; /* type storage scheme */ + bool *attisdropped; /* true if attr is dropped; don't dump it */ + bool *attislocal; /* true if attr has local definition */ ++ char **attsecurity; /* security attribute of attribute (column) */ + + /* + * Note: we need to store per-attribute notnull, default, and constraint +diff -rpNU3 base/src/bin/pg_dump/pg_dumpall.c sepgsql/src/bin/pg_dump/pg_dumpall.c +--- base/src/bin/pg_dump/pg_dumpall.c 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/bin/pg_dump/pg_dumpall.c 2007-10-25 13:12:52.000000000 +0900 +@@ -68,6 +68,9 @@ static int disable_triggers = 0; + static int use_setsessauth = 0; + static int server_version; + ++/* flag to tuen on/off SE-PostgreSQL support */ ++static int enable_selinux = 0; ++ + + int + main(int argc, char *argv[]) +@@ -111,6 +114,7 @@ main(int argc, char *argv[]) + {"disable-dollar-quoting", no_argument, &disable_dollar_quoting, 1}, + {"disable-triggers", no_argument, &disable_triggers, 1}, + {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ++ {"enable-selinux", no_argument, NULL, 1001}, + + {NULL, 0, NULL, 0} + }; +@@ -260,6 +264,10 @@ main(int argc, char *argv[]) + appendPQExpBuffer(pgdumpopts, " --disable-triggers"); + else if (strcmp(optarg, "use-set-session-authorization") == 0) + /* no-op, still allowed for compatibility */ ; ++ else if (strcmp(optarg, "enable-selinux") == 0) { ++ appendPQExpBuffer(pgdumpopts, " --enable-selinux"); ++ enable_selinux = 1; ++ } + else + { + fprintf(stderr, +@@ -270,6 +278,11 @@ main(int argc, char *argv[]) + } + break; + ++ case 1001: ++ appendPQExpBuffer(pgdumpopts, " --enable-selinux"); ++ enable_selinux = 1; ++ break; ++ + case 0: + break; + +@@ -393,6 +406,7 @@ help(void) + printf(_(" --use-set-session-authorization\n" + " use SESSION AUTHORIZATION commands instead of\n" + " OWNER TO commands\n")); ++ printf(_(" --enable-selinux enable to dump security attribute\n")); + + printf(_("\nConnection options:\n")); + printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); +@@ -802,16 +816,18 @@ dumpCreateDB(PGconn *conn) + printf("--\n-- Database creation\n--\n\n"); + + if (server_version >= 80100) +- res = executeQuery(conn, ++ appendPQExpBuffer(buf, + "SELECT datname, " + "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "datistemplate, datacl, datconnlimit, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " ++ "%s" /* security context, if required */ + "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " +- "WHERE datallowconn ORDER BY 1"); ++ "WHERE datallowconn ORDER BY 1", ++ (!enable_selinux ? "" : ", d.security_context ")); + else if (server_version >= 80000) +- res = executeQuery(conn, ++ appendPQExpBuffer(buf, + "SELECT datname, " + "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " +@@ -820,7 +836,7 @@ dumpCreateDB(PGconn *conn) + "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 70300) +- res = executeQuery(conn, ++ appendPQExpBuffer(buf, + "SELECT datname, " + "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " +@@ -829,7 +845,7 @@ dumpCreateDB(PGconn *conn) + "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 70100) +- res = executeQuery(conn, ++ appendPQExpBuffer(buf, + "SELECT datname, " + "coalesce(" + "(select usename from pg_shadow where usesysid=datdba), " +@@ -845,7 +861,7 @@ dumpCreateDB(PGconn *conn) + * Note: 7.0 fails to cope with sub-select in COALESCE, so just deal + * with getting a NULL by not printing any OWNER clause. + */ +- res = executeQuery(conn, ++ appendPQExpBuffer(buf, + "SELECT datname, " + "(select usename from pg_shadow where usesysid=datdba), " + "pg_encoding_to_char(d.encoding), " +@@ -855,6 +871,7 @@ dumpCreateDB(PGconn *conn) + "FROM pg_database d " + "ORDER BY 1"); + } ++ res = executeQuery(conn, buf->data); + + for (i = 0; i < PQntuples(res); i++) + { +@@ -865,6 +882,7 @@ dumpCreateDB(PGconn *conn) + char *dbacl = PQgetvalue(res, i, 4); + char *dbconnlimit = PQgetvalue(res, i, 5); + char *dbtablespace = PQgetvalue(res, i, 6); ++ char *dbsecurity = PQgetvalue(res, i, 7); + char *fdbname; + + fdbname = strdup(fmtId(dbname)); +@@ -908,6 +926,9 @@ dumpCreateDB(PGconn *conn) + appendPQExpBuffer(buf, " CONNECTION LIMIT = %s", + dbconnlimit); + ++ if (enable_selinux && dbsecurity) ++ appendPQExpBuffer(buf, " CONTEXT = '%s'", dbsecurity); ++ + appendPQExpBuffer(buf, ";\n"); + + if (strcmp(dbistemplate, "t") == 0) +diff -rpNU3 base/src/include/access/htup.h sepgsql/src/include/access/htup.h +--- base/src/include/access/htup.h 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/include/access/htup.h 2007-10-25 13:12:52.000000000 +0900 +@@ -137,6 +137,9 @@ typedef struct HeapTupleHeaderData + + ItemPointerData t_ctid; /* current TID of this or newer tuple */ + ++ /* PGAVE: security attribute of the tuple */ ++ Oid t_security; ++ + /* Fields below here must match MinimalTupleData! */ + + int16 t_natts; /* number of attributes */ +@@ -297,6 +300,14 @@ do { \ + *((Oid *) ((char *)(tup) + (tup)->t_hoff - sizeof(Oid))) = (oid); \ + } while (0) + ++#define HeapTupleHeaderGetSecurity(htup) \ ++ ((htup)->t_security) ++#define HeapTupleHeaderSetSecurity(htup, sid) \ ++ ((htup)->t_security = (sid)) ++#define HeapTupleGetSecurity(tuple) \ ++ HeapTupleHeaderGetSecurity((tuple)->t_data) ++#define HeapTupleSetSecurity(tuple, sid) \ ++ HeapTupleHeaderSetSecurity((tuple)->t_data, sid) + + /* + * BITMAPLEN(NATTS) - +@@ -349,8 +360,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 alternate representation that is used for transient +@@ -552,6 +567,7 @@ typedef struct xl_heap_delete + */ + typedef struct xl_heap_header + { ++ Oid t_security; + int16 t_natts; + uint16 t_infomask; + uint8 t_hoff; +diff -rpNU3 base/src/include/catalog/heap.h sepgsql/src/include/catalog/heap.h +--- base/src/include/catalog/heap.h 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/include/catalog/heap.h 2007-10-25 13:12:52.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, +diff -rpNU3 base/src/include/catalog/indexing.h sepgsql/src/include/catalog/indexing.h +--- base/src/include/catalog/indexing.h 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/include/catalog/indexing.h 2007-10-25 13:12:52.000000000 +0900 +@@ -218,6 +218,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 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/include/catalog/pg_attribute.h 2007-10-25 13:12:52.000000000 +0900 +@@ -276,6 +276,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 3403 0 4 -8 0 -1 -1 t p i t f f t 0)); + + /* ---------------- + * pg_proc +@@ -326,6 +327,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 3403 0 4 -8 0 -1 -1 t p i t f f t 0)); + + /* ---------------- + * pg_attribute +@@ -374,6 +376,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 3403 0 4 -8 0 -1 -1 t p i t f f t 0)); + + /* ---------------- + * pg_class +@@ -442,6 +445,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 3403 0 4 -8 0 -1 -1 t p i t f f t 0)); + + /* ---------------- + * pg_index +diff -rpNU3 base/src/include/catalog/pg_cast.h sepgsql/src/include/catalog/pg_cast.h +--- base/src/include/catalog/pg_cast.h 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/include/catalog/pg_cast.h 2007-10-25 13:12:52.000000000 +0900 +@@ -392,4 +392,10 @@ DATA(insert ( 1560 1560 1685 i )); + DATA(insert ( 1562 1562 1687 i )); + DATA(insert ( 1700 1700 1703 i )); + ++/* ++ * Security Context functions ++ */ ++DATA(insert (25 3403 3408 i)); ++DATA(insert (3403 25 3409 i)); ++ + #endif /* PG_CAST_H */ +diff -rpNU3 base/src/include/catalog/pg_proc.h sepgsql/src/include/catalog/pg_proc.h +--- base/src/include/catalog/pg_proc.h 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/include/catalog/pg_proc.h 2007-10-25 13:12:52.000000000 +0900 +@@ -3974,6 +3974,22 @@ DESCR("release shared advisory lock"); + DATA(insert OID = 2892 ( pg_advisory_unlock_all PGNSP PGUID 12 f f t f v 0 2278 "" _null_ _null_ _null_ pg_advisory_unlock_all - _null_ )); + DESCR("release all advisory locks"); + ++/* PostgreSQL Access Control Extension related functions */ ++DATA(insert OID = 3404 ( security_label_in PGNSP PGUID 12 f f t f i 1 3403 "2275" _null_ _null_ _null_ security_label_in - _null_ )); ++DATA(insert OID = 3405 ( security_label_out PGNSP PGUID 12 f f t f i 1 2275 "3403" _null_ _null_ _null_ security_label_out - _null_ )); ++DATA(insert OID = 3406 ( security_label_raw_in PGNSP PGUID 12 f f t f i 1 3403 "2275" _null_ _null_ _null_ security_label_raw_in - _null_ )); ++DATA(insert OID = 3407 ( security_label_raw_out PGNSP PGUID 12 f f t f i 1 2275 "3403" _null_ _null_ _null_ security_label_raw_out - _null_ )); ++DATA(insert OID = 3408 ( text_to_security_label PGNSP PGUID 12 f f t f i 1 3403 "25" _null_ _null_ _null_ text_to_security_label - _null_ )); ++DATA(insert OID = 3409 ( security_label_to_text PGNSP PGUID 12 f f t f i 1 25 "3403" _null_ _null_ _null_ security_label_to_text - _null_ )); ++DATA(insert OID = 3410 ( lo_get_security PGNSP PGUID 12 f f t f v 1 3403 "26" _null_ _null_ _null_ lo_get_security - _null_ )); ++DATA(insert OID = 3411 ( lo_set_security PGNSP PGUID 12 f f t f v 2 16 "26 3403" _null_ _null_ _null_ lo_set_security - _null_ )); ++ ++/* SE-PostgreSQL related functions */ ++DATA(insert OID = 3420 ( sepgsql_getcon PGNSP PGUID 12 f f t f v 0 3403 "" _null_ _null_ _null_ sepgsql_getcon - _null_ )); ++DATA(insert OID = 3421 ( sepgsql_tuple_perms PGNSP PGUID 12 f f t f v 4 16 "26 3403 23 2249" _null_ _null_ _null_ sepgsql_tuple_perms - _null_ )); ++DATA(insert OID = 3422 ( sepgsql_tuple_perms_abort PGNSP PGUID 12 f f t f v 4 16 "26 3403 23 2249" _null_ _null_ _null_ sepgsql_tuple_perms_abort - _null_ )); ++ ++ + /* + * Symbolic values for provolatile column: these indicate whether the result + * of a function is dependent *only* on the values of its explicit arguments, +@@ -4015,7 +4031,8 @@ extern Oid ProcedureCreate(const char *p + oidvector *parameterTypes, + Datum allParameterTypes, + Datum parameterModes, +- Datum parameterNames); ++ Datum parameterNames, ++ void *pgace_item); + + 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 2007-10-25 13:12:52.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/catalog/pg_type.h sepgsql/src/include/catalog/pg_type.h +--- base/src/include/catalog/pg_type.h 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/include/catalog/pg_type.h 2007-10-25 13:12:52.000000000 +0900 +@@ -548,6 +548,9 @@ DATA(insert OID = 2282 ( opaque PGNSP + #define OPAQUEOID 2282 + DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p t \054 0 0 anyelement_in anyelement_out - - - i p f 0 -1 0 _null_ _null_ )); + #define ANYELEMENTOID 2283 ++DATA(insert OID = 3403 ( security_label PGNSP PGUID 4 t b t \054 0 0 security_label_in security_label_out - - - i p f 0 -1 0 _null_ _null_ )); ++DESCR("Security Label Identifier for PGACE"); ++#define SECLABELOID 3403 + + /* + * prototypes for functions in pg_type.c +diff -rpNU3 base/src/include/executor/tuptable.h sepgsql/src/include/executor/tuptable.h +--- base/src/include/executor/tuptable.h 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/include/executor/tuptable.h 2007-10-25 13:12:52.000000000 +0900 +@@ -118,6 +118,7 @@ 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 */ ++ Oid tts_security; /* PGACE: security attribute explicitly specified */ + } TupleTableSlot; + + /* +@@ -139,6 +140,16 @@ typedef TupleTableData *TupleTable; + #define TupIsNull(slot) \ + ((slot) == NULL || (slot)->tts_isempty) + ++/* ++ * HeapTupleStoreSecurityFromSlot ++ */ ++#ifdef HAVE_SELINUX ++#define HeapTupleStoreSecurityFromSlot(tuple, slot) \ ++ HeapTupleSetSecurity((tuple), (slot)->tts_security) ++#else ++#define HeapTupleStoreSecurityFromSlot(tuple, slot) ++#endif ++ + /* in executor/execTuples.c */ + extern TupleTable ExecCreateTupleTable(int tableSize); + extern void ExecDropTupleTable(TupleTable table, bool shouldFree); +diff -rpNU3 base/src/include/fmgr.h sepgsql/src/include/fmgr.h +--- base/src/include/fmgr.h 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/include/fmgr.h 2007-10-25 13:12:52.000000000 +0900 +@@ -52,6 +52,9 @@ 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 */ ++ ++ PGFunction fn_pgace_addr; /* PGACE opaque addr field */ ++ Datum fn_pgace_data; /* PGACE opaque data field */ + } FmgrInfo; + + /* +@@ -486,6 +489,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 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/include/libpq/be-fsstubs.h 2007-10-25 13:12:52.000000000 +0900 +@@ -35,6 +35,9 @@ extern Datum lo_lseek(PG_FUNCTION_ARGS); + extern Datum lo_tell(PG_FUNCTION_ARGS); + extern Datum lo_unlink(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/parsenodes.h sepgsql/src/include/nodes/parsenodes.h +--- base/src/include/nodes/parsenodes.h 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/include/nodes/parsenodes.h 2007-10-25 13:12:52.000000000 +0900 +@@ -145,6 +145,9 @@ typedef struct Query + * plan node, not in the Query. + */ + List *returningLists; /* list of lists of TargetEntry, or NIL */ ++ ++ /* PGACE: opaque list for PGACE */ ++ List *pgaceList; + } Query; + + +@@ -405,6 +408,7 @@ typedef struct ColumnDef + Node *raw_default; /* default value (untransformed parse tree) */ + char *cooked_default; /* nodeToString representation */ + List *constraints; /* other constraints on column */ ++ Node *pgace_item; /* security attribute used by PGACE */ + } ColumnDef; + + /* +@@ -906,7 +910,8 @@ typedef enum AlterTableType + AT_EnableTrigUser, /* ENABLE TRIGGER USER */ + AT_DisableTrigUser, /* DISABLE TRIGGER USER */ + AT_AddInherit, /* INHERIT parent */ +- AT_DropInherit /* NO INHERIT parent */ ++ AT_DropInherit, /* NO INHERIT parent */ ++ AT_SetSecurityLabel, /* SET SECURITY LABEL (via PGACE) */ + } AlterTableType; + + typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */ +@@ -1062,6 +1067,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 *pgace_item; /* security attribute used by PGACE */ + } CreateStmt; + + /* ---------- +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-08 20:37:36.000000000 +0900 ++++ sepgsql/src/include/pg_config.h.in 2008-01-08 20:44:45.000000000 +0900 +@@ -343,6 +343,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 + +@@ -601,6 +604,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 2007-10-25 13:12:52.000000000 +0900 +@@ -0,0 +1,643 @@ ++/* ++ * include/security/pgace.h ++ * headers for PostgreSQL Access Control Extensions (PGACE) ++ * Copyright 2007 KaiGai Kohei ++ */ ++#ifndef PGACE_H ++#define PGACE_H ++ ++#include "access/htup.h" ++#include "commands/trigger.h" ++#include "lib/stringinfo.h" ++#include "nodes/execnodes.h" ++#include "nodes/parsenodes.h" ++#include "storage/itemptr.h" ++#include "storage/large_object.h" ++#include "tcop/dest.h" ++#include "utils/rel.h" ++ ++/* ++ * SECURITY_SYSATTR_NAME is the name of system column name ++ * for security attribute, defined in pg_config.h ++ * If it is not defined, security attribute support is disabled ++ * ++ * see, src/include/pg_config.h ++ */ ++ ++#ifdef HAVE_SELINUX ++#include "security/sepgsql.h" ++// the following line will be fixed by Sun's people ++// #elifdef HAVE_SOLARISTX ++// #include "security/solaristx.h" ++#else ++ ++/****************************************************************** ++ * Initialize / Finalize related hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceShmemSize() have to return the size of shared memory segment ++ * required by PGACE implementation. If no shared memory segment needed, ++ * it should return 0. ++ */ ++static inline Size pgaceShmemSize(void) { ++ return 0; ++} ++ ++/* ++ * pgaceInitialize() is called when a new PostgreSQL instance is generated. ++ * A PGACE implementation can initialize itself. ++ */ ++static inline void pgaceInitialize(void) { ++ /* do nothing */ ++} ++ ++/* ++ * pgaceInitializePostmaster() is called when a postmaster server process ++ * is started up. If it returns false, the server starting up process ++ * will be aborted. ++ */ ++static inline bool pgaceInitializePostmaster(void) { ++ return true; ++} ++ ++/* ++ * pgaceFinalizePostmaster() is called when a postmaster server process ++ * is just ending up. ++ */ ++static inline void pgaceFinalizePostmaster(void) { ++ /* do nothing */ ++} ++ ++/****************************************************************** ++ * SQL proxy hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceProxyQuery() is called just after query rewrite phase. ++ * PGACE implementation can modify the query trees in this hook, ++ * if necessary. ++ * ++ * @queryList : a list of Query typed objects. ++ */ ++static inline List *pgaceProxyQuery(List *queryList) { ++ return queryList; ++} ++ ++/* ++ * pgacePortalStart() is called on the top of PortalStart(). ++ * ++ * @portal : a Portal object currently executed. ++ */ ++static inline void pgacePortalStart(Portal portal) { ++ /* do nothing */ ++} ++ ++/* ++ * pgaceExecutorStart() is called on the top of ExecutorStart(). ++ * ++ * @queryDesc : a QueryDesc object given to ExecutorStart(). ++ * @eflags : eflags valus given to ExecutorStart(). ++ * if EXEC_FLAG_EXPLAIN_ONLY is set, no real access will run. ++ */ ++static inline void pgaceExecutorStart(QueryDesc *queryDesc, int eflags) { ++ /* do nothing */ ++} ++ ++/****************************************************************** ++ * HeapTuple modification hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceExecInsert() is called when a client tries to insert a new tuple ++ * via explicit INSERT statement from ExecInsert() at execMain.c ++ * If it returns false, insertion of the tuple will be cancelled. ++ * ++ * @rel : the target relation of INSERT ++ * @tuple : the contains of the inserted tuple ++ * @with_returning : true, if the query has RETURNING clause ++ */ ++static inline bool pgaceExecInsert(Relation rel, HeapTuple tuple, bool with_returning) { ++ return true; ++} ++ ++/* ++ * pgaceExecUpdate() is called when clients tries to update a tuple ++ * via explicit UPDATE statement from ExecUpdate() at execMain.c ++ * If it returns false, updating the tuple will be cancelled. ++ * ++ * @rel : the target relation of UPDATE ++ * @newtup : the new contains of the updated tuple ++ * @tid : ItemPointer of the tuple updated ++ * @with_returning : true, if the query has RETURNING clause ++ */ ++static inline bool pgaceExecUpdate(Relation rel, HeapTuple newtup, ItemPointer tid, bool with_returning) { ++ return true; ++} ++ ++/* ++ * pgaceExecUpdate() is called when clients tries to delete a tuple ++ * via explicit DELETE statement from ExecDelete() at execMain.c ++ * If it returns false, deletion of the tuple will be cancelled. ++ * ++ * @rel : the target relation of DELETE ++ * @tid : ItemPointer of the tuple deleted ++ * @with_returning : true, if the query has RETURNING clause ++ */ ++static inline bool pgaceExecDelete(Relation rel, ItemPointer tid, bool with_returning) { ++ return true; ++} ++ ++/* ++ * pgaceSimpleHeapInsert() is called just before simple_heap_insert() is processed ++ * ++ * @rel : the target relation of simple_heap_insert() ++ * @tuple : the contains of the inserted tuple ++ */ ++static inline void pgaceSimpleHeapInsert(Relation rel, HeapTuple tuple) { ++ /* do nothing */ ++} ++ ++/* ++ * pgaceSimpleHeapUpdate() is called just before simple_heap_update() is processed ++ * ++ * @rel : the target relation of simple_heap_update() ++ * @tid : ItemPointer of the tuple updated ++ * @tuple : the new contains of the updated tuple ++ */ ++static inline void pgaceSimpleHeapUpdate(Relation rel, ItemPointer tid, HeapTuple tuple) { ++ /* do nothing */ ++} ++ ++/* ++ * pgaceSimpleHeapDelete() is called just before simple_heap_delete() is processed ++ * ++ * @rel : the target relation of simple_heap_delete() ++ * @tid : ItemPointer of the tuple deleted ++ */ ++static inline void pgaceSimpleHeapDelete(Relation rel, ItemPointer tid) { ++ /* do nothing */ ++} ++ ++/* ++ * pgaceHeapInsert() is called from heap_insert() ++ * ++ * @rel : the target relation of heap_insert() ++ * @tuple : the contains of the inserted tuples. It also contains system attribute like Oid ++ */ ++static inline void pgaceHeapInsert(Relation rel, HeapTuple tuple) { ++ /* do nothing */ ++} ++ ++/* ++ * pgaceHeapUpdate() is called from heap_update() ++ * ++ * @rel : the target relation of heap_update() ++ * @newtup : the contains of the updated tuples. It also contains system attribute like Oid ++ * @oldtup : the tuple which will be updated ++ */ ++static inline void pgaceHeapUpdate(Relation rel, HeapTuple newtup, HeapTuple oldtup) { ++ /* do nothing */ ++} ++ ++/* ++ * pgaceHeapDelete() is called from heap_delete() ++ * ++ * @rel : the target relation of heap_delete() ++ * @oldtup : the tuple which will be deleted ++ */ ++static inline void pgaceHeapDelete(Relation rel, HeapTuple oldtup) { ++ /* do nothing */ ++} ++ ++/****************************************************************** ++ * Extended SQL statement hooks ++ ******************************************************************/ ++/* ++ * PGACE implementation can use pgaceGramSecurityLabel() hook to extend ++ * SQL statement for explicit labeling. This hook is deployed on parser/gram.y ++ * as a part of the SQL grammer. If no SQL extension is necessary, it has to ++ * return NULL to cause yyerror(). ++ * ++ * @defname : given string ++ * @value : given string ++ */ ++static inline DefElem *pgaceGramSecurityLabel(char *defname, char *value) { ++ return NULL; ++} ++ ++/* ++ * PGACE implementation has to return true, if the given DefElem holds ++ * security label generated in pgaceGramSecurityLabel(). false, if any other. ++ * ++ * @defel : given DefElem object ++ */ ++static inline bool pgaceNodeIsSecurityLabel(DefElem *defel) { ++ return false; ++} ++ ++/* ++ * PGACE implementation has to translate DefElem object generated by ++ * pgaceGramSecurityLabel(), into t_security of HeapTupleHeader. ++ * ++ * @defel : given DefElem object ++ */ ++static inline Oid pgaceParseSecurityLabel(DefElem *defel) { ++ return InvalidOid; ++} ++ ++/****************************************************************** ++ * DATABASE related hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceSetDatabaseParam() is called when clients tries to set GUC variables ++ * ++ * @name : The name of GUC variable ++ * @argstr : The new valus of GUC variable. If argstr is NULL, it means ++ * clients tries to reset the variable. ++ */ ++static inline void pgaceSetDatabaseParam(const char *name, char *argstring) { ++ /* do nothing */ ++} ++ ++/* ++ * pgaceGetDatabaseParam() is called when clients tries to refer GUC variables ++ * ++ * @name : The name of GUC variable ++ */ ++static inline void pgaceGetDatabaseParam(const char *name) { ++ /* do nothing */ ++} ++ ++/****************************************************************** ++ * FUNCTION related hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceCallFunction() is called just before executing SQL function ++ * as a part of query. ++ * ++ * @finfo : FmgrInfo object for the target function ++ */ ++static inline void pgaceCallFunction(FmgrInfo *finfo) { ++ /* do nothing */ ++} ++ ++/* ++ * pgaceCallFunctionTrigger() is called just before executing ++ * trigger function. ++ * If it returns false, the trigger function will not be called and caller ++ * receives NULL tuple as a result. In the case when Before-Row triggers, ++ * it means the current operations on the tuple should be skipped. ++ * ++ * @finfo : FmgrInfo object for the target function ++ * @tgdata : TriggerData object for the current trigger invokation ++ */ ++static inline bool pgaceCallFunctionTrigger(FmgrInfo *finfo, TriggerData *tgdata) { ++ return true; ++} ++ ++/* ++ * pgaceCallFunctionFastPath() is called just before executing ++ * SQL function in the fast path. ++ * ++ * @finfo : FmgrInfo object for the target function ++ */ ++static inline void pgaceCallFunctionFastPath(FmgrInfo *finfo) { ++ /* do nothing */ ++} ++ ++/* ++ * pgacePreparePlanCheck() is called before foreign key/primary key constraint checks, ++ * at ri_PlanCheck(). PGACE implementation can return its opaque data for any purpose. ++ * ++ * @rel : the target relation in which a constraint is configured ++ */ ++static inline Datum pgacePreparePlanCheck(Relation rel) { ++ return (Datum) 0; ++} ++ ++/* ++ * pgaceRestorePlanCheck() is called after foreign key/primary key constraint checks, ++ * at ri_PlanCheck(). PGACE implementation can use an opaque data generated in the above ++ * pgacePreparePlanCheck(). ++ * ++ * @rel : the target relation in which a constraint is configured ++ * @pgace_saved : an opaque data returned from pgacePreparePlanCheck() ++ */ ++static inline void pgaceRestorePlanCheck(Relation rel, Datum pgace_saved) { ++ /* do nothing */ ++} ++ ++/****************************************************************** ++ * TABLE related hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceLockTable() is called when explicit LOCK statement used. ++ * ++ * @relid : the target relation id ++ */ ++static inline void pgaceLockTable(Oid relid) { ++ /* do nothing */ ++} ++ ++/* ++ * pgaceAlterTable() is called to modify table/column. The PGACE implementation ++ * have to update the target tuples within pg_class or pg_attribute. ++ * If AlterTableCmd tag is unexpected one, ++ * ++ * @rel : the target relation ++ * @cmd : AlterTableCmd object ++ */ ++static inline bool pgaceAlterTable(Relation rel, AlterTableCmd *cmd) { ++ return false; ++} ++ ++/****************************************************************** ++ * COPY TO/COPY FROM statement hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceCopyTable() is called when COPY TO/COPY FROM statement is processed ++ * ++ * @rel : the target relation ++ * @attNumList : the list of attribute numbers ++ * @isFrom : true, if the given statement is 'COPY FROM' ++ */ ++static inline void pgaceCopyTable(Relation rel, List *attNumList, bool isFrom) { ++ /* do nothing */ ++} ++ ++/* ++ * pgaceCopyToTuple() is called to check whether the given tuple should be ++ * filtered, or not in the process of COPY TO statement. ++ * If it returns false, the given tuple will be filtered from the result set ++ * ++ * @rel : the target relation ++ * @tuple : the target tuple ++ */ ++static inline bool pgaceCopyToTuple(Relation rel, HeapTuple tuple) { ++ return true; ++} ++ ++/* ++ * pgaceCopyFromTuple() is called to check whether the given tuple should be ++ * filtered, or not in the process of COPY FROM statement. ++ * If it returns false, the given tuple will be filtered from the result set ++ * ++ * @rel : the target relation ++ * @tuple : the target tuple ++ */ ++static inline bool pgaceCopyFromTuple(Relation rel, HeapTuple tuple) { ++ return true; ++} ++ ++/****************************************************************** ++ * Loadable shared library module hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceLoadSharedModule() is called just before load a shared library ++ * module. ++ * ++ * @filename : full path name of the shared library module ++ */ ++static inline void pgaceLoadSharedModule(const char *filename) { ++ /* do nothing */ ++} ++ ++/****************************************************************** ++ * Binary Large Object (BLOB) hooks ++ ******************************************************************/ ++ ++/* ++ * pgaceLargeObjectGetSecurity() is called when lo_get_security() is executed ++ * It returns it's security attribute. ++ * ++ * @tuple : a tuple which is a part of the target largeobject. ++ */ ++static inline Oid pgaceLargeObjectGetSecurity(HeapTuple tuple) { ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("There is no security attribute support."))); ++ return InvalidOid; ++} ++ ++/* ++ * pgaceLargeObjectSetSecurity() is called when lo_set_security() is executed ++ * ++ * @tuple : a tuple which is a part of the target largeobject. ++ * @lo_security : new security attribute specified ++ * @is_first : true, if it's the first call in the largeobject. ++ * Because a largeobject may contain some tuples, this hook ++ * may be called several times for a single largeobject. ++ */ ++static inline void pgaceLargeObjectSetSecurity(HeapTuple tuple, Oid lo_security, bool is_first) { ++ ereport(ERROR, ++ (errcode(ERRCODE_INTERNAL_ERROR), ++ errmsg("There is no security attribute support."))); ++} ++ ++/* ++ * pgaceLargeObjectCreate() is called when a new large object is created ++ * ++ * @rel : pg_largeobject relation opened with RowExclusiveLock ++ * @tuple : a new tuple for the new large object ++ */ ++static inline void pgaceLargeObjectCreate(Relation rel, HeapTuple tuple) { ++ /* do nothing */ ++} ++ ++/* ++ * pgaceLargeObjectDrop() is called when a large object is dropped once for ++ * a large object ++ * ++ * @rel : pg_largeobject relation opened with RowExclusiveLock ++ * @tuple : one of the tuples within the target large object ++ */ ++static inline void pgaceLargeObjectDrop(Relation rel, HeapTuple tuple) { ++ /* do nothing */ ++} ++ ++/* ++ * pgaceLargeObjectOpen() is called when a large object is opened ++ * ++ * @rel : pg_largeobject relation opened with RowExclusiveLock ++ * @tuple : head of the tuples within the target large object ++ * @read_only : true, if large object is opened as read only mode ++ */ ++static inline void pgaceLargeObjectOpen(Relation rel, HeapTuple tuple, bool read_only) { ++ /* do nothing */ ++} ++ ++/* ++ * pgaceLargeObjectRead is called when they read from a large object ++ * ++ * @rel : pg_largeobject relation opened with AccessShareLock ++ * @tuple : a tuple within the target large object ++ */ ++static inline void pgaceLargeObjectRead(Relation rel, HeapTuple tuple) { ++ /* do nothing */ ++} ++ ++/* ++ * pgaceLargeObjectWrite() is called when they write to a large object ++ * ++ * @rel : pg_largeobject relation opened with RowExclusiveLock ++ * @newtup : a new tuple within the target large object ++ * @oldtup : a original tuple within the target large object, if exist ++ */ ++static inline void pgaceLargeObjectWrite(Relation rel, HeapTuple newtup, HeapTuple oldtup) { ++ /* do nothing */ ++} ++ ++/* ++ * pgaceLargeObjectImport() is called when lo_import() is processed ++ */ ++static inline void pgaceLargeObjectImport(void) { ++ /* do nothing */ ++} ++ ++/* ++ * pgaceLargeObjectExport() is called when lo_import() is processed ++ */ ++static inline void pgaceLargeObjectExport(void) { ++ /* do nothing */ ++} ++ ++/****************************************************************** ++ * Security Label hooks ++ ******************************************************************/ ++ ++/* ++ * PGACE implementation can use pgaceSecurityLabelIn() hook to translate ++ * a input security label from external representation into internal one. ++ * If no translation is necessary, it has to return @seclabel as is. ++ * ++ * @seclabel : security label being input ++ */ ++static inline char *pgaceSecurityLabelIn(char *seclabel) { ++ return seclabel; ++} ++ ++/* ++ * PGACE implementation can use pgaceSecurityLabelOut() hook to translate ++ * a security label in internal representation into external one. ++ * If no translation is necessary, it has to return @seclabel as is. ++ * ++ * @seclabel : security label being output ++ */ ++static inline char *pgaceSecurityLabelOut(char *seclabel) { ++ return seclabel; ++} ++ ++/* ++ * pgaceSecurityLabelIsValid() checks whether the @seclabel is valid or not. ++ * return false, if @seclabel is not valid security attribute in text representation. ++ * ++ * @seclabel : security attribute in text representation ++ */ ++static inline bool pgaceSecurityLabelIsValid(char *seclabel) { ++ return true; ++} ++ ++/* ++ * pgaceSecurityLabelOfLabel() returns the security attribute of a newly ++ * generated tuple within pg_security ++ * ++ * @new_label : a text representation of security context which will be newly ++ * inserted into pg_security. ++ */ ++static inline char *pgaceSecurityLabelOfLabel(char *new_label) { ++ return pstrdup("unlabeled"); ++} ++ ++/* ++ * pgaceSecurityLabelNotFound() has to return a string representation of security ++ * attribute, when no tuple with oid equals to @sid is within pg_security system ++ * catalog. ++ * ++ * @sid : required sid, but not found on pg_security ++ */ ++static inline char *pgaceSecurityLabelNotFound(Oid sid) { ++ return pstrdup("unlabeled"); ++} ++ ++/****************************************************************** ++ * Extended node type hooks ++ ******************************************************************/ ++ ++/* ++ * If PGACE implementation requires new node type, a method to copy object. ++ * pgaceCopyObject() provides a hook to copy new node typed object. ++ * If a given object (@orig) has a tag extended by PGACE implementation, ++ * it have to copy and return it. ++ * If it returns NULL, @orig is not available for the PGACE implementation. ++ * ++ * @orig : a object which to copy ++ */ ++static inline Node *pgaceCopyObject(Node *orig) { ++ return NULL; ++} ++ ++/* ++ * pgaceOutObject() provides a hook to translate a object to text representation. ++ * If a given object (@node) has a tag extended by PGACE implementation, it have ++ * to put a text representation into StringInfo. ++ * If it returns false, @node is not available for the PGACE implementation. ++ * ++ * @str : StringInfo which to put the text representation ++ * @node : a object that text representation is required ++ */ ++static inline bool pgaceOutObject(StringInfo str, Node *node) { ++ return false; ++} ++ ++#endif ++ ++/* writable system column support */ ++#ifdef SECURITY_SYSATTR_NAME ++static inline bool pgaceWritableSystemColumn(int attrno) { ++ return ((attrno == SecurityAttributeNumber) ? true : false); ++} ++extern void pgaceTransformSelectStmt(List *targetList); ++extern void pgaceTransformInsertStmt(List **p_icolumns, List **p_attrnos, List *targetList); ++extern void pgaceFetchSecurityLabel(JunkFilter *junkfilter, TupleTableSlot *slot, Oid *tts_security); ++#else ++static inline bool pgaceWritableSystemColumn(int attrno) { ++ return false; ++} ++static inline void pgaceTransformSelectStmt(List *targetList) { /* do nothing */ } ++static inline void pgaceTransformInsertStmt(List **p_icolumns, ++ List **p_attrnos, ++ List *targetList) { /* do nothing */ } ++static inline void pgaceFetchSecurityLabel(JunkFilter *junkfilter, ++ TupleTableSlot *slot, ++ Oid *tts_security) { /* do nothing */ } ++#endif ++ ++/* Extended SQL statements related */ ++extern List *pgaceBuildAttrListForRelation(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); ++extern void pgaceCreateDatabaseCommon(HeapTuple tuple, DefElem *defel); ++extern void pgaceAlterDatabaseCommon(HeapTuple tuple, DefElem *defel); ++extern void pgaceCreateFunctionCommon(HeapTuple tuple, DefElem *defel); ++extern void pgaceAlterFunctionCommon(HeapTuple tuple, DefElem *defel); ++ ++/* SQL functions related to security label */ ++extern Datum security_label_in(PG_FUNCTION_ARGS); ++extern Datum security_label_out(PG_FUNCTION_ARGS); ++extern Datum security_label_raw_in(PG_FUNCTION_ARGS); ++extern Datum security_label_raw_out(PG_FUNCTION_ARGS); ++extern Datum text_to_security_label(PG_FUNCTION_ARGS); ++extern Datum security_label_to_text(PG_FUNCTION_ARGS); ++ ++/* SQL functions related to large object */ ++extern Datum lo_get_security(PG_FUNCTION_ARGS); ++extern Datum lo_set_security(PG_FUNCTION_ARGS); ++ ++#endif // PGACE_H +diff -rpNU3 base/src/include/security/sepgsql.h sepgsql/src/include/security/sepgsql.h +--- base/src/include/security/sepgsql.h 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/include/security/sepgsql.h 2007-10-25 13:12:52.000000000 +0900 +@@ -0,0 +1,330 @@ ++/* ++ * src/include/sepgsql.h ++ * The header file of Security Enhanced PostgreSQL ++ * ++ * Copyright (c) 2006 - 2007, KaiGai Kohei ++ */ ++#ifndef SEPGSQL_H ++#define SEPGSQL_H ++#include "executor/executor.h" ++#include "security/sepgsql_internal.h" ++#include "utils/portal.h" ++ ++#define SECURITY_SYSATTR_NAME "security_context" ++ ++/****************************************************************** ++ * Initialize / Finalize related hooks ++ ******************************************************************/ ++ ++static inline Size pgaceShmemSize(void) { ++ Size retval = 0; ++ if (sepgsqlIsEnabled()) ++ retval = sepgsqlShmemSize(); ++ return retval; ++} ++ ++static inline void pgaceInitialize(void) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlInitialize(); ++} ++ ++static inline bool pgaceInitializePostmaster(void) { ++ if (!sepgsqlIsEnabled()) ++ return true; ++ return sepgsqlInitializePostmaster(); ++} ++ ++static inline void pgaceFinalizePostmaster(void) { ++ if (!sepgsqlIsEnabled()) ++ return; ++ sepgsqlFinalizePostmaster(); ++} ++ ++/****************************************************************** ++ * SQL proxy hooks ++ ******************************************************************/ ++ ++static inline List *pgaceProxyQuery(List *queryList) { ++ List *newList = NIL; ++ ListCell *l; ++ ++ if (!sepgsqlIsEnabled()) ++ return queryList; ++ foreach (l, queryList) { ++ Query *query = (Query *) lfirst(l); ++ ++ newList = list_concat(newList, sepgsqlProxyQuery(query)); ++ } ++ return newList; ++} ++ ++static inline void pgacePortalStart(Portal portal) { ++ /* do nothing */ ++} ++ ++static inline void pgaceExecutorStart(QueryDesc *queryDesc, int eflags) { ++ if (!sepgsqlIsEnabled() || (eflags & EXEC_FLAG_EXPLAIN_ONLY)) ++ return; ++ ++ Assert(queryDesc->parsetree != NULL); ++ sepgsqlVerifyQuery(queryDesc->parsetree); ++} ++ ++/****************************************************************** ++ * HeapTuple modification hooks ++ ******************************************************************/ ++ ++static inline bool pgaceExecInsert(Relation rel, HeapTuple tuple, bool with_returning) { ++ if (!sepgsqlIsEnabled()) ++ return true; ++ return sepgsqlExecInsert(rel, tuple, with_returning); ++} ++ ++static inline bool pgaceExecUpdate(Relation rel, HeapTuple newtup, ItemPointer tid, bool with_returning) { ++ if (!sepgsqlIsEnabled()) ++ return true; ++ return sepgsqlExecUpdate(rel, newtup, tid, with_returning); ++} ++ ++static inline bool pgaceExecDelete(Relation rel, ItemPointer tid, bool with_returning) { ++ if (!sepgsqlIsEnabled()) ++ return true; ++ return sepgsqlExecDelete(rel, tid, with_returning); ++} ++ ++static inline void pgaceSimpleHeapInsert(Relation rel, HeapTuple tuple) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlSimpleHeapInsert(rel, tuple); ++} ++ ++static inline void pgaceSimpleHeapUpdate(Relation rel, ItemPointer tid, HeapTuple tuple) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlSimpleHeapUpdate(rel, tid, tuple); ++} ++ ++static inline void pgaceSimpleHeapDelete(Relation rel, ItemPointer tid) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlSimpleHeapDelete(rel, tid); ++} ++ ++static inline void pgaceHeapInsert(Relation rel, HeapTuple tuple) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlHeapInsert(rel, tuple); ++} ++ ++static inline void pgaceHeapUpdate(Relation rel, HeapTuple newtup, HeapTuple oldtup) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlHeapUpdate(rel, newtup, oldtup); ++} ++ ++static inline void pgaceHeapDelete(Relation rel, HeapTuple oldtup) { ++ /* do nothing */ ++} ++ ++/****************************************************************** ++ * Extended SQL statement hooks ++ ******************************************************************/ ++static inline DefElem *pgaceGramSecurityLabel(char *defname, char *value) { ++ if (!sepgsqlIsEnabled()) ++ return NULL; ++ return sepgsqlGramSecurityLabel(defname, value); ++} ++ ++static inline bool pgaceNodeIsSecurityLabel(DefElem *defel) { ++ if (!sepgsqlIsEnabled()) ++ return false; ++ return sepgsqlNodeIsSecurityLabel(defel); ++} ++ ++static inline Oid pgaceParseSecurityLabel(DefElem *defel) { ++ if (sepgsqlIsEnabled() && defel) ++ return sepgsqlParseSecurityLabel(defel); ++ return InvalidOid; ++} ++ ++/****************************************************************** ++ * DATABASE related hooks ++ ******************************************************************/ ++ ++static inline void pgaceSetDatabaseParam(const char *name, char *argstring) { ++ /* argstring == NULL means set default */ ++ if (sepgsqlIsEnabled()) ++ sepgsqlSetDatabaseParam(name, argstring); ++} ++ ++static inline void pgaceGetDatabaseParam(const char *name) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlGetDatabaseParam(name); ++} ++ ++/****************************************************************** ++ * FUNCTION related hooks ++ ******************************************************************/ ++ ++static inline void pgaceCallFunction(FmgrInfo *finfo) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlCallFunction(finfo, false); ++} ++ ++static inline bool pgaceCallFunctionTrigger(FmgrInfo *finfo, TriggerData *tgdata) { ++ if (!sepgsqlIsEnabled()) ++ return true; ++ return sepgsqlCallFunctionTrigger(finfo, tgdata); ++} ++ ++static inline void pgaceCallFunctionFastPath(FmgrInfo *finfo) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlCallFunction(finfo, true); ++} ++ ++static inline Datum pgacePreparePlanCheck(Relation rel) { ++ Oid pgace_saved = InvalidOid; ++ if (sepgsqlIsEnabled()) ++ pgace_saved = sepgsqlPreparePlanCheck(rel); ++ return ObjectIdGetDatum(pgace_saved); ++} ++ ++static inline void pgaceRestorePlanCheck(Relation rel, Datum pgace_saved) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlRestorePlanCheck(rel, DatumGetObjectId(pgace_saved)); ++} ++ ++/****************************************************************** ++ * TABLE related hooks ++ ******************************************************************/ ++ ++static inline void pgaceLockTable(Oid relid) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlLockTable(relid); ++} ++ ++/****************************************************************** ++ * COPY TO/COPY FROM statement hooks ++ ******************************************************************/ ++ ++static inline void pgaceCopyTable(Relation rel, List *attNumList, bool isFrom) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlCopyTable(rel, attNumList, isFrom); ++} ++ ++static inline bool pgaceCopyToTuple(Relation rel, HeapTuple tuple) { ++ if (!sepgsqlIsEnabled()) ++ return true; ++ return sepgsqlCopyToTuple(rel, tuple); ++} ++ ++static inline bool pgaceCopyFromTuple(Relation rel, HeapTuple tuple) { ++ if (!sepgsqlIsEnabled()) ++ return true; ++ return sepgsqlCopyFromTuple(rel, tuple); ++} ++ ++/****************************************************************** ++ * Loadable shared library module hooks ++ ******************************************************************/ ++ ++static inline void pgaceLoadSharedModule(const char *filename) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlLoadSharedModule(filename); ++} ++ ++/****************************************************************** ++ * Binary Large Object (BLOB) hooks ++ ******************************************************************/ ++static inline Oid pgaceLargeObjectGetSecurity(HeapTuple tuple) { ++ if (!sepgsqlIsEnabled()) ++ selerror("SELinux is disabled"); ++ return sepgsqlLargeObjectGetSecurity(tuple); ++} ++ ++static inline void pgaceLargeObjectSetSecurity(HeapTuple tuple, Oid lo_security, bool is_first) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlLargeObjectSetSecurity(tuple, lo_security, is_first); ++} ++ ++static inline void pgaceLargeObjectCreate(Relation rel, HeapTuple tuple) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlLargeObjectCreate(rel, tuple); ++} ++ ++static inline void pgaceLargeObjectDrop(Relation rel, HeapTuple tuple) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlLargeObjectDrop(rel, tuple); ++} ++ ++static inline void pgaceLargeObjectOpen(Relation rel, HeapTuple tuple, bool read_only) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlLargeObjectOpen(rel, tuple, read_only); ++} ++ ++static inline void pgaceLargeObjectRead(Relation rel, HeapTuple tuple) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlLargeObjectRead(rel, tuple); ++} ++ ++static inline void pgaceLargeObjectWrite(Relation rel, HeapTuple newtup, HeapTuple oldtup) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlLargeObjectWrite(rel, newtup, oldtup); ++} ++ ++static inline void pgaceLargeObjectImport(void) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlLargeObjectImport(); ++} ++ ++static inline void pgaceLargeObjectExport(void) { ++ if (sepgsqlIsEnabled()) ++ sepgsqlLargeObjectExport(); ++} ++ ++/****************************************************************** ++ * Security Label hooks ++ ******************************************************************/ ++static inline char *pgaceSecurityLabelIn(char *context) { ++ if (!sepgsqlIsEnabled()) ++ return NULL; ++ return sepgsqlSecurityLabelIn(context); ++} ++ ++static inline char *pgaceSecurityLabelOut(char *context) { ++ if (!sepgsqlIsEnabled()) ++ return NULL; ++ return sepgsqlSecurityLabelOut(context); ++} ++ ++static inline bool pgaceSecurityLabelIsValid(char *context) { ++ if (!sepgsqlIsEnabled()) ++ return false; ++ return sepgsqlSecurityLabelIsValid(context); ++} ++ ++static inline char *pgaceSecurityLabelOfLabel(char *new_label) { ++ if (!sepgsqlIsEnabled()) ++ return pstrdup("unlabeled"); ++ return sepgsqlSecurityLabelOfLabel(new_label); ++} ++ ++static inline char *pgaceSecurityLabelNotFound(Oid sid) { ++ if (!sepgsqlIsEnabled()) ++ return pstrdup("unlabeled"); ++ ++ return sepgsqlSecurityLabelNotFound(sid); ++} ++ ++/****************************************************************** ++ * Extended node type hooks ++ ******************************************************************/ ++ ++static inline Node *pgaceCopyObject(Node *orig) { ++ if (!sepgsqlIsEnabled()) ++ return NULL; ++ return sepgsqlCopyObject(orig); ++} ++ ++static inline bool pgaceOutObject(StringInfo str, Node *node) { ++ if (!sepgsqlIsEnabled()) ++ return false; ++ return sepgsqlOutObject(str, node); ++} ++ ++#endif /* SEPGSQL_H */ +diff -rpNU3 base/src/include/security/sepgsql_internal.h sepgsql/src/include/security/sepgsql_internal.h +--- base/src/include/security/sepgsql_internal.h 1970-01-01 09:00:00.000000000 +0900 ++++ sepgsql/src/include/security/sepgsql_internal.h 2008-01-30 17:08:18.000000000 +0900 +@@ -0,0 +1,290 @@ ++#ifndef SEPGSQL_INTERNAL_H ++#define SEPGSQL_INTERNAL_H ++ ++/* system catalogs */ ++#include "catalog/catalog.h" ++#include "catalog/indexing.h" ++#include "catalog/pg_aggregate.h" ++#include "catalog/pg_am.h" ++#include "catalog/pg_attribute.h" ++#include "catalog/pg_authid.h" ++#include "catalog/pg_cast.h" ++#include "catalog/pg_class.h" ++#include "catalog/pg_constraint.h" ++#include "catalog/pg_conversion.h" ++#include "catalog/pg_database.h" ++#include "catalog/pg_language.h" ++#include "catalog/pg_largeobject.h" ++#include "catalog/pg_listener.h" ++#include "catalog/pg_namespace.h" ++#include "catalog/pg_opclass.h" ++#include "catalog/pg_operator.h" ++#include "catalog/pg_pltemplate.h" ++#include "catalog/pg_proc.h" ++#include "catalog/pg_rewrite.h" ++#include "catalog/pg_security.h" ++#include "catalog/pg_tablespace.h" ++#include "catalog/pg_trigger.h" ++#include "catalog/pg_type.h" ++#include "lib/stringinfo.h" ++#include "nodes/nodes.h" ++#include "nodes/parsenodes.h" ++#include "storage/large_object.h" ++ ++#include ++#include ++#include ++ ++#define selerror(fmt, ...) \ ++ ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), \ ++ errmsg("%s(%d): " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__))) ++#define selnotice(fmt, ...) \ ++ ereport(NOTICE, (errcode(ERRCODE_WARNING), \ ++ errmsg("%s(%d): " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__))) ++#define seldebug(fmt, ...) \ ++ ereport(NOTICE, (errcode(ERRCODE_WARNING), \ ++ errmsg("%s(%d): " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__))) ++#define selbugon(x) do { if (x)((char *)NULL)[0] = 'a'; }while(0) ++ ++// for debugging macros ++#define seldump_pg_class(rel) \ ++ selnotice("pg_class (%p) { relname='%s', relnamespace=%u, reltype=%u, " \ ++ "relowner=%u, relam=%u, relfilenode=%u, reltablespace=%u, " \ ++ "relpages=%d, reltuples=%f, reltoastrelid=%u, reltoastidxid=%u, " \ ++ "relhasindex=%c, relisshared=%c, relkind=%c, relnatts=%d, " \ ++ "relchecks=%d, reltriggers=%d, relukeys=%d, relfkeys=%d, " \ ++ "relrefs=%d, relhasoids=%c, relhaspkey=%c, relhasrules=%c, " \ ++ "relhassubclass=%c, ...}", \ ++ (rel), NameStr((rel)->relname), (rel)->relnamespace, \ ++ (rel)->reltype, (rel)->relowner, (rel)->relam, (rel)->relfilenode, \ ++ (rel)->reltablespace, (rel)->relpages, (rel)->reltuples, \ ++ (rel)->reltoastrelid, (rel)->reltoastidxid, (rel)->relhasindex ? 'y' : 'n', \ ++ (rel)->relisshared ? 'y' : 'n', (rel)->relkind, (rel)->relnatts, \ ++ (rel)->relchecks, (rel)->reltriggers, (rel)->relukeys, (rel)->relfkeys, \ ++ (rel)->relrefs, (rel)->relhasoids ? 'y' : 'n', (rel)->relhaspkey ? 'y' : 'n', \ ++ (rel)->relhasrules ? 'y' : 'n', (rel)->relhassubclass ? 'y' : 'n') ++#define seldump_pg_attribute(att) \ ++ selnotice("pg_attribute (%p) { attrelid=%u, attname='%s', atttypid=%u, " \ ++ "attstattarget=%d, attlen=%d, attnum=%d, attndims=%d, attcacheoff=%d, " \ ++ "atttypmod=%d, attbyval=%c, attstorage=%c, attalign=%d, attnotnull=%c, " \ ++ "atthasdef=%c, attisdropped=%c, attislocal=%c, attinhcount=%d }", \ ++ (att), (att)->attrelid, NameStr((att)->attname), (att)->atttypid, \ ++ (att)->attstattarget, (att)->attlen, (att)->attnum, (att)->attndims, \ ++ (att)->attcacheoff, (att)->atttypmod, (att)->attbyval, (att)->attstorage, \ ++ (att)->attalign, (att)->attnotnull ? 'y' : 'n', (att)->atthasdef ? 'y' : 'n', \ ++ (att)->attisdropped ? 'y' : 'n', (att)->attislocal ? 'y' : 'n', (att)->attinhcount) ++ ++/* ++ * Permission codes of internal representation ++ */ ++#define SEPGSQL_PERMS_USE (1UL << (N_ACL_RIGHTS + 0)) ++#define SEPGSQL_PERMS_SELECT (1UL << (N_ACL_RIGHTS + 1)) ++#define SEPGSQL_PERMS_UPDATE (1UL << (N_ACL_RIGHTS + 2)) ++#define SEPGSQL_PERMS_INSERT (1UL << (N_ACL_RIGHTS + 3)) ++#define SEPGSQL_PERMS_DELETE (1UL << (N_ACL_RIGHTS + 4)) ++#define SEPGSQL_PERMS_RELABELFROM (1UL << (N_ACL_RIGHTS + 5)) ++#define SEPGSQL_PERMS_RELABELTO (1UL << (N_ACL_RIGHTS + 6)) ++#define SEPGSQL_PERMS_READ (1UL << (N_ACL_RIGHTS + 7)) ++#define SEPGSQL_PERMS_WRITE (1UL << (N_ACL_RIGHTS + 8)) ++#define SEPGSQL_PERMS_ALL (SEPGSQL_PERMS_WRITE - SEPGSQL_PERMS_USE) ++ ++/* The definition of object classes/access vectors are defined at libselinux-devel */ ++#ifndef SECCLASS_DB_DATABASE /* for legacy selinux/flask.h */ ++#define SECCLASS_DB_DATABASE (62) /* next to SECCLASS_MEMPROTECT */ ++#define SECCLASS_DB_TABLE (SECCLASS_DB_DATABASE + 1) ++#define SECCLASS_DB_PROCEDURE (SECCLASS_DB_DATABASE + 2) ++#define SECCLASS_DB_COLUMN (SECCLASS_DB_DATABASE + 3) ++#define SECCLASS_DB_TUPLE (SECCLASS_DB_DATABASE + 4) ++#define SECCLASS_DB_BLOB (SECCLASS_DB_DATABASE + 5) ++#endif ++ ++#ifndef COMMON_DATABASE__CREATE /* for legacy selinux/av_permission.h */ ++#define COMMON_DATABASE__CREATE 0x00000001UL ++#define COMMON_DATABASE__DROP 0x00000002UL ++#define COMMON_DATABASE__GETATTR 0x00000004UL ++#define COMMON_DATABASE__SETATTR 0x00000008UL ++#define COMMON_DATABASE__RELABELFROM 0x00000010UL ++#define COMMON_DATABASE__RELABELTO 0x00000020UL ++ ++#define DB_DATABASE__CREATE 0x00000001UL ++#define DB_DATABASE__DROP 0x00000002UL ++#define DB_DATABASE__GETATTR 0x00000004UL ++#define DB_DATABASE__SETATTR 0x00000008UL ++#define DB_DATABASE__RELABELFROM 0x00000010UL ++#define DB_DATABASE__RELABELTO 0x00000020UL ++#define DB_DATABASE__ACCESS 0x00000040UL ++#define DB_DATABASE__INSTALL_MODULE 0x00000080UL ++#define DB_DATABASE__LOAD_MODULE 0x00000100UL ++#define DB_DATABASE__GET_PARAM 0x00000200UL ++#define DB_DATABASE__SET_PARAM 0x00000400UL ++#define DB_TABLE__CREATE 0x00000001UL ++#define DB_TABLE__DROP 0x00000002UL ++#define DB_TABLE__GETATTR 0x00000004UL ++#define DB_TABLE__SETATTR 0x00000008UL ++#define DB_TABLE__RELABELFROM 0x00000010UL ++#define DB_TABLE__RELABELTO 0x00000020UL ++#define DB_TABLE__USE 0x00000040UL ++#define DB_TABLE__SELECT 0x00000080UL ++#define DB_TABLE__UPDATE 0x00000100UL ++#define DB_TABLE__INSERT 0x00000200UL ++#define DB_TABLE__DELETE 0x00000400UL ++#define DB_TABLE__LOCK 0x00000800UL ++#define DB_PROCEDURE__CREATE 0x00000001UL ++#define DB_PROCEDURE__DROP 0x00000002UL ++#define DB_PROCEDURE__GETATTR 0x00000004UL ++#define DB_PROCEDURE__SETATTR 0x00000008UL ++#define DB_PROCEDURE__RELABELFROM 0x00000010UL ++#define DB_PROCEDURE__RELABELTO 0x00000020UL ++#define DB_PROCEDURE__EXECUTE 0x00000040UL ++#define DB_PROCEDURE__ENTRYPOINT 0x00000080UL ++#define DB_COLUMN__CREATE 0x00000001UL ++#define DB_COLUMN__DROP 0x00000002UL ++#define DB_COLUMN__GETATTR 0x00000004UL ++#define DB_COLUMN__SETATTR 0x00000008UL ++#define DB_COLUMN__RELABELFROM 0x00000010UL ++#define DB_COLUMN__RELABELTO 0x00000020UL ++#define DB_COLUMN__USE 0x00000040UL ++#define DB_COLUMN__SELECT 0x00000080UL ++#define DB_COLUMN__UPDATE 0x00000100UL ++#define DB_COLUMN__INSERT 0x00000200UL ++#define DB_TUPLE__RELABELFROM 0x00000001UL ++#define DB_TUPLE__RELABELTO 0x00000002UL ++#define DB_TUPLE__USE 0x00000004UL ++#define DB_TUPLE__SELECT 0x00000008UL ++#define DB_TUPLE__UPDATE 0x00000010UL ++#define DB_TUPLE__INSERT 0x00000020UL ++#define DB_TUPLE__DELETE 0x00000040UL ++#define DB_BLOB__CREATE 0x00000001UL ++#define DB_BLOB__DROP 0x00000002UL ++#define DB_BLOB__GETATTR 0x00000004UL ++#define DB_BLOB__SETATTR 0x00000008UL ++#define DB_BLOB__RELABELFROM 0x00000010UL ++#define DB_BLOB__RELABELTO 0x00000020UL ++#define DB_BLOB__READ 0x00000040UL ++#define DB_BLOB__WRITE 0x00000080UL ++#define DB_BLOB__IMPORT 0x00000100UL ++#define DB_BLOB__EXPORT 0x00000200UL ++#endif ++ ++/* ++ * SE-PostgreSQL core functions ++ * src/backend/security/sepgsqlCore.c ++ */ ++extern bool sepgsqlIsEnabled(void); ++extern Size sepgsqlShmemSize(void); ++extern void sepgsqlInitialize(void); ++extern int sepgsqlInitializePostmaster(void); ++extern void sepgsqlFinalizePostmaster(void); ++ ++extern Oid sepgsqlGetServerContext(void); ++extern Oid sepgsqlGetClientContext(void); ++extern void sepgsqlSetClientContext(Oid new_ctx); ++extern Oid sepgsqlGetDatabaseContext(void); ++extern char *sepgsqlGetDatabaseName(void); ++ ++extern bool sepgsql_avc_permission_noaudit(Oid ssid, Oid tsid, uint16 tclass, ++ uint32 perms, char **audit, char *objname); ++extern void sepgsql_avc_permission(Oid ssid, Oid tsid, uint16 tclass, ++ uint32 perms, char *objname); ++extern char *sepgsqlGetTupleName(Oid relid, HeapTuple tuple); ++extern void sepgsql_audit(bool result, char *message); ++extern Oid sepgsql_avc_createcon(Oid ssid, Oid tsid, uint16 tclass); ++extern Oid sepgsql_avc_relabelcon(Oid ssid, Oid tsid, uint16 tclass); ++extern bool sepgsql_check_context(char *context); ++ ++extern Datum sepgsql_getcon(PG_FUNCTION_ARGS); ++ ++/* ++ * SE-PostgreSQL proxy functions ++ * src/backend/security/sepgsqlProxy.c ++ */ ++extern List *sepgsqlProxyQuery(Query *query); ++extern void sepgsqlVerifyQuery(Query *query); ++extern Oid sepgsqlPreparePlanCheck(Relation rel); ++extern void sepgsqlRestorePlanCheck(Relation rel, Oid pgace_saved); ++ ++/* ++ * SE-PostgreSQL hooks ++ * src/backend/security/sepgsqlHooks.c ++ */ ++ ++/* simple_heap_xxxx hooks */ ++extern void sepgsqlSimpleHeapInsert(Relation rel, HeapTuple tuple); ++extern void sepgsqlSimpleHeapUpdate(Relation rel, ItemPointer tid, HeapTuple newtup); ++extern void sepgsqlSimpleHeapDelete(Relation rel, ItemPointer tid); ++ ++/* heap_xxxx hooks for implicit labeling */ ++extern void sepgsqlHeapInsert(Relation rel, HeapTuple tuple); ++extern void sepgsqlHeapUpdate(Relation rel, HeapTuple newtup, HeapTuple oldtup); ++ ++/* INSERT/UPDATE/DELETE statement hooks */ ++extern bool sepgsqlExecInsert(Relation rel, HeapTuple tuple, bool with_returning); ++extern bool sepgsqlExecUpdate(Relation rel, HeapTuple newtup, ItemPointer tid, bool with_returning); ++extern bool sepgsqlExecDelete(Relation rel, ItemPointer tid, bool with_returning); ++ ++/* DATABASE */ ++extern void sepgsqlAlterDatabaseContext(Relation rel, HeapTuple tuple, char *new_context); ++extern void sepgsqlSetDatabaseParam(const char *name, char *argstring); ++extern void sepgsqlGetDatabaseParam(const char *name); ++ ++/* RELATION/ATTRIBUTE */ ++extern void sepgsqlLockTable(Oid relid); ++ ++/* FUNCTION */ ++extern void sepgsqlCallFunction(FmgrInfo *finfo, bool with_perm_check); ++extern bool sepgsqlCallFunctionTrigger(FmgrInfo *finfo, TriggerData *tgdata); ++extern void sepgsqlAlterProcedureContext(Relation rel, HeapTuple tuple, char *context); ++ ++/* COPY */ ++extern void sepgsqlCopyTable(Relation rel, List *attnumlist, bool is_from); ++extern bool sepgsqlCopyToTuple(Relation rel, HeapTuple tuple); ++extern bool sepgsqlCopyFromTuple(Relation rel, HeapTuple tuple); ++ ++/* LOAD shared library module */ ++extern void sepgsqlLoadSharedModule(const char *filename); ++ ++/* copy/print node object */ ++extern Node *sepgsqlCopyObject(Node *node); ++extern bool sepgsqlOutObject(StringInfo str, Node *node); ++ ++/* SECURITY LABEL IN/OUT */ ++extern char *sepgsqlSecurityLabelIn(char *context); ++extern char *sepgsqlSecurityLabelOut(char *context); ++extern bool sepgsqlSecurityLabelIsValid(char *context); ++extern char *sepgsqlSecurityLabelOfLabel(char *context); ++extern char *sepgsqlSecurityLabelNotFound(Oid sid); ++ ++/* ++ * SE-PostgreSQL Binary Large Object (BLOB) functions ++ * src/backend/security/sepgsqlLargeObject.c ++ */ ++extern Oid sepgsqlLargeObjectGetSecurity(HeapTuple tuple); ++extern void sepgsqlLargeObjectSetSecurity(HeapTuple tuple, Oid lo_security, bool is_first); ++extern void sepgsqlLargeObjectCreate(Relation rel, HeapTuple tuple); ++extern void sepgsqlLargeObjectDrop(Relation rel, HeapTuple tuple); ++extern void sepgsqlLargeObjectOpen(Relation rel, HeapTuple tuple, bool read_only); ++extern void sepgsqlLargeObjectRead(Relation rel, HeapTuple tuple); ++extern void sepgsqlLargeObjectWrite(Relation rel, HeapTuple newtup, HeapTuple oldtup); ++extern void sepgsqlLargeObjectImport(void); ++extern void sepgsqlLargeObjectExport(void); ++ ++/* ++ * SE-PostgreSQL Heap related functions ++ * src/backend/security/sepgsqlHeap.c ++ */ ++ ++extern Oid sepgsqlComputeImplicitContext(Relation rel, HeapTuple tuple); ++extern bool sepgsqlCheckTuplePerms(Relation rel, HeapTuple tuple, HeapTuple oldtup, ++ uint32 perms, bool abort); ++extern Datum sepgsql_tuple_perms(PG_FUNCTION_ARGS); ++extern Datum sepgsql_tuple_perms_abort(PG_FUNCTION_ARGS); ++ ++/* ++ * SE-PostgreSQL extended SQL statement ++ * src/backend/security/sepgsqlExtStmt.c ++ */ ++extern DefElem *sepgsqlGramSecurityLabel(char *defname, char *context); ++extern bool sepgsqlNodeIsSecurityLabel(DefElem *defel); ++extern Oid sepgsqlParseSecurityLabel(DefElem *defel); ++ ++#endif /* SEPGSQL_INTERNAL_H */ +diff -rpNU3 base/src/include/utils/syscache.h sepgsql/src/include/utils/syscache.h +--- base/src/include/utils/syscache.h 2007-10-25 08:32:26.000000000 +0900 ++++ sepgsql/src/include/utils/syscache.h 2007-10-25 13:12:52.000000000 +0900 +@@ -63,6 +63,8 @@ + #define STATRELATT 32 + #define TYPENAMENSP 33 + #define TYPEOID 34 ++#define SECURITYOID 35 ++#define SECURITYLABEL 36 + + extern void InitCatalogCache(void); + extern void InitCatalogCachePhase2(void); diff --git a/sepostgresql-fedora-prefix.patch b/sepostgresql-fedora-prefix.patch new file mode 100644 index 0000000..a4ab652 --- /dev/null +++ b/sepostgresql-fedora-prefix.patch @@ -0,0 +1,87 @@ +Index: trunk/src/Makefile.global.in +=================================================================== +--- trunk/src/Makefile.global.in (revision 430) ++++ trunk/src/Makefile.global.in (working copy) +@@ -73,14 +73,14 @@ + datadir := @datadir@ + ifeq "$(findstring pgsql, $(datadir))" "" + ifeq "$(findstring postgres, $(datadir))" "" +-override datadir := $(datadir)/postgresql ++override datadir := $(datadir)/sepgsql + endif + endif + + sysconfdir := @sysconfdir@ + ifeq "$(findstring pgsql, $(sysconfdir))" "" + ifeq "$(findstring postgres, $(sysconfdir))" "" +-override sysconfdir := $(sysconfdir)/postgresql ++override sysconfdir := $(sysconfdir)/sepgsql + endif + endif + +@@ -89,7 +89,7 @@ + pkglibdir = $(libdir) + ifeq "$(findstring pgsql, $(pkglibdir))" "" + ifeq "$(findstring postgres, $(pkglibdir))" "" +-override pkglibdir := $(pkglibdir)/postgresql ++override pkglibdir := $(pkglibdir)/sepgsql + endif + endif + +@@ -98,7 +98,7 @@ + pkgincludedir = $(includedir) + ifeq "$(findstring pgsql, $(pkgincludedir))" "" + ifeq "$(findstring postgres, $(pkgincludedir))" "" +-override pkgincludedir := $(pkgincludedir)/postgresql ++override pkgincludedir := $(pkgincludedir)/sepgsql + endif + endif + +@@ -109,7 +109,7 @@ + ifneq (,$(docdir)) + ifeq "$(findstring pgsql, $(docdir))" "" + ifeq "$(findstring postgres, $(docdir))" "" +-override docdir := $(docdir)/postgresql ++override docdir := $(docdir)/sepgsql + endif + endif + endif +Index: trunk/src/bin/pg_ctl/pg_ctl.c +=================================================================== +--- trunk/src/bin/pg_ctl/pg_ctl.c (revision 429) ++++ trunk/src/bin/pg_ctl/pg_ctl.c (working copy) +@@ -557,7 +557,7 @@ + + postmaster_path = pg_malloc(MAXPGPATH); + +- if ((ret = find_other_exec(argv0, "postgres", PM_VERSIONSTR, ++ if ((ret = find_other_exec(argv0, "sepostgres", PM_VERSIONSTR, + postmaster_path)) < 0) + { + char full_path[MAXPGPATH]; +Index: trunk/src/bin/initdb/initdb.c +=================================================================== +--- trunk/src/bin/initdb/initdb.c (revision 429) ++++ trunk/src/bin/initdb/initdb.c (working copy) +@@ -2646,7 +2646,7 @@ + sprintf(pgdenv, "PGDATA=%s", pg_data); + putenv(pgdenv); + +- if ((ret = find_other_exec(argv[0], "postgres", PG_VERSIONSTR, ++ if ((ret = find_other_exec(argv[0], "sepostgres", PG_VERSIONSTR, + backend_exec)) < 0) + { + char full_path[MAXPGPATH]; +Index: trunk/src/bin/pg_dump/pg_dumpall.c +=================================================================== +--- trunk/src/bin/pg_dump/pg_dumpall.c (revision 429) ++++ trunk/src/bin/pg_dump/pg_dumpall.c (working copy) +@@ -139,7 +139,7 @@ + } + } + +- if ((ret = find_other_exec(argv[0], "pg_dump", PGDUMP_VERSIONSTR, ++ if ((ret = find_other_exec(argv[0], "sepg_dump", PGDUMP_VERSIONSTR, + pg_dump_bin)) < 0) + { + char full_path[MAXPGPATH]; diff --git a/sepostgresql.8 b/sepostgresql.8 new file mode 100644 index 0000000..cff2956 --- /dev/null +++ b/sepostgresql.8 @@ -0,0 +1,100 @@ +.TH "sepostgresql" "8" "Jul 15 2007" "kaigai@kaigai.gr.jp" "Security-Enhanced PostgreSQL" + +.SH "NAME" +sepostgresql \- Security-Enhances PostgreSQL + +.SH "DESCRIPTION" +Security-Enhanced PostgreSQL (SE-PostgreSQL) is an enhancement of PostgreSQL, to apply fine grained mandatory access control for database objects based on the security policy of SELinux. +These features enable to apply flexible integrated access control policy between operating system and database management system, during all stages of the life of the information. +.PP +This document describes the way to customize SE-PostgreSQL on the default security policy. + +.SH "BOOLEANS" +The SELinux policy is customizable via BOOLEAN variable. This variable has two states, 1 (on) or 0 (off). A part of the policy is enabled or disabled depending on related boolean variables. + +\fBsepgsql_enable_unconfined\fP toggles whether \fIunconfined_t\fP and \fIsysadm_t\fP domains are allowed to access database objects without any restruction on type enforcement, or not. +When \fIsepgsql_enable_unconfined\fP is off, those domains are also restricted its operation as other domains begin applied. In the default, it is set to on. +You can set it as follows: + +.EX +setsebool -P sepgsql_enable_unconfined ( \fBon\fP | off ) +.EE + +\fBsepgsql_enable_users_ddl\fP toggles whether non-administrative domain is allowed to use DDL statement like CREATE TABLE and so on. +In the default, it is set to on. You can set it as follows: + +.EX +setsebool -P sepgsql_enable_users_ddl ( \fBon\fP | off ) +.EE + +\fBsepgsql_enable_auditallow\fP toggles output of audit messages in the case when required permission checks are allowed. In the default, it is set to off. You can set it as follows: + +.EX +setsebool -P sepgsql_enable_auditallow ( on | \fBoff\fP ) +.EE + +\fBsepgsql_enable_auditdeny\fP toggles output of audit messages in the case when required permission checks are denied. In the default, it is set to on. You can set it as follows: + +.EX +setsebool -P sepgsql_enable_auditdeny ( \fBon\fP | off ) +.EE + +\fBsepgsql_enable_audittuple\fP toggles output of audit messages for any tuple. Because audit messages for tuples in a large size table can cause flood of messages, we can set \fIsepgsql_enable_audittuple\fP independently from any other object classes. +Audit messages for tuples are generated in the only case when \fIsepgsql_enable_audittuple\fP and either \fIsepgsql_enable_auditallow\fP or \fIsepgsql_enable_auditdeny\fP are enabled. +In the default, it is set to off. You can set it as follows: + +.EX +setsebool -P sepgsql_enable_audittuple ( on | \fBoff\fP ) +.EE + +.SH "TYPES" + +\fBsepgsql_db_t\fP is a only type for database itself. +It is attched for newly created databases in the default. + +\fBsepgsql_table_t\fP is a type for tables, columns and tuples. +It is attached for newly created the objects in the default. +Non-administrative clients can do any kinds of operations except for relabeling. + +\fBsepgsql_secret_table_t\fP is a type for tables, columns and tuples. +Non-administrative clients cannot access the objects with this type. + +\fBsepgsql_ro_table_t\fP is a type for read-only tables, columns and tuples. +Non-administrative clients cannot modify the objects with this type. + +\fBsepgsql_fixed_table_t\fP is a type for non-manupulatable tables, columns and tuples. +Non-administrative clients cannot update or delete the objects with this type. + +\fBsepgsql_proc_t\fP is a type for procedures. +It is attached for newly created procedures by adminictrative domain. +Any client can call these procedures with this type. + +\fBsepgsql_userproc_t\fP is a type for procedures. +It is attached for newly created procedures by non-administrative domain. +Administrative domains cannot call the procedure for safety. He have to relabel it into \fIsepgsql_proc_t\fP at first. It is a policy to avoid to execute doubtful code under administrative domain. + +\fBsepgsql_trusted_proc_t\fP is a type for trusted procedures. +Calling procedures with this type invokes domain transition. +Then the function works as an administrative domain, so database administrator can provide limited path to access protected object. + +\fBsepgsql_blob_t\fP is a type for binary large objects (blob). +It is attached for newly created blob in the default. +Non-administrative clients can read and write the blobs with this type. + +\fBsepgsql_ro_blob_t\fP is a type for read-only binary large objects (blob). +Non-administrative clients cannot write the blobs with this type. + +.SH "BACKUP and RESTORE" +\fI--enable-selinux\fP option in \fBsepg_dump\fP and \fBsepg_dumpall\fP enable to dump database image with security context. We can restore the dumped image using the standard \fIpg_restore\fP and so on. + +.EX +Example) +$ sepg_dump -Ft -b --enable-selinux postgres | gzip -c > postgres.tgz +.EE + +.SH AUTHOR +This manual page was written by KaiGai Kohei + +.SH "SEE ALSO" + +selinux(8), boolean(8) diff --git a/sepostgresql.fc b/sepostgresql.fc new file mode 100644 index 0000000..849a5de --- /dev/null +++ b/sepostgresql.fc @@ -0,0 +1,10 @@ +# +# SE-PostgreSQL install path +# +/usr/bin/sepostgres -- gen_context(system_u:object_r:postgresql_exec_t,s0) +/usr/bin/initdb.sepgsql -- gen_context(system_u:object_r:postgresql_exec_t,s0) +/usr/bin/sepg_ctl -- gen_context(system_u:object_r:initrc_exec_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 --git a/sepostgresql.if b/sepostgresql.if new file mode 100644 index 0000000..5bdd409 --- /dev/null +++ b/sepostgresql.if @@ -0,0 +1,69 @@ +######################################## +## +## marks as a server process of SE-PostgreSQL. +## +## +## +## Type marked as a database object type. +## +## +# +interface(`sepgsql_server_domain',` + gen_require(` + attribute sepgsql_server_type; + ') + typeattribute $1 sepgsql_server_type; +') + +######################################## +## +## Marks as a SE-PostgreSQL loadable shared library module +## +## +## +## Type marked as a database object type. +## +## +# +interface(`sepgsql_module_object',` + gen_require(` + attribute sepgsql_module_type; + ') + typeattribute $1 sepgsql_module_type; +') + +######################################## +## +## marks as a administrative client process of SE-PostgreSQL. +## +## +## +## A domain marked as a administrative client domain +## +## +# +interface(`sepgsql_unconfined_domain',` + gen_require(` + attribute sepgsql_admin_domain; + attribute sepgsql_users_domain; + ') + typeattribute $1 sepgsql_admin_domain; + typeattribute $1 sepgsql_users_domain; +') + +######################################## +## +## marks as a generic client process of SE-PostgreSQL. +## +## +## +## A domain marked as a generic client domain +## +## +# +interface(`sepgsql_client_domain',` + gen_require(` + attribute sepgsql_users_domain; + ') + typeattribute $1 sepgsql_users_domain; +') diff --git a/sepostgresql.init b/sepostgresql.init new file mode 100644 index 0000000..b8aa79b --- /dev/null +++ b/sepostgresql.init @@ -0,0 +1,213 @@ +#!/bin/sh +# sepostgresql This is the init script for starting up SE-PostgreSQL +# +# chkconfig: - 62 38 +# description: Starts and stops the SE-PostgreSQL backend daemon +# processname: postmaster +# pidfile: /var/run/postmaster.pid +#--------------------------------------------------------------------- + +PGVERSION="8.2.11" +PGMAJORVERSION=`echo "$PGVERSION" | sed 's/^\([0-9]*\.[0-9]*\).*$/\1/'` +SEPGVERSION="1.1182" + +# source function library +. /etc/rc.d/init.d/functions + +# get config +. /etc/sysconfig/network + +# find the name of the script +NAME=`basename $0` +if [ ${NAME:0:1} = "S" -o ${NAME:0:1} = "K" ]; then + NAME=${NAME:3} +fi + +# set defaults for configurable variables +SEPGSQL_BIN="/usr/bin" +SEPGSQL_DATA="/var/lib/sepgsql/data" +SEPGSQL_OPTS="-i -p 5432" +SEPGSQL_STARTUP_LOG="/var/lib/sepgsql/pgstartup.log" +SEPGSQL_LOG="/var/log/sepostgresql.log" +SEPGSQL_FALLBACK_CONTEXT="user_u:user_r:user_t" + +# override defaults from /etc/sysconfig/sepostgresql +test -f /etc/sysconfig/${NAME} && . /etc/sysconfig/${NAME} + +export SEPGSQL_FALLBACK_CONTEXT + +# Check that networking is up. +test "${NETWORKING}" = "no" && exit 0 +test -f "${SEPGSQL_BIN}/postmaster" || exit 1 + +script_result=0 + +do_start() { + PSQL_START=$"Starting ${NAME} service: " + echo -n "$PSQL_START" + + # make sure startup-time log file is valid + if [ ! -e "${SEPGSQL_STARTUP_LOG}" -a ! -h "${SEPGSQL_STARTUP_LOG}" ]; then + touch "${SEPGSQL_STARTUP_LOG}" || exit 1 + chown sepgsql:sepgsql "${SEPGSQL_STARTUP_LOG}" + chmod 600 "${SEPGSQL_STARTUP_LOG}" + /sbin/restorecon "${SEPGSQL_STARTUP_LOG}" + fi + + # check for the SEPGSQL_DATA structure + if [ -f "${SEPGSQL_DATA}/PG_VERSION" ] && [ -d "${SEPGSQL_DATA}/base" ]; then + if [ x`cat "${SEPGSQL_DATA}/PG_VERSION"` != x"${PGMAJORVERSION}" ]; then + echo_failure + echo + echo "HINT: An old version of the database format was found." + echo "HINT: You need to upgrade the data format before using SE-PostgreSQL." + exit 1 + fi + else + echo_failure + echo + echo "HINT: ${SEPGSQL_DATA} is missing." + echo "HINT: Use '/etc/init.d/${NAME} initdb'" + echo "HINT: to initialize the database cluster first." + exit 1 + fi + + # make sure SEPGSQL_LOG + touch ${SEPGSQL_LOG} + chown sepgsql:sepgsql ${SEPGSQL_LOG} + chmod 600 ${SEPGSQL_LOG} + test -x /sbin/restorecon && /sbin/restorecon ${SEPGSQL_LOG} + + cd ${SEPGSQL_BIN} + /sbin/runuser sepgsql -c "./sepg_ctl -l ${SEPGSQL_LOG} -D ${SEPGSQL_DATA} -o '${SEPGSQL_OPTS}' start" \ + >> ${SEPGSQL_STARTUP_LOG} 2>&1 < /dev/null + sleep 1 + PID=`/sbin/runuser sepgsql -c "./sepg_ctl -D ${SEPGSQL_DATA} status 2>/dev/null \ + | sed 's/^.*PID: //g' | sed 's/[^0-9].*$//g'"` + if [ ${PIPESTATUS[0]} -eq 0 ]; then + echo "$PID" > "/var/run/${NAME}.pid" + touch "/var/lock/subsys/${NAME}.lock" + echo_success + else + script_result=1 + echo_failure + fi + echo +} + +do_stop() { + echo -n $"Stopping ${NAME} service: " + cd ${SEPGSQL_BIN} + /sbin/runuser sepgsql -c "./sepg_ctl -D ${SEPGSQL_DATA} stop" \ + >> ${SEPGSQL_STARTUP_LOG} 2>&1 < /dev/null + ret=$? + if [ $ret -eq 0 ]; then + echo_success + else + echo_failure + script_result=1 + fi + echo + rm -f "/var/run/${NAME}.pid" + rm -f "/var/lock/subsys/${NAME}.lock" +} + +do_status() { + cd ${SEPGSQL_BIN} + /sbin/runuser sepgsql -- -c "./sepg_ctl -D ${SEPGSQL_DATA} status" 2>/dev/null \ + | head -1 | sed "s/^sepg_ctl:/${NAME}:/g" + + if [ ${PIPESTATUS[0]} -ne 0 ]; then + script_result=3 + test -e "/var/run/${NAME}.pid" && script_result=1 + test -e "/var/lock/subsys/${NAME}.lock" && script_result=2 + fi +} + +do_condrestart() { + cd ${SEPGSQL_BIN} + /sbin/runuser sepgsql -- -c "./sepg_ctl -D ${SEPGSQL_DATA} status" &>/dev/null && do_stop && do_start +} + +do_condstop() { + cd ${SEPGSQL_BIN} + /sbin/runuser sepgsql -- -c "./sepg_ctl -D ${SEPGSQL_DATA} status" &>/dev/null && do_stop +} + +do_reload() { + echo -n $"Reloading ${NAME} service: " + cd ${SEPGSQL_BIN} + /sbin/runuser sepgsql -- -c "./sepg_ctl -D ${SEPGSQL_DATA} reload" &>/dev/null < /dev/null + if [ $? -eq 0 ]; then + echo_success + else + echo_failure + script_result=1 + fi + echo +} + +do_initdb() { + echo -n $"Initializing database: " + + if [ -f "${SEPGSQL_DATA}/PG_VERSION" ]; then + echo_failure + echo + echo "HINT: Data directory is not empty" + script_result=1 + else + if [ ! -e "${SEPGSQL_DATA}" -a ! -h "${SEPGSQL_DATA}" ]; then + mkdir -p "${SEPGSQL_DATA}" || exit 1 + chown sepgsql:sepgsql "${SEPGSQL_DATA}" + chmod 600 "${SEPGSQL_DATA}" + fi + # cleanup SELinux labeling for "${SEPGSQL_DATA}" + test -x /sbin/restorecon && /sbin/restorecon -R "${SEPGSQL_DATA}" + # Initialize the database + cd ${SEPGSQL_BIN} + /sbin/runuser -- sepgsql -c "./initdb.sepgsql -A 'ident sameuser' ${SEPGSQL_DATA}" \ + >> "${SEPGSQL_STARTUP_LOG}" 2>&1 < /dev/null + if [ -f "${SEPGSQL_DATA}/PG_VERSION" ]; then + echo_success + else + echo_failure + script_result=1 + fi + echo + fi +} + +# see how we were called. +case "$1" in + start) + do_start + ;; + stop) + do_stop + ;; + status) + do_status + ;; + restart) + do_stop + do_start + ;; + condrestart) + do_condrestart + ;; + condstop) + do_condstop + ;; + reload|force-reload) + do_reload + ;; + initdb) + do_initdb + ;; + *) + echo $"Usage: $0 {start|stop|status|restart|condrestart|condstop|reload|force-reload|initdb}" + exit 1 + ;; +esac + +exit $script_result diff --git a/sepostgresql.spec b/sepostgresql.spec new file mode 100644 index 0000000..ef8f1e2 --- /dev/null +++ b/sepostgresql.spec @@ -0,0 +1,386 @@ +# +# Security Enhanced PostgreSQL (SE-PostgreSQL) +# +# Copyright 2007 KaiGai Kohei +# ----------------------------------------------------- + +# SELinux policy types +%define selinux_variants mls strict targeted + +# SE-PostgreSQL status extension + + +Summary: Security Enhanced PostgreSQL +Name: sepostgresql +Version: 8.2.11 +Release: 1.1182%{?sepgsql_extension}%{?dist} +License: BSD +Group: Applications/Databases +Url: http://code.google.com/p/sepgsql/ +Buildroot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) +Source0: ftp://ftp.postgresql.org/pub/source/v%{version}/postgresql-%{version}.tar.gz +Source1: sepostgresql.init +Source2: sepostgresql.if +Source3: sepostgresql.te +Source4: sepostgresql.fc +Source5: sepostgresql.8 +Patch0: sepostgresql-8.2.11-1.patch +Patch1: sepostgresql-fedora-prefix.patch +BuildRequires: perl glibc-devel bison flex readline-devel zlib-devel >= 1.0.4 +Buildrequires: checkpolicy libselinux-devel >= 2.0.13 selinux-policy-devel >= 3.0.6 +Requires(pre): shadow-utils +Requires(post): policycoreutils /sbin/chkconfig +Requires(preun): /sbin/chkconfig /sbin/service +Requires(postun): policycoreutils +Requires: postgresql-server = %{version} +Requires: policycoreutils >= 2.0.16 libselinux >= 2.0.13 selinux-policy >= 3.0.6 + +%description +Security Enhanced PostgreSQL is an extension of PostgreSQL +based on SELinux security policy, that applies fine grained +mandatory access control to many objects within the database, +and takes advantage of user authorization integrated within +the operating system. SE-PostgreSQL works as a userspace +reference monitor to check any SQL query. + +%prep +%setup -q -n postgresql-%{version} +%patch0 -p1 +%patch1 -p1 +mkdir selinux-policy +cp -p %{SOURCE2} %{SOURCE3} %{SOURCE4} selinux-policy + +%build +CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS +CXXFLAGS="${CXXFLAGS:-%optflags}" ; export CXXFLAGS + +# build Binary Policy Module +pushd selinux-policy +for selinuxvariant in %{selinux_variants} +do + make NAME=${selinuxvariant} -f %{_datadir}/selinux/devel/Makefile + mv %{name}.pp %{name}.pp.${selinuxvariant} + make NAME=${selinuxvariant} -f %{_datadir}/selinux/devel/Makefile clean +done +popd + +# build SE-PostgreSQL +%configure --disable-rpath \ + --enable-selinux \ +%if %{defined sepgextension} + --enable-debug \ + --enable-cassert \ +%endif + --libdir=%{_libdir}/sepgsql \ + --datadir=%{_datadir}/sepgsql + +# parallel build, if possible +make %{?_smp_mflags} + +%install +rm -rf %{buildroot} + +pushd selinux-policy +for selinuxvariant in %{selinux_variants} +do + install -d %{buildroot}%{_datadir}/selinux/${selinuxvariant} + install -p -m 644 %{name}.pp.${selinuxvariant} \ + %{buildroot}%{_datadir}/selinux/${selinuxvariant}/%{name}.pp +done +popd + +make DESTDIR=%{buildroot} install + +# avoid to conflict with native postgresql package +mv %{buildroot}%{_bindir} %{buildroot}%{_bindir}.orig +install -d %{buildroot}%{_bindir} +mv %{buildroot}%{_bindir}.orig/initdb %{buildroot}%{_bindir}/initdb.sepgsql +mv %{buildroot}%{_bindir}.orig/pg_ctl %{buildroot}%{_bindir}/sepg_ctl +mv %{buildroot}%{_bindir}.orig/postgres %{buildroot}%{_bindir}/sepostgres +mv %{buildroot}%{_bindir}.orig/pg_dump %{buildroot}%{_bindir}/sepg_dump +mv %{buildroot}%{_bindir}.orig/pg_dumpall %{buildroot}%{_bindir}/sepg_dumpall + +# /usr/lib/sepgsql +mv %{buildroot}%{_libdir}/sepgsql %{buildroot}%{_libdir}/sepgsql.orig +install -d %{buildroot}%{_libdir}/sepgsql +mv %{buildroot}%{_libdir}/sepgsql.orig/*_and_*.so %{buildroot}%{_libdir}/sepgsql +mv %{buildroot}%{_libdir}/sepgsql.orig/plpgsql.so %{buildroot}%{_libdir}/sepgsql + +# remove unnecessary files +rm -rf %{buildroot}%{_bindir}.orig +rm -rf %{buildroot}%{_libdir}/sepgsql.orig +rm -rf %{buildroot}%{_includedir} +rm -rf %{buildroot}%{_usr}/doc +rm -rf %{buildroot}%{_mandir} + +# /var/lib/sepgsql +install -d -m 700 %{buildroot}%{_localstatedir}/lib/sepgsql +install -d -m 700 %{buildroot}%{_localstatedir}/lib/sepgsql/data +install -d -m 700 %{buildroot}%{_localstatedir}/lib/sepgsql/backups + +# /etc/rc.d/init.d/* +mkdir -p %{buildroot}%{_initrddir} +install -p -m 755 %{SOURCE1} %{buildroot}%{_initrddir}/sepostgresql + +# /usr/share/man/* +mkdir -p %{buildroot}%{_mandir}/man8 +install -p -m 644 %{SOURCE5} %{buildroot}%{_mandir}/man8 + +%clean +rm -rf %{buildroot} + +%pre +getent group sepgsql >/dev/null || groupadd -r sepgsql +getent passwd sepgsql >/dev/null || \ + useradd -r -g sepgsql -d %{_localstatedir}/lib/sepgsql -s /bin/bash \ + -c "SE-PostgreSQL server" sepgsql +exit 0 + +%post +/sbin/chkconfig --add %{name} +/sbin/ldconfig + +for selinuxvariant in %{selinux_variants} +do + %{_sbindir}/semodule -s ${selinuxvariant} -l >& /dev/null || continue; + + %{_sbindir}/semodule -s ${selinuxvariant} -l | egrep -q '^%{name}' && \ + %{_sbindir}/semodule -s ${selinuxvariant} -r %{name} >& /dev/null || : + %{_sbindir}/semodule -s ${selinuxvariant} -i %{_datadir}/selinux/${selinuxvariant}/%{name}.pp >& /dev/null || : +done + +# Fix up non-standard file contexts +/sbin/fixfiles -R %{name} restore || : +/sbin/restorecon -R %{_localstatedir}/lib/sepgsql || : + +%preun +if [ $1 -eq 0 ]; then # rpm -e case + /sbin/service %{name} condstop >/dev/null 2>&1 + /sbin/chkconfig --del %{name} +fi + +%postun +/sbin/ldconfig +if [ $1 -ge 1 ]; then # rpm -U case + /sbin/service %{name} condrestart >/dev/null 2>&1 || : +fi +if [ $1 -eq 0 ]; then # rpm -e case + for selinuxvariant in %{selinux_variants} + do + %{_sbindir}/semodule -s ${selinuxvariant} -l >& /dev/null || continue; + + %{_sbindir}/semodule -s ${selinuxvariant} -l | egrep -q '^%{name}' && \ + %{_sbindir}/semodule -s ${selinuxvariant} -r %{name} >& /dev/null || : + done + /sbin/fixfiles -R %{name} restore || : + test -d %{_localstatedir}/lib/sepgsql && /sbin/restorecon -R %{_localstatedir}/lib/sepgsql || : +fi + +%files +%defattr(-,root,root,-) +%doc COPYRIGHT README HISTORY +%{_initrddir}/sepostgresql +%{_bindir}/initdb.sepgsql +%{_bindir}/sepg_ctl +%{_bindir}/sepostgres +%{_bindir}/sepg_dump +%{_bindir}/sepg_dumpall +%{_mandir}/man8/sepostgresql.* +%dir %{_libdir}/sepgsql +%{_libdir}/sepgsql/plpgsql.so +%{_libdir}/sepgsql/*_and_*.so +%dir %{_datadir}/sepgsql +%{_datadir}/sepgsql/postgres.bki +%{_datadir}/sepgsql/postgres.description +%{_datadir}/sepgsql/postgres.shdescription +%{_datadir}/sepgsql/system_views.sql +%{_datadir}/sepgsql/*.sample +%{_datadir}/sepgsql/timezone/ +%{_datadir}/sepgsql/timezonesets/ +%{_datadir}/sepgsql/conversion_create.sql +%{_datadir}/sepgsql/information_schema.sql +%{_datadir}/sepgsql/sql_features.txt +%attr(644,root,root) %{_datadir}/selinux/*/sepostgresql.pp +%attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql +%attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/data +%attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/backups + +%changelog +* Wed Nov 5 2008 - 8.2.11-1.1182 +- upgrade base PostgreSQL 8.2.10 -> 8.2.11 + +* Tue Sep 30 2008 - 8.2.10-1.1067 +- upgrade base PostgreSQL 8.2.9 -> 8.2.10 + +* Fri Jun 13 2008 - 8.2.9-1.869 +- upgrade base PostgreSQL 8.2.7 -> 8.2.9 + +* Wed Apr 30 2008 - 8.2.7-1.298 +- BUGFIX: ROW-level control did not work correctly on TRUNCATE + +* Thu Mar 27 2008 - 8.2.7-1.281 +- update base PostgreSQL 8.2.6->8.2.7 + +* Sun Mar 2 2008 - 8.2.6-1.231 +- bugfix: SELECT count(*) was not filtered correctly. + +* Wed Feb 6 2008 - 8.2.6-1.208 +- bugfix: blob:{read} is not evaluated correctly +- policy update: allow db_database:{set_param} +- packs timezone related files + +* Tue Jan 22 2008 - 8.2.6-1.158 +- backporting bugfixes: + lack of lacks when refering buffer pages at update/delete hooks + unexpected behavior in explicit labeling using SELECT ... INTO statement + +* Tue Jan 8 2008 - 8.2.6-1.140 +- add "security_sysattr_name" GUC variable +- update base PostgreSQL to 8.2.6 + +* Wed Nov 21 2007 - 8.2.5-1.66 +- Add a policy module hotfix for labeled networking + +* Thu Nov 1 2007 - 8.2.5-1.51 +- Re-organize repository to prepare to branch 8.3.x based tree. + (no differences from 8.2.5-1.33) + +* Wed Oct 17 2007 - 8.2.5-1.33 +- Fix bug: security context was not canonicalized + when irregular context (but interpretable) was inputed. + +* Mon Oct 15 2007 - 8.2.5-1.31 +- Fix bug: type definitions of security_label_to_text() + and text_to_security_label() are mismatched. + +* Sat Sep 22 2007 - 8.2.5-1.23 +- update base PostgreSQL to 8.2.5 + +* Mon Sep 1 2007 - 8.2.4-1.0 +- mark as SE-PostgreSQL 8.2.4-1.0 + +* Thu Aug 28 2007 - 8.2.4-0.434.beta +- add Requires: postgresql-server, instead of Conflicts: tag + (Some sharable files are removed from sepostgresql package) + +* Fri Aug 24 2007 - 8.2.4-0.429.beta +- add policycoreutils to Requires(post/postun) +- upstreamed selinux-policy got SE-PostgreSQL related object classes definition. + +* Sat Aug 18 2007 - 8.2.4-0.427.beta +- sepg_dumpall uses /usr/bin/sepg_dump + +* Fri Aug 17 2007 - 8.2.4-0.423.beta +- fix policy not to execute sepgsql_user_proc_t from administrative domain + +* Fri Aug 10 2007 - 8.2.4-0.418.beta +- object classes are renamed with "db_" prefix +- /etc/init.d/sepostgresql script is improved. + +* Thu Aug 2 2007 - 8.2.4-0.409.beta +- specfile updated based on the following comments + https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=249522#c5 + +* Mon Jul 30 2007 - 8.2.4-0.407.beta +- fix spec file based on Fedora reviewing process +- add rawhide support + +* Mon Jul 23 2007 - 8.2.4-0.402.beta +- add manpage of sepostgresql +- fix specfile convention for Fedora suitable + +* Sun Jul 15 2007 - 8.2.4-0.398.beta +- SECCLASS_DATABASE is updated (fc7->62, fc6->61) + +* Sun Jul 1 2007 - 8.2.4-0.391.beta +- Mark as a beta version. + +* Sat Jun 30 2007 - 8.2.4-0.384.alpha +- add fallback context support with $SEPGSQL_FALLBACK_CONTEXT +- add sepgsql_enable_users_ddl boolean to restrict sepgsql_sysobj_t +- BUGFIX: incorrect inherited attribute expanding for RECORD type (attno=0) +- BUGFIX: trigger functions were not checked in COPY FROM statement + +* Tue Jun 26 2007 - 8.2.4-0.376.alpha +- add pgaceExecutorStart() to hook ExecutorStart() + +* Mon Jun 25 2007 - 8.2.4-0.372.alpha +- add table name prefix for column name on audit messages +- use security_label_raw_in as an alternative for security_label_in +- add hook for query execution path with SPI_ interface +- add trigger function suppoer +- BUGFIX: remove unnecessary checks for COPY TO/FROM on non-table relation +- BUGFIX: remove unnecessary checks for LOCK on non-table relation +- BUGFIX: incorrect object id for tuples within pg_security +- BUGFIX: CommandCounterIncrement() might be called during heap_create_with_catalog. +- BUGFIX: correct self-deadlock +- update security policy: sepgsql_sysobj_t, sepgsql_user_proc_t, sepgsql_ro_blob_t + +* Tue Jun 19 2007 - 8.2.4-0.351.alpha +- BUGFIX: sepgsql_compute_avc_datum() accessed userspace AVC without + holding any lock. +- improve build scripts. + +* Sat Jun 16 2007 - 8.2.4-0.320.alpha +- update: sepostgresql.pp security policy fot strict/mls suitable +- BUGFIX: column:drop evaluation for ALTER TABLE tbl DROP col; statement +- add --enable-security option for pg_dumpall command +- add {use} permission for table/column/tuple object classes + +* Tue May 29 2007 - 8.2.4-0.306.alpha +- BUGFIX: RangeTblEntry->requiredPerms are polluted. + +* Sun May 27 2007 - 8.2.4-0.304.alpha +- add support for dynamic object class/access vector mapping +- BUGFIX: Lack of implicit labeling on COPY FROM statement for system catalogs +- BUGFIX: Incorrect security context handling for inherited tables + +* Fri May 25 2007 - 8.2.4-0.292.alpha +- add pg_dump/pg_dumpall/pg_restore with --enable-security option +- add support on OUTER JOIN by rewriting query. +- add security_context support on COPY TO/FROM statement +- add unlabeled security context support (enable to obtain /selinux/initial_contexts/*) +- BUGFIX: lack of checks on JOIN ON condition +- BUGFIX: pseudo relation object (sequence, toast, ...) are not handled as database obj. +- BUGFIX: lack of tuple:insert checks at COPY FROM statement +- BUGFIX: server crash when CREATE TABLE command with newly defined CONTEXT = '...'. + +* Wed May 16 2007 - 8.2.4-0.266.alpha +- BUGFIX: incorrect security context of newly generated system object. +- BUGFIX: missing error text when audit log is disabled. +- BUGFIX: incorrect Oid of newly generated tuples within pg_security. +- BUGFIX: sepgsql_enable_audittuple is misconditioned. +- add checks for T_RowExpr/T_RowCompareExpr/T_BooleanTest + T_DistinctExpr/T_ConvertRowtypeExpr +- add support CONTEXT = 'xxx' for CREATE TABLE/FUNCTION/DATABASE statement + +* Sun Apr 30 2007 - 8.2.4-0.240.alpha +- update base version 8.2.3 -> 8.2.4 +- BUGFIX: unexpected expose in OUTER JOIN statement. + add rewrite OUTER JOIN into SUBQUERY to ensure filtering violated tuples. +- BUGFIX: strange operation in text_to_security_label() +- BUGFIX: infinite recursive call on security label -> oid mapping +- BUGFIX: sepgsql_avc_init() is called in policy state monitoring process + to avoid nonsense initialization of avc_shmem. + +* Fri Apr 27 2007 - 8.2.3-0.232.alpha +- object class numbers were redefined. (SECCLASS_DATABASE got into 61) +- is_selinux_enabled() was cached on the shared memory segment. +- BUGFIX: server went into infinit loop on foreign key constraint. + +* Mon Apr 16 2007 - 8.2.3-0.226.alpha +- BUGFIX: cases when several variables with same type in a single table + +* Sat Apr 07 2007 - 8.2.3-0.214.alpha +- add the first implementation of SE-PostgreSQL on PGACE framework + +* Wed Mar 21 2007 - 8.2.3-0.212.alpha +- BUGFIX: SetOperation didn't handle its subquery correctly. + So, it caused server crash. + +* Wed Mar 07 2007 - 8.2.3-0.209.alpha +- BUGFIX: var->varlevelsup was ignored, so outer references + from subqueries cause a fault. + +* Tue Feb 27 2007 - 8.2.3-0.178.alpha +- Initial RPM build diff --git a/sepostgresql.te b/sepostgresql.te new file mode 100644 index 0000000..a309bf1 --- /dev/null +++ b/sepostgresql.te @@ -0,0 +1,315 @@ +policy_module(sepostgresql, 1.1182) + +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; + + type postgresql_t; + type lib_t, shlib_t; + type unlabeled_t; + + attribute domain; + attribute file_type; +') + +######################################## +# +# Declarations +# + +# ---- Booleans ---- +gen_tunable(sepgsql_enable_unconfined, true) +gen_tunable(sepgsql_enable_auditallow, false) +gen_tunable(sepgsql_enable_auditdeny, true) +gen_tunable(sepgsql_enable_audittuple, false) +gen_tunable(sepgsql_enable_users_ddl, true) + +# ---- Database client type ---- +attribute sepgsql_admin_domain; +attribute sepgsql_users_domain; + +# ---- Database object type ---- +attribute sepgsql_server_type; +attribute sepgsql_database_type; +attribute sepgsql_table_type; +attribute sepgsql_procedure_type; +attribute sepgsql_blob_type; +attribute sepgsql_module_type; + +#--------------------------------------- +# SE-PostgreSQL userspace-managed objects + +# for database class +type sepgsql_db_t, sepgsql_database_type; + +# for table/column/tuple class +type sepgsql_table_t, sepgsql_table_type; +type sepgsql_sysobj_t, sepgsql_table_type; +type sepgsql_secret_table_t, sepgsql_table_type; +type sepgsql_ro_table_t, sepgsql_table_type; +type sepgsql_fixed_table_t, sepgsql_table_type; + +# for procedure class +type sepgsql_proc_t, sepgsql_procedure_type; +type sepgsql_user_proc_t, sepgsql_procedure_type; +type sepgsql_trusted_proc_t, sepgsql_procedure_type; + +# for blob class +type sepgsql_blob_t, sepgsql_blob_type; +type sepgsql_ro_blob_t, sepgsql_blob_type; +type sepgsql_secret_blob_t, sepgsql_blob_type; + +#--------------------------------------- +# SE-PostgreSQL trusted procedure domain +type sepgsql_trusted_domain_t; +domain_type(sepgsql_trusted_domain_t) +sepgsql_unconfined_domain(sepgsql_trusted_domain_t) + +#--------------------------------------- +# SE-PostgreSQL loadable shared library modules +sepgsql_module_object(lib_t) +sepgsql_module_object(shlib_t) + +#--------------------------------------- +# unlabeled object (unlabeled_t) +typeattribute unlabeled_t sepgsql_database_type; +typeattribute unlabeled_t sepgsql_table_type; +typeattribute unlabeled_t sepgsql_procedure_type; +typeattribute unlabeled_t sepgsql_blob_type; + +######################################## +# +# SE-PostgreSQL server domain's policy +# +optional_policy(` + gen_require(` + type postgresql_t; + ') + sepgsql_server_domain(postgresql_t) +') + +######################################## +# +# SE-PostgreSQL client domains +# + +# -- unconfined_t -- +optional_policy(` + gen_require(` + type unconfined_t; + ') + sepgsql_unconfined_domain(unconfined_t) +') + +# -- sysadm_t -- +optional_policy(` + gen_require(` + type sysadm_t; + ') + sepgsql_unconfined_domain(sysadm_t) +') + +# -- user_t -- +optional_policy(` + gen_require(` + type user_t; + ') + sepgsql_client_domain(user_t) +') + +# -- staff_t -- +optional_policy(` + gen_require(` + type staff_t; + ') + sepgsql_client_domain(staff_t) +') + +# -- guest_t -- +optional_policy(` + gen_require(` + type guest_t; + ') + sepgsql_client_domain(guest_t) +') + +# -- httpd_t (PHP script) -- +optional_policy(` + gen_require(` + type httpd_t; + ') + sepgsql_client_domain(httpd_t) +') + +# -- httpd_sys_script_t (CGI script) -- +optional_policy(` + gen_require(` + type httpd_sys_script_t; + ') + sepgsql_client_domain(httpd_sys_script_t) +') + +######################################## +# +# SE-PostgreSQL Type Transition +# +type_transition domain domain : db_database sepgsql_db_t; +type_transition { domain - sepgsql_server_type } sepgsql_database_type : db_table sepgsql_table_t; +type_transition sepgsql_server_type sepgsql_database_type : db_table sepgsql_sysobj_t; +type_transition domain sepgsql_database_type : db_blob sepgsql_blob_t; + +######################################## +# +# SE-PostgreSQL policy for server domain +# +allow sepgsql_server_type self : netlink_selinux_socket create_socket_perms; +selinux_get_fs_mount(sepgsql_server_type) +selinux_get_enforce_mode(sepgsql_server_type) +selinux_validate_context(sepgsql_server_type) +selinux_compute_access_vector(sepgsql_server_type) +selinux_compute_create_context(sepgsql_server_type) +selinux_compute_relabel_context(sepgsql_server_type) + +allow sepgsql_server_type sepgsql_database_type : db_database all_db_database_perms; +allow sepgsql_server_type sepgsql_module_type : db_database { install_module }; +allow sepgsql_server_type sepgsql_table_type : db_table all_db_table_perms; +allow sepgsql_server_type sepgsql_table_type : db_column all_db_column_perms; +allow sepgsql_server_type sepgsql_table_type : db_tuple all_db_tuple_perms; +allow sepgsql_server_type sepgsql_procedure_type : db_procedure all_db_procedure_perms; +allow sepgsql_server_type sepgsql_blob_type : db_blob all_db_blob_perms; +# type transition for procedure +type_transition sepgsql_server_type sepgsql_database_type : db_procedure sepgsql_proc_t; + +######################################## +# +# SE-PostgreSQL policy for administrative domain +# +tunable_policy(`sepgsql_enable_unconfined',` + allow sepgsql_admin_domain sepgsql_database_type : db_database all_db_database_perms; + allow sepgsql_admin_domain sepgsql_module_type : db_database { install_module }; + allow sepgsql_admin_domain sepgsql_table_type : db_table all_db_table_perms; + allow sepgsql_admin_domain sepgsql_table_type : db_column all_db_column_perms; + allow sepgsql_admin_domain sepgsql_table_type : db_tuple all_db_tuple_perms; + allow sepgsql_admin_domain { sepgsql_procedure_type - sepgsql_user_proc_t } : db_procedure all_db_procedure_perms; + allow sepgsql_admin_domain sepgsql_user_proc_t : db_procedure { create drop getattr setattr relabelfrom relabelto }; + allow sepgsql_admin_domain sepgsql_blob_type : db_blob all_db_blob_perms; + allow sepgsql_admin_domain sepgsql_server_type : db_blob { import export }; + # type transition for procedure + type_transition sepgsql_admin_domain sepgsql_database_type : db_procedure sepgsql_proc_t; +',` + # type transition for procedure + type_transition sepgsql_admin_domain sepgsql_database_type : db_procedure sepgsql_user_proc_t; +') + +######################################## +# +# SE-PostgreSQL policy for generic domain +# + +allow sepgsql_users_domain sepgsql_db_t : db_database { getattr access get_param set_param }; + +allow sepgsql_users_domain sepgsql_table_t : db_table { getattr use select update insert delete }; +allow sepgsql_users_domain sepgsql_table_t : db_column { getattr use select update insert }; +allow sepgsql_users_domain sepgsql_table_t : db_tuple { use select update insert delete }; + +allow sepgsql_users_domain sepgsql_sysobj_t : db_table { getattr use select }; +allow sepgsql_users_domain sepgsql_sysobj_t : db_column { getattr use select }; +allow sepgsql_users_domain sepgsql_sysobj_t : db_tuple { use select }; +tunable_policy(`sepgsql_enable_users_ddl',` + allow sepgsql_users_domain sepgsql_table_t : db_table { create drop setattr }; + allow sepgsql_users_domain sepgsql_table_t : db_column { create drop setattr }; + allow sepgsql_users_domain sepgsql_sysobj_t : db_tuple { update insert delete }; +') + +allow sepgsql_users_domain sepgsql_secret_table_t : db_table { getattr }; +allow sepgsql_users_domain sepgsql_secret_table_t : db_column { getattr }; + +allow sepgsql_users_domain sepgsql_ro_table_t : db_table { getattr use select }; +allow sepgsql_users_domain sepgsql_ro_table_t : db_column { getattr use select }; +allow sepgsql_users_domain sepgsql_ro_table_t : db_tuple { use select }; + +allow sepgsql_users_domain sepgsql_fixed_table_t : db_table { getattr use select insert }; +allow sepgsql_users_domain sepgsql_fixed_table_t : db_column { getattr use select insert }; +allow sepgsql_users_domain sepgsql_fixed_table_t : db_tuple { use select insert }; + +allow sepgsql_users_domain sepgsql_proc_t : db_procedure { getattr execute }; +allow { sepgsql_users_domain - sepgsql_admin_domain} sepgsql_user_proc_t : db_procedure { create drop getattr setattr execute }; +allow sepgsql_users_domain sepgsql_trusted_proc_t : db_procedure { getattr execute entrypoint }; + +allow sepgsql_users_domain sepgsql_blob_t : db_blob { create drop getattr setattr read write }; +allow sepgsql_users_domain sepgsql_ro_blob_t : db_blob { getattr read }; +allow sepgsql_users_domain sepgsql_secret_blob_t : db_blob { getattr }; + +# type transition for procedure +type_transition { sepgsql_users_domain - sepgsql_admin_domain } sepgsql_database_type : db_procedure sepgsql_user_proc_t; + +# trusted procedure +type_transition sepgsql_users_domain sepgsql_trusted_proc_t : process sepgsql_trusted_domain_t; +allow sepgsql_users_domain sepgsql_trusted_domain_t : process { transition }; + +######################################## +# +# SE-PostgreSQL loadable shared library policy +# + +allow sepgsql_database_type sepgsql_module_type : db_database { load_module }; + +######################################## +# +# SE-PostgreSQL audit switch +# +tunable_policy(`sepgsql_enable_auditallow',` + auditallow domain sepgsql_database_type : db_database all_db_database_perms; + auditallow domain sepgsql_table_type : db_table all_db_table_perms; + auditallow domain sepgsql_table_type : db_column all_db_column_perms; + auditallow domain sepgsql_procedure_type : db_procedure all_db_procedure_perms; + auditallow domain sepgsql_blob_type : db_blob all_db_blob_perms; + auditallow domain sepgsql_server_type : db_blob { import export }; + auditallow domain file_type : db_database { install_module }; +') +tunable_policy(`sepgsql_enable_audittuple && sepgsql_enable_auditallow',` + auditallow domain sepgsql_table_type : db_tuple all_db_tuple_perms; +') +tunable_policy(`! sepgsql_enable_auditdeny',` + dontaudit domain sepgsql_database_type : db_database all_db_database_perms; + dontaudit domain sepgsql_table_type : db_table all_db_table_perms; + dontaudit domain sepgsql_table_type : db_column all_db_column_perms; + dontaudit domain sepgsql_procedure_type : db_procedure all_db_procedure_perms; + dontaudit domain sepgsql_blob_type : db_blob all_db_blob_perms; + dontaudit domain sepgsql_server_type : db_blob { import export }; + dontaudit domain file_type : db_database { install_module }; +') +tunable_policy(`! sepgsql_enable_audittuple || ! sepgsql_enable_auditdeny',` + dontaudit domain sepgsql_table_type : db_tuple all_db_tuple_perms; +') + +######################################## +# +# Hotfixes for labeled networking +# +# NOTE: These changes are to be merged in the later releases. +optional_policy(` + gen_require(` + attribute sepgsql_server_type; + attribute sepgsql_users_domain; + attribute unconfined_domain_type; + type unlabeled_t; + type ipsec_spd_t; + type racoon_t; + ') + # allow unconfined_t to set default SPD context + allow unconfined_domain_type ipsec_spd_t : association { setcontext }; + # allow server/client domain to communicate via default SPD context + allow { sepgsql_server_type sepgsql_users_domain } ipsec_spd_t : association { polmatch }; + allow { sepgsql_server_type sepgsql_users_domain } self : association { sendto }; + allow sepgsql_server_type sepgsql_users_domain : { association tcp_socket } recvfrom; + allow sepgsql_users_domain sepgsql_server_type : { association tcp_socket } recvfrom; + # racoon_t/unlabeled_t related + allow unlabeled_t self : association { sendto }; + allow unlabeled_t ipsec_spd_t : association { polmatch }; + allow racoon_t unlabeled_t : association { setcontext }; +') diff --git a/sources b/sources new file mode 100644 index 0000000..a17a10c --- /dev/null +++ b/sources @@ -0,0 +1 @@ +1d0c0647e336f15e6d920ef09cac2478 postgresql-8.2.11.tar.gz