diff -rpNU3 base/configure sepgsql-new/configure --- base/configure 2009-09-10 20:11:35.000000000 +0900 +++ sepgsql-new/configure 2009-09-10 20:24:37.000000000 +0900 @@ -314,7 +314,7 @@ ac_includes_default="\ # include #endif" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS configure_args build build_cpu build_vendor build_os host host_cpu host_vendor host_os PORTNAME docdir enable_nls WANTED_LANGUAGES default_port enable_shared enable_rpath enable_debug enable_profiling DTRACE DTRACEFLAGS enable_dtrace CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP GCC TAS autodepend INCLUDES enable_thread_safety with_tcl with_perl with_python with_gssapi with_krb5 krb_srvtab with_pam with_ldap with_bonjour with_openssl with_ossp_uuid XML2_CONFIG with_libxml with_libxslt with_system_tzdata with_zlib EGREP ELF_SYS LDFLAGS_SL LD with_gnu_ld ld_R_works RANLIB ac_ct_RANLIB STRIP ac_ct_STRIP STRIP_STATIC_LIB STRIP_SHARED_LIB TAR LN_S AWK YACC YFLAGS FLEX FLEXFLAGS PERL perl_archlibexp perl_privlibexp perl_useshrplib perl_embed_ldflags PYTHON python_version python_configdir python_includespec python_libdir python_libspec python_additional_libs OSSP_UUID_LIBS HAVE_IPV6 LIBOBJS acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS LDAP_LIBS_FE LDAP_LIBS_BE HAVE_POSIX_SIGNALS MSGFMT MSGMERGE XGETTEXT localedir TCLSH TCL_CONFIG_SH TCL_INCLUDE_SPEC TCL_LIB_FILE TCL_LIBS TCL_LIB_SPEC TCL_SHARED_BUILD TCL_SHLIB_LD_LIBS NSGMLS JADE have_docbook DOCBOOKSTYLE COLLATEINDEX SGMLSPL vpath_build LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS configure_args build build_cpu build_vendor build_os host host_cpu host_vendor host_os PORTNAME docdir enable_nls WANTED_LANGUAGES default_port enable_shared enable_rpath enable_debug enable_profiling DTRACE DTRACEFLAGS enable_dtrace CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP GCC TAS autodepend INCLUDES enable_thread_safety with_tcl with_perl with_python with_gssapi with_krb5 krb_srvtab with_pam with_ldap with_bonjour with_openssl with_ossp_uuid XML2_CONFIG with_libxml with_libxslt with_system_tzdata with_zlib enable_selinux EGREP ELF_SYS LDFLAGS_SL LD with_gnu_ld ld_R_works RANLIB ac_ct_RANLIB STRIP ac_ct_STRIP STRIP_STATIC_LIB STRIP_SHARED_LIB TAR LN_S AWK YACC YFLAGS FLEX FLEXFLAGS PERL perl_archlibexp perl_privlibexp perl_useshrplib perl_embed_ldflags PYTHON python_version python_configdir python_includespec python_libdir python_libspec python_additional_libs OSSP_UUID_LIBS HAVE_IPV6 LIBOBJS acx_pthread_config PTHREAD_CC PTHREAD_LIBS PTHREAD_CFLAGS LDAP_LIBS_FE LDAP_LIBS_BE HAVE_POSIX_SIGNALS MSGFMT MSGMERGE XGETTEXT localedir TCLSH TCL_CONFIG_SH TCL_INCLUDE_SPEC TCL_LIB_FILE TCL_LIBS TCL_LIB_SPEC TCL_SHARED_BUILD TCL_SHLIB_LD_LIBS NSGMLS JADE have_docbook DOCBOOKSTYLE COLLATEINDEX SGMLSPL vpath_build LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -871,6 +871,7 @@ Optional Features: --enable-cassert enable assertion checks (for debugging) --enable-thread-safety make client libraries thread-safe --enable-thread-safety-force force thread-safety despite thread test failure + --enable-selinux enable to build with SELinux support --disable-largefile omit support for large files Optional Packages: @@ -4619,6 +4620,115 @@ fi; # +# 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 HAVE_SELINUX 1 +_ACEOF + + +else + { { echo "$as_me:$LINENO: error: \"--enable-selinux requires libselinux.\"" >&5 +echo "$as_me: error: \"--enable-selinux requires libselinux.\"" >&2;} + { (exit 1); exit 1; }; } +fi + +fi + +# # Elf # @@ -26019,6 +26129,7 @@ s,@with_libxml@,$with_libxml,;t t s,@with_libxslt@,$with_libxslt,;t t s,@with_system_tzdata@,$with_system_tzdata,;t t s,@with_zlib@,$with_zlib,;t t +s,@enable_selinux@,$enable_selinux,;t t s,@EGREP@,$EGREP,;t t s,@ELF_SYS@,$ELF_SYS,;t t s,@LDFLAGS_SL@,$LDFLAGS_SL,;t t diff -rpNU3 base/configure.in sepgsql-new/configure.in --- base/configure.in 2009-09-10 20:11:35.000000000 +0900 +++ sepgsql-new/configure.in 2009-09-10 20:24:37.000000000 +0900 @@ -626,6 +626,19 @@ PGAC_ARG_BOOL(with, zlib, yes, AC_SUBST(with_zlib) # +# SELinux support +# +PGAC_ARG_BOOL(enable, selinux, no, + [ --enable-selinux enable to build with SELinux support]) +if test "$enable_selinux" = yes; then + AC_CHECK_LIB(selinux, getpeercon, + AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1, + [SE-PostgreSQL feature is enabled]) + AC_SUBST(enable_selinux), + AC_MSG_ERROR("--enable-selinux requires libselinux.")) +fi + +# # Elf # diff -rpNU3 base/src/Makefile.global.in sepgsql-new/src/Makefile.global.in --- base/src/Makefile.global.in 2007-11-17 20:15:40.000000000 +0900 +++ sepgsql-new/src/Makefile.global.in 2009-03-13 18:14:33.000000000 +0900 @@ -165,6 +165,7 @@ enable_rpath = @enable_rpath@ enable_nls = @enable_nls@ enable_debug = @enable_debug@ enable_dtrace = @enable_dtrace@ +enable_selinux = @enable_selinux@ enable_thread_safety = @enable_thread_safety@ python_includespec = @python_includespec@ diff -rpNU3 base/src/backend/Makefile sepgsql-new/src/backend/Makefile --- base/src/backend/Makefile 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/Makefile 2009-03-13 18:14:33.000000000 +0900 @@ -16,7 +16,7 @@ include $(top_builddir)/src/Makefile.glo DIRS = access bootstrap catalog parser commands executor lib libpq \ main nodes optimizer port postmaster regex rewrite \ - storage tcop tsearch utils $(top_builddir)/src/timezone + security storage tcop tsearch utils $(top_builddir)/src/timezone SUBSYSOBJS = $(DIRS:%=%/SUBSYS.o) @@ -32,6 +32,11 @@ LIBS := $(filter-out -lpgport, $(LIBS)) # The backend doesn't need everything that's in LIBS, however LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS)) +# SELinux needs libselinux +ifeq ($(enable_selinux), yes) +LIBS += -lselinux +endif + ########################################################################## all: submake-libpgport postgres $(POSTGRES_IMP) diff -rpNU3 base/src/backend/access/common/heaptuple.c sepgsql-new/src/backend/access/common/heaptuple.c --- base/src/backend/access/common/heaptuple.c 2009-09-10 20:11:35.000000000 +0900 +++ sepgsql-new/src/backend/access/common/heaptuple.c 2009-09-10 20:24:37.000000000 +0900 @@ -66,6 +66,7 @@ #include "access/heapam.h" #include "access/tuptoaster.h" +#include "catalog/pg_security.h" #include "executor/tuptable.h" @@ -473,6 +474,7 @@ heap_attisnull(HeapTuple tup, int attnum case MinCommandIdAttributeNumber: case MaxTransactionIdAttributeNumber: case MaxCommandIdAttributeNumber: + case SecurityLabelAttributeNumber: /* these are never null */ break; @@ -785,6 +787,9 @@ heap_getsysattr(HeapTuple tup, int attnu case TableOidAttributeNumber: result = ObjectIdGetDatum(tup->t_tableOid); break; + case SecurityLabelAttributeNumber: + result = securityHeapGetSecLabelSysattr(tup); + break; default: elog(ERROR, "invalid attnum: %d", attnum); result = 0; /* keep compiler quiet */ @@ -908,6 +913,8 @@ heap_form_tuple(TupleDesc tupleDescripto if (tupleDescriptor->tdhasoid) len += sizeof(Oid); + if (tupleDescriptor->tdhasseclabel) + len += sizeof(Oid); hoff = len = MAXALIGN(len); /* align user data safely */ @@ -939,6 +946,8 @@ heap_form_tuple(TupleDesc tupleDescripto if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ td->t_infomask = HEAP_HASOID; + if (tupleDescriptor->tdhasseclabel) + td->t_infomask |= HEAP_HAS_SECLABEL; heap_fill_tuple(tupleDescriptor, values, @@ -1019,6 +1028,8 @@ heap_formtuple(TupleDesc tupleDescriptor if (tupleDescriptor->tdhasoid) len += sizeof(Oid); + if (tupleDescriptor->tdhasseclabel) + len += sizeof(Oid); hoff = len = MAXALIGN(len); /* align user data safely */ @@ -1050,6 +1061,8 @@ heap_formtuple(TupleDesc tupleDescriptor if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ td->t_infomask = HEAP_HASOID; + if (tupleDescriptor->tdhasseclabel) + td->t_infomask |= HEAP_HAS_SECLABEL; DataFill(tupleDescriptor, values, @@ -1129,6 +1142,8 @@ heap_modify_tuple(HeapTuple tuple, newTuple->t_tableOid = tuple->t_tableOid; if (tupleDesc->tdhasoid) HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); + if (HeapTupleHasSecLabel(newTuple)) + HeapTupleSetSecLabel(newTuple, HeapTupleGetSecLabel(tuple)); return newTuple; } @@ -1201,6 +1216,8 @@ heap_modifytuple(HeapTuple tuple, newTuple->t_tableOid = tuple->t_tableOid; if (tupleDesc->tdhasoid) HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); + if (HeapTupleHasSecLabel(newTuple)) + HeapTupleSetSecLabel(newTuple, HeapTupleGetSecLabel(tuple)); return newTuple; } @@ -1846,6 +1863,8 @@ heap_form_minimal_tuple(TupleDesc tupleD if (tupleDescriptor->tdhasoid) len += sizeof(Oid); + if (tupleDescriptor->tdhasseclabel) + len += sizeof(Oid); hoff = len = MAXALIGN(len); /* align user data safely */ @@ -1867,6 +1886,8 @@ heap_form_minimal_tuple(TupleDesc tupleD if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ tuple->t_infomask = HEAP_HASOID; + if (tupleDescriptor->tdhasseclabel) + tuple->t_infomask |= HEAP_HAS_SECLABEL; heap_fill_tuple(tupleDescriptor, values, @@ -1965,6 +1986,7 @@ minimal_tuple_from_heap_tuple(HeapTuple HeapTuple heap_addheader(int natts, /* max domain index */ bool withoid, /* reserve space for oid */ + bool withseclabel, /* reserve space for security */ Size structlen, /* its length */ void *structure) /* pointer to the struct */ { @@ -1979,6 +2001,8 @@ heap_addheader(int natts, /* max domain hoff = offsetof(HeapTupleHeaderData, t_bits); if (withoid) hoff += sizeof(Oid); + if (withseclabel) + hoff += sizeof(Oid); hoff = MAXALIGN(hoff); len = hoff + structlen; @@ -1996,6 +2020,8 @@ heap_addheader(int natts, /* max domain if (withoid) /* else leave infomask = 0 */ td->t_infomask = HEAP_HASOID; + if (withseclabel) + td->t_infomask |= HEAP_HAS_SECLABEL; memcpy((char *) td + hoff, structure, structlen); diff -rpNU3 base/src/backend/access/common/tupdesc.c sepgsql-new/src/backend/access/common/tupdesc.c --- base/src/backend/access/common/tupdesc.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/access/common/tupdesc.c 2009-03-13 18:14:33.000000000 +0900 @@ -84,6 +84,7 @@ CreateTemplateTupleDesc(int natts, bool desc->tdtypeid = RECORDOID; desc->tdtypmod = -1; desc->tdhasoid = hasoid; + desc->tdhasseclabel = false; desc->tdrefcount = -1; /* assume not reference-counted */ return desc; @@ -117,6 +118,7 @@ CreateTupleDesc(int natts, bool hasoid, desc->tdtypeid = RECORDOID; desc->tdtypmod = -1; desc->tdhasoid = hasoid; + desc->tdhasseclabel = false; desc->tdrefcount = -1; /* assume not reference-counted */ return desc; @@ -146,6 +148,7 @@ CreateTupleDescCopy(TupleDesc tupdesc) desc->tdtypeid = tupdesc->tdtypeid; desc->tdtypmod = tupdesc->tdtypmod; + desc->tdhasseclabel = tupdesc->tdhasseclabel; return desc; } @@ -204,6 +207,7 @@ CreateTupleDescCopyConstr(TupleDesc tupd desc->tdtypeid = tupdesc->tdtypeid; desc->tdtypmod = tupdesc->tdtypmod; + desc->tdhasseclabel = tupdesc->tdhasseclabel; return desc; } @@ -310,6 +314,8 @@ equalTupleDescs(TupleDesc tupdesc1, Tupl return false; if (tupdesc1->tdhasoid != tupdesc2->tdhasoid) return false; + if (tupdesc1->tdhasseclabel != tupdesc2->tdhasseclabel) + return false; for (i = 0; i < tupdesc1->natts; i++) { diff -rpNU3 base/src/backend/access/heap/heapam.c sepgsql-new/src/backend/access/heap/heapam.c --- base/src/backend/access/heap/heapam.c 2009-09-10 20:11:35.000000000 +0900 +++ sepgsql-new/src/backend/access/heap/heapam.c 2009-09-10 20:24:37.000000000 +0900 @@ -50,6 +50,7 @@ #include "catalog/namespace.h" #include "miscadmin.h" #include "pgstat.h" +#include "security/rowlevel.h" #include "storage/procarray.h" #include "storage/smgr.h" #include "utils/datum.h" @@ -1958,6 +1959,8 @@ heap_insert(Relation relation, HeapTuple Oid simple_heap_insert(Relation relation, HeapTuple tup) { + rowlvHeapTupleInsert(relation, tup, true); + return heap_insert(relation, tup, GetCurrentCommandId(true), true, true); } @@ -2239,6 +2242,8 @@ simple_heap_delete(Relation relation, It ItemPointerData update_ctid; TransactionId update_xmax; + rowlvHeapTupleDelete(relation, tid, true); + result = heap_delete(relation, tid, &update_ctid, &update_xmax, GetCurrentCommandId(true), InvalidSnapshot, @@ -2883,6 +2888,8 @@ simple_heap_update(Relation relation, It ItemPointerData update_ctid; TransactionId update_xmax; + rowlvHeapTupleUpdate(relation, otid, tup, true); + result = heap_update(relation, otid, tup, &update_ctid, &update_xmax, GetCurrentCommandId(true), InvalidSnapshot, diff -rpNU3 base/src/backend/access/heap/tuptoaster.c sepgsql-new/src/backend/access/heap/tuptoaster.c --- base/src/backend/access/heap/tuptoaster.c 2008-09-25 15:09:40.000000000 +0900 +++ sepgsql-new/src/backend/access/heap/tuptoaster.c 2009-03-13 18:14:33.000000000 +0900 @@ -35,6 +35,7 @@ #include "access/tuptoaster.h" #include "access/xact.h" #include "catalog/catalog.h" +#include "security/rowlevel.h" #include "utils/fmgroids.h" #include "utils/pg_lzcompress.h" #include "utils/typcache.h" @@ -589,6 +590,8 @@ toast_insert_or_update(Relation rel, Hea hoff += BITMAPLEN(numAttrs); if (newtup->t_data->t_infomask & HEAP_HASOID) hoff += sizeof(Oid); + if (HeapTupleHasSecLabel(newtup)) + hoff += sizeof(Oid); hoff = MAXALIGN(hoff); Assert(hoff == newtup->t_data->t_hoff); /* now convert to a limit on the tuple data size */ @@ -838,6 +841,8 @@ toast_insert_or_update(Relation rel, Hea new_len += BITMAPLEN(numAttrs); if (olddata->t_infomask & HEAP_HASOID) new_len += sizeof(Oid); + if (HeapTupleHeaderHasSecLabel(olddata)) + new_len += sizeof(Oid); new_len = MAXALIGN(new_len); Assert(new_len == olddata->t_hoff); new_data_len = heap_compute_data_size(tupleDesc, @@ -989,6 +994,8 @@ toast_flatten_tuple_attribute(Datum valu new_len += BITMAPLEN(numAttrs); if (olddata->t_infomask & HEAP_HASOID) new_len += sizeof(Oid); + if (HeapTupleHeaderHasSecLabel(olddata)) + new_len += sizeof(Oid); new_len = MAXALIGN(new_len); Assert(new_len == olddata->t_hoff); new_data_len = heap_compute_data_size(tupleDesc, @@ -1173,6 +1180,8 @@ toast_save_datum(Relation rel, Datum val memcpy(VARDATA(&chunk_data), data_p, chunk_size); toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull); + rowlvHeapTupleInsert(toastrel, toasttup, true); + heap_insert(toastrel, toasttup, mycid, use_wal, use_fsm); /* diff -rpNU3 base/src/backend/access/transam/xact.c sepgsql-new/src/backend/access/transam/xact.c --- base/src/backend/access/transam/xact.c 2009-03-15 17:47:25.000000000 +0900 +++ sepgsql-new/src/backend/access/transam/xact.c 2009-05-20 17:35:01.000000000 +0900 @@ -35,6 +35,7 @@ #include "miscadmin.h" #include "pg_trace.h" #include "pgstat.h" +#include "security/rowlevel.h" #include "storage/fd.h" #include "storage/lmgr.h" #include "storage/procarray.h" @@ -137,6 +138,7 @@ typedef struct TransactionStateData Oid prevUser; /* previous CurrentUserId setting */ bool prevSecDefCxt; /* previous SecurityDefinerContext setting */ bool prevXactReadOnly; /* entry-time xact r/o state */ + int prevRowlv; /* previous Row-level control behavior */ struct TransactionStateData *parent; /* back link to parent */ } TransactionStateData; @@ -165,6 +167,7 @@ static TransactionStateData TopTransacti InvalidOid, /* previous CurrentUserId setting */ false, /* previous SecurityDefinerContext setting */ false, /* entry-time xact r/o state */ + ROWLV_FILTER_MODE, /* previous Row-level control behavior */ NULL /* link to parent state block */ }; @@ -1571,6 +1574,7 @@ StartTransaction(void) s->nChildXids = 0; s->maxChildXids = 0; GetUserIdAndContext(&s->prevUser, &s->prevSecDefCxt); + s->prevRowlv = rowlvGetPerformingMode(); /* SecurityDefinerContext should never be set outside a transaction */ Assert(!s->prevSecDefCxt); @@ -2071,6 +2075,11 @@ AbortTransaction(void) SetUserIdAndContext(s->prevUser, s->prevSecDefCxt); /* + * Reset behavior of row-level access controls + */ + rowlvSetPerformingMode(s->prevRowlv); + + /* * do abort processing */ AfterTriggerEndXact(false); @@ -3911,6 +3920,11 @@ AbortSubTransaction(void) SetUserIdAndContext(s->prevUser, s->prevSecDefCxt); /* + * Reset behavior of row-level access controls + */ + rowlvSetPerformingMode(s->prevRowlv); + + /* * We can skip all this stuff if the subxact failed before creating a * ResourceOwner... */ @@ -4052,6 +4066,7 @@ PushTransaction(void) s->blockState = TBLOCK_SUBBEGIN; GetUserIdAndContext(&s->prevUser, &s->prevSecDefCxt); s->prevXactReadOnly = XactReadOnly; + s->prevRowlv = rowlvGetPerformingMode(); CurrentTransactionState = s; diff -rpNU3 base/src/backend/bootstrap/bootparse.y sepgsql-new/src/backend/bootstrap/bootparse.y --- base/src/backend/bootstrap/bootparse.y 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/bootstrap/bootparse.y 2009-03-13 18:14:33.000000000 +0900 @@ -33,6 +33,7 @@ #include "catalog/pg_class.h" #include "catalog/pg_namespace.h" #include "catalog/pg_tablespace.h" +#include "catalog/pg_security.h" #include "catalog/toasting.h" #include "commands/defrem.h" #include "miscadmin.h" @@ -42,6 +43,7 @@ #include "nodes/pg_list.h" #include "nodes/primnodes.h" #include "rewrite/prs2lock.h" +#include "security/sepgsql.h" #include "storage/block.h" #include "storage/fd.h" #include "storage/ipc.h" @@ -194,6 +196,12 @@ Boot_CreateStmt: RELKIND_RELATION, $3, true); + /* + * fixup boot_reldesc->rd_att->tdhassecXXXX + */ + boot_reldesc->rd_rel->relkind = RELKIND_RELATION; + boot_reldesc->rd_att->tdhasseclabel + = securityTupleDescHasSecLabel(boot_reldesc); elog(DEBUG4, "bootstrap relation created"); } else @@ -212,7 +220,8 @@ Boot_CreateStmt: 0, ONCOMMIT_NOOP, (Datum) 0, - true); + true, + NIL); elog(DEBUG4, "relation created with oid %u", id); } do_end(); diff -rpNU3 base/src/backend/bootstrap/bootstrap.c sepgsql-new/src/backend/bootstrap/bootstrap.c --- base/src/backend/bootstrap/bootstrap.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/bootstrap/bootstrap.c 2009-03-13 18:14:33.000000000 +0900 @@ -25,6 +25,7 @@ #include "access/xact.h" #include "bootstrap/bootstrap.h" #include "catalog/index.h" +#include "catalog/pg_security.h" #include "catalog/pg_type.h" #include "libpq/pqsignal.h" #include "miscadmin.h" @@ -499,6 +500,11 @@ BootstrapModeMain(void) */ boot_yyparse(); + /* + * Flush all the cached security label + */ + securityPostBootstrapingMode(); + /* Perform a checkpoint to ensure everything's down to disk */ SetProcessingMode(NormalProcessing); CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE); @@ -796,6 +802,8 @@ InsertOneTuple(Oid objectid) tupDesc = CreateTupleDesc(numattr, RelationGetForm(boot_reldesc)->relhasoids, attrtypes); + tupDesc->tdhasseclabel + = securityTupleDescHasSecLabel(boot_reldesc); tuple = heap_formtuple(tupDesc, values, Blanks); if (objectid != (Oid) 0) HeapTupleSetOid(tuple, objectid); diff -rpNU3 base/src/backend/catalog/Makefile sepgsql-new/src/backend/catalog/Makefile --- base/src/backend/catalog/Makefile 2007-09-11 10:53:53.000000000 +0900 +++ sepgsql-new/src/backend/catalog/Makefile 2009-03-13 18:14:33.000000000 +0900 @@ -13,7 +13,7 @@ include $(top_builddir)/src/Makefile.glo OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o pg_enum.o \ pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o pg_shdepend.o \ - pg_type.o toasting.o + pg_security.o pg_type.o toasting.o BKIFILES = postgres.bki postgres.description postgres.shdescription @@ -35,7 +35,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_sr pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ pg_database.h pg_tablespace.h pg_pltemplate.h \ pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ - pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ + pg_security.h pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ pg_ts_parser.h pg_ts_template.h \ toasting.h indexing.h \ ) diff -rpNU3 base/src/backend/catalog/aclchk.c sepgsql-new/src/backend/catalog/aclchk.c --- base/src/backend/catalog/aclchk.c 2008-06-12 22:34:19.000000000 +0900 +++ sepgsql-new/src/backend/catalog/aclchk.c 2009-05-20 17:08:02.000000000 +0900 @@ -39,6 +39,7 @@ #include "commands/dbcommands.h" #include "miscadmin.h" #include "parser/parse_func.h" +#include "security/sepgsql.h" #include "utils/acl.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" @@ -1979,7 +1980,13 @@ AclResult pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode) { if (pg_database_aclmask(db_oid, roleid, mode, ACLMASK_ANY) != 0) + { + /* SELinux: db_database:{access} permission */ + if ((mode & ACL_CONNECT) != 0 && !sepgsqlCheckDatabaseAccess(db_oid)) + return ACLCHECK_NO_PRIV; + return ACLCHECK_OK; + } else return ACLCHECK_NO_PRIV; } @@ -1991,7 +1998,13 @@ AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode) { if (pg_proc_aclmask(proc_oid, roleid, mode, ACLMASK_ANY) != 0) + { + /* SELinux: db_procedure:{execute} permission */ + if ((mode & ACL_EXECUTE) != 0 && !sepgsqlCheckProcedureExecute(proc_oid)) + return ACLCHECK_NO_PRIV; + return ACLCHECK_OK; + } else return ACLCHECK_NO_PRIV; } @@ -2015,7 +2028,13 @@ AclResult pg_namespace_aclcheck(Oid nsp_oid, Oid roleid, AclMode mode) { if (pg_namespace_aclmask(nsp_oid, roleid, mode, ACLMASK_ANY) != 0) + { + /* SELinux: db_schema:{search} permission */ + if ((mode & ACL_USAGE) && !sepgsqlCheckSchemaSearch(nsp_oid)) + return ACLCHECK_NO_PRIV; + return ACLCHECK_OK; + } else return ACLCHECK_NO_PRIV; } diff -rpNU3 base/src/backend/catalog/catalog.c sepgsql-new/src/backend/catalog/catalog.c --- base/src/backend/catalog/catalog.c 2008-03-19 09:48:23.000000000 +0900 +++ sepgsql-new/src/backend/catalog/catalog.c 2009-03-13 18:14:33.000000000 +0900 @@ -30,6 +30,7 @@ #include "catalog/pg_database.h" #include "catalog/pg_namespace.h" #include "catalog/pg_pltemplate.h" +#include "catalog/pg_security.h" #include "catalog/pg_shdepend.h" #include "catalog/pg_shdescription.h" #include "catalog/pg_tablespace.h" @@ -257,6 +258,7 @@ IsSharedRelation(Oid relationId) relationId == AuthMemRelationId || relationId == DatabaseRelationId || relationId == PLTemplateRelationId || + relationId == SecurityRelationId || relationId == SharedDescriptionRelationId || relationId == SharedDependRelationId || relationId == TableSpaceRelationId) @@ -269,6 +271,8 @@ IsSharedRelation(Oid relationId) relationId == DatabaseNameIndexId || relationId == DatabaseOidIndexId || relationId == PLTemplateNameIndexId || + relationId == SecurityOidIndexId || + relationId == SecuritySeclabelIndexId || relationId == SharedDescriptionObjIndexId || relationId == SharedDependDependerIndexId || relationId == SharedDependReferenceIndexId || diff -rpNU3 base/src/backend/catalog/heap.c sepgsql-new/src/backend/catalog/heap.c --- base/src/backend/catalog/heap.c 2009-03-15 17:47:25.000000000 +0900 +++ sepgsql-new/src/backend/catalog/heap.c 2009-06-19 15:02:12.000000000 +0900 @@ -42,6 +42,7 @@ #include "catalog/pg_constraint.h" #include "catalog/pg_inherits.h" #include "catalog/pg_namespace.h" +#include "catalog/pg_security.h" #include "catalog/pg_statistic.h" #include "catalog/pg_tablespace.h" #include "catalog/pg_type.h" @@ -67,7 +68,8 @@ static void AddNewRelationTuple(Relation Oid new_rel_oid, Oid new_type_oid, Oid relowner, char relkind, - Datum reloptions); + Datum reloptions, + List *selblList); static Oid AddNewRelationType(const char *typeName, Oid typeNamespace, Oid new_rel_oid, @@ -145,7 +147,16 @@ static FormData_pg_attribute a7 = { true, 'p', 'i', true, false, false, true, 0 }; -static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; +/* + * System columns for enhanced security features + */ +static FormData_pg_attribute a8 = { + 0, {SecurityLabelAttributeName}, TEXTOID, 0, -1, + SecurityLabelAttributeNumber, 0, -1, -1, + false, 'x', 'i', true, false, false, true, 0 +}; + +static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8}; /* * This function returns a Form_pg_attribute pointer for a system attribute. @@ -185,6 +196,18 @@ SystemAttributeByName(const char *attnam return NULL; } +/* + * This function returns true, if the given attribute number is writable + * system column. If not, returns false. + */ +bool +SystemAttributeIsWritable(AttrNumber attnum) +{ + if (attnum == SecurityLabelAttributeNumber) + return true; + + return false; +} /* ---------------------------------------------------------------- * XXX END OF UGLY HARD CODED BADNESS XXX @@ -468,7 +491,8 @@ AddNewAttributeTuples(Oid new_rel_oid, TupleDesc tupdesc, char relkind, bool oidislocal, - int oidinhcount) + int oidinhcount, + List *selblList) { const Form_pg_attribute *dpp; int i; @@ -493,16 +517,40 @@ AddNewAttributeTuples(Oid new_rel_oid, dpp = tupdesc->attrs; for (i = 0; i < natts; i++) { + ListCell *l; + Oid attselabel = InvalidOid; + /* Fill in the correct relation OID */ (*dpp)->attrelid = new_rel_oid; /* Make sure these are OK, too */ (*dpp)->attstattarget = -1; (*dpp)->attcacheoff = -1; + /* SELinux: extract a given security context */ + foreach (l, selblList) + { + DefElem *defel = lfirst(l); + + if (defel->defname && + strcmp(defel->defname, NameStr((*dpp)->attname)) == 0) + { + attselabel = intVal(defel->arg); + break; + } + } + tup = heap_addheader(Natts_pg_attribute, false, + RelationGetDescr(rel)->tdhasseclabel, ATTRIBUTE_TUPLE_SIZE, (void *) *dpp); + if (OidIsValid(attselabel)) + { + if (!HeapTupleHasSecLabel(tup)) + elog(ERROR, "Unable to assign security label on \"%s\"", + RelationGetRelationName(rel)); + HeapTupleSetSecLabel(tup, attselabel); + } simple_heap_insert(rel, tup); @@ -538,6 +586,7 @@ AddNewAttributeTuples(Oid new_rel_oid, tup = heap_addheader(Natts_pg_attribute, false, + RelationGetDescr(rel)->tdhasseclabel, ATTRIBUTE_TUPLE_SIZE, (void *) *dpp); attStruct = (Form_pg_attribute) GETSTRUCT(tup); @@ -593,7 +642,8 @@ void InsertPgClassTuple(Relation pg_class_desc, Relation new_rel_desc, Oid new_rel_oid, - Datum reloptions) + Datum reloptions, + Oid relselabel) { Form_pg_class rd_rel = new_rel_desc->rd_rel; Datum values[Natts_pg_class]; @@ -644,11 +694,23 @@ InsertPgClassTuple(Relation pg_class_des */ HeapTupleSetOid(tup, new_rel_oid); + /* set explicit security label */ + if (OidIsValid(relselabel)) + { + if (!HeapTupleHasSecLabel(tup)) + elog(ERROR, "Unable to assign security label on \"%s\"", + RelationGetRelationName(pg_class_desc)); + HeapTupleSetSecLabel(tup, relselabel); + } + /* finally insert the new tuple, update the indexes, and clean up */ simple_heap_insert(pg_class_desc, tup); CatalogUpdateIndexes(pg_class_desc, tup); + /* temporary use for this tuple */ + InsertSysCache(RelationGetRelid(pg_class_desc), tup); + heap_freetuple(tup); } @@ -666,9 +728,12 @@ AddNewRelationTuple(Relation pg_class_de Oid new_type_oid, Oid relowner, char relkind, - Datum reloptions) + Datum reloptions, + List *selblList) { Form_pg_class new_rel_reltup; + Oid relselabel = InvalidOid; + ListCell *l; /* * first we update some of the information in our uncataloged relation's @@ -725,8 +790,21 @@ AddNewRelationTuple(Relation pg_class_de new_rel_desc->rd_att->tdtypeid = new_type_oid; + /* SELinux: extract a given security context */ + foreach (l, selblList) + { + DefElem *defel = lfirst(l); + + if (!defel->defname) + { + relselabel = intVal(defel->arg); + break; + } + } + /* 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, relselabel); } @@ -794,7 +872,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 *selblList) { Relation pg_class_desc; Relation new_rel_desc; @@ -968,13 +1047,20 @@ heap_create_with_catalog(const char *rel new_type_oid, ownerid, relkind, - reloptions); + reloptions, + selblList); /* * 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, selblList); + + /* + * Fixup rel->rd_att->tdhassecacl and rel->rd_att->tdhasseclabel + */ + new_rel_desc->rd_att->tdhasseclabel + = securityTupleDescHasSecLabel(new_rel_desc); /* * Make a dependency link to force the relation to be deleted if its diff -rpNU3 base/src/backend/catalog/index.c sepgsql-new/src/backend/catalog/index.c --- base/src/backend/catalog/index.c 2009-02-02 11:47:17.000000000 +0900 +++ sepgsql-new/src/backend/catalog/index.c 2009-03-13 18:14:33.000000000 +0900 @@ -37,6 +37,7 @@ #include "catalog/pg_constraint.h" #include "catalog/pg_operator.h" #include "catalog/pg_opclass.h" +#include "catalog/pg_security.h" #include "catalog/pg_tablespace.h" #include "catalog/pg_type.h" #include "commands/tablecmds.h" @@ -315,6 +316,7 @@ AppendAttributeTuples(Relation indexRela new_tuple = heap_addheader(Natts_pg_attribute, false, + RelationGetDescr(pg_attribute)->tdhasseclabel, ATTRIBUTE_TUPLE_SIZE, (void *) indexTupDesc->attrs[i]); @@ -602,6 +604,12 @@ index_create(Oid heapRelationId, Assert(indexRelationId == RelationGetRelid(indexRelation)); /* + * Fixup rel->rd_att->tdhassecXXX + */ + indexRelation->rd_att->tdhasseclabel + = securityTupleDescHasSecLabel(indexRelation); + + /* * Obtain exclusive lock on it. Although no other backends can see it * until we commit, this prevents deadlock-risk complaints from lock * manager in cases such as CLUSTER. @@ -624,7 +632,7 @@ index_create(Oid heapRelationId, */ InsertPgClassTuple(pg_class, indexRelation, RelationGetRelid(indexRelation), - reloptions); + reloptions, InvalidOid); /* done with pg_class */ heap_close(pg_class, RowExclusiveLock); diff -rpNU3 base/src/backend/catalog/namespace.c sepgsql-new/src/backend/catalog/namespace.c --- base/src/backend/catalog/namespace.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/catalog/namespace.c 2009-09-10 20:24:37.000000000 +0900 @@ -37,6 +37,7 @@ #include "commands/dbcommands.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "security/sepgsql.h" #include "storage/backendid.h" #include "storage/ipc.h" #include "utils/acl.h" @@ -2629,7 +2630,9 @@ InitTempTableNamespace(void) * temp tables. This works because the places that access the temp * namespace for my own backend skip permissions checks on it. */ - namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); + namespaceId = NamespaceCreate(namespaceName, + BOOTSTRAP_SUPERUSERID, + InvalidOid); /* Advance command counter to make namespace visible */ CommandCounterIncrement(); } @@ -2655,7 +2658,9 @@ InitTempTableNamespace(void) 0, 0, 0); if (!OidIsValid(toastspaceId)) { - toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); + toastspaceId = NamespaceCreate(namespaceName, + BOOTSTRAP_SUPERUSERID, + InvalidOid); /* Advance command counter to make namespace visible */ CommandCounterIncrement(); } @@ -2797,6 +2802,7 @@ static void RemoveTempRelations(Oid tempNamespaceId) { ObjectAddress object; + int mode; /* * We want to get rid of everything in the target namespace, but not the @@ -2808,7 +2814,22 @@ RemoveTempRelations(Oid tempNamespaceId) object.objectId = tempNamespaceId; object.objectSubId = 0; - deleteWhatDependsOn(&object, false); + /* + * SELinux does not check anything while cleaning up + * temporary objects. + */ + mode = sepgsqlSetExceptionMode(1); + PG_TRY(); + { + deleteWhatDependsOn(&object, false); + } + PG_CATCH(); + { + sepgsqlSetExceptionMode(mode); + PG_RE_THROW(); + } + PG_END_TRY(); + sepgsqlSetExceptionMode(mode); } /* diff -rpNU3 base/src/backend/catalog/pg_aggregate.c sepgsql-new/src/backend/catalog/pg_aggregate.c --- base/src/backend/catalog/pg_aggregate.c 2008-01-14 22:59:48.000000000 +0900 +++ sepgsql-new/src/backend/catalog/pg_aggregate.c 2009-03-13 18:14:33.000000000 +0900 @@ -214,7 +214,8 @@ AggregateCreate(const char *aggName, PointerGetDatum(NULL), /* parameterNames */ PointerGetDatum(NULL), /* proconfig */ 1, /* procost */ - 0); /* prorows */ + 0, /* prorows */ + InvalidOid); /* proseclabel */ /* * Okay to create the pg_aggregate entry. diff -rpNU3 base/src/backend/catalog/pg_largeobject.c sepgsql-new/src/backend/catalog/pg_largeobject.c --- base/src/backend/catalog/pg_largeobject.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/catalog/pg_largeobject.c 2009-05-20 17:08:02.000000000 +0900 @@ -18,6 +18,7 @@ #include "access/heapam.h" #include "catalog/indexing.h" #include "catalog/pg_largeobject.h" +#include "security/sepgsql.h" #include "utils/builtins.h" #include "utils/fmgroids.h" @@ -57,6 +58,9 @@ LargeObjectCreate(Oid loid) ntup = heap_formtuple(pg_largeobject->rd_att, values, nulls); + /* SELinux checks db_blob:{create} */ + sepgsqlCheckBlobCreate(pg_largeobject, ntup); + /* * Insert it */ @@ -91,6 +95,9 @@ LargeObjectDrop(Oid loid) while ((tuple = systable_getnext(sd)) != NULL) { + /* SELinux checks db_blob:{drop} */ + if (!found) + sepgsqlCheckBlobDrop(pg_largeobject, tuple); simple_heap_delete(pg_largeobject, &tuple->t_self); found = true; } diff -rpNU3 base/src/backend/catalog/pg_namespace.c sepgsql-new/src/backend/catalog/pg_namespace.c --- base/src/backend/catalog/pg_namespace.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/catalog/pg_namespace.c 2009-06-19 15:02:12.000000000 +0900 @@ -27,7 +27,7 @@ * --------------- */ Oid -NamespaceCreate(const char *nspName, Oid ownerId) +NamespaceCreate(const char *nspName, Oid ownerId, Oid secid) { Relation nspdesc; HeapTuple tup; @@ -65,6 +65,8 @@ NamespaceCreate(const char *nspName, Oid tupDesc = nspdesc->rd_att; tup = heap_formtuple(tupDesc, values, nulls); + if (HeapTupleHasSecLabel(tup)) + HeapTupleSetSecLabel(tup, secid); nspoid = simple_heap_insert(nspdesc, tup); Assert(OidIsValid(nspoid)); diff -rpNU3 base/src/backend/catalog/pg_proc.c sepgsql-new/src/backend/catalog/pg_proc.c --- base/src/backend/catalog/pg_proc.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/catalog/pg_proc.c 2009-03-13 18:14:33.000000000 +0900 @@ -27,6 +27,7 @@ #include "mb/pg_wchar.h" #include "miscadmin.h" #include "parser/parse_type.h" +#include "security/sepgsql.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" #include "utils/acl.h" @@ -74,7 +75,8 @@ ProcedureCreate(const char *procedureNam Datum parameterNames, Datum proconfig, float4 procost, - float4 prorows) + float4 prorows, + Oid proselabel) { Oid retval; int parameterCount; @@ -339,6 +341,13 @@ ProcedureCreate(const char *procedureNam /* Okay, do it... */ tup = heap_modifytuple(oldtup, tupDesc, values, nulls, replaces); + if (OidIsValid(proselabel)) + { + if (!HeapTupleHasSecLabel(tup)) + elog(ERROR, "Unable to assign security label on \"%s\"", + RelationGetRelationName(rel)); + HeapTupleSetSecLabel(tup, proselabel); + } simple_heap_update(rel, &tup->t_self, tup); ReleaseSysCache(oldtup); @@ -348,6 +357,13 @@ ProcedureCreate(const char *procedureNam { /* Creating a new procedure */ tup = heap_formtuple(tupDesc, values, nulls); + if (OidIsValid(proselabel)) + { + if (!HeapTupleHasSecLabel(tup)) + elog(ERROR, "Unable to assign security label on \"%s\"", + RelationGetRelationName(rel)); + HeapTupleSetSecLabel(tup, proselabel); + } simple_heap_insert(rel, tup); is_update = false; } diff -rpNU3 base/src/backend/catalog/pg_security.c sepgsql-new/src/backend/catalog/pg_security.c --- base/src/backend/catalog/pg_security.c 1970-01-01 09:00:00.000000000 +0900 +++ sepgsql-new/src/backend/catalog/pg_security.c 2009-06-10 17:43:08.000000000 +0900 @@ -0,0 +1,299 @@ +/* + * src/backend/catalog/pg_security.c + * routines to support security label management + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "access/xact.h" +#include "catalog/catalog.h" +#include "catalog/indexing.h" +#include "catalog/pg_security.h" +#include "miscadmin.h" +#include "security/sepgsql.h" +#include "utils/builtins.h" +#include "utils/memutils.h" +#include "utils/rel.h" +#include "utils/syscache.h" + +bool +securityTupleDescHasSecLabel(Relation rel) +{ + return sepgsqlTupleDescHasSecLabel(rel); +} + +static char * +securityMetaSecurityLabel(void) +{ + return sepgsqlMetaSecurityLabel(true); +} + +typedef struct earlySecLabel +{ + struct earlySecLabel *next; + Oid secid; + char seclabel[1]; +} earlySecLabel; + +static earlySecLabel *earlySecLabelList = NULL; + +static Oid +earlyInputSecurityAttr(const char *seclabel) +{ + static Oid dummySecid = SecurityRelationId; + earlySecLabel *es; + + for (es = earlySecLabelList; es; es = es->next) + { + if (strcmp(seclabel, es->seclabel) == 0) + return es->secid; + } + + /* not found */ + es = MemoryContextAlloc(TopMemoryContext, + sizeof(*es) + strlen(seclabel)); + es->secid = --dummySecid; + strcpy(es->seclabel, seclabel); + + es->next = earlySecLabelList; + earlySecLabelList = es; + + return es->secid; +} + +static char * +earlyOutputSecurityAttr(Oid secid) +{ + earlySecLabel *es; + + for (es = earlySecLabelList; es; es = es->next) + { + if (es->secid == secid) + return pstrdup(es->seclabel); + } + return NULL; /* Not found */ +} + +void +securityPostBootstrapingMode(void) +{ + Relation rel; + CatalogIndexState ind; + HeapTuple tuple; + earlySecLabel *es; + Oid labelSid = InvalidOid; + Datum values[Natts_pg_security]; + bool nulls[Natts_pg_security]; + char *meta_label; + + if (!earlySecLabelList) + return; /* do nothing */ + + StartTransactionCommand(); + + meta_label = securityMetaSecurityLabel(); + if (meta_label) + labelSid = securityTransSecLabelIn(SecurityRelationId, meta_label); + + rel = heap_open(SecurityRelationId, RowExclusiveLock); + ind = CatalogOpenIndexes(rel); + + for (es = earlySecLabelList; es; es = es->next) + { + memset(nulls, false, sizeof(nulls)); + + values[Anum_pg_security_seclabel - 1] + = CStringGetTextDatum(es->seclabel); + + tuple = heap_form_tuple(RelationGetDescr(rel), + values, nulls); + HeapTupleSetOid(tuple, es->secid); + if (HeapTupleHasSecLabel(tuple)) + HeapTupleSetSecLabel(tuple, labelSid); + + simple_heap_insert(rel, tuple); + CatalogIndexInsert(ind, tuple); + + heap_freetuple(tuple); + } + + CatalogCloseIndexes(ind); + heap_close(rel, RowExclusiveLock); + + CommitTransactionCommand(); +} + +/* + * InputSecurityAttr + */ +static Oid +InputSecurityAttr(Oid relid, const char *seclabel) +{ + Relation rel; + CatalogIndexState ind; + HeapTuple tuple; + Oid labelOid; + Oid labelSid; + Datum values[Natts_pg_security]; + bool nulls[Natts_pg_security]; + char *meta_label; + + if (IsBootstrapProcessingMode()) + return earlyInputSecurityAttr(seclabel); + /* + * lookup syscache at first + */ + tuple = SearchSysCache(SECURITYLABEL, + CStringGetTextDatum(seclabel), + 0, 0, 0); + if (HeapTupleIsValid(tuple)) + { + labelOid = HeapTupleGetOid(tuple); + ReleaseSysCache(tuple); + return labelOid; + } + + /* + * Not found, insert a new one into pg_security + */ + rel = heap_open(SecurityRelationId, RowExclusiveLock); + + ind = CatalogOpenIndexes(rel); + + if (RelationGetDescr(rel)->tdhasseclabel && + (meta_label = securityMetaSecurityLabel()) != NULL) + { + if (strcmp(seclabel, meta_label) == 0) + { + labelOid = labelSid = GetNewOid(rel); + } + else + { + labelOid = GetNewOid(rel); + labelSid = securityTransSecLabelIn(SecurityRelationId, meta_label); + } + } + else + { + labelOid = GetNewOid(rel); + labelSid = InvalidOid; + } + + memset(nulls, false, sizeof(nulls)); + values[Anum_pg_security_seclabel - 1] + = CStringGetTextDatum(seclabel); + + tuple = heap_form_tuple(RelationGetDescr(rel), + values, nulls); + + if (HeapTupleHasSecLabel(tuple)) + HeapTupleSetSecLabel(tuple, labelSid); + HeapTupleSetOid(tuple, labelOid); + + simple_heap_insert(rel, tuple); + CatalogIndexInsert(ind, tuple); + + /* + * NOTE: + * We also have to insert a cache entry of new tuple of + * pg_security for temporary usage. + * If user tries to apply same security attribute twice + * or more within same command id, we cannot decide + * whether it should be inserted, or not, because it + * cannot scan the prior one with SnapshotNow. + * + * A cache entry inserted will be invalidated on the + * next CommandIdIncrement(). + * The purpose of InsertSysCache() here is to prevent + * duplicate insertion + */ + InsertSysCache(RelationGetRelid(rel), tuple); + + CatalogCloseIndexes(ind); + + heap_close(rel, RowExclusiveLock); + + return labelOid; +} + +static char * +OutputSecurityAttr(Oid relid, Oid secid) +{ + HeapTuple tuple; + Datum labelTxt; + char *label; + bool isnull; + + if (IsBootstrapProcessingMode()) + return earlyOutputSecurityAttr(secid); + + tuple = SearchSysCache(SECURITYOID, + ObjectIdGetDatum(secid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + return NULL; + + labelTxt = SysCacheGetAttr(SECURITYOID, tuple, + Anum_pg_security_seclabel, + &isnull); + Assert(!isnull); + label = TextDatumGetCString(labelTxt); + ReleaseSysCache(tuple); + + return label; +} + +/* + * input/output handler + */ +Oid +securityRawSecLabelIn(Oid relid, char *seclabel) +{ + seclabel = sepgsqlRawSecLabelIn(seclabel); + + return InputSecurityAttr(relid, seclabel); +} + +char * +securityRawSecLabelOut(Oid relid, Oid secid) +{ + char *seclabel = OutputSecurityAttr(relid, secid); + + return sepgsqlRawSecLabelOut(seclabel); +} + + +Oid +securityTransSecLabelIn(Oid relid, char *seclabel) +{ + seclabel = sepgsqlTransSecLabelIn(seclabel); + + return securityRawSecLabelIn(relid, seclabel); +} + +char * +securityTransSecLabelOut(Oid relid, Oid secid) +{ + char *seclabel = securityRawSecLabelOut(relid, secid); + + return sepgsqlTransSecLabelOut(seclabel); +} + +/* + * Output handler for system columns + */ +Datum +securityHeapGetSecLabelSysattr(HeapTuple tuple) +{ + Oid secid = HeapTupleGetSecLabel(tuple); + char *seclabel; + + seclabel = securityTransSecLabelOut(tuple->t_tableOid, secid); + if (!seclabel) + seclabel = pstrdup("unlabeled"); + + return CStringGetTextDatum(seclabel); +} diff -rpNU3 base/src/backend/catalog/toasting.c sepgsql-new/src/backend/catalog/toasting.c --- base/src/backend/catalog/toasting.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/catalog/toasting.c 2009-03-13 18:14:33.000000000 +0900 @@ -199,7 +199,8 @@ create_toast_table(Relation rel, Oid toa 0, ONCOMMIT_NOOP, (Datum) 0, - true); + true, + NIL); /* make the toast relation visible, else index creation will fail */ CommandCounterIncrement(); diff -rpNU3 base/src/backend/commands/alter.c sepgsql-new/src/backend/commands/alter.c --- base/src/backend/commands/alter.c 2008-03-19 09:48:23.000000000 +0900 +++ sepgsql-new/src/backend/commands/alter.c 2009-06-19 15:02:12.000000000 +0900 @@ -269,3 +269,32 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) (int) stmt->objectType); } } + +void +ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt) +{ + DefElem *seclabel = (DefElem *)stmt->secLabel; + + switch (stmt->objectType) + { + case OBJECT_DATABASE: + AlterDatabaseSecLabel(strVal(linitial(stmt->object)), seclabel); + break; + case OBJECT_SCHEMA: + AlterSchemaSecLabel(strVal(linitial(stmt->object)), seclabel); + break; + case OBJECT_TABLE: + case OBJECT_SEQUENCE: + case OBJECT_COLUMN: + CheckRelationOwnership(stmt->relation, false); + AlterRelationSecLabel(stmt->relation, stmt->subname, + stmt->objectType, seclabel); + break; + case OBJECT_FUNCTION: + AlterFunctionSecLabel(stmt->object, stmt->objarg, seclabel); + break; + default: + elog(ERROR, "unrecognized AlterSecLabelStmt type: %d", + (int) stmt->objectType); + } +} diff -rpNU3 base/src/backend/commands/cluster.c sepgsql-new/src/backend/commands/cluster.c --- base/src/backend/commands/cluster.c 2008-02-03 01:11:28.000000000 +0900 +++ sepgsql-new/src/backend/commands/cluster.c 2009-03-13 18:14:33.000000000 +0900 @@ -34,6 +34,7 @@ #include "commands/trigger.h" #include "commands/vacuum.h" #include "miscadmin.h" +#include "security/sepgsql.h" #include "storage/procarray.h" #include "utils/acl.h" #include "utils/fmgroids.h" @@ -666,7 +667,8 @@ make_new_heap(Oid OIDOldHeap, const char 0, ONCOMMIT_NOOP, reloptions, - allowSystemTableMods); + allowSystemTableMods, + NIL); ReleaseSysCache(tuple); @@ -856,6 +858,9 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOl /* Preserve OID, if any */ if (NewHeap->rd_rel->relhasoids) HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple)); + /* Preserve SecLabel, if any */ + if (HeapTupleHasSecLabel(copiedTuple)) + HeapTupleSetSecLabel(copiedTuple, HeapTupleGetSecLabel(tuple)); /* The heap rewrite module does the rest */ rewrite_heap_tuple(rwstate, tuple, copiedTuple); diff -rpNU3 base/src/backend/commands/copy.c sepgsql-new/src/backend/commands/copy.c --- base/src/backend/commands/copy.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/commands/copy.c 2009-06-10 17:43:08.000000000 +0900 @@ -22,7 +22,9 @@ #include "access/heapam.h" #include "access/xact.h" +#include "catalog/heap.h" #include "catalog/namespace.h" +#include "catalog/pg_security.h" #include "catalog/pg_type.h" #include "commands/copy.h" #include "commands/trigger.h" @@ -34,6 +36,8 @@ #include "optimizer/planner.h" #include "parser/parse_relation.h" #include "rewrite/rewriteHandler.h" +#include "security/rowlevel.h" +#include "security/sepgsql.h" #include "storage/fd.h" #include "tcop/tcopprot.h" #include "utils/acl.h" @@ -159,6 +163,10 @@ typedef struct CopyStateData char *raw_buf; int raw_buf_index; /* next byte to process */ int raw_buf_len; /* total # of bytes stored */ + + /* dump/restore support for security_label */ + FmgrInfo seclabel_out_function; + bool seclabel_force_quot; } CopyStateData; typedef CopyStateData *CopyState; @@ -242,7 +250,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 secLabelId, Datum *values, bool *nulls); static void CopyFrom(CopyState cstate); static bool CopyReadLine(CopyState cstate); @@ -1073,6 +1081,9 @@ DoCopy(const CopyStmt *stmt, const char /* Generate or convert list of attributes to process */ cstate->attnumlist = CopyGetAttnums(tupDesc, cstate->rel, attnamelist); + /* SELinux checks table/column level permissions */ + sepgsqlCheckCopyTable(cstate->rel, cstate->attnumlist, is_from); + num_phys_attrs = tupDesc->natts; /* Convert FORCE QUOTE name list to per-column flags, check validity */ @@ -1089,11 +1100,32 @@ DoCopy(const CopyStmt *stmt, const char int attnum = lfirst_int(cur); if (!list_member_int(cstate->attnumlist, attnum)) + { + Form_pg_attribute attForm; + + if (SystemAttributeIsWritable(attnum)) + attForm = SystemAttributeDefinition(attnum, true); + else + attForm = tupDesc->attrs[attnum - 1]; + + Assert(attForm != NULL); + ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", - NameStr(tupDesc->attrs[attnum - 1]->attname)))); - cstate->force_quote_flags[attnum - 1] = true; + NameStr(attForm->attname)))); + } + + switch (attnum) + { + case SecurityLabelAttributeNumber: + cstate->seclabel_force_quot = true; + break; + + default: + cstate->force_quote_flags[attnum - 1] = true; + break; + } } } @@ -1111,10 +1143,23 @@ DoCopy(const CopyStmt *stmt, const char int attnum = lfirst_int(cur); if (!list_member_int(cstate->attnumlist, attnum)) + { + Form_pg_attribute attForm; + + if (SystemAttributeIsWritable(attnum)) + attForm = SystemAttributeDefinition(attnum, true); + else + attForm = tupDesc->attrs[attnum - 1]; + + Assert(attForm != NULL); + ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", - NameStr(tupDesc->attrs[attnum - 1]->attname)))); + NameStr(attForm->attname)))); + } + if (SystemAttributeIsWritable(attnum)) + continue; /* ignore, if specified */ cstate->force_notnull_flags[attnum - 1] = true; } } @@ -1242,6 +1287,9 @@ DoCopyTo(CopyState cstate) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is a directory", cstate->filename))); + + /* SELinux: check file:{write} permission */ + sepgsqlCheckFileWrite(fileno(cstate->copy_file), cstate->filename); } PG_TRY(); @@ -1305,16 +1353,31 @@ CopyTo(CopyState cstate) int attnum = lfirst_int(cur); Oid out_func_oid; bool isvarlena; + FmgrInfo *out_fmgr; + Form_pg_attribute attForm; + + switch (attnum) + { + case SecurityLabelAttributeNumber: + attForm = SystemAttributeDefinition(attnum, true); + out_fmgr = &cstate->seclabel_out_function; + break; + + default: + attForm = attr[attnum - 1]; + out_fmgr = &cstate->out_functions[attnum - 1]; + break; + } if (cstate->binary) - getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid, + getTypeBinaryOutputInfo(attForm->atttypid, &out_func_oid, &isvarlena); else - getTypeOutputInfo(attr[attnum - 1]->atttypid, + getTypeOutputInfo(attForm->atttypid, &out_func_oid, &isvarlena); - fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); + fmgr_info(out_func_oid, out_fmgr); } /* @@ -1369,7 +1432,14 @@ CopyTo(CopyState cstate) CopySendChar(cstate, cstate->delim[0]); hdr_delim = true; - colname = NameStr(attr[attnum - 1]->attname); + if (SystemAttributeIsWritable(attnum)) + { + Form_pg_attribute attForm + = SystemAttributeDefinition(attnum, true); + colname = NameStr(attForm->attname); + } + else + colname = NameStr(attr[attnum - 1]->attname); CopyAttributeOutCSV(cstate, colname, false, list_length(cstate->attnumlist) == 1); @@ -1395,11 +1465,18 @@ CopyTo(CopyState cstate) { CHECK_FOR_INTERRUPTS(); + /* Row-level controls on the fetched tuple */ + if (!rowlvCopyToTuple(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), + HeapTupleGetSecLabel(tuple), + values, nulls); } heap_endscan(scandesc); @@ -1425,7 +1502,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 secLabelId, Datum *values, bool *nulls) { bool need_delim = false; FmgrInfo *out_functions = cstate->out_functions; @@ -1464,8 +1541,11 @@ CopyOneRowTo(CopyState cstate, Oid tuple foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); - Datum value = values[attnum - 1]; - bool isnull = nulls[attnum - 1]; + Oid relid; + Datum value; + bool isnull; + bool force_quot; + FmgrInfo *out_fmgr; if (!cstate->binary) { @@ -1474,6 +1554,24 @@ CopyOneRowTo(CopyState cstate, Oid tuple need_delim = true; } + switch (attnum) + { + case SecurityLabelAttributeNumber: + relid = RelationGetRelid(cstate->rel); + value = CStringGetTextDatum(securityTransSecLabelOut(relid, secLabelId)); + isnull = false; + force_quot = cstate->seclabel_force_quot; + out_fmgr = &cstate->seclabel_out_function; + break; + + default: + value = values[attnum - 1]; + isnull = nulls[attnum - 1]; + force_quot = cstate->force_quote_flags[attnum - 1]; + out_fmgr = &out_functions[attnum - 1]; + break; + } + if (isnull) { if (!cstate->binary) @@ -1485,11 +1583,9 @@ CopyOneRowTo(CopyState cstate, Oid tuple { if (!cstate->binary) { - string = OutputFunctionCall(&out_functions[attnum - 1], - value); + string = OutputFunctionCall(out_fmgr, value); if (cstate->csv_mode) - CopyAttributeOutCSV(cstate, string, - cstate->force_quote_flags[attnum - 1], + CopyAttributeOutCSV(cstate, string, force_quot, list_length(cstate->attnumlist) == 1); else CopyAttributeOutText(cstate, string); @@ -1498,8 +1594,7 @@ CopyOneRowTo(CopyState cstate, Oid tuple { bytea *outputbytes; - outputbytes = SendFunctionCall(&out_functions[attnum - 1], - value); + outputbytes = SendFunctionCall(out_fmgr, value); CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); CopySendData(cstate, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); @@ -1633,8 +1728,10 @@ CopyFrom(CopyState cstate) num_defaults; FmgrInfo *in_functions; FmgrInfo oid_in_function; + FmgrInfo seclabel_in_function; Oid *typioparams; Oid oid_typioparam; + Oid seclabel_typioparam; int attnum; int i; Oid in_func_oid; @@ -1737,6 +1834,9 @@ CopyFrom(CopyState cstate) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is a directory", cstate->filename))); + + /* SELinux checks file:{read} permission */ + sepgsqlCheckFileRead(fileno(cstate->copy_file), cstate->filename); } tupDesc = RelationGetDescr(cstate->rel); @@ -1872,6 +1972,18 @@ CopyFrom(CopyState cstate) fmgr_info(in_func_oid, &oid_in_function); } + if (list_member_int(cstate->attnumlist, + SecurityLabelAttributeNumber)) + { + if (!cstate->binary) + getTypeInputInfo(TEXTOID, + &in_func_oid, &seclabel_typioparam); + else + getTypeBinaryInputInfo(TEXTOID, + &in_func_oid, &seclabel_typioparam); + fmgr_info(in_func_oid, &seclabel_in_function); + } + values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); nulls = (char *) palloc(num_phys_attrs * sizeof(char)); @@ -1904,6 +2016,7 @@ CopyFrom(CopyState cstate) { bool skip_tuple; Oid loaded_oid = InvalidOid; + Oid loaded_seclabel = InvalidOid; CHECK_FOR_INTERRUPTS(); @@ -1975,31 +2088,56 @@ CopyFrom(CopyState cstate) /* Loop to read the user attributes on the line. */ foreach(cur, cstate->attnumlist) { + Form_pg_attribute attForm; + Datum dat; int attnum = lfirst_int(cur); int m = attnum - 1; + if (SystemAttributeIsWritable(attnum)) + attForm = SystemAttributeDefinition(attnum, true); + else + attForm = attr[m]; + if (fieldno >= fldct) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("missing data for column \"%s\"", - NameStr(attr[m]->attname)))); + NameStr(attForm->attname)))); string = field_strings[fieldno++]; - if (cstate->csv_mode && string == NULL && - cstate->force_notnull_flags[m]) + cstate->cur_attname = NameStr(attForm->attname); + cstate->cur_attval = string; + + switch (attnum) { - /* Go ahead and read the NULL string */ - string = cstate->null_print; - } + case SecurityLabelAttributeNumber: + if (!string) + break; - cstate->cur_attname = NameStr(attr[m]->attname); - cstate->cur_attval = string; - values[m] = InputFunctionCall(&in_functions[m], - string, - typioparams[m], - attr[m]->atttypmod); - if (string != NULL) - nulls[m] = ' '; + dat = InputFunctionCall(&seclabel_in_function, + string, + seclabel_typioparam, + attForm->atttypmod); + loaded_seclabel + = securityTransSecLabelIn(RelationGetRelid(cstate->rel), + TextDatumGetCString(dat)); + break; + + default: + if (cstate->csv_mode && string == NULL && + cstate->force_notnull_flags[m]) + { + /* Go ahead and read the NULL string */ + string = cstate->null_print; + } + + values[m] = InputFunctionCall(&in_functions[m], + string, + typioparams[m], + attForm->atttypmod); + if (string != NULL) + nulls[m] = ' '; + } cstate->cur_attname = NULL; cstate->cur_attval = NULL; } @@ -2045,18 +2183,42 @@ CopyFrom(CopyState cstate) i = 0; foreach(cur, cstate->attnumlist) { + Form_pg_attribute attForm; + Datum dat; int attnum = lfirst_int(cur); int m = attnum - 1; - cstate->cur_attname = NameStr(attr[m]->attname); + if (SystemAttributeIsWritable(attnum)) + attForm = SystemAttributeDefinition(attnum, false); + else + attForm = attr[m]; + + cstate->cur_attname = NameStr(attForm->attname); i++; - values[m] = CopyReadBinaryAttribute(cstate, - i, - &in_functions[m], - typioparams[m], - attr[m]->atttypmod, - &isnull); - nulls[m] = isnull ? 'n' : ' '; + + switch (attnum) + { + case SecurityLabelAttributeNumber: + dat = CopyReadBinaryAttribute(cstate, i, + &seclabel_in_function, + seclabel_typioparam, + attForm->atttypmod, + &isnull); + if (!isnull) + loaded_seclabel + = securityTransSecLabelIn(RelationGetRelid(cstate->rel), + TextDatumGetCString(dat)); + break; + + default: + values[m] = CopyReadBinaryAttribute(cstate, i, + &in_functions[m], + typioparams[m], + attr[m]->atttypmod, + &isnull); + nulls[m] = isnull ? 'n' : ' '; + break; + } cstate->cur_attname = NULL; } } @@ -2080,6 +2242,9 @@ CopyFrom(CopyState cstate) if (cstate->oids && file_has_oids) HeapTupleSetOid(tuple, loaded_oid); + if (HeapTupleHasSecLabel(tuple)) + HeapTupleSetSecLabel(tuple, loaded_seclabel); + /* Triggers and stuff need to be invoked in query context. */ MemoryContextSwitchTo(oldcontext); @@ -2102,6 +2267,9 @@ CopyFrom(CopyState cstate) } } + if (!skip_tuple && !rowlvHeapTupleInsert(cstate->rel, tuple, false)) + skip_tuple = true; + if (!skip_tuple) { /* Place tuple in tuple slot */ @@ -3366,6 +3534,13 @@ CopyGetAttnums(TupleDesc tupDesc, Relati } if (attnum == InvalidAttrNumber) { + Form_pg_attribute attForm + = SystemAttributeByName(name, tupDesc->tdhasoid); + if (attForm && SystemAttributeIsWritable(attForm->attnum)) + attnum = attForm->attnum; + } + if (attnum == InvalidAttrNumber) + { if (rel != NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), @@ -3413,7 +3588,8 @@ 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-new/src/backend/commands/dbcommands.c --- base/src/backend/commands/dbcommands.c 2008-11-05 09:57:00.000000000 +0900 +++ sepgsql-new/src/backend/commands/dbcommands.c 2009-06-19 15:02:12.000000000 +0900 @@ -40,6 +40,7 @@ #include "miscadmin.h" #include "pgstat.h" #include "postmaster/bgwriter.h" +#include "security/sepgsql.h" #include "storage/freespace.h" #include "storage/ipc.h" #include "storage/procarray.h" @@ -100,6 +101,7 @@ createdb(const CreatedbStmt *stmt) DefElem *dtemplate = NULL; DefElem *dencoding = NULL; DefElem *dconnlimit = NULL; + DefElem *dselabel = NULL; char *dbname = stmt->dbname; char *dbowner = NULL; const char *dbtemplate = NULL; @@ -160,6 +162,14 @@ createdb(const CreatedbStmt *stmt) errmsg("LOCATION is not supported anymore"), errhint("Consider using tablespaces instead."))); } + else if (strcmp(defel->defname, "security_context") == 0) + { + if (dselabel) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dselabel = defel; + } else elog(ERROR, "option \"%s\" not recognized", defel->defname); @@ -433,7 +443,15 @@ createdb(const CreatedbStmt *stmt) new_record, new_record_nulls); HeapTupleSetOid(tuple, dboid); + if (dselabel) + { + Oid secid = sepgsqlGivenSecLabelIn(DatabaseRelationId, dselabel); + if (!HeapTupleHasSecLabel(tuple)) + elog(ERROR, "Unable to assign security label on \"%s\"", + RelationGetRelationName(pg_database_rel)); + HeapTupleSetSecLabel(tuple, secid); + } simple_heap_insert(pg_database_rel, tuple); /* Update indexes */ @@ -1143,6 +1161,61 @@ AlterDatabaseOwner(const char *dbname, O */ } +/* + * ALTER DATABASE name SECURITY_LABEL [=] newlabel + */ +void +AlterDatabaseSecLabel(const char *dbname, DefElem *seclabel) +{ + Relation rel; + HeapTuple tuple, newtup; + ScanKeyData scankey; + SysScanDesc scan; + Oid secid; + bool replaces[Natts_pg_database]; + + /* Translate text representation to security id */ + secid = sepgsqlGivenSecLabelIn(DatabaseRelationId, seclabel); + + /* Fetch the old tuple */ + rel = heap_open(DatabaseRelationId, RowExclusiveLock); + ScanKeyInit(&scankey, + Anum_pg_database_datname, + BTEqualStrategyNumber, F_NAMEEQ, + NameGetDatum(dbname)); + scan = systable_beginscan(rel, DatabaseNameIndexId, true, + SnapshotNow, 1, &scankey); + tuple = systable_getnext(scan); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_DATABASE), + errmsg("database \"%s\" does not exist", dbname))); + + /* check DAC permission */ + if (!pg_database_ownercheck(HeapTupleGetOid(tuple), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname); + /* + * NOTE: we should not use heap_copytuple() here because of + * possibility that the fetched tuple was inserted while + * SE-PostgreSQL is disabled and it does not have a field + * to store security label. + */ + memset(replaces, false, sizeof(replaces)); + newtup = heap_modify_tuple(tuple, RelationGetDescr(rel), + NULL, NULL, replaces); + if (!HeapTupleHasSecLabel(newtup)) + elog(ERROR, "Unable to set security label on tuples in pg_database"); + HeapTupleSetSecLabel(newtup, secid); + + simple_heap_update(rel, &newtup->t_self, newtup); + CatalogUpdateIndexes(rel, newtup); + + heap_freetuple(newtup); + + systable_endscan(scan); + + heap_close(rel, RowExclusiveLock); +} /* * Helper functions diff -rpNU3 base/src/backend/commands/functioncmds.c sepgsql-new/src/backend/commands/functioncmds.c --- base/src/backend/commands/functioncmds.c 2009-03-15 17:47:25.000000000 +0900 +++ sepgsql-new/src/backend/commands/functioncmds.c 2009-06-19 15:02:12.000000000 +0900 @@ -47,6 +47,7 @@ #include "miscadmin.h" #include "parser/parse_func.h" #include "parser/parse_type.h" +#include "security/sepgsql.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" @@ -412,7 +413,8 @@ compute_attributes_sql_style(List *optio bool *security_definer, ArrayType **proconfig, float4 *procost, - float4 *prorows) + float4 *prorows, + Oid *proselabel) { ListCell *option; DefElem *as_item = NULL; @@ -423,6 +425,7 @@ compute_attributes_sql_style(List *optio List *set_items = NIL; DefElem *cost_item = NULL; DefElem *rows_item = NULL; + DefElem *selabel_item = NULL; foreach(option, options) { @@ -444,6 +447,14 @@ compute_attributes_sql_style(List *optio errmsg("conflicting or redundant options"))); language_item = defel; } + else if (strcmp(defel->defname, "security_context") == 0) + { + if (selabel_item) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + selabel_item = defel; + } else if (compute_common_attribute(defel, &volatility_item, &strict_item, @@ -506,6 +517,8 @@ compute_attributes_sql_style(List *optio (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("ROWS must be positive"))); } + if (selabel_item) + *proselabel = sepgsqlGivenSecLabelIn(ProcedureRelationId, selabel_item); } @@ -621,6 +634,7 @@ CreateFunction(CreateFunctionStmt *stmt) ArrayType *proconfig; float4 procost; float4 prorows; + Oid proselabel; HeapTuple languageTuple; Form_pg_language languageStruct; List *as_clause; @@ -642,12 +656,13 @@ CreateFunction(CreateFunctionStmt *stmt) proconfig = NULL; procost = -1; /* indicates not set */ prorows = -1; /* indicates not set */ + proselabel = InvalidOid; /* follows default labeling behavior */ /* override attributes from explicit list */ compute_attributes_sql_style(stmt->options, &as_clause, &language, &volatility, &isStrict, &security, - &proconfig, &procost, &prorows); + &proconfig, &procost, &prorows, &proselabel); /* Convert language name to canonical case */ languageName = case_translate_language_name(language); @@ -801,7 +816,8 @@ CreateFunction(CreateFunctionStmt *stmt) PointerGetDatum(parameterNames), PointerGetDatum(proconfig), procost, - prorows); + prorows, + proselabel); } @@ -1133,6 +1149,58 @@ AlterFunctionOwner_internal(Relation rel } /* + * ALTER FUNCTION name(args,...) SECURITY_LABEL [=] newlabel + */ +void +AlterFunctionSecLabel(List *name, List *argtypes, DefElem *seclabel) +{ + Relation rel; + HeapTuple tuple, newtup; + Oid proid, secid; + bool replaces[Natts_pg_proc]; + + /* Translate text representation to security id */ + secid = sepgsqlGivenSecLabelIn(ProcedureRelationId, seclabel); + + /* open pg_proc system catalog */ + rel = heap_open(ProcedureRelationId, RowExclusiveLock); + + proid = LookupFuncNameTypeNames(name, argtypes, false); + + tuple = SearchSysCache(PROCOID, + ObjectIdGetDatum(proid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for function %u", proid); + + /* DAC permission checks */ + if (!pg_proc_ownercheck(proid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname)); + /* + * NOTE: we should not use heap_copytuple() here because of + * possibility that the fetched tuple was inserted while + * SE-PostgreSQL is disabled and it does not have a field + * to store security label. + */ + memset(replaces, false, sizeof(replaces)); + newtup = heap_modify_tuple(tuple, RelationGetDescr(rel), + NULL, NULL, replaces); + if (!HeapTupleHasSecLabel(newtup)) + elog(ERROR, "Unable to set security label on tuples in pg_proc"); + HeapTupleSetSecLabel(newtup, secid); + + simple_heap_update(rel, &newtup->t_self, newtup); + CatalogUpdateIndexes(rel, newtup); + + ReleaseSysCache(tuple); + + heap_freetuple(newtup); + + heap_close(rel, RowExclusiveLock); +} + +/* * Implements the ALTER FUNCTION utility command (except for the * RENAME and OWNER clauses, which are handled as part of the generic * ALTER framework). diff -rpNU3 base/src/backend/commands/lockcmds.c sepgsql-new/src/backend/commands/lockcmds.c --- base/src/backend/commands/lockcmds.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/commands/lockcmds.c 2009-05-20 17:08:02.000000000 +0900 @@ -18,6 +18,7 @@ #include "catalog/namespace.h" #include "commands/lockcmds.h" #include "miscadmin.h" +#include "security/sepgsql.h" #include "utils/acl.h" #include "utils/lsyscache.h" @@ -59,6 +60,9 @@ LockTableCommand(LockStmt *lockstmt) aclcheck_error(aclresult, ACL_KIND_CLASS, get_rel_name(reloid)); + /* SELinux: check db_table:{lock} permission */ + sepgsqlCheckTableLock(reloid); + if (lockstmt->nowait) rel = relation_open_nowait(reloid, lockstmt->mode); else diff -rpNU3 base/src/backend/commands/proclang.c sepgsql-new/src/backend/commands/proclang.c --- base/src/backend/commands/proclang.c 2008-06-12 22:34:19.000000000 +0900 +++ sepgsql-new/src/backend/commands/proclang.c 2009-03-13 18:14:33.000000000 +0900 @@ -146,7 +146,8 @@ CreateProceduralLanguage(CreatePLangStmt PointerGetDatum(NULL), PointerGetDatum(NULL), 1, - 0); + 0, + InvalidOid); } /* @@ -179,7 +180,8 @@ CreateProceduralLanguage(CreatePLangStmt PointerGetDatum(NULL), PointerGetDatum(NULL), 1, - 0); + 0, + InvalidOid); } } else diff -rpNU3 base/src/backend/commands/schemacmds.c sepgsql-new/src/backend/commands/schemacmds.c --- base/src/backend/commands/schemacmds.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/commands/schemacmds.c 2009-06-19 15:02:12.000000000 +0900 @@ -25,6 +25,7 @@ #include "commands/schemacmds.h" #include "miscadmin.h" #include "parser/parse_utilcmd.h" +#include "security/sepgsql.h" #include "tcop/utility.h" #include "utils/acl.h" #include "utils/builtins.h" @@ -48,6 +49,7 @@ CreateSchemaCommand(CreateSchemaStmt *st ListCell *parsetree_item; Oid owner_uid; Oid saved_uid; + Oid secid = InvalidOid; bool saved_secdefcxt; AclResult aclresult; @@ -93,8 +95,15 @@ CreateSchemaCommand(CreateSchemaStmt *st if (saved_uid != owner_uid) SetUserIdAndContext(owner_uid, true); + /* Explicitly given security label */ + if (stmt->secLabel) + { + secid = sepgsqlGivenSecLabelIn(NamespaceRelationId, + (DefElem *)stmt->secLabel); + } + /* Create the schema's namespace */ - namespaceId = NamespaceCreate(schemaName, owner_uid); + namespaceId = NamespaceCreate(schemaName, owner_uid, secid); /* Advance cmd counter to make the namespace visible */ CommandCounterIncrement(); @@ -412,3 +421,54 @@ AlterSchemaOwner_internal(HeapTuple tup, } } + +/* + * ALTER SCHEMA name SECURITY_LABEL [=] newlabel + */ +void +AlterSchemaSecLabel(const char *name, DefElem *seclabel) +{ + Relation rel; + HeapTuple tuple, newtup; + Oid secid; + bool replaces[Natts_pg_namespace]; + + /* Translate text representation to security id */ + secid = sepgsqlGivenSecLabelIn(NamespaceRelationId, seclabel); + + /* open pg_namespace relation */ + rel = heap_open(NamespaceRelationId, RowExclusiveLock); + tuple = SearchSysCache(NAMESPACENAME, + CStringGetDatum(name), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_SCHEMA), + errmsg("schema \"%s\" does not exist", name))); + + /* DAC permission check */ + if (!pg_namespace_ownercheck(HeapTupleGetOid(tuple), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, + NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname)); + /* + * NOTE: we should not use heap_copytuple() here because of + * possibility that the fetched tuple was inserted while + * SE-PostgreSQL is disabled and it does not have a field + * to store security label. + */ + memset(replaces, false, sizeof(replaces)); + newtup = heap_modify_tuple(tuple, RelationGetDescr(rel), + NULL, NULL, replaces); + if (!HeapTupleHasSecLabel(newtup)) + elog(ERROR, "Unable to set security label on tuples in pg_namespace"); + HeapTupleSetSecLabel(newtup, secid); + + simple_heap_update(rel, &newtup->t_self, newtup); + CatalogUpdateIndexes(rel, newtup); + + heap_freetuple(newtup); + + ReleaseSysCache(tuple); + + heap_close(rel, RowExclusiveLock); +} diff -rpNU3 base/src/backend/commands/sequence.c sepgsql-new/src/backend/commands/sequence.c --- base/src/backend/commands/sequence.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/commands/sequence.c 2009-06-19 15:02:12.000000000 +0900 @@ -25,6 +25,7 @@ #include "commands/tablecmds.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "security/sepgsql.h" #include "storage/proc.h" #include "utils/acl.h" #include "utils/builtins.h" @@ -193,6 +194,7 @@ DefineSequence(CreateSeqStmt *seq) stmt->options = list_make1(defWithOids(false)); stmt->oncommit = ONCOMMIT_NOOP; stmt->tablespacename = NULL; + stmt->secLabel = seq->secLabel; seqoid = DefineRelation(stmt, RELKIND_SEQUENCE); @@ -442,6 +444,9 @@ nextval_internal(Oid relid) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); + /* SELinux check db_sequence:{next_value} */ + sepgsqlCheckSequenceNextValue(elm->relid); + if (elm->last != elm->cached) /* some numbers were cached */ { Assert(elm->last_valid); @@ -637,6 +642,9 @@ currval_oid(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); + /* SELinux check db_sequence:{get_value} */ + sepgsqlCheckSequenceGetValue(elm->relid); + if (!elm->last_valid) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), @@ -681,6 +689,9 @@ lastval(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); + /* SELinux check db_sequence:{get_value} */ + sepgsqlCheckSequenceGetValue(last_used_seq->relid); + result = last_used_seq->last; relation_close(seqrel, NoLock); @@ -717,6 +728,9 @@ do_setval(Oid relid, int64 next, bool is errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); + /* SELinux check db_sequence:{set_value} */ + sepgsqlCheckSequenceSetValue(elm->relid); + /* lock page' buffer and read tuple */ seq = read_info(elm, seqrel, &buf); diff -rpNU3 base/src/backend/commands/tablecmds.c sepgsql-new/src/backend/commands/tablecmds.c --- base/src/backend/commands/tablecmds.c 2008-11-05 09:57:00.000000000 +0900 +++ sepgsql-new/src/backend/commands/tablecmds.c 2009-06-19 15:02:12.000000000 +0900 @@ -57,6 +57,7 @@ #include "parser/parser.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" +#include "security/sepgsql.h" #include "storage/smgr.h" #include "utils/acl.h" #include "utils/builtins.h" @@ -281,6 +282,7 @@ DefineRelation(CreateStmt *stmt, char re TupleDesc descriptor; List *inheritOids; List *old_constraints; + List *selblList; bool localHasOids; int parentOidCount; List *rawDefaults; @@ -365,6 +367,10 @@ DefineRelation(CreateStmt *stmt, char re schema = MergeAttributes(schema, stmt->inhRelations, stmt->relation->istemp, &inheritOids, &old_constraints, &parentOidCount); + /* + * SELinux: fetch SECURITY_CONTEXT = '...' from CREATE TABLE statement + */ + selblList = sepgsqlParseCreateStmtSecLabelIn(stmt); /* * Create a relation descriptor from the relation schema and create the @@ -422,6 +428,11 @@ DefineRelation(CreateStmt *stmt, char re } } + /* + * SELinux: fetch SECURITY_LABEL = '...' from CREATE TABLE + */ + selblList = sepgsqlParseCreateStmtSecLabelIn(stmt); + relationId = heap_create_with_catalog(relname, namespaceId, tablespaceId, @@ -434,7 +445,8 @@ DefineRelation(CreateStmt *stmt, char re parentOidCount, stmt->oncommit, reloptions, - allowSystemTableMods); + allowSystemTableMods, + selblList); StoreCatalogInheritance(relationId, inheritOids); @@ -660,6 +672,9 @@ truncate_check_rel(Relation rel) errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(rel)))); + /* SELinux: check db_table:{delete} permission */ + sepgsqlCheckTableTruncate(rel); + /* * We can never allow truncation of shared or nailed-in-cache relations, * because we can't support changing their relfilenode values. @@ -2591,11 +2606,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oi if (newrel) { Oid tupOid = InvalidOid; + Oid tupSecLabel = InvalidOid; /* Extract data from old tuple */ heap_deform_tuple(tuple, oldTupDesc, values, isnull); if (oldTupDesc->tdhasoid) tupOid = HeapTupleGetOid(tuple); + if (HeapTupleHasSecLabel(tuple)) + tupSecLabel = HeapTupleGetSecLabel(tuple); /* Set dropped attributes to null in new tuple */ foreach(lc, dropped_attrs) @@ -2627,6 +2645,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oi /* Preserve OID, if any */ if (newTupDesc->tdhasoid) HeapTupleSetOid(tuple, tupOid); + /* Preserve SecLabel, if any */ + if (HeapTupleHasSecLabel(tuple)) + HeapTupleSetSecLabel(tuple, tupSecLabel); } /* Now check any constraints on the possibly-changed tuple */ @@ -3113,6 +3134,7 @@ ATExecAddColumn(AlteredTableInfo *tab, R attributeTuple = heap_addheader(Natts_pg_attribute, false, + RelationGetDescr(attrdesc)->tdhasseclabel, ATTRIBUTE_TUPLE_SIZE, (void *) &attributeD); @@ -4058,6 +4080,10 @@ ATAddForeignKeyConstraint(AlteredTableIn opclasses); } + /* SELinux: check db_table/db_column:{reference} permission */ + sepgsqlCheckTableReference(pkrel, pkattnum, numpks); + sepgsqlCheckTableReference(rel, fkattnum, numfks); + /* * Look up the equality operators to use in the constraint. * @@ -6776,6 +6802,128 @@ AlterSeqNamespaces(Relation classRel, Re relation_close(depRel, AccessShareLock); } +/* + * ALTER TABLE/SEQUENCE name SECURITY_LABEL [=] newlabel + * ALTER TABLE/SEQUENCE name ALTER column SECURITY_LABEL [=] newlabel + */ +static void +ExecRelationSetSecLabel(Oid relid, DefElem *seclabel) +{ + Relation rel; + HeapTuple tuple, newtup; + Oid secid; + bool replaces[Natts_pg_class]; + + /* Translate text representation to security id */ + secid = sepgsqlGivenSecLabelIn(RelationRelationId, seclabel); + + rel = heap_open(RelationRelationId, RowExclusiveLock); + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation: %u", relid); + + memset(replaces, false, sizeof(replaces)); + newtup = heap_modify_tuple(tuple, RelationGetDescr(rel), + NULL, NULL, replaces); + + if (!HeapTupleHasSecLabel(newtup)) + elog(ERROR, "Unable to set security label on: %s", + NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname)); + HeapTupleSetSecLabel(newtup, secid); + + simple_heap_update(rel, &newtup->t_self, newtup); + + CatalogUpdateIndexes(rel, newtup); + + ReleaseSysCache(tuple); + + heap_close(rel, RowExclusiveLock); +} + +static void +ExecAttributeSetSecLabel(Oid relid, const char *attname, DefElem *seclabel) +{ + Relation rel; + HeapTuple tuple, newtup; + Oid secid; + bool replaces[Natts_pg_class]; + + /* Translate text representation to security id */ + secid = sepgsqlGivenSecLabelIn(AttributeRelationId, seclabel); + + rel = heap_open(AttributeRelationId, RowExclusiveLock); + tuple = SearchSysCacheAttName(relid, attname); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + attname, get_rel_name(relid)))); + + memset(replaces, false, sizeof(replaces)); + newtup = heap_modify_tuple(tuple, RelationGetDescr(rel), + NULL, NULL, replaces); + if (!HeapTupleHasSecLabel(newtup)) + elog(ERROR, "Unable to set security label on: %s", + NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname)); + HeapTupleSetSecLabel(newtup, secid); + + simple_heap_update(rel, &newtup->t_self, newtup); + + CatalogUpdateIndexes(rel, newtup); + + ReleaseSysCache(tuple); + + heap_close(rel, RowExclusiveLock); +} + +void +AlterRelationSecLabel(RangeVar *relation, const char *attname, + ObjectType objtype, DefElem *seclabel) +{ + Form_pg_class clsForm; + HeapTuple tuple; + Oid relid; + + /* Check relation type against type specified in the ALTER command */ + relid = RangeVarGetRelid(relation, false); + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", relid); + clsForm = (Form_pg_class) GETSTRUCT(tuple); + + switch (objtype) + { + case OBJECT_TABLE: + case OBJECT_COLUMN: + if (clsForm->relkind != RELKIND_RELATION) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table", + NameStr(clsForm->relname)))); + break; + case OBJECT_SEQUENCE: + if (clsForm->relkind != RELKIND_SEQUENCE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a sequence", + NameStr(clsForm->relname)))); + break; + default: + elog(ERROR, "unrecognized object type: %d", (int)objtype); + break; + } + ReleaseSysCache(tuple); + + /* Exec set security label */ + if (objtype != OBJECT_COLUMN) + ExecRelationSetSecLabel(relid, seclabel); + else + ExecAttributeSetSecLabel(relid, attname, seclabel); +} /* * This code supports diff -rpNU3 base/src/backend/executor/execJunk.c sepgsql-new/src/backend/executor/execJunk.c --- base/src/backend/executor/execJunk.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/executor/execJunk.c 2009-03-13 18:14:33.000000000 +0900 @@ -60,7 +60,7 @@ * An optional resultSlot can be passed as well. */ JunkFilter * -ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) +ExecInitJunkFilter(List *targetList, bool hasoid, bool hasseclabel, TupleTableSlot *slot) { JunkFilter *junkfilter; TupleDesc cleanTupType; @@ -72,7 +72,7 @@ ExecInitJunkFilter(List *targetList, boo /* * Compute the tuple descriptor for the cleaned tuple. */ - cleanTupType = ExecCleanTypeFromTL(targetList, hasoid); + cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, hasseclabel); /* * Use the given slot, or make a new slot if we weren't given one. diff -rpNU3 base/src/backend/executor/execMain.c sepgsql-new/src/backend/executor/execMain.c --- base/src/backend/executor/execMain.c 2008-09-25 15:09:40.000000000 +0900 +++ sepgsql-new/src/backend/executor/execMain.c 2009-06-10 21:37:54.000000000 +0900 @@ -38,6 +38,7 @@ #include "access/xact.h" #include "catalog/heap.h" #include "catalog/namespace.h" +#include "catalog/pg_security.h" #include "catalog/toasting.h" #include "commands/tablespace.h" #include "commands/trigger.h" @@ -49,6 +50,8 @@ #include "parser/parse_clause.h" #include "parser/parse_expr.h" #include "parser/parsetree.h" +#include "security/rowlevel.h" +#include "security/sepgsql.h" #include "storage/smgr.h" #include "utils/acl.h" #include "utils/builtins.h" @@ -383,7 +386,10 @@ ExecCheckRTPerms(List *rangeTable) foreach(l, rangeTable) { - ExecCheckRTEPerms((RangeTblEntry *) lfirst(l)); + RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + + ExecCheckRTEPerms(rte); + sepgsqlCheckRTEPerms(rte); } } @@ -410,6 +416,13 @@ ExecCheckRTEPerms(RangeTblEntry *rte) * No work if requiredPerms is empty. */ requiredPerms = rte->requiredPerms; + + /* fixup ACL_SELECT_FOR_UPDATE */ + if (requiredPerms & ACL_SELECT_FOR_UPDATE) + { + requiredPerms &= ~ACL_SELECT_FOR_UPDATE; + requiredPerms |= ACL_UPDATE; + } if (requiredPerms == 0) return; @@ -738,15 +751,16 @@ InitPlan(QueryDesc *queryDesc, int eflag for (i = 0; i < as_nplans; i++) { PlanState *subplan = appendplans[i]; + Relation resultRel = resultRelInfo->ri_RelationDesc; JunkFilter *j; if (operation == CMD_UPDATE) - ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, - subplan->plan->targetlist); + ExecCheckPlanOutput(resultRel, subplan->plan->targetlist); j = ExecInitJunkFilter(subplan->plan->targetlist, - resultRelInfo->ri_RelationDesc->rd_att->tdhasoid, - ExecAllocTableSlot(estate->es_tupleTable)); + RelationGetDescr(resultRel)->tdhasoid, + RelationGetDescr(resultRel)->tdhasseclabel, + ExecAllocTableSlot(estate->es_tupleTable)); /* * Since it must be UPDATE/DELETE, there had better be a @@ -790,6 +804,7 @@ InitPlan(QueryDesc *queryDesc, int eflag j = ExecInitJunkFilter(planstate->plan->targetlist, tupType->tdhasoid, + tupType->tdhasseclabel, ExecAllocTableSlot(estate->es_tupleTable)); estate->es_junkFilter = j; if (estate->es_result_relation_info) @@ -848,7 +863,7 @@ InitPlan(QueryDesc *queryDesc, int eflag * We assume all the sublists will generate the same output tupdesc. */ tupType = ExecTypeFromTL((List *) linitial(plannedstmt->returningLists), - false); + false, false); /* Set up a slot for the output of the RETURNING projection(s) */ slot = ExecAllocTableSlot(estate->es_tupleTable); @@ -1171,6 +1186,33 @@ ExecContextForcesOids(PlanState *plansta return false; } +/* + * ExecContextForcesSecLabel + * + * We need to ensure that result tuples have space for security label, + * if the security feature need to store it within the given relation. + */ +bool ExecContextForcesSecLabel(PlanState *planstate, bool *hassecurity) +{ + if (planstate->state->es_select_into) + { + *hassecurity = securityTupleDescHasSecLabel(NULL); + return true; + } + else + { + ResultRelInfo *ri = planstate->state->es_result_relation_info; + + if (ri && ri->ri_RelationDesc) + { + *hassecurity = securityTupleDescHasSecLabel(ri->ri_RelationDesc); + return true; + } + } + + return false; +} + /* ---------------------------------------------------------------- * ExecEndPlan * @@ -1251,6 +1293,58 @@ ExecEndPlan(PlanState *planstate, EState } } +/* + * fetchWritableSystemAttribute() fetches writable system column data + * using Junkfilter, and saves them at TupleTableSlot temporary. + * + * storeWritableSystemAttribute() copies these fetched data into + * header structure of HeapTuple. + */ +static void +fetchWritableSystemAttribute(JunkFilter *junkfilter, TupleTableSlot *slot, + Datum *tts_seclabel) +{ + AttrNumber attno; + Datum datum; + bool isnull; + + /* for Security Label */ + attno = ExecFindJunkAttribute(junkfilter, SecurityLabelAttributeName); + if (attno != InvalidAttrNumber) + { + datum = ExecGetJunkAttribute(slot, attno, &isnull); + if (isnull) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("setting NULL on \"%s\" system column is not supported", + SecurityLabelAttributeName))); + *tts_seclabel = datum; + } +} + +static void +storeWritableSystemAttribute(Relation rel, TupleTableSlot *slot, HeapTuple tuple) +{ + Oid relid = RelationGetRelid(rel); + Oid secid; + + /* "security_label" */ + if (DatumGetPointer(slot->tts_seclabel) != NULL) + { + char *seclabel = TextDatumGetCString(slot->tts_seclabel); + + if (!HeapTupleHasSecLabel(tuple)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Unable to assign security label on \"%s\"", + RelationGetRelationName(rel)))); + secid = securityTransSecLabelIn(relid, seclabel); + HeapTupleSetSecLabel(tuple, secid); + } + else if (HeapTupleHasSecLabel(tuple)) + HeapTupleSetSecLabel(tuple, InvalidOid); +} + /* ---------------------------------------------------------------- * ExecutePlan * @@ -1318,6 +1412,8 @@ ExecutePlan(EState *estate, for (;;) { + Datum tts_seclabel = PointerGetDatum(NULL); + /* Reset the per-output-tuple exprcontext */ ResetPerTupleExprContext(estate); @@ -1442,6 +1538,11 @@ lnext: ; } /* + * extract writable system attribute + */ + fetchWritableSystemAttribute(junkfilter, slot, &tts_seclabel); + + /* * extract the 'ctid' junk attribute. */ if (operation == CMD_UPDATE || operation == CMD_DELETE) @@ -1468,6 +1569,7 @@ lnext: ; if (operation != CMD_DELETE) slot = ExecFilterJunk(junkfilter, slot); } + slot->tts_seclabel = tts_seclabel; /* * now that we have a tuple, do the appropriate thing with it.. either @@ -1588,6 +1690,8 @@ ExecInsert(TupleTableSlot *slot, resultRelInfo = estate->es_result_relation_info; resultRelationDesc = resultRelInfo->ri_RelationDesc; + storeWritableSystemAttribute(resultRelationDesc, slot, tuple); + /* BEFORE ROW INSERT Triggers */ if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) @@ -1624,6 +1728,12 @@ ExecInsert(TupleTableSlot *slot, ExecConstraints(resultRelInfo, slot, estate); /* + * Check row-level permission on the tuple + */ + if (!rowlvHeapTupleInsert(resultRelationDesc, tuple, false)) + return; + + /* * insert the tuple * * Note: heap_insert returns the tid (location) of the new tuple in the @@ -1691,6 +1801,12 @@ ExecDelete(ItemPointer tupleid, } /* + * Check row-level permission on the tuple + */ + if (!rowlvHeapTupleDelete(resultRelationDesc, tupleid, false)) + return; + + /* * delete the tuple * * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check that @@ -1826,6 +1942,8 @@ ExecUpdate(TupleTableSlot *slot, resultRelInfo = estate->es_result_relation_info; resultRelationDesc = resultRelInfo->ri_RelationDesc; + storeWritableSystemAttribute(resultRelationDesc, slot, tuple); + /* BEFORE ROW UPDATE Triggers */ if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) @@ -1870,6 +1988,12 @@ lreplace:; ExecConstraints(resultRelInfo, slot, estate); /* + * Check row-level permission on the tuple + */ + if (!rowlvHeapTupleUpdate(resultRelationDesc, tupleid, tuple, false)) + return; + + /* * replace the heap tuple * * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check that @@ -2733,7 +2857,8 @@ OpenIntoRel(QueryDesc *queryDesc) 0, into->onCommit, reloptions, - allowSystemTableMods); + allowSystemTableMods, + NIL); FreeTupleDesc(tupdesc); @@ -2839,6 +2964,10 @@ intorel_receive(TupleTableSlot *slot, De tuple = ExecCopySlotTuple(slot); + storeWritableSystemAttribute(estate->es_into_relation_descriptor, slot, tuple); + if (!rowlvHeapTupleInsert(estate->es_into_relation_descriptor, tuple, false)) + return; + heap_insert(estate->es_into_relation_descriptor, tuple, estate->es_output_cid, diff -rpNU3 base/src/backend/executor/execScan.c sepgsql-new/src/backend/executor/execScan.c --- base/src/backend/executor/execScan.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/executor/execScan.c 2009-05-20 17:08:02.000000000 +0900 @@ -20,6 +20,7 @@ #include "executor/executor.h" #include "miscadmin.h" +#include "security/rowlevel.h" #include "utils/memutils.h" @@ -53,6 +54,7 @@ ExecScan(ScanState *node, ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; + Scan *scan = (Scan *)node->ps.plan; /* * Fetch data from node @@ -64,7 +66,7 @@ ExecScan(ScanState *node, * If we have neither a qual to check nor a projection to do, just skip * all the overhead and return the raw scan tuple. */ - if (!qual && !projInfo) + if (!qual && !projInfo && !scan->rowlvPerms) return (*accessMtd) (node); /* @@ -128,9 +130,18 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (rowlvExecScanFilter(scan, node->ss_currentRelation, slot) + && (!qual || ExecQual(qual, econtext, false))) { /* + * NOTE: On FK checks, the Row-level feature needs to raise + * an error after evaluation of all the given quals to avoid + * incorrect error reporting. We assume FK implementation + * does not use malicious functions as the quals. + */ + rowlvExecScanAbort(scan, node->ss_currentRelation, slot); + + /* * Found a satisfactory scan tuple. */ if (projInfo) @@ -197,6 +208,7 @@ tlist_matches_tupdesc(PlanState *ps, Lis int numattrs = tupdesc->natts; int attrno; bool hasoid; + bool hasseclabel; ListCell *tlist_item = list_head(tlist); /* Check the tlist attributes */ @@ -240,12 +252,17 @@ tlist_matches_tupdesc(PlanState *ps, Lis return false; /* tlist too long */ /* - * If the plan context requires a particular hasoid setting, then that has + * If the plan context requires a particular hasoid, hasseclabel + * setting, then that has * to match, too. */ if (ExecContextForcesOids(ps, &hasoid) && hasoid != tupdesc->tdhasoid) return false; + if (ExecContextForcesSecLabel(ps, &hasseclabel) && + hasseclabel != tupdesc->tdhasseclabel) + return false; + return true; } diff -rpNU3 base/src/backend/executor/execTuples.c sepgsql-new/src/backend/executor/execTuples.c --- base/src/backend/executor/execTuples.c 2009-09-10 20:11:35.000000000 +0900 +++ sepgsql-new/src/backend/executor/execTuples.c 2009-09-10 20:24:37.000000000 +0900 @@ -100,7 +100,7 @@ static TupleDesc ExecTypeFromTLInternal(List *targetList, - bool hasoid, bool skipjunk); + bool hasoid, bool hasseclabel, bool skipjunk); /* ---------------------------------------------------------------- @@ -941,9 +941,9 @@ ExecInitNullTupleSlot(EState *estate, Tu * ---------------------------------------------------------------- */ TupleDesc -ExecTypeFromTL(List *targetList, bool hasoid) +ExecTypeFromTL(List *targetList, bool hasoid, bool hasseclabel) { - return ExecTypeFromTLInternal(targetList, hasoid, false); + return ExecTypeFromTLInternal(targetList, hasoid, hasseclabel, false); } /* ---------------------------------------------------------------- @@ -953,13 +953,13 @@ ExecTypeFromTL(List *targetList, bool ha * ---------------------------------------------------------------- */ TupleDesc -ExecCleanTypeFromTL(List *targetList, bool hasoid) +ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hasseclabel) { - return ExecTypeFromTLInternal(targetList, hasoid, true); + return ExecTypeFromTLInternal(targetList, hasoid, hasseclabel, true); } static TupleDesc -ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) +ExecTypeFromTLInternal(List *targetList, bool hasoid, bool hasseclabel, bool skipjunk) { TupleDesc typeInfo; ListCell *l; @@ -971,6 +971,7 @@ ExecTypeFromTLInternal(List *targetList, else len = ExecTargetListLength(targetList); typeInfo = CreateTemplateTupleDesc(len, hasoid); + typeInfo->tdhasseclabel = hasseclabel; foreach(l, targetList) { diff -rpNU3 base/src/backend/executor/execUtils.c sepgsql-new/src/backend/executor/execUtils.c --- base/src/backend/executor/execUtils.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/executor/execUtils.c 2009-03-13 18:14:33.000000000 +0900 @@ -506,6 +506,7 @@ void ExecAssignResultTypeFromTL(PlanState *planstate) { bool hasoid; + bool hasseclabel; TupleDesc tupDesc; if (ExecContextForcesOids(planstate, &hasoid)) @@ -518,12 +519,16 @@ ExecAssignResultTypeFromTL(PlanState *pl hasoid = false; } + if (!ExecContextForcesSecLabel(planstate, &hasseclabel)) + hasseclabel = false; + /* * ExecTypeFromTL needs the parse-time representation of the tlist, not a * list of ExprStates. This is good because some plan nodes don't bother * to set up planstate->targetlist ... */ - tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid); + tupDesc = ExecTypeFromTL(planstate->plan->targetlist, + hasoid, hasseclabel); ExecAssignResultType(planstate, tupDesc); } diff -rpNU3 base/src/backend/executor/functions.c sepgsql-new/src/backend/executor/functions.c --- base/src/backend/executor/functions.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/executor/functions.c 2009-03-13 18:14:33.000000000 +0900 @@ -995,7 +995,7 @@ check_sql_fn_retval(Oid func_id, Oid ret * what the caller expects will happen at runtime. */ if (junkFilter) - *junkFilter = ExecInitJunkFilter(tlist, false, NULL); + *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); return true; } Assert(tupdesc); diff -rpNU3 base/src/backend/executor/nodeSubplan.c sepgsql-new/src/backend/executor/nodeSubplan.c --- base/src/backend/executor/nodeSubplan.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/executor/nodeSubplan.c 2009-03-13 18:14:33.000000000 +0900 @@ -855,7 +855,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanSt * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. */ - tupDesc = ExecTypeFromTL(leftptlist, false); + tupDesc = ExecTypeFromTL(leftptlist, false, false); slot = ExecAllocTableSlot(tupTable); ExecSetSlotDescriptor(slot, tupDesc); sstate->projLeft = ExecBuildProjectionInfo(lefttlist, @@ -863,7 +863,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanSt slot, NULL); - tupDesc = ExecTypeFromTL(rightptlist, false); + tupDesc = ExecTypeFromTL(rightptlist, false, false); slot = ExecAllocTableSlot(tupTable); ExecSetSlotDescriptor(slot, tupDesc); sstate->projRight = ExecBuildProjectionInfo(righttlist, diff -rpNU3 base/src/backend/executor/spi.c sepgsql-new/src/backend/executor/spi.c --- base/src/backend/executor/spi.c 2009-02-02 11:47:17.000000000 +0900 +++ sepgsql-new/src/backend/executor/spi.c 2009-03-13 18:14:33.000000000 +0900 @@ -631,6 +631,8 @@ SPI_modifytuple(Relation rel, HeapTuple mtuple->t_tableOid = tuple->t_tableOid; if (rel->rd_att->tdhasoid) HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple)); + if (HeapTupleHasSecLabel(mtuple)) + HeapTupleSetSecLabel(mtuple, HeapTupleGetSecLabel(tuple)); } else { diff -rpNU3 base/src/backend/libpq/be-fsstubs.c sepgsql-new/src/backend/libpq/be-fsstubs.c --- base/src/backend/libpq/be-fsstubs.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/libpq/be-fsstubs.c 2009-06-10 21:37:54.000000000 +0900 @@ -42,11 +42,15 @@ #include #include +#include "catalog/pg_largeobject.h" +#include "catalog/pg_security.h" #include "libpq/be-fsstubs.h" #include "libpq/libpq-fs.h" #include "miscadmin.h" +#include "security/sepgsql.h" #include "storage/fd.h" #include "storage/large_object.h" +#include "utils/builtins.h" #include "utils/memutils.h" @@ -153,6 +157,10 @@ lo_read(int fd, char *buf, int len) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("invalid large-object descriptor: %d", fd))); + /* + * SELinux: check db_blob:{read} + */ + sepgsqlCheckBlobRead(cookies[fd]); status = inv_read(cookies[fd], buf, len); @@ -175,6 +183,11 @@ lo_write(int fd, const char *buf, int le errmsg("large object descriptor %d was not opened for writing", fd))); + /* + * SELinux: check db_blob:{write} permission + */ + sepgsqlCheckBlobWrite(cookies[fd]); + status = inv_write(cookies[fd], buf, len); return status; @@ -363,6 +376,11 @@ lo_import(PG_FUNCTION_ARGS) */ lobj = inv_open(lobjOid, INV_WRITE, fscxt); + /* + * SELinux: check db_blob:{write import} and file:{read} permission + */ + sepgsqlCheckBlobImport(lobj, FileRawDescriptor(fd), fnamebuf); + while ((nbytes = FileRead(fd, buf, BUFSIZE)) > 0) { tmp = inv_write(lobj, buf, nbytes); @@ -435,6 +453,11 @@ lo_export(PG_FUNCTION_ARGS) fnamebuf))); /* + * SELinux: check db_blob:{read export} and file:{write} + */ + sepgsqlCheckBlobExport(lobj, FileRawDescriptor(fd), fnamebuf); + + /* * read in from the inversion file and write to the filesystem */ while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0) @@ -468,12 +491,66 @@ lo_truncate(PG_FUNCTION_ARGS) (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("invalid large-object descriptor: %d", fd))); + /* + * SELinux: check db_blob:{write} permission + */ + sepgsqlCheckBlobWrite(cookies[fd]); + inv_truncate(cookies[fd], len); PG_RETURN_INT32(0); } /* + * lo_get_seclabel + * get a security label of large object + */ +Datum +lo_get_security(PG_FUNCTION_ARGS) +{ + Oid loid = PG_GETARG_OID(0); + Oid secid; + char *seclabel; + + secid = inv_get_security(loid); + seclabel = securityTransSecLabelOut(LargeObjectRelationId, secid); + + return CStringGetTextDatum(seclabel); +} + +/* + * lo_set_seclabel + * set a security label of large object + */ +Datum +lo_set_security(PG_FUNCTION_ARGS) +{ + Oid loid = PG_GETARG_OID(0); + char *seclabel = TextDatumGetCString(PG_GETARG_DATUM(1)); + Oid secid; + + secid = securityTransSecLabelIn(LargeObjectRelationId, seclabel); + + inv_set_security(loid, secid); + + /* + * Also on memory caches to be updated + */ + if (fscxt != NULL) + { + int i; + + for (i = 0; i < cookies_size; i++) + { + if (cookies[i] != NULL && cookies[i]->id == loid) + cookies[i]->secid = secid; + } + } + + PG_RETURN_BOOL(true); +} + +/* * AtEOXact_LargeObject - * prepares large objects for transaction commit */ diff -rpNU3 base/src/backend/nodes/copyfuncs.c sepgsql-new/src/backend/nodes/copyfuncs.c --- base/src/backend/nodes/copyfuncs.c 2009-09-10 20:11:35.000000000 +0900 +++ sepgsql-new/src/backend/nodes/copyfuncs.c 2009-09-10 20:24:37.000000000 +0900 @@ -226,6 +226,7 @@ CopyScanFields(Scan *from, Scan *newnode CopyPlanFields((Plan *) from, (Plan *) newnode); COPY_SCALAR_FIELD(scanrelid); + COPY_SCALAR_FIELD(rowlvPerms); } /* @@ -1524,6 +1525,8 @@ _copyRangeTblEntry(RangeTblEntry *from) COPY_SCALAR_FIELD(inFromCl); COPY_SCALAR_FIELD(requiredPerms); COPY_SCALAR_FIELD(checkAsUser); + COPY_BITMAPSET_FIELD(selectedCols); + COPY_BITMAPSET_FIELD(modifiedCols); return newnode; } @@ -1790,6 +1793,7 @@ _copyColumnDef(ColumnDef *from) COPY_NODE_FIELD(raw_default); COPY_STRING_FIELD(cooked_default); COPY_NODE_FIELD(constraints); + COPY_NODE_FIELD(secLabel); return newnode; } @@ -2106,6 +2110,7 @@ _copyCreateStmt(CreateStmt *from) COPY_NODE_FIELD(options); COPY_SCALAR_FIELD(oncommit); COPY_STRING_FIELD(tablespacename); + COPY_NODE_FIELD(secLabel); return newnode; } @@ -2328,6 +2333,21 @@ _copyAlterOwnerStmt(AlterOwnerStmt *from return newnode; } +static AlterSecLabelStmt * +_copyAlterSecLabelStmt(AlterSecLabelStmt *from) +{ + AlterSecLabelStmt *newnode = makeNode(AlterSecLabelStmt); + + COPY_SCALAR_FIELD(objectType); + COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(object); + COPY_NODE_FIELD(objarg); + COPY_STRING_FIELD(subname); + COPY_NODE_FIELD(secLabel); + + return newnode; +} + static RuleStmt * _copyRuleStmt(RuleStmt *from) { @@ -3372,6 +3392,9 @@ copyObject(void *from) case T_AlterOwnerStmt: retval = _copyAlterOwnerStmt(from); break; + case T_AlterSecLabelStmt: + retval = _copyAlterSecLabelStmt(from); + break; case T_RuleStmt: retval = _copyRuleStmt(from); break; diff -rpNU3 base/src/backend/nodes/equalfuncs.c sepgsql-new/src/backend/nodes/equalfuncs.c --- base/src/backend/nodes/equalfuncs.c 2009-09-10 20:11:35.000000000 +0900 +++ sepgsql-new/src/backend/nodes/equalfuncs.c 2009-09-10 20:24:37.000000000 +0900 @@ -971,6 +971,7 @@ _equalCreateStmt(CreateStmt *a, CreateSt COMPARE_NODE_FIELD(options); COMPARE_SCALAR_FIELD(oncommit); COMPARE_STRING_FIELD(tablespacename); + COMPARE_NODE_FIELD(secLabel); return true; } @@ -1162,6 +1163,19 @@ _equalAlterOwnerStmt(AlterOwnerStmt *a, } static bool +_equalAlterSecLabelStmt(AlterSecLabelStmt *a, AlterSecLabelStmt *b) +{ + COMPARE_SCALAR_FIELD(objectType); + COMPARE_NODE_FIELD(relation); + COMPARE_NODE_FIELD(object); + COMPARE_NODE_FIELD(objarg); + COMPARE_STRING_FIELD(subname); + COMPARE_NODE_FIELD(secLabel); + + return true; +} + +static bool _equalRuleStmt(RuleStmt *a, RuleStmt *b) { COMPARE_NODE_FIELD(relation); @@ -1819,6 +1833,7 @@ _equalColumnDef(ColumnDef *a, ColumnDef COMPARE_NODE_FIELD(raw_default); COMPARE_STRING_FIELD(cooked_default); COMPARE_NODE_FIELD(constraints); + COMPARE_NODE_FIELD(secLabel); return true; } @@ -1874,6 +1889,8 @@ _equalRangeTblEntry(RangeTblEntry *a, Ra COMPARE_SCALAR_FIELD(inFromCl); COMPARE_SCALAR_FIELD(requiredPerms); COMPARE_SCALAR_FIELD(checkAsUser); + COMPARE_BITMAPSET_FIELD(selectedCols); + COMPARE_BITMAPSET_FIELD(modifiedCols); return true; } @@ -2298,6 +2315,9 @@ equal(void *a, void *b) case T_AlterOwnerStmt: retval = _equalAlterOwnerStmt(a, b); break; + case T_AlterSecLabelStmt: + retval = _equalAlterSecLabelStmt(a, b); + break; case T_RuleStmt: retval = _equalRuleStmt(a, b); break; diff -rpNU3 base/src/backend/nodes/outfuncs.c sepgsql-new/src/backend/nodes/outfuncs.c --- base/src/backend/nodes/outfuncs.c 2009-09-10 20:11:35.000000000 +0900 +++ sepgsql-new/src/backend/nodes/outfuncs.c 2009-09-10 20:24:37.000000000 +0900 @@ -282,6 +282,7 @@ _outScanInfo(StringInfo str, Scan *node) _outPlanInfo(str, (Plan *) node); WRITE_UINT_FIELD(scanrelid); + WRITE_UINT_FIELD(rowlvPerms); } /* @@ -1376,6 +1377,7 @@ _outRelOptInfo(StringInfo str, RelOptInf WRITE_BOOL_FIELD(has_eclass_joins); WRITE_BITMAPSET_FIELD(index_outer_relids); WRITE_NODE_FIELD(index_inner_paths); + WRITE_UINT_FIELD(rowlvPerms); } static void @@ -1546,6 +1548,7 @@ _outCreateStmt(StringInfo str, CreateStm WRITE_NODE_FIELD(options); WRITE_ENUM_FIELD(oncommit, OnCommitAction); WRITE_STRING_FIELD(tablespacename); + WRITE_NODE_FIELD(secLabel); } static void @@ -1661,6 +1664,7 @@ _outColumnDef(StringInfo str, ColumnDef WRITE_NODE_FIELD(raw_default); WRITE_STRING_FIELD(cooked_default); WRITE_NODE_FIELD(constraints); + WRITE_NODE_FIELD(secLabel); } static void @@ -1835,6 +1839,8 @@ _outRangeTblEntry(StringInfo str, RangeT WRITE_BOOL_FIELD(inFromCl); WRITE_UINT_FIELD(requiredPerms); WRITE_OID_FIELD(checkAsUser); + WRITE_BITMAPSET_FIELD(selectedCols); + WRITE_BITMAPSET_FIELD(modifiedCols); } static void diff -rpNU3 base/src/backend/nodes/readfuncs.c sepgsql-new/src/backend/nodes/readfuncs.c --- base/src/backend/nodes/readfuncs.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/nodes/readfuncs.c 2009-03-13 13:09:47.000000000 +0900 @@ -102,6 +102,10 @@ token = pg_strtok(&length); /* skip :fldname */ \ local_node->fldname = nodeRead(NULL, 0) +#define READ_BITMAPSET_FIELD(fldname) \ + token = pg_strtok(&length); /* skip :fldname */ \ + local_node->fldname = _readBitmapset() + /* Routine exit */ #define READ_DONE() \ return local_node @@ -125,6 +129,46 @@ static Datum readDatum(bool typbyval); +/* + * _readBitmapset + */ +static Bitmapset * +_readBitmapset(void) +{ + Bitmapset *result = NULL; + READ_TEMP_LOCALS(); + + token = pg_strtok(&length); + if (token == NULL) + elog(ERROR, "incomplete Bitmapset structure"); + if (length != 1 || token[0] != '(') + elog(ERROR, "unrecognized token: \"%.*s\"", length, token); + + token = pg_strtok(&length); + if (token == NULL) + elog(ERROR, "incomplete Bitmapset structure"); + if (length != 1 || token[0] != 'b') + elog(ERROR, "unrecognized token: \"%.*s\"", length, token); + + for (;;) + { + int val; + char *endptr; + + token = pg_strtok(&length); + if (token == NULL) + elog(ERROR, "unterminated Bitmapset structure"); + if (length == 1 && token[0] == ')') + break; + val = (int) strtol(token, &endptr, 10); + if (endptr != token + length) + elog(ERROR, "unrecognized integer: \"%.*s\"", length, token); + result = bms_add_member(result, val); + } + + return result; +} + /* * _readQuery @@ -1003,6 +1047,8 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(inFromCl); READ_UINT_FIELD(requiredPerms); READ_OID_FIELD(checkAsUser); + READ_BITMAPSET_FIELD(selectedCols); + READ_BITMAPSET_FIELD(modifiedCols); READ_DONE(); } diff -rpNU3 base/src/backend/optimizer/plan/createplan.c sepgsql-new/src/backend/optimizer/plan/createplan.c --- base/src/backend/optimizer/plan/createplan.c 2009-09-10 20:11:35.000000000 +0900 +++ sepgsql-new/src/backend/optimizer/plan/createplan.c 2009-09-10 20:24:37.000000000 +0900 @@ -286,6 +286,9 @@ create_scan_plan(PlannerInfo *root, Path break; } + /* Copy of row-level permissions to Scan node */ + ((Scan *)plan)->rowlvPerms = rel->rowlvPerms; + /* * If there are any pseudoconstant clauses attached to this node, insert a * gating Result node that evaluates the pseudoconstants as one-time diff -rpNU3 base/src/backend/optimizer/prep/prepunion.c sepgsql-new/src/backend/optimizer/prep/prepunion.c --- base/src/backend/optimizer/prep/prepunion.c 2009-02-02 11:47:17.000000000 +0900 +++ sepgsql-new/src/backend/optimizer/prep/prepunion.c 2009-03-13 13:09:47.000000000 +0900 @@ -75,6 +75,8 @@ static void make_inh_translation_lists(R Index newvarno, List **col_mappings, List **translated_vars); +static Bitmapset *translate_col_privs(const Bitmapset *parent_privs, + List *translated_vars); static Node *adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context); static Relids adjust_relid_set(Relids relids, Index oldrelid, Index newrelid); @@ -882,6 +884,19 @@ expand_inherited_rtentry(PlannerInfo *ro appinfo->parent_reloid = parentOID; appinfos = lappend(appinfos, appinfo); + /* + * Translate the column permissions bitmaps to the child's attnums + * (we have to build the translated_vars list before we can do this). + * But if this is the parent table, leave copyObject's result alone. + */ + if (childOID != parentOID) + { + childrte->selectedCols = translate_col_privs(rte->selectedCols, + appinfo->translated_vars); + childrte->modifiedCols = translate_col_privs(rte->modifiedCols, + appinfo->translated_vars); + } + /* Close child relations, but keep locks */ if (childOID != parentOID) heap_close(newrelation, NoLock); @@ -1015,6 +1030,62 @@ make_inh_translation_lists(Relation oldr } /* + * translate_col_privs + * Translate a bitmapset representing per-column privileges from the + * parent rel's attribute numbering to the child's. + * + * The only surprise here is that we don't translate a parent whole-row + * reference into a child whole-row reference. That would mean requiring + * permissions on all child columns, which is overly strict, since the + * query is really only going to reference the inherited columns. Instead + * we set the per-column bits for all inherited columns. + */ +static Bitmapset * +translate_col_privs(const Bitmapset *parent_privs, + List *translated_vars) +{ + Bitmapset *child_privs = NULL; + bool whole_row; + int attno; + ListCell *lc; + + /* System attributes have the same numbers in all tables */ + for (attno = FirstLowInvalidHeapAttributeNumber+1; attno < 0; attno++) + { + if (bms_is_member(attno - FirstLowInvalidHeapAttributeNumber, + parent_privs)) + child_privs = bms_add_member(child_privs, + attno - FirstLowInvalidHeapAttributeNumber); + } + + /* Check if parent has whole-row reference */ + whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber, + parent_privs); + /* Check if parent has whole-row reference */ + whole_row = bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber, + parent_privs); + + /* And now translate the regular user attributes, using the vars list */ + attno = InvalidAttrNumber; + foreach(lc, translated_vars) + { + Var *var = (Var *) lfirst(lc); + + attno++; + if (var == NULL) /* ignore dropped columns */ + continue; + Assert(IsA(var, Var)); + if (whole_row || + bms_is_member(attno - FirstLowInvalidHeapAttributeNumber, + parent_privs)) + child_privs = bms_add_member(child_privs, + var->varattno - FirstLowInvalidHeapAttributeNumber); + } + + return child_privs; +} + +/* * adjust_appendrel_attrs * Copy the specified query or expression and translate Vars referring * to the parent rel of the specified AppendRelInfo to refer to the diff -rpNU3 base/src/backend/optimizer/util/clauses.c sepgsql-new/src/backend/optimizer/util/clauses.c --- base/src/backend/optimizer/util/clauses.c 2008-09-25 15:09:40.000000000 +0900 +++ sepgsql-new/src/backend/optimizer/util/clauses.c 2009-03-13 18:14:33.000000000 +0900 @@ -38,6 +38,7 @@ #include "parser/parse_clause.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" +#include "security/sepgsql.h" #include "tcop/tcopprot.h" #include "utils/acl.h" #include "utils/builtins.h" @@ -3046,6 +3047,7 @@ inline_function(Oid funcid, Oid result_t funcform->prosecdef || funcform->proretset || !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || + !sepgsqlAllowFunctionInlined(func_tuple) || funcform->pronargs != list_length(args)) return NULL; diff -rpNU3 base/src/backend/optimizer/util/relnode.c sepgsql-new/src/backend/optimizer/util/relnode.c --- base/src/backend/optimizer/util/relnode.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/optimizer/util/relnode.c 2009-06-11 10:19:07.000000000 +0900 @@ -20,6 +20,7 @@ #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" +#include "security/rowlevel.h" #include "utils/hsearch.h" @@ -90,6 +91,7 @@ build_simple_rel(PlannerInfo *root, int rel->has_eclass_joins = false; rel->index_outer_relids = NULL; rel->index_inner_paths = NIL; + rel->rowlvPerms = rowlvSetupPermissions(rte); /* Check type of rtable entry */ switch (rte->rtekind) diff -rpNU3 base/src/backend/parser/analyze.c sepgsql-new/src/backend/parser/analyze.c --- base/src/backend/parser/analyze.c 2009-03-15 17:47:25.000000000 +0900 +++ sepgsql-new/src/backend/parser/analyze.c 2009-03-15 17:58:10.000000000 +0900 @@ -24,6 +24,8 @@ #include "postgres.h" +#include "access/htup.h" +#include "catalog/heap.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "optimizer/clauses.h" @@ -427,6 +429,7 @@ transformInsertStmt(ParseState *pstate, * bugs of just that nature...) */ sub_pstate->p_rtable = sub_rtable; + sub_pstate->p_joinexprs = NIL; /* sub_rtable has no joins */ sub_pstate->p_relnamespace = sub_relnamespace; sub_pstate->p_varnamespace = sub_varnamespace; @@ -608,6 +611,7 @@ transformInsertStmt(ParseState *pstate, /* * Generate query's target list using the computed list of expressions. */ + rte = pstate->p_target_rangetblentry; qry->targetList = NIL; icols = list_head(icolumns); attnos = list_head(attrnos); @@ -615,17 +619,22 @@ transformInsertStmt(ParseState *pstate, { Expr *expr = (Expr *) lfirst(lc); ResTarget *col; + AttrNumber attr_num; TargetEntry *tle; col = (ResTarget *) lfirst(icols); Assert(IsA(col, ResTarget)); + attr_num = (AttrNumber) lfirst_int(attnos); tle = makeTargetEntry(expr, - (AttrNumber) lfirst_int(attnos), + attr_num, col->name, - false); + attr_num < 0 ? true : false); qry->targetList = lappend(qry->targetList, tle); + rte->modifiedCols = bms_add_member(rte->modifiedCols, + attr_num - FirstLowInvalidHeapAttributeNumber); + icols = lnext(icols); attnos = lnext(attnos); } @@ -721,6 +730,46 @@ transformInsertRow(ParseState *pstate, L return result; } +static void +transformSelectIntoSystemColumn(ParseState *pstate, Query *qry) +{ + ListCell *l; + uint32 system_attrs = 0; + bool relhasoids + = interpretOidsOption(qry->intoClause->options); + + foreach (l, qry->targetList) { + Form_pg_attribute attr; + TargetEntry *tle = lfirst(l); + + if (tle->resjunk) + continue; + + attr = SystemAttributeByName(tle->resname, relhasoids); + if (attr && SystemAttributeIsWritable(attr->attnum)) + { + uint32 mask = (1<<(-attr->attnum)); + + /* duplication checks */ + if (system_attrs & mask) + continue; + system_attrs |= mask; + + if (exprType((Node *) tle->expr) != attr->atttypid) + { + tle->expr = + (Expr *) coerce_to_target_type(pstate, + (Node *) tle->expr, + exprType((Node *) tle->expr), + attr->atttypid, + attr->atttypmod, + COERCION_IMPLICIT, + COERCE_IMPLICIT_CAST); + } + tle->resjunk = true; + } + } +} /* * transformSelectStmt - @@ -787,6 +836,7 @@ transformSelectStmt(ParseState *pstate, if (stmt->intoClause) { qry->intoClause = stmt->intoClause; + transformSelectIntoSystemColumn(pstate, qry); if (stmt->intoClause->colNames) applyColumnNames(qry->targetList, stmt->intoClause->colNames); } @@ -1478,6 +1528,7 @@ static Query * transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) { Query *qry = makeNode(Query); + RangeTblEntry *target_rte; Node *qual; ListCell *origTargetList; ListCell *tl; @@ -1527,6 +1578,7 @@ transformUpdateStmt(ParseState *pstate, pstate->p_next_resno = pstate->p_target_relation->rd_rel->relnatts + 1; /* Prepare non-junk columns for assignment to target table */ + target_rte = pstate->p_target_rangetblentry; origTargetList = list_head(stmt->targetList); foreach(tl, qry->targetList) @@ -1567,6 +1619,10 @@ transformUpdateStmt(ParseState *pstate, origTarget->indirection, origTarget->location); + /* Mark the target column as requiring update permissions */ + target_rte->modifiedCols = bms_add_member(target_rte->modifiedCols, + attrno - FirstLowInvalidHeapAttributeNumber); + origTargetList = lnext(origTargetList); } if (origTargetList != NULL) diff -rpNU3 base/src/backend/parser/gram.y sepgsql-new/src/backend/parser/gram.y --- base/src/backend/parser/gram.y 2008-03-19 09:48:23.000000000 +0900 +++ sepgsql-new/src/backend/parser/gram.y 2009-06-19 15:02:12.000000000 +0900 @@ -56,6 +56,7 @@ #include "commands/defrem.h" #include "nodes/makefuncs.h" #include "parser/gramparse.h" +#include "security/sepgsql.h" #include "storage/lmgr.h" #include "utils/date.h" #include "utils/datetime.h" @@ -150,7 +151,7 @@ static Node *makeXmlExpr(XmlExprOp op, c %type stmt schema_stmt AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterGroupStmt - AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt + AlterObjectSchemaStmt AlterOwnerStmt AlterSecLabelStmt AlterSeqStmt AlterTableStmt AlterUserStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt @@ -351,6 +352,8 @@ static Node *makeXmlExpr(XmlExprOp op, c %type OptTableSpace OptConsTableSpace OptTableSpaceOwner %type opt_check_option +%type OptSecLabel SecLabelItem + %type xml_attribute_el %type xml_attribute_list xml_attributes %type xml_root_version opt_xml_root_standalone @@ -534,6 +537,7 @@ stmt : | AlterGroupStmt | AlterObjectSchemaStmt | AlterOwnerStmt + | AlterSecLabelStmt | AlterSeqStmt | AlterTableStmt | AlterRoleSetStmt @@ -961,7 +965,7 @@ DropGroupStmt: *****************************************************************************/ CreateSchemaStmt: - CREATE SCHEMA OptSchemaName AUTHORIZATION RoleId OptSchemaEltList + CREATE SCHEMA OptSchemaName AUTHORIZATION RoleId OptSecLabel OptSchemaEltList { CreateSchemaStmt *n = makeNode(CreateSchemaStmt); /* One can omit the schema name or the authorization id. */ @@ -970,16 +974,18 @@ CreateSchemaStmt: else n->schemaname = $5; n->authid = $5; - n->schemaElts = $6; + n->secLabel = $6; + n->schemaElts = $7; $$ = (Node *)n; } - | CREATE SCHEMA ColId OptSchemaEltList + | CREATE SCHEMA ColId OptSecLabel OptSchemaEltList { CreateSchemaStmt *n = makeNode(CreateSchemaStmt); /* ...but not both */ n->schemaname = $3; n->authid = NULL; - n->schemaElts = $4; + n->secLabel = $4; + n->schemaElts = $5; $$ = (Node *)n; } ; @@ -1637,7 +1643,7 @@ alter_table_cmd: n->def = (Node *) $3; $$ = (Node *)n; } - | alter_rel_cmd + | alter_rel_cmd { $$ = $1; } @@ -1883,7 +1889,7 @@ opt_using: *****************************************************************************/ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' - OptInherit OptWith OnCommitOption OptTableSpace + OptInherit OptWith OnCommitOption OptTableSpace OptSecLabel { CreateStmt *n = makeNode(CreateStmt); $4->istemp = $2; @@ -1894,10 +1900,11 @@ CreateStmt: CREATE OptTemp TABLE qualifi n->options = $9; n->oncommit = $10; n->tablespacename = $11; + n->secLabel = (Node *) $12; $$ = (Node *)n; } | CREATE OptTemp TABLE qualified_name OF qualified_name - '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace + '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace OptSecLabel { /* SQL99 CREATE TABLE OF (cols) seems to be satisfied * by our inheritance capabilities. Let's try it... @@ -1911,6 +1918,7 @@ CreateStmt: CREATE OptTemp TABLE qualifi n->options = $10; n->oncommit = $11; n->tablespacename = $12; + n->secLabel = (Node *) $13; $$ = (Node *)n; } ; @@ -1953,13 +1961,14 @@ TableElement: | TableConstraint { $$ = $1; } ; -columnDef: ColId Typename ColQualList +columnDef: ColId Typename ColQualList OptSecLabel { ColumnDef *n = makeNode(ColumnDef); n->colname = $1; n->typename = $2; n->constraints = $3; n->is_local = true; + n->secLabel = (Node *) $4; $$ = (Node *)n; } ; @@ -2420,12 +2429,13 @@ CreateAsElement: *****************************************************************************/ CreateSeqStmt: - CREATE OptTemp SEQUENCE qualified_name OptSeqList + CREATE OptTemp SEQUENCE qualified_name OptSeqList OptSecLabel { CreateSeqStmt *n = makeNode(CreateSeqStmt); $4->istemp = $2; n->sequence = $4; n->options = $5; + n->secLabel = $6; $$ = (Node *)n; } ; @@ -4278,7 +4288,7 @@ common_func_opt_item: /* we abuse the normal content of a DefElem here */ $$ = makeDefElem("set", (Node *)$1); } - ; + ; createfunc_opt_item: AS func_as @@ -4289,6 +4299,10 @@ createfunc_opt_item: { $$ = makeDefElem("language", (Node *)makeString($2)); } + | SecLabelItem + { + $$ = $1; + } | common_func_opt_item { $$ = $1; @@ -4923,6 +4937,75 @@ AlterOwnerStmt: ALTER AGGREGATE func_nam } ; +/***************************************************************************** + * + * ALTER THING name SECURITY_LABEL [=] + * + *****************************************************************************/ + +AlterSecLabelStmt: ALTER DATABASE database_name SecLabelItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_DATABASE; + n->object = list_make1(makeString($3)); + n->secLabel = (Node *)$4; + $$ = (Node *) n; + } + | ALTER SCHEMA name SecLabelItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_SCHEMA; + n->object = list_make1(makeString($3)); + n->secLabel = (Node *)$4; + $$ = (Node *) n; + } + | ALTER TABLE relation_expr SecLabelItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_TABLE; + n->relation = $3; + n->secLabel = (Node *)$4; + $$ = (Node *) n; + } + | ALTER TABLE relation_expr ALTER opt_column ColId SecLabelItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_COLUMN; + n->relation = $3; + n->subname = $6; + n->secLabel = (Node *)$7; + $$ = (Node *) n; + } + | ALTER SEQUENCE relation_expr SecLabelItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_SEQUENCE; + n->relation = $3; + n->secLabel = (Node *)$4; + $$ = (Node *) n; + } + | ALTER FUNCTION function_with_argtypes SecLabelItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_FUNCTION; + n->object = $3->funcname; + n->objarg = $3->funcargs; + n->secLabel = (Node *)$4; + $$ = (Node *) n; + } + ; + +OptSecLabel: SecLabelItem { $$ = $1; } + | /* EMPTY */ { $$ = NULL; } + ; + +SecLabelItem: IDENT opt_equal Sconst + { + if (strcmp("security_context", $1) != 0) + yyerror("syntax error"); + $$ = makeDefElem($1, (Node *) makeString($3)); + } + ; /***************************************************************************** * @@ -5361,6 +5444,10 @@ createdb_opt_item: { $$ = makeDefElem("owner", NULL); } + | SecLabelItem + { + $$ = $1; + } ; /* @@ -8736,7 +8823,6 @@ target_el: a_expr AS ColLabel } ; - /***************************************************************************** * * Names and constants diff -rpNU3 base/src/backend/parser/parse_clause.c sepgsql-new/src/backend/parser/parse_clause.c --- base/src/backend/parser/parse_clause.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/parser/parse_clause.c 2009-03-13 18:14:33.000000000 +0900 @@ -46,6 +46,7 @@ static void extractRemainingColumns(List List *src_colnames, List *src_colvars, List **res_colnames, List **res_colvars); static Node *transformJoinUsingClause(ParseState *pstate, + RangeTblEntry *leftRTE, RangeTblEntry *rightRTE, List *leftVars, List *rightVars); static Node *transformJoinOnClause(ParseState *pstate, JoinExpr *j, RangeTblEntry *l_rte, @@ -286,7 +287,9 @@ extractRemainingColumns(List *common_col * Result is a transformed qualification expression. */ static Node * -transformJoinUsingClause(ParseState *pstate, List *leftVars, List *rightVars) +transformJoinUsingClause(ParseState *pstate, + RangeTblEntry *leftRTE, RangeTblEntry *rightRTE, + List *leftVars, List *rightVars) { Node *result = NULL; ListCell *lvars, @@ -303,6 +306,10 @@ transformJoinUsingClause(ParseState *pst Node *rvar = (Node *) lfirst(rvars); A_Expr *e; + /* Require read access to the join variables */ + markVarForSelectPriv(pstate, (Var *) lvar, leftRTE); + markVarForSelectPriv(pstate, (Var *) rvar, rightRTE); + e = makeSimpleA_Expr(AEXPR_OP, "=", copyObject(lvar), copyObject(rvar), -1); @@ -665,6 +672,7 @@ transformFromClauseItem(ParseState *psta *r_colvars, *res_colvars; RangeTblEntry *rte; + int k; /* * Recursively process the left and right subtrees @@ -849,6 +857,8 @@ transformFromClauseItem(ParseState *psta } j->quals = transformJoinUsingClause(pstate, + l_rte, + r_rte, l_usingvars, r_usingvars); } @@ -909,6 +919,12 @@ transformFromClauseItem(ParseState *psta *top_rte = rte; *top_rti = j->rtindex; + /* make a matching link to the JoinExpr for later use */ + for (k = list_length(pstate->p_joinexprs) + 1; k < j->rtindex; k++) + pstate->p_joinexprs = lappend(pstate->p_joinexprs, NULL); + pstate->p_joinexprs = lappend(pstate->p_joinexprs, j); + Assert(list_length(pstate->p_joinexprs) == j->rtindex); + /* * Prepare returned namespace list. If the JOIN has an alias then it * hides the contained RTEs as far as the relnamespace goes; diff -rpNU3 base/src/backend/parser/parse_expr.c sepgsql-new/src/backend/parser/parse_expr.c --- base/src/backend/parser/parse_expr.c 2008-11-05 09:57:00.000000000 +0900 +++ sepgsql-new/src/backend/parser/parse_expr.c 2009-03-13 13:09:47.000000000 +0900 @@ -1764,6 +1764,9 @@ transformWholeRowRef(ParseState *pstate, break; } + /* mark relation as requiring whole-row SELECT access */ + markVarForSelectPriv(pstate, (Var *) result, rte); + return result; } diff -rpNU3 base/src/backend/parser/parse_relation.c sepgsql-new/src/backend/parser/parse_relation.c --- base/src/backend/parser/parse_relation.c 2008-06-12 22:34:19.000000000 +0900 +++ sepgsql-new/src/backend/parser/parse_relation.c 2009-03-13 18:14:33.000000000 +0900 @@ -335,6 +335,7 @@ scanRTEForColumn(ParseState *pstate, Ran { Node *result = NULL; int attnum = 0; + Var *var; ListCell *c; /* @@ -361,9 +362,10 @@ scanRTEForColumn(ParseState *pstate, Ran errmsg("column reference \"%s\" is ambiguous", colname), parser_errposition(pstate, location))); - result = (Node *) make_var(pstate, rte, attnum); - /* Require read access */ - rte->requiredPerms |= ACL_SELECT; + var = make_var(pstate, rte, attnum); + /* Require read access to the column */ + markVarForSelectPriv(pstate, var, rte); + result = (Node *) var; } } @@ -389,9 +391,10 @@ scanRTEForColumn(ParseState *pstate, Ran Int16GetDatum(attnum), 0, 0)) { - result = (Node *) make_var(pstate, rte, attnum); - /* Require read access */ - rte->requiredPerms |= ACL_SELECT; + var = make_var(pstate, rte, attnum); + /* Require read access to the column */ + markVarForSelectPriv(pstate, var, rte); + result = (Node *) var; } } } @@ -479,6 +482,122 @@ qualifiedNameToVar(ParseState *pstate, } /* + * markRTEForSelectPriv + * Mark the specified column of an RTE as requiring SELECT privilege + * + * col == InvalidAttrNumber means a "whole row" reference + * + * The caller should pass the actual RTE if it has it handy; otherwise pass + * NULL, and we'll look it up here. (This uglification of the API is + * worthwhile because nearly all external callers have the RTE at hand.) + */ +static void +markRTEForSelectPriv(ParseState *pstate, RangeTblEntry *rte, + int rtindex, AttrNumber col) +{ + if (rte == NULL) + rte = rt_fetch(rtindex, pstate->p_rtable); + + if (rte->rtekind == RTE_RELATION) + { + /* Make sure the rel as a whole is marked for SELECT access */ + rte->requiredPerms |= ACL_SELECT; + /* Must offset the attnum to fit in a bitmapset */ + rte->selectedCols = bms_add_member(rte->selectedCols, + col - FirstLowInvalidHeapAttributeNumber); + } + else if (rte->rtekind == RTE_JOIN) + { + if (col == InvalidAttrNumber) + { + /* + * A whole-row reference to a join has to be treated as + * whole-row references to the two inputs. + */ + JoinExpr *j; + + if (rtindex > 0 && rtindex <= list_length(pstate->p_joinexprs)) + j = (JoinExpr *) list_nth(pstate->p_joinexprs, rtindex - 1); + else + j = NULL; + if (j == NULL) + elog(ERROR, "could not find JoinExpr for whole-row reference"); + Assert(IsA(j, JoinExpr)); + + /* Note: we can't see FromExpr here */ + if (IsA(j->larg, RangeTblRef)) + { + int varno = ((RangeTblRef *) j->larg)->rtindex; + + markRTEForSelectPriv(pstate, NULL, varno, InvalidAttrNumber); + } + else if (IsA(j->larg, JoinExpr)) + { + int varno = ((JoinExpr *) j->larg)->rtindex; + + markRTEForSelectPriv(pstate, NULL, varno, InvalidAttrNumber); + } + else + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(j->larg)); + if (IsA(j->rarg, RangeTblRef)) + { + int varno = ((RangeTblRef *) j->rarg)->rtindex; + + markRTEForSelectPriv(pstate, NULL, varno, InvalidAttrNumber); + } + else if (IsA(j->rarg, JoinExpr)) + { + int varno = ((JoinExpr *) j->rarg)->rtindex; + + markRTEForSelectPriv(pstate, NULL, varno, InvalidAttrNumber); + } + else + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(j->rarg)); + } + else + { + /* + * Regular join attribute, look at the alias-variable list. + * + * The aliasvar could be either a Var or a COALESCE expression, + * but in the latter case we should already have marked the two + * referent variables as being selected, due to their use in the + * JOIN clause. So we need only be concerned with the simple + * Var case. + */ + Var *aliasvar; + + Assert(col > 0 && col <= list_length(rte->joinaliasvars)); + aliasvar = (Var *) list_nth(rte->joinaliasvars, col - 1); + if (IsA(aliasvar, Var)) + markVarForSelectPriv(pstate, aliasvar, NULL); + } + } + /* other RTE types don't require privilege marking */ +} + +/* + * markVarForSelectPriv + * Mark the RTE referenced by a Var as requiring SELECT privilege + * + * The caller should pass the Var's referenced RTE if it has it handy + * (nearly all do); otherwise pass NULL. + */ +void +markVarForSelectPriv(ParseState *pstate, Var *var, RangeTblEntry *rte) +{ + Index lv; + + Assert(IsA(var, Var)); + /* Find the appropriate pstate if it's an uplevel Var */ + for (lv = 0; lv < var->varlevelsup; lv++) + pstate = pstate->parentParseState; + markRTEForSelectPriv(pstate, rte, var->varno, var->varattno); +} + +/* * buildRelationAliases * Construct the eref column name list for a relation RTE. * This code is also used for the case of a function RTE returning @@ -669,6 +788,8 @@ addRangeTableEntry(ParseState *pstate, rte->requiredPerms = ACL_SELECT; rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ + rte->selectedCols = NULL; + rte->modifiedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -722,6 +843,8 @@ addRangeTableEntryForRelation(ParseState rte->requiredPerms = ACL_SELECT; rte->checkAsUser = InvalidOid; /* not set-uid by default, either */ + rte->selectedCols = NULL; + rte->modifiedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -800,6 +923,8 @@ addRangeTableEntryForSubquery(ParseState rte->requiredPerms = 0; rte->checkAsUser = InvalidOid; + rte->selectedCols = NULL; + rte->modifiedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -928,6 +1053,8 @@ addRangeTableEntryForFunction(ParseState rte->requiredPerms = 0; rte->checkAsUser = InvalidOid; + rte->selectedCols = NULL; + rte->modifiedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -997,6 +1124,8 @@ addRangeTableEntryForValues(ParseState * rte->inFromCl = inFromCl; rte->requiredPerms = 0; rte->checkAsUser = InvalidOid; + rte->selectedCols = NULL; + rte->modifiedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -1066,6 +1195,8 @@ addRangeTableEntryForJoin(ParseState *ps rte->requiredPerms = 0; rte->checkAsUser = InvalidOid; + rte->selectedCols = NULL; + rte->modifiedCols = NULL; /* * Add completed RTE to pstate's range table list, but not to join list @@ -1520,6 +1651,9 @@ expandRelAttrs(ParseState *pstate, Range label, false); te_list = lappend(te_list, te); + + /* Require read access to each column */ + markVarForSelectPriv(pstate, (Var *) varnode, rte); } Assert(name == NULL && var == NULL); /* lists not the same length? */ diff -rpNU3 base/src/backend/parser/parse_target.c sepgsql-new/src/backend/parser/parse_target.c --- base/src/backend/parser/parse_target.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/parser/parse_target.c 2009-03-13 18:14:33.000000000 +0900 @@ -14,6 +14,7 @@ */ #include "postgres.h" +#include "catalog/heap.h" #include "catalog/pg_type.h" #include "commands/dbcommands.h" #include "funcapi.h" @@ -333,16 +334,33 @@ transformAssignedExpr(ParseState *pstate Oid attrtype; /* type of target column */ int32 attrtypmod; Relation rd = pstate->p_target_relation; + bool relhasoids = RelationGetForm(rd)->relhasoids; Assert(rd != NULL); - if (attrno <= 0) - 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 + { + Form_pg_attribute attr + = SystemAttributeDefinition(attrno, relhasoids); + if (attr && SystemAttributeIsWritable(attrno)) + { + attrtype = attr->atttypid; + attrtypmod = attr->atttypmod; + } + else + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot assign to system column \"%s\"", + colname), + parser_errposition(pstate, location))); + return NULL; /* compiler kindness */ + } + } /* * If the expression is a DEFAULT placeholder, insert the attribute's @@ -483,6 +501,9 @@ updateTargetListEntry(ParseState *pstate */ tle->resno = (AttrNumber) attrno; tle->resname = colname; + + if (SystemAttributeIsWritable(attrno)) + tle->resjunk = true; } @@ -749,6 +770,7 @@ checkInsertTargets(ParseState *pstate, L Bitmapset *wholecols = NULL; Bitmapset *partialcols = NULL; ListCell *tl; + uint32 system_attrs = 0UL; foreach(tl, cols) { @@ -757,14 +779,37 @@ checkInsertTargets(ParseState *pstate, L int attrno; /* Lookup column name, ereport on failure */ - attrno = attnameAttNum(pstate->p_target_relation, name, false); + attrno = attnameAttNum(pstate->p_target_relation, name, true); if (attrno == InvalidAttrNumber) + { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", name, RelationGetRelationName(pstate->p_target_relation)), parser_errposition(pstate, col->location))); + } + else if (attrno < 0) + { + if (SystemAttributeIsWritable(attrno)) + { + uint32 mask = (1<<(-attrno)); + + if ((system_attrs & mask) != 0) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_COLUMN), + errmsg("column \"%s\" specified more than once", name), + parser_errposition(pstate, col->location))); + system_attrs |= mask; + *attrnos = lappend_int(*attrnos, attrno); + continue; + } + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("column \"%s\" of relation \"%s\" is system column", + name, RelationGetRelationName(pstate->p_target_relation)), + parser_errposition(pstate, col->location))); + } /* * Check for duplicates, but only of whole columns --- we allow @@ -902,9 +947,25 @@ ExpandColumnRefStar(ParseState *pstate, else { List *vars; + ListCell *l; expandRTE(rte, rtindex, sublevels_up, false, NULL, &vars); + /* + * Require read access to the table. This is normally redundant + * with the markVarForSelectPriv calls below, but not if the table + * has zero columns. + */ + rte->requiredPerms |= ACL_SELECT; + + /* Require read access to each column */ + foreach(l, vars) + { + Var *var = (Var *) lfirst(l); + + markVarForSelectPriv(pstate, var, rte); + } + return vars; } } diff -rpNU3 base/src/backend/postmaster/postmaster.c sepgsql-new/src/backend/postmaster/postmaster.c --- base/src/backend/postmaster/postmaster.c 2009-09-10 20:11:35.000000000 +0900 +++ sepgsql-new/src/backend/postmaster/postmaster.c 2009-09-10 20:24:37.000000000 +0900 @@ -107,6 +107,7 @@ #include "postmaster/pgarch.h" #include "postmaster/postmaster.h" #include "postmaster/syslogger.h" +#include "security/sepgsql.h" #include "storage/fd.h" #include "storage/ipc.h" #include "storage/pg_shmem.h" @@ -214,7 +215,8 @@ static pid_t StartupPID = 0, AutoVacPID = 0, PgArchPID = 0, PgStatPID = 0, - SysLoggerPID = 0; + SysLoggerPID = 0, + sepgsqlWorkerPID = 0; /* Startup/shutdown state */ #define NoShutdown 0 @@ -1323,6 +1325,10 @@ ServerLoop(void) if (PgStatPID == 0 && pmState == PM_RUN) PgStatPID = pgstat_start(); + /* If we have lost the sepgsql worker (if needed), try to start a new one */ + if (sepgsqlWorkerPID == 0 && pmState == PM_RUN) + sepgsqlWorkerPID = sepgsqlStartupWorkerProcess(); + /* If we need to signal the autovacuum launcher, do so now */ if (avlauncher_needs_signal) { @@ -1921,6 +1927,8 @@ SIGHUP_handler(SIGNAL_ARGS) signal_child(PgArchPID, SIGHUP); if (SysLoggerPID != 0) signal_child(SysLoggerPID, SIGHUP); + if (sepgsqlWorkerPID != 0) + signal_child(sepgsqlWorkerPID, SIGHUP); /* PgStatPID does not currently need SIGHUP */ /* Reload authentication config files too */ @@ -1978,6 +1986,9 @@ pmdie(SIGNAL_ARGS) /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); + /* and the sepgsql worker too */ + if (sepgsqlWorkerPID != 0) + signal_child(sepgsqlWorkerPID, SIGTERM); pmState = PM_WAIT_BACKENDS; } @@ -2017,6 +2028,9 @@ pmdie(SIGNAL_ARGS) /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); + /* and the walwriter too */ + if (sepgsqlWorkerPID != 0) + signal_child(sepgsqlWorkerPID, SIGTERM); pmState = PM_WAIT_BACKENDS; } @@ -2050,6 +2064,8 @@ pmdie(SIGNAL_ARGS) signal_child(PgArchPID, SIGQUIT); if (PgStatPID != 0) signal_child(PgStatPID, SIGQUIT); + if (sepgsqlWorkerPID != 0) + signal_child(sepgsqlWorkerPID, SIGQUIT); ExitPostmaster(0); break; } @@ -2298,6 +2314,16 @@ reaper(SIGNAL_ARGS) continue; } + /* Was it the sepgsql worker process? */ + if (pid == sepgsqlWorkerPID) + { + sepgsqlWorkerPID = 0; + if (!EXIT_STATUS_0(exitstatus)) + LogChildExit(LOG, _("SE-PostgreSQL worker process"), + pid, exitstatus); + continue; + } + /* * Else do standard backend child cleanup. */ @@ -2465,6 +2491,18 @@ HandleChildCrash(int pid, int exitstatus signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT)); } + /* Take care of the sepgsql worker too */ + if (pid == sepgsqlWorkerPID) + sepgsqlWorkerPID = 0; + else if (sepgsqlWorkerPID != 0 && !FatalError) + { + ereport(DEBUG2, + (errmsg_internal("sending %s to process %d", + (SendStop ? "SIGSTOP" : "SIGQUIT"), + (int) sepgsqlWorkerPID))); + signal_child(sepgsqlWorkerPID, (SendStop ? SIGSTOP : SIGQUIT)); + } + /* * Force a power-cycle of the pgarch process too. (This isn't absolutely * necessary, but it seems like a good idea for robustness, and it @@ -2584,7 +2622,8 @@ PostmasterStateMachine(void) StartupPID == 0 && (BgWriterPID == 0 || !FatalError) && WalWriterPID == 0 && - AutoVacPID == 0) + AutoVacPID == 0 && + sepgsqlWorkerPID == 0) { if (FatalError) { diff -rpNU3 base/src/backend/rewrite/rewriteHandler.c sepgsql-new/src/backend/rewrite/rewriteHandler.c --- base/src/backend/rewrite/rewriteHandler.c 2008-11-05 09:57:00.000000000 +0900 +++ sepgsql-new/src/backend/rewrite/rewriteHandler.c 2009-03-13 13:09:47.000000000 +0900 @@ -1168,9 +1168,13 @@ ApplyRetrieveRule(Query *parsetree, Assert(subrte->relid == relation->rd_id); subrte->requiredPerms = rte->requiredPerms; subrte->checkAsUser = rte->checkAsUser; + subrte->selectedCols = rte->selectedCols; + subrte->modifiedCols = rte->modifiedCols; rte->requiredPerms = 0; /* no permission check on subquery itself */ rte->checkAsUser = InvalidOid; + rte->selectedCols = NULL; + rte->modifiedCols = NULL; /* * FOR UPDATE/SHARE of view? diff -rpNU3 base/src/backend/security/Makefile sepgsql-new/src/backend/security/Makefile --- base/src/backend/security/Makefile 1970-01-01 09:00:00.000000000 +0900 +++ sepgsql-new/src/backend/security/Makefile 2009-03-13 18:14:33.000000000 +0900 @@ -0,0 +1,34 @@ +# +# src/backend/security/Makefile +# Makefile for Security Purpose Extensions +# +# Copyright (c) 2006 - 2007 KaiGai Kohei +# +subdir = src/backend/security +top_builddir = ../../.. +include $(top_builddir)/src/Makefile.global + + +OBJS := rowlevel.o + +ifeq ($(enable_selinux), yes) +OBJS += sepgsql/avc.o sepgsql/checker.o sepgsql/core.o \ + sepgsql/hooks.o sepgsql/label.o sepgsql/perms.o +else +OBJS += sepgsql/dummy.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/rowlevel.c sepgsql-new/src/backend/security/rowlevel.c --- base/src/backend/security/rowlevel.c 1970-01-01 09:00:00.000000000 +0900 +++ sepgsql-new/src/backend/security/rowlevel.c 2009-05-20 17:08:02.000000000 +0900 @@ -0,0 +1,177 @@ +/* + * src/backend/security/common.c + * common facilities for row-level access controls both of DAC and MAC + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "catalog/pg_security.h" +#include "security/rowlevel.h" +#include "security/sepgsql.h" +#include "storage/bufmgr.h" +#include "storage/bufpage.h" +#include "utils/rel.h" + +/* + * rowlvGetPerformingMode + * rowlvSetPerformingMode + * enables to control the behavior of row-level features + * when violated tuples are detected. + * The default is ROWLV_FILTER_MODE which filters out + * violated tuples from result set, ROWLV_ABORT_MODE + * raises an error and ROWLV_BYPASS_MODE do nothing. + */ +static int rowlv_mode = ROWLV_FILTER_MODE; + +int rowlvGetPerformingMode(void) +{ + return rowlv_mode; +} + +int rowlvSetPerformingMode(int new_mode) +{ + int old_mode = new_mode; + + rowlv_mode = new_mode; + + return old_mode; +} + +/* + * rowlvSetupPermissions + * setups permissions for row-level access controls. + */ +#define ROWLV_PERMS_MASK 0xffff +#define ROWLV_PERMS_SHIFT 16 +#define ROWLV_DAC_PERMS(perms) ((perms) & ROWLV_PERMS_MASK) +#define ROWLV_MAC_PERMS(perms) (((perms) >> ROWLV_PERMS_SHIFT) & ROWLV_PERMS_MASK) + +uint32 +rowlvSetupPermissions(RangeTblEntry *rte) +{ + uint32 mac_perms; + + mac_perms = sepgsqlSetupTuplePerms(rte); + Assert((mac_perms & ROWLV_PERMS_MASK) == mac_perms); + + return (mac_perms << ROWLV_PERMS_SHIFT); +} + +/* + * rowlvExecScan + * a hook to filter out invisible/untouchable tuples. + */ +static bool +rowlvExecScan(Scan *scan, Relation rel, TupleTableSlot *slot, bool abort) +{ + HeapTuple tuple; + uint32 perms = scan->rowlvPerms; + + tuple = ExecMaterializeSlot(slot); + + if (ROWLV_MAC_PERMS(perms) != 0 && + !sepgsqlExecScan(rel, tuple, ROWLV_MAC_PERMS(perms), abort)) + return false; + + return true; +} + +bool +rowlvExecScanFilter(Scan *scan, Relation rel, TupleTableSlot *slot) +{ + if (!rel || !scan->rowlvPerms || rowlv_mode != ROWLV_FILTER_MODE) + return true; + + return rowlvExecScan(scan, rel, slot, false); +} + +void +rowlvExecScanAbort(Scan *scan, Relation rel, TupleTableSlot *slot) +{ + if (!rel || !scan->rowlvPerms || rowlv_mode != ROWLV_ABORT_MODE) + return; + + rowlvExecScan(scan, rel, slot, true); +} + +/* + * rowlvCopyToTuple + * checks permission on fetched tuple + */ +bool +rowlvCopyToTuple(Relation rel, HeapTuple tuple) +{ + if (!sepgsqlExecScan(rel, tuple, SEPG_DB_TUPLE__SELECT, false)) + return false; + + return true; +} + +/* + * rowlvHeapTupleInsert + * assigns default acl and label on a newly inserted tuple, and checks + * permissions on insert a tuple. + */ +bool +rowlvHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal) +{ + if (!sepgsqlHeapTupleInsert(rel, newtup, internal)) + { + Assert(!internal); + return false; + } + + return true; +} + +/* + * rowlvHeapTupleUpdate + * preserves original acl and label if necessary, and checks + * permissions on update a tuple. + */ +bool +rowlvHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup, bool internal) +{ + HeapTupleData oldtup; + Buffer oldbuf; + + ItemPointerCopy(otid, &oldtup.t_self); + if (!heap_fetch(rel, SnapshotAny, &oldtup, &oldbuf, false, NULL)) + elog(ERROR, "failed to fetch a tuple for row-level access controls"); + + if (!sepgsqlHeapTupleUpdate(rel, &oldtup, newtup, internal)) + { + ReleaseBuffer(oldbuf); + return false; + } + + ReleaseBuffer(oldbuf); + return true; +} + +/* + * rowlvHeapTupleDelete + * checks permissions on delete a tuple. + */ +bool +rowlvHeapTupleDelete(Relation rel, ItemPointer otid, bool internal) +{ + HeapTupleData oldtup; + Buffer oldbuf; + + ItemPointerCopy(otid, &oldtup.t_self); + if (!heap_fetch(rel, SnapshotAny, &oldtup, &oldbuf, false, NULL)) + elog(ERROR, "failed to fetch a tuple for row-level access controls"); + + if (!sepgsqlHeapTupleDelete(rel, &oldtup, internal)) + { + ReleaseBuffer(oldbuf); + return false; + } + + ReleaseBuffer(oldbuf); + return true; +} diff -rpNU3 base/src/backend/security/sepgsql/avc.c sepgsql-new/src/backend/security/sepgsql/avc.c --- base/src/backend/security/sepgsql/avc.c 1970-01-01 09:00:00.000000000 +0900 +++ sepgsql-new/src/backend/security/sepgsql/avc.c 2009-06-10 17:43:08.000000000 +0900 @@ -0,0 +1,859 @@ +/* + * src/backend/security/sepgsql/avc.c + * SE-PostgreSQL userspace access vector cache + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ +#include "postgres.h" + +#include "access/hash.h" +#include "catalog/catalog.h" +#include "catalog/pg_security.h" +#include "libpq/pqsignal.h" +#include "postmaster/postmaster.h" +#include "security/sepgsql.h" +#include "storage/ipc.h" +#include "storage/lwlock.h" +#include "utils/memutils.h" +#include "utils/syscache.h" +#include +#include +#include + +/* + * AVC: userspace access vector cache + * + * SE-PostgreSQL asks in-kernel SELinux to make its decision whether + * the required accesses should be allowed, or not, based on the unified + * security policy. It needs a system call invocation to communicate + * a kernel feature, such as SELinux, but it is a heavy task in most cases + * due to the context switching. + * + * The userspace avc enables to minimize the number of system call + * invocations, using a chache mechanim for the certain pair of security + * contexts and object classes (it means the kind of actions). + * It enables to hold recently fetched results from the in-kernel SELinux, + * and make a decision without context switching, if the cache hit. + * + * When the state of security policy is changed, the cached results + * shall to be invalidated. The state monitoring process launched by + * postmaster can receives the notification messages from the kernel + * space, and invalidate the current version of avc. + */ +static MemoryContext AvcMemCtx; + +#define AVC_HASH_NUM_SLOTS 256 +#define AVC_HASH_NUM_NODES 180 + +typedef struct +{ + Oid relid; + Oid secid; +} sepgsql_sid_t; + +#define AVC_DATUM_NSID_SLOTS 19 +typedef struct +{ + uint32 hash_key; + + security_class_t tclass; + sepgsql_sid_t tsid; + sepgsql_sid_t nsid[AVC_DATUM_NSID_SLOTS]; + + access_vector_t allowed; + access_vector_t decided; + access_vector_t auditallow; + access_vector_t auditdeny; + + bool hot_cache; + bool permissive; + + char ncontext[1]; +} avc_datum; + +typedef struct avc_page +{ + struct avc_page *next; + + security_context_t scontext; + + List *slot[AVC_HASH_NUM_SLOTS]; + + uint32 avc_count; + uint32 lru_hint; +} avc_page; + +static avc_page *client_avc_page = NULL; + +static int avc_version; + +static bool avc_enforcing; + +static bool avc_exception = false; + +/* + * selinux_state + * + * This structure shows the global state of SELinux and its security + * policy, and it is assigned on shared memory region. + * + * The selinux_state->version should be checked prior to any avc + * accesses. If avc_page->avc_version is not matched with the + * global state, it means security policy is reloaded, system booleans + * are changed, or working mode (enforcing/permissive) is changed. + * The selinux_state->enforcing means current working mode. If it it + * true, it works in enforcing mode, elsewhere permissive mode. + * + * The only process able to update these variable are policy state + * monitoring process forked by postmaster. It enables to receive + * notifications from the kernwl via netlink socket. + * + * These global state is protected by SepgsqlAvcLock LWlock, so + * we need to acquire this lock when it is refered. + */ +struct +{ + int version; + + bool enforcing; + +} *selinux_state = NULL; + +Size +sepgsqlShmemSize(void) +{ + if (!sepgsqlIsEnabled()) + return 0; + + return sizeof(*selinux_state); +} + +/* + * sepgsqlGetExceptionMode + * sepgsqlSetExceptionMode + * They control exception mode bit to disable checks + * temporary (for internal processing purpose). + */ +bool sepgsqlGetExceptionMode(void) +{ + return avc_exception; +} + +bool sepgsqlSetExceptionMode(bool new_exception) +{ + bool old_exception = avc_exception; + + avc_exception = new_exception; + + return old_exception; +} + +/* + * sepgsql_avc_check_valid + * returns false, if the given avc_page is already obsolete. + */ +static bool +sepgsql_avc_check_valid(void) +{ + bool result = true; + + LWLockAcquire(SepgsqlAvcLock, LW_SHARED); + if (avc_version != selinux_state->version) + result = false; + LWLockRelease(SepgsqlAvcLock); + + return result; +} + + +/* + * sepgsql_avc_reset + * clears all AVC entries and update its version. + * caller need to hold SepgsqlAvcLock + */ +static void +sepgsql_avc_reset(void) +{ + MemoryContextReset(AvcMemCtx); + + client_avc_page = NULL; + + LWLockAcquire(SepgsqlAvcLock, LW_SHARED); + avc_version = selinux_state->version; + avc_enforcing = selinux_state->enforcing; + LWLockRelease(SepgsqlAvcLock); + + sepgsqlAvcSwitchClient(); +} + +/* + * sepgsql_avc_reclaim + * reclaims recently unused AVC entries, when the number of + * caches overs AVC_HASH_NUM_NODES. + */ +static void +sepgsql_avc_reclaim(avc_page *page) +{ + ListCell *l; + avc_datum *cache; + + while (page->avc_count > AVC_HASH_NUM_NODES) + { + foreach (l, page->slot[page->lru_hint]) + { + cache = lfirst(l); + + if (cache->hot_cache) + cache->hot_cache = false; + else + { + list_delete_ptr(page->slot[page->lru_hint], cache); + pfree(cache); + page->avc_count--; + } + } + page->lru_hint = (page->lru_hint + 1) % AVC_HASH_NUM_SLOTS; + } +} + +/* + * avc_audit_common + * generates an audit message on the give string buffer based on + * the given av_decision which means the resutl of permission checks. + */ +static void +avc_audit_common(security_context_t scontext, + security_context_t tcontext, + security_class_t tclass, + bool denied, access_vector_t audited, + const char *audit_name) +{ + char buffer[2048]; + access_vector_t mask; + int ofs = 0; + + ofs += snprintf(buffer + ofs, sizeof(buffer) - ofs, "%s {", + denied ? "denied" : "granted"); + for (mask = 1; audited != 0; mask <<= 1) + { + if (audited & mask) + ofs += snprintf(buffer + ofs, sizeof(buffer) - ofs, " %s", + sepgsqlGetPermissionString(tclass, mask)); + audited &= ~mask; + } + ofs += snprintf(buffer + ofs, sizeof(buffer) - ofs, " } "); + + ofs += snprintf(buffer + ofs, sizeof(buffer) - ofs, + "scontext=%s tcontext=%s tclass=%s", + scontext, tcontext, + sepgsqlGetClassString(tclass)); + + if (audit_name) + ofs += snprintf(buffer + ofs, sizeof(buffer) - ofs, " name=%s", audit_name); + + ereport(LOG, + (errcode(ERRCODE_SELINUX_AUDIT), + errmsg("SELinux: %s", buffer))); +} + +/* + * avc_make_entry + * makes a query to in-kernel SELinux and an avc_datum object to + * cache the result of SELinux's decision for access rights and + * default security context. + */ +#define avc_hash_key(trelid,tsecid,tclass) \ + (hash_uint32((trelid) ^ (tsecid) ^ ((tclass) << 3))) + +static avc_datum * +avc_make_entry(avc_page *page, Oid relid, Oid secid, security_class_t tclass) +{ + security_context_t scontext, tcontext, ncontext; + security_class_t tclass_ex; + MemoryContext oldctx; + struct av_decision avd; + avc_datum *cache; + uint32 hash_key, index; + + hash_key = avc_hash_key(relid, secid, tclass); + index = hash_key % AVC_HASH_NUM_SLOTS; + + scontext = page->scontext; + tcontext = securityRawSecLabelOut(relid, secid); + + /* + * Compute SELinux permission + */ + tclass_ex = sepgsqlTransToExternalClass(tclass); + if (tclass_ex > 0) + { + if (security_compute_av_flags_raw(scontext, tcontext, + tclass_ex, 0, &avd) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: unable to compute av_decision: " + "scontext=%s tcontext=%s tclass=%s", + scontext, tcontext, + sepgsqlGetClassString(tclass)))); + sepgsqlTransToInternalPerms(tclass, &avd); + } + else + { + /* fill it up as undefined class */ + avd.allowed = (security_deny_unknown() ? 0 : ~0UL); + avd.decided = ~0UL; + avd.auditallow = 0UL; + avd.auditdeny = ~0UL; + avd.flags = 0; + } + + /* + * Compute New security context + */ + if (security_compute_create_raw(scontext, tcontext, + tclass_ex, &ncontext) < 0) + { + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: unable to compute new context: " + "scontext=%s tcontext=%s tclass=%s", + scontext, tcontext, sepgsqlGetClassString(tclass)))); + } + + /* + * Copy them to avc_datum + */ + oldctx = MemoryContextSwitchTo(AvcMemCtx); + PG_TRY(); + { + cache = palloc0(sizeof(avc_datum) + strlen(ncontext)); + } + PG_CATCH(); + { + freecon(ncontext); + PG_RE_THROW(); + } + PG_END_TRY(); + + cache->hash_key = hash_key; + cache->tclass = tclass; + cache->tsid.relid = relid; + cache->tsid.secid = secid; + /* cache->nsid shall be set later */ + + cache->allowed = avd.allowed; + cache->decided = avd.decided; + cache->auditallow = avd.auditallow; + cache->auditdeny = avd.auditdeny; + + cache->hot_cache = true; + if (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) + cache->permissive = true; + strcpy(cache->ncontext, ncontext); + freecon(ncontext); + + sepgsql_avc_reclaim(page); + + page->slot[index] = lcons(cache, page->slot[index]); + page->avc_count++; + + MemoryContextSwitchTo(oldctx); + + return cache; +} + +/* + * avc_lookup + * It lookups required avc entry. Because it also checks avc_version + * on the global state, the caller has to hold SepgsqlAvcLock. + */ +static avc_datum * +avc_lookup(avc_page *page, Oid trelid, Oid tsecid, + security_class_t tclass) +{ + avc_datum *cache = NULL; + uint32 hash_key, index; + ListCell *l; + + /* check avc invalidation */ + if (!sepgsql_avc_check_valid()) + sepgsql_avc_reset(); + + /* lookup avc entry */ + hash_key = avc_hash_key(trelid, tsecid, tclass); + index = hash_key % AVC_HASH_NUM_SLOTS; + + foreach (l, page->slot[index]) + { + cache = lfirst(l); + if (cache->hash_key == hash_key + && cache->tclass == tclass + && cache->tsid.relid == trelid + && cache->tsid.secid == tsecid) + { + cache->hot_cache = true; + return cache; + } + } + return NULL; +} + +/* + * sepgsqlAvcSwitchClientLabel() + * switches current avc_page. + * + * NOTE: In most cases, SE-PostgreSQL checks whether client is allowed + * to do required actions (like SELECT, UPDATE, ...) on the targets. + * Both of client and targets have its security context, and all rules + * are described as relationship between security context of a client, + * a target and kind of actions. + * However, the security context of client is unchanged in SE-PostgreSQL + * (an exception is invocation of trusted procedure), so we can omit + * to compare security context of client with entries of uAVC. + * The avc_page is a set of avc_datum sorted out by the security context + * of client, so we can lookup correct avc_datum on currently focued + * avc_page without comparing the security context of client. + * The reason why we don't not use a unique uAVC is the security context + * of client does not have its security identifier on pg_security, so + * it requires strcmp() for each entries, but it is heavier than integer + * comparisons. + * Thus we have to switch current avc_page, whenever the security context + * of client changes (via trusted procedure). It makes performance well + * in most cases. + */ +static avc_page * +sepgsqlAvcSwitch(avc_page *old_page, security_context_t scontext) +{ + MemoryContext oldctx; + avc_page *new_page; + int i; + + if (old_page) + { + new_page = old_page; + do { + if (strcmp(new_page->scontext, scontext) == 0) + return new_page; + + new_page = old_page->next; + } while (new_page != old_page); + } + + /* Not found, so create a new avc_page */ + oldctx = MemoryContextSwitchTo(AvcMemCtx); + new_page = palloc0(sizeof(avc_page)); + new_page->scontext = pstrdup(scontext); + MemoryContextSwitchTo(oldctx); + + for (i=0; i < AVC_HASH_NUM_SLOTS; i++) + new_page->slot[i] = NIL; + + if (!old_page) + { + new_page->next = new_page; + } + else + { + new_page->next = old_page->next; + old_page->next = new_page; + } + + return new_page; +} + +void +sepgsqlAvcSwitchClient(void) +{ + client_avc_page = sepgsqlAvcSwitch(client_avc_page, + sepgsqlGetClientLabel()); +} + +/* + * sepgsqlClientHasPerms + * checks client's privileges on given objects via uAVC. + */ +bool +sepgsqlClientHasPermsSid(Oid relid, Oid secid, + security_class_t tclass, + access_vector_t required, + const char *audit_name, bool abort) +{ + security_context_t scontext, tcontext; + access_vector_t denied, audited; + avc_datum *cache; + bool result = true; + + Assert(required != 0); + + if (avc_exception) + return true; + +retry: + cache = avc_lookup(client_avc_page, relid, secid, tclass); + if (!cache) + cache = avc_make_entry(client_avc_page, relid, secid, tclass); + if (!sepgsql_avc_check_valid()) + goto retry; + + denied = required & ~cache->allowed; + audited = denied ? (denied & cache->auditdeny) + : (required & cache->auditallow); + if (audited) + { + scontext = sepgsqlTransSecLabelOut(client_avc_page->scontext); + tcontext = securityTransSecLabelOut(relid, secid); + avc_audit_common(scontext, tcontext, cache->tclass, + !!denied, audited, audit_name); + } + + if (denied) + { + if (!avc_enforcing || cache->permissive) + cache->allowed |= required; /* prevent flood of audit log */ + else + result = false; + } + + if (abort && !result) + { + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: security policy violation"))); + } + + return result; +} + +bool +sepgsqlClientHasPermsTup(Oid relid, HeapTuple tuple, + security_class_t tclass, + access_vector_t required, bool abort) +{ + Oid secid = HeapTupleGetSecLabel(tuple); + const char *audit_name = sepgsqlAuditName(relid, tuple); + + return sepgsqlClientHasPermsSid(relid, secid, tclass, required, + audit_name, abort); +} + +/* + * sepgsqlClientCreateSecid + * sepgsqlClientCreateLabel + */ +Oid +sepgsqlClientCreateSecid(Oid trelid, Oid tsecid, + security_class_t tclass, Oid nrelid) +{ + avc_datum *cache; + int index; + Oid nsecid; + +retry: + cache = avc_lookup(client_avc_page, trelid, tsecid, tclass); + if (!cache) + cache = avc_make_entry(client_avc_page, + trelid, tsecid, tclass); + + index = (nrelid % AVC_DATUM_NSID_SLOTS); + if (cache->nsid[index].relid != nrelid) + { + cache->nsid[index].secid + = securityRawSecLabelIn(nrelid, cache->ncontext); + cache->nsid[index].relid = nrelid; + } + nsecid = cache->nsid[index].secid; + + if (!sepgsql_avc_check_valid()) + goto retry; + + return nsecid; +} + +security_context_t +sepgsqlClientCreateLabel(Oid trelid, Oid tsecid, + security_class_t tclass) +{ + avc_datum *cache; + +retry: + cache = avc_lookup(client_avc_page, + trelid, tsecid, tclass); + if (!cache) + cache = avc_make_entry(client_avc_page, + trelid, tsecid, tclass); + if (!sepgsql_avc_check_valid()) + goto retry; + + return cache->ncontext; +} + +/* + * sepgsql_shmem_init + * attaches shared memory segment. + */ +static void +sepgsql_shmem_init(void) +{ + bool found; + + selinux_state = ShmemInitStruct("SELinux policy state", + sepgsqlShmemSize(), &found); + if (!found) + { + LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); + + selinux_state->version = 0; + selinux_state->enforcing = (security_getenforce() > 0); + + LWLockRelease(SepgsqlAvcLock); + } +} + +/* + * sepgsqlAvcInit + * initialize local uAVC facility. + */ +void +sepgsqlAvcInit(void) +{ + /* + * local memory context + */ + AvcMemCtx = AllocSetContextCreate(TopMemoryContext, + "SE-PostgreSQL userspace avc", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + sepgsql_shmem_init(); + + /* + * reset local avc + */ + sepgsql_avc_reset(); +} + +/* + * sepgsqlComputePerms + * sepgsqlComputeCreate + * + * The following two functions make a query to in-kernel SELinux + * without userspace caches, due to some reasons. + * The AVC can cover most of cases, but some of corner cases are + * not suitable for AVC structure, so we need uncached interfaces. + * For example, AVC is unavailable when we tries to load a shared + * library module, because security context of the library does not + * have its security identifier, so we cannot put it on AVC. + */ +bool +sepgsqlComputePerms(security_context_t scontext, + security_context_t tcontext, + security_class_t tclass, + access_vector_t required, + const char *audit_name, bool abort) +{ + access_vector_t denied, audited; + security_class_t tclass_ex; + struct av_decision avd; + + Assert(required != 0); + + if (avc_exception) + return true; + + tclass_ex = sepgsqlTransToExternalClass(tclass); + if (tclass_ex > 0) + { + if (security_compute_av_flags_raw(scontext, tcontext, + tclass_ex, 0, &avd) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not compute av_decision: " + "scontext=%s tcontext=%s tclass=%s", + scontext, tcontext, + sepgsqlGetClassString(tclass)))); + sepgsqlTransToInternalPerms(tclass, &avd); + } + else + { + /* fill it up as undefined class */ + avd.allowed = (security_deny_unknown() ? 0 : ~0UL); + avd.decided = ~0UL; + avd.auditallow = 0UL; + avd.auditdeny = ~0UL; + avd.flags = 0; + } + + denied = required & ~avd.allowed; + audited = denied ? (denied & avd.auditdeny) + : (required & avd.auditallow); + if (audited) + { + avc_audit_common(sepgsqlTransSecLabelOut(scontext), + sepgsqlTransSecLabelOut(tcontext), + tclass, !!denied, audited, audit_name); + } + + if (denied && avc_enforcing && + (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) == 0) + { + if (abort) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: security policy violation"))); + return false; + } + + return true; +} + +security_context_t +sepgsqlComputeCreate(security_context_t scontext, + security_context_t tcontext, + security_class_t tclass) +{ + security_context_t ncontext, result; + security_class_t tclass_ex; + + tclass_ex = sepgsqlTransToExternalClass(tclass); + if (security_compute_create_raw(scontext, tcontext, tclass_ex, &ncontext) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not compute a default context" + " scontext=%s tcontext=%s tclass=%s", + scontext, tcontext, sepgsqlGetClassString(tclass)))); + PG_TRY(); + { + result = pstrdup(ncontext); + } + PG_CATCH(); + { + freecon(ncontext); + PG_RE_THROW(); + } + PG_END_TRY(); + + freecon(ncontext); + + return result; +} + +/* + * SELinux state monitoring process + * + * This process is forked from postmaster to monitor the state of SELinux. + * SELinux can make a notifier message to userspace object manager via + * netlink socket. When it receives the message, it updates selinux_state + * structure assigned on shared memory region to make any instance reset + * its AVC soon. + */ +static int +sepgsql_cb_log(int type, const char *fmt, ...) +{ + char *c, buffer[1024]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + + c = strrchr(buffer, '\n'); + if (c) + *c = '\0'; + + ereport(LOG, + (errcode(ERRCODE_SELINUX_INFO), + errmsg("%s", buffer))); + + return 0; +} + +static int +sepgsql_cb_setenforce(int enforce) +{ + /* switch enforcing/permissive */ + LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); + selinux_state->version = selinux_state->version + 1; + selinux_state->enforcing = (enforce ? true : false); + LWLockRelease(SepgsqlAvcLock); + + return 0; +} + +static int +sepgsql_cb_policyload(int seqno) +{ + /* invalidate local avc */ + LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); + selinux_state->version = selinux_state->version + 1; + LWLockRelease(SepgsqlAvcLock); + + return 0; +} + +static int +sepgsqlWorkerMain(void) +{ + union selinux_callback cb; + + ClosePostmasterPorts(false); + + on_exit_reset(); + + /* + * map shared memory segment + */ + sepgsql_shmem_init(); + + /* + * setup the signal handler + */ + pqinitmask(); + pqsignal(SIGHUP, SIG_IGN); + pqsignal(SIGINT, SIG_IGN); + pqsignal(SIGTERM, exit); + pqsignal(SIGQUIT, exit); + pqsignal(SIGUSR1, SIG_IGN); + pqsignal(SIGUSR2, SIG_IGN); + pqsignal(SIGCHLD, SIG_DFL); + PG_SETMASK(&UnBlockSig); + + ereport(LOG, + (errcode(ERRCODE_SELINUX_INFO), + errmsg("SELinux: security policy monitor (pid=%u)", getpid()))); + /* + * setup callback functions from avc_netlink_loop() + */ + cb.func_log = sepgsql_cb_log; + selinux_set_callback(SELINUX_CB_LOG, cb); + cb.func_setenforce = sepgsql_cb_setenforce; + selinux_set_callback(SELINUX_CB_SETENFORCE, cb); + cb.func_policyload = sepgsql_cb_policyload; + selinux_set_callback(SELINUX_CB_POLICYLOAD, cb); + + /* + * open netlink socket and wait for messages + */ + avc_netlink_open(1); + + avc_netlink_loop(); + + return 0; +} + +pid_t +sepgsqlStartupWorkerProcess(void) +{ + pid_t chld; + + if (!sepgsqlIsEnabled()) + return (pid_t) 0; + + chld = fork(); + if (chld == 0) + exit(sepgsqlWorkerMain()); + else if (chld > 0) + return chld; + + return (pid_t) 0; +} diff -rpNU3 base/src/backend/security/sepgsql/checker.c sepgsql-new/src/backend/security/sepgsql/checker.c --- base/src/backend/security/sepgsql/checker.c 1970-01-01 09:00:00.000000000 +0900 +++ sepgsql-new/src/backend/security/sepgsql/checker.c 2009-06-25 23:06:01.000000000 +0900 @@ -0,0 +1,577 @@ +/* + * src/backend/security/sepgsql/checker.c + * walks on given Query tree and applies checks + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ +#include "postgres.h" + +#include "catalog/pg_database.h" +#include "catalog/pg_language.h" +#include "catalog/pg_largeobject.h" +#include "catalog/pg_namespace.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_rewrite.h" +#include "catalog/pg_security.h" +#include "security/sepgsql.h" +#include "storage/bufmgr.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" +#include "utils/tqual.h" + +/* + * fixupWholeRowReference + */ +static Bitmapset * +fixupWholeRowReference(Oid relid, int nattrs, Bitmapset *columns) +{ + Bitmapset *result; + AttrNumber attno; + + attno = InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber; + + if (!bms_is_member(attno, columns)) + return columns; /* no need to fixup */ + + result = bms_copy(columns); + result = bms_del_member(result, attno); + + for (attno=1; attno <= nattrs; attno++) + { + Form_pg_attribute attform; + HeapTuple atttup; + + atttup = SearchSysCache(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attno), + 0, 0); + if (!HeapTupleIsValid(atttup)) + continue; + + attform = (Form_pg_attribute) GETSTRUCT(atttup); + if (!attform->attisdropped) + { + int cindex = attno - FirstLowInvalidHeapAttributeNumber; + result = bms_add_member(result, cindex); + } + ReleaseSysCache(atttup); + } + + return result; +} + +/* + * checkTabelColumnPerms + * This functions applies table/column level permissions for + * all the appeared ones in user's query, and raises an error + * if violated. + * It also applies a few hardwired policy which prevent to + * modified some of system catalogs. + */ +static void +checkTabelColumnPerms(Oid relid, Bitmapset *selected, Bitmapset *modified, + access_vector_t required) +{ + Bitmapset *columns; + Bitmapset *selected_ex; + Bitmapset *modified_ex; + HeapTuple tuple; + AttrNumber attno; + int nattrs; + security_class_t tclass; + + /* + * NOTE: HARDWIRED POLICY IN SE-POSTGRESQL + * - User cannot access RELKIND_TOASTVALUE by hand, because + * it is used to store variable length data within other + * column and tuples, and it should be considered as a part + * of content within them. + * - User cannot modify pg_rewrite.* by hand, because it holds + * a parsed Query tree which includes requiredPerms and + * RangeTblEntry with selectedCols/modifiedCols. + * The correctness of access controls depends on these data + * are protected from unexpected manipulation.. + * + * - User cannot modify pg_security.* by hand, because it holds + * all the pairs of security identifier and label, so the + * correctness of access controls depends on these data are + * protected from unexpected manipulation. + * + * - User cannot modify pg_largeobject.* by hand, because we + * assumes largeobjects are accessed via certain functions + * such as lowrite(), so the correctness of access controls + * depends on these data are protected from unexpected + * manipulation. + * + * SE-PostgreSQL always prevent user's query tries to modify + * these system catalogs by hand. Please use approariate + * interfaces. + */ + if (!sepgsqlGetExceptionMode()) + { + switch (relid) + { + case RewriteRelationId: + case SecurityRelationId: + case LargeObjectRelationId: + if ((required & (SEPG_DB_TABLE__UPDATE | + SEPG_DB_TABLE__INSERT | + SEPG_DB_TABLE__DELETE)) != 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SE-PostgreSQL prevent to modify \"%s\" " + "by hand due to the hardwired policy", + get_rel_name(relid)))); + break; + + default: + if (get_rel_relkind(relid) == RELKIND_TOASTVALUE) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SE-PostgreSQL prevent to accuees \"%s\" " + "by hand due to the hardwired policy", + get_rel_name(relid)))); + break; + } + } + + /* + * Check db_table:{...} or db_sequence permissions + */ + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SELinux: cache lookup failed for relation %u", relid); + + tclass = sepgsqlTupleObjectClass(RelationRelationId, tuple); + + if (tclass != SEPG_CLASS_DB_TABLE) + { + /* check db_sequence:{xxx} permission */ + if (tclass == SEPG_CLASS_DB_SEQUENCE) + { + if (required & SEPG_DB_TABLE__SELECT) + { + sepgsqlClientHasPermsTup(RelationRelationId, tuple, + SEPG_CLASS_DB_SEQUENCE, + SEPG_DB_SEQUENCE__GET_VALUE, + true); + } + } + ReleaseSysCache(tuple); + return; + } + + sepgsqlClientHasPermsTup(RelationRelationId, tuple, + SEPG_CLASS_DB_TABLE, + required, true); + + nattrs = ((Form_pg_class) GETSTRUCT(tuple))->relnatts; + + ReleaseSysCache(tuple); + + /* + * Check db_column:{...} permissions + */ + selected_ex = fixupWholeRowReference(relid, nattrs, selected); + modified_ex = fixupWholeRowReference(relid, nattrs, modified); + columns = bms_union(selected_ex, modified_ex); + + while ((attno = bms_first_member(columns)) >= 0) + { + Form_pg_attribute attForm; + access_vector_t attperms = 0; + + if (bms_is_member(attno, selected_ex)) + attperms |= SEPG_DB_COLUMN__SELECT; + if (bms_is_member(attno, modified_ex)) + { + if (required & SEPG_DB_TABLE__UPDATE) + attperms |= SEPG_DB_COLUMN__UPDATE; + if (required & SEPG_DB_TABLE__INSERT) + attperms |= SEPG_DB_COLUMN__INSERT; + } + if (attperms == 0) + continue; + + /* remove the attribute number offset */ + attno += FirstLowInvalidHeapAttributeNumber; + tuple = SearchSysCache(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attno), + 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for attribute %d of relation %u", + attno, relid); + + attForm = (Form_pg_attribute) GETSTRUCT(tuple); + if (attForm->attisdropped) + elog(ERROR, "attribute %d of relation %u does not exist", + attno, relid); + + sepgsqlClientHasPermsTup(AttributeRelationId, tuple, + SEPG_CLASS_DB_COLUMN, + attperms, true); + ReleaseSysCache(tuple); + } + + if (selected_ex != selected) + bms_free(selected_ex); + + if (modified_ex != modified) + bms_free(modified_ex); + + bms_free(columns); +} + +/* + * sepgsqlCheckQueryPerms + * It checks permission for all the required tables/columns on + * generic user queries. + */ +void +sepgsqlCheckRTEPerms(RangeTblEntry *rte) +{ + access_vector_t required = 0; + + if (!sepgsqlIsEnabled()) + return; + + if (rte->rtekind != RTE_RELATION) + return; + + if (rte->requiredPerms & ACL_SELECT) + required |= SEPG_DB_TABLE__SELECT; + if (rte->requiredPerms & ACL_INSERT) + required |= SEPG_DB_TABLE__INSERT; + if (rte->requiredPerms & ACL_UPDATE) + { + /* + * ACL_SELECT_FOR_UPDATE is defined as an aliase of ACL_UPDATE, + * so we cannot determine whether the given relation is accessed + * with UPDATE statement or SELECT FOR SHARE/UPDATE immediately. + * UPDATE statements set a bit on rte->modifiedCols at least, + * so we use it as a watermark. + */ + if (!bms_is_empty(rte->modifiedCols)) + required |= SEPG_DB_TABLE__UPDATE; + else + required |= SEPG_DB_TABLE__LOCK; + } + if (rte->requiredPerms & ACL_DELETE) + required |= SEPG_DB_TABLE__DELETE; + + if (required == 0) + return; + + checkTabelColumnPerms(rte->relid, + rte->selectedCols, + rte->modifiedCols, + required); +} + +/* + * sepgsqlCheckCopyTable + * It checks permissions on COPY TO/FROM. + */ +void +sepgsqlCheckCopyTable(Relation rel, List *attnumlist, bool is_from) +{ + Bitmapset *selected = NULL; + Bitmapset *modified = NULL; + ListCell *l; + + if (!sepgsqlIsEnabled()) + return; + + /* all checkes are done in sepgsqlCheckRTEPerms */ + if (!rel) + return; + + foreach (l, attnumlist) + { + AttrNumber attno = lfirst_int(l); + + attno -= FirstLowInvalidHeapAttributeNumber; + if (is_from) + modified = bms_add_member(modified, attno); + else + selected = bms_add_member(selected, attno); + } + + checkTabelColumnPerms(RelationGetRelid(rel), + selected, modified, + is_from ? SEPG_DB_TABLE__INSERT + : SEPG_DB_TABLE__SELECT); +} + +/* + * sepgsqlCheckSelectInto + * It checks db_table/db_column:{insert} on the table newly created + */ +void +sepgsqlCheckSelectInto(Oid relationId) +{ + Bitmapset *modified = NULL; + + if (!sepgsqlIsEnabled()) + return; + + modified = bms_add_member(modified, InvalidAttrNumber + - FirstLowInvalidHeapAttributeNumber); + + checkTabelColumnPerms(relationId, NULL, modified, + SEPG_DB_TABLE__INSERT); +} + +/* + * fixupColumnAvPerms + * To change pg_attribute.attisdropped means dropping a column, + * although this operation done by update, so it need to change + * required permmision in this special case. + */ +static access_vector_t +fixupColumnAvPerms(HeapTuple oldtup, HeapTuple newtup) +{ + Form_pg_attribute oldatt = (Form_pg_attribute) GETSTRUCT(oldtup); + Form_pg_attribute newatt = (Form_pg_attribute) GETSTRUCT(newtup); + + if (!oldatt->attisdropped && newatt->attisdropped) + return SEPG_DB_COLUMN__DROP; + if (oldatt->attisdropped && !newatt->attisdropped) + return SEPG_DB_COLUMN__CREATE; + + return 0; +} + +/* + * sepgsqlExecScan + * makes a decision on the given tuple. + */ +bool +sepgsqlExecScan(Relation rel, HeapTuple tuple, uint32 required, bool abort) +{ + security_class_t tclass; + + if (!sepgsqlIsEnabled() || + !required || + RelationGetForm(rel)->relkind != RELKIND_RELATION) + return true; + + tclass = sepgsqlTupleObjectClass(RelationGetRelid(rel), tuple); + return sepgsqlClientHasPermsTup(RelationGetRelid(rel), tuple, + tclass, required, abort); +} + +uint32 +sepgsqlSetupTuplePerms(RangeTblEntry *rte) +{ + AclMode perms = 0; + + if (!sepgsqlIsEnabled()) + return 0; + + if (rte->rtekind != RTE_RELATION) + return 0; + + if (rte->requiredPerms & ACL_SELECT) + perms |= SEPG_DB_TUPLE__SELECT; + if (rte->requiredPerms & ACL_UPDATE && !bms_is_empty(rte->modifiedCols)) + perms |= SEPG_DB_TUPLE__UPDATE; + if (rte->requiredPerms & ACL_DELETE) + perms |= SEPG_DB_TUPLE__DELETE; + + return perms; +} + +/* + * checkCLibraryInstallation + * It checks the correctness of C-library when user tries to + * create / replace C-functions. + */ +static void +checkCLibraryInstallation(HeapTuple newtup, HeapTuple oldtup) +{ + Form_pg_proc oldpro, newpro; + Datum oldbin, newbin; + bool isnull; + + newpro = (Form_pg_proc) GETSTRUCT(newtup); + if (newpro->prolang != ClanguageId) + return; + + newbin = SysCacheGetAttr(PROCOID, newtup, + Anum_pg_proc_probin, &isnull); + if (!isnull) + { + if (HeapTupleIsValid(oldtup)) + { + oldpro = (Form_pg_proc) GETSTRUCT(oldtup); + oldbin = SysCacheGetAttr(PROCOID, oldtup, + Anum_pg_proc_probin, &isnull); + if (!isnull && + oldpro->prolang == newpro->prolang && + DatumGetBool(DirectFunctionCall2(byteaeq, oldbin, newbin))) + return; /* no need to check, if unchanged */ + } + sepgsqlCheckDatabaseInstallModule(); + } +} + +/* + * checkTrustedAction + * It returns true, if we can ignore access controls for create/alter/drop + * on the given database objects. + */ +static bool +checkTrustedAction(Relation rel, bool internal) +{ + if (RelationGetForm(rel)->relkind != RELKIND_RELATION) + return true; + + if (internal && + (RelationGetRelid(rel) == SecurityRelationId)) + return true; + + if (RelationGetRelid(rel) == DatabaseRelationId || + RelationGetRelid(rel) == NamespaceRelationId || + RelationGetRelid(rel) == RelationRelationId || + RelationGetRelid(rel) == AttributeRelationId || + RelationGetRelid(rel) == ProcedureRelationId) + return false; + + return !sepostgresql_row_level; +} + +/* + * HeapTuple INSERT/UPDATE/DELETE + */ +bool +sepgsqlHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal) +{ + Oid relid = RelationGetRelid(rel); + security_class_t tclass; + + if (!sepgsqlIsEnabled()) + return true; + + /* set a default security context */ + if (!OidIsValid(HeapTupleGetSecLabel(newtup))) + { + if (HeapTupleHasSecLabel(newtup)) + sepgsqlSetDefaultSecLabel(rel, newtup); + } + + if (checkTrustedAction(rel, internal)) + return true; + /* check binary library installation */ + if (relid == ProcedureRelationId) + checkCLibraryInstallation(newtup, NULL); + /* check db_schema:{add_object}, if necessary */ + sepgsqlCheckSchemaAddRemove(rel, newtup, NULL); + /* check db_procedure:{install} */ + sepgsqlCheckProcedureInstall(rel, newtup, NULL); + + tclass = sepgsqlTupleObjectClass(relid, newtup); + return sepgsqlClientHasPermsTup(relid, newtup, tclass, + SEPG_DB_TUPLE__INSERT, + internal); +} + +bool +sepgsqlHeapTupleUpdate(Relation rel, HeapTuple oldtup, + HeapTuple newtup, bool internal) +{ + Oid relid = RelationGetRelid(rel); + access_vector_t required = 0; + security_class_t newclass; + security_class_t oldclass; + + if (!sepgsqlIsEnabled()) + return true; + + /* preserve security label, if unchanged */ + if (!OidIsValid(HeapTupleGetSecLabel(newtup))) + { + if (HeapTupleHasSecLabel(newtup)) + HeapTupleSetSecLabel(newtup, HeapTupleGetSecLabel(oldtup)); + } + + if (checkTrustedAction(rel, internal)) + return true; + + newclass = sepgsqlTupleObjectClass(relid, newtup); + oldclass = sepgsqlTupleObjectClass(relid, oldtup); + + /* already checked at ExecScan? */ + if (internal) + required |= SEPG_DB_TUPLE__UPDATE; + /* special case for pg_attribute */ + if (relid == AttributeRelationId) + required |= fixupColumnAvPerms(oldtup, newtup); + /* relabeled? */ + if (oldclass != newclass || + HeapTupleGetSecLabel(oldtup) != HeapTupleGetSecLabel(newtup)) + required |= SEPG_DB_TUPLE__RELABELFROM; + /* check binary library installation */ + if (relid == ProcedureRelationId) + checkCLibraryInstallation(newtup, oldtup); + /* check db_schema:{add_object remove_object}, if necessary */ + sepgsqlCheckSchemaAddRemove(rel, newtup, oldtup); + /* check db_procedure:{install}, if necessary */ + sepgsqlCheckProcedureInstall(rel, newtup, oldtup); + + if (required != 0) + { + if (!sepgsqlClientHasPermsTup(relid, oldtup, oldclass, + required, false)) + return false; + } + + if ((required & SEPG_DB_TUPLE__RELABELFROM) != 0) + { + if (!sepgsqlClientHasPermsTup(relid, newtup, newclass, + SEPG_DB_TUPLE__RELABELTO, + internal)) + return false; + } + + return true; +} + +bool +sepgsqlHeapTupleDelete(Relation rel, HeapTuple oldtup, bool internal) +{ + Oid relid = RelationGetRelid(rel); + security_class_t tclass; + access_vector_t required = 0; + + if (!sepgsqlIsEnabled()) + return true; + + if (checkTrustedAction(rel, internal)) + return true; + + /* check db_schema:{remove_object}, if necessary */ + sepgsqlCheckSchemaAddRemove(rel, NULL, oldtup); + + /* already checked at ExecScan? */ + if (internal) + required |= SEPG_DB_TUPLE__DELETE; + + if (required != 0) + { + + tclass = sepgsqlTupleObjectClass(relid, oldtup); + if (!sepgsqlClientHasPermsTup(relid, oldtup, tclass, + SEPG_DB_TUPLE__DELETE, + internal)) + return false; + } + + return true; +} diff -rpNU3 base/src/backend/security/sepgsql/core.c sepgsql-new/src/backend/security/sepgsql/core.c --- base/src/backend/security/sepgsql/core.c 1970-01-01 09:00:00.000000000 +0900 +++ sepgsql-new/src/backend/security/sepgsql/core.c 2009-06-10 17:43:08.000000000 +0900 @@ -0,0 +1,326 @@ +/* + * src/backend/security/sepgsql/core.c + * The core facility of SE-PostgreSQL + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ +#include "postgres.h" + +#include "catalog/indexing.h" +#include "catalog/pg_database.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_security.h" +#include "libpq/libpq-be.h" +#include "miscadmin.h" +#include "security/sepgsql.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/syscache.h" +#include "utils/tqual.h" + +static security_context_t clientLabel = NULL; +static security_context_t serverLabel = NULL; + +security_context_t +sepgsqlGetServerLabel(void) +{ + if (!serverLabel) + { + if (getcon_raw(&serverLabel) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not get server label"))); + } + return serverLabel; +} + +security_context_t +sepgsqlGetClientLabel(void) +{ + if (!clientLabel) + { + /* + * When the process is not invoked as a backend of client, + * it works as a server process and as a client process + * in same time. + */ + if (!MyProcPort) + return sepgsqlGetServerLabel(); + + /* + * SELinux provides getpeercon(3) which enables to obtain + * the security context of peer process. + * If MyProcPort->sock is unix domain socket, no special + * configuration is necessary. If it is tcp/ip socket, + * labeled IPsec or fallback context to be configured. + */ + if (getpeercon_raw(MyProcPort->sock, &clientLabel) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not obtain client label"))); + } + + return clientLabel; +} + +security_context_t +sepgsqlSwitchClient(security_context_t new_client) +{ + char *old_client = sepgsqlGetClientLabel(); + + clientLabel = new_client; + + PG_TRY(); + { + sepgsqlAvcSwitchClient(); + } + PG_CATCH(); + { + clientLabel = old_client; + PG_RE_THROW(); + } + PG_END_TRY(); + + return new_client; +} + +/* + * sepgsqlIsEnabled() + * + * returns the state of SE-PostgreSQL whether enabled, or not. + * When functions under src/backend/utils/ are invoked, they have to + * be checked on the head. + * This status is decided with two factors. The one is GUC parameter + * of "sepostgresql=on/off", and the other is is_selinux_enabled(). + * Both of them have to be true, when SE-PostgreSQL is activated. + */ +bool sepostgresql_is_enabled; /* default is false */ + +bool +sepgsqlIsEnabled(void) +{ + static int enabled = -1; /* unchecked */ + + if (!sepostgresql_is_enabled) + return false; + + if (enabled < 0) + enabled = is_selinux_enabled(); + + return enabled > 0 ? true : false; +} + +/* + * sepgsqlInitialize + */ +void +sepgsqlInitialize(void) +{ + if (!sepgsqlIsEnabled()) + return; + + sepgsqlGetClientLabel(); + + sepgsqlAvcInit(); +} + +/* + * SE-PostgreSQL specific functions + */ +Datum +sepgsql_getcon(PG_FUNCTION_ARGS) +{ + security_context_t context; + + if (!sepgsqlIsEnabled()) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: disabled now"))); + + context = sepgsqlGetClientLabel(); + context = sepgsqlTransSecLabelOut(context); + return CStringGetTextDatum(context); +} + +Datum +sepgsql_getservcon(PG_FUNCTION_ARGS) +{ + security_context_t context; + + if (!sepgsqlIsEnabled()) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: disabled now"))); + + context = sepgsqlGetServerLabel(); + context = sepgsqlTransSecLabelOut(context); + return CStringGetTextDatum(context); +} + +/* + * sepgsql_(get|set)_(user|role|type|range) + * get/set a component of security context. + */ +static void +parse_security_context(security_context_t context, + char **user, char **role, char **type, char **range) +{ + security_context_t raw_context; + char *tok; + + if (!sepgsqlIsEnabled()) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: disabled now"))); + + if (selinux_trans_to_raw_context(context, &raw_context) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not translate mls label: %s", context))); + + PG_TRY(); + { + tok = strtok(raw_context, ":"); + if (user) + *user = (!tok ? NULL : pstrdup(tok)); + + tok = strtok(NULL, ":"); + if (role) + *role = (!tok ? NULL : pstrdup(tok)); + + tok = strtok(NULL, ":"); + if (type) + *type = (!tok ? NULL : pstrdup(tok)); + + tok = strtok(NULL, "\0"); + if (range) + *range = (!tok ? NULL : pstrdup(tok)); + } + PG_CATCH(); + { + freecon(raw_context); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(raw_context); +} + +Datum +sepgsql_get_user(PG_FUNCTION_ARGS) +{ + security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); + char *user; + + parse_security_context(context, &user, NULL, NULL, NULL); + if (!user) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not extract user of \"%s\"", context))); + + PG_RETURN_TEXT_P(CStringGetTextDatum(user)); +} + +Datum +sepgsql_get_role(PG_FUNCTION_ARGS) +{ + security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); + char *role; + + parse_security_context(context, NULL, &role, NULL, NULL); + if (!role) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not extract role of \"%s\"", context))); + + PG_RETURN_TEXT_P(CStringGetTextDatum(role)); +} + +Datum +sepgsql_get_type(PG_FUNCTION_ARGS) +{ + security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); + char *type; + + parse_security_context(context, NULL, NULL, &type, NULL); + if (!type) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not extract type of \"%s\"", context))); + + PG_RETURN_TEXT_P(CStringGetTextDatum(type)); +} + +Datum +sepgsql_get_range(PG_FUNCTION_ARGS) +{ + security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); + char *range; + + parse_security_context(context, NULL, NULL, NULL, &range); + if (!range) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not extract range of \"%s\"", context))); + + PG_RETURN_TEXT_P(CStringGetTextDatum(range)); +} + +static Datum +sepgsql_set_common(char *context, char *user, char *role, char *type, char *range) +{ + StringInfoData newcon; + + parse_security_context(context, + !user ? &user : NULL, + !role ? &role : NULL, + !type ? &type : NULL, + !range ? &range : NULL); + if (!user || !role || !type) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: invalid security context: \"%s\"", context))); + + initStringInfo(&newcon); + appendStringInfo(&newcon, "%s:%s:%s", user, role, type); + if (range) + appendStringInfo(&newcon, ":%s", range); + + return CStringGetTextDatum(sepgsqlTransSecLabelOut(newcon.data)); +} + +Datum +sepgsql_set_user(PG_FUNCTION_ARGS) +{ + security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); + char *user = TextDatumGetCString(PG_GETARG_TEXT_P(1)); + + return sepgsql_set_common(context, user, NULL, NULL, NULL); +} + +Datum +sepgsql_set_role(PG_FUNCTION_ARGS) +{ + security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); + char *role = TextDatumGetCString(PG_GETARG_TEXT_P(1)); + + return sepgsql_set_common(context, NULL, role, NULL, NULL); +} + +Datum +sepgsql_set_type(PG_FUNCTION_ARGS) +{ + security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); + char *type = TextDatumGetCString(PG_GETARG_TEXT_P(1)); + + return sepgsql_set_common(context, NULL, NULL, type, NULL); +} + +Datum +sepgsql_set_range(PG_FUNCTION_ARGS) +{ + security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); + char *range = TextDatumGetCString(PG_GETARG_TEXT_P(1)); + + return sepgsql_set_common(context, NULL, NULL, NULL, range); +} diff -rpNU3 base/src/backend/security/sepgsql/dummy.c sepgsql-new/src/backend/security/sepgsql/dummy.c --- base/src/backend/security/sepgsql/dummy.c 1970-01-01 09:00:00.000000000 +0900 +++ sepgsql-new/src/backend/security/sepgsql/dummy.c 2009-03-13 18:14:33.000000000 +0900 @@ -0,0 +1,79 @@ +/* + * src/backend/utils/sepgsql/dummy.c + * A set of stubs when SE-PostgreSQL is not activated + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ +#include "postgres.h" + +#include "security/sepgsql.h" + +static Datum +unavailable_function(const char *fn_name) +{ + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("function \"%s\" is not available", fn_name))); + PG_RETURN_VOID(); +} + +Datum +sepgsql_getcon(PG_FUNCTION_ARGS) +{ + return unavailable_function(__FUNCTION__); +} + +Datum +sepgsql_getservcon(PG_FUNCTION_ARGS) +{ + return unavailable_function(__FUNCTION__); +} + +Datum +sepgsql_get_user(PG_FUNCTION_ARGS) +{ + return unavailable_function(__FUNCTION__); +} + +Datum +sepgsql_get_role(PG_FUNCTION_ARGS) +{ + return unavailable_function(__FUNCTION__); +} + +Datum +sepgsql_get_type(PG_FUNCTION_ARGS) +{ + return unavailable_function(__FUNCTION__); +} + +Datum +sepgsql_get_range(PG_FUNCTION_ARGS) +{ + return unavailable_function(__FUNCTION__); +} + +Datum +sepgsql_set_user(PG_FUNCTION_ARGS) +{ + return unavailable_function(__FUNCTION__); +} + +Datum +sepgsql_set_role(PG_FUNCTION_ARGS) +{ + return unavailable_function(__FUNCTION__); +} + +Datum +sepgsql_set_type(PG_FUNCTION_ARGS) +{ + return unavailable_function(__FUNCTION__); +} + +Datum +sepgsql_set_range(PG_FUNCTION_ARGS) +{ + return unavailable_function(__FUNCTION__); +} diff -rpNU3 base/src/backend/security/sepgsql/hooks.c sepgsql-new/src/backend/security/sepgsql/hooks.c --- base/src/backend/security/sepgsql/hooks.c 1970-01-01 09:00:00.000000000 +0900 +++ sepgsql-new/src/backend/security/sepgsql/hooks.c 2009-06-11 10:19:07.000000000 +0900 @@ -0,0 +1,905 @@ +/* + * src/backend/security/sepgsql/hooks.c + * SE-PostgreSQL security hooks + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "catalog/pg_aggregate.h" +#include "catalog/pg_am.h" +#include "catalog/pg_amop.h" +#include "catalog/pg_amproc.h" +#include "catalog/pg_cast.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_namespace.h" +#include "catalog/pg_opclass.h" +#include "catalog/pg_operator.h" +#include "catalog/pg_opfamily.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_security.h" +#include "catalog/pg_trigger.h" +#include "catalog/pg_ts_dict.h" +#include "catalog/pg_ts_parser.h" +#include "catalog/pg_ts_template.h" +#include "catalog/pg_type.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodes.h" +#include "security/sepgsql.h" +#include "storage/bufmgr.h" +#include "utils/syscache.h" +#include "utils/tqual.h" + +/* + * sepgsqlCheckDatabaseAccess + * checks db_database:{access} permission when the client logs-in + * the given database. + * + * sepgsqlCheckDatabaseSuperuser + * checks db_database:{superuser} permission when the client tries + * to perform as a superuser on the given databse. + * + * sepgsqlCheckDatabaseInstallModule + * checks db_database:{install_module} permission when the client + * tries to install a dynamic link library on the current databse. + * + * sepgsqlCheckDatabaseLoadModule + * checks capability of the database when it loads a certain DLL + * into its process address space. + */ +static bool +checkDatabaseCommon(Oid datoid, access_vector_t perms, bool abort) +{ + HeapTuple tuple; + bool rc; + + if (!sepgsqlIsEnabled()) + return true; + + tuple = SearchSysCache(DATABASEOID, + ObjectIdGetDatum(datoid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for database: %u", datoid); + + rc = sepgsqlClientHasPermsTup(DatabaseRelationId, tuple, + SEPG_CLASS_DB_DATABASE, + perms, abort); + ReleaseSysCache(tuple); + + return rc; +} + +bool +sepgsqlCheckDatabaseAccess(Oid database_oid) +{ + return checkDatabaseCommon(database_oid, + SEPG_DB_DATABASE__ACCESS, + false); +} + +bool +sepgsqlCheckDatabaseSuperuser(void) +{ + return checkDatabaseCommon(MyDatabaseId, + SEPG_DB_DATABASE__SUPERUSER, + false); +} + +void +sepgsqlCheckDatabaseInstallModule(void) +{ + checkDatabaseCommon(MyDatabaseId, + SEPG_DB_DATABASE__INSTALL_MODULE, + true); +} + +void +sepgsqlCheckDatabaseLoadModule(const char *filename) +{ + HeapTuple tuple; + security_context_t filecon; + security_context_t dbcon; + + if (!sepgsqlIsEnabled()) + return; + /* + * It assumes preloaded libraries are secure, + * because it can be set up using guc variable + * not any SQL statements. + */ + if (GetProcessingMode() == InitProcessing) + return; + + /* Get database context */ + tuple = SearchSysCache(DATABASEOID, + ObjectIdGetDatum(MyDatabaseId), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for database: %u", MyDatabaseId); + + dbcon = securityRawSecLabelOut(DatabaseRelationId, + HeapTupleGetSecLabel(tuple)); + ReleaseSysCache(tuple); + + /* Get library context */ + if (getfilecon_raw(filename, &filecon) < 0) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not access file \"%s\": %m", filename))); + PG_TRY(); + { + sepgsqlComputePerms(dbcon, + filecon, + SEPG_CLASS_DB_DATABASE, + SEPG_DB_DATABASE__LOAD_MODULE, + filename, true); + } + PG_CATCH(); + { + freecon(filecon); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(filecon); +} + +/* + * sepgsqlCheckSchemaSearch + * checks db_schema:{search} permission when the given namespace + * is searched. It is not available on temporary namespace due to + * the limitation of implementation. + * + * sepgsqlCheckSchemaAddRemove + * checks db_schema:{add_object} and db_schema:{remove_object} + * permission when a database object within a certain schema + * is added or removed. + */ +static bool +sepgsqlCheckSchemaCommon(Oid nsid, access_vector_t required, bool abort) +{ + security_class_t tclass; + HeapTuple tuple; + bool rc; + + tuple = SearchSysCache(NAMESPACEOID, + ObjectIdGetDatum(nsid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for namespace: %u", nsid); + + tclass = sepgsqlTupleObjectClass(NamespaceRelationId, tuple); + rc = sepgsqlClientHasPermsTup(NamespaceRelationId, tuple, + tclass, required, false); + ReleaseSysCache(tuple); + + return rc; +} + +bool +sepgsqlCheckSchemaSearch(Oid nsid) +{ + if (!sepgsqlIsEnabled()) + return true; + + return sepgsqlCheckSchemaCommon(nsid, SEPG_DB_SCHEMA__SEARCH, false); +} + +static void +checkSchemaAddRemove(Oid nsid, bool remove) +{ + if (IsBootstrapProcessingMode() || !OidIsValid(nsid)) + return; + + sepgsqlCheckSchemaCommon(nsid, !remove + ? SEPG_DB_SCHEMA__ADD_OBJECT + : SEPG_DB_SCHEMA__REMOVE_OBJECT, + true); +} + +#define CHECK_SCHEMA_ADD_REMOVE(catalog,member,newtup,oldtup) \ + do { \ + Oid nsid_new = !HeapTupleIsValid(newtup) ? InvalidOid \ + : (((Form_##catalog) GETSTRUCT(newtup))->member); \ + Oid nsid_old = !HeapTupleIsValid(oldtup) ? InvalidOid \ + : (((Form_##catalog) GETSTRUCT(oldtup))->member); \ + if (nsid_new != nsid_old) \ + { \ + checkSchemaAddRemove(nsid_old, true); \ + checkSchemaAddRemove(nsid_new, false); \ + } \ + } while(0) + +void +sepgsqlCheckSchemaAddRemove(Relation rel, HeapTuple newtup, HeapTuple oldtup) +{ + switch (RelationGetRelid(rel)) + { + case RelationRelationId: + CHECK_SCHEMA_ADD_REMOVE(pg_class,relnamespace,newtup,oldtup); + break; + case ConstraintRelationId: + CHECK_SCHEMA_ADD_REMOVE(pg_constraint,connamespace,newtup,oldtup); + break; + case ConversionRelationId: + CHECK_SCHEMA_ADD_REMOVE(pg_conversion,connamespace,newtup,oldtup); + break; + case OperatorClassRelationId: + CHECK_SCHEMA_ADD_REMOVE(pg_opclass,opcnamespace,newtup,oldtup); + break; + case OperatorRelationId: + CHECK_SCHEMA_ADD_REMOVE(pg_operator,oprnamespace,newtup,oldtup); + break; + case OperatorFamilyRelationId: + CHECK_SCHEMA_ADD_REMOVE(pg_opfamily,opfnamespace,newtup,oldtup); + break; + case ProcedureRelationId: + CHECK_SCHEMA_ADD_REMOVE(pg_proc,pronamespace,newtup,oldtup); + break; + case TSDictionaryRelationId: + CHECK_SCHEMA_ADD_REMOVE(pg_ts_dict,dictnamespace,newtup,oldtup); + break; + case TSParserRelationId: + CHECK_SCHEMA_ADD_REMOVE(pg_ts_parser,prsnamespace,newtup,oldtup); + break; + case TSTemplateRelationId: + CHECK_SCHEMA_ADD_REMOVE(pg_ts_template,tmplnamespace,newtup,oldtup); + break; + case TypeRelationId: + CHECK_SCHEMA_ADD_REMOVE(pg_type,typnamespace,newtup,oldtup); + break; + default: + /* do nothing */ + break; + } +} + +/* + * sepgsqlCheckTableLock + * checks db_table:{lock} permission when the client tries to + * aquire explicit lock on the given relation. + * + * sepgsqlCheckTableTruncate + * checks db_table:{delete} permission when the client tries to + * truncate the given relation. + * + * sepgsqlCheckTableReference + * checks db_table:{reference} and db_column:{reference} permission + * when the client tries to set up a foreign key constraint on the + * certain tables and columns. + */ +static void +checkTableCommon(Oid table_oid, access_vector_t perms) +{ + security_class_t tclass; + HeapTuple tuple; + + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(table_oid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SELinux: cache lookup failed for relation %u", table_oid); + + tclass = sepgsqlTupleObjectClass(RelationRelationId, tuple); + if (tclass == SEPG_CLASS_DB_TABLE) + { + sepgsqlClientHasPermsTup(RelationRelationId, tuple, + SEPG_CLASS_DB_TABLE, + perms, true); + } + ReleaseSysCache(tuple); +} + +void +sepgsqlCheckTableLock(Oid table_oid) +{ + if (!sepgsqlIsEnabled()) + return; + + /* check db_table:{lock} permission */ + checkTableCommon(table_oid, SEPG_DB_TABLE__LOCK); +} + +void +sepgsqlCheckTableTruncate(Relation rel) +{ + HeapScanDesc scan; + HeapTuple tuple; + security_class_t tclass; + + if (!sepgsqlIsEnabled()) + return; + + /* check db_table:{delete} permission */ + checkTableCommon(RelationGetRelid(rel), SEPG_DB_TABLE__DELETE); + + /* check db_tuple:{delete} permission */ + scan = heap_beginscan(rel, SnapshotNow, 0, NULL); + + while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + { + tclass = sepgsqlTupleObjectClass(RelationGetRelid(rel), tuple); + sepgsqlClientHasPermsTup(RelationGetRelid(rel), tuple, tclass, + SEPG_DB_TUPLE__DELETE, true); + } + heap_endscan(scan); +} + +void +sepgsqlCheckTableReference(Relation rel, int16 *attnums, int natts) +{ + HeapTuple tuple; + int i; + + if (!sepgsqlIsEnabled()) + return; + + /* check db_table:{reference} permission */ + checkTableCommon(RelationGetRelid(rel), SEPG_DB_TABLE__REFERENCE); + + /* check db_column:{reference} permission */ + for (i=0; i < natts; i++) + { + tuple = SearchSysCache(ATTNUM, + RelationGetRelid(rel), + attnums[i], 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for attribute %u of %s", + attnums[i], RelationGetRelationName(rel)); + + sepgsqlClientHasPermsTup(AttributeRelationId, tuple, + SEPG_CLASS_DB_COLUMN, + SEPG_DB_COLUMN__REFERENCE, + true); + ReleaseSysCache(tuple); + } +} + +/* + * sepgsqlCheckSequenceGetValue + * checks db_sequence:{get_value} permission when the client + * refers the given sequence object without any increments. + * + * sepgsqlCheckSequenceNextValue + * checks db_sequence:{next_value} permission when the client + * fetchs a value from the given sequence object with an + * increment of the counter. + * + * sepgsqlCheckSequenceSetValue + * checks db_sequence:{set_value} permission when the client + * set a discretionary value on the given sequence object. + */ +static void +sepgsqlCheckSequenceCommon(Oid seqid, access_vector_t required) +{ + HeapTuple tuple; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(seqid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for sequence: %u", seqid); + + sepgsqlClientHasPermsTup(RelationRelationId, tuple, + SEPG_CLASS_DB_SEQUENCE, + required, true); + ReleaseSysCache(tuple); +} + +void sepgsqlCheckSequenceGetValue(Oid seqid) +{ + sepgsqlCheckSequenceCommon(seqid, SEPG_DB_SEQUENCE__GET_VALUE); +} + +void sepgsqlCheckSequenceNextValue(Oid seqid) +{ + sepgsqlCheckSequenceCommon(seqid, SEPG_DB_SEQUENCE__NEXT_VALUE); +} + +void sepgsqlCheckSequenceSetValue(Oid seqid) +{ + sepgsqlCheckSequenceCommon(seqid, SEPG_DB_SEQUENCE__SET_VALUE); +} + +/* + * sepgsqlCheckProcedureExecute + * checks db_procedure:{execute} permission when the client tries + * to invoke the given SQL function. + */ +bool sepgsqlCheckProcedureExecute(Oid proc_oid) +{ + HeapTuple tuple; + bool rc; + + if (!sepgsqlIsEnabled()) + return true; + + /* + * check db_procedure:{execute} permission + */ + tuple = SearchSysCache(PROCOID, + ObjectIdGetDatum(proc_oid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SELinux: cache lookup failed for procedure: %u", proc_oid); + + rc = sepgsqlClientHasPermsTup(ProcedureRelationId, tuple, + SEPG_CLASS_DB_PROCEDURE, + SEPG_DB_PROCEDURE__EXECUTE, + false); + ReleaseSysCache(tuple); + + return rc; +} + +/* + * sepgsqlCheckProcedureEntrypoint + * checks whether the given function call causes domain transition, + * or not. If it needs a domain transition, it injects a wrapper + * function to invoke it under new domain. + */ +struct TrustedProcedureCache +{ + FmgrInfo flinfo; + char newcon[1]; +}; + +static Datum +sepgsqlTrustedProcedure(PG_FUNCTION_ARGS) +{ + struct TrustedProcedureCache *tcache; + security_context_t save_context; + FmgrInfo *save_flinfo; + Datum result; + + tcache = fcinfo->flinfo->fn_extra; + Assert(tcache != NULL); + + save_context = sepgsqlSwitchClient(tcache->newcon); + save_flinfo = fcinfo->flinfo; + fcinfo->flinfo = &tcache->flinfo; + + PG_TRY(); + { + result = FunctionCallInvoke(fcinfo); + } + PG_CATCH(); + { + sepgsqlSwitchClient(save_context); + fcinfo->flinfo = save_flinfo; + PG_RE_THROW(); + } + PG_END_TRY(); + sepgsqlSwitchClient(save_context); + fcinfo->flinfo = save_flinfo; + + return result; +} + +void +sepgsqlCheckProcedureEntrypoint(FmgrInfo *flinfo, HeapTuple protup) +{ + struct TrustedProcedureCache *tcache; + security_context_t newcon; + + if (!sepgsqlIsEnabled()) + return; + + newcon = sepgsqlClientCreateLabel(ProcedureRelationId, + HeapTupleGetSecLabel(protup), + SEPG_CLASS_PROCESS); + + /* Do nothing, if it is not a trusted procedure */ + if (strcmp(newcon, sepgsqlGetClientLabel()) == 0) + return; + + /* check db_procedure:{entrypoint} */ + sepgsqlClientHasPermsTup(ProcedureRelationId, protup, + SEPG_CLASS_DB_PROCEDURE, + SEPG_DB_PROCEDURE__ENTRYPOINT, + true); + + /* check process:{transition} */ + sepgsqlComputePerms(sepgsqlGetClientLabel(), + newcon, + SEPG_CLASS_PROCESS, + SEPG_PROCESS__TRANSITION, + NULL, true); + + /* setup trusted procedure */ + tcache = MemoryContextAllocZero(flinfo->fn_mcxt, + sizeof(*tcache) + strlen(newcon)); + memcpy(&tcache->flinfo, flinfo, sizeof(*flinfo)); + strcpy(tcache->newcon, newcon); + flinfo->fn_addr = sepgsqlTrustedProcedure; + flinfo->fn_extra = tcache; +} + +/* + * sepgsqlCheckProcedureInstall + * checks permission: db_procedure:{install}, when client tries to modify + * a system catalog which contains procedure id to invoke it later. + * Because these functions are invoked internally, to search a table with + * a special index algorithm for example, the security policy has to prevent + * malicious user-defined functions to be installed. + */ +static void +checkProcedureInstall(Oid proc_oid) +{ + HeapTuple tuple; + + if (!OidIsValid(proc_oid)) + return; + + /* + * NOTE: we assume all the function installed + * during bootstraping mode can be trusted. + */ + if (IsBootstrapProcessingMode()) + return; + + tuple = SearchSysCache(PROCOID, + ObjectIdGetDatum(proc_oid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + return; + + sepgsqlClientHasPermsTup(ProcedureRelationId, tuple, + SEPG_CLASS_DB_PROCEDURE, + SEPG_DB_PROCEDURE__INSTALL, + true); + ReleaseSysCache(tuple); +} + +#define CHECK_PROC_INSTALL_PERM(catalog,member,newtup,oldtup) \ + do { \ + if (!HeapTupleIsValid(oldtup)) \ + checkProcedureInstall(((Form_##catalog) GETSTRUCT(newtup))->member); \ + else if (((Form_##catalog) GETSTRUCT(newtup))->member \ + != ((Form_##catalog) GETSTRUCT(oldtup))->member) \ + checkProcedureInstall(((Form_##catalog) GETSTRUCT(newtup))->member); \ + } while(0) + +void +sepgsqlCheckProcedureInstall(Relation rel, HeapTuple newtup, HeapTuple oldtup) +{ + /* + * db_procedure:{install} check prevent a malicious functions + * to be installed, as a part of system catalogs. + * It is necessary to prevent other person implicitly to invoke + * malicious functions. + */ + switch (RelationGetRelid(rel)) + { + case AggregateRelationId: + /* + * db_procedure:{execute} is checked on invocations of: + * pg_aggregate.aggfnoid + * pg_aggregate.aggtransfn + * pg_aggregate.aggfinalfn + */ + break; + + case AccessMethodRelationId: + CHECK_PROC_INSTALL_PERM(pg_am, aminsert, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_am, ambeginscan, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_am, amgettuple, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_am, amgetmulti, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_am, amrescan, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_am, amendscan, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_am, ammarkpos, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_am, amrestrpos, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_am, ambuild, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_am, ambulkdelete, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_am, amvacuumcleanup, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_am, amcostestimate, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_am, amoptions, newtup, oldtup); + break; + + case AccessMethodProcedureRelationId: + CHECK_PROC_INSTALL_PERM(pg_amproc, amproc, newtup, oldtup); + break; + + case CastRelationId: + CHECK_PROC_INSTALL_PERM(pg_cast, castfunc, newtup, oldtup); + break; + + case ConversionRelationId: + CHECK_PROC_INSTALL_PERM(pg_conversion, conproc, newtup, oldtup); + break; + + case LanguageRelationId: + CHECK_PROC_INSTALL_PERM(pg_language, lanplcallfoid, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_language, lanvalidator, newtup, oldtup); + break; + + case OperatorRelationId: + CHECK_PROC_INSTALL_PERM(pg_operator, oprcode, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_operator, oprrest, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_operator, oprjoin, newtup, oldtup); + break; + + case TriggerRelationId: + CHECK_PROC_INSTALL_PERM(pg_trigger, tgfoid, newtup, oldtup); + break; + + case TSParserRelationId: + CHECK_PROC_INSTALL_PERM(pg_ts_parser, prsstart, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_ts_parser, prstoken, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_ts_parser, prsend, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_ts_parser, prsheadline, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_ts_parser, prslextype, newtup, oldtup); + break; + + case TSTemplateRelationId: + CHECK_PROC_INSTALL_PERM(pg_ts_template, tmplinit, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_ts_template, tmpllexize, newtup, oldtup); + break; + + case TypeRelationId: + CHECK_PROC_INSTALL_PERM(pg_type, typinput, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_type, typoutput, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_type, typreceive, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_type, typsend, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_type, typmodin, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_type, typmodout, newtup, oldtup); + CHECK_PROC_INSTALL_PERM(pg_type, typanalyze, newtup, oldtup); + break; + } +} + +/* + * sepgsqlCheckBlobCreate + * assigns a default security label and checks db_blob:{create} + */ +void +sepgsqlCheckBlobCreate(Relation rel, HeapTuple lotup) +{ + if (!sepgsqlIsEnabled()) + return; + + /* set a default security context */ + sepgsqlSetDefaultSecLabel(rel, lotup); + sepgsqlClientHasPermsTup(RelationGetRelid(rel), lotup, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__CREATE, + true); +} + +/* + * sepgsqlCheckBlobDrop + * checks db_blob:{drop} permission + */ +void +sepgsqlCheckBlobDrop(Relation rel, HeapTuple lotup) +{ + if (!sepgsqlIsEnabled()) + return; + + sepgsqlClientHasPermsTup(RelationGetRelid(rel), lotup, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__DROP, + true); +} + +/* + * sepgsqlCheckBlobRead + * checks db_blob:{read} permission + */ +void +sepgsqlCheckBlobRead(LargeObjectDesc *lobj) +{ + if (!sepgsqlIsEnabled()) + return; + + sepgsqlClientHasPermsSid(LargeObjectRelationId, + lobj->secid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__READ, + NULL, true); +} + +/* + * sepgsqlCheckBlobWrite + * check db_blob:{write} permission + */ +void +sepgsqlCheckBlobWrite(LargeObjectDesc *lobj) +{ + if (!sepgsqlIsEnabled()) + return; + + sepgsqlClientHasPermsSid(LargeObjectRelationId, + lobj->secid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__WRITE, + NULL, true); +} + +/* + * sepgsqlCheckBlobGetattr + * check db_blob:{getattr} permission + */ +void +sepgsqlCheckBlobGetattr(HeapTuple tuple) +{ + if (!sepgsqlIsEnabled()) + return; + + sepgsqlClientHasPermsTup(LargeObjectRelationId, tuple, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__GETATTR, + true); +} + +/* + * sepgsqlCheckBlobSetattr + * check db_blob:{setattr} permission + */ +void +sepgsqlCheckBlobSetattr(HeapTuple tuple) +{ + if (!sepgsqlIsEnabled()) + return; + + sepgsqlClientHasPermsTup(LargeObjectRelationId, tuple, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__SETATTR, + true); +} + +/* + * sepgsqlCheckBlobExport + * check db_blob:{read export} and file:{write} permission + */ +void +sepgsqlCheckBlobExport(LargeObjectDesc *lobj, + int fdesc, const char *filename) +{ + if (!sepgsqlIsEnabled()) + return; + + /* db_blob:{read export} */ + sepgsqlClientHasPermsSid(LargeObjectRelationId, + lobj->secid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__READ | SEPG_DB_BLOB__EXPORT, + NULL, true); + /* file:{write} */ + sepgsqlCheckFileWrite(fdesc, filename); +} + +/* + * sepgsqlCheckBlobImport + * check db_blob:{write import} and file:{read} permission + */ +void +sepgsqlCheckBlobImport(LargeObjectDesc *lobj, + int fdesc, const char *filename) +{ + if (!sepgsqlIsEnabled()) + return; + + /* db_blob:{write import} */ + sepgsqlClientHasPermsSid(LargeObjectRelationId, + lobj->secid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__WRITE | SEPG_DB_BLOB__IMPORT, + NULL, true); + /* file:{read} */ + sepgsqlCheckFileRead(fdesc, filename); +} + +/* + * sepgsqlCheckBlobRelabel + * check db_blob:{setattr relabelfrom relabelto} + */ +void +sepgsqlCheckBlobRelabel(HeapTuple oldtup, HeapTuple newtup) +{ + access_vector_t required = SEPG_DB_BLOB__SETATTR; + + if (HeapTupleGetSecLabel(oldtup) != HeapTupleGetSecLabel(newtup)) + required |= SEPG_DB_BLOB__RELABELFROM; + + sepgsqlClientHasPermsTup(LargeObjectRelationId, oldtup, + SEPG_CLASS_DB_BLOB, + required, + true); + if ((required & SEPG_DB_BLOB__RELABELFROM) == 0) + return; + + sepgsqlClientHasPermsTup(LargeObjectRelationId, newtup, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__RELABELTO, + true); +} + +/* + * sepgsqlCheckFileRead + * sepgsqlCheckFileWrite + * check file:{read} or file:{write} permission on the given file, + * and raises an error if violated. + */ +static void +checkFileCommon(int fdesc, const char *filename, access_vector_t perms) +{ + security_context_t context; + security_class_t tclass; + + if (!sepgsqlIsEnabled()) + return; + + tclass = sepgsqlFileObjectClass(fdesc); + + if (fgetfilecon_raw(fdesc, &context) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not get context of %s", filename))); + PG_TRY(); + { + sepgsqlComputePerms(sepgsqlGetClientLabel(), + context, + tclass, + perms, + filename, true); + } + PG_CATCH(); + { + freecon(context); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(context); +} + +void +sepgsqlCheckFileRead(int fdesc, const char *filename) +{ + checkFileCommon(fdesc, filename, SEPG_FILE__READ); +} + +void +sepgsqlCheckFileWrite(int fdesc, const char *filename) +{ + checkFileCommon(fdesc, filename, SEPG_FILE__WRITE); +} + +/* + * sepgsqlAllowFunctionInlined + * It provides the optimizer a hint whether the given SQL function + * can be inlined, or not. If it can be configured as a trusted + * procedure, we should not allow it inlined. + */ +bool +sepgsqlAllowFunctionInlined(HeapTuple protup) +{ + security_context_t newcon; + + if (!sepgsqlIsEnabled()) + return true; + + newcon = sepgsqlClientCreateLabel(ProcedureRelationId, + HeapTupleGetSecLabel(protup), + SEPG_CLASS_PROCESS); + /* + * If the security context of client is unchange + * before or after invocation of the functions, + * it is not a trusted procedure, so it can be + * inlined due to performance purpose. + */ + if (strcmp(sepgsqlGetClientLabel(), newcon) == 0) + return true; + + return false; +} diff -rpNU3 base/src/backend/security/sepgsql/label.c sepgsql-new/src/backend/security/sepgsql/label.c --- base/src/backend/security/sepgsql/label.c 1970-01-01 09:00:00.000000000 +0900 +++ sepgsql-new/src/backend/security/sepgsql/label.c 2009-06-19 15:02:12.000000000 +0900 @@ -0,0 +1,560 @@ +/* + * src/backend/security/sepgsql/label.c + * SE-PostgreSQL security label management + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ +#include "postgres.h" + +#include "access/xact.h" +#include "catalog/catalog.h" +#include "catalog/indexing.h" +#include "catalog/pg_attribute.h" +#include "catalog/pg_class.h" +#include "catalog/pg_database.h" +#include "catalog/pg_largeobject.h" +#include "catalog/pg_namespace.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_security.h" +#include "catalog/pg_type.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "security/sepgsql.h" +#include "storage/fd.h" +#include "utils/builtins.h" +#include "utils/memutils.h" +#include "utils/syscache.h" + +/* GUC: to turn on/off row level controls in SE-PostgreSQL */ +bool sepostgresql_row_level; + +/* GUC parameter to turn on/off mcstrans */ +bool sepostgresql_use_mcstrans; + +/* + * sepgsqlTupleDescHasSecLabel + * + * returns a hint whether we should allocate a field to store + * security label on the given relation, or not. + */ +bool +sepgsqlTupleDescHasSecLabel(Relation rel) +{ + if (!sepgsqlIsEnabled()) + return false; + + if (rel == NULL) + return sepostgresql_row_level; /* target of SELECT INTO */ + + if (RelationGetForm(rel)->relkind != RELKIND_RELATION) + return false; + + if (RelationGetRelid(rel) == DatabaseRelationId || + RelationGetRelid(rel) == NamespaceRelationId || + RelationGetRelid(rel) == RelationRelationId || + RelationGetRelid(rel) == AttributeRelationId || + RelationGetRelid(rel) == ProcedureRelationId) + return true; + + return sepostgresql_row_level; +} + +/* + * sepgsqlSetDefaultSecLabel + * + * assigns a default security context for the newly inserted tuple. + */ +static Oid +defaultDatabaseSecLabel(void) +{ + security_context_t context; + char filename[MAXPGPATH]; + char buffer[512], *ptype, *tmp; + FILE *filp; + + /* + * NOTE: A special handling is necessary to determine the default + * label for db_database obejct class because it does not have + * its parent object, so we cannot apply normal type transition + * here. At first, it tries to fetch the default context from the + * configuration file of selinux-policy. If it is invalid, we + * determine it based on only the context of client (compatible + * behavior). + */ + if (selinux_getpolicytype(&ptype) < 0) + goto fallback; + + snprintf(filename, sizeof(filename), + "%s%s/contexts/sepgsql_context", selinux_path(), ptype); + filp = AllocateFile(filename, PG_BINARY_R); + if (!filp) + goto fallback; + + while (fgets(buffer, sizeof(buffer), filp) != NULL) + { + tmp = strchr(buffer, '#'); + if (tmp) + *tmp = '\0'; + + context = strtok(buffer, " \t\n\r"); + if (!context) + continue; + + /* An entry found */ + FreeFile(filp); + return securityTransSecLabelIn(DatabaseRelationId, context); + } + FreeFile(filp); + +fallback: + context = sepgsqlComputeCreate(sepgsqlGetClientLabel(), + sepgsqlGetClientLabel(), + SEPG_CLASS_DB_DATABASE); + return securityTransSecLabelIn(DatabaseRelationId, context); +} + +static Oid +defaultSecLabelWithDatabase(Oid relid, Oid datoid, security_class_t tclass) +{ + HeapTuple tuple; + Oid datsid; + + if (IsBootstrapProcessingMode()) + { + static Oid cached = InvalidOid; + + if (!OidIsValid(cached)) + cached = defaultDatabaseSecLabel(); + datsid = cached; + } + else + { + tuple = SearchSysCache(DATABASEOID, + ObjectIdGetDatum(datoid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for database: %u", datoid); + datsid = HeapTupleGetSecLabel(tuple); + + ReleaseSysCache(tuple); + } + + return sepgsqlClientCreateSecid(DatabaseRelationId, datsid, + tclass, relid); +} + +static Oid +defaultSchemaSecLabel(void) +{ + return defaultSecLabelWithDatabase(NamespaceRelationId, + MyDatabaseId, + SEPG_CLASS_DB_SCHEMA); +} + +static Oid +defaultSchemaTempSecLabel(void) +{ + return defaultSecLabelWithDatabase(NamespaceRelationId, + MyDatabaseId, + SEPG_CLASS_DB_SCHEMA_TEMP); +} + +static Oid +defaultSecLabelWithSchema(Oid relid, Oid nspoid, security_class_t tclass) +{ + HeapTuple tuple; + Oid nspsid; + + if (IsBootstrapProcessingMode()) + { + static Oid cached = InvalidOid; + + if (!OidIsValid(cached)) + cached = defaultSchemaSecLabel(); + nspsid = cached; + } + else + { + tuple = SearchSysCache(NAMESPACEOID, + ObjectIdGetDatum(nspoid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for namespace: %u", nspoid); + + nspsid = HeapTupleGetSecLabel(tuple); + + ReleaseSysCache(tuple); + } + + return sepgsqlClientCreateSecid(NamespaceRelationId, nspsid, + tclass, relid); +} + +static Oid +defaultTableSecLabel(Oid nspoid) +{ + return defaultSecLabelWithSchema(RelationRelationId, + nspoid, + SEPG_CLASS_DB_TABLE); +} + +static Oid +defaultSequenceSecLabel(Oid nspoid) +{ + return defaultSecLabelWithSchema(RelationRelationId, + nspoid, + SEPG_CLASS_DB_SEQUENCE); +} + +static Oid +defaultProcedureSecLabel(Oid nspoid) +{ + return defaultSecLabelWithSchema(ProcedureRelationId, + nspoid, + SEPG_CLASS_DB_PROCEDURE); +} + +static Oid +defaultSecLabelWithTable(Oid relid, Oid tbloid, security_class_t tclass) +{ + HeapTuple tuple; + Oid tblsid; + + if (IsBootstrapProcessingMode() + && (tbloid == TypeRelationId || + tbloid == ProcedureRelationId || + tbloid == AttributeRelationId || + tbloid == RelationRelationId)) + { + static Oid cached = InvalidOid; + + if (!OidIsValid(cached)) + cached = defaultTableSecLabel(PG_CATALOG_NAMESPACE); + tblsid = cached; + } + else + { + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(tbloid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation: %u", tbloid); + + tblsid = HeapTupleGetSecLabel(tuple); + + ReleaseSysCache(tuple); + } + + return sepgsqlClientCreateSecid(RelationRelationId, tblsid, + tclass, relid); +} + +static Oid +defaultColumnSecLabel(Oid tbloid) +{ + return defaultSecLabelWithTable(AttributeRelationId, + tbloid, + SEPG_CLASS_DB_COLUMN); +} + +static Oid +defaultTupleSecLabel(Oid relid) +{ + return defaultSecLabelWithTable(relid, + relid, + SEPG_CLASS_DB_TUPLE); +} + +static Oid +defaultBlobSecLabel(void) +{ + /* + * NOTE: + * A binary largeobject has its characteristic which has + * one-to-any relationship between itself and tuples. + * In other word, a large object consists of multiple + * tuple, and the security context of thr first page + * represents whole of the binary largeobject. + * It requires all the pages within a single largeobejct + * to have identical security context, and the assumption + * is kept by a hardwired rule which prevent to manipulate + * pg_largeobject system catalog by hand. + * + * The security context of the first page is copied to + * write new pages, so the default security context is + * only asked when we create a new largeobject. + */ + return defaultSecLabelWithDatabase(LargeObjectRelationId, + MyDatabaseId, + SEPG_CLASS_DB_BLOB); +} + +extern void +sepgsqlSetDefaultSecLabel(Relation rel, HeapTuple tuple) +{ + Form_pg_class clsForm; + Form_pg_proc proForm; + Form_pg_attribute attForm; + Oid relid = RelationGetRelid(rel); + Oid newsid; + + Assert(HeapTupleHasSecLabel(tuple)); + + switch (sepgsqlTupleObjectClass(relid, tuple)) + { + case SEPG_CLASS_DB_DATABASE: + newsid = defaultDatabaseSecLabel(); + break; + + case SEPG_CLASS_DB_SCHEMA: + newsid = defaultSchemaSecLabel(); + break; + + case SEPG_CLASS_DB_SCHEMA_TEMP: + newsid = defaultSchemaTempSecLabel(); + break; + + case SEPG_CLASS_DB_TABLE: + clsForm = (Form_pg_class) GETSTRUCT(tuple); + newsid = defaultTableSecLabel(clsForm->relnamespace); + break; + + case SEPG_CLASS_DB_SEQUENCE: + clsForm = (Form_pg_class) GETSTRUCT(tuple); + newsid = defaultSequenceSecLabel(clsForm->relnamespace); + break; + + case SEPG_CLASS_DB_PROCEDURE: + proForm = (Form_pg_proc) GETSTRUCT(tuple); + newsid = defaultProcedureSecLabel(proForm->pronamespace); + break; + + case SEPG_CLASS_DB_COLUMN: + attForm = (Form_pg_attribute) GETSTRUCT(tuple); + newsid = defaultColumnSecLabel(attForm->attrelid); + break; + + case SEPG_CLASS_DB_BLOB: + newsid = defaultBlobSecLabel(); + break; + + default: /* SEPG_CLASS_DB_TUPLE */ + newsid = defaultTupleSecLabel(relid); + break; + } + + HeapTupleSetSecLabel(tuple, newsid); +} + +/* + * sepgsqlMetaSecurityLabel + * It returns a security label of tuples within pg_security system + * catalog. The purpose of this special handling is to avoid infinite + * function invocations to insert new entry for meta security labels. + */ +char * +sepgsqlMetaSecurityLabel(bool shared) +{ + Oid secrelid; + HeapTuple tuple; + security_context_t tcontext; + Oid tsecid; + + if (!sepgsqlIsEnabled()) + return NULL; + + secrelid = SecurityRelationId; + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(secrelid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SELinux: cache lookup failed for relation: pg_security"); + + tsecid = HeapTupleGetSecLabel(tuple); + tcontext = securityRawSecLabelOut(RelationRelationId, tsecid); + + ReleaseSysCache(tuple); + + return sepgsqlComputeCreate(sepgsqlGetServerLabel(), + tcontext, + SEPG_CLASS_DB_TUPLE); +} + +/* + * sepgsqlGivenSecLabelIn + * translate a given security label in text form into a security + * identifier. It can raise an error, if its format is violated, + * but permission checks are done later. + */ +Oid +sepgsqlGivenSecLabelIn(Oid relid, DefElem *defel) +{ + if (!defel) + return InvalidOid; + + if (!sepgsqlIsEnabled()) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux is disabled now"))); + + return securityTransSecLabelIn(relid, strVal(defel->arg)); +} + +/* + * sepgsqlParseCreateStmtSecLabelIn + * picks up the given security context using CREATE TABLE and + * SECURITY_LABEL enhancement. It returns a DefElem list. + */ +List * +sepgsqlParseCreateStmtSecLabelIn(CreateStmt *stmt) +{ + List *results = NIL; + ListCell *l; + DefElem *defel; + Oid secid; + + if (stmt->secLabel) + { + defel = (DefElem *) stmt->secLabel; + Assert(IsA(defel, DefElem)); + + secid = sepgsqlGivenSecLabelIn(RelationRelationId, defel); + defel = makeDefElem(NULL, makeInteger(secid)); + results = lappend(results, defel); + } + + foreach (l, stmt->tableElts) + { + ColumnDef *cdef = lfirst(l); + + if (cdef->secLabel) + { + defel = (DefElem *) cdef->secLabel; + Assert(IsA(defel, DefElem)); + + secid = sepgsqlGivenSecLabelIn(AttributeRelationId, defel); + defel = makeDefElem(pstrdup(cdef->colname), + makeInteger(secid)); + results = lappend(results, defel); + } + } + + return results; +} + +/* + * sepgsqlRawSecLabelIn + * correctness checks for the given security context + */ +security_context_t +sepgsqlRawSecLabelIn(security_context_t seclabel) +{ + if (!sepgsqlIsEnabled()) + return seclabel; + + if (!seclabel || security_check_context_raw(seclabel) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("Invalid security context: \"%s\"", seclabel))); + + return seclabel; +} + +/* + * sepgsqlRawSecLabelOut + * correctness checks for the given security context, + * and replace it if invalid security context + */ +security_context_t +sepgsqlRawSecLabelOut(security_context_t seclabel) +{ + if (!sepgsqlIsEnabled()) + return seclabel; + + if (!seclabel || security_check_context_raw(seclabel) < 0) + { + security_context_t unlabeledcon; + + if (security_get_initial_context_raw("unlabeled", + &unlabeledcon) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("Unabled to get unlabeled security context"))); + PG_TRY(); + { + seclabel = pstrdup(unlabeledcon); + } + PG_CATCH(); + { + freecon(unlabeledcon); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(unlabeledcon); + } + return seclabel; +} + +/* + * sepgsqlTransSecLabelIn + * sepgsqlTransSecLabelOut + * translation between human-readable and raw format + */ +security_context_t +sepgsqlTransSecLabelIn(security_context_t seclabel) +{ + security_context_t rawlabel; + security_context_t result; + + if (!sepgsqlIsEnabled() || + !sepostgresql_use_mcstrans) + return seclabel; + + if (selinux_trans_to_raw_context(seclabel, &rawlabel) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: failed to translate \"%s\"", seclabel))); + PG_TRY(); + { + result = pstrdup(rawlabel); + } + PG_CATCH(); + { + freecon(rawlabel); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(rawlabel); + + return result; +} + +security_context_t +sepgsqlTransSecLabelOut(security_context_t seclabel) +{ + security_context_t translabel; + security_context_t result; + + if (!sepgsqlIsEnabled() || + !sepostgresql_use_mcstrans) + return seclabel; + + if (selinux_raw_to_trans_context(seclabel, &translabel) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: failed to translate \"%s\"", seclabel))); + PG_TRY(); + { + result = pstrdup(translabel); + } + PG_CATCH(); + { + freecon(translabel); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(translabel); + + return result; +} diff -rpNU3 base/src/backend/security/sepgsql/perms.c sepgsql-new/src/backend/security/sepgsql/perms.c --- base/src/backend/security/sepgsql/perms.c 1970-01-01 09:00:00.000000000 +0900 +++ sepgsql-new/src/backend/security/sepgsql/perms.c 2009-05-20 17:08:02.000000000 +0900 @@ -0,0 +1,469 @@ +/* + * src/backend/utils/sepgsql/perms.c + * SE-PostgreSQL permission checks + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ +#include "postgres.h" + +#include "catalog/indexing.h" +#include "catalog/pg_database.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_language.h" +#include "catalog/pg_largeobject.h" +#include "catalog/pg_namespace.h" +#include "catalog/pg_type.h" +#include "miscadmin.h" +#include "security/sepgsql.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" +#include + + +/* + * Dynamic object class/permissions mapping + * + * SELinux exports the list of object classes and permissions at + * /selinux/class. The libselinux provides an interface to translate + * between their names and codes. + */ +static struct +{ + const char *class_name; + security_class_t class_code; + struct + { + const char *perm_name; + access_vector_t perm_code; + } av[sizeof(access_vector_t) * 8]; +} selinux_catalog[] = { + { + "process", SEPG_CLASS_PROCESS, + { + {"translation", SEPG_PROCESS__TRANSITION }, + {NULL, 0} + } + }, + { + "file", SEPG_CLASS_FILE, + { + {"read", SEPG_FILE__READ }, + {"write", SEPG_FILE__WRITE }, + {NULL, 0} + } + }, + { + "dir", SEPG_CLASS_DIR, + { + {"read", SEPG_DIR__READ }, + {"write", SEPG_DIR__WRITE }, + {NULL,0} + } + }, + { + "lnk_file", SEPG_CLASS_LNK_FILE, + { + {"read", SEPG_LNK_FILE__READ }, + {"write", SEPG_LNK_FILE__WRITE }, + {NULL,0} + } + }, + { + "chr_file", SEPG_CLASS_CHR_FILE, + { + {"read", SEPG_CHR_FILE__READ }, + {"write", SEPG_CHR_FILE__WRITE }, + {NULL,0} + } + }, + { + "blk_file", SEPG_CLASS_BLK_FILE, + { + {"read", SEPG_BLK_FILE__READ }, + {"write", SEPG_BLK_FILE__WRITE }, + {NULL,0} + } + }, + { + "sock_file", SEPG_CLASS_SOCK_FILE, + { + {"read", SEPG_SOCK_FILE__READ }, + {"write", SEPG_SOCK_FILE__WRITE }, + {NULL,0} + } + }, + { + "fifo_file", SEPG_CLASS_FIFO_FILE, + { + {"read", SEPG_FIFO_FILE__READ }, + {"write", SEPG_FIFO_FILE__WRITE }, + {NULL, 0UL } + } + }, + { + "db_database", SEPG_CLASS_DB_DATABASE, + { + { "create", SEPG_DB_DATABASE__CREATE }, + { "drop", SEPG_DB_DATABASE__DROP }, + { "getattr", SEPG_DB_DATABASE__GETATTR }, + { "setattr", SEPG_DB_DATABASE__SETATTR }, + { "relabelfrom", SEPG_DB_DATABASE__RELABELFROM }, + { "relabelto", SEPG_DB_DATABASE__RELABELTO }, + { "access", SEPG_DB_DATABASE__ACCESS }, + { "install_module", SEPG_DB_DATABASE__INSTALL_MODULE }, + { "load_module", SEPG_DB_DATABASE__LOAD_MODULE }, + { "superuser", SEPG_DB_DATABASE__SUPERUSER }, + { NULL, 0UL }, + } + }, + { + "db_schema", SEPG_CLASS_DB_SCHEMA, + { + { "create", SEPG_DB_SCHEMA__CREATE }, + { "drop", SEPG_DB_SCHEMA__DROP }, + { "getattr", SEPG_DB_SCHEMA__GETATTR }, + { "setattr", SEPG_DB_SCHEMA__SETATTR }, + { "relabelfrom", SEPG_DB_SCHEMA__RELABELFROM }, + { "relabelto", SEPG_DB_SCHEMA__RELABELTO }, + { "search", SEPG_DB_SCHEMA__SEARCH }, + { "add_object", SEPG_DB_SCHEMA__ADD_OBJECT }, + { "remove_object", SEPG_DB_SCHEMA__REMOVE_OBJECT }, + { NULL, 0UL }, + } + }, + { + "db_schema_temp", SEPG_CLASS_DB_SCHEMA_TEMP, + { + { "create", SEPG_DB_SCHEMA_TEMP__CREATE }, + { "drop", SEPG_DB_SCHEMA_TEMP__DROP}, + { "getattr", SEPG_DB_SCHEMA_TEMP__GETATTR }, + { "setattr", SEPG_DB_SCHEMA_TEMP__SETATTR }, + { "relabelfrom", SEPG_DB_SCHEMA_TEMP__RELABELFROM }, + { "relabelto", SEPG_DB_SCHEMA_TEMP__RELABELTO }, + { "search", SEPG_DB_SCHEMA_TEMP__SEARCH }, + { "add_object", SEPG_DB_SCHEMA_TEMP__ADD_OBJECT }, + { "remove_object", SEPG_DB_SCHEMA_TEMP__REMOVE_OBJECT }, + { NULL, 0UL }, + } + }, + { + "db_table", SEPG_CLASS_DB_TABLE, + { + { "create", SEPG_DB_TABLE__CREATE }, + { "drop", SEPG_DB_TABLE__DROP }, + { "getattr", SEPG_DB_TABLE__GETATTR }, + { "setattr", SEPG_DB_TABLE__SETATTR }, + { "relabelfrom", SEPG_DB_TABLE__RELABELFROM }, + { "relabelto", SEPG_DB_TABLE__RELABELTO }, + { "select", SEPG_DB_TABLE__SELECT }, + { "update", SEPG_DB_TABLE__UPDATE }, + { "insert", SEPG_DB_TABLE__INSERT }, + { "delete", SEPG_DB_TABLE__DELETE }, + { "lock", SEPG_DB_TABLE__LOCK }, + { "reference", SEPG_DB_TABLE__REFERENCE }, + { NULL, 0UL }, + } + }, + { + "db_sequence", SEPG_CLASS_DB_SEQUENCE, + { + { "create", SEPG_DB_SEQUENCE__CREATE }, + { "drop", SEPG_DB_SEQUENCE__DROP }, + { "getattr", SEPG_DB_SEQUENCE__GETATTR }, + { "setattr", SEPG_DB_SEQUENCE__SETATTR }, + { "relabelfrom", SEPG_DB_SEQUENCE__RELABELFROM }, + { "relabelto", SEPG_DB_SEQUENCE__RELABELTO }, + { "get_value", SEPG_DB_SEQUENCE__GET_VALUE }, + { "next_value", SEPG_DB_SEQUENCE__NEXT_VALUE }, + { "set_value", SEPG_DB_SEQUENCE__SET_VALUE }, + { NULL, 0UL }, + } + }, + { + "db_procedure", SEPG_CLASS_DB_PROCEDURE, + { + { "create", SEPG_DB_PROCEDURE__CREATE }, + { "drop", SEPG_DB_PROCEDURE__DROP }, + { "getattr", SEPG_DB_PROCEDURE__GETATTR }, + { "setattr", SEPG_DB_PROCEDURE__SETATTR }, + { "relabelfrom", SEPG_DB_PROCEDURE__RELABELFROM }, + { "relabelto", SEPG_DB_PROCEDURE__RELABELTO }, + { "execute", SEPG_DB_PROCEDURE__EXECUTE }, + { "entrypoint", SEPG_DB_PROCEDURE__ENTRYPOINT }, + { "install", SEPG_DB_PROCEDURE__INSTALL }, + { NULL, 0UL }, + } + }, + { + "db_column", SEPG_CLASS_DB_COLUMN, + { + { "create", SEPG_DB_COLUMN__CREATE }, + { "drop", SEPG_DB_COLUMN__DROP }, + { "getattr", SEPG_DB_COLUMN__GETATTR }, + { "setattr", SEPG_DB_COLUMN__SETATTR }, + { "relabelfrom", SEPG_DB_COLUMN__RELABELFROM }, + { "relabelto", SEPG_DB_COLUMN__RELABELTO }, + { "select", SEPG_DB_COLUMN__SELECT }, + { "update", SEPG_DB_COLUMN__UPDATE }, + { "insert", SEPG_DB_COLUMN__INSERT }, + { "reference", SEPG_DB_COLUMN__REFERENCE }, + { NULL, 0UL }, + } + }, + { + "db_tuple", SEPG_CLASS_DB_TUPLE, + { + { "relabelfrom", SEPG_DB_TUPLE__RELABELFROM }, + { "relabelto", SEPG_DB_TUPLE__RELABELTO }, + { "select", SEPG_DB_TUPLE__SELECT }, + { "update", SEPG_DB_TUPLE__UPDATE }, + { "insert", SEPG_DB_TUPLE__INSERT }, + { "delete", SEPG_DB_TUPLE__DELETE }, + { NULL, 0UL }, + } + }, + { + "db_blob", SEPG_CLASS_DB_BLOB, + { + { "create", SEPG_DB_BLOB__CREATE }, + { "drop", SEPG_DB_BLOB__DROP }, + { "getattr", SEPG_DB_BLOB__GETATTR }, + { "setattr", SEPG_DB_BLOB__SETATTR }, + { "relabelfrom", SEPG_DB_BLOB__RELABELFROM }, + { "relabelto", SEPG_DB_BLOB__RELABELTO }, + { "read", SEPG_DB_BLOB__READ }, + { "write", SEPG_DB_BLOB__WRITE }, + { "import", SEPG_DB_BLOB__IMPORT }, + { "export", SEPG_DB_BLOB__EXPORT }, + { NULL, 0UL }, + } + } +}; + +/* + * sepgsqlTransToExternalClass + * It translate the given class code (defined as SEPGCLASS_(class)) into + * external code which is necessary to communicate in-kernel SELinux + */ +extern security_class_t +sepgsqlTransToExternalClass(security_class_t tclass) +{ + Assert(tclass < SEPG_CLASS_MAX); + + return string_to_security_class(selinux_catalog[tclass].class_name); +} + +/* + * sepgsqlTransToInternalPerms + * It translate the given permission masks into internal representation + * defined as SEPG_(class)_(permission). + */ +extern void +sepgsqlTransToInternalPerms(security_class_t tclass, struct av_decision *avd) +{ + security_class_t tclass_ex; + struct av_decision i_avd; + int i, deny_unknown; + + Assert(tclass < SEPG_CLASS_MAX); + + memset(&i_avd, 0, sizeof(struct av_decision)); + + deny_unknown = security_deny_unknown(); + + tclass_ex = sepgsqlTransToExternalClass(tclass); + for (i=0; selinux_catalog[tclass].av[i].perm_name; i++) + { + const char *perm_name = selinux_catalog[tclass].av[i].perm_name; + access_vector_t perm_code = selinux_catalog[tclass].av[i].perm_code; + access_vector_t perm_code_ex; + + perm_code_ex = string_to_av_perm(tclass_ex, perm_name); + if (!perm_code_ex) + { + /* fill up undefined permission */ + if (!deny_unknown) + i_avd.allowed |= perm_code; + i_avd.decided |= perm_code; + i_avd.auditdeny |= perm_code; + continue; + } + + if (avd->allowed & perm_code_ex) + i_avd.allowed |= perm_code; + if (avd->decided & perm_code_ex) + i_avd.decided |= perm_code; + if (avd->auditallow & perm_code_ex) + i_avd.auditallow |= perm_code; + if (avd->auditdeny & perm_code_ex) + i_avd.auditdeny |= perm_code; + } + + avd->allowed = i_avd.allowed; + avd->decided = i_avd.decided; + avd->auditallow = i_avd.auditallow; + avd->auditdeny = i_avd.auditdeny; +} + +/* + * sepgsqlGetClassString + * sepgsqlGetPermissionString + * It returns text representation of object classes/permissions + */ +const char * +sepgsqlGetClassString(security_class_t tclass) +{ + Assert(tclass < SEPG_CLASS_MAX); + + return selinux_catalog[tclass].class_name; +} + +const char * +sepgsqlGetPermissionString(security_class_t tclass, access_vector_t av) +{ + int i; + + Assert(tclass < SEPG_CLASS_MAX); + + for (i=0; selinux_catalog[tclass].av[i].perm_name; i++) + { + if (selinux_catalog[tclass].av[i].perm_code == av) + return selinux_catalog[tclass].av[i].perm_name; + } + return NULL; +} + +/* + * sepgsqlAuditName + * returns an identifier string to generate audit record for + * the given tuple. Please note that its results can indicate + * an address within the given tuple, so we should not refer + * the returned pointer after HeapTuple is released. + */ +const char * +sepgsqlAuditName(Oid relid, HeapTuple tuple) +{ + static char buffer[NAMEDATALEN * 2 + 10]; + + switch (relid) + { + case DatabaseRelationId: + return NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); + + case NamespaceRelationId: + return NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); + + case RelationRelationId: + return NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname); + + case AttributeRelationId: + if (!IsBootstrapProcessingMode()) + { + Form_pg_attribute attForm + = (Form_pg_attribute) GETSTRUCT(tuple); + char *relname + = get_rel_name(attForm->attrelid); + + if (relname) + { + snprintf(buffer, sizeof(buffer), "%s.%s", + relname, NameStr(attForm->attname)); + pfree(relname); + return buffer; + } + } + return NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname); + + case ProcedureRelationId: + return NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname); + } + return NULL; +} + +/* + * sepgsqlFileObjectClass + * + * It returns proper object class of filesystem object already opened. + * It is necessary to check privileges voluntarily. + */ +security_class_t +sepgsqlFileObjectClass(int fdesc) +{ + struct stat stbuf; + + if (fstat(fdesc, &stbuf) != 0) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not stat file descriptor: %d", fdesc))); + + if (S_ISDIR(stbuf.st_mode)) + return SEPG_CLASS_DIR; + else if (S_ISCHR(stbuf.st_mode)) + return SEPG_CLASS_CHR_FILE; + else if (S_ISBLK(stbuf.st_mode)) + return SEPG_CLASS_BLK_FILE; + else if (S_ISFIFO(stbuf.st_mode)) + return SEPG_CLASS_FIFO_FILE; + else if (S_ISLNK(stbuf.st_mode)) + return SEPG_CLASS_LNK_FILE; + else if (S_ISSOCK(stbuf.st_mode)) + return SEPG_CLASS_SOCK_FILE; + + return SEPG_CLASS_FILE; +} + +/* + * sepgsqlTupleObjectClass + * + * It returns correct object class of given tuple + */ +security_class_t +sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple) +{ + Form_pg_namespace nspForm; + Form_pg_class clsForm; + Form_pg_attribute attForm; + + switch (relid) + { + case DatabaseRelationId: + return SEPG_CLASS_DB_DATABASE; + + case NamespaceRelationId: + nspForm = (Form_pg_namespace) GETSTRUCT(tuple); + if (strncmp(NameStr(nspForm->nspname), "pg_temp_", 8) == 0 || + strncmp(NameStr(nspForm->nspname), "pg_toast_temp_", 14) == 0) + return SEPG_CLASS_DB_SCHEMA_TEMP; + return SEPG_CLASS_DB_SCHEMA; + + case RelationRelationId: + clsForm = (Form_pg_class) GETSTRUCT(tuple); + if (clsForm->relkind == RELKIND_RELATION) + return SEPG_CLASS_DB_TABLE; + if (clsForm->relkind == RELKIND_SEQUENCE) + return SEPG_CLASS_DB_SEQUENCE; + break; + + case AttributeRelationId: + attForm = (Form_pg_attribute) GETSTRUCT(tuple); + if (IsBootstrapProcessingMode() && + (attForm->attrelid == TypeRelationId || + attForm->attrelid == ProcedureRelationId || + attForm->attrelid == AttributeRelationId || + attForm->attrelid == RelationRelationId)) + return SEPG_CLASS_DB_COLUMN; + + if (get_rel_relkind(attForm->attrelid) == RELKIND_RELATION) + return SEPG_CLASS_DB_COLUMN; + break; + + case ProcedureRelationId: + return SEPG_CLASS_DB_PROCEDURE; + + case LargeObjectRelationId: + return SEPG_CLASS_DB_BLOB; + } + return SEPG_CLASS_DB_TUPLE; +} diff -rpNU3 base/src/backend/security/sepgsql/policy/Makefile sepgsql-new/src/backend/security/sepgsql/policy/Makefile --- base/src/backend/security/sepgsql/policy/Makefile 1970-01-01 09:00:00.000000000 +0900 +++ sepgsql-new/src/backend/security/sepgsql/policy/Makefile 2009-03-13 18:14:33.000000000 +0900 @@ -0,0 +1,28 @@ +# +# Makefile for SE-PostgreSQL security policy module +# +top_builddir = ../../../../.. +include $(top_builddir)/src/Makefile.global + +POLICY_BASEDIR := $(DESTDIR)/usr/share/selinux +POLICY_MAKEFILE := $(POLICY_BASEDIR)/devel/Makefile +POLICY_TYPES := targeted mls +POLICY_TARGET := $(strip $(shell $(AWK) -F= '/^SELINUXTYPE/{ print $$2 }' /etc/selinux/config)) +PREFIX_RULE := "s/%%__prefix__%%/$(shell echo $(prefix)|sed 's/\//\\\//g')/g" +BINDIR_RULE := "s/%%__bindir__%%/$(shell echo $(bindir)|sed 's/\//\\\//g')/g" +LIBDIR_RULE := "s/%%__libdir__%%/$(shell echo $(pkglibdir)|sed 's/\//\\\//g')/g" + +all: $(addprefix sepostgresql-devel.pp., $(POLICY_TYPES)) + ln -sf sepostgresql-devel.pp.$(POLICY_TARGET) sepostgresql-devel.pp + +sepostgresql-devel.pp.%: sepostgresql-devel.te sepostgresql-devel.fc + $(MAKE) NAME=$(POLICY_TARGET) -f $(POLICY_MAKEFILE) clean + $(MAKE) NAME=$(POLICY_TARGET) -f $(POLICY_MAKEFILE) + mv $(basename $@) $@ + +sepostgresql-devel.fc: sepostgresql-devel.fc.template + cat $< | sed -e $(PREFIX_RULE) -e $(BINDIR_RULE) -e $(LIBDIR_RULE) > $@ + +clean: + $(MAKE) -f $(POLICY_MAKEFILE) clean + rm -f *.pp *.pp.* *.if *.fc diff -rpNU3 base/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template sepgsql-new/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template --- base/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template 1970-01-01 09:00:00.000000000 +0900 +++ sepgsql-new/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template 2009-03-13 18:14:33.000000000 +0900 @@ -0,0 +1,12 @@ +# +# SE-PostgreSQL install path +# +%%__prefix__%%(/.*)? -- gen_context(system_u:object_r:usr_t,s0) + +%%__bindir__%%/(se)?postgres -- gen_context(system_u:object_r:postgresql_exec_t,s0) +%%__bindir__%%/(se)?pg_ctl -- gen_context(system_u:object_r:initrc_exec_t,s0) +%%__bindir__%%/initdb(\.sepgsql)? -- gen_context(system_u:object_r:postgresql_exec_t,s0) +%%__bindir__%%(/.*)? -- gen_context(system_u:object_r:bin_t,s0) + +%%__libdir__%%(/.*)? -- gen_context(system_u:object_r:lib_t,s0) + diff -rpNU3 base/src/backend/security/sepgsql/policy/sepostgresql-devel.te sepgsql-new/src/backend/security/sepgsql/policy/sepostgresql-devel.te --- base/src/backend/security/sepgsql/policy/sepostgresql-devel.te 1970-01-01 09:00:00.000000000 +0900 +++ sepgsql-new/src/backend/security/sepgsql/policy/sepostgresql-devel.te 2009-03-28 11:17:32.000000000 +0900 @@ -0,0 +1,128 @@ +policy_module(sepostgresql-devel, 3.27) + +gen_require(` + class db_database all_db_database_perms; + class db_table all_db_table_perms; + class db_procedure all_db_procedure_perms; + class db_column all_db_column_perms; + class db_tuple all_db_tuple_perms; + class db_blob all_db_blob_perms; + + attribute sepgsql_client_type; + attribute sepgsql_unconfined_type; + + attribute sepgsql_database_type; + attribute sepgsql_table_type; + attribute sepgsql_sysobj_table_type; + attribute sepgsql_procedure_type; + attribute sepgsql_blob_type; + attribute sepgsql_module_type; + + # for regression test + type bin_t; + type user_home_t; + type sepgsql_trusted_proc_t; + + attribute tmpfile; + attribute ptynode; +') + +################################# +# +# Domain for Testcases +# + +role sepgsql_test_r; + +userdom_unpriv_user_template(sepgsql_test) + +ifdef(`postgresql_role', ` + postgresql_role(sepgsql_test_r, sepgsql_test_t) +',` + postgresql_userdom_template(sepgsql_test, sepgsql_test_t, sepgsql_test_r) +') + +# workaround for sepgsql tests +allow sepgsql_test_t user_sepgsql_blob_t : db_blob { import export }; +dontaudit sepgsql_test_t sepgsql_blob_type : db_blob *; + +allow sepgsql_test_t tmpfile : dir search_dir_perms; +allow sepgsql_test_t tmpfile : file rw_file_perms; +allow sepgsql_test_t ptynode : chr_file rw_file_perms; + +optional_policy(` + gen_require(` + type unconfined_t; + role unconfined_r; + ') + + tunable_policy(`sepgsql_regression_test_mode',` + allow unconfined_t sepgsql_test_t : process transition; + ') + allow sepgsql_test_t unconfined_t : fifo_file read_file_perms; + role unconfined_r types sepgsql_test_t; + role unconfined_r types sepgsql_trusted_proc_t; +') + +################################# +# +# SE-PostgreSQL Declarations +# + +## +##

+## Allow to generate auditallow logs +##

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

+## Allow to generate auditdeny logs +##

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

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

+##
+gen_tunable(sepgsql_regression_test_mode, false) + +######################################## +# +# SE-PostgreSQL audit switch for debugging +# +tunable_policy(`sepgsql_enable_auditallow',` + auditallow domain sepgsql_database_type : db_database *; + auditallow domain sepgsql_table_type : db_table *; + auditallow domain sepgsql_table_type : db_column *; + auditallow domain sepgsql_table_type : db_tuple { relabelfrom relabelto }; + auditallow domain sepgsql_procedure_type : db_procedure *; + auditallow domain sepgsql_blob_type : db_blob *; + auditallow domain sepgsql_module_type : db_database { install_module }; + auditallow sepgsql_database_type sepgsql_module_type : db_database { load_module }; +') + +tunable_policy(`! sepgsql_enable_auditdeny',` + dontaudit domain sepgsql_database_type : db_database *; + dontaudit domain sepgsql_table_type : db_table *; + dontaudit domain sepgsql_table_type : db_column *; + dontaudit domain sepgsql_table_type : db_tuple { relabelfrom relabelto }; + dontaudit domain sepgsql_procedure_type : db_procedure *; + dontaudit domain sepgsql_blob_type : db_blob *; + dontaudit domain sepgsql_module_type : db_database { install_module }; + dontaudit sepgsql_database_type sepgsql_module_type : db_database { load_module }; +') + +######################################## +# +# SE-PostgreSQL regression test mode switch +# +tunable_policy(`sepgsql_regression_test_mode',` + allow sepgsql_client_type user_home_t : db_database { install_module }; + allow sepgsql_unconfined_type user_home_t : db_database { install_module }; + allow sepgsql_database_type user_home_t : db_database { load_module }; +') diff -rpNU3 base/src/backend/storage/file/fd.c sepgsql-new/src/backend/storage/file/fd.c --- base/src/backend/storage/file/fd.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/storage/file/fd.c 2009-03-13 18:14:33.000000000 +0900 @@ -1241,6 +1241,13 @@ FileTruncate(File file, long offset) return returnCode; } +int +FileRawDescriptor(File file) +{ + Assert(FileIsValid(file)); + + return VfdCache[file].fd; +} /* * Routines that want to use stdio (ie, FILE*) should use AllocateFile diff -rpNU3 base/src/backend/storage/ipc/ipci.c sepgsql-new/src/backend/storage/ipc/ipci.c --- base/src/backend/storage/ipc/ipci.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/storage/ipc/ipci.c 2009-03-13 18:14:33.000000000 +0900 @@ -25,6 +25,7 @@ #include "postmaster/autovacuum.h" #include "postmaster/bgwriter.h" #include "postmaster/postmaster.h" +#include "security/sepgsql.h" #include "storage/freespace.h" #include "storage/ipc.h" #include "storage/pg_shmem.h" @@ -117,6 +118,7 @@ CreateSharedMemoryAndSemaphores(bool mak #ifdef EXEC_BACKEND size = add_size(size, ShmemBackendArraySize()); #endif + size = add_size(size, sepgsqlShmemSize()); /* freeze the addin request size and include it */ addin_request_allowed = false; diff -rpNU3 base/src/backend/storage/large_object/inv_api.c sepgsql-new/src/backend/storage/large_object/inv_api.c --- base/src/backend/storage/large_object/inv_api.c 2008-03-19 09:48:23.000000000 +0900 +++ sepgsql-new/src/backend/storage/large_object/inv_api.c 2009-03-13 18:14:33.000000000 +0900 @@ -39,6 +39,7 @@ #include "catalog/pg_largeobject.h" #include "commands/comment.h" #include "libpq/libpq-fs.h" +#include "security/sepgsql.h" #include "storage/large_object.h" #include "utils/fmgroids.h" #include "utils/resowner.h" @@ -134,12 +135,13 @@ close_lo_relation(bool isCommit) * read with can be specified. */ static bool -myLargeObjectExists(Oid loid, Snapshot snapshot) +myLargeObjectExists(LargeObjectDesc *lobj) { bool retval = false; Relation pg_largeobject; ScanKeyData skey[1]; SysScanDesc sd; + HeapTuple tuple; /* * See if we can find any tuples belonging to the specified LO @@ -147,15 +149,19 @@ myLargeObjectExists(Oid loid, Snapshot s ScanKeyInit(&skey[0], Anum_pg_largeobject_loid, BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(loid)); + ObjectIdGetDatum(lobj->id)); pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock); sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true, - snapshot, 1, skey); + lobj->snapshot, 1, skey); - if (systable_getnext(sd) != NULL) + tuple = systable_getnext(sd); + if (HeapTupleIsValid(tuple)) + { retval = true; + lobj->secid = HeapTupleGetSecLabel(tuple); + } systable_endscan(sd); @@ -254,7 +260,7 @@ inv_open(Oid lobjId, int flags, MemoryCo elog(ERROR, "invalid flags: %d", flags); /* Can't use LargeObjectExists here because it always uses SnapshotNow */ - if (!myLargeObjectExists(lobjId, retval->snapshot)) + if (!myLargeObjectExists(retval)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("large object %u does not exist", lobjId))); @@ -676,6 +682,8 @@ 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); + if (HeapTupleHasSecLabel(newtup)) + HeapTupleSetSecLabel(newtup, obj_desc->secid); simple_heap_insert(lo_heap_r, newtup); CatalogIndexInsert(indstate, newtup); heap_freetuple(newtup); @@ -836,6 +844,8 @@ inv_truncate(LargeObjectDesc *obj_desc, 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); + if (HeapTupleHasSecLabel(newtup)) + HeapTupleSetSecLabel(newtup, obj_desc->secid); simple_heap_insert(lo_heap_r, newtup); CatalogIndexInsert(indstate, newtup); heap_freetuple(newtup); @@ -859,3 +869,99 @@ inv_truncate(LargeObjectDesc *obj_desc, */ CommandCounterIncrement(); } + +Oid +inv_get_security(Oid loid) +{ + Relation rel; + ScanKeyData skey; + SysScanDesc scan; + HeapTuple tuple; + Oid secid = InvalidOid; + + ScanKeyInit(&skey, + Anum_pg_largeobject_loid, + BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(loid)); + + rel = heap_open(LargeObjectRelationId, AccessShareLock); + + scan = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, + SnapshotNow, 1, &skey); + tuple = systable_getnext(scan); + + if (HeapTupleIsValid(tuple)) + { + /* + * SELinux: check db_blob:{getattr} + */ + sepgsqlCheckBlobGetattr(tuple); + secid = HeapTupleGetSecLabel(tuple); + } + systable_endscan(scan); + + heap_close(rel, AccessShareLock); + + return secid; +} + +void +inv_set_security(Oid loid, Oid secid) +{ + Relation rel; + ScanKeyData skey; + SysScanDesc scan; + HeapTuple tuple; + CatalogIndexState ind; + bool found = false; + + ScanKeyInit(&skey, + Anum_pg_largeobject_loid, + BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(loid)); + + rel = heap_open(LargeObjectRelationId, RowExclusiveLock); + + ind = CatalogOpenIndexes(rel); + + scan = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, + SnapshotNow, 1, &skey); + while (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + HeapTuple newtuple; + Datum values[Natts_pg_largeobject]; + bool nulls[Natts_pg_largeobject]; + bool replaces[Natts_pg_largeobject]; + + memset(replaces, false, sizeof(replaces)); + + newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), + values, nulls, replaces); + if (!HeapTupleHasSecLabel(newtuple)) + elog(ERROR, "Unable to assign security label on \"%s\"", + RelationGetRelationName(rel)); + HeapTupleSetSecLabel(newtuple, secid); + + /* + * SELinux: check db_blob:{setattr relabelfrom relabelto} + */ + if (!found) + sepgsqlCheckBlobRelabel(tuple, newtuple); + + simple_heap_update(rel, &tuple->t_self, newtuple); + CatalogUpdateIndexes(rel, newtuple); + found = true; + } + systable_endscan(scan); + + CatalogCloseIndexes(ind); + + heap_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); + + if (!found) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("large object %u does not exist", loid))); +} diff -rpNU3 base/src/backend/tcop/pquery.c sepgsql-new/src/backend/tcop/pquery.c --- base/src/backend/tcop/pquery.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/tcop/pquery.c 2009-03-13 18:14:33.000000000 +0900 @@ -560,7 +560,7 @@ PortalStart(Portal portal, ParamListInfo Assert(pstmt->returningLists); portal->tupDesc = ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), - false); + false, false); } /* diff -rpNU3 base/src/backend/tcop/utility.c sepgsql-new/src/backend/tcop/utility.c --- base/src/backend/tcop/utility.c 2008-11-05 09:57:00.000000000 +0900 +++ sepgsql-new/src/backend/tcop/utility.c 2009-06-19 15:02:12.000000000 +0900 @@ -49,6 +49,7 @@ #include "postmaster/bgwriter.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteRemove.h" +#include "security/sepgsql.h" #include "storage/fd.h" #include "tcop/pquery.h" #include "tcop/utility.h" @@ -315,6 +316,7 @@ check_xact_readonly(Node *parsetree) case T_AlterRoleSetStmt: case T_AlterObjectSchemaStmt: case T_AlterOwnerStmt: + case T_AlterSecLabelStmt: case T_AlterSeqStmt: case T_AlterTableStmt: case T_RenameStmt: @@ -744,6 +746,10 @@ ProcessUtility(Node *parsetree, ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree); break; + case T_AlterSecLabelStmt: + ExecAlterSecLabelStmt((AlterSecLabelStmt *) parsetree); + break; + case T_AlterTableStmt: { List *stmts; @@ -1022,6 +1028,10 @@ ProcessUtility(Node *parsetree, LoadStmt *stmt = (LoadStmt *) parsetree; closeAllVfds(); /* probably not necessary... */ + + /* SELinux checks db_database:{install_module} */ + sepgsqlCheckDatabaseInstallModule(); + /* Allowed names are restricted if you're not superuser */ load_file(stmt->filename, !superuser()); } @@ -1709,6 +1719,28 @@ CreateCommandTag(Node *parsetree) } break; + case T_AlterSecLabelStmt: + switch (((AlterSecLabelStmt *) parsetree)->objectType) + { + case OBJECT_DATABASE: + tag = "ALTER DATABASE"; + break; + case OBJECT_SCHEMA: + tag = "ALTER SCHEMA"; + break; + case OBJECT_TABLE: + case OBJECT_COLUMN: + tag = "ALTER TABLE"; + break; + case OBJECT_SEQUENCE: + tag = "ALTER SEQUENCE"; + break; + case OBJECT_FUNCTION: + tag = "ALTER FUNCTION"; + break; + } + break; + case T_AlterTableStmt: { AlterTableStmt *stmt = (AlterTableStmt *) parsetree; @@ -2248,6 +2280,10 @@ GetCommandLogLevel(Node *parsetree) lev = LOGSTMT_DDL; break; + case T_AlterSecLabelStmt: + lev = LOGSTMT_DDL; + break; + case T_AlterTableStmt: lev = LOGSTMT_DDL; break; diff -rpNU3 base/src/backend/utils/adt/genfile.c sepgsql-new/src/backend/utils/adt/genfile.c --- base/src/backend/utils/adt/genfile.c 2008-06-12 22:34:19.000000000 +0900 +++ sepgsql-new/src/backend/utils/adt/genfile.c 2009-06-10 21:37:54.000000000 +0900 @@ -25,6 +25,7 @@ #include "funcapi.h" #include "miscadmin.h" #include "postmaster/syslogger.h" +#include "security/sepgsql.h" #include "storage/fd.h" #include "utils/builtins.h" #include "utils/memutils.h" @@ -109,6 +110,9 @@ pg_read_file(PG_FUNCTION_ARGS) errmsg("could not open file \"%s\" for reading: %m", filename))); + /* SELinux: check file:{read} permission */ + sepgsqlCheckFileRead(fileno(file), filename); + if (fseeko(file, (off_t) seek_offset, (seek_offset >= 0) ? SEEK_SET : SEEK_END) != 0) ereport(ERROR, diff -rpNU3 base/src/backend/utils/adt/ri_triggers.c sepgsql-new/src/backend/utils/adt/ri_triggers.c --- base/src/backend/utils/adt/ri_triggers.c 2008-09-25 15:09:40.000000000 +0900 +++ sepgsql-new/src/backend/utils/adt/ri_triggers.c 2009-05-20 17:08:02.000000000 +0900 @@ -37,6 +37,7 @@ #include "parser/parse_coerce.h" #include "parser/parse_relation.h" #include "miscadmin.h" +#include "security/rowlevel.h" #include "utils/acl.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" @@ -2620,6 +2621,7 @@ RI_Initial_Check(Trigger *trigger, Relat const char *sep; int i; int old_work_mem; + int save_rowlv; char workmembuf[32]; int spi_result; SPIPlanPtr qplan; @@ -2752,6 +2754,11 @@ RI_Initial_Check(Trigger *trigger, Relat SPI_result, querybuf.data); /* + * Disables the Row-level stuff during the internal consistency checks. + */ + save_rowlv = rowlvSetPerformingMode(ROWLV_BYPASS_MODE); + + /* * Run the plan. For safety we force a current snapshot to be used. (In * serializable mode, this arguably violates serializability, but we * really haven't got much choice.) We need at most one tuple returned, @@ -2763,6 +2770,9 @@ RI_Initial_Check(Trigger *trigger, Relat InvalidSnapshot, true, false, 1); + /* Restore Row-level stuff */ + rowlvSetPerformingMode(save_rowlv); + /* Check result */ if (spi_result != SPI_OK_SELECT) elog(ERROR, "SPI_execute_snapshot returned %d", spi_result); @@ -3256,6 +3266,7 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPl int spi_result; Oid save_userid; bool save_secdefcxt; + int save_rowlv, temp_rowlv; Datum vals[RI_MAX_NUMKEYS * 2]; char nulls[RI_MAX_NUMKEYS * 2]; @@ -3336,12 +3347,19 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPl GetUserIdAndContext(&save_userid, &save_secdefcxt); SetUserIdAndContext(RelationGetForm(query_rel)->relowner, true); + /* Switch Row-level stuff behavior on FK checks, if necessary */ + temp_rowlv = (detectNewRows ? ROWLV_ABORT_MODE : ROWLV_FILTER_MODE); + save_rowlv = rowlvSetPerformingMode(temp_rowlv); + /* Finally we can run the query. */ spi_result = SPI_execute_snapshot(qplan, vals, nulls, test_snapshot, crosscheck_snapshot, false, false, limit); + /* Restore Row-level stuff behavior */ + rowlvSetPerformingMode(save_rowlv); + /* Restore UID */ SetUserIdAndContext(save_userid, save_secdefcxt); diff -rpNU3 base/src/backend/utils/adt/varlena.c sepgsql-new/src/backend/utils/adt/varlena.c --- base/src/backend/utils/adt/varlena.c 2008-03-19 09:48:23.000000000 +0900 +++ sepgsql-new/src/backend/utils/adt/varlena.c 2009-03-13 13:09:47.000000000 +0900 @@ -70,6 +70,103 @@ static text *text_substring(Datum str, static void appendStringInfoText(StringInfo str, const text *t); +/***************************************************************************** + * CONVERSION ROUTINES EXPORTED FOR USE BY C CODE * + *****************************************************************************/ + +/* + * cstring_to_text + * + * Create a text value from a null-terminated C string. + * + * The new text value is freshly palloc'd with a full-size VARHDR. + */ +text * +cstring_to_text(const char *s) +{ + return cstring_to_text_with_len(s, strlen(s)); +} + +/* + * cstring_to_text_with_len + * + * Same as cstring_to_text except the caller specifies the string length; + * the string need not be null_terminated. + */ +text * +cstring_to_text_with_len(const char *s, int len) +{ + text *result = (text *) palloc(len + VARHDRSZ); + + SET_VARSIZE(result, len + VARHDRSZ); + memcpy(VARDATA(result), s, len); + + return result; +} + +/* + * text_to_cstring + * + * Create a palloc'd, null-terminated C string from a text value. + * + * We support being passed a compressed or toasted text value. + * This is a bit bogus since such values shouldn't really be referred to as + * "text *", but it seems useful for robustness. If we didn't handle that + * case here, we'd need another routine that did, anyway. + */ +char * +text_to_cstring(const text *t) +{ + /* must cast away the const, unfortunately */ + text *tunpacked = pg_detoast_datum_packed((struct varlena *) t); + int len = VARSIZE_ANY_EXHDR(tunpacked); + char *result; + + result = (char *) palloc(len + 1); + memcpy(result, VARDATA_ANY(tunpacked), len); + result[len] = '\0'; + + if (tunpacked != t) + pfree(tunpacked); + + return result; +} + +/* + * text_to_cstring_buffer + * + * Copy a text value into a caller-supplied buffer of size dst_len. + * + * The text string is truncated if necessary to fit. The result is + * guaranteed null-terminated (unless dst_len == 0). + * + * We support being passed a compressed or toasted text value. + * This is a bit bogus since such values shouldn't really be referred to as + * "text *", but it seems useful for robustness. If we didn't handle that + * case here, we'd need another routine that did, anyway. + */ +void +text_to_cstring_buffer(const text *src, char *dst, size_t dst_len) +{ + /* must cast away the const, unfortunately */ + text *srcunpacked = pg_detoast_datum_packed((struct varlena *) src); + size_t src_len = VARSIZE_ANY_EXHDR(srcunpacked); + + if (dst_len > 0) + { + dst_len--; + if (dst_len >= src_len) + dst_len = src_len; + else /* ensure truncation is encoding-safe */ + dst_len = pg_mbcliplen(VARDATA_ANY(srcunpacked), src_len, dst_len); + memcpy(dst, VARDATA_ANY(srcunpacked), dst_len); + dst[dst_len] = '\0'; + } + + if (srcunpacked != src) + pfree(srcunpacked); +} + /***************************************************************************** * USER I/O ROUTINES * diff -rpNU3 base/src/backend/utils/cache/catcache.c sepgsql-new/src/backend/utils/cache/catcache.c --- base/src/backend/utils/cache/catcache.c 2008-03-19 09:48:23.000000000 +0900 +++ sepgsql-new/src/backend/utils/cache/catcache.c 2009-03-13 18:14:33.000000000 +0900 @@ -1313,6 +1313,38 @@ ReleaseCatCache(HeapTuple tuple) CatCacheRemoveCTup(ct->my_cache, ct); } +/* + * InsertCatCache + * + * This function enables to refer a tuple recently inserted, using catcache + * until next CommandCounterIncrement. + */ +void InsertCatCache(CatCache *cache, HeapTuple tuple) +{ + ScanKeyData skey[4]; + uint32 hashValue; + Index hashIndex; + bool isnull; + int i; + + /* initialize the search key information */ + memcpy(skey, cache->cc_skey, sizeof(skey)); + for (i=0; i < cache->cc_nkeys; i++) + { + skey[i].sk_argument = heap_getattr(tuple, cache->cc_key[i], + cache->cc_tupdesc, &isnull); + Assert(!isnull); + } + + /* find the hash bucket in which to look for the tuple */ + if (cache->cc_tupdesc == NULL) + CatalogCacheInitializeCache(cache); + hashValue = CatalogCacheComputeHashValue(cache, cache->cc_nkeys, skey); + hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets); + + /* Insert a new tuple */ + CatalogCacheCreateEntry(cache, tuple, hashValue, hashIndex, false); +} /* * SearchCatCacheList diff -rpNU3 base/src/backend/utils/cache/plancache.c sepgsql-new/src/backend/utils/cache/plancache.c --- base/src/backend/utils/cache/plancache.c 2009-09-10 20:11:35.000000000 +0900 +++ sepgsql-new/src/backend/utils/cache/plancache.c 2009-09-10 20:24:37.000000000 +0900 @@ -894,12 +894,14 @@ PlanCacheComputeResultDesc(List *stmt_li if (IsA(node, Query)) { query = (Query *) node; - return ExecCleanTypeFromTL(query->targetList, false); + return ExecCleanTypeFromTL(query->targetList, + false, false); } if (IsA(node, PlannedStmt)) { pstmt = (PlannedStmt *) node; - return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false); + return ExecCleanTypeFromTL(pstmt->planTree->targetlist, + false, false); } /* other cases shouldn't happen, but return NULL */ break; @@ -910,13 +912,15 @@ PlanCacheComputeResultDesc(List *stmt_li { query = (Query *) node; Assert(query->returningList); - return ExecCleanTypeFromTL(query->returningList, false); + return ExecCleanTypeFromTL(query->returningList, + false, false); } if (IsA(node, PlannedStmt)) { pstmt = (PlannedStmt *) node; Assert(pstmt->returningLists); - return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false); + return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), + false, false); } /* other cases shouldn't happen, but return NULL */ break; diff -rpNU3 base/src/backend/utils/cache/relcache.c sepgsql-new/src/backend/utils/cache/relcache.c --- base/src/backend/utils/cache/relcache.c 2009-02-02 11:47:17.000000000 +0900 +++ sepgsql-new/src/backend/utils/cache/relcache.c 2009-03-13 18:14:33.000000000 +0900 @@ -46,6 +46,7 @@ #include "catalog/pg_opclass.h" #include "catalog/pg_proc.h" #include "catalog/pg_rewrite.h" +#include "catalog/pg_security.h" #include "catalog/pg_type.h" #include "commands/trigger.h" #include "miscadmin.h" @@ -324,7 +325,13 @@ AllocateRelationDesc(Relation relation, /* initialize relation tuple form */ relation->rd_rel = relationForm; - /* and allocate attribute tuple form storage */ + /* + * and allocate attribute tuple form storage + * + * Please note that relation->rd_att->tdhasrowacl and tdhasseclabel + * have to be fixed up correctly at RelationBuildTupleDesc(), because + * security module may need reloptions info to make its decision. + */ relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts, relationForm->relhasoids); /* which we mark as a reference-counted tupdesc */ @@ -877,6 +884,10 @@ RelationBuildDesc(Oid targetRelId, Relat /* extract reloptions if any */ RelationParseRelOptions(relation, pg_class_tuple); + /* fixup relation->rd_att->tdhasseclabel */ + relation->rd_att->tdhasseclabel + = securityTupleDescHasSecLabel(relation); + /* * initialize the relation lock manager information */ @@ -1462,6 +1473,12 @@ formrdesc(const char *relationName, Oid relation->rd_rel->relfilenode = RelationGetRelid(relation); /* + * Fixup relation->rd_att->tdhasrowacl and tdhasseclabel + */ + RelationGetDescr(relation)->tdhasseclabel + = securityTupleDescHasSecLabel(relation); + + /* * initialize the relation lock manager information */ RelationInitLockInfo(relation); /* see lmgr.c */ @@ -2687,6 +2704,13 @@ BuildHardcodedDescriptor(int natts, Form oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + /* + * NOTE: we assume the returned TupleDesc is only used for + * references to toast'ed data, and it is not delivered to + * heap_form_tuple(), so TupleDesc->tdhasrowacl and tdhasseclabel + * don't give us any effect. + * We omit to invoke securityTupleDescHasSecLabel() here. + */ result = CreateTemplateTupleDesc(natts, hasoids); result->tdtypeid = RECORDOID; /* not right, but we don't care */ result->tdtypmod = -1; @@ -3446,6 +3470,12 @@ load_relcache_init_file(void) rel->rd_options = NULL; } + /* + * fixup rel->rd_att->tdhassecurity + */ + rel->rd_att->tdhasseclabel + = securityTupleDescHasSecLabel(rel); + /* mark not-null status */ if (has_not_null) { diff -rpNU3 base/src/backend/utils/cache/syscache.c sepgsql-new/src/backend/utils/cache/syscache.c --- base/src/backend/utils/cache/syscache.c 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/backend/utils/cache/syscache.c 2009-03-13 18:14:33.000000000 +0900 @@ -39,6 +39,7 @@ #include "catalog/pg_opfamily.h" #include "catalog/pg_proc.h" #include "catalog/pg_rewrite.h" +#include "catalog/pg_security.h" #include "catalog/pg_statistic.h" #include "catalog/pg_ts_config.h" #include "catalog/pg_ts_config_map.h" @@ -533,6 +534,30 @@ static const struct cachedesc cacheinfo[ }, 1024 }, + {SecurityRelationId, /* SECURITYLABEL */ + SecuritySeclabelIndexId, + 0, + 1, + { + Anum_pg_security_seclabel, + 0, + 0, + 0 + }, + 128 + }, + {SecurityRelationId, /*SECURITYOID */ + SecurityOidIndexId, + 0, + 1, + { + ObjectIdAttributeNumber, + 0, + 0, + 0 + }, + 128 + }, {StatisticRelationId, /* STATRELATT */ StatisticRelidAttnumIndexId, Anum_pg_statistic_starelid, @@ -784,6 +809,21 @@ ReleaseSysCache(HeapTuple tuple) } /* + * InsertSysCache + * interts a tuple temporary until next CommandCounterIncrement + */ +void InsertSysCache(Oid relid, HeapTuple tuple) +{ + int cacheId; + + for (cacheId = 0; cacheId < SysCacheSize; cacheId++) + { + if (SysCache[cacheId]->cc_reloid == relid) + InsertCatCache(SysCache[cacheId], tuple); + } +} + +/* * SearchSysCacheCopy * * A convenience routine that does SearchSysCache and (if successful) diff -rpNU3 base/src/backend/utils/fmgr/dfmgr.c sepgsql-new/src/backend/utils/fmgr/dfmgr.c --- base/src/backend/utils/fmgr/dfmgr.c 2009-09-10 20:11:35.000000000 +0900 +++ sepgsql-new/src/backend/utils/fmgr/dfmgr.c 2009-09-10 20:24:37.000000000 +0900 @@ -22,6 +22,7 @@ #include "port/dynloader/win32.h" #endif #include "miscadmin.h" +#include "security/sepgsql.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); + /* SELinux checks db_database:{load_module} permission */ + sepgsqlCheckDatabaseLoadModule(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); + /* SELinux checks db_database:{load_module} */ + sepgsqlCheckDatabaseLoadModule(fullname); + /* Unload the library if currently loaded */ internal_unload_library(fullname); @@ -403,7 +409,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/fmgr/fmgr.c sepgsql-new/src/backend/utils/fmgr/fmgr.c --- base/src/backend/utils/fmgr/fmgr.c 2009-02-02 11:47:17.000000000 +0900 +++ sepgsql-new/src/backend/utils/fmgr/fmgr.c 2009-03-13 18:14:33.000000000 +0900 @@ -23,6 +23,7 @@ #include "executor/spi.h" #include "miscadmin.h" #include "parser/parse_expr.h" +#include "security/sepgsql.h" #include "utils/builtins.h" #include "utils/fmgrtab.h" #include "utils/guc.h" @@ -227,6 +228,12 @@ fmgr_info_cxt_security(Oid functionId, F finfo->fn_addr = fmgr_security_definer; finfo->fn_oid = functionId; ReleaseSysCache(procedureTuple); + /* + * NOTE: It is not necessary to set up Trusted Procedure + * here, because fmgr_security_definer() invokes this + * function with ignore_security=true again. It is set up + * later. + */ return; } @@ -274,6 +281,7 @@ fmgr_info_cxt_security(Oid functionId, F } finfo->fn_oid = functionId; + sepgsqlCheckProcedureEntrypoint(finfo, procedureTuple); ReleaseSysCache(procedureTuple); } diff -rpNU3 base/src/backend/utils/init/postinit.c sepgsql-new/src/backend/utils/init/postinit.c --- base/src/backend/utils/init/postinit.c 2008-09-25 15:09:40.000000000 +0900 +++ sepgsql-new/src/backend/utils/init/postinit.c 2009-03-13 18:14:33.000000000 +0900 @@ -31,6 +31,7 @@ #include "pgstat.h" #include "postmaster/autovacuum.h" #include "postmaster/postmaster.h" +#include "security/sepgsql.h" #include "storage/backendid.h" #include "storage/fd.h" #include "storage/ipc.h" @@ -194,12 +195,9 @@ CheckMyDatabase(const char *name, bool a name))); /* - * Check privilege to connect to the database. (The am_superuser test - * is redundant, but since we have the flag, might as well check it - * and save a few cycles.) + * Check privilege to connect to the database. */ - if (!am_superuser && - pg_database_aclcheck(MyDatabaseId, GetUserId(), + if (pg_database_aclcheck(MyDatabaseId, GetUserId(), ACL_CONNECT) != ACLCHECK_OK) ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), @@ -541,6 +539,9 @@ InitPostgres(const char *in_dbname, Oid */ RelationCacheInitializePhase2(); + /* initialize SE-PostgreSQL feature */ + sepgsqlInitialize(); + /* * Figure out our postgres user id, and see if we are a superuser. * diff -rpNU3 base/src/backend/utils/misc/guc.c sepgsql-new/src/backend/utils/misc/guc.c --- base/src/backend/utils/misc/guc.c 2009-09-10 20:11:35.000000000 +0900 +++ sepgsql-new/src/backend/utils/misc/guc.c 2009-09-10 20:24:37.000000000 +0900 @@ -54,6 +54,7 @@ #include "postmaster/postmaster.h" #include "postmaster/syslogger.h" #include "postmaster/walwriter.h" +#include "security/sepgsql.h" #include "storage/fd.h" #include "storage/freespace.h" #include "tcop/tcopprot.h" @@ -1091,6 +1092,32 @@ static struct config_bool ConfigureNames &IgnoreSystemIndexes, false, NULL, NULL }, +#ifdef HAVE_SELINUX + { + {"sepostgresql", PGC_POSTMASTER, CONN_AUTH_SECURITY, + gettext_noop("SE-PostgreSQL activation option to be turned on/off"), + NULL, + }, + &sepostgresql_is_enabled, + false, NULL, NULL + }, + { + {"sepostgresql_row_level", PGC_POSTMASTER, CONN_AUTH_SECURITY, + gettext_noop("Row-level access controls on SE-PostgreSQL"), + NULL, + }, + &sepostgresql_row_level, + true, NULL, NULL + }, + { + {"sepostgresql_mcstrans", PGC_USERSET, CONN_AUTH_SECURITY, + gettext_noop("SE-PostgreSQL uses mcstrans on printing security labels"), + NULL, + }, + &sepostgresql_use_mcstrans, + true, NULL, NULL + }, +#endif /* End-of-list marker */ { diff -rpNU3 base/src/backend/utils/misc/postgresql.conf.sample sepgsql-new/src/backend/utils/misc/postgresql.conf.sample --- base/src/backend/utils/misc/postgresql.conf.sample 2008-02-03 01:11:28.000000000 +0900 +++ sepgsql-new/src/backend/utils/misc/postgresql.conf.sample 2009-03-13 18:14:33.000000000 +0900 @@ -48,7 +48,7 @@ #------------------------------------------------------------------------------ -# CONNECTIONS AND AUTHENTICATION +# CONNECTIONS, AUTHENTICATION AND SECURITY #------------------------------------------------------------------------------ # - Connection Settings - @@ -96,7 +96,7 @@ # 0 selects the system default #tcp_keepalives_count = 0 # TCP_KEEPCNT; # 0 selects the system default - +#sepostgresql = off # SE-PostgreSQL support #------------------------------------------------------------------------------ # RESOURCE USAGE (except WAL) diff -rpNU3 base/src/include/access/heapam.h sepgsql-new/src/include/access/heapam.h --- base/src/include/access/heapam.h 2008-03-19 09:48:23.000000000 +0900 +++ sepgsql-new/src/include/access/heapam.h 2009-03-13 18:14:33.000000000 +0900 @@ -249,7 +249,7 @@ extern void heap_free_minimal_tuple(Mini extern MinimalTuple heap_copy_minimal_tuple(MinimalTuple mtup); extern HeapTuple heap_tuple_from_minimal_tuple(MinimalTuple mtup); extern MinimalTuple minimal_tuple_from_heap_tuple(HeapTuple htup); -extern HeapTuple heap_addheader(int natts, bool withoid, +extern HeapTuple heap_addheader(int natts, bool withoid, bool withseclabel, Size structlen, void *structure); /* in heap/pruneheap.c */ diff -rpNU3 base/src/include/access/htup.h sepgsql-new/src/include/access/htup.h --- base/src/include/access/htup.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/access/htup.h 2009-03-13 18:14:33.000000000 +0900 @@ -161,7 +161,7 @@ typedef HeapTupleHeaderData *HeapTupleHe #define HEAP_HASVARWIDTH 0x0002 /* has variable-width attribute(s) */ #define HEAP_HASEXTERNAL 0x0004 /* has external stored attribute(s) */ #define HEAP_HASOID 0x0008 /* has an object-id field */ -/* bit 0x0010 is available */ +#define HEAP_HAS_SECLABEL 0x0010 /* has an security label field */ #define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */ #define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */ #define HEAP_XMAX_SHARED_LOCK 0x0080 /* xmax is shared locker */ @@ -288,6 +288,9 @@ do { \ (tup)->t_choice.t_datum.datum_typmod = (typmod) \ ) +#define HeapTupleHeaderHasOid(tup) \ + ((tup)->t_infomask & HEAP_HASOID) + #define HeapTupleHeaderGetOid(tup) \ ( \ ((tup)->t_infomask & HEAP_HASOID) ? \ @@ -347,6 +350,34 @@ do { \ (tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \ ) +#define HeapTupleHeaderHasSecLabel(tup) \ + ((tup)->t_infomask & HEAP_HAS_SECLABEL) + +#define HeapTupleHeaderGetSecLabel(tup) \ + ( \ + HeapTupleHeaderHasSecLabel(tup) \ + ? (*((Oid *)((char *)(tup) + (tup)->t_hoff \ + - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ + - sizeof(Oid)))) \ + : InvalidOid \ + ) + +#define HeapTupleHeaderSetSecLabel(tup, seclabel) \ + do { \ + Assert(HeapTupleHeaderHasSecLabel(tup)); \ + *((Oid *)((char *)(tup) + (tup)->t_hoff \ + - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ + - sizeof(Oid))) = (seclabel); \ + } while(0) + +#define HeapTupleHasSecLabel(tuple) \ + HeapTupleHeaderHasSecLabel((tuple)->t_data) + +#define HeapTupleGetSecLabel(tuple) \ + HeapTupleHeaderGetSecLabel((tuple)->t_data) + +#define HeapTupleSetSecLabel(tuple, seclabel) \ + HeapTupleHeaderSetSecLabel((tuple)->t_data, (seclabel)) /* * BITMAPLEN(NATTS) - @@ -402,8 +433,13 @@ do { \ #define MaxTransactionIdAttributeNumber (-5) #define MaxCommandIdAttributeNumber (-6) #define TableOidAttributeNumber (-7) -#define FirstLowInvalidHeapAttributeNumber (-8) +#define SecurityLabelAttributeNumber (-8) +#define FirstLowInvalidHeapAttributeNumber (-9) +/* + * Attribute names for the system-defined attributes + */ +#define SecurityLabelAttributeName "security_context" /* * MinimalTuple is an alternative representation that is used for transient @@ -548,6 +584,9 @@ typedef HeapTupleData *HeapTuple; #define HeapTupleClearHeapOnly(tuple) \ HeapTupleHeaderClearHeapOnly((tuple)->t_data) +#define HeapTupleHasOid(tuple) \ + HeapTupleHeaderHasOid((tuple)->t_data) + #define HeapTupleGetOid(tuple) \ HeapTupleHeaderGetOid((tuple)->t_data) diff -rpNU3 base/src/include/access/tupdesc.h sepgsql-new/src/include/access/tupdesc.h --- base/src/include/access/tupdesc.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/access/tupdesc.h 2009-03-13 18:14:33.000000000 +0900 @@ -75,6 +75,7 @@ typedef struct tupleDesc Oid tdtypeid; /* composite type ID for tuple type */ int32 tdtypmod; /* typmod for tuple type */ bool tdhasoid; /* tuple has oid attribute in its header */ + bool tdhasseclabel; /* tuple has security label in its header */ int tdrefcount; /* reference count, or -1 if not counting */ } *TupleDesc; diff -rpNU3 base/src/include/catalog/heap.h sepgsql-new/src/include/catalog/heap.h --- base/src/include/catalog/heap.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/catalog/heap.h 2009-03-13 18:14:33.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 *selblList); 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, + Oid relselabel); extern List *AddRelationRawConstraints(Relation rel, List *rawColDefaults, @@ -96,6 +98,8 @@ extern Form_pg_attribute SystemAttribute extern Form_pg_attribute SystemAttributeByName(const char *attname, bool relhasoids); +extern bool SystemAttributeIsWritable(AttrNumber attnum); + extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind); extern void CheckAttributeType(const char *attname, Oid atttypid); diff -rpNU3 base/src/include/catalog/indexing.h sepgsql-new/src/include/catalog/indexing.h --- base/src/include/catalog/indexing.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/catalog/indexing.h 2009-03-13 18:14:33.000000000 +0900 @@ -252,6 +252,11 @@ DECLARE_UNIQUE_INDEX(pg_type_oid_index, DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typname name_ops, typnamespace oid_ops)); #define TypeNameNspIndexId 2704 +DECLARE_UNIQUE_INDEX(pg_security_oid_index, 3401, on pg_security using btree(oid oid_ops)); +#define SecurityOidIndexId 3401 +DECLARE_UNIQUE_INDEX(pg_security_seclabel_index, 3402, on pg_security using btree(seclabel text_ops)); +#define SecuritySeclabelIndexId 3402 + /* last step of initialization script: build the indexes declared above */ BUILD_INDICES diff -rpNU3 base/src/include/catalog/pg_attribute.h sepgsql-new/src/include/catalog/pg_attribute.h --- base/src/include/catalog/pg_attribute.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/catalog/pg_attribute.h 2009-03-13 18:14:33.000000000 +0900 @@ -282,6 +282,7 @@ DATA(insert ( 1247 cmin 29 0 4 -4 0 DATA(insert ( 1247 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0)); DATA(insert ( 1247 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0)); DATA(insert ( 1247 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0)); +DATA(insert ( 1247 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0)); /* ---------------- * pg_proc @@ -338,6 +339,7 @@ DATA(insert ( 1255 cmin 29 0 4 -4 0 DATA(insert ( 1255 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0)); DATA(insert ( 1255 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0)); DATA(insert ( 1255 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0)); +DATA(insert ( 1255 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0)); /* ---------------- * pg_attribute @@ -386,6 +388,7 @@ DATA(insert ( 1249 cmin 29 0 4 -4 0 DATA(insert ( 1249 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0)); DATA(insert ( 1249 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0)); DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0)); +DATA(insert ( 1249 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0)); /* ---------------- * pg_class @@ -454,6 +457,7 @@ DATA(insert ( 1259 cmin 29 0 4 -4 0 DATA(insert ( 1259 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0)); DATA(insert ( 1259 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0)); DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0)); +DATA(insert ( 1259 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0)); /* ---------------- * pg_index diff -rpNU3 base/src/include/catalog/pg_namespace.h sepgsql-new/src/include/catalog/pg_namespace.h --- base/src/include/catalog/pg_namespace.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/catalog/pg_namespace.h 2009-06-19 15:02:12.000000000 +0900 @@ -82,6 +82,6 @@ DESCR("standard public schema"); /* * prototypes for functions in pg_namespace.c */ -extern Oid NamespaceCreate(const char *nspName, Oid ownerId); +extern Oid NamespaceCreate(const char *nspName, Oid ownerId, Oid secid); #endif /* PG_NAMESPACE_H */ diff -rpNU3 base/src/include/catalog/pg_proc.h sepgsql-new/src/include/catalog/pg_proc.h --- base/src/include/catalog/pg_proc.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/catalog/pg_proc.h 2009-03-13 18:14:33.000000000 +0900 @@ -4113,6 +4113,22 @@ DESCR("I/O"); DATA(insert OID = 2963 ( uuid_hash PGNSP PGUID 12 1 0 f f t f i 1 23 "2950" _null_ _null_ _null_ uuid_hash - _null_ _null_ )); DESCR("hash"); +/* PostgreSQL Access Control Extension related functions */ +DATA(insert OID = 3410 ( lo_get_security PGNSP PGUID 12 1 0 f f t f v 1 25 "26" _null_ _null_ _null_ lo_get_security - _null_ _null_ )); +DATA(insert OID = 3411 ( lo_set_security PGNSP PGUID 12 1 0 f f t f v 2 16 "26 25" _null_ _null_ _null_ lo_set_security - _null_ _null_ )); + +/* SE-PostgreSQL related function */ +DATA(insert OID = 3450 ( sepgsql_getcon PGNSP PGUID 12 1 0 f f t f v 0 25 "" _null_ _null_ _null_ sepgsql_getcon - _null_ _null_ )); +DATA(insert OID = 3451 ( sepgsql_getservcon PGNSP PGUID 12 1 0 f f t f v 0 25 "" _null_ _null_ _null_ sepgsql_getservcon - _null_ _null_ )); +DATA(insert OID = 3452 ( sepgsql_get_user PGNSP PGUID 12 1 0 f f t f v 1 25 "25" _null_ _null_ _null_ sepgsql_get_user - _null_ _null_ )); +DATA(insert OID = 3453 ( sepgsql_set_user PGNSP PGUID 12 1 0 f f t f v 2 25 "25 25" _null_ _null_ _null_ sepgsql_set_user - _null_ _null_ )); +DATA(insert OID = 3454 ( sepgsql_get_role PGNSP PGUID 12 1 0 f f t f v 1 25 "25" _null_ _null_ _null_ sepgsql_get_role - _null_ _null_ )); +DATA(insert OID = 3455 ( sepgsql_set_role PGNSP PGUID 12 1 0 f f t f v 2 25 "25 25" _null_ _null_ _null_ sepgsql_set_role - _null_ _null_ )); +DATA(insert OID = 3456 ( sepgsql_get_type PGNSP PGUID 12 1 0 f f t f v 1 25 "25" _null_ _null_ _null_ sepgsql_get_type - _null_ _null_ )); +DATA(insert OID = 3457 ( sepgsql_set_type PGNSP PGUID 12 1 0 f f t f v 2 25 "25 25" _null_ _null_ _null_ sepgsql_set_type - _null_ _null_ )); +DATA(insert OID = 3458 ( sepgsql_get_range PGNSP PGUID 12 1 0 f f t f v 1 25 "25" _null_ _null_ _null_ sepgsql_get_range - _null_ _null_ )); +DATA(insert OID = 3459 ( sepgsql_set_range PGNSP PGUID 12 1 0 f f t f v 2 25 "25 25" _null_ _null_ _null_ sepgsql_set_range - _null_ _null_ )); + /* enum related procs */ DATA(insert OID = 3504 ( anyenum_in PGNSP PGUID 12 1 0 f f t f i 1 3500 "2275" _null_ _null_ _null_ anyenum_in - _null_ _null_ )); DESCR("I/O"); @@ -4460,7 +4476,8 @@ extern Oid ProcedureCreate(const char *p Datum parameterNames, Datum proconfig, float4 procost, - float4 prorows); + float4 prorows, + Oid proselabel); extern bool function_parse_error_transpose(const char *prosrc); diff -rpNU3 base/src/include/catalog/pg_security.h sepgsql-new/src/include/catalog/pg_security.h --- base/src/include/catalog/pg_security.h 1970-01-01 09:00:00.000000000 +0900 +++ sepgsql-new/src/include/catalog/pg_security.h 2009-06-10 17:43:08.000000000 +0900 @@ -0,0 +1,58 @@ +/* + * 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 + +#include "access/htup.h" +#include "utils/relcache.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 + +/* + * functions to translate between security label and identifier + */ +extern void +securityPostBootstrapingMode(void); + +extern bool +securityTupleDescHasSecLabel(Relation rel); + +extern Oid +securityRawSecLabelIn(Oid relid, char *seclabel); + +extern char * +securityRawSecLabelOut(Oid relid, Oid secid); + +extern Oid +securityTransSecLabelIn(Oid relid, char *seclabel); + +extern char * +securityTransSecLabelOut(Oid relid, Oid secid); + +extern Datum +securityHeapGetSecLabelSysattr(HeapTuple tuple); + +#endif /* PG_SELINUX_H */ diff -rpNU3 base/src/include/commands/alter.h sepgsql-new/src/include/commands/alter.h --- base/src/include/commands/alter.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/commands/alter.h 2009-06-19 15:02:12.000000000 +0900 @@ -19,5 +19,6 @@ extern void ExecRenameStmt(RenameStmt *stmt); extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt); extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt); +extern void ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt); #endif /* ALTER_H */ diff -rpNU3 base/src/include/commands/dbcommands.h sepgsql-new/src/include/commands/dbcommands.h --- base/src/include/commands/dbcommands.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/commands/dbcommands.h 2009-06-19 15:02:12.000000000 +0900 @@ -58,6 +58,7 @@ extern void RenameDatabase(const char *o extern void AlterDatabase(AlterDatabaseStmt *stmt); extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt); extern void AlterDatabaseOwner(const char *dbname, Oid newOwnerId); +extern void AlterDatabaseSecLabel(const char *dbname, DefElem *seclabel); extern Oid get_database_oid(const char *dbname); extern char *get_database_name(Oid dbid); diff -rpNU3 base/src/include/commands/defrem.h sepgsql-new/src/include/commands/defrem.h --- base/src/include/commands/defrem.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/commands/defrem.h 2009-06-19 15:02:12.000000000 +0900 @@ -54,6 +54,7 @@ extern void SetFunctionArgType(Oid funcO extern void RenameFunction(List *name, List *argtypes, const char *newname); extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId); extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId); +extern void AlterFunctionSecLabel(List *name, List *argtypes, DefElem *seclabel); extern void AlterFunction(AlterFunctionStmt *stmt); extern void CreateCast(CreateCastStmt *stmt); extern void DropCast(DropCastStmt *stmt); diff -rpNU3 base/src/include/commands/schemacmds.h sepgsql-new/src/include/commands/schemacmds.h --- base/src/include/commands/schemacmds.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/commands/schemacmds.h 2009-06-19 15:02:12.000000000 +0900 @@ -26,5 +26,6 @@ extern void RemoveSchemaById(Oid schemaO extern void RenameSchema(const char *oldname, const char *newname); extern void AlterSchemaOwner(const char *name, Oid newOwnerId); extern void AlterSchemaOwner_oid(Oid schemaOid, Oid newOwnerId); +extern void AlterSchemaSecLabel(const char *name, DefElem *seclabel); #endif /* SCHEMACMDS_H */ diff -rpNU3 base/src/include/commands/tablecmds.h sepgsql-new/src/include/commands/tablecmds.h --- base/src/include/commands/tablecmds.h 2008-02-03 01:11:28.000000000 +0900 +++ sepgsql-new/src/include/commands/tablecmds.h 2009-06-19 15:02:12.000000000 +0900 @@ -34,6 +34,9 @@ extern void AlterRelationNamespaceIntern Oid oldNspOid, Oid newNspOid, bool hasDependEntry); +extern void AlterRelationSecLabel(RangeVar *relation, const char *attname, + ObjectType objtype, DefElem *seclabel); + extern void CheckTableNotInUse(Relation rel, const char *stmt); extern void ExecuteTruncate(TruncateStmt *stmt); diff -rpNU3 base/src/include/executor/executor.h sepgsql-new/src/include/executor/executor.h --- base/src/include/executor/executor.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/executor/executor.h 2009-03-13 18:14:33.000000000 +0900 @@ -117,7 +117,7 @@ extern TupleHashEntry FindTupleHashEntry * prototypes from functions in execJunk.c */ extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, - TupleTableSlot *slot); + bool hasseclabel, TupleTableSlot *slot); extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, TupleDesc cleanTupType, TupleTableSlot *slot); @@ -140,6 +140,7 @@ extern void ExecutorEnd(QueryDesc *query extern void ExecutorRewind(QueryDesc *queryDesc); extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); +extern bool ExecContextForcesSecLabel(PlanState *planstate, bool *hasseclabel); extern void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate); extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, @@ -199,8 +200,8 @@ extern void ExecInitScanTupleSlot(EState extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate); extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, TupleDesc tupType); -extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid); -extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid); +extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid, bool hasseclabel); +extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hasseclabel); extern TupleDesc ExecTypeFromExprList(List *exprList); extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); diff -rpNU3 base/src/include/executor/tuptable.h sepgsql-new/src/include/executor/tuptable.h --- base/src/include/executor/tuptable.h 2009-09-10 20:11:35.000000000 +0900 +++ sepgsql-new/src/include/executor/tuptable.h 2009-09-10 20:24:37.000000000 +0900 @@ -127,6 +127,7 @@ typedef struct TupleTableSlot MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */ HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */ long tts_off; /* saved state for slot_deform_tuple */ + Datum tts_seclabel; /* temp storage for the given security_label */ } TupleTableSlot; #define TTS_HAS_PHYSICAL_TUPLE(slot) \ diff -rpNU3 base/src/include/fmgr.h sepgsql-new/src/include/fmgr.h --- base/src/include/fmgr.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/fmgr.h 2009-03-13 18:14:33.000000000 +0900 @@ -52,6 +52,15 @@ 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 */ + +#ifdef HAVE_SELINUX + /* + * Note: SELinux allows to switch the security context of client + * inside the specific functions labeled as trusted procedure. + */ + PGFunction sepgsql_addr; /* address of original pointer */ + char *sepgsql_label; /* temporary security context */ +#endif } FmgrInfo; /* @@ -511,6 +520,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-new/src/include/libpq/be-fsstubs.h --- base/src/include/libpq/be-fsstubs.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/libpq/be-fsstubs.h 2009-03-13 18:14:33.000000000 +0900 @@ -36,6 +36,9 @@ extern Datum lo_tell(PG_FUNCTION_ARGS); extern Datum lo_unlink(PG_FUNCTION_ARGS); extern Datum lo_truncate(PG_FUNCTION_ARGS); +extern Datum lo_get_security(PG_FUNCTION_ARGS); +extern Datum lo_set_security(PG_FUNCTION_ARGS); + /* * These are not fmgr-callable, but are available to C code. * Probably these should have had the underscore-free names, diff -rpNU3 base/src/include/nodes/nodes.h sepgsql-new/src/include/nodes/nodes.h --- base/src/include/nodes/nodes.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/nodes/nodes.h 2009-06-19 15:02:12.000000000 +0900 @@ -313,6 +313,7 @@ typedef enum NodeTag T_CreateEnumStmt, T_AlterTSDictionaryStmt, T_AlterTSConfigurationStmt, + T_AlterSecLabelStmt, /* * TAGS FOR PARSE TREE NODES (parsenodes.h) diff -rpNU3 base/src/include/nodes/parsenodes.h sepgsql-new/src/include/nodes/parsenodes.h --- base/src/include/nodes/parsenodes.h 2008-03-19 09:48:23.000000000 +0900 +++ sepgsql-new/src/include/nodes/parsenodes.h 2009-06-19 15:02:12.000000000 +0900 @@ -14,6 +14,7 @@ #ifndef PARSENODES_H #define PARSENODES_H +#include "nodes/bitmapset.h" #include "nodes/primnodes.h" #include "nodes/value.h" @@ -391,6 +392,7 @@ typedef struct ColumnDef Node *raw_default; /* default value (untransformed parse tree) */ char *cooked_default; /* nodeToString representation */ List *constraints; /* other constraints on column */ + Node *secLabel; /* explicitly specified security label */ } ColumnDef; /* @@ -602,6 +604,8 @@ typedef struct RangeTblEntry bool inFromCl; /* present in FROM clause? */ AclMode requiredPerms; /* bitmask of required access permissions */ Oid checkAsUser; /* if valid, check access as this role */ + Bitmapset *selectedCols; /* columns needing SELECT permission */ + Bitmapset *modifiedCols; /* columns needing INSERT/UPDATE permission */ } RangeTblEntry; /* @@ -857,6 +861,7 @@ typedef struct CreateSchemaStmt NodeTag type; char *schemaname; /* the name of the schema to create */ char *authid; /* the owner of the created schema */ + Node *secLabel; /* explicitly specified security label */ List *schemaElts; /* schema components (list of parsenodes) */ } CreateSchemaStmt; @@ -1108,6 +1113,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 *secLabel; /* explicitly specified security label */ } CreateStmt; /* ---------- @@ -1320,6 +1326,7 @@ typedef struct CreateSeqStmt NodeTag type; RangeVar *sequence; /* the sequence to create */ List *options; + Node *secLabel; } CreateSeqStmt; typedef struct AlterSeqStmt @@ -1671,6 +1678,20 @@ typedef struct AlterOwnerStmt char *newowner; /* the new owner */ } AlterOwnerStmt; +/* ---------------------- + * Alter Object Security Label Statement + * ---------------------- + */ +typedef struct AlterSecLabelStmt +{ + NodeTag type; + ObjectType objectType; /* OBJECT_TABLE, OBJECT_COLUMN, etc */ + RangeVar *relation; /* in case it's a table */ + List *object; /* in case it's some other object */ + List *objarg; /* argument types, if applicable */ + char *subname; /* column name, if needed */ + Node *secLabel; /* the new security label */ +} AlterSecLabelStmt; /* ---------------------- * Create Rule Statement diff -rpNU3 base/src/include/nodes/plannodes.h sepgsql-new/src/include/nodes/plannodes.h --- base/src/include/nodes/plannodes.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/nodes/plannodes.h 2009-05-20 17:08:02.000000000 +0900 @@ -16,6 +16,7 @@ #include "access/sdir.h" #include "nodes/bitmapset.h" +#include "nodes/parsenodes.h" #include "nodes/primnodes.h" @@ -73,6 +74,8 @@ typedef struct PlannedStmt List *relationOids; /* OIDs of relations the plan depends on */ int nParamExec; /* number of PARAM_EXEC Params used */ + + List *selinuxItems; /* list of SelinuxEvalItem */ } PlannedStmt; /* macro for fetching the Plan associated with a SubPlan node */ @@ -216,6 +219,12 @@ typedef struct Scan { Plan plan; Index scanrelid; /* relid is index into the range table */ + + /* + * Row-level access control stuff. Zero means we don't need + * to apply row-level access control on the Scan. + */ + uint32 rowlvPerms; } Scan; /* ---------------- diff -rpNU3 base/src/include/nodes/relation.h sepgsql-new/src/include/nodes/relation.h --- base/src/include/nodes/relation.h 2009-09-10 20:11:35.000000000 +0900 +++ sepgsql-new/src/include/nodes/relation.h 2009-09-10 20:24:37.000000000 +0900 @@ -366,6 +366,15 @@ typedef struct RelOptInfo * list just to avoid recomputing the best inner indexscan repeatedly for * similar outer relations. See comments for InnerIndexscanInfo. */ + + /* + * Permissions used in Row-level access control features both of DAC + * and MAC. The lower 16bit is used for DAC, and rest of upper bits + * are used for MAC. When rowlvPerms is zero, so it means we don't need + * to apply the row-level stuff on the relation in both of levels. + * It can be used as a hint for optimization stuff. + */ + uint32 rowlvPerms; } RelOptInfo; /* diff -rpNU3 base/src/include/parser/parse_node.h sepgsql-new/src/include/parser/parse_node.h --- base/src/include/parser/parse_node.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/parser/parse_node.h 2009-03-13 13:09:47.000000000 +0900 @@ -64,6 +64,7 @@ typedef struct ParseState struct ParseState *parentParseState; /* stack link */ const char *p_sourcetext; /* source text, or NULL if not available */ List *p_rtable; /* range table so far */ + List *p_joinexprs; /* JoinExprs for RTE_JOIN p_rtable entries */ List *p_joinlist; /* join items so far (will become FromExpr * node's fromlist) */ List *p_relnamespace; /* current namespace for relations */ diff -rpNU3 base/src/include/parser/parse_relation.h sepgsql-new/src/include/parser/parse_relation.h --- base/src/include/parser/parse_relation.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/parser/parse_relation.h 2009-03-13 13:09:47.000000000 +0900 @@ -40,6 +40,8 @@ extern Node *qualifiedNameToVar(ParseSta char *colname, bool implicitRTEOK, int location); +extern void markVarForSelectPriv(ParseState *pstate, Var *var, + RangeTblEntry *rte); extern RangeTblEntry *addRangeTableEntry(ParseState *pstate, RangeVar *relation, Alias *alias, diff -rpNU3 base/src/include/pg_config.h.in sepgsql-new/src/include/pg_config.h.in --- base/src/include/pg_config.h.in 2008-01-28 16:06:37.000000000 +0900 +++ sepgsql-new/src/include/pg_config.h.in 2009-03-13 18:14:33.000000000 +0900 @@ -366,6 +366,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SECURITY_PAM_APPL_H +/* Define to 1 if you enable SELinux support */ +#undef HAVE_SELINUX + /* Define to 1 if you have the `setproctitle' function. */ #undef HAVE_SETPROCTITLE diff -rpNU3 base/src/include/security/rowlevel.h sepgsql-new/src/include/security/rowlevel.h --- base/src/include/security/rowlevel.h 1970-01-01 09:00:00.000000000 +0900 +++ sepgsql-new/src/include/security/rowlevel.h 2009-05-20 17:08:02.000000000 +0900 @@ -0,0 +1,47 @@ +/* + * src/include/security/rowlevel.h + * Definition of the facility of row-level access controls + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ +#ifndef ROWLEVEL_H +#define ROWLEVEL_H + +#include "access/htup.h" +#include "executor/tuptable.h" +#include "nodes/plannodes.h" +#include "utils/relcache.h" + +#define ROWLV_BYPASS_MODE 1 +#define ROWLV_FILTER_MODE 2 +#define ROWLV_ABORT_MODE 3 + +extern int +rowlvGetPerformingMode(void); + +extern int +rowlvSetPerformingMode(int mode); + +extern uint32 +rowlvSetupPermissions(RangeTblEntry *rte); + +extern bool +rowlvExecScanFilter(Scan *scan, Relation rel, TupleTableSlot *slot); + +extern void +rowlvExecScanAbort(Scan *scan, Relation rel, TupleTableSlot *slot); + +extern bool +rowlvHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal); + +extern bool +rowlvHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup, bool internal); + +extern bool +rowlvHeapTupleDelete(Relation rel, ItemPointer otid, bool internal); + +extern bool +rowlvCopyToTuple(Relation rel, HeapTuple tuple); + +#endif /* PG_SELINUX_H */ diff -rpNU3 base/src/include/security/sepgsql.h sepgsql-new/src/include/security/sepgsql.h --- base/src/include/security/sepgsql.h 1970-01-01 09:00:00.000000000 +0900 +++ sepgsql-new/src/include/security/sepgsql.h 2009-06-19 15:02:12.000000000 +0900 @@ -0,0 +1,466 @@ +/* + * src/include/utils/sepgsql.h + * Headers of SE-PostgreSQL + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ +#ifndef SEPGSQL_H +#define SEPGSQL_H + +#include "access/htup.h" +#include "executor/execdesc.h" +#include "fmgr.h" +#include "nodes/parsenodes.h" +#include "storage/large_object.h" +#include "utils/relcache.h" + +#ifdef HAVE_SELINUX + +#include + +/* GUC parameter to turn on/off SE-PostgreSQL */ +extern bool sepostgresql_is_enabled; + +/* GUC parameter to turn on/off Row-level controls */ +extern bool sepostgresql_row_level; + +/* GUC parameter to turn on/off mcstrans */ +extern bool sepostgresql_use_mcstrans; + +/* Objject classes and permissions internally used */ +enum SepgsqlClasses +{ + SEPG_CLASS_PROCESS = 0, + SEPG_CLASS_FILE, + SEPG_CLASS_DIR, + SEPG_CLASS_LNK_FILE, + SEPG_CLASS_CHR_FILE, + SEPG_CLASS_BLK_FILE, + SEPG_CLASS_SOCK_FILE, + SEPG_CLASS_FIFO_FILE, + SEPG_CLASS_DB_DATABASE, + SEPG_CLASS_DB_SCHEMA, + SEPG_CLASS_DB_SCHEMA_TEMP, + SEPG_CLASS_DB_TABLE, + SEPG_CLASS_DB_SEQUENCE, + SEPG_CLASS_DB_PROCEDURE, + SEPG_CLASS_DB_COLUMN, + SEPG_CLASS_DB_TUPLE, + SEPG_CLASS_DB_BLOB, + SEPG_CLASS_MAX, +}; + +#define SEPG_PROCESS__TRANSITION (1<<0) + +#define SEPG_FILE__READ (1<<0) +#define SEPG_FILE__WRITE (1<<1) + +#define SEPG_DIR__READ (SEPG_FILE__READ) +#define SEPG_DIR__WRITE (SEPG_FILE__WRITE) + +#define SEPG_LNK_FILE__READ (SEPG_FILE__READ) +#define SEPG_LNK_FILE__WRITE (SEPG_FILE__WRITE) + +#define SEPG_CHR_FILE__READ (SEPG_FILE__READ) +#define SEPG_CHR_FILE__WRITE (SEPG_FILE__WRITE) + +#define SEPG_BLK_FILE__READ (SEPG_FILE__READ) +#define SEPG_BLK_FILE__WRITE (SEPG_FILE__WRITE) + +#define SEPG_SOCK_FILE__READ (SEPG_FILE__READ) +#define SEPG_SOCK_FILE__WRITE (SEPG_FILE__WRITE) + +#define SEPG_FIFO_FILE__READ (SEPG_FILE__READ) +#define SEPG_FIFO_FILE__WRITE (SEPG_FILE__WRITE) + +#define SEPG_DB_DATABASE__CREATE (1<<0) +#define SEPG_DB_DATABASE__DROP (1<<1) +#define SEPG_DB_DATABASE__GETATTR (1<<2) +#define SEPG_DB_DATABASE__SETATTR (1<<3) +#define SEPG_DB_DATABASE__RELABELFROM (1<<4) +#define SEPG_DB_DATABASE__RELABELTO (1<<5) +#define SEPG_DB_DATABASE__ACCESS (1<<6) +#define SEPG_DB_DATABASE__INSTALL_MODULE (1<<7) +#define SEPG_DB_DATABASE__LOAD_MODULE (1<<8) +#define SEPG_DB_DATABASE__SUPERUSER (1<<9) + +#define SEPG_DB_SCHEMA__CREATE (SEPG_DB_DATABASE__CREATE) +#define SEPG_DB_SCHEMA__DROP (SEPG_DB_DATABASE__DROP) +#define SEPG_DB_SCHEMA__GETATTR (SEPG_DB_DATABASE__GETATTR) +#define SEPG_DB_SCHEMA__SETATTR (SEPG_DB_DATABASE__SETATTR) +#define SEPG_DB_SCHEMA__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) +#define SEPG_DB_SCHEMA__RELABELTO (SEPG_DB_DATABASE__RELABELTO) +#define SEPG_DB_SCHEMA__SEARCH (1<<6) +#define SEPG_DB_SCHEMA__ADD_OBJECT (1<<7) +#define SEPG_DB_SCHEMA__REMOVE_OBJECT (1<<8) + +#define SEPG_DB_SCHEMA_TEMP__CREATE (SEPG_DB_DATABASE__CREATE) +#define SEPG_DB_SCHEMA_TEMP__DROP (SEPG_DB_DATABASE__DROP) +#define SEPG_DB_SCHEMA_TEMP__GETATTR (SEPG_DB_DATABASE__GETATTR) +#define SEPG_DB_SCHEMA_TEMP__SETATTR (SEPG_DB_DATABASE__SETATTR) +#define SEPG_DB_SCHEMA_TEMP__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) +#define SEPG_DB_SCHEMA_TEMP__RELABELTO (SEPG_DB_DATABASE__RELABELTO) +#define SEPG_DB_SCHEMA_TEMP__SEARCH (SEPG_DB_SCHEMA__SEARCH) +#define SEPG_DB_SCHEMA_TEMP__ADD_OBJECT (SEPG_DB_SCHEMA__ADD_OBJECT) +#define SEPG_DB_SCHEMA_TEMP__REMOVE_OBJECT (SEPG_DB_SCHEMA__REMOVE_OBJECT) + +#define SEPG_DB_TABLE__CREATE (SEPG_DB_DATABASE__CREATE) +#define SEPG_DB_TABLE__DROP (SEPG_DB_DATABASE__DROP) +#define SEPG_DB_TABLE__GETATTR (SEPG_DB_DATABASE__GETATTR) +#define SEPG_DB_TABLE__SETATTR (SEPG_DB_DATABASE__SETATTR) +#define SEPG_DB_TABLE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) +#define SEPG_DB_TABLE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) +#define SEPG_DB_TABLE__SELECT (1<<6) +#define SEPG_DB_TABLE__UPDATE (1<<7) +#define SEPG_DB_TABLE__INSERT (1<<8) +#define SEPG_DB_TABLE__DELETE (1<<9) +#define SEPG_DB_TABLE__LOCK (1<<10) +#define SEPG_DB_TABLE__REFERENCE (1<<11) + +#define SEPG_DB_SEQUENCE__CREATE (SEPG_DB_DATABASE__CREATE) +#define SEPG_DB_SEQUENCE__DROP (SEPG_DB_DATABASE__DROP) +#define SEPG_DB_SEQUENCE__GETATTR (SEPG_DB_DATABASE__GETATTR) +#define SEPG_DB_SEQUENCE__SETATTR (SEPG_DB_DATABASE__SETATTR) +#define SEPG_DB_SEQUENCE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) +#define SEPG_DB_SEQUENCE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) +#define SEPG_DB_SEQUENCE__GET_VALUE (1<<6) +#define SEPG_DB_SEQUENCE__NEXT_VALUE (1<<7) +#define SEPG_DB_SEQUENCE__SET_VALUE (1<<8) + +#define SEPG_DB_PROCEDURE__CREATE (SEPG_DB_DATABASE__CREATE) +#define SEPG_DB_PROCEDURE__DROP (SEPG_DB_DATABASE__DROP) +#define SEPG_DB_PROCEDURE__GETATTR (SEPG_DB_DATABASE__GETATTR) +#define SEPG_DB_PROCEDURE__SETATTR (SEPG_DB_DATABASE__SETATTR) +#define SEPG_DB_PROCEDURE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) +#define SEPG_DB_PROCEDURE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) +#define SEPG_DB_PROCEDURE__EXECUTE (1<<6) +#define SEPG_DB_PROCEDURE__ENTRYPOINT (1<<7) +#define SEPG_DB_PROCEDURE__INSTALL (1<<8) + +#define SEPG_DB_COLUMN__CREATE (SEPG_DB_DATABASE__CREATE) +#define SEPG_DB_COLUMN__DROP (SEPG_DB_DATABASE__DROP) +#define SEPG_DB_COLUMN__GETATTR (SEPG_DB_DATABASE__GETATTR) +#define SEPG_DB_COLUMN__SETATTR (SEPG_DB_DATABASE__SETATTR) +#define SEPG_DB_COLUMN__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) +#define SEPG_DB_COLUMN__RELABELTO (SEPG_DB_DATABASE__RELABELTO) +#define SEPG_DB_COLUMN__SELECT (1<<6) +#define SEPG_DB_COLUMN__UPDATE (1<<7) +#define SEPG_DB_COLUMN__INSERT (1<<8) +#define SEPG_DB_COLUMN__REFERENCE (1<<9) + +#define SEPG_DB_TUPLE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) +#define SEPG_DB_TUPLE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) +#define SEPG_DB_TUPLE__SELECT (SEPG_DB_DATABASE__GETATTR) +#define SEPG_DB_TUPLE__UPDATE (SEPG_DB_DATABASE__SETATTR) +#define SEPG_DB_TUPLE__INSERT (SEPG_DB_DATABASE__CREATE) +#define SEPG_DB_TUPLE__DELETE (SEPG_DB_DATABASE__DROP) + +#define SEPG_DB_BLOB__CREATE (SEPG_DB_DATABASE__CREATE) +#define SEPG_DB_BLOB__DROP (SEPG_DB_DATABASE__DROP) +#define SEPG_DB_BLOB__GETATTR (SEPG_DB_DATABASE__GETATTR) +#define SEPG_DB_BLOB__SETATTR (SEPG_DB_DATABASE__SETATTR) +#define SEPG_DB_BLOB__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) +#define SEPG_DB_BLOB__RELABELTO (SEPG_DB_DATABASE__RELABELTO) +#define SEPG_DB_BLOB__READ (1<<6) +#define SEPG_DB_BLOB__WRITE (1<<7) +#define SEPG_DB_BLOB__IMPORT (1<<8) +#define SEPG_DB_BLOB__EXPORT (1<<9) + +/* + * avc.c : userspace access vector cache + */ +extern Size sepgsqlShmemSize(void); + +extern bool sepgsqlGetExceptionMode(void); + +extern bool sepgsqlSetExceptionMode(bool exception); + +extern void sepgsqlAvcInit(void); + +extern pid_t sepgsqlStartupWorkerProcess(void); + +extern void sepgsqlAvcSwitchClient(void); + +extern bool +sepgsqlClientHasPermsTup(Oid relid, HeapTuple tuple, + security_class_t tclass, + access_vector_t required, bool abort); +extern bool +sepgsqlClientHasPermsSid(Oid relid, Oid secid, + security_class_t tclass, + access_vector_t required, + const char *audit_name, bool abort); +extern Oid +sepgsqlClientCreateSecid(Oid trelid, Oid tsecid, + security_class_t tclass, Oid nrelid); + +extern security_context_t +sepgsqlClientCreateLabel(Oid trelid, Oid tsecid, + security_class_t tclass); + +extern bool +sepgsqlComputePerms(security_context_t scontext, + security_context_t tcontext, + security_class_t tclass, + access_vector_t required, + const char *audit_name, bool abort); + +extern security_context_t +sepgsqlComputeCreate(security_context_t scontext, + security_context_t tcontext, + security_class_t tclass); + +/* + * checker.c : check permission on given queries + */ +extern void +sepgsqlCheckRTEPerms(RangeTblEntry *rte); + +extern void +sepgsqlCheckCopyTable(Relation rel, List *attnumlist, bool is_from); + +extern void +sepgsqlCheckSelectInto(Oid relaionId); + +extern bool +sepgsqlExecScan(Relation rel, HeapTuple tuple, uint32 required, bool abort); + +extern uint32 +sepgsqlSetupTuplePerms(RangeTblEntry *rte); + +extern bool +sepgsqlHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal); + +extern bool +sepgsqlHeapTupleUpdate(Relation rel, HeapTuple oldtup, HeapTuple newtup, bool internal); + +extern bool +sepgsqlHeapTupleDelete(Relation rel, HeapTuple oldtup, bool internal); + +/* + * core.c : core facilities + */ +extern security_context_t +sepgsqlGetServerLabel(void); + +extern security_context_t +sepgsqlGetClientLabel(void); + +extern security_context_t +sepgsqlSwitchClient(security_context_t new_client); + +extern bool +sepgsqlIsEnabled(void); + +extern void +sepgsqlInitialize(void); + +/* + * hooks.c : test certain permissions + */ +extern bool +sepgsqlCheckDatabaseAccess(Oid database_oid); + +extern bool +sepgsqlCheckDatabaseSuperuser(void); + +extern void +sepgsqlCheckDatabaseInstallModule(void); + +extern void +sepgsqlCheckDatabaseLoadModule(const char *filename); + +extern bool +sepgsqlCheckSchemaSearch(Oid nsid); + +extern void +sepgsqlCheckSchemaAddRemove(Relation rel, HeapTuple newtup, HeapTuple oldtup); + +extern void +sepgsqlCheckTableLock(Oid table_oid); + +extern void +sepgsqlCheckTableTruncate(Relation rel); + +extern void +sepgsqlCheckTableReference(Relation rel, int16 *attnums, int natts); + +extern void +sepgsqlCheckSequenceGetValue(Oid seqid); + +extern void +sepgsqlCheckSequenceNextValue(Oid seqid); + +extern void +sepgsqlCheckSequenceSetValue(Oid seqid); + +extern bool +sepgsqlCheckProcedureExecute(Oid proc_oid); + +extern void +sepgsqlCheckProcedureEntrypoint(FmgrInfo *finfo, HeapTuple protup); + +extern void +sepgsqlCheckProcedureInstall(Relation rel, HeapTuple newtup, HeapTuple oldtup); + +extern void +sepgsqlCheckBlobCreate(Relation rel, HeapTuple lotup); + +extern void +sepgsqlCheckBlobDrop(Relation rel, HeapTuple lotup); + +extern void +sepgsqlCheckBlobRead(LargeObjectDesc *lobj); + +extern void +sepgsqlCheckBlobWrite(LargeObjectDesc *lobj); + +extern void +sepgsqlCheckBlobGetattr(HeapTuple tuple); + +extern void +sepgsqlCheckBlobSetattr(HeapTuple tuple); + +extern void +sepgsqlCheckBlobExport(LargeObjectDesc *lobj, + int fdesc, const char *filename); +extern void +sepgsqlCheckBlobImport(LargeObjectDesc *lobj, + int fdesc, const char *filename); +extern void +sepgsqlCheckBlobRelabel(HeapTuple oldtup, HeapTuple newtup); + +extern void +sepgsqlCheckFileRead(int fdesc, const char *filename); + +extern void +sepgsqlCheckFileWrite(int fdesc, const char *filename); + +// Hint for optimizer +extern bool +sepgsqlAllowFunctionInlined(HeapTuple protup); + +/* + * label.c : security label management + */ +extern bool +sepgsqlTupleDescHasSecLabel(Relation rel); + +extern void +sepgsqlSetDefaultSecLabel(Relation rel, HeapTuple tuple); + +extern security_context_t +sepgsqlMetaSecurityLabel(bool shared); + +extern Oid +sepgsqlGivenSecLabelIn(Oid relid, DefElem *defel); + +extern List * +sepgsqlParseCreateStmtSecLabelIn(CreateStmt *stmt); + +extern security_context_t +sepgsqlTransSecLabelIn(security_context_t seclabel); + +extern security_context_t +sepgsqlTransSecLabelOut(security_context_t seclabel); + +extern security_context_t +sepgsqlRawSecLabelIn(security_context_t seclabel); + +extern security_context_t +sepgsqlRawSecLabelOut(security_context_t seclabel); + +/* + * perms.c : SELinux permission related stuff + */ +extern const char * +sepgsqlAuditName(Oid relid, HeapTuple tuple); + +extern security_class_t +sepgsqlFileObjectClass(int fdesc); + +extern security_class_t +sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple); + +extern security_class_t +sepgsqlTransToExternalClass(security_class_t tclass_in); + +extern void +sepgsqlTransToInternalPerms(security_class_t tclass_ex, struct av_decision *avd); + +extern const char * +sepgsqlGetClassString(security_class_t tclass); + +extern const char * +sepgsqlGetPermissionString(security_class_t tclass, access_vector_t av); + +#else /* HAVE_SELINUX */ + +/* avc.c */ +#define sepgsqlShmemSize() (0) +#define sepgsqlStartupWorkerProcess() (0) +#define sepgsqlGetExceptionMode() (0) +#define sepgsqlSetExceptionMode(a) (0) +/* checker.c */ +#define sepgsqlCheckRTEPerms(a) do {} while(0) +#define sepgsqlCheckCopyTable(a,b,c) do {} while(0) +#define sepgsqlCheckSelectInto(a) do {} while(0) +#define sepgsqlSetupTuplePerms(a) (0) +#define sepgsqlExecScan(a,b,c,d) (true) +#define sepgsqlHeapTupleInsert(a,b,c) (true) +#define sepgsqlHeapTupleUpdate(a,b,c,d) (true) +#define sepgsqlHeapTupleDelete(a,b,c) (true) +/* core.c */ +#define sepgsqlIsEnabled() (false) +#define sepgsqlInitialize() do {} while(0) +/* hooks.c */ +#define sepgsqlCheckDatabaseAccess(a) (true) +#define sepgsqlCheckDatabaseSuperuser() (true) +#define sepgsqlCheckDatabaseInstallModule() do {} while(0) +#define sepgsqlCheckDatabaseLoadModule(a) do {} while(0) +#define sepgsqlCheckSchemaSearch(a) (true) +#define sepgsqlCheckTableLock(a) do {} while(0) +#define sepgsqlCheckTableTruncate(a) do {} while(0) +#define sepgsqlCheckTableReference(a,b,c) do {} while(0) +#define sepgsqlCheckSequenceGetValue(a) do {} while(0) +#define sepgsqlCheckSequenceNextValue(a) do {} while(0) +#define sepgsqlCheckSequenceSetValue(a) do {} while(0) +#define sepgsqlCheckProcedureExecute(a) (true) +#define sepgsqlCheckProcedureEntrypoint(a,b) do {} while(0) +#define sepgsqlCheckBlobCreate(a,b) do {} while(0) +#define sepgsqlCheckBlobDrop(a,b) do {} while(0) +#define sepgsqlCheckBlobRead(a) do {} while(0) +#define sepgsqlCheckBlobWrite(a) do {} while(0) +#define sepgsqlCheckBlobGetattr(a) do {} while(0) +#define sepgsqlCheckBlobSetattr(a) do {} while(0) +#define sepgsqlCheckBlobExport(a,b,c) do {} while(0) +#define sepgsqlCheckBlobImport(a,b,c) do {} while(0) +#define sepgsqlCheckBlobRelabel(a,b) do {} while(0) +#define sepgsqlCheckFileRead(a,b) do {} while(0) +#define sepgsqlCheckFileWrite(a,b) do {} while(0) +#define sepgsqlAllowFunctionInlined(a) (true) +/* label.c */ +#define sepgsqlTupleDescHasSecLabel(a) (false) +#define sepgsqlMetaSecurityLabel(a) (NULL) +#define sepgsqlGivenSecLabelIn(a,b) (InvalidOid) +#define sepgsqlParseCreateStmtSecLabelIn(a) (NIL) +#define sepgsqlTransSecLabelIn(a) (a) +#define sepgsqlTransSecLabelOut(a) (a) +#define sepgsqlRawSecLabelIn(a) (a) +#define sepgsqlRawSecLabelOut(a) (a) + +#endif /* HAVE_SELINUX */ + +extern Datum sepgsql_getcon(PG_FUNCTION_ARGS); +extern Datum sepgsql_getservcon(PG_FUNCTION_ARGS); +extern Datum sepgsql_get_user(PG_FUNCTION_ARGS); +extern Datum sepgsql_get_role(PG_FUNCTION_ARGS); +extern Datum sepgsql_get_type(PG_FUNCTION_ARGS); +extern Datum sepgsql_get_range(PG_FUNCTION_ARGS); +extern Datum sepgsql_set_user(PG_FUNCTION_ARGS); +extern Datum sepgsql_set_role(PG_FUNCTION_ARGS); +extern Datum sepgsql_set_type(PG_FUNCTION_ARGS); +extern Datum sepgsql_set_range(PG_FUNCTION_ARGS); + +#endif /* SEPGSQL_H */ diff -rpNU3 base/src/include/storage/fd.h sepgsql-new/src/include/storage/fd.h --- base/src/include/storage/fd.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/storage/fd.h 2009-03-13 18:14:33.000000000 +0900 @@ -67,6 +67,7 @@ extern int FileWrite(File file, char *bu extern int FileSync(File file); extern long FileSeek(File file, long offset, int whence); extern int FileTruncate(File file, long offset); +extern int FileRawDescriptor(File file); /* Operations that allow use of regular stdio --- USE WITH CAUTION */ extern FILE *AllocateFile(const char *name, const char *mode); diff -rpNU3 base/src/include/storage/large_object.h sepgsql-new/src/include/storage/large_object.h --- base/src/include/storage/large_object.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/storage/large_object.h 2009-03-13 18:14:33.000000000 +0900 @@ -44,6 +44,7 @@ typedef struct LargeObjectDesc #define IFS_RDLOCK (1 << 0) #define IFS_WRLOCK (1 << 1) + Oid secid; /* security id of the largeobject */ } LargeObjectDesc; @@ -79,5 +80,7 @@ extern int inv_tell(LargeObjectDesc *obj extern int inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes); extern int inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes); extern void inv_truncate(LargeObjectDesc *obj_desc, int len); +extern Oid inv_get_security(Oid loid); +extern void inv_set_security(Oid loid, Oid secid); #endif /* LARGE_OBJECT_H */ diff -rpNU3 base/src/include/storage/lwlock.h sepgsql-new/src/include/storage/lwlock.h --- base/src/include/storage/lwlock.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/storage/lwlock.h 2009-03-13 18:14:33.000000000 +0900 @@ -63,6 +63,7 @@ typedef enum LWLockId AutovacuumLock, AutovacuumScheduleLock, SyncScanLock, + SepgsqlAvcLock, /* Individual lock IDs end here */ FirstBufMappingLock, FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS, diff -rpNU3 base/src/include/utils/builtins.h sepgsql-new/src/include/utils/builtins.h --- base/src/include/utils/builtins.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/utils/builtins.h 2009-03-13 13:09:47.000000000 +0900 @@ -617,6 +617,14 @@ extern Datum varchartypmodout(PG_FUNCTIO extern Datum varchar(PG_FUNCTION_ARGS); /* varlena.c */ +extern text *cstring_to_text(const char *s); +extern text *cstring_to_text_with_len(const char *s, int len); +extern char *text_to_cstring(const text *t); +extern void text_to_cstring_buffer(const text *src, char *dst, size_t dst_len); + +#define CStringGetTextDatum(s) PointerGetDatum(cstring_to_text(s)) +#define TextDatumGetCString(d) text_to_cstring((text *) DatumGetPointer(d)) + extern Datum textin(PG_FUNCTION_ARGS); extern Datum textout(PG_FUNCTION_ARGS); extern Datum textrecv(PG_FUNCTION_ARGS); diff -rpNU3 base/src/include/utils/catcache.h sepgsql-new/src/include/utils/catcache.h --- base/src/include/utils/catcache.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/utils/catcache.h 2009-03-13 18:14:33.000000000 +0900 @@ -172,6 +172,7 @@ extern HeapTuple SearchCatCache(CatCache Datum v1, Datum v2, Datum v3, Datum v4); extern void ReleaseCatCache(HeapTuple tuple); +extern void InsertCatCache(CatCache *cache, HeapTuple tuple); extern CatCList *SearchCatCacheList(CatCache *cache, int nkeys, Datum v1, Datum v2, diff -rpNU3 base/src/include/utils/errcodes.h sepgsql-new/src/include/utils/errcodes.h --- base/src/include/utils/errcodes.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/utils/errcodes.h 2009-03-13 18:14:33.000000000 +0900 @@ -339,6 +339,11 @@ #define ERRCODE_NO_DATA_FOUND MAKE_SQLSTATE('P','0', '0','0','2') #define ERRCODE_TOO_MANY_ROWS MAKE_SQLSTATE('P','0', '0','0','3') +/* Class SE - SE-PostgreSQL Error */ +#define ERRCODE_SELINUX_ERROR MAKE_SQLSTATE('S','E', '0','0','1') +#define ERRCODE_SELINUX_AUDIT MAKE_SQLSTATE('S','E', '0','0','2') +#define ERRCODE_SELINUX_INFO MAKE_SQLSTATE('S','E', '0','0','3') + /* Class XX - Internal Error (PostgreSQL-specific error class) */ /* (this is for "can't-happen" conditions and software bugs) */ #define ERRCODE_INTERNAL_ERROR MAKE_SQLSTATE('X','X', '0','0','0') diff -rpNU3 base/src/include/utils/syscache.h sepgsql-new/src/include/utils/syscache.h --- base/src/include/utils/syscache.h 2008-01-07 23:51:33.000000000 +0900 +++ sepgsql-new/src/include/utils/syscache.h 2009-03-13 18:14:33.000000000 +0900 @@ -64,18 +64,20 @@ #define RELNAMENSP 33 #define RELOID 34 #define RULERELNAME 35 -#define STATRELATT 36 -#define TSCONFIGMAP 37 -#define TSCONFIGNAMENSP 38 -#define TSCONFIGOID 39 -#define TSDICTNAMENSP 40 -#define TSDICTOID 41 -#define TSPARSERNAMENSP 42 -#define TSPARSEROID 43 -#define TSTEMPLATENAMENSP 44 -#define TSTEMPLATEOID 45 -#define TYPENAMENSP 46 -#define TYPEOID 47 +#define SECURITYLABEL 36 +#define SECURITYOID 37 +#define STATRELATT 38 +#define TSCONFIGMAP 39 +#define TSCONFIGNAMENSP 40 +#define TSCONFIGOID 41 +#define TSDICTNAMENSP 42 +#define TSDICTOID 43 +#define TSPARSERNAMENSP 44 +#define TSPARSEROID 45 +#define TSTEMPLATENAMENSP 46 +#define TSTEMPLATEOID 47 +#define TYPENAMENSP 48 +#define TYPEOID 49 extern void InitCatalogCache(void); extern void InitCatalogCachePhase2(void); @@ -84,6 +86,8 @@ extern HeapTuple SearchSysCache(int cach Datum key1, Datum key2, Datum key3, Datum key4); extern void ReleaseSysCache(HeapTuple tuple); +extern void InsertSysCache(Oid relid, HeapTuple tuple); + /* convenience routines */ extern HeapTuple SearchSysCacheCopy(int cacheId, Datum key1, Datum key2, Datum key3, Datum key4);