diff -Nrpc base/configure sepgsql/configure *** base/configure Sun Sep 6 19:40:49 2009 --- sepgsql/configure Sun Sep 6 19:53:10 2009 *************** with_libxml *** 710,715 **** --- 710,716 ---- with_libxslt with_system_tzdata with_zlib + enable_selinux GREP EGREP ELF_SYS *************** Optional Features: *** 1378,1383 **** --- 1379,1385 ---- --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-float4-byval disable float4 passed by value --disable-float8-byval disable float8 passed by value --disable-largefile omit support for large files *************** fi *** 5532,5537 **** --- 5534,5645 ---- # + # SELinux support + # + + pgac_args="$pgac_args enable_selinux" + + # Check whether --enable-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 avc_netlink_loop in -lselinux" >&5 + echo $ECHO_N "checking for avc_netlink_loop in -lselinux... $ECHO_C" >&6; } + if test "${ac_cv_lib_selinux_avc_netlink_loop+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 GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ + #ifdef __cplusplus + extern "C" + #endif + char avc_netlink_loop (); + int + main () + { + return avc_netlink_loop (); + ; + return 0; + } + _ACEOF + rm -f conftest.$ac_objext conftest$ac_exeext + if { (ac_try="$ac_link" + case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; + esac + eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_selinux_avc_netlink_loop=yes + else + echo "$as_me: failed program was:" >&5 + sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_selinux_avc_netlink_loop=no + fi + + rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + LIBS=$ac_check_lib_save_LIBS + fi + { echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_avc_netlink_loop" >&5 + echo "${ECHO_T}$ac_cv_lib_selinux_avc_netlink_loop" >&6; } + if test $ac_cv_lib_selinux_avc_netlink_loop = 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 # *************** with_libxml!$with_libxml$ac_delim *** 28137,28147 **** with_libxslt!$with_libxslt$ac_delim with_system_tzdata!$with_system_tzdata$ac_delim with_zlib!$with_zlib$ac_delim GREP!$GREP$ac_delim EGREP!$EGREP$ac_delim ELF_SYS!$ELF_SYS$ac_delim LDFLAGS_SL!$LDFLAGS_SL$ac_delim - LD!$LD$ac_delim _ACEOF if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then --- 28245,28255 ---- with_libxslt!$with_libxslt$ac_delim with_system_tzdata!$with_system_tzdata$ac_delim with_zlib!$with_zlib$ac_delim + enable_selinux!$enable_selinux$ac_delim GREP!$GREP$ac_delim EGREP!$EGREP$ac_delim ELF_SYS!$ELF_SYS$ac_delim LDFLAGS_SL!$LDFLAGS_SL$ac_delim _ACEOF if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then *************** _ACEOF *** 28183,28188 **** --- 28291,28297 ---- ac_delim='%!_!# ' for ac_last_try in false false false false false :; do cat >conf$$subs.sed <<_ACEOF + LD!$LD$ac_delim with_gnu_ld!$with_gnu_ld$ac_delim ld_R_works!$ld_R_works$ac_delim RANLIB!$RANLIB$ac_delim *************** vpath_build!$vpath_build$ac_delim *** 28245,28251 **** LTLIBOBJS!$LTLIBOBJS$ac_delim _ACEOF ! if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 60; then break elif $ac_last_try; then { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 --- 28354,28360 ---- LTLIBOBJS!$LTLIBOBJS$ac_delim _ACEOF ! if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 61; then break elif $ac_last_try; then { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 diff -Nrpc base/configure.in sepgsql/configure.in *** base/configure.in Sun Sep 6 19:40:49 2009 --- sepgsql/configure.in Sun Sep 6 19:53:10 2009 *************** PGAC_ARG_BOOL(with, zlib, yes, *** 764,769 **** --- 764,782 ---- AC_SUBST(with_zlib) # + # SELinux support + # + PGAC_ARG_BOOL(enable, selinux, no, + [enable to build with SELinux support]) + if test "$enable_selinux" = yes; then + AC_CHECK_LIB(selinux, avc_netlink_loop, + 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 -Nrpc base/src/Makefile.global.in sepgsql/src/Makefile.global.in *** base/src/Makefile.global.in Tue Jun 30 01:26:47 2009 --- sepgsql/src/Makefile.global.in Wed Jul 15 19:35:52 2009 *************** enable_rpath = @enable_rpath@ *** 164,169 **** --- 164,170 ---- enable_nls = @enable_nls@ enable_debug = @enable_debug@ enable_dtrace = @enable_dtrace@ + enable_selinux = @enable_selinux@ enable_coverage = @enable_coverage@ enable_thread_safety = @enable_thread_safety@ diff -Nrpc base/src/backend/Makefile sepgsql/src/backend/Makefile *** base/src/backend/Makefile Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/Makefile Wed Jul 15 19:35:52 2009 *************** include $(top_builddir)/src/Makefile.glo *** 16,22 **** SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \ main nodes optimizer port postmaster regex rewrite \ ! storage tcop tsearch utils $(top_builddir)/src/timezone include $(srcdir)/common.mk --- 16,22 ---- SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \ main nodes optimizer port postmaster regex rewrite \ ! security storage tcop tsearch utils $(top_builddir)/src/timezone include $(srcdir)/common.mk *************** LIBS := $(filter-out -lpgport, $(LIBS)) *** 34,39 **** --- 34,44 ---- # 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 -Nrpc base/src/backend/access/common/heaptuple.c sepgsql/src/backend/access/common/heaptuple.c *** base/src/backend/access/common/heaptuple.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/access/common/heaptuple.c Thu Sep 10 15:18:03 2009 *************** *** 60,65 **** --- 60,66 ---- #include "access/heapam.h" #include "access/sysattr.h" #include "access/tuptoaster.h" + #include "catalog/pg_security.h" #include "executor/tuptable.h" *************** heap_attisnull(HeapTuple tup, int attnum *** 287,292 **** --- 288,294 ---- case MinCommandIdAttributeNumber: case MaxTransactionIdAttributeNumber: case MaxCommandIdAttributeNumber: + case SecurityAttributeNumber: /* these are never null */ break; *************** heap_getsysattr(HeapTuple tup, int attnu *** 599,604 **** --- 601,609 ---- case TableOidAttributeNumber: result = ObjectIdGetDatum(tup->t_tableOid); break; + case SecurityAttributeNumber: + result = securitySysattSecLabelOut(tup->t_tableOid, tup); + break; default: elog(ERROR, "invalid attnum: %d", attnum); result = 0; /* keep compiler quiet */ *************** heap_form_tuple(TupleDesc tupleDescripto *** 722,727 **** --- 727,734 ---- if (tupleDescriptor->tdhasoid) len += sizeof(Oid); + if (tupleDescriptor->tdhassecid) + len += sizeof(Oid); hoff = len = MAXALIGN(len); /* align user data safely */ *************** heap_form_tuple(TupleDesc tupleDescripto *** 753,758 **** --- 760,767 ---- if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ td->t_infomask = HEAP_HASOID; + if (tupleDescriptor->tdhassecid) + td->t_infomask |= HEAP_HASSECID; heap_fill_tuple(tupleDescriptor, values, *************** heap_modify_tuple(HeapTuple tuple, *** 864,869 **** --- 873,880 ---- newTuple->t_tableOid = tuple->t_tableOid; if (tupleDesc->tdhasoid) HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); + if (HeapTupleHasSecid(newTuple)) + HeapTupleSetSecid(newTuple, HeapTupleGetSecid(tuple)); return newTuple; } *************** heap_form_minimal_tuple(TupleDesc tupleD *** 1474,1479 **** --- 1485,1492 ---- if (tupleDescriptor->tdhasoid) len += sizeof(Oid); + if (tupleDescriptor->tdhassecid) + len += sizeof(Oid); hoff = len = MAXALIGN(len); /* align user data safely */ *************** heap_form_minimal_tuple(TupleDesc tupleD *** 1495,1500 **** --- 1508,1515 ---- if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ tuple->t_infomask = HEAP_HASOID; + if (tupleDescriptor->tdhassecid) + tuple->t_infomask |= HEAP_HASSECID; heap_fill_tuple(tupleDescriptor, values, diff -Nrpc base/src/backend/access/common/tupdesc.c sepgsql/src/backend/access/common/tupdesc.c *** base/src/backend/access/common/tupdesc.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/access/common/tupdesc.c Wed Sep 9 13:14:37 2009 *************** CreateTemplateTupleDesc(int natts, bool *** 88,93 **** --- 88,94 ---- desc->tdtypeid = RECORDOID; desc->tdtypmod = -1; desc->tdhasoid = hasoid; + desc->tdhassecid = false; desc->tdrefcount = -1; /* assume not reference-counted */ return desc; *************** CreateTupleDesc(int natts, bool hasoid, *** 121,126 **** --- 122,128 ---- desc->tdtypeid = RECORDOID; desc->tdtypmod = -1; desc->tdhasoid = hasoid; + desc->tdhassecid = false; desc->tdrefcount = -1; /* assume not reference-counted */ return desc; *************** CreateTupleDescCopy(TupleDesc tupdesc) *** 150,155 **** --- 152,158 ---- desc->tdtypeid = tupdesc->tdtypeid; desc->tdtypmod = tupdesc->tdtypmod; + desc->tdhassecid = tupdesc->tdhassecid; return desc; } *************** CreateTupleDescCopyConstr(TupleDesc tupd *** 208,213 **** --- 211,217 ---- desc->tdtypeid = tupdesc->tdtypeid; desc->tdtypmod = tupdesc->tdtypmod; + desc->tdhassecid = tupdesc->tdhassecid; return desc; } *************** equalTupleDescs(TupleDesc tupdesc1, Tupl *** 314,319 **** --- 318,325 ---- return false; if (tupdesc1->tdhasoid != tupdesc2->tdhasoid) return false; + if (tupdesc1->tdhassecid != tupdesc2->tdhassecid) + return false; for (i = 0; i < tupdesc1->natts; i++) { diff -Nrpc base/src/backend/access/heap/heapam.c sepgsql/src/backend/access/heap/heapam.c *** base/src/backend/access/heap/heapam.c Sun Sep 6 19:40:49 2009 --- sepgsql/src/backend/access/heap/heapam.c Tue Dec 8 14:53:28 2009 *************** *** 54,59 **** --- 54,60 ---- #include "catalog/namespace.h" #include "miscadmin.h" #include "pgstat.h" + #include "security/sepgsql.h" #include "storage/bufmgr.h" #include "storage/freespace.h" #include "storage/lmgr.h" *************** heap_insert(Relation relation, HeapTuple *** 2016,2021 **** --- 2017,2028 ---- Oid simple_heap_insert(Relation relation, HeapTuple tup) { + /* + * SELinux assigns default security label for the tuple, + * but does not check permissions to the internal operations. + */ + sepgsqlHeapTupleInsert(relation, tup, true); + return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL); } *************** l2: *** 2558,2563 **** --- 2565,2575 ---- Assert(!(newtup->t_data->t_infomask & HEAP_HASOID)); } + /* Preserve SecurityId, if not changed */ + if (HeapTupleHasSecid(newtup) && + !OidIsValid(HeapTupleGetSecid(newtup))) + HeapTupleSetSecid(newtup, HeapTupleGetSecid(&oldtup)); + newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK); newtup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK); newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED); diff -Nrpc base/src/backend/access/heap/tuptoaster.c sepgsql/src/backend/access/heap/tuptoaster.c *** base/src/backend/access/heap/tuptoaster.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/access/heap/tuptoaster.c Tue Sep 8 23:55:48 2009 *************** toast_insert_or_update(Relation rel, Hea *** 591,596 **** --- 591,598 ---- hoff += BITMAPLEN(numAttrs); if (newtup->t_data->t_infomask & HEAP_HASOID) hoff += sizeof(Oid); + if (HeapTupleHasSecid(newtup)) + hoff += sizeof(Oid); hoff = MAXALIGN(hoff); Assert(hoff == newtup->t_data->t_hoff); /* now convert to a limit on the tuple data size */ *************** toast_insert_or_update(Relation rel, Hea *** 864,869 **** --- 866,873 ---- new_len += BITMAPLEN(numAttrs); if (olddata->t_infomask & HEAP_HASOID) new_len += sizeof(Oid); + if (HeapTupleHeaderHasSecid(olddata)) + new_len += sizeof(Oid); new_len = MAXALIGN(new_len); Assert(new_len == olddata->t_hoff); new_data_len = heap_compute_data_size(tupleDesc, *************** toast_flatten_tuple_attribute(Datum valu *** 1015,1020 **** --- 1019,1026 ---- new_len += BITMAPLEN(numAttrs); if (olddata->t_infomask & HEAP_HASOID) new_len += sizeof(Oid); + if (HeapTupleHeaderHasSecid(olddata)) + new_len += sizeof(Oid); new_len = MAXALIGN(new_len); Assert(new_len == olddata->t_hoff); new_data_len = heap_compute_data_size(tupleDesc, *************** toast_save_datum(Relation rel, Datum val *** 1213,1218 **** --- 1219,1230 ---- memcpy(VARDATA(&chunk_data), data_p, chunk_size); toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull); + /* + * NOTE: SE-PostgreSQL does not assign any security label + * for tuples within the TOASTVALUE relation, so we omit + * to put sepgsqlHeapTupleInsert() hook here. + */ + heap_insert(toastrel, toasttup, mycid, options, NULL); /* diff -Nrpc base/src/backend/access/transam/xact.c sepgsql/src/backend/access/transam/xact.c *** base/src/backend/access/transam/xact.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/access/transam/xact.c Sun Sep 6 19:31:39 2009 *************** *** 36,41 **** --- 36,43 ---- #include "libpq/be-fsstubs.h" #include "miscadmin.h" #include "pgstat.h" + #include "security/rowlevel.h" + #include "security/sepgsql.h" #include "storage/bufmgr.h" #include "storage/fd.h" #include "storage/lmgr.h" *************** typedef struct TransactionStateData *** 140,145 **** --- 142,148 ---- 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; *************** static TransactionStateData TopTransacti *** 168,173 **** --- 171,177 ---- 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 */ }; *************** StartTransaction(void) *** 1524,1529 **** --- 1528,1534 ---- 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); *************** AbortTransaction(void) *** 2030,2035 **** --- 2035,2050 ---- SetUserIdAndContext(s->prevUser, s->prevSecDefCxt); /* + * Reset behavior of row-level access controls + */ + rowlvSetPerformingMode(s->prevRowlv); + + /* + * Reset access vector cache on error + */ + sepgsqlAvcReset(); + + /* * do abort processing */ AfterTriggerEndXact(false); *************** AbortSubTransaction(void) *** 3873,3878 **** --- 3888,3903 ---- SetUserIdAndContext(s->prevUser, s->prevSecDefCxt); /* + * Reset behavior of row-level access controls + */ + rowlvSetPerformingMode(s->prevRowlv); + + /* + * Reset access vector cache on error + */ + sepgsqlAvcReset(); + + /* * We can skip all this stuff if the subxact failed before creating a * ResourceOwner... */ *************** PushTransaction(void) *** 4014,4019 **** --- 4039,4045 ---- s->blockState = TBLOCK_SUBBEGIN; GetUserIdAndContext(&s->prevUser, &s->prevSecDefCxt); s->prevXactReadOnly = XactReadOnly; + s->prevRowlv = rowlvGetPerformingMode(); CurrentTransactionState = s; diff -Nrpc base/src/backend/bootstrap/bootparse.y sepgsql/src/backend/bootstrap/bootparse.y *** base/src/backend/bootstrap/bootparse.y Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/bootstrap/bootparse.y Thu Oct 8 09:29:32 2009 *************** *** 42,47 **** --- 42,48 ---- #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" *************** Boot_CreateStmt: *** 211,216 **** --- 212,224 ---- else { Oid id; + Oid *secLabels = + sepgsql_relation_create(LexIDStr($5), + RELKIND_RELATION, + tupdesc, + PG_CATALOG_NAMESPACE, + NULL, NIL, + false, false); id = heap_create_with_catalog(LexIDStr($5), PG_CATALOG_NAMESPACE, *************** Boot_CreateStmt: *** 225,231 **** 0, ONCOMMIT_NOOP, (Datum) 0, ! true); elog(DEBUG4, "relation created with oid %u", id); } do_end(); --- 233,240 ---- 0, ONCOMMIT_NOOP, (Datum) 0, ! true, ! secLabels); elog(DEBUG4, "relation created with oid %u", id); } do_end(); diff -Nrpc base/src/backend/bootstrap/bootstrap.c sepgsql/src/backend/bootstrap/bootstrap.c *** base/src/backend/bootstrap/bootstrap.c Fri Feb 20 22:15:36 2009 --- sepgsql/src/backend/bootstrap/bootstrap.c Tue Dec 8 14:04:25 2009 *************** *** 26,37 **** --- 26,39 ---- #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" #include "nodes/makefuncs.h" #include "postmaster/bgwriter.h" #include "postmaster/walwriter.h" + #include "security/sepgsql.h" #include "storage/bufmgr.h" #include "storage/ipc.h" #include "storage/proc.h" *************** AuxiliaryProcessMain(int argc, char *arg *** 338,343 **** --- 340,350 ---- case WalWriterProcess: statmsg = "wal writer process"; break; + #ifdef HAVE_SELINUX + case SelinuxReceiverProcess: + statmsg = "selinux netlink receiver"; + break; + #endif default: statmsg = "??? process"; break; *************** AuxiliaryProcessMain(int argc, char *arg *** 430,435 **** --- 437,448 ---- WalWriterMain(); proc_exit(1); /* should never return */ + #ifdef HAVE_SELINUX + case SelinuxReceiverProcess: + sepgsqlReceiverMain(); + proc_exit(1); /* should nener return */ + #endif + default: elog(PANIC, "unrecognized process type: %d", auxType); proc_exit(1); *************** BootstrapModeMain(void) *** 497,502 **** --- 510,520 ---- */ 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); *************** InsertOneTuple(Oid objectid) *** 794,799 **** --- 812,819 ---- tupDesc = CreateTupleDesc(numattr, RelationGetForm(boot_reldesc)->relhasoids, attrtypes); + tupDesc->tdhassecid = RelationGetDescr(boot_reldesc)->tdhassecid; + tuple = heap_form_tuple(tupDesc, values, Nulls); if (objectid != (Oid) 0) HeapTupleSetOid(tuple, objectid); diff -Nrpc base/src/backend/catalog/Makefile sepgsql/src/backend/catalog/Makefile *** base/src/backend/catalog/Makefile Wed May 13 11:30:07 2009 --- sepgsql/src/backend/catalog/Makefile Wed Jul 15 19:30:50 2009 *************** include $(top_builddir)/src/Makefile.glo *** 13,19 **** 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_inherits.o pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o \ ! pg_shdepend.o pg_type.o storage.o toasting.o BKIFILES = postgres.bki postgres.description postgres.shdescription --- 13,19 ---- 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_inherits.o pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o \ ! pg_security.o pg_shdepend.o pg_type.o storage.o toasting.o BKIFILES = postgres.bki postgres.description postgres.shdescription *************** POSTGRES_BKI_SRCS = $(addprefix $(top_sr *** 34,40 **** 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_ts_parser.h pg_ts_template.h \ pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ toasting.h indexing.h \ --- 34,40 ---- pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ pg_database.h pg_tablespace.h pg_pltemplate.h \ pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ ! pg_security.h pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ pg_ts_parser.h pg_ts_template.h \ pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ toasting.h indexing.h \ diff -Nrpc base/src/backend/catalog/aclchk.c sepgsql/src/backend/catalog/aclchk.c *** base/src/backend/catalog/aclchk.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/catalog/aclchk.c Fri Sep 18 14:51:00 2009 *************** *** 35,40 **** --- 35,41 ---- #include "catalog/pg_operator.h" #include "catalog/pg_opfamily.h" #include "catalog/pg_proc.h" + #include "catalog/pg_security.h" #include "catalog/pg_tablespace.h" #include "catalog/pg_type.h" #include "catalog/pg_ts_config.h" *************** *** 43,48 **** --- 44,50 ---- #include "foreign/foreign.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" *************** expand_all_col_privileges(Oid table_oid, *** 672,677 **** --- 674,685 ---- if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids) continue; + /* Skip OID column, if it doesn't exist */ + if (curr_att == SecurityAttributeNumber && + (classForm->relkind != RELKIND_RELATION || + table_oid == SecurityRelationId)) + continue; + /* Views don't have any system columns at all */ if (classForm->relkind == RELKIND_VIEW && curr_att < 0) continue; *************** ExecGrant_Attribute(InternalGrant *istmt *** 774,779 **** --- 782,789 ---- relOid, grantorId, ACL_KIND_COLUMN, relname, attnum, NameStr(pg_attribute_tuple->attname)); + /* SELinux checks */ + sepgsql_attribute_grant(relOid, attnum); /* * Generate new ACL. *************** ExecGrant_Relation(InternalGrant *istmt) *** 1029,1034 **** --- 1039,1046 ---- ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS, NameStr(pg_class_tuple->relname), 0, NULL); + /* SELinux checks */ + sepgsql_relation_grant(relOid); /* * Generate new ACL. *************** ExecGrant_Database(InternalGrant *istmt) *** 1217,1222 **** --- 1229,1236 ---- datId, grantorId, ACL_KIND_DATABASE, NameStr(pg_database_tuple->datname), 0, NULL); + /* SELinux permission checks */ + sepgsql_database_grant(datId); /* * Generate new ACL. *************** ExecGrant_Fdw(InternalGrant *istmt) *** 1335,1340 **** --- 1349,1356 ---- fdwid, grantorId, ACL_KIND_FDW, NameStr(pg_fdw_tuple->fdwname), 0, NULL); + /* SELinux permission checks */ + sepgsql_fdw_grant(fdwid); /* * Generate new ACL. *************** ExecGrant_ForeignServer(InternalGrant *i *** 1454,1459 **** --- 1470,1477 ---- srvid, grantorId, ACL_KIND_FOREIGN_SERVER, NameStr(pg_server_tuple->srvname), 0, NULL); + /* SELinux checks */ + sepgsql_foreign_server_grant(srvid); /* * Generate new ACL. *************** ExecGrant_Function(InternalGrant *istmt) *** 1572,1577 **** --- 1590,1597 ---- funcId, grantorId, ACL_KIND_PROC, NameStr(pg_proc_tuple->proname), 0, NULL); + /* SELinux: db_procedure:{setattr} */ + sepgsql_proc_grant(funcId); /* * Generate new ACL. *************** ExecGrant_Language(InternalGrant *istmt) *** 1696,1701 **** --- 1716,1723 ---- langId, grantorId, ACL_KIND_LANGUAGE, NameStr(pg_language_tuple->lanname), 0, NULL); + /* SELinux checks */ + sepgsql_language_grant(langId); /* * Generate new ACL. *************** ExecGrant_Namespace(InternalGrant *istmt *** 1815,1820 **** --- 1837,1845 ---- NameStr(pg_namespace_tuple->nspname), 0, NULL); + /* SELinux: db_schema:{setattr} */ + sepgsql_schema_grant(nspid); + /* * Generate new ACL. * diff -Nrpc base/src/backend/catalog/catalog.c sepgsql/src/backend/catalog/catalog.c *** base/src/backend/catalog/catalog.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/catalog/catalog.c Wed Jul 15 19:30:50 2009 *************** *** 31,36 **** --- 31,37 ---- #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" *************** IsSharedRelation(Oid relationId) *** 304,309 **** --- 305,311 ---- relationId == AuthMemRelationId || relationId == DatabaseRelationId || relationId == PLTemplateRelationId || + relationId == SecurityRelationId || relationId == SharedDescriptionRelationId || relationId == SharedDependRelationId || relationId == TableSpaceRelationId) *************** IsSharedRelation(Oid relationId) *** 316,321 **** --- 318,325 ---- relationId == DatabaseNameIndexId || relationId == DatabaseOidIndexId || relationId == PLTemplateNameIndexId || + relationId == SecuritySecidIndexId || + relationId == SecuritySecattrIndexId || relationId == SharedDescriptionObjIndexId || relationId == SharedDependDependerIndexId || relationId == SharedDependReferenceIndexId || *************** IsSharedRelation(Oid relationId) *** 327,332 **** --- 331,338 ---- relationId == PgAuthidToastIndex || relationId == PgDatabaseToastTable || relationId == PgDatabaseToastIndex || + relationId == PgSecurityToastTable || + relationId == PgSecurityToastIndex || relationId == PgShdescriptionToastTable || relationId == PgShdescriptionToastIndex) return true; diff -Nrpc base/src/backend/catalog/dependency.c sepgsql/src/backend/catalog/dependency.c *** base/src/backend/catalog/dependency.c Sun Sep 6 19:40:49 2009 --- sepgsql/src/backend/catalog/dependency.c Thu Sep 17 23:44:07 2009 *************** *** 63,68 **** --- 63,69 ---- #include "nodes/nodeFuncs.h" #include "parser/parsetree.h" #include "rewrite/rewriteRemove.h" + #include "security/sepgsql.h" #include "storage/lmgr.h" #include "utils/builtins.h" #include "utils/fmgroids.h" *************** static void reportDependentObjects(const *** 160,166 **** DropBehavior behavior, int msglevel, const ObjectAddress *origObject); ! static void deleteOneObject(const ObjectAddress *object, Relation depRel); static void doDeletion(const ObjectAddress *object); static void AcquireDeletionLock(const ObjectAddress *object); static void ReleaseDeletionLock(const ObjectAddress *object); --- 161,168 ---- DropBehavior behavior, int msglevel, const ObjectAddress *origObject); ! static void deleteOneObject(const ObjectAddress *object, ! Relation depRel, bool permission); static void doDeletion(const ObjectAddress *object); static void AcquireDeletionLock(const ObjectAddress *object); static void ReleaseDeletionLock(const ObjectAddress *object); *************** static void getOpFamilyDescription(Strin *** 192,200 **** * are variants on the same theme; if you change anything here you'll likely * need to fix them too. */ ! void ! performDeletion(const ObjectAddress *object, ! DropBehavior behavior) { Relation depRel; ObjectAddresses *targetObjects; --- 194,202 ---- * are variants on the same theme; if you change anything here you'll likely * need to fix them too. */ ! static void ! performDeletionInternal(const ObjectAddress *object, ! DropBehavior behavior, bool permission) { Relation depRel; ObjectAddresses *targetObjects; *************** performDeletion(const ObjectAddress *obj *** 240,246 **** { ObjectAddress *thisobj = targetObjects->refs + i; ! deleteOneObject(thisobj, depRel); } /* And clean up */ --- 242,248 ---- { ObjectAddress *thisobj = targetObjects->refs + i; ! deleteOneObject(thisobj, depRel, permission); } /* And clean up */ *************** performDeletion(const ObjectAddress *obj *** 249,254 **** --- 251,268 ---- heap_close(depRel, RowExclusiveLock); } + void + performDeletion(const ObjectAddress *object, DropBehavior behavior) + { + performDeletionInternal(object, behavior, true); + } + + void + performDeletionNoPerms(const ObjectAddress *object, DropBehavior behavior) + { + performDeletionInternal(object, behavior, false); + } + /* * performMultipleDeletions: Similar to performDeletion, but act on multiple * objects at once. *************** performMultipleDeletions(const ObjectAdd *** 322,328 **** { ObjectAddress *thisobj = targetObjects->refs + i; ! deleteOneObject(thisobj, depRel); } /* And clean up */ --- 336,343 ---- { ObjectAddress *thisobj = targetObjects->refs + i; ! /* currently, all the caller path need permission checks */ ! deleteOneObject(thisobj, depRel, true); } /* And clean up */ *************** deleteWhatDependsOn(const ObjectAddress *** 393,399 **** if (thisextra->flags & DEPFLAG_ORIGINAL) continue; ! deleteOneObject(thisobj, depRel); } /* And clean up */ --- 408,414 ---- if (thisextra->flags & DEPFLAG_ORIGINAL) continue; ! deleteOneObject(thisobj, depRel, false); } /* And clean up */ *************** reportDependentObjects(const ObjectAddre *** 942,954 **** * depRel is the already-open pg_depend relation. */ static void ! deleteOneObject(const ObjectAddress *object, Relation depRel) { ScanKeyData key[3]; int nkeys; SysScanDesc scan; HeapTuple tup; /* * First remove any pg_depend records that link from this object to * others. (Any records linking to this object should be gone already.) --- 957,973 ---- * depRel is the already-open pg_depend relation. */ static void ! deleteOneObject(const ObjectAddress *object, Relation depRel, bool permission) { ScanKeyData key[3]; int nkeys; SysScanDesc scan; HeapTuple tup; + /* SELinux checks db_xxx:{drop}, if necessary */ + if (permission) + sepgsql_sysobj_drop(object); + /* * First remove any pg_depend records that link from this object to * others. (Any records linking to this object should be gone already.) diff -Nrpc base/src/backend/catalog/heap.c sepgsql/src/backend/catalog/heap.c *** base/src/backend/catalog/heap.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/catalog/heap.c Wed Sep 9 16:47:01 2009 *************** *** 43,48 **** --- 43,49 ---- #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" *************** *** 56,61 **** --- 57,63 ---- #include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "parser/parse_relation.h" + #include "security/sepgsql.h" #include "storage/bufmgr.h" #include "storage/freespace.h" #include "storage/smgr.h" *************** static void AddNewRelationTuple(Relation *** 74,80 **** Oid new_rel_oid, Oid new_type_oid, Oid relowner, char relkind, ! Datum reloptions); static Oid AddNewRelationType(const char *typeName, Oid typeNamespace, Oid new_rel_oid, --- 76,83 ---- Oid new_rel_oid, Oid new_type_oid, Oid relowner, char relkind, ! Datum reloptions, ! Oid *secLabels); static Oid AddNewRelationType(const char *typeName, Oid typeNamespace, Oid new_rel_oid, *************** static FormData_pg_attribute a7 = { *** 158,164 **** true, 'p', 'i', true, false, false, true, 0, {0} }; ! static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; /* * This function returns a Form_pg_attribute pointer for a system attribute. --- 161,176 ---- true, 'p', 'i', true, false, false, true, 0, {0} }; ! /* ! * System columns for enhanced security features ! */ ! static FormData_pg_attribute a8 = { ! 0, {SecurityAttributeName}, TEXTOID, 0, -1, ! SecurityAttributeNumber, 0, -1, -1, ! false, 'x', 'i', true, false, false, true, 0, {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. *************** SystemAttributeByName(const char *attnam *** 198,203 **** --- 210,226 ---- return NULL; } + /* + * If the given attribute number is writable, returns true. + */ + bool + SystemAttributeIsWritable(AttrNumber attnum) + { + if (attnum == SecurityAttributeNumber) + return true; + + return false; + } /* ---------------------------------------------------------------- * XXX END OF UGLY HARD CODED BADNESS XXX *************** heap_create(const char *relname, *** 293,298 **** --- 316,326 ---- relid, reltablespace, shared_relation); + /* + * Does the relation have security attribute? + */ + RelationGetDescr(rel)->tdhassecid + = securityTupleDescHasSecid(relid, relkind); /* * Have the storage manager create the relation's disk file, if needed. *************** CheckAttributeType(const char *attname, *** 487,493 **** void InsertPgAttributeTuple(Relation pg_attribute_rel, Form_pg_attribute new_attribute, ! CatalogIndexState indstate) { Datum values[Natts_pg_attribute]; bool nulls[Natts_pg_attribute]; --- 515,522 ---- void InsertPgAttributeTuple(Relation pg_attribute_rel, Form_pg_attribute new_attribute, ! CatalogIndexState indstate, ! Oid new_att_secid) { Datum values[Natts_pg_attribute]; bool nulls[Natts_pg_attribute]; *************** InsertPgAttributeTuple(Relation pg_attri *** 520,525 **** --- 549,557 ---- tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, new_att_secid); + /* finally insert the new tuple, update the indexes, and clean up */ simple_heap_insert(pg_attribute_rel, tup); *************** AddNewAttributeTuples(Oid new_rel_oid, *** 543,555 **** TupleDesc tupdesc, char relkind, bool oidislocal, ! int oidinhcount) { Form_pg_attribute attr; int i; Relation rel; CatalogIndexState indstate; int natts = tupdesc->natts; ObjectAddress myself, referenced; --- 575,589 ---- TupleDesc tupdesc, char relkind, bool oidislocal, ! int oidinhcount, ! Oid *secLabels) { Form_pg_attribute attr; int i; Relation rel; CatalogIndexState indstate; int natts = tupdesc->natts; + Oid new_att_secid; ObjectAddress myself, referenced; *************** AddNewAttributeTuples(Oid new_rel_oid, *** 573,579 **** attr->attstattarget = -1; attr->attcacheoff = -1; ! InsertPgAttributeTuple(rel, attr, indstate); /* Add dependency info */ myself.classId = RelationRelationId; --- 607,617 ---- attr->attstattarget = -1; attr->attcacheoff = -1; ! /* Security label of the column */ ! new_att_secid = (!secLabels ? InvalidOid ! : secLabels[i - FirstLowInvalidHeapAttributeNumber]); ! ! InsertPgAttributeTuple(rel, attr, indstate, new_att_secid); /* Add dependency info */ myself.classId = RelationRelationId; *************** AddNewAttributeTuples(Oid new_rel_oid, *** 601,606 **** --- 639,650 ---- SysAtt[i]->attnum == ObjectIdAttributeNumber) continue; + /* skip Secid where appropriate */ + if (SysAtt[i]->attnum == SecurityAttributeNumber && + (relkind != RELKIND_RELATION || + new_rel_oid == SecurityRelationId)) + continue; + memcpy(&attStruct, (char *) SysAtt[i], sizeof(FormData_pg_attribute)); /* Fill in the correct relation OID in the copied tuple */ *************** AddNewAttributeTuples(Oid new_rel_oid, *** 613,619 **** attStruct.attinhcount = oidinhcount; } ! InsertPgAttributeTuple(rel, &attStruct, indstate); } } --- 657,667 ---- attStruct.attinhcount = oidinhcount; } ! /* Security label of the system column */ ! new_att_secid = (!secLabels ? InvalidOid ! : secLabels[SysAtt[i]->attnum - FirstLowInvalidHeapAttributeNumber]); ! ! InsertPgAttributeTuple(rel, &attStruct, indstate, new_att_secid); } } *************** void *** 641,647 **** InsertPgClassTuple(Relation pg_class_desc, Relation new_rel_desc, Oid new_rel_oid, ! Datum reloptions) { Form_pg_class rd_rel = new_rel_desc->rd_rel; Datum values[Natts_pg_class]; --- 689,696 ---- InsertPgClassTuple(Relation pg_class_desc, Relation new_rel_desc, Oid new_rel_oid, ! Datum reloptions, ! Oid new_rel_secid) { Form_pg_class rd_rel = new_rel_desc->rd_rel; Datum values[Natts_pg_class]; *************** InsertPgClassTuple(Relation pg_class_des *** 690,695 **** --- 739,747 ---- */ HeapTupleSetOid(tup, new_rel_oid); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, new_rel_secid); + /* finally insert the new tuple, update the indexes, and clean up */ simple_heap_insert(pg_class_desc, tup); *************** AddNewRelationTuple(Relation pg_class_de *** 712,720 **** Oid new_type_oid, Oid relowner, char relkind, ! Datum reloptions) { Form_pg_class new_rel_reltup; /* * first we update some of the information in our uncataloged relation's --- 764,774 ---- Oid new_type_oid, Oid relowner, char relkind, ! Datum reloptions, ! Oid *secLabels) { Form_pg_class new_rel_reltup; + Oid new_rel_secid = InvalidOid; /* * first we update some of the information in our uncataloged relation's *************** AddNewRelationTuple(Relation pg_class_de *** 771,778 **** new_rel_desc->rd_att->tdtypeid = new_type_oid; /* Now build and insert the tuple */ ! InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions); } --- 825,836 ---- new_rel_desc->rd_att->tdtypeid = new_type_oid; + if (secLabels) + new_rel_secid = secLabels[0]; + /* Now build and insert the tuple */ ! InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, ! reloptions, new_rel_secid); } *************** heap_create_with_catalog(const char *rel *** 843,849 **** int oidinhcount, OnCommitAction oncommit, Datum reloptions, ! bool allow_system_table_mods) { Relation pg_class_desc; Relation new_rel_desc; --- 901,908 ---- int oidinhcount, OnCommitAction oncommit, Datum reloptions, ! bool allow_system_table_mods, ! Oid *secLabels) { Relation pg_class_desc; Relation new_rel_desc; *************** heap_create_with_catalog(const char *rel *** 1019,1031 **** new_type_oid, ownerid, relkind, ! reloptions); /* * now add tuples to pg_attribute for the attributes in our new relation. */ AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind, ! oidislocal, oidinhcount); /* * Make a dependency link to force the relation to be deleted if its --- 1078,1091 ---- new_type_oid, ownerid, relkind, ! reloptions, ! secLabels); /* * now add tuples to pg_attribute for the attributes in our new relation. */ AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind, ! oidislocal, oidinhcount, secLabels); /* * Make a dependency link to force the relation to be deleted if its *************** heap_drop_with_catalog(Oid relid) *** 1484,1489 **** --- 1544,1554 ---- * delete relation tuple */ DeleteRelationTuple(relid); + + /* + * delete orphan pg_security entries + */ + securityReclaimOnDropTable(relid); } diff -Nrpc base/src/backend/catalog/index.c sepgsql/src/backend/catalog/index.c *** base/src/backend/catalog/index.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/catalog/index.c Fri Sep 18 17:02:48 2009 *************** *** 48,53 **** --- 48,54 ---- #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/var.h" + #include "security/sepgsql.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/procarray.h" *************** AppendAttributeTuples(Relation indexRela *** 351,357 **** Assert(indexTupDesc->attrs[i]->attnum == i + 1); Assert(indexTupDesc->attrs[i]->attcacheoff == -1); ! InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], indstate); } CatalogCloseIndexes(indstate); --- 352,359 ---- Assert(indexTupDesc->attrs[i]->attnum == i + 1); Assert(indexTupDesc->attrs[i]->attcacheoff == -1); ! InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], ! indstate, InvalidOid); } CatalogCloseIndexes(indstate); *************** index_create(Oid heapRelationId, *** 652,658 **** */ InsertPgClassTuple(pg_class, indexRelation, RelationGetRelid(indexRelation), ! reloptions); /* done with pg_class */ heap_close(pg_class, RowExclusiveLock); --- 654,660 ---- */ InsertPgClassTuple(pg_class, indexRelation, RelationGetRelid(indexRelation), ! reloptions, InvalidOid); /* done with pg_class */ heap_close(pg_class, RowExclusiveLock); diff -Nrpc base/src/backend/catalog/namespace.c sepgsql/src/backend/catalog/namespace.c *** base/src/backend/catalog/namespace.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/catalog/namespace.c Thu Sep 17 17:04:16 2009 *************** *** 39,44 **** --- 39,45 ---- #include "miscadmin.h" #include "nodes/makefuncs.h" #include "parser/parse_func.h" + #include "security/sepgsql.h" #include "storage/backendid.h" #include "storage/ipc.h" #include "utils/acl.h" *************** LookupExplicitNamespace(const char *nspn *** 2105,2111 **** --- 2106,2115 ---- if (strcmp(nspname, "pg_temp") == 0) { if (OidIsValid(myTempNamespace)) + { + sepgsql_schema_search(myTempNamespace, true); return myTempNamespace; + } /* * Since this is used only for looking up existing objects, there is *************** LookupExplicitNamespace(const char *nspn *** 2127,2132 **** --- 2131,2137 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, nspname); + sepgsql_schema_search(namespaceId, true); return namespaceId; } *************** recomputeNamespacePath(void) *** 2722,2728 **** if (OidIsValid(namespaceId) && !list_member_oid(oidlist, namespaceId) && pg_namespace_aclcheck(namespaceId, roleid, ! ACL_USAGE) == ACLCHECK_OK) oidlist = lappend_oid(oidlist, namespaceId); } } --- 2727,2734 ---- if (OidIsValid(namespaceId) && !list_member_oid(oidlist, namespaceId) && pg_namespace_aclcheck(namespaceId, roleid, ! ACL_USAGE) == ACLCHECK_OK && ! sepgsql_schema_search(namespaceId, false)) oidlist = lappend_oid(oidlist, namespaceId); } } *************** recomputeNamespacePath(void) *** 2731,2737 **** /* pg_temp --- substitute temp namespace, if any */ if (OidIsValid(myTempNamespace)) { ! if (!list_member_oid(oidlist, myTempNamespace)) oidlist = lappend_oid(oidlist, myTempNamespace); } else --- 2737,2744 ---- /* pg_temp --- substitute temp namespace, if any */ if (OidIsValid(myTempNamespace)) { ! if (!list_member_oid(oidlist, myTempNamespace) && ! sepgsql_schema_search(myTempNamespace, false)) oidlist = lappend_oid(oidlist, myTempNamespace); } else *************** recomputeNamespacePath(void) *** 2750,2756 **** if (OidIsValid(namespaceId) && !list_member_oid(oidlist, namespaceId) && pg_namespace_aclcheck(namespaceId, roleid, ! ACL_USAGE) == ACLCHECK_OK) oidlist = lappend_oid(oidlist, namespaceId); } } --- 2757,2764 ---- if (OidIsValid(namespaceId) && !list_member_oid(oidlist, namespaceId) && pg_namespace_aclcheck(namespaceId, roleid, ! ACL_USAGE) == ACLCHECK_OK && ! sepgsql_schema_search(namespaceId, false)) oidlist = lappend_oid(oidlist, namespaceId); } } *************** InitTempTableNamespace(void) *** 2816,2821 **** --- 2824,2830 ---- char namespaceName[NAMEDATALEN]; Oid namespaceId; Oid toastspaceId; + Oid nspsecid; Assert(!OidIsValid(myTempNamespace)); *************** InitTempTableNamespace(void) *** 2836,2841 **** --- 2845,2853 ---- errmsg("permission denied to create temporary tables in database \"%s\"", get_database_name(MyDatabaseId)))); + /* SELinux checks permission to create temp schema */ + nspsecid = sepgsql_schema_create(namespaceName, true, NULL); + snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId); namespaceId = GetSysCacheOid(NAMESPACENAME, *************** InitTempTableNamespace(void) *** 2851,2857 **** * 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); /* Advance command counter to make namespace visible */ CommandCounterIncrement(); } --- 2863,2871 ---- * 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, ! nspsecid); /* Advance command counter to make namespace visible */ CommandCounterIncrement(); } *************** InitTempTableNamespace(void) *** 2877,2883 **** 0, 0, 0); if (!OidIsValid(toastspaceId)) { ! toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); /* Advance command counter to make namespace visible */ CommandCounterIncrement(); } --- 2891,2899 ---- 0, 0, 0); if (!OidIsValid(toastspaceId)) { ! toastspaceId = NamespaceCreate(namespaceName, ! BOOTSTRAP_SUPERUSERID, ! nspsecid); /* Advance command counter to make namespace visible */ CommandCounterIncrement(); } *************** RemoveTempRelations(Oid tempNamespaceId) *** 3030,3035 **** --- 3046,3058 ---- object.objectId = tempNamespaceId; object.objectSubId = 0; + /* + * TODO: + * SELinux should not check db_xxx:{drop} permission during cleaning + * up all the temporary objects. It may be necessary a bool argument + * to control MAC permission check on deleteOneObject() called from + * deleteWhatDependsOn() and so on. + */ deleteWhatDependsOn(&object, false); } diff -Nrpc base/src/backend/catalog/pg_aggregate.c sepgsql/src/backend/catalog/pg_aggregate.c *** base/src/backend/catalog/pg_aggregate.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/catalog/pg_aggregate.c Wed Jul 15 19:37:35 2009 *************** AggregateCreate(const char *aggName, *** 231,237 **** NIL, /* parameterDefaults */ PointerGetDatum(NULL), /* proconfig */ 1, /* procost */ ! 0); /* prorows */ /* * Okay to create the pg_aggregate entry. --- 231,238 ---- NIL, /* parameterDefaults */ PointerGetDatum(NULL), /* proconfig */ 1, /* procost */ ! 0, /* prorows */ ! NULL); /* proseclabel*/ /* * Okay to create the pg_aggregate entry. diff -Nrpc base/src/backend/catalog/pg_conversion.c sepgsql/src/backend/catalog/pg_conversion.c *** base/src/backend/catalog/pg_conversion.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/catalog/pg_conversion.c Thu Sep 17 22:10:19 2009 *************** Oid *** 40,46 **** ConversionCreate(const char *conname, Oid connamespace, Oid conowner, int32 conforencoding, int32 contoencoding, ! Oid conproc, bool def) { int i; Relation rel; --- 40,46 ---- ConversionCreate(const char *conname, Oid connamespace, Oid conowner, int32 conforencoding, int32 contoencoding, ! Oid conproc, Oid consecid, bool def) { int i; Relation rel; *************** ConversionCreate(const char *conname, Oi *** 104,109 **** --- 104,111 ---- values[Anum_pg_conversion_condefault - 1] = BoolGetDatum(def); tup = heap_form_tuple(tupDesc, values, nulls); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, consecid); /* insert a new tuple */ oid = simple_heap_insert(rel, tup); diff -Nrpc base/src/backend/catalog/pg_largeobject.c sepgsql/src/backend/catalog/pg_largeobject.c *** base/src/backend/catalog/pg_largeobject.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/catalog/pg_largeobject.c Wed Jul 15 19:48:58 2009 *************** *** 18,23 **** --- 18,24 ---- #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" #include "utils/rel.h" *************** LargeObjectCreate(Oid loid) *** 59,64 **** --- 60,68 ---- ntup = heap_form_tuple(pg_largeobject->rd_att, values, nulls); + /* SELinux checks db_blob:{create} */ + sepgsqlCheckBlobCreate(pg_largeobject, ntup); + /* * Insert it */ *************** LargeObjectDrop(Oid loid) *** 93,98 **** --- 97,105 ---- 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 -Nrpc base/src/backend/catalog/pg_namespace.c sepgsql/src/backend/catalog/pg_namespace.c *** base/src/backend/catalog/pg_namespace.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/catalog/pg_namespace.c Tue Sep 8 23:55:48 2009 *************** *** 28,34 **** * --------------- */ Oid ! NamespaceCreate(const char *nspName, Oid ownerId) { Relation nspdesc; HeapTuple tup; --- 28,34 ---- * --------------- */ Oid ! NamespaceCreate(const char *nspName, Oid ownerId, Oid nspsecid) { Relation nspdesc; HeapTuple tup; *************** NamespaceCreate(const char *nspName, Oid *** 66,71 **** --- 66,73 ---- tupDesc = nspdesc->rd_att; tup = heap_form_tuple(tupDesc, values, nulls); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, nspsecid); nspoid = simple_heap_insert(nspdesc, tup); Assert(OidIsValid(nspoid)); diff -Nrpc base/src/backend/catalog/pg_operator.c sepgsql/src/backend/catalog/pg_operator.c *** base/src/backend/catalog/pg_operator.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/catalog/pg_operator.c Thu Sep 17 22:10:19 2009 *************** *** 28,33 **** --- 28,34 ---- #include "catalog/pg_type.h" #include "miscadmin.h" #include "parser/parse_oper.h" + #include "security/sepgsql.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" *************** OperatorShellMake(const char *operatorNa *** 204,209 **** --- 205,211 ---- { Relation pg_operator_desc; Oid operatorObjectId; + Oid secid; int i; HeapTuple tup; Datum values[Natts_pg_operator]; *************** OperatorShellMake(const char *operatorNa *** 220,225 **** --- 222,231 ---- errmsg("\"%s\" is not a valid operator name", operatorName))); + /* SELinux permission check */ + secid = sepgsql_operator_create(operatorName, InvalidOid, + operatorNamespace, + InvalidOid, InvalidOid, InvalidOid); /* * initialize our *nulls and *values arrays */ *************** OperatorShellMake(const char *operatorNa *** 260,265 **** --- 266,273 ---- * create a new operator tuple */ tup = heap_form_tuple(tupDesc, values, nulls); + if (HeapTupleHasSecid(tup) && OidIsValid(secid)) + HeapTupleSetSecid(tup, secid); /* * insert our "shell" operator tuple *************** OperatorCreate(const char *operatorName, *** 347,352 **** --- 355,361 ---- bool selfCommutator = false; NameData oname; TupleDesc tupDesc; + Oid secid; int i; /* *************** OperatorCreate(const char *operatorName, *** 476,481 **** --- 485,494 ---- else negatorId = InvalidOid; + /* SELinux permission checks */ + secid = sepgsql_operator_create(operatorName, operatorObjectId, + operatorNamespace, + procedureId, restrictionId, joinId); /* * set up values in the operator tuple */ *************** OperatorCreate(const char *operatorName, *** 523,528 **** --- 536,543 ---- values, nulls, replaces); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, secid); simple_heap_update(pg_operator_desc, &tup->t_self, tup); } *************** OperatorCreate(const char *operatorName, *** 530,535 **** --- 545,552 ---- { tupDesc = pg_operator_desc->rd_att; tup = heap_form_tuple(tupDesc, values, nulls); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, secid); operatorObjectId = simple_heap_insert(pg_operator_desc, tup); } diff -Nrpc base/src/backend/catalog/pg_proc.c sepgsql/src/backend/catalog/pg_proc.c *** base/src/backend/catalog/pg_proc.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/catalog/pg_proc.c Fri Sep 18 17:39:46 2009 *************** *** 29,34 **** --- 29,35 ---- #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parse_type.h" + #include "security/sepgsql.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" #include "utils/acl.h" *************** ProcedureCreate(const char *procedureNam *** 78,84 **** List *parameterDefaults, Datum proconfig, float4 procost, ! float4 prorows) { Oid retval; int parameterCount; --- 79,86 ---- List *parameterDefaults, Datum proconfig, float4 procost, ! float4 prorows, ! Node *proseclabel) { Oid retval; int parameterCount; *************** ProcedureCreate(const char *procedureNam *** 96,101 **** --- 98,104 ---- Datum values[Natts_pg_proc]; bool replaces[Natts_pg_proc]; Oid relid; + Oid prosecid = InvalidOid; NameData procname; TupleDesc tupDesc; bool is_update; *************** ProcedureCreate(const char *procedureNam *** 343,348 **** --- 346,356 ---- ObjectIdGetDatum(procNamespace), 0); + /* Check permission to create/replace a function */ + prosecid = sepgsql_proc_create(procedureName, oldtup, + procNamespace, languageObjectId, + (DefElem *)proseclabel); + if (HeapTupleIsValid(oldtup)) { /* There is one; okay to replace it? */ *************** ProcedureCreate(const char *procedureNam *** 477,482 **** --- 485,492 ---- /* Okay, do it... */ tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, prosecid); simple_heap_update(rel, &tup->t_self, tup); ReleaseSysCache(oldtup); *************** ProcedureCreate(const char *procedureNam *** 486,491 **** --- 496,503 ---- { /* Creating a new procedure */ tup = heap_form_tuple(tupDesc, values, nulls); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, prosecid); simple_heap_insert(rel, tup); is_update = false; } diff -Nrpc base/src/backend/catalog/pg_security.c sepgsql/src/backend/catalog/pg_security.c *** base/src/backend/catalog/pg_security.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/catalog/pg_security.c Thu Sep 10 15:18:03 2009 *************** *** 0 **** --- 1,637 ---- + /* + * 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/genam.h" + #include "access/heapam.h" + #include "access/sysattr.h" + #include "access/xact.h" + #include "catalog/catalog.h" + #include "catalog/indexing.h" + #include "catalog/pg_proc.h" + #include "catalog/pg_security.h" + #include "catalog/pg_type.h" + #include "executor/spi.h" + #include "miscadmin.h" + #include "security/rowlevel.h" + #include "security/sepgsql.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" + #include "utils/memutils.h" + #include "utils/rel.h" + #include "utils/lsyscache.h" + #include "utils/syscache.h" + #include "utils/tqual.h" + + bool + securityTupleDescHasSecid(Oid relid, char relkind) + { + return sepgsqlTupleDescHasSecid(relid, relkind); + } + + /* + * security attribute management at the initdb phase. + */ + typedef struct earlySecAttr + { + struct earlySecAttr *next; + Oid secid; + Oid datid; + Oid relid; + char seckind; + char secattr[1]; + } earlySecAttr; + + static earlySecAttr *earlySecAttrList = NULL; + + static Oid + earlyInputSecurityAttr(Oid datid, Oid relid, char seckind, const char *secattr) + { + static Oid dummySecid = SecurityRelationId; + earlySecAttr *es; + + for (es = earlySecAttrList; es; es = es->next) + { + if (es->datid == datid && + es->relid == relid && + es->seckind == seckind && + strcmp(es->secattr, secattr) == 0) + return es->secid; + } + /* Not found */ + es = MemoryContextAlloc(TopMemoryContext, + sizeof(*es) + strlen(secattr)); + es->secid = --dummySecid; + es->datid = datid; + es->relid = relid; + es->seckind = seckind; + strcpy(es->secattr, secattr); + + es->next = earlySecAttrList; + earlySecAttrList = es; + + return es->secid; + } + + static char * + earlyOutputSecurityAttr(Oid datid, Oid relid, char seckind, Oid secid) + { + earlySecAttr *es; + + for (es = earlySecAttrList; es; es = es->next) + { + if (es->datid == datid && + es->relid == relid && + es->seckind == seckind && + es->secid == secid) + return pstrdup(es->secattr); + } + return NULL; /* Not found */ + } + + void + securityPostBootstrapingMode(void) + { + Relation rel; + HeapTuple tuple; + earlySecAttr *es; + Datum values[Natts_pg_security]; + bool nulls[Natts_pg_security]; + + if (!earlySecAttrList) + return; /* do nothing */ + + StartTransactionCommand(); + + /* flush all the cached entries */ + rel = heap_open(SecurityRelationId, RowExclusiveLock); + for (es = earlySecAttrList; es; es = es->next) + { + memset(nulls, false, sizeof(nulls)); + values[Anum_pg_security_secid - 1] = ObjectIdGetDatum(es->secid); + values[Anum_pg_security_datid - 1] = ObjectIdGetDatum(es->datid); + values[Anum_pg_security_relid - 1] = ObjectIdGetDatum(es->relid); + values[Anum_pg_security_seckind - 1] = CharGetDatum(es->seckind); + values[Anum_pg_security_secattr - 1] = CStringGetTextDatum(es->secattr); + + tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); + + simple_heap_insert(rel, tuple); + CatalogUpdateIndexes(rel, tuple); + heap_freetuple(tuple); + } + heap_close(rel, RowExclusiveLock); + + CommitTransactionCommand(); + } + + /* + * securityOnCreateDatabase + * copies all the entries refered by source database + */ + void + securityOnCreateDatabase(Oid src_datid, Oid dst_datid) + { + Relation rel; + ScanKeyData keys[1]; + SysScanDesc scan; + HeapTuple oldtup, newtup; + Datum values[Natts_pg_security]; + bool nulls[Natts_pg_security]; + bool replaces[Natts_pg_security]; + + /* Scan all entries with pg_security.datid = src_datid */ + ScanKeyInit(&keys[0], + Anum_pg_security_datid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(src_datid)); + + rel = heap_open(SecurityRelationId, RowExclusiveLock); + + scan = systable_beginscan(rel, SecuritySecidIndexId, true, + SnapshotNow, 1, keys); + + /* pg_security.datid shall be replaced */ + memset(values, 0, sizeof(values)); + memset(nulls, false, sizeof(nulls)); + memset(replaces, false, sizeof(replaces)); + + values[Anum_pg_security_datid - 1] = ObjectIdGetDatum(dst_datid); + replaces[Anum_pg_security_datid - 1] = true; + + while (HeapTupleIsValid(oldtup = systable_getnext(scan))) + { + newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), + values, nulls, replaces); + simple_heap_insert(rel, newtup); + + CatalogUpdateIndexes(rel, newtup); + + heap_freetuple(newtup); + } + systable_endscan(scan); + + heap_close(rel, RowExclusiveLock); + } + + /* + * securityOnDropDatabase + * drops all the entries refered by dropped database + */ + void + securityOnDropDatabase(Oid datid) + { + Relation rel; + ScanKeyData keys[1]; + SysScanDesc scan; + HeapTuple tuple; + + /* Scan all entries with pg_security.datid = datid */ + ScanKeyInit(&keys[0], + Anum_pg_security_datid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(datid)); + + rel = heap_open(SecurityRelationId, RowExclusiveLock); + + scan = systable_beginscan(rel, SecuritySecidIndexId, true, + SnapshotNow, 1, keys); + + while (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + simple_heap_delete(rel, &tuple->t_self); + } + + systable_endscan(scan); + + heap_close(rel, RowExclusiveLock); + } + + /* + * InputSecurityAttr + */ + static Oid + InputSecurityAttr(Oid relid, char seckind, const char *secattr) + { + Relation rel; + HeapTuple tuple; + Oid datid; + Oid secid; + Datum values[Natts_pg_security]; + bool nulls[Natts_pg_security]; + + datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); + + if (IsBootstrapProcessingMode()) + return earlyInputSecurityAttr(datid, relid, seckind, secattr); + + /* + * Lookup the syscache first + */ + tuple = SearchSysCache(SECURITYATTR, + ObjectIdGetDatum(datid), + ObjectIdGetDatum(relid), + CharGetDatum(seckind), + CStringGetTextDatum(secattr)); + if (HeapTupleIsValid(tuple)) + { + secid = ((Form_pg_security) GETSTRUCT(tuple))->secid; + + ReleaseSysCache(tuple); + + return secid; + } + + /* + * Insert a new tuple, if not exist + */ + rel = heap_open(SecurityRelationId, RowExclusiveLock); + + memset(nulls, false, sizeof(nulls)); + secid = GetNewOidWithIndex(rel, SecuritySecidIndexId, + Anum_pg_security_secid); + values[Anum_pg_security_secid - 1] = ObjectIdGetDatum(secid); + values[Anum_pg_security_datid - 1] = ObjectIdGetDatum(datid); + values[Anum_pg_security_relid - 1] = ObjectIdGetDatum(relid); + values[Anum_pg_security_seckind - 1] = CharGetDatum(seckind); + values[Anum_pg_security_secattr - 1] = CStringGetTextDatum(secattr); + + tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); + + simple_heap_insert(rel, tuple); + CatalogUpdateIndexes(rel, tuple); + + heap_close(rel, RowExclusiveLock); + + /* + * Newly inserted security label needs to be visible by + * later operations in this transaction. + */ + CommandCounterIncrement(); + + return secid; + } + + static char * + OutputSecurityAttr(Oid relid, char seckind, Oid secid) + { + Form_pg_security secForm; + Oid datid; + HeapTuple tuple; + Datum datum; + bool isnull; + char *result; + + datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); + + if (IsBootstrapProcessingMode()) + return earlyOutputSecurityAttr(datid, relid, seckind, secid); + + tuple = SearchSysCache(SECURITYSECID, + ObjectIdGetDatum(secid), + ObjectIdGetDatum(datid), + 0, 0); + if (!HeapTupleIsValid(tuple)) + return NULL; + + /* + * Integrity checks + */ + secForm = (Form_pg_security) GETSTRUCT(tuple); + if (secForm->relid != relid) + goto error; + if (secForm->seckind != seckind) + goto error; + datum = SysCacheGetAttr(SECURITYSECID, tuple, + Anum_pg_security_secattr, + &isnull); + if (isnull) + goto error; + + result = TextDatumGetCString(datum); + + ReleaseSysCache(tuple); + + return result; + + error: + ReleaseSysCache(tuple); + + elog(NOTICE, + "invalid pg_security (secid=%u, datid=%u, relid=%u, seckind=%c)" + " for relid=%u, seckind=%c secid=%u", + secForm->secid, secForm->datid, secForm->relid, secForm->seckind, + relid, seckind, secid); + + return NULL; + } + + /* + * input/output handler + */ + Oid + securityRawSecLabelIn(Oid relid, char *seclabel) + { + seclabel = sepgsqlRawSecLabelIn(seclabel); + + return InputSecurityAttr(relid, SECKIND_SECURITY_LABEL, seclabel); + } + + char * + securityRawSecLabelOut(Oid relid, Oid secid) + { + char *seclabel = OutputSecurityAttr(relid, SECKIND_SECURITY_LABEL, 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 + securitySysattSecLabelOut(Oid relid, HeapTuple tuple) + { + char *seclabel; + + seclabel = sepgsqlSysattSecLabelOut(relid, tuple); + if (!seclabel) + seclabel = "unlabled"; + + return CStringGetTextDatum(seclabel); + } + + /* + * securityReclaimOnDropTable + * drop orphan entries within pg_security on drop table + */ + void + securityReclaimOnDropTable(Oid relid) + { + Relation rel; + SysScanDesc scan; + ScanKeyData key[2]; + HeapTuple tuple; + Oid database_oid; + + database_oid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); + ScanKeyInit(&key[0], + Anum_pg_security_datid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(database_oid)); + ScanKeyInit(&key[1], + Anum_pg_security_relid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + + rel = heap_open(SecurityRelationId, RowExclusiveLock); + scan = systable_beginscan(rel, SecuritySecattrIndexId, true, + SnapshotNow, 2, key); + while (HeapTupleIsValid(tuple = systable_getnext(scan))) + simple_heap_delete(rel, &tuple->t_self); + + systable_endscan(scan); + + heap_close(rel, RowExclusiveLock); + } + + /* + * security_quote_relation + * returns palloc'de identifier with explicit namespace + */ + static char * + security_quote_relation(Oid relid) + { + Oid nspoid = get_rel_namespace(relid); + char *nspname; + char *relname; + + nspname = get_namespace_name(nspoid); + relname = get_rel_name(relid); + + return quote_qualified_identifier(nspname, relname); + } + + /* + * security_reclaim_table + * reclaims orphan entries associated to a certain table + */ + static int + security_reclaim_table(Oid relid, char seckind) + { + StringInfoData query; + SPIPlanPtr plan; + Oid types[2]; + Datum values[2]; + Oid proc_oid; + Oid database_oid; + char *relname_full; + char *attname_datid; + char *attname_relid; + char *attname_secid; + char *attname_seckind; + char *attname_secattr; + char *sec_proname; + char *sec_nspname; + Form_pg_proc proForm; + HeapTuple protup; + + /* + * LOCK the target table + */ + initStringInfo(&query); + relname_full = security_quote_relation(relid); + appendStringInfo(&query, "LOCK %s IN SHARE MODE", relname_full); + if (SPI_execute(query.data, false, 0) != SPI_OK_UTILITY) + elog(ERROR, "SPI_execute failed on %s", query.data); + + /* + * DELETE orphan entries + */ + initStringInfo(&query); + attname_secid = get_attname(SecurityRelationId, Anum_pg_security_secid); + attname_datid = get_attname(SecurityRelationId, Anum_pg_security_datid); + attname_relid = get_attname(SecurityRelationId, Anum_pg_security_relid); + attname_seckind = get_attname(SecurityRelationId, Anum_pg_security_seckind); + attname_secattr = get_attname(SecurityRelationId, Anum_pg_security_secattr); + + appendStringInfo(&query, + "DELETE FROM %s " + "WHERE %s = $1 AND %s = $2 " + " AND %s = $3 AND %s NOT IN ", + security_quote_relation(SecurityRelationId), + quote_identifier(attname_datid), + quote_identifier(attname_relid), + quote_identifier(attname_seckind), + quote_identifier(attname_secid)); + switch (seckind) + { + case SECKIND_SECURITY_LABEL: + proc_oid = F_SECURITY_LABEL_TO_SECID; + break; + default: + elog(ERROR, "unexpected seckind: %c", seckind); + proc_oid = InvalidOid; /* to compiler silent */ + break; + } + + protup = SearchSysCache(PROCOID, + ObjectIdGetDatum(proc_oid), + 0, 0, 0); + if (!HeapTupleIsValid(protup)) + elog(ERROR, "cache lookup failed for procedure: %u", proc_oid); + proForm = (Form_pg_proc) GETSTRUCT(protup); + sec_proname = NameStr(proForm->proname); + sec_nspname = get_namespace_name(proForm->pronamespace); + + appendStringInfo(&query, + "(SELECT %s.%s(%s) FROM ONLY %s)", + quote_identifier(sec_nspname), + quote_identifier(sec_proname), + quote_identifier(get_rel_name(relid)), + relname_full); + ReleaseSysCache(protup); + + /* + * Setup and execute query + */ + types[0] = OIDOID; + types[1] = OIDOID; + types[2] = CHAROID; + plan = SPI_prepare(query.data, 3, types); + if (!plan) + elog(ERROR, "SPI_prepare failed on %s", query.data); + + database_oid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); + + values[0] = ObjectIdGetDatum(database_oid); + values[1] = ObjectIdGetDatum(relid); + values[2] = CharGetDatum(seckind); + if (SPI_execute_plan(plan, values, NULL, false, 0) != SPI_OK_DELETE) + elog(ERROR, "SPI_execute_plan failed on %s", query.data); + + SPI_freetuptable(SPI_tuptable); + + return SPI_processed; + } + + static int + security_reclaim_all_tables(char seckind) + { + StringInfoData query; + char *attname_datid; + char *attname_relid; + int index; + Datum datum; + bool isnull; + int count = 0; + List *relidList = NIL; + ListCell *l; + + initStringInfo(&query); + + attname_datid = get_attname(SecurityRelationId, Anum_pg_security_datid); + attname_relid = get_attname(SecurityRelationId, Anum_pg_security_relid); + appendStringInfo(&query, + "SELECT DISTINCT %s FROM %s WHERE %s IN (%u,%u)", + attname_relid, + security_quote_relation(SecurityRelationId), + attname_datid, InvalidOid, MyDatabaseId); + + if (SPI_execute(query.data, true, 0) != SPI_OK_SELECT) + elog(ERROR, "SPI_execute failed on %s", query.data); + + for (index = 0; index < SPI_processed; index++) + { + datum = SPI_getbinval(SPI_tuptable->vals[index], + SPI_tuptable->tupdesc, 1, &isnull); + if (isnull) + continue; + relidList = lappend_oid(relidList, DatumGetObjectId(datum)); + } + SPI_freetuptable(SPI_tuptable); + + foreach (l, relidList) + count += security_reclaim_table(lfirst_oid(l), seckind); + + return count; + } + + static int + security_reclaim(Oid relid, char seckind) + { + int saved_mode; + int count; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to reclaim security attributes"))); + /* + * Disables SE-PostgreSQL temporary + */ + saved_mode = sepgsqlSetEnforce(0); + + PG_TRY(); + { + if (SPI_connect() != SPI_OK_CONNECT) + elog(ERROR, "SPI_connect failed"); + + if (OidIsValid(relid)) + count = security_reclaim_table(relid, seckind); + else + count = security_reclaim_all_tables(seckind); + + if (SPI_finish() != SPI_OK_FINISH) + elog(ERROR, "SPI_finish failed"); + } + PG_CATCH(); + { + sepgsqlSetEnforce(saved_mode); + PG_RE_THROW(); + } + PG_END_TRY(); + sepgsqlSetEnforce(saved_mode); + + return count; + } + + Datum + security_reclaim_label(PG_FUNCTION_ARGS) + { + PG_RETURN_INT32(security_reclaim(InvalidOid, SECKIND_SECURITY_LABEL)); + } + + Datum + security_reclaim_table_label(PG_FUNCTION_ARGS) + { + PG_RETURN_INT32(security_reclaim(PG_GETARG_OID(0), SECKIND_SECURITY_LABEL)); + } + + Datum + security_label_to_secid(PG_FUNCTION_ARGS) + { + HeapTupleHeader tuphdr = PG_GETARG_HEAPTUPLEHEADER(0); + + PG_RETURN_OID(HeapTupleHeaderGetSecid(tuphdr)); + } diff -Nrpc base/src/backend/catalog/pg_shdepend.c sepgsql/src/backend/catalog/pg_shdepend.c *** base/src/backend/catalog/pg_shdepend.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/catalog/pg_shdepend.c Fri Sep 18 14:51:00 2009 *************** *** 36,41 **** --- 36,42 ---- #include "commands/schemacmds.h" #include "commands/tablecmds.h" #include "commands/typecmds.h" + #include "security/sepgsql.h" #include "storage/lmgr.h" #include "miscadmin.h" #include "utils/acl.h" *************** shdepReassignOwned(List *roleids, Oid ne *** 1336,1341 **** --- 1337,1344 ---- break; case TypeRelationId: + /* SELinux checks */ + sepgsql_type_alter(sdepForm->objid, NULL, InvalidOid); AlterTypeOwnerInternal(sdepForm->objid, newrole, true); break; *************** shdepReassignOwned(List *roleids, Oid ne *** 1348,1354 **** break; case RelationRelationId: ! /* * Pass recursing = true so that we don't fail on indexes, * owned sequences, etc when we happen to visit them --- 1351,1358 ---- break; case RelationRelationId: ! /* SELinux checks */ ! sepgsql_relation_alter(sdepForm->objid, NULL, InvalidOid); /* * Pass recursing = true so that we don't fail on indexes, * owned sequences, etc when we happen to visit them diff -Nrpc base/src/backend/catalog/pg_type.c sepgsql/src/backend/catalog/pg_type.c *** base/src/backend/catalog/pg_type.c Sun Sep 6 19:40:49 2009 --- sepgsql/src/backend/catalog/pg_type.c Fri Sep 18 17:39:46 2009 *************** *** 25,30 **** --- 25,31 ---- #include "commands/typecmds.h" #include "miscadmin.h" #include "parser/scansup.h" + #include "security/sepgsql.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" *************** TypeShellMake(const char *typeName, Oid *** 56,65 **** --- 57,73 ---- Datum values[Natts_pg_type]; bool nulls[Natts_pg_type]; Oid typoid; + Oid typsid; NameData name; Assert(PointerIsValid(typeName)); + /* SELinux check permission to create a shell type */ + typsid = sepgsql_type_create(typeName, InvalidOid, typeNamespace, + F_SHELL_IN, F_SHELL_OUT, + InvalidOid, InvalidOid, + InvalidOid, InvalidOid, InvalidOid); + /* * open pg_type */ *************** TypeCreate(Oid newTypeOid, *** 201,206 **** --- 209,215 ---- { Relation pg_type_desc; Oid typeObjectId; + Oid typeSecid = InvalidOid; bool rebuildDeps = false; HeapTuple tup; bool nulls[Natts_pg_type]; *************** TypeCreate(Oid newTypeOid, *** 367,372 **** --- 376,390 ---- CStringGetDatum(typeName), ObjectIdGetDatum(typeNamespace), 0, 0); + + /* SELinux checks to create/replace type */ + if (!isImplicitArray && typeType != TYPTYPE_COMPOSITE) + typeSecid = sepgsql_type_create(typeName, tup, typeNamespace, + inputProcedure, outputProcedure, + receiveProcedure, sendProcedure, + typmodinProcedure, typmodoutProcedure, + analyzeProcedure); + if (HeapTupleIsValid(tup)) { /* *************** TypeCreate(Oid newTypeOid, *** 412,417 **** --- 430,437 ---- /* Force the OID if requested by caller, else heap_insert does it */ if (OidIsValid(newTypeOid)) HeapTupleSetOid(tup, newTypeOid); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, typeSecid); typeObjectId = simple_heap_insert(pg_type_desc, tup); } diff -Nrpc base/src/backend/catalog/toasting.c sepgsql/src/backend/catalog/toasting.c *** base/src/backend/catalog/toasting.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/catalog/toasting.c Thu Oct 8 09:29:32 2009 *************** *** 28,33 **** --- 28,34 ---- #include "catalog/toasting.h" #include "miscadmin.h" #include "nodes/makefuncs.h" + #include "security/sepgsql.h" #include "utils/builtins.h" #include "utils/syscache.h" *************** create_toast_table(Relation rel, Oid toa *** 125,130 **** --- 126,132 ---- char toast_relname[NAMEDATALEN]; char toast_idxname[NAMEDATALEN]; IndexInfo *indexInfo; + Oid *secLabels; Oid classObjectId[2]; int16 coloptions[2]; ObjectAddress baseobject, *************** create_toast_table(Relation rel, Oid toa *** 199,204 **** --- 201,211 ---- else namespaceid = PG_TOAST_NAMESPACE; + secLabels = sepgsql_relation_create(toast_relname, + RELKIND_TOASTVALUE, + tupdesc, namespaceid, + NULL, NIL, false, false); + toast_relid = heap_create_with_catalog(toast_relname, namespaceid, rel->rd_rel->reltablespace, *************** create_toast_table(Relation rel, Oid toa *** 212,218 **** 0, ONCOMMIT_NOOP, reloptions, ! true); /* make the toast relation visible, else index creation will fail */ CommandCounterIncrement(); --- 219,226 ---- 0, ONCOMMIT_NOOP, reloptions, ! true, ! secLabels); /* make the toast relation visible, else index creation will fail */ CommandCounterIncrement(); diff -Nrpc base/src/backend/commands/aggregatecmds.c sepgsql/src/backend/commands/aggregatecmds.c *** base/src/backend/commands/aggregatecmds.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/commands/aggregatecmds.c Thu Sep 17 22:10:19 2009 *************** *** 32,37 **** --- 32,38 ---- #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/lsyscache.h" *************** RenameAggregate(List *name, List *args, *** 311,316 **** --- 312,320 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* SELinux permission checks */ + sepgsql_proc_alter(procOid, newname, InvalidOid); + /* rename */ namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname); simple_heap_update(rel, &tup->t_self, tup); diff -Nrpc base/src/backend/commands/alter.c sepgsql/src/backend/commands/alter.c *** base/src/backend/commands/alter.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/commands/alter.c Fri Sep 18 14:51:00 2009 *************** ExecAlterOwnerStmt(AlterOwnerStmt *stmt) *** 284,286 **** --- 284,315 ---- (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, true); + 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 -Nrpc base/src/backend/commands/cluster.c sepgsql/src/backend/commands/cluster.c *** base/src/backend/commands/cluster.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/commands/cluster.c Fri Sep 18 14:51:00 2009 *************** *** 36,41 **** --- 36,42 ---- #include "commands/trigger.h" #include "commands/vacuum.h" #include "miscadmin.h" + #include "security/sepgsql.h" #include "storage/bufmgr.h" #include "storage/procarray.h" #include "utils/acl.h" *************** rebuild_relation(Relation OldHeap, Oid i *** 617,624 **** /* * The new relation is local to our transaction and we know nothing * depends on it, so DROP_RESTRICT should be OK. */ ! performDeletion(&object, DROP_RESTRICT); /* performDeletion does CommandCounterIncrement at end */ --- 618,626 ---- /* * The new relation is local to our transaction and we know nothing * depends on it, so DROP_RESTRICT should be OK. + * SELinux does not check any permissions here. */ ! performDeletionNoPerms(&object, DROP_RESTRICT); /* performDeletion does CommandCounterIncrement at end */ *************** make_new_heap(Oid OIDOldHeap, const char *** 712,718 **** 0, ONCOMMIT_NOOP, reloptions, ! allowSystemTableMods); ReleaseSysCache(tuple); --- 714,721 ---- 0, ONCOMMIT_NOOP, reloptions, ! allowSystemTableMods, ! sepgsql_relation_copy(OldHeap)); ReleaseSysCache(tuple); *************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl *** 924,929 **** --- 927,936 ---- if (NewHeap->rd_rel->relhasoids) HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple)); + /* Preserve SID, if any */ + if (HeapTupleHasSecid(copiedTuple)) + HeapTupleSetSecid(copiedTuple, HeapTupleGetSecid(tuple)); + /* The heap rewrite module does the rest */ rewrite_heap_tuple(rwstate, tuple, copiedTuple); diff -Nrpc base/src/backend/commands/conversioncmds.c sepgsql/src/backend/commands/conversioncmds.c *** base/src/backend/commands/conversioncmds.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/commands/conversioncmds.c Thu Sep 17 22:10:19 2009 *************** *** 24,29 **** --- 24,30 ---- #include "mb/pg_wchar.h" #include "miscadmin.h" #include "parser/parse_func.h" + #include "security/sepgsql.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" *************** CreateConversionCommand(CreateConversion *** 45,50 **** --- 46,52 ---- int from_encoding; int to_encoding; Oid funcoid; + Oid secid; const char *from_encoding_name = stmt->for_encoding_name; const char *to_encoding_name = stmt->to_encoding_name; List *func_name = stmt->func_name; *************** CreateConversionCommand(CreateConversion *** 96,101 **** --- 98,106 ---- aclcheck_error(aclresult, ACL_KIND_PROC, NameListToString(func_name)); + /* SELinux checks */ + secid = sepgsql_conversion_create(conversion_name, namespaceId, funcoid); + /* * Check that the conversion function is suitable for the requested source * and target encodings. We do that by calling the function with an empty *************** CreateConversionCommand(CreateConversion *** 114,120 **** * name) */ ConversionCreate(conversion_name, namespaceId, GetUserId(), ! from_encoding, to_encoding, funcoid, stmt->def); } /* --- 119,125 ---- * name) */ ConversionCreate(conversion_name, namespaceId, GetUserId(), ! from_encoding, to_encoding, funcoid, secid, stmt->def); } /* *************** RenameConversion(List *name, const char *** 240,245 **** --- 245,253 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* SELinux checks */ + sepgsql_conversion_alter(conversionOid, newname); + /* rename */ namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname); simple_heap_update(rel, &tup->t_self, tup); *************** AlterConversionOwner_internal(Relation r *** 336,341 **** --- 344,351 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(convForm->connamespace)); } + /* SELinux checks */ + sepgsql_conversion_alter(HeapTupleGetOid(tup), NULL); /* * Modify the owner --- okay to scribble on tup because it's a copy diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c *** base/src/backend/commands/copy.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/commands/copy.c Mon Sep 28 09:29:32 2009 *************** *** 21,28 **** --- 21,31 ---- #include #include "access/heapam.h" + #include "access/sysattr.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,39 **** --- 37,44 ---- #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" *************** typedef struct CopyStateData *** 160,165 **** --- 165,174 ---- 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; *************** static const char BinarySignature[11] = *** 243,250 **** /* non-export function prototypes */ static void DoCopyTo(CopyState cstate); static void CopyTo(CopyState cstate); ! static void CopyOneRowTo(CopyState cstate, Oid tupleOid, ! Datum *values, bool *nulls); static void CopyFrom(CopyState cstate); static bool CopyReadLine(CopyState cstate); static bool CopyReadLineText(CopyState cstate); --- 252,259 ---- /* non-export function prototypes */ static void DoCopyTo(CopyState cstate); static void CopyTo(CopyState cstate); ! static void CopyOneRowTo(CopyState cstate, HeapTuple tuple, ! Datum *values, bool *nulls); static void CopyFrom(CopyState cstate); static bool CopyReadLine(CopyState cstate); static bool CopyReadLineText(CopyState cstate); *************** DoCopy(const CopyStmt *stmt, const char *** 958,969 **** errmsg("CSV quote character must not appear in the NULL specification"))); /* Disallow file COPY except to superusers. */ ! if (!pipe && !superuser()) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("must be superuser to COPY to or from a file"), ! errhint("Anyone can COPY to stdout or from stdin. " ! "psql's \\copy command also works for anyone."))); if (stmt->relation) { --- 967,985 ---- errmsg("CSV quote character must not appear in the NULL specification"))); /* Disallow file COPY except to superusers. */ ! if (!pipe) ! { ! if (!superuser()) ! ereport(ERROR, ! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), ! errmsg("must be superuser to COPY to or from a file"), ! errhint("Anyone can COPY to stdout or from stdin. " ! "psql's \\copy command also works for anyone."))); ! if (is_from) ! sepgsql_file_read(stmt->filename); ! else ! sepgsql_file_write(stmt->filename); ! } if (stmt->relation) { *************** DoCopy(const CopyStmt *stmt, const char *** 1090,1095 **** --- 1106,1114 ---- num_phys_attrs = tupDesc->natts; + /* SELinux: check table/column level permission */ + sepgsqlCheckCopyTable(cstate->rel, cstate->attnumlist, is_from); + /* Convert FORCE QUOTE name list to per-column flags, check validity */ cstate->force_quote_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool)); if (force_quote) *************** DoCopy(const CopyStmt *stmt, const char *** 1104,1114 **** int attnum = lfirst_int(cur); if (!list_member_int(cstate->attnumlist, attnum)) 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; } } --- 1123,1153 ---- 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(attForm->attname)))); ! } ! ! switch (attnum) ! { ! case SecurityAttributeNumber: ! cstate->seclabel_force_quot = true; ! break; ! default: ! cstate->force_quote_flags[attnum - 1] = true; ! break; ! } } } *************** DoCopy(const CopyStmt *stmt, const char *** 1126,1135 **** int attnum = lfirst_int(cur); if (!list_member_int(cstate->attnumlist, attnum)) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", ! NameStr(tupDesc->attrs[attnum - 1]->attname)))); cstate->force_notnull_flags[attnum - 1] = true; } } --- 1165,1187 ---- 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(attForm->attname)))); ! } ! if (SystemAttributeIsWritable(attnum)) ! continue; /* ignore, if specified */ cstate->force_notnull_flags[attnum - 1] = true; } } *************** CopyTo(CopyState cstate) *** 1321,1336 **** int attnum = lfirst_int(cur); Oid out_func_oid; bool isvarlena; if (cstate->binary) ! getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid, &out_func_oid, &isvarlena); else ! getTypeOutputInfo(attr[attnum - 1]->atttypid, &out_func_oid, &isvarlena); ! fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); } /* --- 1373,1403 ---- int attnum = lfirst_int(cur); Oid out_func_oid; bool isvarlena; + FmgrInfo *out_fmgr; + Form_pg_attribute attForm; + + switch (attnum) + { + case SecurityAttributeNumber: + 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(attForm->atttypid, &out_func_oid, &isvarlena); else ! getTypeOutputInfo(attForm->atttypid, &out_func_oid, &isvarlena); ! fmgr_info(out_func_oid, out_fmgr); } /* *************** CopyTo(CopyState cstate) *** 1385,1391 **** CopySendChar(cstate, cstate->delim[0]); hdr_delim = true; ! colname = NameStr(attr[attnum - 1]->attname); CopyAttributeOutCSV(cstate, colname, false, list_length(cstate->attnumlist) == 1); --- 1452,1465 ---- CopySendChar(cstate, cstate->delim[0]); hdr_delim = true; ! 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); *************** CopyTo(CopyState cstate) *** 1411,1421 **** { CHECK_FOR_INTERRUPTS(); /* 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); } heap_endscan(scandesc); --- 1485,1499 ---- { CHECK_FOR_INTERRUPTS(); + /* check Row-level permission on the 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, tuple, values, nulls); } heap_endscan(scandesc); *************** CopyTo(CopyState cstate) *** 1441,1447 **** * Emit one row during CopyTo(). */ static void ! CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) { bool need_delim = false; FmgrInfo *out_functions = cstate->out_functions; --- 1519,1526 ---- * Emit one row during CopyTo(). */ static void ! CopyOneRowTo(CopyState cstate, HeapTuple tuple, ! Datum *values, bool *nulls) { bool need_delim = false; FmgrInfo *out_functions = cstate->out_functions; *************** CopyOneRowTo(CopyState cstate, Oid tuple *** 1461,1467 **** { /* Hack --- assume Oid is same size as int32 */ CopySendInt32(cstate, sizeof(int32)); ! CopySendInt32(cstate, tupleOid); } } else --- 1540,1546 ---- { /* Hack --- assume Oid is same size as int32 */ CopySendInt32(cstate, sizeof(int32)); ! CopySendInt32(cstate, HeapTupleGetOid(tuple)); } } else *************** CopyOneRowTo(CopyState cstate, Oid tuple *** 1471,1477 **** if (cstate->oids) { string = DatumGetCString(DirectFunctionCall1(oidout, ! ObjectIdGetDatum(tupleOid))); CopySendString(cstate, string); need_delim = true; } --- 1550,1556 ---- if (cstate->oids) { string = DatumGetCString(DirectFunctionCall1(oidout, ! ObjectIdGetDatum(HeapTupleGetOid(tuple)))); CopySendString(cstate, string); need_delim = true; } *************** CopyOneRowTo(CopyState cstate, Oid tuple *** 1480,1487 **** foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); ! Datum value = values[attnum - 1]; ! bool isnull = nulls[attnum - 1]; if (!cstate->binary) { --- 1559,1569 ---- foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); ! Oid relid; ! Datum value; ! bool isnull; ! bool force_quot; ! FmgrInfo *out_fmgr; if (!cstate->binary) { *************** CopyOneRowTo(CopyState cstate, Oid tuple *** 1490,1495 **** --- 1572,1595 ---- need_delim = true; } + switch (attnum) + { + case SecurityAttributeNumber: + relid = RelationGetRelid(cstate->rel); + value = securitySysattSecLabelOut(relid, tuple); + 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) *************** CopyOneRowTo(CopyState cstate, Oid tuple *** 1501,1511 **** { if (!cstate->binary) { ! string = OutputFunctionCall(&out_functions[attnum - 1], ! value); if (cstate->csv_mode) ! CopyAttributeOutCSV(cstate, string, ! cstate->force_quote_flags[attnum - 1], list_length(cstate->attnumlist) == 1); else CopyAttributeOutText(cstate, string); --- 1601,1609 ---- { if (!cstate->binary) { ! string = OutputFunctionCall(out_fmgr, value); if (cstate->csv_mode) ! CopyAttributeOutCSV(cstate, string, force_quot, list_length(cstate->attnumlist) == 1); else CopyAttributeOutText(cstate, string); *************** CopyOneRowTo(CopyState cstate, Oid tuple *** 1514,1521 **** { bytea *outputbytes; ! outputbytes = SendFunctionCall(&out_functions[attnum - 1], ! value); CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); CopySendData(cstate, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); --- 1612,1618 ---- { bytea *outputbytes; ! outputbytes = SendFunctionCall(out_fmgr, value); CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); CopySendData(cstate, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); *************** CopyFrom(CopyState cstate) *** 1649,1656 **** --- 1746,1755 ---- 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; *************** CopyFrom(CopyState cstate) *** 1888,1893 **** --- 1987,2004 ---- fmgr_info(in_func_oid, &oid_in_function); } + if (list_member_int(cstate->attnumlist, + SecurityAttributeNumber)) + { + 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 = (bool *) palloc(num_phys_attrs * sizeof(bool)); *************** CopyFrom(CopyState cstate) *** 1922,1927 **** --- 2033,2039 ---- { bool skip_tuple; Oid loaded_oid = InvalidOid; + Oid loaded_seclabel = InvalidOid; CHECK_FOR_INTERRUPTS(); *************** CopyFrom(CopyState cstate) *** 1993,2006 **** /* Loop to read the user attributes on the line. */ foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); int m = attnum - 1; if (fieldno >= fldct) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("missing data for column \"%s\"", ! NameStr(attr[m]->attname)))); string = field_strings[fieldno++]; if (cstate->csv_mode && string == NULL && --- 2105,2125 ---- /* 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(attForm->attname)))); string = field_strings[fieldno++]; if (cstate->csv_mode && string == NULL && *************** CopyFrom(CopyState cstate) *** 2010,2023 **** string = cstate->null_print; } ! 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] = false; cstate->cur_attname = NULL; cstate->cur_attval = NULL; } --- 2129,2168 ---- string = cstate->null_print; } ! cstate->cur_attname = NameStr(attForm->attname); cstate->cur_attval = string; ! ! switch (attnum) ! { ! case SecurityAttributeNumber: ! if (!string) ! break; ! ! 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] = false; ! break; ! } cstate->cur_attname = NULL; cstate->cur_attval = NULL; } *************** CopyFrom(CopyState cstate) *** 2063,2079 **** i = 0; foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); int m = attnum - 1; ! cstate->cur_attname = NameStr(attr[m]->attname); i++; ! values[m] = CopyReadBinaryAttribute(cstate, ! i, ! &in_functions[m], ! typioparams[m], ! attr[m]->atttypmod, ! &nulls[m]); cstate->cur_attname = NULL; } } --- 2208,2248 ---- i = 0; 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, false); ! else ! attForm = attr[m]; ! ! cstate->cur_attname = NameStr(attForm->attname); i++; ! ! switch (attnum) ! { ! case SecurityAttributeNumber: ! 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, ! &nulls[m]); ! break; ! } cstate->cur_attname = NULL; } } *************** CopyFrom(CopyState cstate) *** 2094,2099 **** --- 2263,2270 ---- if (cstate->oids && file_has_oids) HeapTupleSetOid(tuple, loaded_oid); + if (HeapTupleHasSecid(tuple)) + HeapTupleSetSecid(tuple, loaded_seclabel); /* Triggers and stuff need to be invoked in query context. */ MemoryContextSwitchTo(oldcontext); *************** CopyFrom(CopyState cstate) *** 2118,2123 **** --- 2289,2297 ---- } if (!skip_tuple) + sepgsqlHeapTupleInsert(cstate->rel, tuple, false); + + if (!skip_tuple) { /* Place tuple in tuple slot */ ExecStoreTuple(tuple, slot, InvalidBuffer, false); *************** CopyGetAttnums(TupleDesc tupDesc, Relati *** 3398,3403 **** --- 3572,3584 ---- } 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), *************** copy_dest_receive(TupleTableSlot *slot, *** 3445,3451 **** slot_getallattrs(slot); /* And send the data */ ! CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull); } /* --- 3626,3633 ---- slot_getallattrs(slot); /* And send the data */ ! CopyOneRowTo(cstate, slot->tts_tuple, ! slot->tts_values, slot->tts_isnull); } /* diff -Nrpc base/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/dbcommands.c *** base/src/backend/commands/dbcommands.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/commands/dbcommands.c Thu Sep 17 17:04:16 2009 *************** *** 33,38 **** --- 33,39 ---- #include "catalog/indexing.h" #include "catalog/pg_authid.h" #include "catalog/pg_database.h" + #include "catalog/pg_security.h" #include "catalog/pg_tablespace.h" #include "commands/comment.h" #include "commands/dbcommands.h" *************** *** 41,46 **** --- 42,48 ---- #include "miscadmin.h" #include "pgstat.h" #include "postmaster/bgwriter.h" + #include "security/sepgsql.h" #include "storage/bufmgr.h" #include "storage/fd.h" #include "storage/lmgr.h" *************** createdb(const CreatedbStmt *stmt) *** 111,116 **** --- 113,119 ---- bool new_record_nulls[Natts_pg_database]; Oid dboid; Oid datdba; + Oid datsecid; ListCell *option; DefElem *dtablespacename = NULL; DefElem *downer = NULL; *************** createdb(const CreatedbStmt *stmt) *** 119,124 **** --- 122,128 ---- DefElem *dcollate = NULL; DefElem *dctype = NULL; DefElem *dconnlimit = NULL; + DefElem *dseclabel = NULL; char *dbname = stmt->dbname; char *dbowner = NULL; const char *dbtemplate = NULL; *************** createdb(const CreatedbStmt *stmt) *** 200,205 **** --- 204,217 ---- errmsg("LOCATION is not supported anymore"), errhint("Consider using tablespaces instead."))); } + else if (strcmp(defel->defname, "security_context") == 0) + { + if (dseclabel) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dseclabel = defel; + } else elog(ERROR, "option \"%s\" not recognized", defel->defname); *************** createdb(const CreatedbStmt *stmt) *** 272,277 **** --- 284,292 ---- check_is_member_of_role(GetUserId(), datdba); + /* SELinux checks db_database:{create} */ + datsecid = sepgsql_database_create(dbname, dseclabel); + /* * Lookup database (template) to be cloned, and obtain share lock on it. * ShareLock allows two CREATE DATABASEs to work from the same template *************** createdb(const CreatedbStmt *stmt) *** 557,562 **** --- 572,579 ---- new_record, new_record_nulls); HeapTupleSetOid(tuple, dboid); + if (HeapTupleHasSecid(tuple)) + HeapTupleSetSecid(tuple, datsecid); simple_heap_insert(pg_database_rel, tuple); *************** createdb(const CreatedbStmt *stmt) *** 573,578 **** --- 590,598 ---- /* Create pg_shdepend entries for objects within database */ copyTemplateDependencies(src_dboid, dboid); + /* Create pg_security entries for objects within database */ + securityOnCreateDatabase(src_dboid, dboid); + /* * Force a checkpoint before starting the copy. This will force dirty * buffers out to disk, to ensure source database is up-to-date on disk *************** dropdb(const char *dbname, bool missing_ *** 776,781 **** --- 796,804 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname); + /* SELinux checks db_database:{drop} permission */ + sepgsql_database_drop(db_id); + /* * Disallow dropping a DB that is marked istemplate. This is just to * prevent people from accidentally dropping template0 or template1; they *************** dropdb(const char *dbname, bool missing_ *** 829,834 **** --- 852,862 ---- dropDatabaseDependencies(db_id); /* + * Remove pg_security entries for the database. + */ + securityOnDropDatabase(db_id); + + /* * Drop pages for this database that are in the shared buffer cache. This * is important to ensure that no remaining backend tries to write out a * dirty buffer to the dead database later... *************** RenameDatabase(const char *oldname, cons *** 913,918 **** --- 941,949 ---- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to rename database"))); + /* SELinux: check db_database:{setattr} */ + sepgsql_database_alter(db_id); + /* * Make sure the new name doesn't exist. See notes for same error in * CREATE DATABASE. *************** movedb(const char *dbname, const char *t *** 1025,1030 **** --- 1056,1064 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname); + /* SELinux checks db_database:{setattr} */ + sepgsql_database_alter(db_id); + /* * Obviously can't move the tables of my own database */ *************** AlterDatabase(AlterDatabaseStmt *stmt, b *** 1377,1382 **** --- 1411,1419 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, stmt->dbname); + /* SELinux checks db_database:{setattr} */ + sepgsql_database_alter(HeapTupleGetOid(tuple)); + /* * Build an updated tuple, perusing the information just obtained */ *************** AlterDatabaseSet(AlterDatabaseSetStmt *s *** 1449,1454 **** --- 1486,1494 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, stmt->dbname); + /* SELinux checks db_database:{setattr} */ + sepgsql_database_alter(HeapTupleGetOid(tuple)); + memset(repl_repl, false, sizeof(repl_repl)); repl_repl[Anum_pg_database_datconfig - 1] = true; *************** AlterDatabaseOwner(const char *dbname, O *** 1571,1576 **** --- 1611,1619 ---- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to change owner of database"))); + /* SELinux checks db_database:{setattr} */ + sepgsql_database_alter(HeapTupleGetOid(tuple)); + memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); *************** AlterDatabaseOwner(const char *dbname, O *** 1615,1620 **** --- 1658,1715 ---- */ } + /* + * ALTER DATABASE name SECURITY_LABEL [=] newlabel + */ + void + AlterDatabaseSecLabel(const char *dbname, DefElem *seclabel) + { + Relation rel; + HeapTuple oldtup; + HeapTuple newtup; + ScanKeyData scankey; + SysScanDesc scan; + Oid secid; + bool replaces[Natts_pg_database]; + + /* 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); + oldtup = systable_getnext(scan); + if (!HeapTupleIsValid(oldtup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_DATABASE), + errmsg("database \"%s\" does not exist", dbname))); + + memset(replaces, false, sizeof(replaces)); + newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), + NULL, NULL, replaces); + if (!HeapTupleHasSecid(newtup)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Unable to set security label on \"%s\"", dbname))); + systable_endscan(scan); + + /* check DAC permission */ + if (!pg_database_ownercheck(HeapTupleGetOid(newtup), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname); + + /* SELinux checks db_database:{setattr relabelfrom relabelto} */ + secid = sepgsql_database_relabel(HeapTupleGetOid(newtup), seclabel); + HeapTupleSetSecid(newtup, secid); + + simple_heap_update(rel, &newtup->t_self, newtup); + CatalogUpdateIndexes(rel, newtup); + + heap_freetuple(newtup); + + heap_close(rel, RowExclusiveLock); + } /* * Helper functions diff -Nrpc base/src/backend/commands/foreigncmds.c sepgsql/src/backend/commands/foreigncmds.c *** base/src/backend/commands/foreigncmds.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/commands/foreigncmds.c Thu Sep 17 22:10:19 2009 *************** *** 27,32 **** --- 27,33 ---- #include "foreign/foreign.h" #include "miscadmin.h" #include "parser/parse_func.h" + #include "security/sepgsql.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" *************** AlterForeignDataWrapperOwner(const char *** 233,238 **** --- 234,242 ---- if (form->fdwowner != newOwnerId) { + /* SELinux permission check */ + sepgsql_fdw_alter(fdwId, InvalidOid); + form->fdwowner = newOwnerId; simple_heap_update(rel, &tup->t_self, tup); *************** AlterForeignServerOwner(const char *name *** 297,302 **** --- 301,308 ---- aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); } } + /* SELinux permission checks */ + sepgsql_foreign_server_alter(srvId); form->srvowner = newOwnerId; *************** CreateForeignDataWrapper(CreateFdwStmt * *** 342,347 **** --- 348,354 ---- Oid fdwvalidator; Datum fdwoptions; Oid ownerId; + Oid secid; /* Must be super user */ if (!superuser()) *************** CreateForeignDataWrapper(CreateFdwStmt * *** 380,385 **** --- 387,395 ---- else fdwvalidator = InvalidOid; + /* SELinux permission checks */ + secid = sepgsql_fdw_create(stmt->fdwname, fdwvalidator); + values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = fdwvalidator; nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true; *************** CreateForeignDataWrapper(CreateFdwStmt * *** 393,398 **** --- 403,410 ---- nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true; tuple = heap_form_tuple(rel->rd_att, values, nulls); + if (HeapTupleHasSecid(tuple)) + HeapTupleSetSecid(tuple, secid); fdwId = simple_heap_insert(rel, tuple); CatalogUpdateIndexes(rel, tuple); *************** AlterForeignDataWrapper(AlterFdwStmt *st *** 487,492 **** --- 499,507 ---- fdwvalidator = DatumGetObjectId(datum); } + /* SELinux permission checks */ + sepgsql_fdw_alter(fdwId, fdwvalidator); + /* * Options specified, validate and update. */ *************** CreateForeignServer(CreateForeignServerS *** 609,614 **** --- 624,630 ---- HeapTuple tuple; Oid srvId; Oid ownerId; + Oid secid; AclResult aclresult; ObjectAddress myself; ObjectAddress referenced; *************** CreateForeignServer(CreateForeignServerS *** 636,641 **** --- 652,659 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); + secid = sepgsql_foreign_server_create(stmt->fdwname); + /* * Insert tuple into pg_foreign_server. */ *************** CreateForeignServer(CreateForeignServerS *** 676,681 **** --- 694,701 ---- nulls[Anum_pg_foreign_server_srvoptions - 1] = true; tuple = heap_form_tuple(rel->rd_att, values, nulls); + if (HeapTupleHasSecid(tuple)) + HeapTupleSetSecid(tuple, secid); srvId = simple_heap_insert(rel, tuple); *************** AlterForeignServer(AlterForeignServerStm *** 732,737 **** --- 752,760 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, stmt->servername); + /* SELinux permission checks */ + sepgsql_foreign_server_alter(srvId); + memset(repl_val, 0, sizeof(repl_val)); memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); diff -Nrpc base/src/backend/commands/functioncmds.c sepgsql/src/backend/commands/functioncmds.c *** base/src/backend/commands/functioncmds.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/commands/functioncmds.c Thu Sep 17 17:04:16 2009 *************** *** 53,58 **** --- 53,59 ---- #include "parser/parse_expr.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" *************** compute_attributes_sql_style(List *optio *** 517,523 **** bool *security_definer, ArrayType **proconfig, float4 *procost, ! float4 *prorows) { ListCell *option; DefElem *as_item = NULL; --- 518,525 ---- bool *security_definer, ArrayType **proconfig, float4 *procost, ! float4 *prorows, ! Node **proseclabel) { ListCell *option; DefElem *as_item = NULL; *************** compute_attributes_sql_style(List *optio *** 529,534 **** --- 531,537 ---- List *set_items = NIL; DefElem *cost_item = NULL; DefElem *rows_item = NULL; + DefElem *seclabel_item = NULL; foreach(option, options) { *************** compute_attributes_sql_style(List *optio *** 558,563 **** --- 561,574 ---- errmsg("conflicting or redundant options"))); windowfunc_item = defel; } + else if (strcmp(defel->defname, "security_context") == 0) + { + if (seclabel_item) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + seclabel_item = defel; + } else if (compute_common_attribute(defel, &volatility_item, &strict_item, *************** compute_attributes_sql_style(List *optio *** 622,627 **** --- 633,640 ---- (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("ROWS must be positive"))); } + if (seclabel_item) + *proseclabel = (Node *)seclabel_item; } *************** CreateFunction(CreateFunctionStmt *stmt, *** 762,767 **** --- 775,781 ---- ArrayType *proconfig; float4 procost; float4 prorows; + Node *proseclabel; HeapTuple languageTuple; Form_pg_language languageStruct; List *as_clause; *************** CreateFunction(CreateFunctionStmt *stmt, *** 784,796 **** proconfig = NULL; procost = -1; /* indicates not set */ prorows = -1; /* indicates not set */ /* override attributes from explicit list */ compute_attributes_sql_style(stmt->options, &as_clause, &language, &isWindowFunc, &volatility, &isStrict, &security, ! &proconfig, &procost, &prorows); /* Convert language name to canonical case */ languageName = case_translate_language_name(language); --- 798,811 ---- proconfig = NULL; procost = -1; /* indicates not set */ prorows = -1; /* indicates not set */ + proseclabel = NULL; /* override attributes from explicit list */ compute_attributes_sql_style(stmt->options, &as_clause, &language, &isWindowFunc, &volatility, &isStrict, &security, ! &proconfig, &procost, &prorows, &proseclabel); /* Convert language name to canonical case */ languageName = case_translate_language_name(language); *************** CreateFunction(CreateFunctionStmt *stmt, *** 926,932 **** parameterDefaults, PointerGetDatum(proconfig), procost, ! prorows); } --- 941,948 ---- parameterDefaults, PointerGetDatum(proconfig), procost, ! prorows, ! proseclabel); } *************** RenameFunction(List *name, List *argtype *** 1112,1117 **** --- 1128,1136 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* SELinux permission checks */ + sepgsql_proc_alter(procOid, newname, InvalidOid); + /* rename */ namestrcpy(&(procForm->proname), newname); simple_heap_update(rel, &tup->t_self, tup); *************** AlterFunctionOwner_internal(Relation rel *** 1220,1225 **** --- 1239,1246 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(procForm->pronamespace)); } + /* SELinux permission checks */ + sepgsql_proc_alter(procOid, NULL, InvalidOid); memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); *************** AlterFunctionOwner_internal(Relation rel *** 1258,1263 **** --- 1279,1337 ---- } /* + * ALTER FUNCTION name(args,...) SECURITY_LABEL [=] newlabel + */ + void + AlterFunctionSecLabel(List *name, List *argtypes, DefElem *seclabel) + { + Relation rel; + HeapTuple oldtup; + HeapTuple newtup; + Oid procOid; + Oid secid; + bool replaces[Natts_pg_proc]; + + /* open pg_proc system catalog */ + rel = heap_open(ProcedureRelationId, RowExclusiveLock); + + procOid = LookupFuncNameTypeNames(name, argtypes, false); + + oldtup = SearchSysCache(PROCOID, + ObjectIdGetDatum(procOid), + 0, 0, 0); + if (!HeapTupleIsValid(oldtup)) + elog(ERROR, "cache lookup failed for function %u", procOid); + + memset(replaces, false, sizeof(replaces)); + newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), + NULL, NULL, replaces); + + if (!HeapTupleHasSecid(newtup)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Unable to set security label on \"%s\"", + get_func_name(procOid)))); + + ReleaseSysCache(oldtup); + + /* DAC permission checks */ + if (!pg_proc_ownercheck(HeapTupleGetOid(newtup), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + get_func_name(HeapTupleGetOid(newtup))); + + /* SELinux permission checks */ + secid = sepgsql_proc_relabel(procOid, seclabel); + HeapTupleSetSecid(newtup, secid); + + simple_heap_update(rel, &newtup->t_self, newtup); + CatalogUpdateIndexes(rel, newtup); + + 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). *************** AlterFunction(AlterFunctionStmt *stmt) *** 1296,1301 **** --- 1370,1378 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(stmt->func->funcname)); + /* SELinux checks permissions */ + sepgsql_proc_alter(funcOid, NULL, InvalidOid); + if (procForm->proisagg) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), *************** CreateCast(CreateCastStmt *stmt) *** 1473,1478 **** --- 1550,1556 ---- char sourcetyptype; char targettyptype; Oid funcid; + Oid secid; int nargs; char castcontext; char castmethod; *************** CreateCast(CreateCastStmt *stmt) *** 1674,1679 **** --- 1752,1759 ---- castcontext = 0; /* keep compiler quiet */ break; } + /* SELinux permission check */ + secid = sepgsql_cast_create(sourcetypeid, targettypeid, funcid); relation = heap_open(CastRelationId, RowExclusiveLock); *************** CreateCast(CreateCastStmt *stmt) *** 1704,1709 **** --- 1784,1792 ---- tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls); + if (HeapTupleHasSecid(tuple)) + HeapTupleSetSecid(tuple, secid); + simple_heap_insert(relation, tuple); CatalogUpdateIndexes(relation, tuple); *************** AlterFunctionNamespace(List *name, List *** 1897,1902 **** --- 1980,1988 ---- NameStr(proc->proname), newschema))); + /* SELinux checks permissions */ + sepgsql_proc_alter(procOid, NULL, nspOid); + /* OK, modify the pg_proc row */ /* tup is a copy, so we can scribble directly on it */ diff -Nrpc base/src/backend/commands/indexcmds.c sepgsql/src/backend/commands/indexcmds.c *** base/src/backend/commands/indexcmds.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/commands/indexcmds.c Fri Sep 18 17:02:48 2009 *************** *** 37,42 **** --- 37,43 ---- #include "parser/parse_coerce.h" #include "parser/parse_func.h" #include "parser/parsetree.h" + #include "security/sepgsql.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/procarray.h" *************** DefineIndex(RangeVar *heapRelation, *** 258,263 **** --- 259,267 ---- } } + /* SELinux checks */ + sepgsql_index_create(relationId, namespaceId, check_rights); + /* * look up the access method, verify it can handle the requested features */ diff -Nrpc base/src/backend/commands/lockcmds.c sepgsql/src/backend/commands/lockcmds.c *** base/src/backend/commands/lockcmds.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/commands/lockcmds.c Fri Sep 18 14:51:00 2009 *************** *** 20,25 **** --- 20,26 ---- #include "commands/lockcmds.h" #include "miscadmin.h" #include "parser/parse_clause.h" + #include "security/sepgsql.h" #include "storage/lmgr.h" #include "utils/acl.h" #include "utils/lsyscache.h" *************** LockTableRecurse(Oid reloid, RangeVar *r *** 140,145 **** --- 141,149 ---- errmsg("\"%s\" is not a table", RelationGetRelationName(rel)))); + /* SELinux: check db_table:{lock} permission */ + sepgsql_relation_lock(reloid); + /* * If requested, recurse to children. We use find_inheritance_children * not find_all_inheritors to avoid taking locks far in advance of diff -Nrpc base/src/backend/commands/opclasscmds.c sepgsql/src/backend/commands/opclasscmds.c *** base/src/backend/commands/opclasscmds.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/commands/opclasscmds.c Thu Sep 17 17:04:16 2009 *************** *** 35,40 **** --- 35,41 ---- #include "parser/parse_func.h" #include "parser/parse_oper.h" #include "parser/parse_type.h" + #include "security/sepgsql.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" *************** CreateOpFamily(char *amname, char *opfna *** 177,182 **** --- 178,184 ---- HeapTuple tup; Datum values[Natts_pg_opfamily]; bool nulls[Natts_pg_opfamily]; + Oid opfSecid; NameData opfName; ObjectAddress myself, referenced; *************** CreateOpFamily(char *amname, char *opfna *** 197,202 **** --- 199,207 ---- errmsg("operator family \"%s\" for access method \"%s\" already exists", opfname, amname))); + /* SELinux check permission */ + opfSecid = sepgsql_opfamily_create(opfname, namespaceoid); + /* * Okay, let's create the pg_opfamily entry. */ *************** CreateOpFamily(char *amname, char *opfna *** 210,215 **** --- 215,222 ---- values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId()); tup = heap_form_tuple(rel->rd_att, values, nulls); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, opfSecid); opfamilyoid = simple_heap_insert(rel, tup); *************** DefineOpClass(CreateOpClassStmt *stmt) *** 265,270 **** --- 272,278 ---- Form_pg_am pg_am; Datum values[Natts_pg_opclass]; bool nulls[Natts_pg_opclass]; + Oid opcSecid; AclResult aclresult; NameData opcName; ObjectAddress myself, *************** DefineOpClass(CreateOpClassStmt *stmt) *** 353,358 **** --- 361,369 ---- NameListToString(stmt->opfamilyname), stmt->amname))); opfamilyoid = HeapTupleGetOid(tup); + /* SELinux checks permission */ + sepgsql_opfamily_alter(opfamilyoid, NULL); + /* * XXX given the superuser check above, there's no need for an * ownership check here *************** DefineOpClass(CreateOpClassStmt *stmt) *** 371,376 **** --- 382,390 ---- { opfamilyoid = HeapTupleGetOid(tup); + /* SELinux checks permission */ + sepgsql_opfamily_alter(opfamilyoid, NULL); + /* * XXX given the superuser check above, there's no need for an * ownership check here *************** DefineOpClass(CreateOpClassStmt *stmt) *** 441,446 **** --- 455,462 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, get_func_name(funcOid)); #endif + /* SELinux check permission */ + sepgsql_opfamily_add_operator(opfamilyoid, operOid); /* Save the info */ member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); *************** DefineOpClass(CreateOpClassStmt *stmt) *** 465,470 **** --- 481,488 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, get_func_name(funcOid)); #endif + /* SELinux check permission */ + sepgsql_opfamily_add_procedure(opfamilyoid, funcOid); /* Save the info */ member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); *************** DefineOpClass(CreateOpClassStmt *stmt) *** 531,536 **** --- 549,557 ---- errmsg("operator class \"%s\" for access method \"%s\" already exists", opcname, stmt->amname))); + /* SELinux permission check */ + opcSecid = sepgsql_opclass_create(opcname, namespaceoid); + /* * If we are creating a default opclass, check there isn't one already. * (Note we do not restrict this test to visible opclasses; this ensures *************** DefineOpFamily(CreateOpFamilyStmt *stmt) *** 657,662 **** --- 678,684 ---- HeapTuple tup; Datum values[Natts_pg_opfamily]; bool nulls[Natts_pg_opfamily]; + Oid opfSecid; AclResult aclresult; NameData opfName; ObjectAddress myself, *************** DefineOpFamily(CreateOpFamilyStmt *stmt) *** 699,704 **** --- 721,729 ---- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to create an operator family"))); + /* SELinux permission check */ + opfSecid = sepgsql_opfamily_create(opfname, namespaceoid); + rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock); /* *************** AlterOpFamily(AlterOpFamilyStmt *stmt) *** 773,778 **** --- 798,804 ---- int maxOpNumber, /* amstrategies value */ maxProcNumber; /* amsupport value */ HeapTuple tup; + Oid opfSecid; Form_pg_am pg_am; /* Get necessary info about access method */ *************** AlterOpFamily(AlterOpFamilyStmt *stmt) *** 805,810 **** --- 831,837 ---- errmsg("operator family \"%s\" does not exist for access method \"%s\"", NameListToString(stmt->opfamilyname), stmt->amname))); opfamilyoid = HeapTupleGetOid(tup); + opfSecid = HeapTupleGetSecid(tup); ReleaseSysCache(tup); /* *************** AlterOpFamily(AlterOpFamilyStmt *stmt) *** 817,822 **** --- 844,852 ---- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to alter an operator family"))); + /* SELinux permission checks */ + sepgsql_opfamily_alter(opfamilyoid, NULL); + /* * ADD and DROP cases need separate code from here on down. */ *************** AlterOpFamilyAdd(List *opfamilyname, Oid *** 893,898 **** --- 923,930 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, get_func_name(funcOid)); #endif + /* SELinux permission check */ + sepgsql_opfamily_add_operator(opfamilyoid, operOid); /* Save the info */ member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); *************** AlterOpFamilyAdd(List *opfamilyname, Oid *** 917,922 **** --- 949,956 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, get_func_name(funcOid)); #endif + /* SELinux permission check */ + sepgsql_opfamily_add_procedure(opfamilyoid, funcOid); /* Save the info */ member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); *************** RenameOpClass(List *name, const char *ac *** 1815,1820 **** --- 1849,1857 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* SELinux permission checks */ + sepgsql_opclass_alter(opcOid, newname); + /* rename */ namestrcpy(&(((Form_pg_opclass) GETSTRUCT(tup))->opcname), newname); simple_heap_update(rel, &tup->t_self, tup); *************** RenameOpFamily(List *name, const char *a *** 1915,1920 **** --- 1952,1960 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* SELinux check permissions */ + sepgsql_opfamily_alter(opfOid, newname); + /* rename */ namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname); simple_heap_update(rel, &tup->t_self, tup); *************** AlterOpClassOwner_internal(Relation rel, *** 2035,2040 **** --- 2075,2082 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } + /* SELinux permission check */ + sepgsql_opclass_alter(HeapTupleGetOid(tup), NULL); /* * Modify the owner --- okay to scribble on tup because it's a copy *************** AlterOpFamilyOwner_internal(Relation rel *** 2162,2167 **** --- 2204,2211 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } + /* SELinux permission checks */ + sepgsql_opfamily_alter(HeapTupleGetOid(tup), NULL); /* * Modify the owner --- okay to scribble on tup because it's a copy diff -Nrpc base/src/backend/commands/operatorcmds.c sepgsql/src/backend/commands/operatorcmds.c *** base/src/backend/commands/operatorcmds.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/commands/operatorcmds.c Thu Sep 17 22:10:19 2009 *************** *** 45,50 **** --- 45,51 ---- #include "parser/parse_func.h" #include "parser/parse_oper.h" #include "parser/parse_type.h" + #include "security/sepgsql.h" #include "utils/acl.h" #include "utils/lsyscache.h" #include "utils/rel.h" *************** AlterOperatorOwner_internal(Relation rel *** 432,437 **** --- 433,440 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(oprForm->oprnamespace)); } + /* SELinux permission check */ + sepgsql_operator_alter(operOid); /* * Modify the owner --- okay to scribble on tup because it's a copy diff -Nrpc base/src/backend/commands/proclang.c sepgsql/src/backend/commands/proclang.c *** base/src/backend/commands/proclang.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/commands/proclang.c Thu Sep 17 22:10:19 2009 *************** *** 30,35 **** --- 30,36 ---- #include "miscadmin.h" #include "parser/gramparse.h" #include "parser/parse_func.h" + #include "security/sepgsql.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" *************** CreateProceduralLanguage(CreatePLangStmt *** 151,157 **** NIL, PointerGetDatum(NULL), 1, ! 0); } /* --- 152,159 ---- NIL, PointerGetDatum(NULL), 1, ! 0, ! NULL); } /* *************** CreateProceduralLanguage(CreatePLangStmt *** 186,192 **** NIL, PointerGetDatum(NULL), 1, ! 0); } } else --- 188,195 ---- NIL, PointerGetDatum(NULL), 1, ! 0, ! NULL); } } else *************** create_proc_lang(const char *languageNam *** 275,284 **** --- 278,293 ---- bool nulls[Natts_pg_language]; NameData langname; HeapTuple tup; + Oid langSecid; ObjectAddress myself, referenced; /* + * SELinux permission checks + */ + langSecid = sepgsql_language_create(languageName, handlerOid, valOid); + + /* * Insert the new language into pg_language */ rel = heap_open(LanguageRelationId, RowExclusiveLock); *************** create_proc_lang(const char *languageNam *** 297,302 **** --- 306,313 ---- nulls[Anum_pg_language_lanacl - 1] = true; tup = heap_form_tuple(tupDesc, values, nulls); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, langSecid); simple_heap_insert(rel, tup); *************** RenameLanguage(const char *oldname, cons *** 518,523 **** --- 529,537 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, oldname); + /* SELinux permission checks */ + sepgsql_language_alter(HeapTupleGetOid(tup)); + /* rename */ namestrcpy(&(((Form_pg_language) GETSTRUCT(tup))->lanname), newname); simple_heap_update(rel, &tup->t_self, tup); *************** AlterLanguageOwner_internal(HeapTuple tu *** 613,618 **** --- 627,635 ---- /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); + /* SELinux permission checks */ + sepgsql_language_alter(HeapTupleGetOid(tup)); + memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); diff -Nrpc base/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/schemacmds.c *** base/src/backend/commands/schemacmds.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/commands/schemacmds.c Thu Sep 17 17:04:16 2009 *************** *** 25,30 **** --- 25,31 ---- #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" *************** CreateSchemaCommand(CreateSchemaStmt *st *** 48,53 **** --- 49,55 ---- ListCell *parsetree_item; Oid owner_uid; Oid saved_uid; + Oid nspsecid; bool saved_secdefcxt; AclResult aclresult; *************** CreateSchemaCommand(CreateSchemaStmt *st *** 75,80 **** --- 77,86 ---- check_is_member_of_role(saved_uid, owner_uid); + /* SELinux checks db_schema:{create} */ + nspsecid = sepgsql_schema_create(schemaName, false, + (DefElem *)stmt->secLabel); + /* Additional check to protect reserved schema names */ if (!allowSystemTableMods && IsReservedName(schemaName)) ereport(ERROR, *************** CreateSchemaCommand(CreateSchemaStmt *st *** 94,100 **** SetUserIdAndContext(owner_uid, true); /* Create the schema's namespace */ ! namespaceId = NamespaceCreate(schemaName, owner_uid); /* Advance cmd counter to make the namespace visible */ CommandCounterIncrement(); --- 100,106 ---- SetUserIdAndContext(owner_uid, true); /* Create the schema's namespace */ ! namespaceId = NamespaceCreate(schemaName, owner_uid, nspsecid); /* Advance cmd counter to make the namespace visible */ CommandCounterIncrement(); *************** RenameSchema(const char *oldname, const *** 267,274 **** errmsg("schema \"%s\" does not exist", oldname))); /* make sure the new name doesn't exist */ ! if (HeapTupleIsValid( ! SearchSysCache(NAMESPACENAME, CStringGetDatum(newname), 0, 0, 0))) ereport(ERROR, --- 273,279 ---- errmsg("schema \"%s\" does not exist", oldname))); /* make sure the new name doesn't exist */ ! if (HeapTupleIsValid(SearchSysCache(NAMESPACENAME, CStringGetDatum(newname), 0, 0, 0))) ereport(ERROR, *************** RenameSchema(const char *oldname, const *** 286,291 **** --- 291,299 ---- aclcheck_error(aclresult, ACL_KIND_DATABASE, get_database_name(MyDatabaseId)); + /* SELinux checks db_schema:{setattr} */ + sepgsql_schema_alter(HeapTupleGetOid(tup)); + if (!allowSystemTableMods && IsReservedName(newname)) ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), *************** AlterSchemaOwner_internal(HeapTuple tup, *** 397,402 **** --- 405,413 ---- aclcheck_error(aclresult, ACL_KIND_DATABASE, get_database_name(MyDatabaseId)); + /* SELinux checks db_schema:{setattr} */ + sepgsql_schema_alter(HeapTupleGetOid(tup)); + memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); *************** AlterSchemaOwner_internal(HeapTuple tup, *** 431,433 **** --- 442,492 ---- } } + + /* + * ALTER SCHEMA name SECURITY_LABEL [=] newlabel + */ + void + AlterSchemaSecLabel(const char *name, DefElem *secLabel) + { + Relation rel; + HeapTuple oldtup; + HeapTuple newtup; + Oid secid; + bool replaces[Natts_pg_namespace]; + + /* open pg_namespace relation */ + rel = heap_open(NamespaceRelationId, RowExclusiveLock); + oldtup = SearchSysCache(NAMESPACENAME, + CStringGetDatum(name), + 0, 0, 0); + if (!HeapTupleIsValid(oldtup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_SCHEMA), + errmsg("schema \"%s\" does not exist", name))); + + memset(replaces, false, sizeof(replaces)); + newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), + NULL, NULL, replaces); + if (!HeapTupleHasSecid(newtup)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Unable to set security label on \"%s\"", name))); + + ReleaseSysCache(oldtup); + + /* DAC permission check */ + if (!pg_namespace_ownercheck(HeapTupleGetOid(newtup), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, name); + /* SELinux checks db_schema:{setattr relabelfrom relabelto} */ + secid = sepgsql_schema_relabel(HeapTupleGetOid(newtup), secLabel); + HeapTupleSetSecid(newtup, secid); + + simple_heap_update(rel, &newtup->t_self, newtup); + + CatalogUpdateIndexes(rel, newtup); + + heap_freetuple(newtup); + + heap_close(rel, RowExclusiveLock); + } diff -Nrpc base/src/backend/commands/sequence.c sepgsql/src/backend/commands/sequence.c *** base/src/backend/commands/sequence.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/commands/sequence.c Fri Sep 18 14:51:00 2009 *************** *** 26,31 **** --- 26,32 ---- #include "commands/tablecmds.h" #include "miscadmin.h" #include "nodes/makefuncs.h" + #include "security/sepgsql.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/proc.h" *************** DefineSequence(CreateSeqStmt *seq) *** 201,206 **** --- 202,208 ---- stmt->options = list_make1(defWithOids(false)); stmt->oncommit = ONCOMMIT_NOOP; stmt->tablespacename = NULL; + stmt->secLabel = seq->secLabel; seqoid = DefineRelation(stmt, RELKIND_SEQUENCE); *************** AlterSequence(AlterSeqStmt *stmt) *** 328,333 **** --- 330,337 ---- if (!pg_class_ownercheck(relid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, stmt->sequence->relname); + /* SELinux checks db_sequence:{setattr} */ + sepgsql_relation_alter(relid, NULL, InvalidOid); /* do the work */ AlterSequenceInternal(relid, stmt->options); *************** nextval_internal(Oid relid) *** 467,472 **** --- 471,479 ---- errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); + /* SELinux check db_sequence:{next_value} */ + sepgsql_sequence_next_value(elm->relid); + if (elm->last != elm->cached) /* some numbers were cached */ { Assert(elm->last_valid); *************** currval_oid(PG_FUNCTION_ARGS) *** 662,667 **** --- 669,677 ---- errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); + /* SELinux check db_sequence:{get_value} */ + sepgsql_sequence_get_value(elm->relid); + if (!elm->last_valid) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), *************** lastval(PG_FUNCTION_ARGS) *** 706,711 **** --- 716,724 ---- errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); + /* SELinux check db_sequence:{get_value} */ + sepgsql_sequence_get_value(last_used_seq->relid); + result = last_used_seq->last; relation_close(seqrel, NoLock); *************** do_setval(Oid relid, int64 next, bool is *** 742,747 **** --- 755,763 ---- errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); + /* SELinux check db_sequence:{set_value} */ + sepgsql_sequence_set_value(elm->relid); + /* lock page' buffer and read tuple */ seq = read_info(elm, seqrel, &buf); diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/tablecmds.c *** base/src/backend/commands/tablecmds.c Sun Sep 6 19:40:49 2009 --- sepgsql/src/backend/commands/tablecmds.c Thu Oct 8 09:29:32 2009 *************** *** 62,67 **** --- 62,68 ---- #include "parser/parser.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" + #include "security/sepgsql.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/smgr.h" *************** static void ATExecCmd(List **wqueue, Alt *** 260,267 **** static void ATRewriteTables(List **wqueue); static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap); static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel); ! static void ATSimplePermissions(Relation rel, bool allowView); ! static void ATSimplePermissionsRelationOrIndex(Relation rel); static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse); static void ATOneLevelRecursion(List **wqueue, Relation rel, --- 261,268 ---- static void ATRewriteTables(List **wqueue); static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap); static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel); ! static void ATSimplePermissions(Relation rel, const char *colname, bool allowView); ! static void ATSimplePermissionsRelationOrIndex(Relation rel, const char *colname); static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse); static void ATOneLevelRecursion(List **wqueue, Relation rel, *************** DefineRelation(CreateStmt *stmt, char re *** 351,356 **** --- 352,358 ---- List *rawDefaults; List *cookedDefaults; Datum reloptions; + Oid *secLabels; ListCell *listptr; AttrNumber attnum; static char *validnsps[] = HEAP_RELOPT_NAMESPACES; *************** DefineRelation(CreateStmt *stmt, char re *** 444,449 **** --- 446,461 ---- localHasOids = interpretOidsOption(stmt->options); descriptor->tdhasoid = (localHasOids || parentOidCount > 0); + /* SELinux checks db_table:{create} and db_column:{create} */ + secLabels = sepgsql_relation_create(relname, + relkind, + descriptor, + namespaceId, + (DefElem *)stmt->secLabel, + schema, + false, + true); + /* * Find columns with default values and prepare for insertion of the * defaults. Pre-cooked (that is, inherited) defaults go into a list of *************** DefineRelation(CreateStmt *stmt, char re *** 513,519 **** parentOidCount, stmt->oncommit, reloptions, ! allowSystemTableMods); StoreCatalogInheritance(relationId, inheritOids); --- 525,532 ---- parentOidCount, stmt->oncommit, reloptions, ! allowSystemTableMods, ! secLabels); StoreCatalogInheritance(relationId, inheritOids); *************** ExecuteTruncate(TruncateStmt *stmt) *** 887,892 **** --- 900,907 ---- if (!pg_class_ownercheck(seq_relid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(seq_rel)); + /* SELinux checks */ + sepgsql_relation_alter(seq_relid, NULL, InvalidOid); seq_relids = lappend_oid(seq_relids, seq_relid); *************** truncate_check_rel(Relation rel) *** 1042,1047 **** --- 1057,1065 ---- errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(rel)))); + /* SELinux: check db_table:{delete} permission */ + sepgsql_relation_truncate(rel); + /* * We can never allow truncation of shared or nailed-in-cache relations, * because we can't support changing their relfilenode values. *************** MergeAttributes(List *schema, List *supe *** 1216,1221 **** --- 1234,1241 ---- if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(relation)); + /* SELinux checks db_table:{setattr} */ + sepgsql_relation_alter(RelationGetRelid(relation), NULL, InvalidOid); /* * Reject duplications in the list of parents. *************** renameatt(Oid myrelid, *** 1921,1926 **** --- 1941,1949 ---- errmsg("cannot rename system column \"%s\"", oldattname))); + /* SELinux checks db_column:{setattr} */ + sepgsql_attribute_alter(myrelid, oldattname); + /* * if the attribute is inherited, forbid the renaming, unless we are * already inside a recursive rename. *************** RenameRelation(Oid myrelid, const char * *** 2026,2031 **** --- 2049,2057 ---- Oid namespaceId; char relkind; + /* SELinux checks */ + sepgsql_relation_alter(myrelid, newrelname, InvalidOid); + /* * Grab an exclusive lock on the target table, index, sequence or view, * which we will NOT release until end of transaction. *************** ATPrepCmd(List **wqueue, Relation rel, A *** 2359,2372 **** switch (cmd->subtype) { case AT_AddColumn: /* ADD COLUMN */ ! ATSimplePermissions(rel, false); /* Performs own recursion */ ATPrepAddColumn(wqueue, rel, recurse, cmd); pass = AT_PASS_ADD_COL; break; case AT_AddColumnToView: /* add column via CREATE OR REPLACE * VIEW */ ! ATSimplePermissions(rel, true); /* Performs own recursion */ ATPrepAddColumn(wqueue, rel, recurse, cmd); pass = AT_PASS_ADD_COL; --- 2385,2398 ---- switch (cmd->subtype) { case AT_AddColumn: /* ADD COLUMN */ ! ATSimplePermissions(rel, NULL, false); /* Performs own recursion */ ATPrepAddColumn(wqueue, rel, recurse, cmd); pass = AT_PASS_ADD_COL; break; case AT_AddColumnToView: /* add column via CREATE OR REPLACE * VIEW */ ! ATSimplePermissions(rel, NULL, true); /* Performs own recursion */ ATPrepAddColumn(wqueue, rel, recurse, cmd); pass = AT_PASS_ADD_COL; *************** ATPrepCmd(List **wqueue, Relation rel, A *** 2379,2397 **** * substitutes default values into INSERTs before it expands * rules. */ ! ATSimplePermissions(rel, true); ATSimpleRecursion(wqueue, rel, cmd, recurse); /* No command-specific prep needed */ pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP; break; case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ ! ATSimplePermissions(rel, false); ATSimpleRecursion(wqueue, rel, cmd, recurse); /* No command-specific prep needed */ pass = AT_PASS_DROP; break; case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */ ! ATSimplePermissions(rel, false); ATSimpleRecursion(wqueue, rel, cmd, recurse); /* No command-specific prep needed */ pass = AT_PASS_ADD_CONSTR; --- 2405,2423 ---- * substitutes default values into INSERTs before it expands * rules. */ ! ATSimplePermissions(rel, cmd->name, true); ATSimpleRecursion(wqueue, rel, cmd, recurse); /* No command-specific prep needed */ pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP; break; case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ ! ATSimplePermissions(rel, cmd->name, false); ATSimpleRecursion(wqueue, rel, cmd, recurse); /* No command-specific prep needed */ pass = AT_PASS_DROP; break; case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */ ! ATSimplePermissions(rel, cmd->name, false); ATSimpleRecursion(wqueue, rel, cmd, recurse); /* No command-specific prep needed */ pass = AT_PASS_ADD_CONSTR; *************** ATPrepCmd(List **wqueue, Relation rel, A *** 2403,2415 **** pass = AT_PASS_COL_ATTRS; break; case AT_SetStorage: /* ALTER COLUMN STORAGE */ ! ATSimplePermissions(rel, false); ATSimpleRecursion(wqueue, rel, cmd, recurse); /* No command-specific prep needed */ pass = AT_PASS_COL_ATTRS; break; case AT_DropColumn: /* DROP COLUMN */ ! ATSimplePermissions(rel, false); /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) --- 2429,2441 ---- pass = AT_PASS_COL_ATTRS; break; case AT_SetStorage: /* ALTER COLUMN STORAGE */ ! ATSimplePermissions(rel, cmd->name, false); ATSimpleRecursion(wqueue, rel, cmd, recurse); /* No command-specific prep needed */ pass = AT_PASS_COL_ATTRS; break; case AT_DropColumn: /* DROP COLUMN */ ! ATSimplePermissions(rel, NULL, false); /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) *************** ATPrepCmd(List **wqueue, Relation rel, A *** 2417,2429 **** pass = AT_PASS_DROP; break; case AT_AddIndex: /* ADD INDEX */ ! ATSimplePermissions(rel, false); /* This command never recurses */ /* No command-specific prep needed */ pass = AT_PASS_ADD_INDEX; break; case AT_AddConstraint: /* ADD CONSTRAINT */ ! ATSimplePermissions(rel, false); /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) --- 2443,2455 ---- pass = AT_PASS_DROP; break; case AT_AddIndex: /* ADD INDEX */ ! ATSimplePermissions(rel, NULL, false); /* This command never recurses */ /* No command-specific prep needed */ pass = AT_PASS_ADD_INDEX; break; case AT_AddConstraint: /* ADD CONSTRAINT */ ! ATSimplePermissions(rel, NULL, false); /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) *************** ATPrepCmd(List **wqueue, Relation rel, A *** 2431,2437 **** pass = AT_PASS_ADD_CONSTR; break; case AT_DropConstraint: /* DROP CONSTRAINT */ ! ATSimplePermissions(rel, false); /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) --- 2457,2463 ---- pass = AT_PASS_ADD_CONSTR; break; case AT_DropConstraint: /* DROP CONSTRAINT */ ! ATSimplePermissions(rel, NULL, false); /* Recursion occurs during execution phase */ /* No command-specific prep needed except saving recurse flag */ if (recurse) *************** ATPrepCmd(List **wqueue, Relation rel, A *** 2439,2445 **** pass = AT_PASS_DROP; break; case AT_AlterColumnType: /* ALTER COLUMN TYPE */ ! ATSimplePermissions(rel, false); /* Performs own recursion */ ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd); pass = AT_PASS_ALTER_TYPE; --- 2465,2471 ---- pass = AT_PASS_DROP; break; case AT_AlterColumnType: /* ALTER COLUMN TYPE */ ! ATSimplePermissions(rel, cmd->name, false); /* Performs own recursion */ ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd); pass = AT_PASS_ALTER_TYPE; *************** ATPrepCmd(List **wqueue, Relation rel, A *** 2451,2470 **** break; case AT_ClusterOn: /* CLUSTER ON */ case AT_DropCluster: /* SET WITHOUT CLUSTER */ ! ATSimplePermissions(rel, false); /* These commands never recurse */ /* No command-specific prep needed */ pass = AT_PASS_MISC; break; case AT_AddOids: /* SET WITH OIDS */ ! ATSimplePermissions(rel, false); /* Performs own recursion */ if (!rel->rd_rel->relhasoids || recursing) ATPrepAddOids(wqueue, rel, recurse, cmd); pass = AT_PASS_ADD_COL; break; case AT_DropOids: /* SET WITHOUT OIDS */ ! ATSimplePermissions(rel, false); /* Performs own recursion */ if (rel->rd_rel->relhasoids) { --- 2477,2496 ---- break; case AT_ClusterOn: /* CLUSTER ON */ case AT_DropCluster: /* SET WITHOUT CLUSTER */ ! ATSimplePermissions(rel, NULL, false); /* These commands never recurse */ /* No command-specific prep needed */ pass = AT_PASS_MISC; break; case AT_AddOids: /* SET WITH OIDS */ ! ATSimplePermissions(rel, NULL, false); /* Performs own recursion */ if (!rel->rd_rel->relhasoids || recursing) ATPrepAddOids(wqueue, rel, recurse, cmd); pass = AT_PASS_ADD_COL; break; case AT_DropOids: /* SET WITHOUT OIDS */ ! ATSimplePermissions(rel, NULL, false); /* Performs own recursion */ if (rel->rd_rel->relhasoids) { *************** ATPrepCmd(List **wqueue, Relation rel, A *** 2478,2491 **** pass = AT_PASS_DROP; break; case AT_SetTableSpace: /* SET TABLESPACE */ ! ATSimplePermissionsRelationOrIndex(rel); /* This command never recurses */ ATPrepSetTableSpace(tab, rel, cmd->name); pass = AT_PASS_MISC; /* doesn't actually matter */ break; case AT_SetRelOptions: /* SET (...) */ case AT_ResetRelOptions: /* RESET (...) */ ! ATSimplePermissionsRelationOrIndex(rel); /* This command never recurses */ /* No command-specific prep needed */ pass = AT_PASS_MISC; --- 2504,2517 ---- pass = AT_PASS_DROP; break; case AT_SetTableSpace: /* SET TABLESPACE */ ! ATSimplePermissionsRelationOrIndex(rel, NULL); /* This command never recurses */ ATPrepSetTableSpace(tab, rel, cmd->name); pass = AT_PASS_MISC; /* doesn't actually matter */ break; case AT_SetRelOptions: /* SET (...) */ case AT_ResetRelOptions: /* RESET (...) */ ! ATSimplePermissionsRelationOrIndex(rel, NULL); /* This command never recurses */ /* No command-specific prep needed */ pass = AT_PASS_MISC; *************** ATPrepCmd(List **wqueue, Relation rel, A *** 2504,2510 **** case AT_DisableRule: case AT_AddInherit: /* INHERIT / NO INHERIT */ case AT_DropInherit: ! ATSimplePermissions(rel, false); /* These commands never recurse */ /* No command-specific prep needed */ pass = AT_PASS_MISC; --- 2530,2536 ---- case AT_DisableRule: case AT_AddInherit: /* INHERIT / NO INHERIT */ case AT_DropInherit: ! ATSimplePermissions(rel, NULL, false); /* These commands never recurse */ /* No command-specific prep needed */ pass = AT_PASS_MISC; *************** ATRewriteTables(List **wqueue) *** 2850,2857 **** /* * The new relation is local to our transaction and we know * nothing depends on it, so DROP_RESTRICT should be OK. */ ! performDeletion(&object, DROP_RESTRICT); /* performDeletion does CommandCounterIncrement at end */ /* --- 2876,2884 ---- /* * The new relation is local to our transaction and we know * nothing depends on it, so DROP_RESTRICT should be OK. + * SELinux does not apply any permission checks here. */ ! performDeletionNoPerms(&object, DROP_RESTRICT); /* performDeletion does CommandCounterIncrement at end */ /* *************** ATRewriteTable(AlteredTableInfo *tab, Oi *** 3076,3086 **** --- 3103,3116 ---- if (newrel) { Oid tupOid = InvalidOid; + Oid tupSecid = InvalidOid; /* Extract data from old tuple */ heap_deform_tuple(tuple, oldTupDesc, values, isnull); if (oldTupDesc->tdhasoid) tupOid = HeapTupleGetOid(tuple); + if (HeapTupleHasSecid(tuple)) + tupSecid = HeapTupleGetSecid(tuple); /* Set dropped attributes to null in new tuple */ foreach(lc, dropped_attrs) *************** ATRewriteTable(AlteredTableInfo *tab, Oi *** 3112,3117 **** --- 3142,3150 ---- /* Preserve OID, if any */ if (newTupDesc->tdhasoid) HeapTupleSetOid(tuple, tupOid); + /* Preserve SID, if any */ + if (HeapTupleHasSecid(tuple)) + HeapTupleSetSecid(tuple, tupSecid); } /* Now check any constraints on the possibly-changed tuple */ *************** ATGetQueueEntry(List **wqueue, Relation *** 3213,3219 **** * - Ensure that it is not a system table */ static void ! ATSimplePermissions(Relation rel, bool allowView) { if (rel->rd_rel->relkind != RELKIND_RELATION) { --- 3246,3252 ---- * - Ensure that it is not a system table */ static void ! ATSimplePermissions(Relation rel, const char *colName, bool allowView) { if (rel->rd_rel->relkind != RELKIND_RELATION) { *************** ATSimplePermissions(Relation rel, bool a *** 3237,3242 **** --- 3270,3281 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(rel)); + /* SELinux checks */ + if (!colName) + sepgsql_relation_alter(RelationGetRelid(rel), NULL, InvalidOid); + else + sepgsql_attribute_alter(RelationGetRelid(rel), colName); + if (!allowSystemTableMods && IsSystemRelation(rel)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), *************** ATSimplePermissions(Relation rel, bool a *** 3252,3258 **** * - Ensure that it is not a system table */ static void ! ATSimplePermissionsRelationOrIndex(Relation rel) { if (rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_INDEX) --- 3291,3297 ---- * - Ensure that it is not a system table */ static void ! ATSimplePermissionsRelationOrIndex(Relation rel, const char *colName) { if (rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_INDEX) *************** ATSimplePermissionsRelationOrIndex(Relat *** 3266,3271 **** --- 3305,3316 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(rel)); + /* SELinux checks */ + if (!colName) + sepgsql_relation_alter(RelationGetRelid(rel), NULL, InvalidOid); + else + sepgsql_attribute_alter(RelationGetRelid(rel), colName); + if (!allowSystemTableMods && IsSystemRelation(rel)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), *************** ATExecAddColumn(AlteredTableInfo *tab, R *** 3509,3514 **** --- 3554,3560 ---- HeapTuple typeTuple; Oid typeOid; int32 typmod; + Oid attsecid; Form_pg_type tform; Expr *defval; *************** ATExecAddColumn(AlteredTableInfo *tab, R *** 3546,3551 **** --- 3592,3600 ---- errmsg("child table \"%s\" has a conflicting \"%s\" column", RelationGetRelationName(rel), colDef->colname))); + /* SELinux checks db_column:{setattr} */ + sepgsql_attribute_alter(myrelid, colDef->colname); + /* Bump the existing child att's inhcount */ childatt->attinhcount++; simple_heap_update(attrdesc, &tuple->t_self, tuple); *************** ATExecAddColumn(AlteredTableInfo *tab, R *** 3585,3590 **** --- 3634,3642 ---- errmsg("column \"%s\" of relation \"%s\" already exists", colDef->colname, RelationGetRelationName(rel)))); + /* SELinux checks db_column:{create} */ + attsecid = sepgsql_attribute_create(myrelid, colDef); + /* Determine the new attribute's number */ if (isOid) newattnum = ObjectIdAttributeNumber; *************** ATExecAddColumn(AlteredTableInfo *tab, R *** 3627,3633 **** ReleaseSysCache(typeTuple); ! InsertPgAttributeTuple(attrdesc, &attribute, NULL); heap_close(attrdesc, RowExclusiveLock); --- 3679,3685 ---- ReleaseSysCache(typeTuple); ! InsertPgAttributeTuple(attrdesc, &attribute, NULL, attsecid); heap_close(attrdesc, RowExclusiveLock); *************** ATPrepSetStatistics(Relation rel, const *** 4016,4021 **** --- 4068,4075 ---- if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(rel)); + /* SELinux checks */ + sepgsql_attribute_alter(RelationGetRelid(rel), colName); } static void *************** ATExecDropColumn(List **wqueue, Relation *** 4171,4177 **** /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) ! ATSimplePermissions(rel, false); /* * get the number of the attribute --- 4225,4231 ---- /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) ! ATSimplePermissions(rel, NULL, false); /* * get the number of the attribute *************** ATAddCheckConstraint(List **wqueue, Alte *** 4473,4479 **** /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) ! ATSimplePermissions(rel, false); /* * Call AddRelationNewConstraints to do the work, making sure it works on --- 4527,4533 ---- /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) ! ATSimplePermissions(rel, NULL, false); /* * Call AddRelationNewConstraints to do the work, making sure it works on *************** checkFkeyPermissions(Relation rel, int16 *** 5102,5108 **** aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid, ACL_REFERENCES); if (aclresult == ACLCHECK_OK) ! return; /* Else we must have REFERENCES on each column */ for (i = 0; i < natts; i++) { --- 5156,5162 ---- aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid, ACL_REFERENCES); if (aclresult == ACLCHECK_OK) ! goto ok; /* Else we must have REFERENCES on each column */ for (i = 0; i < natts; i++) { *************** checkFkeyPermissions(Relation rel, int16 *** 5112,5117 **** --- 5166,5174 ---- aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(rel)); } + ok: + /* SELinux: check db_table/db_column:{reference} */ + sepgsql_relation_references(rel, attnums, natts); } /* *************** ATExecDropConstraint(Relation rel, const *** 5375,5381 **** /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) ! ATSimplePermissions(rel, false); conrel = heap_open(ConstraintRelationId, RowExclusiveLock); --- 5432,5438 ---- /* At top level, permission check was done in ATPrepCmd, else do it */ if (recursing) ! ATSimplePermissions(rel, NULL, false); conrel = heap_open(ConstraintRelationId, RowExclusiveLock); *************** ATExecChangeOwner(Oid relationOid, Oid n *** 6308,6313 **** --- 6365,6372 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } + /* SELinux checks db_table:{setattr} */ + sepgsql_relation_alter(relationOid, NULL, InvalidOid); } memset(repl_null, false, sizeof(repl_null)); *************** ATExecAddInherit(Relation child_rel, Ran *** 6912,6918 **** * Must be owner of both parent and child -- child was checked by * ATSimplePermissions call in ATPrepCmd */ ! ATSimplePermissions(parent_rel, false); /* Permanent rels cannot inherit from temporary ones */ if (parent_rel->rd_istemp && !child_rel->rd_istemp) --- 6971,6977 ---- * Must be owner of both parent and child -- child was checked by * ATSimplePermissions call in ATPrepCmd */ ! ATSimplePermissions(parent_rel, NULL, false); /* Permanent rels cannot inherit from temporary ones */ if (parent_rel->rd_istemp && !child_rel->rd_istemp) *************** AlterTableNamespace(RangeVar *relation, *** 7570,7575 **** --- 7629,7637 ---- RelationGetRelationName(rel), newschema))); + /* SELinux checks */ + sepgsql_relation_alter(relid, NULL, nspOid); + /* disallow renaming into or out of temp schemas */ if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid)) ereport(ERROR, *************** AlterSeqNamespaces(Relation classRel, Re *** 7762,7767 **** --- 7824,7957 ---- 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 oldtup; + HeapTuple newtup; + Oid secid; + bool replaces[Natts_pg_class]; + + rel = heap_open(RelationRelationId, RowExclusiveLock); + oldtup = SearchSysCache(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (!HeapTupleIsValid(oldtup)) + elog(ERROR, "cache lookup failed for relation: %u", relid); + + memset(replaces, false, sizeof(replaces)); + newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), + NULL, NULL, replaces); + if (!HeapTupleHasSecid(newtup)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Unable to set security label on \"%s\"", + get_rel_name(relid)))); + + ReleaseSysCache(oldtup); + + /* SELinux checks db_table:{setattr relabelfrom relabelto} */ + secid = sepgsql_relation_relabel(relid, seclabel); + + HeapTupleSetSecid(newtup, secid); + + simple_heap_update(rel, &newtup->t_self, newtup); + + CatalogUpdateIndexes(rel, newtup); + + heap_freetuple(newtup); + + heap_close(rel, RowExclusiveLock); + } + + static void + ExecAttributeSetSecLabel(Oid relid, const char *attname, DefElem *seclabel) + { + Relation rel; + HeapTuple oldtup; + HeapTuple newtup; + AttrNumber attnum; + Oid secid; + bool replaces[Natts_pg_attribute]; + + rel = heap_open(AttributeRelationId, RowExclusiveLock); + oldtup = SearchSysCacheAttName(relid, attname); + if (!HeapTupleIsValid(oldtup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + attname, get_rel_name(relid)))); + attnum = ((Form_pg_attribute) GETSTRUCT(oldtup))->attnum; + + memset(replaces, false, sizeof(replaces)); + newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), + NULL, NULL, replaces); + if (!HeapTupleHasSecid(newtup)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Unable to set security context on \"%s.%s\"", + get_rel_name(relid), attname))); + + ReleaseSysCache(oldtup); + + /* SELinux checks db_column:{setattr relabelfrom relabelto} */ + secid = sepgsql_attribute_relabel(relid, attnum, seclabel); + + HeapTupleSetSecid(newtup, secid); + + simple_heap_update(rel, &newtup->t_self, newtup); + + CatalogUpdateIndexes(rel, newtup); + + heap_freetuple(newtup); + + heap_close(rel, RowExclusiveLock); + } + + void + AlterRelationSecLabel(RangeVar *relation, const char *attname, + ObjectType objtype, DefElem *seclabel) + { + Oid relid; + char relkind; + + /* Check relation type against type specified in the ALTER command */ + relid = RangeVarGetRelid(relation, false); + relkind = get_rel_relkind(relid); + + switch (objtype) + { + case OBJECT_TABLE: + case OBJECT_COLUMN: + if (relkind != RELKIND_RELATION) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table", get_rel_name(relid)))); + break; + + case OBJECT_SEQUENCE: + if (relkind != RELKIND_SEQUENCE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a sequence", get_rel_name(relid)))); + break; + + default: + elog(ERROR, "unrecognized object type: %d", (int)objtype); + break; + } + + /* Exec set security label */ + if (objtype != OBJECT_COLUMN) + ExecRelationSetSecLabel(relid, seclabel); + else + ExecAttributeSetSecLabel(relid, attname, seclabel); + } /* * This code supports diff -Nrpc base/src/backend/commands/trigger.c sepgsql/src/backend/commands/trigger.c *** base/src/backend/commands/trigger.c Fri Jun 19 13:40:37 2009 --- sepgsql/src/backend/commands/trigger.c Thu Sep 17 22:10:19 2009 *************** *** 33,38 **** --- 33,39 ---- #include "nodes/makefuncs.h" #include "parser/parse_func.h" #include "pgstat.h" + #include "security/sepgsql.h" #include "storage/bufmgr.h" #include "tcop/utility.h" #include "utils/acl.h" *************** CreateTrigger(CreateTrigStmt *stmt, Oid *** 182,187 **** --- 183,192 ---- NameListToString(stmt->funcname)))); } + /* SELinux checks */ + if (checkPermissions) + sepgsql_trigger_create(RelationGetRelid(rel), stmt->trigname, funcoid); + /* * If the command is a user-entered CREATE CONSTRAINT TRIGGER command that * references one of the built-in RI_FKey trigger functions, assume it is *************** DropTrigger(Oid relid, const char *trign *** 746,751 **** --- 751,757 ---- if (!pg_class_ownercheck(relid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, get_rel_name(relid)); + sepgsql_trigger_drop(relid, trigname); object.classId = TriggerRelationId; object.objectId = HeapTupleGetOid(tup); *************** renametrig(Oid relid, *** 862,867 **** --- 868,876 ---- */ targetrel = heap_open(relid, AccessExclusiveLock); + /* SELinux checks */ + sepgsql_trigger_alter(relid, oldname); + /* * Scan pg_trigger twice for existing triggers on relation. We do this in * order to ensure a trigger does not exist with newname (The unique index diff -Nrpc base/src/backend/commands/tsearchcmds.c sepgsql/src/backend/commands/tsearchcmds.c *** base/src/backend/commands/tsearchcmds.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/commands/tsearchcmds.c Thu Sep 17 23:44:07 2009 *************** *** 35,40 **** --- 35,41 ---- #include "miscadmin.h" #include "nodes/makefuncs.h" #include "parser/parse_func.h" + #include "security/sepgsql.h" #include "tsearch/ts_cache.h" #include "tsearch/ts_public.h" #include "tsearch/ts_utils.h" *************** DefineTSParser(List *names, List *parame *** 171,176 **** --- 172,178 ---- NameData pname; Oid prsOid; Oid namespaceoid; + Oid secid; if (!superuser()) ereport(ERROR, *************** DefineTSParser(List *names, List *parame *** 250,261 **** --- 252,273 ---- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("text search parser lextypes method is required"))); + /* Permission checks */ + secid = sepgsql_ts_parser_create(prsname, namespaceoid, + DatumGetObjectId(values[Anum_pg_ts_parser_prsstart - 1]), + DatumGetObjectId(values[Anum_pg_ts_parser_prstoken - 1]), + DatumGetObjectId(values[Anum_pg_ts_parser_prsend - 1]), + DatumGetObjectId(values[Anum_pg_ts_parser_prsheadline - 1]), + DatumGetObjectId(values[Anum_pg_ts_parser_prslextype - 1])); + /* * Looks good, insert */ prsRel = heap_open(TSParserRelationId, RowExclusiveLock); tup = heap_form_tuple(prsRel->rd_att, values, nulls); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, secid); prsOid = simple_heap_insert(prsRel, tup); *************** RenameTSParser(List *oldname, const char *** 372,377 **** --- 384,392 ---- prsId = TSParserGetPrsid(oldname, false); + /* SELinux checks */ + sepgsql_ts_parser_alter(prsId, newname); + tup = SearchSysCacheCopy(TSPARSEROID, ObjectIdGetDatum(prsId), 0, 0, 0); *************** DefineTSDictionary(List *names, List *pa *** 503,508 **** --- 518,524 ---- List *dictoptions = NIL; Oid dictOid; Oid namespaceoid; + Oid secid; AclResult aclresult; char *dictname; *************** DefineTSDictionary(List *names, List *pa *** 515,520 **** --- 531,539 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceoid)); + /* SELinux check */ + secid = sepgsql_ts_dict_create(dictname, namespaceoid); + /* * loop over the definition list and extract the information we need. */ *************** DefineTSDictionary(List *names, List *pa *** 563,568 **** --- 582,589 ---- dictRel = heap_open(TSDictionaryRelationId, RowExclusiveLock); tup = heap_form_tuple(dictRel->rd_att, values, nulls); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, secid); dictOid = simple_heap_insert(dictRel, tup); *************** RenameTSDictionary(List *oldname, const *** 621,626 **** --- 642,650 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* SELinux checks */ + sepgsql_ts_dict_alter(dictId, newname); + namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); *************** AlterTSDictionary(AlterTSDictionaryStmt *** 762,767 **** --- 786,794 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, NameListToString(stmt->dictname)); + /* SELinux checks */ + sepgsql_ts_dict_alter(dictId, NULL); + /* deserialize the existing set of options */ opt = SysCacheGetAttr(TSDICTOID, tup, Anum_pg_ts_dict_dictinitoption, *************** AlterTSDictionaryOwner(List *name, Oid n *** 889,894 **** --- 916,923 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } + /* SELinux checks */ + sepgsql_ts_dict_alter(dictId, NULL); form->dictowner = newOwnerId; *************** DefineTSTemplate(List *names, List *para *** 999,1004 **** --- 1028,1034 ---- NameData dname; int i; Oid dictOid; + Oid dictSecid; Oid namespaceoid; char *tmplname; *************** DefineTSTemplate(List *names, List *para *** 1054,1059 **** --- 1084,1094 ---- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("text search template lexize method is required"))); + /* SELinux checks */ + dictSecid = sepgsql_ts_template_create(tmplname, namespaceoid, + DatumGetObjectId(values[Anum_pg_ts_template_tmplinit - 1]), + DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1])); + /* * Looks good, insert */ *************** DefineTSTemplate(List *names, List *para *** 1061,1066 **** --- 1096,1103 ---- tmplRel = heap_open(TSTemplateRelationId, RowExclusiveLock); tup = heap_form_tuple(tmplRel->rd_att, values, nulls); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, dictSecid); dictOid = simple_heap_insert(tmplRel, tup); *************** RenameTSTemplate(List *oldname, const ch *** 1093,1098 **** --- 1130,1138 ---- tmplId = TSTemplateGetTmplid(oldname, false); + /* Permission checks */ + sepgsql_ts_template_alter(tmplId, newname); + tup = SearchSysCacheCopy(TSTEMPLATEOID, ObjectIdGetDatum(tmplId), 0, 0, 0); *************** DefineTSConfiguration(List *names, List *** 1335,1340 **** --- 1375,1381 ---- Oid sourceOid = InvalidOid; Oid prsOid = InvalidOid; Oid cfgOid; + Oid cfgSecid; ListCell *pl; /* Convert list of names to a name and namespace */ *************** DefineTSConfiguration(List *names, List *** 1399,1404 **** --- 1440,1448 ---- (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("text search parser is required"))); + /* SELinux checks */ + cfgSecid = sepgsql_ts_config_create(cfgname, namespaceoid); + /* * Looks good, build tuple and insert */ *************** DefineTSConfiguration(List *names, List *** 1414,1419 **** --- 1458,1465 ---- cfgRel = heap_open(TSConfigRelationId, RowExclusiveLock); tup = heap_form_tuple(cfgRel->rd_att, values, nulls); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, cfgSecid); cfgOid = simple_heap_insert(cfgRel, tup); *************** RenameTSConfiguration(List *oldname, con *** 1519,1524 **** --- 1565,1573 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* permission checks */ + sepgsql_ts_config_alter(cfgId, newname); + namestrcpy(&(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); *************** AlterTSConfigurationOwner(List *name, Oi *** 1690,1695 **** --- 1739,1746 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } + /* SELinux checks */ + sepgsql_ts_config_alter(cfgId, NULL); form->cfgowner = newOwnerId; *************** AlterTSConfiguration(AlterTSConfiguratio *** 1727,1732 **** --- 1778,1786 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, NameListToString(stmt->cfgname)); + /* SELinux checks */ + sepgsql_ts_config_alter(HeapTupleGetOid(tup), NULL); + relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock); /* Add or drop mappings */ diff -Nrpc base/src/backend/commands/typecmds.c sepgsql/src/backend/commands/typecmds.c *** base/src/backend/commands/typecmds.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/commands/typecmds.c Thu Sep 17 22:10:19 2009 *************** *** 56,61 **** --- 56,62 ---- #include "parser/parse_expr.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" *************** AlterDomainDefault(List *names, Node *de *** 1543,1548 **** --- 1544,1550 ---- /* Check it's a domain and check user has permission for ALTER DOMAIN */ checkDomainOwner(tup, typename); + sepgsql_type_alter(domainoid, NULL, InvalidOid); /* Setup new tuple */ MemSet(new_record, (Datum) 0, sizeof(new_record)); *************** AlterDomainNotNull(List *names, bool not *** 1671,1676 **** --- 1673,1679 ---- /* Check it's a domain and check user has permission for ALTER DOMAIN */ checkDomainOwner(tup, typename); + sepgsql_type_alter(domainoid, NULL, InvalidOid); /* Is the domain already set to the desired constraint? */ if (typTup->typnotnull == notNull) *************** AlterDomainDropConstraint(List *names, c *** 1772,1777 **** --- 1775,1781 ---- /* Check it's a domain and check user has permission for ALTER DOMAIN */ checkDomainOwner(tup, typename); + sepgsql_type_alter(domainoid, NULL, InvalidOid); /* Grab an appropriate lock on the pg_constraint relation */ conrel = heap_open(ConstraintRelationId, RowExclusiveLock); *************** AlterDomainAddConstraint(List *names, No *** 1848,1853 **** --- 1852,1858 ---- /* Check it's a domain and check user has permission for ALTER DOMAIN */ checkDomainOwner(tup, typename); + sepgsql_type_alter(domainoid, NULL, InvalidOid); /* Check for unsupported constraint types */ if (IsA(newConstraint, FkConstraint)) *************** RenameType(List *names, const char *newT *** 2470,2475 **** --- 2475,2483 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, format_type_be(typeOid)); + /* SELinux check permission */ + sepgsql_type_alter(typeOid, newTypeName, InvalidOid); + /* * If it's a composite type, we need to check that it really is a * free-standing composite type, and not a table's rowtype. We want people *************** AlterTypeOwner(List *names, Oid newOwner *** 2590,2595 **** --- 2598,2605 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(typTup->typnamespace)); } + /* SELinux checks permissions */ + sepgsql_type_alter(HeapTupleGetOid(tup), NULL, InvalidOid); /* * If it's a composite type, invoke ATExecChangeOwner so that we fix *************** AlterTypeNamespace(List *names, const ch *** 2706,2711 **** --- 2716,2724 ---- errhint("You can alter type %s, which will alter the array type as well.", format_type_be(elemOid)))); + /* SELinux checks permissions */ + sepgsql_type_alter(typeOid, NULL, nspOid); + /* and do the work */ AlterTypeNamespaceInternal(typeOid, nspOid, false, true); } diff -Nrpc base/src/backend/commands/view.c sepgsql/src/backend/commands/view.c *** base/src/backend/commands/view.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/commands/view.c Fri Sep 18 14:51:00 2009 *************** *** 28,33 **** --- 28,34 ---- #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" #include "rewrite/rewriteSupport.h" + #include "security/sepgsql.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" *************** DefineVirtualRelation(const RangeVar *re *** 166,171 **** --- 167,175 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(rel)); + /* SELinux checks */ + sepgsql_view_replace(viewOid); + /* Also check it's not in use already */ CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW"); diff -Nrpc base/src/backend/executor/execJunk.c sepgsql/src/backend/executor/execJunk.c *** base/src/backend/executor/execJunk.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/executor/execJunk.c Wed Jul 15 19:30:50 2009 *************** *** 60,66 **** * An optional resultSlot can be passed as well. */ JunkFilter * ! ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) { JunkFilter *junkfilter; TupleDesc cleanTupType; --- 60,67 ---- * An optional resultSlot can be passed as well. */ JunkFilter * ! ExecInitJunkFilter(List *targetList, bool hasoid, bool hasseclabel, ! TupleTableSlot *slot) { JunkFilter *junkfilter; TupleDesc cleanTupType; *************** ExecInitJunkFilter(List *targetList, boo *** 72,78 **** /* * Compute the tuple descriptor for the cleaned tuple. */ ! cleanTupType = ExecCleanTypeFromTL(targetList, hasoid); /* * Use the given slot, or make a new slot if we weren't given one. --- 73,79 ---- /* * Compute the tuple descriptor for the cleaned tuple. */ ! cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, hasseclabel); /* * Use the given slot, or make a new slot if we weren't given one. diff -Nrpc base/src/backend/executor/execMain.c sepgsql/src/backend/executor/execMain.c *** base/src/backend/executor/execMain.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/executor/execMain.c Thu Oct 8 09:29:32 2009 *************** *** 39,44 **** --- 39,45 ---- #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" *************** *** 50,55 **** --- 51,57 ---- #include "optimizer/clauses.h" #include "parser/parse_clause.h" #include "parser/parsetree.h" + #include "security/sepgsql.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/smgr.h" *************** ExecCheckRTPerms(List *rangeTable) *** 442,448 **** foreach(l, rangeTable) { ! ExecCheckRTEPerms((RangeTblEntry *) lfirst(l)); } } --- 444,453 ---- foreach(l, rangeTable) { ! RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); ! ! ExecCheckRTEPerms(rte); ! sepgsqlCheckRTEPerms(rte); } } *************** InitPlan(QueryDesc *queryDesc, int eflag *** 901,916 **** for (i = 0; i < as_nplans; i++) { PlanState *subplan = appendplans[i]; JunkFilter *j; if (operation == CMD_UPDATE) ! ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, ! subplan->plan->targetlist); j = ExecInitJunkFilter(subplan->plan->targetlist, ! resultRelInfo->ri_RelationDesc->rd_att->tdhasoid, ! ExecAllocTableSlot(estate->es_tupleTable)); ! /* * Since it must be UPDATE/DELETE, there had better be a * "ctid" junk attribute in the tlist ... but ctid could --- 906,921 ---- for (i = 0; i < as_nplans; i++) { PlanState *subplan = appendplans[i]; + Relation resultRel = resultRelInfo->ri_RelationDesc; JunkFilter *j; if (operation == CMD_UPDATE) ! ExecCheckPlanOutput(resultRel, subplan->plan->targetlist); j = ExecInitJunkFilter(subplan->plan->targetlist, ! RelationGetDescr(resultRel)->tdhasoid, ! RelationGetDescr(resultRel)->tdhassecid, ! ExecAllocTableSlot(estate->es_tupleTable)); /* * Since it must be UPDATE/DELETE, there had better be a * "ctid" junk attribute in the tlist ... but ctid could *************** InitPlan(QueryDesc *queryDesc, int eflag *** 953,958 **** --- 958,964 ---- j = ExecInitJunkFilter(planstate->plan->targetlist, tupType->tdhasoid, + tupType->tdhassecid, ExecAllocTableSlot(estate->es_tupleTable)); estate->es_junkFilter = j; if (estate->es_result_relation_info) *************** InitPlan(QueryDesc *queryDesc, int eflag *** 1023,1029 **** * We assume all the sublists will generate the same output tupdesc. */ tupType = ExecTypeFromTL((List *) linitial(plannedstmt->returningLists), ! false); /* Set up a slot for the output of the RETURNING projection(s) */ slot = ExecAllocTableSlot(estate->es_tupleTable); --- 1029,1035 ---- * We assume all the sublists will generate the same output tupdesc. */ tupType = ExecTypeFromTL((List *) linitial(plannedstmt->returningLists), ! false, false); /* Set up a slot for the output of the RETURNING projection(s) */ slot = ExecAllocTableSlot(estate->es_tupleTable); *************** ExecContextForcesOids(PlanState *plansta *** 1346,1351 **** --- 1352,1388 ---- return false; } + /* + * ExecContextForcesSecids + * + * We need to ensure that result tuples have space for security identifier. + * if the security feature need to store it within the given relation. + */ + bool ExecContextForcesSecids(PlanState *planstate, bool *hassecid) + { + if (planstate->state->es_select_into) + { + *hassecid = securityTupleDescHasSecid(InvalidOid, + RELKIND_RELATION); + return true; + } + else + { + ResultRelInfo *ri = planstate->state->es_result_relation_info; + + if (ri && ri->ri_RelationDesc) + { + Oid relid = RelationGetRelid(ri->ri_RelationDesc); + char relkind = RelationGetForm(ri->ri_RelationDesc)->relkind; + + *hassecid = securityTupleDescHasSecid(relid, relkind); + + return true; + } + } + return false; + } + /* ---------------------------------------------------------------- * ExecEndPlan * *************** ExecEndPlan(PlanState *planstate, EState *** 1426,1431 **** --- 1463,1520 ---- } } + /* + * 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, SecurityAttributeName); + if (attno != InvalidAttrNumber) + { + datum = ExecGetJunkAttribute(slot, attno, &isnull); + if (isnull) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Unable to set NULL on \"%s\"", + SecurityAttributeName))); + *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 (!HeapTupleHasSecid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Unable to assign security label on \"%s\"", + RelationGetRelationName(rel)))); + secid = securityTransSecLabelIn(relid, seclabel); + HeapTupleSetSecid(tuple, secid); + } + else if (HeapTupleHasSecid(tuple)) + HeapTupleSetSecid(tuple, InvalidOid); + } + /* ---------------------------------------------------------------- * ExecutePlan * *************** ExecutePlan(EState *estate, *** 1487,1492 **** --- 1576,1583 ---- */ for (;;) { + Datum tts_seclabel = PointerGetDatum(NULL); + /* Reset the per-output-tuple exprcontext */ ResetPerTupleExprContext(estate); *************** lnext: ; *** 1631,1636 **** --- 1722,1732 ---- } /* + * extract writable system attribute + */ + fetchWritableSystemAttribute(junkfilter, slot, &tts_seclabel); + + /* * extract the 'ctid' junk attribute. */ if (operation == CMD_UPDATE || operation == CMD_DELETE) *************** lnext: ; *** 1657,1662 **** --- 1753,1759 ---- 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 *************** ExecInsert(TupleTableSlot *slot, *** 1781,1786 **** --- 1878,1885 ---- if (resultRelationDesc->rd_rel->relhasoids) HeapTupleSetOid(tuple, InvalidOid); + storeWritableSystemAttribute(resultRelationDesc, slot, tuple); + /* BEFORE ROW INSERT Triggers */ if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) *************** ExecInsert(TupleTableSlot *slot, *** 1811,1816 **** --- 1910,1921 ---- } /* + * SELinux assigns default security label, and + * it also checks db_tuple:{insert} permission + */ + sepgsqlHeapTupleInsert(resultRelationDesc, tuple, false); + + /* * Check the constraints of the tuple */ if (resultRelationDesc->rd_att->constr) *************** ExecUpdate(TupleTableSlot *slot, *** 2018,2023 **** --- 2123,2130 ---- 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) *************** ExecUpdate(TupleTableSlot *slot, *** 2048,2053 **** --- 2155,2163 ---- } } + /* SELinux checks db_tuple:{relabelfrom relabelto}, if needed */ + sepgsqlHeapTupleUpdate(resultRelationDesc, tupleid, tuple); + /* * Check the constraints of the tuple * *************** OpenIntoRel(QueryDesc *queryDesc) *** 2843,2848 **** --- 2953,2959 ---- Oid namespaceId; Oid tablespaceId; Datum reloptions; + Oid *secLabels; AclResult aclresult; Oid intoRelationId; TupleDesc tupdesc; *************** OpenIntoRel(QueryDesc *queryDesc) *** 2871,2876 **** --- 2982,2995 ---- aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceId)); + /* SELinux checks */ + secLabels = sepgsql_relation_create(intoName, + RELKIND_RELATION, + queryDesc->tupDesc, + namespaceId, + NULL, NIL, + true, true); + /* * Select tablespace to use. If not specified, use default tablespace * (which may in turn default to database's default). *************** OpenIntoRel(QueryDesc *queryDesc) *** 2929,2935 **** 0, into->onCommit, reloptions, ! allowSystemTableMods); FreeTupleDesc(tupdesc); --- 3048,3055 ---- 0, into->onCommit, reloptions, ! allowSystemTableMods, ! secLabels); FreeTupleDesc(tupdesc); *************** intorel_receive(TupleTableSlot *slot, De *** 3054,3059 **** --- 3174,3183 ---- if (myState->rel->rd_rel->relhasoids) HeapTupleSetOid(tuple, InvalidOid); + storeWritableSystemAttribute(myState->rel, slot, tuple); + /* SELinux checks db_tuple:{insert} */ + sepgsqlHeapTupleInsert(myState->rel, tuple, false); + heap_insert(myState->rel, tuple, myState->estate->es_output_cid, diff -Nrpc base/src/backend/executor/execQual.c sepgsql/src/backend/executor/execQual.c *** base/src/backend/executor/execQual.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/executor/execQual.c Thu Sep 17 17:04:16 2009 *************** *** 47,52 **** --- 47,53 ---- #include "nodes/nodeFuncs.h" #include "optimizer/planner.h" #include "pgstat.h" + #include "security/sepgsql.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" *************** init_fcache(Oid foid, FuncExprState *fca *** 1034,1039 **** --- 1035,1041 ---- aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid)); + sepgsql_proc_execute(foid); /* * Safety check on nargs. Under normal circumstances this should never *************** ExecEvalArrayCoerceExpr(ArrayCoerceExprS *** 4032,4037 **** --- 4034,4040 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(acoerce->elemfuncid)); + sepgsql_proc_execute(acoerce->elemfuncid); /* Set up the primary fmgr lookup information */ fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc), diff -Nrpc base/src/backend/executor/execScan.c sepgsql/src/backend/executor/execScan.c *** base/src/backend/executor/execScan.c Thu Apr 9 00:13:21 2009 --- sepgsql/src/backend/executor/execScan.c Wed Sep 9 13:14:37 2009 *************** *** 20,25 **** --- 20,26 ---- #include "executor/executor.h" #include "miscadmin.h" + #include "security/rowlevel.h" #include "utils/memutils.h" *************** ExecScan(ScanState *node, *** 53,58 **** --- 54,60 ---- ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; + Scan *scan = (Scan *)node->ps.plan; /* * Fetch data from node *************** ExecScan(ScanState *node, *** 64,70 **** * 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) return (*accessMtd) (node); /* --- 66,72 ---- * 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 && !scan->rowlvPerms) return (*accessMtd) (node); /* *************** ExecScan(ScanState *node, *** 128,136 **** * when the qual is nil ... saves only a few cycles, but they add up * ... */ ! if (!qual || ExecQual(qual, econtext, false)) { /* * Found a satisfactory scan tuple. */ if (projInfo) --- 130,147 ---- * when the qual is nil ... saves only a few cycles, but they add up * ... */ ! 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) *************** tlist_matches_tupdesc(PlanState *ps, Lis *** 197,202 **** --- 208,214 ---- int numattrs = tupdesc->natts; int attrno; bool hasoid; + bool hassecid; ListCell *tlist_item = list_head(tlist); /* Check the tlist attributes */ *************** tlist_matches_tupdesc(PlanState *ps, Lis *** 240,251 **** return false; /* tlist too long */ /* ! * If the plan context requires a particular hasoid setting, then that has ! * to match, too. */ if (ExecContextForcesOids(ps, &hasoid) && hasoid != tupdesc->tdhasoid) return false; return true; } --- 252,267 ---- return false; /* tlist too long */ /* ! * If the plan context requires a particular hasoid or hassecid setting, ! * then that has to match, too. */ if (ExecContextForcesOids(ps, &hasoid) && hasoid != tupdesc->tdhasoid) return false; + if (ExecContextForcesSecids(ps, &hassecid) && + hassecid != tupdesc->tdhassecid) + return false; + return true; } diff -Nrpc base/src/backend/executor/execTuples.c sepgsql/src/backend/executor/execTuples.c *** base/src/backend/executor/execTuples.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/executor/execTuples.c Wed Sep 9 13:14:37 2009 *************** *** 100,106 **** static TupleDesc ExecTypeFromTLInternal(List *targetList, ! bool hasoid, bool skipjunk); /* ---------------------------------------------------------------- --- 100,106 ---- static TupleDesc ExecTypeFromTLInternal(List *targetList, ! bool hasoid, bool hasseclabel, bool skipjunk); /* ---------------------------------------------------------------- *************** ExecInitNullTupleSlot(EState *estate, Tu *** 968,976 **** * ---------------------------------------------------------------- */ TupleDesc ! ExecTypeFromTL(List *targetList, bool hasoid) { ! return ExecTypeFromTLInternal(targetList, hasoid, false); } /* ---------------------------------------------------------------- --- 968,976 ---- * ---------------------------------------------------------------- */ TupleDesc ! ExecTypeFromTL(List *targetList, bool hasoid, bool hassecid) { ! return ExecTypeFromTLInternal(targetList, hasoid, hassecid, false); } /* ---------------------------------------------------------------- *************** ExecTypeFromTL(List *targetList, bool ha *** 980,992 **** * ---------------------------------------------------------------- */ TupleDesc ! ExecCleanTypeFromTL(List *targetList, bool hasoid) { ! return ExecTypeFromTLInternal(targetList, hasoid, true); } static TupleDesc ! ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) { TupleDesc typeInfo; ListCell *l; --- 980,993 ---- * ---------------------------------------------------------------- */ TupleDesc ! ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hassecid) { ! return ExecTypeFromTLInternal(targetList, hasoid, hassecid, true); } static TupleDesc ! ExecTypeFromTLInternal(List *targetList, bool hasoid, ! bool hassecid, bool skipjunk) { TupleDesc typeInfo; ListCell *l; *************** ExecTypeFromTLInternal(List *targetList, *** 998,1003 **** --- 999,1005 ---- else len = ExecTargetListLength(targetList); typeInfo = CreateTemplateTupleDesc(len, hasoid); + typeInfo->tdhassecid = hassecid; foreach(l, targetList) { diff -Nrpc base/src/backend/executor/execUtils.c sepgsql/src/backend/executor/execUtils.c *** base/src/backend/executor/execUtils.c Sun Sep 6 19:40:49 2009 --- sepgsql/src/backend/executor/execUtils.c Wed Sep 9 13:14:37 2009 *************** void *** 512,517 **** --- 512,518 ---- ExecAssignResultTypeFromTL(PlanState *planstate) { bool hasoid; + bool hassecid; TupleDesc tupDesc; if (ExecContextForcesOids(planstate, &hasoid)) *************** ExecAssignResultTypeFromTL(PlanState *pl *** 524,535 **** hasoid = 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); ExecAssignResultType(planstate, tupDesc); } --- 525,539 ---- hasoid = false; } + if (!ExecContextForcesSecids(planstate, &hassecid)) + hassecid = 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, hassecid); ExecAssignResultType(planstate, tupDesc); } diff -Nrpc base/src/backend/executor/functions.c sepgsql/src/backend/executor/functions.c *** base/src/backend/executor/functions.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/executor/functions.c Wed Jul 15 19:30:50 2009 *************** check_sql_fn_retval(Oid func_id, Oid ret *** 1135,1141 **** /* Set up junk filter if needed */ if (junkFilter) ! *junkFilter = ExecInitJunkFilter(tlist, false, NULL); } else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) { --- 1135,1141 ---- /* Set up junk filter if needed */ if (junkFilter) ! *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); } else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) { *************** check_sql_fn_retval(Oid func_id, Oid ret *** 1167,1173 **** COERCE_DONTCARE); /* Set up junk filter if needed */ if (junkFilter) ! *junkFilter = ExecInitJunkFilter(tlist, false, NULL); return false; /* NOT returning whole tuple */ } } --- 1167,1173 ---- COERCE_DONTCARE); /* Set up junk filter if needed */ if (junkFilter) ! *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); return false; /* NOT returning whole tuple */ } } *************** check_sql_fn_retval(Oid func_id, Oid ret *** 1180,1186 **** * what the caller expects will happen at runtime. */ if (junkFilter) ! *junkFilter = ExecInitJunkFilter(tlist, false, NULL); return true; } Assert(tupdesc); --- 1180,1186 ---- * what the caller expects will happen at runtime. */ if (junkFilter) ! *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); return true; } Assert(tupdesc); diff -Nrpc base/src/backend/executor/nodeAgg.c sepgsql/src/backend/executor/nodeAgg.c *** base/src/backend/executor/nodeAgg.c Sun Sep 6 19:40:49 2009 --- sepgsql/src/backend/executor/nodeAgg.c Thu Sep 17 17:04:16 2009 *************** *** 81,86 **** --- 81,87 ---- #include "parser/parse_agg.h" #include "parser/parse_coerce.h" #include "parser/parse_oper.h" + #include "security/sepgsql.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" *************** ExecInitAgg(Agg *node, EState *estate, i *** 1431,1436 **** --- 1432,1438 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(aggref->aggfnoid)); + sepgsql_proc_execute(aggref->aggfnoid); peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn; peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn; *************** ExecInitAgg(Agg *node, EState *estate, i *** 1454,1459 **** --- 1456,1462 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(transfn_oid)); + sepgsql_proc_execute(transfn_oid); if (OidIsValid(finalfn_oid)) { aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner, *************** ExecInitAgg(Agg *node, EState *estate, i *** 1461,1466 **** --- 1464,1470 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(finalfn_oid)); + sepgsql_proc_execute(finalfn_oid); } } diff -Nrpc base/src/backend/executor/nodeMergejoin.c sepgsql/src/backend/executor/nodeMergejoin.c *** base/src/backend/executor/nodeMergejoin.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/executor/nodeMergejoin.c Thu Sep 17 17:04:16 2009 *************** *** 98,103 **** --- 98,104 ---- #include "executor/execdefs.h" #include "executor/nodeMergejoin.h" #include "miscadmin.h" + #include "security/sepgsql.h" #include "utils/acl.h" #include "utils/lsyscache.h" #include "utils/memutils.h" *************** MJExamineQuals(List *mergeclauses, *** 215,220 **** --- 216,222 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(cmpproc)); + sepgsql_proc_execute(cmpproc); /* Set up the fmgr lookup information */ fmgr_info(cmpproc, &(clause->cmpfinfo)); diff -Nrpc base/src/backend/executor/nodeSubplan.c sepgsql/src/backend/executor/nodeSubplan.c *** base/src/backend/executor/nodeSubplan.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/executor/nodeSubplan.c Wed Jul 15 19:30:50 2009 *************** ExecInitSubPlan(SubPlan *subplan, PlanSt *** 869,875 **** * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. */ ! tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecAllocTableSlot(tupTable); ExecSetSlotDescriptor(slot, tupDesc); sstate->projLeft = ExecBuildProjectionInfo(lefttlist, --- 869,875 ---- * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. */ ! tupDesc = ExecTypeFromTL(leftptlist, false, false); slot = ExecAllocTableSlot(tupTable); ExecSetSlotDescriptor(slot, tupDesc); sstate->projLeft = ExecBuildProjectionInfo(lefttlist, *************** ExecInitSubPlan(SubPlan *subplan, PlanSt *** 877,883 **** slot, NULL); ! tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecAllocTableSlot(tupTable); ExecSetSlotDescriptor(slot, tupDesc); sstate->projRight = ExecBuildProjectionInfo(righttlist, --- 877,883 ---- slot, NULL); ! tupDesc = ExecTypeFromTL(rightptlist, false, false); slot = ExecAllocTableSlot(tupTable); ExecSetSlotDescriptor(slot, tupDesc); sstate->projRight = ExecBuildProjectionInfo(righttlist, diff -Nrpc base/src/backend/executor/nodeWindowAgg.c sepgsql/src/backend/executor/nodeWindowAgg.c *** base/src/backend/executor/nodeWindowAgg.c Tue Jun 23 11:09:47 2009 --- sepgsql/src/backend/executor/nodeWindowAgg.c Thu Sep 17 17:04:16 2009 *************** *** 43,48 **** --- 43,49 ---- #include "optimizer/clauses.h" #include "parser/parse_agg.h" #include "parser/parse_coerce.h" + #include "security/sepgsql.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/datum.h" *************** ExecInitWindowAgg(WindowAgg *node, EStat *** 1224,1229 **** --- 1225,1231 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(wfunc->winfnoid)); + sepgsql_proc_execute(wfunc->winfnoid); /* Fill in the perfuncstate data */ perfuncstate->wfuncstate = wfuncstate; *************** initialize_peragg(WindowAggState *winsta *** 1418,1423 **** --- 1420,1426 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(transfn_oid)); + sepgsql_proc_execute(transfn_oid); if (OidIsValid(finalfn_oid)) { aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner, *************** initialize_peragg(WindowAggState *winsta *** 1425,1430 **** --- 1428,1434 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(finalfn_oid)); + sepgsql_proc_execute(finalfn_oid); } } diff -Nrpc base/src/backend/executor/spi.c sepgsql/src/backend/executor/spi.c *** base/src/backend/executor/spi.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/executor/spi.c Tue Sep 8 23:55:48 2009 *************** SPI_modifytuple(Relation rel, HeapTuple *** 705,710 **** --- 705,712 ---- mtuple->t_tableOid = tuple->t_tableOid; if (rel->rd_att->tdhasoid) HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple)); + if (HeapTupleHasSecid(mtuple)) + HeapTupleSetSecid(mtuple, HeapTupleGetSecid(tuple)); } else { diff -Nrpc base/src/backend/libpq/be-fsstubs.c sepgsql/src/backend/libpq/be-fsstubs.c *** base/src/backend/libpq/be-fsstubs.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/libpq/be-fsstubs.c Mon Sep 28 10:07:19 2009 *************** *** 42,50 **** --- 42,53 ---- #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" *************** lo_read(int fd, char *buf, int len) *** 156,161 **** --- 159,167 ---- (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("invalid large-object descriptor: %d", fd))); + /* SELinux checks db_blob:{read} */ + sepgsqlCheckBlobRead(cookies[fd]); + status = inv_read(cookies[fd], buf, len); return status; *************** lo_write(int fd, const char *buf, int le *** 177,182 **** --- 183,191 ---- errmsg("large object descriptor %d was not opened for writing", fd))); + /* SELinux checks db_blob:{write} */ + sepgsqlCheckBlobWrite(cookies[fd]); + status = inv_write(cookies[fd], buf, len); return status; *************** lo_import_internal(text *filename, Oid l *** 364,369 **** --- 373,381 ---- * open the file to be read in */ text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); + /* SELinux checks db_blob:{write import} and file:{read} */ + //sepgsqlCheckBlobImport(lobj, fnamebuf); + fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, 0666); if (fd < 0) ereport(ERROR, *************** lo_export(PG_FUNCTION_ARGS) *** 439,444 **** --- 451,459 ---- * world-writable export files doesn't seem wise. */ text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); + /* SELinux checks db_blob:{read export} and file:{write} */ + //sepgsqlCheckBlobExport(lobj, fnamebuf); + oumask = umask((mode_t) 0022); fd = PathNameOpenFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666); umask(oumask); *************** lo_export(PG_FUNCTION_ARGS) *** 447,453 **** (errcode_for_file_access(), errmsg("could not create server file \"%s\": %m", fnamebuf))); - /* * read in from the inversion file and write to the filesystem */ --- 462,467 ---- *************** lo_truncate(PG_FUNCTION_ARGS) *** 482,493 **** --- 496,559 ---- (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("invalid large-object descriptor: %d", fd))); + /* SELinux checks db_blob:{write} */ + 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 -Nrpc base/src/backend/nodes/copyfuncs.c sepgsql/src/backend/nodes/copyfuncs.c *** base/src/backend/nodes/copyfuncs.c Fri Jun 19 13:40:37 2009 --- sepgsql/src/backend/nodes/copyfuncs.c Wed Jul 15 19:39:56 2009 *************** CopyScanFields(Scan *from, Scan *newnode *** 259,264 **** --- 259,265 ---- CopyPlanFields((Plan *) from, (Plan *) newnode); COPY_SCALAR_FIELD(scanrelid); + COPY_SCALAR_FIELD(rowlvPerms); } /* *************** _copyColumnDef(ColumnDef *from) *** 2075,2080 **** --- 2076,2082 ---- COPY_NODE_FIELD(raw_default); COPY_STRING_FIELD(cooked_default); COPY_NODE_FIELD(constraints); + COPY_NODE_FIELD(secLabel); return newnode; } *************** _copyCreateStmt(CreateStmt *from) *** 2414,2419 **** --- 2416,2422 ---- COPY_NODE_FIELD(options); COPY_SCALAR_FIELD(oncommit); COPY_STRING_FIELD(tablespacename); + COPY_NODE_FIELD(secLabel); return newnode; } *************** _copyAlterOwnerStmt(AlterOwnerStmt *from *** 2638,2643 **** --- 2641,2661 ---- 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) { *************** _copyCreateSeqStmt(CreateSeqStmt *from) *** 2887,2892 **** --- 2905,2911 ---- COPY_NODE_FIELD(sequence); COPY_NODE_FIELD(options); + COPY_NODE_FIELD(secLabel); return newnode; } *************** copyObject(void *from) *** 3819,3824 **** --- 3838,3846 ---- case T_AlterOwnerStmt: retval = _copyAlterOwnerStmt(from); break; + case T_AlterSecLabelStmt: + retval = _copyAlterSecLabelStmt(from); + break; case T_RuleStmt: retval = _copyRuleStmt(from); break; diff -Nrpc base/src/backend/nodes/equalfuncs.c sepgsql/src/backend/nodes/equalfuncs.c *** base/src/backend/nodes/equalfuncs.c Fri Jun 19 13:40:37 2009 --- sepgsql/src/backend/nodes/equalfuncs.c Wed Jul 15 19:37:35 2009 *************** _equalCreateStmt(CreateStmt *a, CreateSt *** 1078,1083 **** --- 1078,1084 ---- COMPARE_NODE_FIELD(options); COMPARE_SCALAR_FIELD(oncommit); COMPARE_STRING_FIELD(tablespacename); + COMPARE_NODE_FIELD(secLabel); return true; } *************** _equalAlterOwnerStmt(AlterOwnerStmt *a, *** 1271,1276 **** --- 1272,1290 ---- } 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); *************** _equalCreateSeqStmt(CreateSeqStmt *a, Cr *** 1477,1482 **** --- 1491,1497 ---- { COMPARE_NODE_FIELD(sequence); COMPARE_NODE_FIELD(options); + COMPARE_NODE_FIELD(secLabel); return true; } *************** _equalColumnDef(ColumnDef *a, ColumnDef *** 2054,2059 **** --- 2069,2075 ---- COMPARE_NODE_FIELD(raw_default); COMPARE_STRING_FIELD(cooked_default); COMPARE_NODE_FIELD(constraints); + COMPARE_NODE_FIELD(secLabel); return true; } *************** equal(void *a, void *b) *** 2596,2601 **** --- 2612,2620 ---- 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 -Nrpc base/src/backend/nodes/outfuncs.c sepgsql/src/backend/nodes/outfuncs.c *** base/src/backend/nodes/outfuncs.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/nodes/outfuncs.c Wed Jul 15 19:39:56 2009 *************** _outScanInfo(StringInfo str, Scan *node) *** 285,290 **** --- 285,291 ---- _outPlanInfo(str, (Plan *) node); WRITE_UINT_FIELD(scanrelid); + WRITE_UINT_FIELD(rowlvPerms); } /* *************** _outRelOptInfo(StringInfo str, RelOptInf *** 1534,1539 **** --- 1535,1541 ---- WRITE_BOOL_FIELD(has_eclass_joins); WRITE_BITMAPSET_FIELD(index_outer_relids); WRITE_NODE_FIELD(index_inner_paths); + WRITE_UINT_FIELD(rowlvPerms); } static void *************** _outCreateStmt(StringInfo str, CreateStm *** 1717,1722 **** --- 1719,1725 ---- WRITE_NODE_FIELD(options); WRITE_ENUM_FIELD(oncommit, OnCommitAction); WRITE_STRING_FIELD(tablespacename); + WRITE_NODE_FIELD(secLabel); } static void *************** _outColumnDef(StringInfo str, ColumnDef *** 1839,1844 **** --- 1842,1848 ---- WRITE_NODE_FIELD(raw_default); WRITE_STRING_FIELD(cooked_default); WRITE_NODE_FIELD(constraints); + WRITE_NODE_FIELD(secLabel); } static void diff -Nrpc base/src/backend/optimizer/plan/createplan.c sepgsql/src/backend/optimizer/plan/createplan.c *** base/src/backend/optimizer/plan/createplan.c Sun Sep 6 19:40:49 2009 --- sepgsql/src/backend/optimizer/plan/createplan.c Sun Sep 6 19:53:10 2009 *************** create_scan_plan(PlannerInfo *root, Path *** 305,310 **** --- 305,313 ---- 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 -Nrpc base/src/backend/optimizer/util/clauses.c sepgsql/src/backend/optimizer/util/clauses.c *** base/src/backend/optimizer/util/clauses.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/optimizer/util/clauses.c Thu Sep 17 17:04:16 2009 *************** *** 38,43 **** --- 38,44 ---- #include "parser/parse_coerce.h" #include "parser/parse_func.h" #include "rewrite/rewriteManip.h" + #include "security/sepgsql.h" #include "tcop/tcopprot.h" #include "utils/acl.h" #include "utils/builtins.h" *************** inline_function(Oid funcid, Oid result_t *** 3502,3507 **** --- 3503,3509 ---- funcform->prosecdef || funcform->proretset || !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || + !sepgsql_proc_hint_inlined(func_tuple) || funcform->pronargs != list_length(args)) return NULL; *************** inline_set_returning_function(PlannerInf *** 3970,3975 **** --- 3972,3978 ---- funcform->prosecdef || !funcform->proretset || !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || + !sepgsql_proc_hint_inlined(func_tuple) || funcform->pronargs != list_length(fexpr->args)) { ReleaseSysCache(func_tuple); diff -Nrpc base/src/backend/optimizer/util/relnode.c sepgsql/src/backend/optimizer/util/relnode.c *** base/src/backend/optimizer/util/relnode.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/optimizer/util/relnode.c Wed Jul 15 19:39:56 2009 *************** *** 21,26 **** --- 21,27 ---- #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" + #include "security/rowlevel.h" #include "utils/hsearch.h" *************** build_simple_rel(PlannerInfo *root, int *** 91,96 **** --- 92,98 ---- 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 -Nrpc base/src/backend/parser/analyze.c sepgsql/src/backend/parser/analyze.c *** base/src/backend/parser/analyze.c Sun Sep 6 19:40:49 2009 --- sepgsql/src/backend/parser/analyze.c Sun Sep 6 19:53:10 2009 *************** *** 25,30 **** --- 25,31 ---- #include "postgres.h" #include "access/sysattr.h" + #include "catalog/heap.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" *************** transformInsertStmt(ParseState *pstate, *** 653,659 **** tle = makeTargetEntry(expr, attr_num, col->name, ! false); qry->targetList = lappend(qry->targetList, tle); rte->modifiedCols = bms_add_member(rte->modifiedCols, --- 654,660 ---- tle = makeTargetEntry(expr, attr_num, col->name, ! attr_num < 0 ? true : false); qry->targetList = lappend(qry->targetList, tle); rte->modifiedCols = bms_add_member(rte->modifiedCols, *************** transformInsertRow(ParseState *pstate, L *** 768,773 **** --- 769,816 ---- 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, + -1); + } + tle->resjunk = true; + } + } + } /* * transformSelectStmt - *************** transformSelectStmt(ParseState *pstate, *** 872,877 **** --- 915,921 ---- if (stmt->intoClause) { qry->intoClause = stmt->intoClause; + transformSelectIntoSystemColumn(pstate, qry); if (stmt->intoClause->colNames) applyColumnNames(qry->targetList, stmt->intoClause->colNames); } diff -Nrpc base/src/backend/parser/gram.y sepgsql/src/backend/parser/gram.y *** base/src/backend/parser/gram.y Sun Sep 6 19:40:49 2009 --- sepgsql/src/backend/parser/gram.y Tue Sep 8 17:13:41 2009 *************** *** 58,63 **** --- 58,64 ---- #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "parser/gramparse.h" + #include "security/sepgsql.h" #include "storage/lmgr.h" #include "utils/date.h" #include "utils/datetime.h" *************** static TypeName *TableFuncTypeName(List *** 184,190 **** %type stmt schema_stmt AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt AlterForeignServerStmt AlterGroupStmt ! AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt --- 185,191 ---- %type stmt schema_stmt AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt AlterForeignServerStmt AlterGroupStmt ! AlterObjectSchemaStmt AlterOwnerStmt AlterSecLabelStmt AlterSeqStmt AlterTableStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt *************** static TypeName *TableFuncTypeName(List *** 401,406 **** --- 402,409 ---- %type OptTableSpace OptConsTableSpace OptTableSpaceOwner %type opt_check_option + %type OptSecCxt SecCxtItem + %type xml_attribute_el %type xml_attribute_list xml_attributes %type xml_root_version opt_xml_root_standalone *************** stmt : *** 607,612 **** --- 610,616 ---- | AlterGroupStmt | AlterObjectSchemaStmt | AlterOwnerStmt + | AlterSecLabelStmt | AlterSeqStmt | AlterTableStmt | AlterRoleSetStmt *************** DropGroupStmt: *** 1041,1047 **** *****************************************************************************/ CreateSchemaStmt: ! CREATE SCHEMA OptSchemaName AUTHORIZATION RoleId OptSchemaEltList { CreateSchemaStmt *n = makeNode(CreateSchemaStmt); /* One can omit the schema name or the authorization id. */ --- 1045,1051 ---- *****************************************************************************/ CreateSchemaStmt: ! CREATE SCHEMA OptSchemaName AUTHORIZATION RoleId OptSecCxt OptSchemaEltList { CreateSchemaStmt *n = makeNode(CreateSchemaStmt); /* One can omit the schema name or the authorization id. */ *************** CreateSchemaStmt: *** 1050,1065 **** else n->schemaname = $5; n->authid = $5; ! n->schemaElts = $6; $$ = (Node *)n; } ! | CREATE SCHEMA ColId OptSchemaEltList { CreateSchemaStmt *n = makeNode(CreateSchemaStmt); /* ...but not both */ n->schemaname = $3; n->authid = NULL; ! n->schemaElts = $4; $$ = (Node *)n; } ; --- 1054,1071 ---- else n->schemaname = $5; n->authid = $5; ! n->secLabel = (Node *)$6; ! n->schemaElts = $7; $$ = (Node *)n; } ! | CREATE SCHEMA ColId OptSecCxt OptSchemaEltList { CreateSchemaStmt *n = makeNode(CreateSchemaStmt); /* ...but not both */ n->schemaname = $3; n->authid = NULL; ! n->secLabel = (Node *)$4; ! n->schemaElts = $5; $$ = (Node *)n; } ; *************** opt_using: *** 2036,2042 **** *****************************************************************************/ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' ! OptInherit OptWith OnCommitOption OptTableSpace { CreateStmt *n = makeNode(CreateStmt); $4->istemp = $2; --- 2042,2048 ---- *****************************************************************************/ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' ! OptInherit OptWith OnCommitOption OptTableSpace OptSecCxt { CreateStmt *n = makeNode(CreateStmt); $4->istemp = $2; *************** CreateStmt: CREATE OptTemp TABLE qualifi *** 2047,2056 **** n->options = $9; n->oncommit = $10; n->tablespacename = $11; $$ = (Node *)n; } | CREATE OptTemp TABLE qualified_name OF qualified_name ! '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace { /* SQL99 CREATE TABLE OF (cols) seems to be satisfied * by our inheritance capabilities. Let's try it... --- 2053,2063 ---- 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 OptSecCxt { /* SQL99 CREATE TABLE OF (cols) seems to be satisfied * by our inheritance capabilities. Let's try it... *************** CreateStmt: CREATE OptTemp TABLE qualifi *** 2064,2069 **** --- 2071,2077 ---- n->options = $10; n->oncommit = $11; n->tablespacename = $12; + n->secLabel = (Node *) $13; $$ = (Node *)n; } ; *************** TableElement: *** 2106,2118 **** | TableConstraint { $$ = $1; } ; ! columnDef: ColId Typename ColQualList { ColumnDef *n = makeNode(ColumnDef); n->colname = $1; n->typename = $2; n->constraints = $3; n->is_local = true; $$ = (Node *)n; } ; --- 2114,2127 ---- | TableConstraint { $$ = $1; } ; ! columnDef: ColId Typename ColQualList OptSecCxt { ColumnDef *n = makeNode(ColumnDef); n->colname = $1; n->typename = $2; n->constraints = $3; n->is_local = true; + n->secLabel = (Node *) $4; $$ = (Node *)n; } ; *************** opt_with_data: *** 2584,2595 **** *****************************************************************************/ CreateSeqStmt: ! CREATE OptTemp SEQUENCE qualified_name OptSeqOptList { CreateSeqStmt *n = makeNode(CreateSeqStmt); $4->istemp = $2; n->sequence = $4; n->options = $5; $$ = (Node *)n; } ; --- 2593,2605 ---- *****************************************************************************/ CreateSeqStmt: ! CREATE OptTemp SEQUENCE qualified_name OptSeqOptList OptSecCxt { CreateSeqStmt *n = makeNode(CreateSeqStmt); $4->istemp = $2; n->sequence = $4; n->options = $5; + n->secLabel = (Node *)$6; $$ = (Node *)n; } ; *************** createfunc_opt_item: *** 4885,4890 **** --- 4895,4904 ---- { $$ = makeDefElem("window", (Node *)makeInteger(TRUE)); } + | SecCxtItem + { + $$ = $1; + } | common_func_opt_item { $$ = $1; *************** AlterOwnerStmt: ALTER AGGREGATE func_nam *** 5591,5596 **** --- 5605,5680 ---- } ; + /***************************************************************************** + * + * ALTER THING name SECURITY_CONTEXT [=] + * + *****************************************************************************/ + + AlterSecLabelStmt: ALTER DATABASE database_name SecCxtItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_DATABASE; + n->object = list_make1(makeString($3)); + n->secLabel = (Node *)$4; + $$ = (Node *) n; + } + | ALTER SCHEMA name SecCxtItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_SCHEMA; + n->object = list_make1(makeString($3)); + n->secLabel = (Node *)$4; + $$ = (Node *) n; + } + | ALTER TABLE relation_expr SecCxtItem + { + 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 SecCxtItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_COLUMN; + n->relation = $3; + n->subname = $6; + n->secLabel = (Node *)$7; + $$ = (Node *) n; + } + | ALTER SEQUENCE relation_expr SecCxtItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_SEQUENCE; + n->relation = $3; + n->secLabel = (Node *)$4; + $$ = (Node *) n; + } + | ALTER FUNCTION function_with_argtypes SecCxtItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_FUNCTION; + n->object = $3->funcname; + n->objarg = $3->funcargs; + n->secLabel = (Node *)$4; + $$ = (Node *) n; + } + ; + + OptSecCxt: SecCxtItem { $$ = $1; } + | /* EMPTY */ { $$ = NULL; } + ; + + SecCxtItem: IDENT opt_equal Sconst + { + if (strcmp("security_context", $1) != 0) + yyerror("syntax error"); + $$ = makeDefElem($1, (Node *) makeString($3)); + } + ; + /***************************************************************************** * *************** createdb_opt_item: *** 6033,6038 **** --- 6117,6126 ---- { $$ = makeDefElem("owner", NULL); } + | SecCxtItem + { + $$ = $1; + } ; /* diff -Nrpc base/src/backend/parser/parse_target.c sepgsql/src/backend/parser/parse_target.c *** base/src/backend/parser/parse_target.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/parser/parse_target.c Wed Jul 15 19:38:52 2009 *************** *** 14,19 **** --- 14,20 ---- */ #include "postgres.h" + #include "catalog/heap.h" #include "catalog/pg_type.h" #include "commands/dbcommands.h" #include "funcapi.h" *************** transformAssignedExpr(ParseState *pstate *** 361,376 **** Oid attrtype; /* type of target column */ int32 attrtypmod; Relation rd = pstate->p_target_relation; Assert(rd != NULL); ! if (attrno <= 0) ! ereport(ERROR, ! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("cannot assign to system column \"%s\"", ! colname), ! parser_errposition(pstate, location))); ! attrtype = attnumTypeId(rd, attrno); ! attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; /* * If the expression is a DEFAULT placeholder, insert the attribute's --- 362,394 ---- 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) ! { ! attrtype = attnumTypeId(rd, attrno); ! attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; ! } ! else ! { ! Form_pg_attribute attForm ! = SystemAttributeDefinition(attrno, relhasoids); ! if (attForm && SystemAttributeIsWritable(attrno)) ! { ! attrtype = attForm->atttypid; ! attrtypmod = attForm->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 *************** updateTargetListEntry(ParseState *pstate *** 515,520 **** --- 533,541 ---- */ tle->resno = (AttrNumber) attrno; tle->resname = colname; + + if (SystemAttributeIsWritable(attrno)) + tle->resjunk = true; } *************** checkInsertTargets(ParseState *pstate, L *** 789,794 **** --- 810,816 ---- Bitmapset *wholecols = NULL; Bitmapset *partialcols = NULL; ListCell *tl; + uint32 system_attrs = 0UL; foreach(tl, cols) { *************** checkInsertTargets(ParseState *pstate, L *** 797,810 **** int attrno; /* Lookup column name, ereport on failure */ ! attrno = attnameAttNum(pstate->p_target_relation, name, false); 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))); /* * Check for duplicates, but only of whole columns --- we allow --- 819,855 ---- int attrno; /* Lookup column name, ereport on failure */ ! 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 diff -Nrpc base/src/backend/parser/parse_utilcmd.c sepgsql/src/backend/parser/parse_utilcmd.c *** base/src/backend/parser/parse_utilcmd.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/parser/parse_utilcmd.c Fri Sep 18 14:51:00 2009 *************** *** 49,54 **** --- 49,55 ---- #include "parser/parse_type.h" #include "parser/parse_utilcmd.h" #include "rewrite/rewriteManip.h" + #include "security/sepgsql.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" *************** transformInhRelation(ParseState *pstate, *** 565,570 **** --- 566,573 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(relation)); + /* SELinux checks */ + sepgsql_relation_copy_definition(RelationGetRelid(relation)); tupleDesc = RelationGetDescr(relation); constr = tupleDesc->constr; diff -Nrpc base/src/backend/postmaster/autovacuum.c sepgsql/src/backend/postmaster/autovacuum.c *** base/src/backend/postmaster/autovacuum.c Sun Sep 6 19:40:49 2009 --- sepgsql/src/backend/postmaster/autovacuum.c Sun Sep 6 19:53:10 2009 *************** do_autovacuum(void) *** 2004,2010 **** object.classId = RelationRelationId; object.objectId = relid; object.objectSubId = 0; ! performDeletion(&object, DROP_CASCADE); } else { --- 2004,2010 ---- object.classId = RelationRelationId; object.objectId = relid; object.objectSubId = 0; ! performDeletionNoPerms(&object, DROP_CASCADE); } else { diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmaster/postmaster.c *** base/src/backend/postmaster/postmaster.c Sun Sep 6 19:40:49 2009 --- sepgsql/src/backend/postmaster/postmaster.c Tue Dec 8 14:04:25 2009 *************** *** 108,113 **** --- 108,114 ---- #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" *************** static pid_t StartupPID = 0, *** 209,215 **** AutoVacPID = 0, PgArchPID = 0, PgStatPID = 0, ! SysLoggerPID = 0; /* Startup/shutdown state */ #define NoShutdown 0 --- 210,217 ---- AutoVacPID = 0, PgArchPID = 0, PgStatPID = 0, ! SysLoggerPID = 0, ! sepgsqlReceiverPID = 0; /* Startup/shutdown state */ #define NoShutdown 0 *************** static void ShmemBackendArrayRemove(Back *** 445,450 **** --- 447,453 ---- #define StartupDataBase() StartChildProcess(StartupProcess) #define StartBackgroundWriter() StartChildProcess(BgWriterProcess) #define StartWalWriter() StartChildProcess(WalWriterProcess) + #define StartSELinuxReceiver() StartChildProcess(SelinuxReceiverProcess) /* Macros to check exit status of a child process */ #define EXIT_STATUS_0(st) ((st) == 0) *************** ServerLoop(void) *** 1436,1441 **** --- 1439,1449 ---- if (PgStatPID == 0 && pmState == PM_RUN) PgStatPID = pgstat_start(); + /* if we have lost the selinux netlink receiver, try to start */ + if (sepgsqlIsEnabled() && + sepgsqlReceiverPID == 0 && pmState == PM_RUN) + sepgsqlReceiverPID = StartSELinuxReceiver(); + /* If we need to signal the autovacuum launcher, do so now */ if (avlauncher_needs_signal) { *************** SIGHUP_handler(SIGNAL_ARGS) *** 2053,2058 **** --- 2061,2068 ---- signal_child(SysLoggerPID, SIGHUP); if (PgStatPID != 0) signal_child(PgStatPID, SIGHUP); + if (sepgsqlReceiverPID != 0) + signal_child(sepgsqlReceiverPID, SIGHUP); /* Reload authentication config files too */ if (!load_hba()) *************** pmdie(SIGNAL_ARGS) *** 2113,2118 **** --- 2123,2131 ---- /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); + /* and the selinux netlink receiver too */ + if (sepgsqlReceiverPID != 0) + signal_child(sepgsqlReceiverPID, SIGTERM); pmState = PM_WAIT_BACKUP; } *************** pmdie(SIGNAL_ARGS) *** 2160,2165 **** --- 2173,2181 ---- /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); + /* and the selinux netlink receiver too */ + if (sepgsqlReceiverPID != 0) + signal_child(sepgsqlReceiverPID, SIGTERM); pmState = PM_WAIT_BACKENDS; } *************** pmdie(SIGNAL_ARGS) *** 2193,2198 **** --- 2209,2216 ---- signal_child(PgArchPID, SIGQUIT); if (PgStatPID != 0) signal_child(PgStatPID, SIGQUIT); + if (sepgsqlReceiverPID != 0) + signal_child(sepgsqlReceiverPID, SIGQUIT); ExitPostmaster(0); break; } *************** reaper(SIGNAL_ARGS) *** 2455,2460 **** --- 2473,2488 ---- continue; } + /* Was it the selinux netlink receiver process? */ + if (pid == sepgsqlReceiverPID) + { + sepgsqlReceiverPID = 0; + if (!EXIT_STATUS_0(exitstatus)) + LogChildExit(LOG, _("SELinux netlink receiver process"), + pid, exitstatus); + continue; + } + /* * Else do standard backend child cleanup. */ *************** HandleChildCrash(int pid, int exitstatus *** 2646,2651 **** --- 2674,2691 ---- signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT)); } + /* Take care of the selinux netlink receiver too */ + if (pid == sepgsqlReceiverPID) + sepgsqlReceiverPID = 0; + else if (sepgsqlReceiverPID != 0 && !FatalError) + { + ereport(DEBUG2, + (errmsg_internal("sending %s to process %d", + (SendStop ? "SIGSTOP" : "SIGQUIT"), + (int) sepgsqlReceiverPID))); + signal_child(sepgsqlReceiverPID, (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 *************** PostmasterStateMachine(void) *** 2778,2784 **** StartupPID == 0 && (BgWriterPID == 0 || !FatalError) && WalWriterPID == 0 && ! AutoVacPID == 0) { if (FatalError) { --- 2818,2825 ---- StartupPID == 0 && (BgWriterPID == 0 || !FatalError) && WalWriterPID == 0 && ! AutoVacPID == 0 && ! sepgsqlReceiverPID == 0) { if (FatalError) { *************** StartChildProcess(AuxProcType type) *** 4321,4326 **** --- 4362,4373 ---- ereport(LOG, (errmsg("could not fork WAL writer process: %m"))); break; + #ifdef HAVE_SELINUX + case SelinuxReceiverProcess: + ereport(LOG, + (errmsg("could not fork selinux receiver process: %m"))); + break; + #endif default: ereport(LOG, (errmsg("could not fork process: %m"))); diff -Nrpc base/src/backend/rewrite/rewriteDefine.c sepgsql/src/backend/rewrite/rewriteDefine.c *** base/src/backend/rewrite/rewriteDefine.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/rewrite/rewriteDefine.c Fri Sep 18 14:51:00 2009 *************** *** 27,32 **** --- 27,33 ---- #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" #include "rewrite/rewriteSupport.h" + #include "security/sepgsql.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/inval.h" *************** DefineQueryRewrite(char *rulename, *** 266,271 **** --- 267,275 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(event_relation)); + /* SELinux checks */ + sepgsql_rule_create(event_relid, rulename); + /* * No rule actions that modify OLD or NEW */ diff -Nrpc base/src/backend/rewrite/rewriteRemove.c sepgsql/src/backend/rewrite/rewriteRemove.c *** base/src/backend/rewrite/rewriteRemove.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/rewrite/rewriteRemove.c Fri Sep 18 14:51:00 2009 *************** *** 22,27 **** --- 22,28 ---- #include "catalog/pg_rewrite.h" #include "miscadmin.h" #include "rewrite/rewriteRemove.h" + #include "security/sepgsql.h" #include "utils/acl.h" #include "utils/fmgroids.h" #include "utils/inval.h" *************** RemoveRewriteRule(Oid owningRel, const c *** 78,83 **** --- 79,87 ---- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, get_rel_name(eventRelationOid)); + /* SELinux checks */ + sepgsql_rule_drop(eventRelationOid, ruleName); + /* * Do the deletion */ diff -Nrpc base/src/backend/security/Makefile sepgsql/src/backend/security/Makefile *** base/src/backend/security/Makefile Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/Makefile Wed Jul 15 19:39:56 2009 *************** *** 0 **** --- 1,13 ---- + # + # Makefile for the enhanced security subsystem + # + + subdir = src/backend/security + top_builddir = ../../.. + include $(top_builddir)/src/Makefile.global + + SUBDIRS = sepgsql + + OBJS = rowlevel.o + + include $(top_srcdir)/src/backend/common.mk diff -Nrpc base/src/backend/security/rowlevel.c sepgsql/src/backend/security/rowlevel.c *** base/src/backend/security/rowlevel.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/rowlevel.c Thu Jul 16 17:22:29 2009 *************** *** 0 **** --- 1,121 ---- + /* + * 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 "catalog/pg_security.h" + #include "security/rowlevel.h" + #include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/bufpage.h" + #include "utils/rel.h" + #include "utils/tqual.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. + */ + uint32 + rowlvSetupPermissions(RangeTblEntry *rte) + { + return sepgsqlSetupTuplePerms(rte); + } + + /* + * 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; + + if (!perms) + return true; + + tuple = ExecMaterializeSlot(slot); + + return sepgsqlExecScan(rel, tuple, perms, abort); + } + + 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 + * assign default security attribute, and check permission + * if necessary. + */ + void + rowlvHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal) + { + sepgsqlHeapTupleInsert(rel, newtup, internal); + } + + /* + * rowlvHeapTupleUpdate + * check permission to change security attribute, if necesary + */ + void + rowlvHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup) + { + sepgsqlHeapTupleUpdate(rel, otid, newtup); + } diff -Nrpc base/src/backend/security/sepgsql/Makefile sepgsql/src/backend/security/sepgsql/Makefile *** base/src/backend/security/sepgsql/Makefile Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/Makefile Thu Sep 17 17:04:16 2009 *************** *** 0 **** --- 1,16 ---- + # + # Makefile + # Makefile for utils/sepgsql : SE-PostgreSQL + # + + subdir = src/backend/security/sepgsql + top_builddir = ../../../.. + include $(top_builddir)/src/Makefile.global + + ifeq ($(enable_selinux), yes) + OBJS = avc.o checker.o bridge.o hooks.o label.o misc.o perms.o + else + OBJS = dummy.o + endif + + include $(top_srcdir)/src/backend/common.mk diff -Nrpc base/src/backend/security/sepgsql/avc.c sepgsql/src/backend/security/sepgsql/avc.c *** base/src/backend/security/sepgsql/avc.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/avc.c Tue Dec 8 14:16:15 2009 *************** *** 0 **** --- 1,880 ---- + /* + * 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/pg_security.h" + #include "libpq/pqsignal.h" + #include "miscadmin.h" + #include "postmaster/postmaster.h" + #include "security/sepgsql.h" + #include "storage/ipc.h" + #include "storage/lwlock.h" + #include "utils/memutils.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 + + #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 *current_page = NULL; + + static int avc_version; + + /* + * selinux_state + * + * It is deployed on the shared memory region, to show the system + * state of SELinux and its security policy. + * + * The selinux_state->version should be checked prior to avc accesses. + * If it does not match with the local avc_version, it means that + * system security policy was reloaded or system state (enforcing + * or permissive) was changed. + * + * The state monitoring worker process receives messages from the + * kernel using libselinux, and it updates the selinux_state. + */ + struct + { + int version; + + bool enforcing; + + } *selinux_state = NULL; + + Size + sepgsqlShmemSize(void) + { + if (!sepgsqlIsEnabled()) + return 0; + + return sizeof(*selinux_state); + } + + /* + * sepgsql_shmem_init + * attaches shared memory segment. + */ + static void + sepgsqlShmemInit(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); + } + } + + /* + * sepgsqlAvcReset + * + * It invalidate access vector cache. It has to be called on errors, + * because avc entries for newly created context is uncertain whether + * it is still valid, or not. + */ + void + sepgsqlAvcReset(void) + { + if (!sepgsqlIsEnabled()) + return; + + MemoryContextReset(AvcMemCtx); + + current_page = NULL; + + sepgsqlAvcSwitchClient(sepgsqlGetClientLabel()); + } + + /* + * sepgsqlAvcCheckValid + * + * It checks whether the current AVC pages are valid, or not. + * If state monitoring process already received an invalidation + * message from the kernel, it clears current AVC pages and + * returns false. + */ + static bool + sepgsqlAvcCheckValid(void) + { + bool result = true; + + LWLockAcquire(SepgsqlAvcLock, LW_SHARED); + if (avc_version != selinux_state->version) + { + /* reset invalid avc pages, and makes an empty one */ + MemoryContextReset(AvcMemCtx); + + current_page = NULL; + + sepgsqlAvcSwitchClient(sepgsqlGetClientLabel()); + + /* copy current version to local */ + avc_version = selinux_state->version; + + result = false; + } + LWLockRelease(SepgsqlAvcLock); + + return result; + } + + /* + * sepgsqlAvcInitialize + * + * It allocates a memory context for userspace AVC, + * map shared memory segment, and initialize avc_page + * for the current client's privilege. + * + * If the current backend is not associated with a certain + * client process, it switches to permissive mode to avoid + * to prevent any internal processes. + */ + void + sepgsqlAvcInitialize(void) + { + if (!sepgsqlIsEnabled()) + return; + + /* + * local memory context + */ + AvcMemCtx = AllocSetContextCreate(TopMemoryContext, + "SE-PostgreSQL userspace avc", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + sepgsqlShmemInit(); + + /* + * Switch to local permissive mode + */ + if (!MyProcPort) + sepgsqlSetEnforce(0); + + /* + * selinux_state->version is never negative value, + * so this call always reset local avc. + */ + avc_version = -1; + sepgsqlAvcCheckValid(); + } + + /* + * sepgsqlGetEnforce + * sepgsqlSetEnforce + * + * SELinux has two working mode called Enforcing/Permissive. + * In enforcing mode, it checks security policy and actually + * applies its access controls. In permissive mode, it also + * checks security policy, but does not apply any access + * controls. It is used to collect access denied logs to + * debug security policy. + * + * sepgsqlGetEnforce() returns the current working mode, and + * sepgsqlSetEnforce() switches the current working mode + * temporary. When we switches the mode, any errors have to + * be acquired, and it should be restored correctly. + */ + static int local_enforce = -1; /* undefined */ + + bool + sepgsqlGetEnforce(void) + { + bool rc; + + if (local_enforce < 0) + { + LWLockAcquire(SepgsqlAvcLock, LW_SHARED); + rc = selinux_state->enforcing; + LWLockRelease(SepgsqlAvcLock); + + return rc; + } + + return (local_enforce > 0 ? true : false); + } + + int + sepgsqlSetEnforce(int new_mode) + { + int old_mode = local_enforce; + + local_enforce = new_mode; + + return old_mode; + } + + /* + * sepgsqlAvcAudit + * + * It write out audit message, when auditdeny or auditallow + * matches the required permission bits. + * If external module support sepgsqlAvcAuditHook, it allows + * to write audit logs to external log manager, such as system + * auditd. + */ + + PGDLLIMPORT sepgsqlAvcAuditHook_t sepgsqlAvcAuditHook = NULL; + + static void + sepgsqlAvcAudit(bool denied, char *scontext, char *tcontext, + uint16 tclass, uint32 audited, const char *audit_name) + { + StringInfoData buf; + uint32 mask; + const char *tclass_name; + + /* translate to human readable form */ + scontext = sepgsqlTransSecLabelOut(scontext); + tcontext = sepgsqlTransSecLabelOut(tcontext); + + /* permissions in text representation */ + initStringInfo(&buf); + appendStringInfo(&buf, "{"); + for (mask = 1; audited != 0; mask <<= 1) + { + if (audited & mask) + appendStringInfo(&buf, " %s", sepgsqlGetPermString(tclass, mask)); + + audited &= ~mask; + } + appendStringInfo(&buf, " }"); + + tclass_name = sepgsqlGetClassString(tclass); + + /* call external audit module, if loaded */ + if (sepgsqlAvcAuditHook) + (*sepgsqlAvcAuditHook) (denied, scontext, tcontext, + tclass_name, buf.data, audit_name); + else + { + appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s", + scontext, tcontext, tclass_name); + if (audit_name) + appendStringInfo(&buf, " name=%s", audit_name); + + ereport(LOG, + (errcode(ERRCODE_SELINUX_AUDIT), + errmsg("SELinux: %s %s", + denied ? "denied" : "granted", buf.data))); + } + } + + /* + * sepgsqlAvcReclaim + * + * It wipes recently unused AVC entries, when the number of entries + * reaches AVC_HASH_NUM_NODES.. + */ + static void + sepgsqlAvcReclaim(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; + } + } + + /* + * sepgsqlAvcMakeEntry + * + * It makes a new AVC entry and insert it on the avc_page. + * If is hold more than AVC_HASH_NUM_NODES entries, recently unused + * avc_datum shall be reclaimed. + */ + #define avc_hash_key(trelid,tsecid,tclass) \ + (hash_uint32((trelid) ^ (tsecid) ^ ((tclass) << 3))) + + static avc_datum * + sepgsqlAvcMakeEntry(avc_page *page, sepgsql_sid_t tsid, uint16 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(tsid.relid, tsid.secid, tclass); + index = hash_key % AVC_HASH_NUM_SLOTS; + + scontext = page->scontext; + tcontext = securityRawSecLabelOut(tsid.relid, tsid.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 = tsid.relid; + cache->tsid.secid = tsid.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); + + sepgsqlAvcReclaim(page); + + page->slot[index] = lcons(cache, page->slot[index]); + page->avc_count++; + + MemoryContextSwitchTo(oldctx); + + return cache; + } + + /* + * sepgsqlAvcLookup + * + * It lookups required AVC entry. + */ + static avc_datum * + sepgsqlAvcLookup(avc_page *page, sepgsql_sid_t tsid, uint16 tclass) + { + avc_datum *cache = NULL; + uint32 hash_key, index; + ListCell *l; + + hash_key = avc_hash_key(tsid.relid, tsid.secid, 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 == tsid.relid + && cache->tsid.secid == tsid.secid) + { + cache->hot_cache = true; + return cache; + } + } + return NULL; + } + + /* + * sepgsqlAvcSwitchClientLabel() + * + * It switches the current avc_page. + * An avc_page is a set of cached access control decisions associated + * with a certain privilege of the client. This structure enables to + * lookup required avc_datum without any comparison to the subject + * label. + */ + void + sepgsqlAvcSwitchClient(const char *scontext) + { + MemoryContext oldctx; + avc_page *new_page; + int i; + + if (current_page) + { + new_page = current_page; + do { + if (strcmp(new_page->scontext, scontext) == 0) + { + current_page = new_page; + return; + } + new_page = new_page->next; + } while (new_page != current_page); + } + + /* Not found, 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 (!current_page) + new_page->next = new_page; + else + { + new_page->next = current_page->next; + current_page->next = new_page; + } + + current_page = new_page; + } + + /* + * sepgsqlClientHasPerms + * + * It checks client's privileges on the given object using avc. + */ + bool + sepgsqlClientHasPerms(sepgsql_sid_t tsid, + uint16 tclass, uint32 required, + const char *audit_name, bool abort) + { + avc_datum *cache; + uint32 denied, audited; + bool result = true; + + Assert(required != 0); + + do { + cache = sepgsqlAvcLookup(current_page, tsid, tclass); + if (!cache) + cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass); + } while (!sepgsqlAvcCheckValid()); + + denied = required & ~cache->allowed; + audited = denied ? (denied & cache->auditdeny) + : (required & cache->auditallow); + if (audited) + { + sepgsqlAvcAudit(!!denied, + current_page->scontext, + securityRawSecLabelOut(tsid.relid, tsid.secid), + cache->tclass, audited, audit_name); + } + + if (denied) + { + if (!sepgsqlGetEnforce() || cache->permissive) + cache->allowed |= required; /* prevent flood of audit log */ + else + { + if (abort) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: security policy violation"))); + result = false; + } + } + + return result; + } + + /* + * sepgsqlClientCreateSecid + * sepgsqlClientCreateLabel + */ + sepgsql_sid_t + sepgsqlClientCreateSecid(sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) + { + sepgsql_sid_t nsid; + avc_datum *cache; + int index; + + do { + cache = sepgsqlAvcLookup(current_page, tsid, tclass); + if (!cache) + cache = sepgsqlAvcMakeEntry(current_page, tsid, 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; + } + nsid = cache->nsid[index]; + } while (!sepgsqlAvcCheckValid()); + + return nsid; + } + + security_context_t + sepgsqlClientCreateLabel(sepgsql_sid_t tsid, uint16 tclass) + { + avc_datum *cache; + + do { + cache = sepgsqlAvcLookup(current_page, tsid, tclass); + if (!cache) + cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass); + } while (!sepgsqlAvcCheckValid()); + + return cache->ncontext; + } + + /* + * 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(char *scontext, char *tcontext, + uint16 tclass_in, uint32 required, + const char *audit_name, bool abort) + { + access_vector_t denied, audited; + security_class_t tclass_ex; + struct av_decision avd; + + Assert(required != 0); + + tclass_ex = sepgsqlTransToExternalClass(tclass_in); + if (tclass_ex > 0) + { + /* + * security_compute_av_flags_raw() is a SELinux's API that + * returns its access control decision based on the security + * policy, to the given combination of user's privilege + * (scontext; security label of the client process), + * target's attribute (tcontext; security label of the + * object) and type of actions (tclass; object classes). + * + * The returned avd.allowed is a bitmap of allowed actions. + */ + 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_in)))); + sepgsqlTransToInternalPerms(tclass_in, &avd); + } + else + { + /* + * If security policy does not support database related + * permissions, it fulls up permission bits by dummy + * data. + * If security_deny_unknown() returns positive value, + * undefined permissions should not be allowed. + * Otherwise, it shall be allowed. + */ + avd.allowed = (security_deny_unknown() > 0 ? 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) + { + /* + * If security policy requires to generate an audit log + * record for the given request, it should be logged. + */ + sepgsqlAvcAudit(!!denied, scontext, tcontext, + tclass_in, audited, audit_name); + } + + /* + * If any required permissions are not allowed, and + * SE-PgSQL performs in enforcing mode, and the given + * combination of subject, object and action does not + * have special flag to be handled as permission, + * SE-PgSQL returns false or raises an error. + * Otherwise, it returns true that means required + * actions are allowed. + */ + if (!denied || /* no policy violation */ + !sepgsqlGetEnforce() || /* permissive mode */ + (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) != 0) /* permissive domain */ + return true; + + if (abort) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: security policy violation"))); + + return false; + } + + char * + sepgsqlComputeCreate(char *scontext, char *tcontext, uint16 tclass_in) + { + security_context_t ncontext, result; + security_class_t tclass_ex; + + tclass_ex = sepgsqlTransToExternalClass(tclass_in); + /* + * security_compute_create_raw() is a SELinux's API that + * returns a default security context to be assigned on + * a new object (categorized by object class) when a client + * labeled as scontext tries to create a new one under the + * parent object labeled as tcontext. + */ + if (security_compute_create_raw(scontext, tcontext, + tclass_ex, &ncontext) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not compute a new context " + "scontext=%s tcontext=%s tclass=%s", + scontext, tcontext, sepgsqlGetClassString(tclass_in)))); + 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->enforcing = (enforce ? true : false); + selinux_state->version++; + LWLockRelease(SepgsqlAvcLock); + + return 0; + } + + static int + sepgsql_cb_policyload(int seqno) + { + /* invalidate local avc */ + LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); + selinux_state->version++; + LWLockRelease(SepgsqlAvcLock); + + return 0; + } + + void + sepgsqlReceiverMain(void) + { + union selinux_callback cb; + + Assert(sepgsqlIsEnabled()); + + #ifdef HAVE_SETSID + if (setsid() < 0) + elog(FATAL, "setsid() failed: %m"); + #endif + + /* + * 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); + + /* + * map shared memory segment + */ + sepgsqlShmemInit(); + + 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(); + + exit(0); + } diff -Nrpc base/src/backend/security/sepgsql/bridge.c sepgsql/src/backend/security/sepgsql/bridge.c *** base/src/backend/security/sepgsql/bridge.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/bridge.c Tue Dec 1 17:11:40 2009 *************** *** 0 **** --- 1,2876 ---- + /* + * src/backend/security/sepgsql/bridge.c + * + * New style security hooks for 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 "access/sysattr.h" + #include "catalog/heap.h" + #include "catalog/indexing.h" + #include "catalog/pg_authid.h" + #include "catalog/pg_cast.h" + #include "catalog/pg_conversion.h" + #include "catalog/pg_database.h" + #include "catalog/pg_foreign_data_wrapper.h" + #include "catalog/pg_foreign_server.h" + #include "catalog/pg_language.h" + #include "catalog/pg_namespace.h" + #include "catalog/pg_operator.h" + #include "catalog/pg_opclass.h" + #include "catalog/pg_opfamily.h" + #include "catalog/pg_proc.h" + #include "catalog/pg_rewrite.h" + #include "catalog/pg_security.h" + #include "catalog/pg_tablespace.h" + #include "catalog/pg_ts_parser.h" + #include "catalog/pg_ts_dict.h" + #include "catalog/pg_ts_template.h" + #include "catalog/pg_ts_config.h" + #include "catalog/pg_type.h" + #include "catalog/pg_user_mapping.h" + #include "commands/dbcommands.h" + #include "miscadmin.h" + #include "security/sepgsql.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" + #include "utils/lsyscache.h" + #include "utils/syscache.h" + #include "utils/tqual.h" + + #include + + /* ------------------------------------------------------------ * + * Common Helper Routines + * ------------------------------------------------------------ */ + static bool sepgsql_database_common(Oid datOid, uint32 required, bool abort); + static bool sepgsql_schema_common(Oid nspOid, uint32 required, bool abort); + static bool sepgsql_attribute_common(Oid relOid, AttrNumber attnum, + uint32 required, bool abort); + static bool sepgsql_relation_common(Oid relOid, uint32 required, bool abort); + static bool sepgsql_proc_common(Oid procOid, uint32 required, bool abort); + static bool sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort); + static bool sepgsql_foreign_server_common(Oid fsrvOid, uint32 required, bool abort); + static bool sepgsql_language_common(Oid langOid, uint32 required, bool abort); + static bool sepgsql_operator_common(Oid oprOid, uint32 required, bool abort); + + /* ------------------------------------------------------------ * + * + * Pg_database related security hooks + * + * ------------------------------------------------------------ */ + static bool + sepgsql_database_common(Oid datOid, uint32 required, bool abort) + { + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + const char *auname; + bool rc; + + tuple = SearchSysCache(DATABASEOID, + ObjectIdGetDatum(datOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for database: %u", datOid); + + auname = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); + + sid = sepgsqlGetTupleSecid(DatabaseRelationId, tuple, &tclass); + + rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); + + ReleaseSysCache(tuple); + + return rc; + } + + Oid + sepgsql_database_create(const char *datName, DefElem *newLabel) + { + sepgsql_sid_t sid; + + if (!sepgsqlIsEnabled()) + return InvalidOid; + + if (!newLabel) + sid = sepgsqlGetDefaultDatabaseSecid(); + else + { + sid.relid = DatabaseRelationId; + sid.secid = securityTransSecLabelIn(sid.relid, + strVal(newLabel->arg)); + } + + sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_DATABASE, + SEPG_DB_DATABASE__CREATE, + datName, true); + return sid.secid; + } + + void + sepgsql_database_alter(Oid datOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_database_common(datOid, SEPG_DB_DATABASE__SETATTR, true); + } + + void + sepgsql_database_drop(Oid datOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_database_common(datOid, SEPG_DB_DATABASE__DROP, true); + } + + Oid + sepgsql_database_relabel(Oid datOid, DefElem *newLabel) + { + sepgsql_sid_t sid; + + if (!sepgsqlIsEnabled()) + { + if (newLabel) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux is disabled now"))); + + return InvalidOid; + } + sid.relid = DatabaseRelationId; + sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); + + /* db_database:{setattr relabelfrom} to older seclabel */ + sepgsql_database_common(datOid, + SEPG_DB_DATABASE__SETATTR | + SEPG_DB_DATABASE__RELABELFROM, true); + + /* db_database:{relabelto} to newer seclabel */ + sepgsqlClientHasPerms(sid, + SEPG_CLASS_DB_DATABASE, + SEPG_DB_DATABASE__RELABELTO, + get_database_name(datOid), true); + + return sid.secid; + } + + void + sepgsql_database_grant(Oid datOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_database_common(datOid, SEPG_DB_DATABASE__SETATTR, true); + } + + void + sepgsql_database_access(Oid datOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_database_common(datOid, SEPG_DB_DATABASE__ACCESS, true); + } + + bool + sepgsql_database_superuser(Oid datOid) + { + if (!sepgsqlIsEnabled()) + return true; + + return sepgsql_database_common(datOid, SEPG_DB_DATABASE__SUPERUSER, false); + } + + void + sepgsql_database_load_module(Oid datOid, const char *filename) + { + HeapTuple tuple; + security_context_t filecon; + security_context_t datcon; + + 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(datOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for database: %u", datOid); + + datcon = securityRawSecLabelOut(DatabaseRelationId, + HeapTupleGetSecid(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(datcon, + filecon, + SEPG_CLASS_DB_DATABASE, + SEPG_DB_DATABASE__LOAD_MODULE, + filename, true); + } + PG_CATCH(); + { + freecon(filecon); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(filecon); + } + + /* ------------------------------------------------------------ * + * + * Pg_namespace related security hooks + * + * ------------------------------------------------------------ */ + static bool + sepgsql_schema_common(Oid nspOid, uint32 required, bool abort) + { + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + const char *auname; + bool rc; + + tuple = SearchSysCache(NAMESPACEOID, + ObjectIdGetDatum(nspOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for namespace: %u", nspOid); + + sid = sepgsqlGetTupleSecid(NamespaceRelationId, tuple, &tclass); + + auname = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); + + rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); + + ReleaseSysCache(tuple); + + return rc; + } + + Oid + sepgsql_schema_create(const char *nspName, bool isTemp, DefElem *newLabel) + { + sepgsql_sid_t sid; + + if (!sepgsqlIsEnabled()) + return InvalidOid; + + if (!newLabel) + sid = sepgsqlGetDefaultSchemaSecid(MyDatabaseId); + else + { + sid.relid = NamespaceRelationId; + sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); + } + + sepgsqlClientHasPerms(sid, + SEPG_CLASS_DB_SCHEMA, + SEPG_DB_SCHEMA__CREATE, + nspName, true); + return sid.secid; + } + + void + sepgsql_schema_alter(Oid nspOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SETATTR, true); + } + + void + sepgsql_schema_drop(Oid nspOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__DROP, true); + } + + Oid + sepgsql_schema_relabel(Oid nspOid, DefElem *newLabel) + { + sepgsql_sid_t sid; + + if (!sepgsqlIsEnabled()) + { + if (newLabel) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux is disabled now"))); + return InvalidOid; + } + sid.relid = NamespaceRelationId; + sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); + + /* db_schema:{setattr relabelfrom} for older seclabel */ + sepgsql_schema_common(nspOid, + SEPG_DB_SCHEMA__SETATTR | + SEPG_DB_SCHEMA__RELABELFROM, true); + + /* db_schema:{relabelto} for newer seclabel */ + sepgsqlClientHasPerms(sid, + SEPG_CLASS_DB_SCHEMA, + SEPG_DB_SCHEMA__RELABELTO, + get_namespace_name(nspOid), true); + + return sid.secid; + } + + void + sepgsql_schema_grant(Oid nspOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SETATTR, true); + } + + bool + sepgsql_schema_search(Oid nspOid, bool abort) + { + if (!sepgsqlIsEnabled()) + return true; + + return sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SEARCH, abort); + } + + /* ------------------------------------------------------------ * + * + * Pg_attribute related security hooks + * + * ------------------------------------------------------------ */ + static bool + sepgsql_attribute_common(Oid relOid, AttrNumber attnum, + uint32 required, bool abort) + { + Form_pg_attribute attForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + char auname[NAMEDATALEN * 2 + 3]; + bool rc = true; + + /* Caller prevent case when relkind != RELKIND_RELATION */ + Assert(get_rel_relkind(relOid) == RELKIND_RELATION); + + tuple = SearchSysCache(ATTNUM, + ObjectIdGetDatum(relOid), + Int16GetDatum(attnum), + 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for attribute %d of relation %u", + attnum, relOid); + attForm = (Form_pg_attribute) GETSTRUCT(tuple); + + /* + * NOTE: when a table to be dropped, corresponding attributes + * are also removed. Some of them can be already logically + * dropped using ALTER TABLE ... DROP statement. + * In this case, SE-PostgreSQL does not check anything. + * If any other situation touches dropped column, it is a bug. + */ + if (attForm->attisdropped) + goto skip; + + sprintf(auname, "%s.%s", get_rel_name(relOid), NameStr(attForm->attname)); + + sid = sepgsqlGetTupleSecid(AttributeRelationId, tuple, &tclass); + + rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); + + skip: + ReleaseSysCache(tuple); + + return rc; + } + + Oid + sepgsql_attribute_create(Oid relOid, ColumnDef *cdef) + { + sepgsql_sid_t sid; + char relkind; + + if (!sepgsqlIsEnabled()) + { + if (cdef->secLabel) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux is disabled now"))); + return InvalidOid; + } + + relkind = get_rel_relkind(relOid); + if (relkind == RELKIND_RELATION) + { + char auname[NAMEDATALEN * 2 + 3]; + + if (!cdef->secLabel) + sid = sepgsqlGetDefaultColumnSecid(relOid); + else + { + char *label = strVal(((DefElem *)cdef->secLabel)->arg); + + sid.relid = AttributeRelationId; + sid.secid = securityTransSecLabelIn(sid.relid, label); + } + + sprintf(auname, "%s.%s", get_rel_name(relOid), cdef->colname); + sepgsqlClientHasPerms(sid, + SEPG_CLASS_DB_COLUMN, + SEPG_DB_COLUMN__CREATE, + auname, true); + } + else + { + /* no need to check for toast relation */ + if (relkind != RELKIND_TOASTVALUE) + sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); + return InvalidOid; + } + + return sid.secid; + } + + void + sepgsql_attribute_alter(Oid relOid, const char *attname) + { + AttrNumber attno; + char relkind; + + if (!sepgsqlIsEnabled()) + return; + + /* + * If the target attribute does not exist, an error + * shall be raised later. + */ + attno = get_attnum(relOid, attname); + if (attno == InvalidAttrNumber) + return; + + relkind = get_rel_relkind(relOid); + if (relkind == RELKIND_RELATION) + { + sepgsql_attribute_common(relOid, attno, SEPG_DB_COLUMN__SETATTR, true); + } + else if (relkind != RELKIND_TOASTVALUE) + { + sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); + } + } + + void + sepgsql_attribute_drop(Oid relOid, AttrNumber attnum) + { + if (!sepgsqlIsEnabled()) + return; + + /* + * We only need to check db_column:{drop} when relkind equals + * RELKIND_RELATION, because db_xxx:{drop} permission is already + * checked in other cases. (e.g DROP SEQUENCE, ...) + */ + if (get_rel_relkind(relOid) == RELKIND_RELATION) + sepgsql_attribute_common(relOid, attnum, + SEPG_DB_COLUMN__DROP, true); + } + + void + sepgsql_attribute_grant(Oid relOid, AttrNumber attnum) + { + char relkind; + + if (!sepgsqlIsEnabled()) + return; + + relkind = get_rel_relkind(relOid); + if (relkind == RELKIND_RELATION) + { + sepgsql_attribute_common(relOid, attnum, SEPG_DB_COLUMN__SETATTR, true); + } + else if (relkind != RELKIND_TOASTVALUE) + { + sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); + } + } + + Oid + sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, DefElem *newLabel) + { + sepgsql_sid_t sid; + char auname[NAMEDATALEN * 2 + 3]; + + if (!sepgsqlIsEnabled()) + { + if (!newLabel) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux is disabled now"))); + return InvalidOid; + } + + Assert(get_rel_relkind(relOid) == RELKIND_RELATION); + + sid.relid = AttributeRelationId; + sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); + + /* db_column:{setattr relabelfrom} */ + sepgsql_attribute_common(relOid, attnum, + SEPG_DB_COLUMN__SETATTR | + SEPG_DB_COLUMN__RELABELFROM, true); + + /* db_column:{relabelto} */ + sprintf(auname, "%s.%s", + get_rel_name(relOid), + get_attname(relOid, attnum)); + sepgsqlClientHasPerms(sid, + SEPG_CLASS_DB_COLUMN, + SEPG_DB_COLUMN__RELABELTO, + auname, true); + + return sid.secid; + } + + /* ------------------------------------------------------------ * + * + * Pg_class related security hooks + * + * ------------------------------------------------------------ */ + static bool + sepgsql_relation_common(Oid relOid, uint32 required, bool abort) + { + Form_pg_class relForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + bool rc; + + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(relOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", relOid); + relForm = (Form_pg_class) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(RelationRelationId, tuple, &tclass); + rc = sepgsqlClientHasPerms(sid, tclass, required, + NameStr(relForm->relname), abort); + + ReleaseSysCache(tuple); + + return rc; + } + + /* + * sepgsql_relation_create + * It returns an array of security identifier for the new table + * and columns to be assigned. The corresponding security labels + * are already checked for db_table/db_sequence/db_column:{create} + * permission. + * In the default labeling rule, a column inherits the security + * label of its table, but we cannot refer it using system caches, + * because the command counter is not incremented under the + * heap_create_with_catalog(). Thus, we need to compute and check + * them prior to the actual creation of table and columns. + */ + Oid * + sepgsql_relation_create(const char *relName, + char relkind, + TupleDesc tupDesc, + Oid nspOid, + DefElem *relLabel, + List *colList, + bool createAs, + bool permission) + { + Oid *secLabels; + sepgsql_sid_t relsid; + uint16 tclass; + uint32 required; + int index; + + if (!sepgsqlIsEnabled()) + return NULL; + + switch (relkind) + { + case RELKIND_RELATION: + if (!relLabel) + relsid = sepgsqlGetDefaultTableSecid(nspOid); + else + { + relsid.relid = RelationRelationId; + relsid.secid = securityTransSecLabelIn(relsid.relid, + strVal(relLabel->arg)); + } + tclass = SEPG_CLASS_DB_TABLE; + required = SEPG_DB_TABLE__CREATE; + if (createAs) + required |= SEPG_DB_TABLE__INSERT; + break; + + case RELKIND_SEQUENCE: + if (!relLabel) + relsid = sepgsqlGetDefaultSequenceSecid(nspOid); + else + { + relsid.relid = RelationRelationId; + relsid.secid = securityTransSecLabelIn(relsid.relid, + strVal(relLabel->arg)); + } + tclass = SEPG_CLASS_DB_SEQUENCE; + required = SEPG_DB_SEQUENCE__CREATE; + break; + + default: + if (!relLabel) + relsid = sepgsqlGetDefaultTupleSecid(RelationRelationId); + else + { + /* should not be happen */ + relsid.relid = RelationRelationId; + relsid.secid = securityTransSecLabelIn(relsid.relid, + strVal(relLabel->arg)); + } + tclass = SEPG_CLASS_DB_TUPLE; + required = SEPG_DB_TUPLE__INSERT; + break; + } + + /* + * The secLabeld array stores security identifiers to be assigned + * on the new table and columns. + * + * secLabels[0] is security identifier of the table. + * secLabels[attnum - FirstLowInvalidHeapAttributeNumber] + * is security identifier of columns (if necessary). + */ + secLabels = palloc0(sizeof(Oid) * (tupDesc->natts + - FirstLowInvalidHeapAttributeNumber)); + + /* relation's security identifier to be assigned on */ + secLabels[0] = relsid.secid; + + /* + * Note that this hook can be called during initdb processes. + * It is an exception of access controls, so we skip any checks. + * + * And, we don't need any checks for toast relations, because + * it is a quite internal stuff. + */ + if (permission) + { + /* db_schema:{add_name} */ + sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_table:{create}, db_sequence:{create} or db_tuple:{insert} */ + sepgsqlClientHasPerms(relsid, tclass, required, relName, true); + } + + /* no individual security context expect for RELKIND_RELATION */ + if (relkind != RELKIND_RELATION) + return secLabels; + + /* + * db_column:{create} permission + */ + for (index = FirstLowInvalidHeapAttributeNumber + 1; + index < tupDesc->natts; + index++) + { + Form_pg_attribute attr; + sepgsql_sid_t attsid = { InvalidOid, InvalidOid }; + char attname[NAMEDATALEN * 2 + 3]; + ListCell *l; + + /* skip unnecessary attributes */ + if (index == ObjectIdAttributeNumber && !tupDesc->tdhasoid) + continue; + + if (index < 0) + attr = SystemAttributeDefinition(index, tupDesc->tdhasoid); + else + attr = tupDesc->attrs[index]; + + /* Is there any given security context? */ + foreach (l, colList) + { + ColumnDef *cdef = lfirst(l); + + if (cdef->secLabel && + strcmp(cdef->colname, NameStr(attr->attname)) == 0) + { + attsid.relid = AttributeRelationId; + attsid.secid = securityTransSecLabelIn(attsid.relid, + strVal(((DefElem *)cdef->secLabel)->arg)); + break; + } + } + + /* default security context, if not given */ + if (!SidIsValid(attsid)) + attsid = sepgsqlClientCreateSecid(relsid, + SEPG_CLASS_DB_COLUMN, + AttributeRelationId); + if (permission) + { + required = SEPG_DB_COLUMN__CREATE; + + if (createAs) + required |= SEPG_DB_COLUMN__INSERT; + + /* db_column:{create (insert)} */ + sprintf(attname, "%s.%s", relName, NameStr(attr->attname)); + sepgsqlClientHasPerms(attsid, + SEPG_CLASS_DB_COLUMN, + required, attname, true); + } + /* column's security identifier to be assigend on */ + secLabels[index - FirstLowInvalidHeapAttributeNumber] = attsid.secid; + } + + return secLabels; + } + + /* + * sepgsql_relation_copy + * It returns an array of security identifier of table and columns + * to be copied on make_new_heap(). It actually create a new temporary + * relation and insert all the tuples within original one into the + * temporary one, but swap_relation_files() swaps their file nodes. + * Thus, there are no changes from the viewpoint of users. + * SE-PostgreSQL also does not check and change anything. It simply + * copies security identifier of the source relation to the destination + * relation. + */ + Oid * + sepgsql_relation_copy(Relation src) + { + Oid *secLabels; + HeapTuple tuple; + Oid relOid = RelationGetRelid(src); + int index; + + if (!sepgsqlIsEnabled()) + return NULL; + + /* see the comment at sepgsqlCreateTableColumn*/ + secLabels = palloc0(sizeof(Oid) * (RelationGetDescr(src)->natts + - FirstLowInvalidHeapAttributeNumber)); + + /* copy table's security identifier */ + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(relOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation \"%s\"", + RelationGetRelationName(src)); + + secLabels[0] = HeapTupleGetSecid(tuple); + + ReleaseSysCache(tuple); + + /* copy column's security identifier */ + for (index = FirstLowInvalidHeapAttributeNumber + 1; + index < RelationGetDescr(src)->natts; + index++) + { + Form_pg_attribute attr; + + if (index < 0) + attr = SystemAttributeDefinition(index, true); + else + attr = RelationGetDescr(src)->attrs[index]; + + tuple = SearchSysCache(ATTNUM, + ObjectIdGetDatum(relOid), + Int16GetDatum(attr->attnum), + 0, 0); + if (!HeapTupleIsValid(tuple)) + continue; + + secLabels[index - FirstLowInvalidHeapAttributeNumber] + = HeapTupleGetSecid(tuple); + + ReleaseSysCache(tuple); + } + + return secLabels; + } + + void + sepgsql_relation_alter(Oid relOid, const char *newName, Oid newNsp) + { + Form_pg_class relForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(relOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", relOid); + relForm = (Form_pg_class) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(RelationRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TABLE__SETATTR, + NameStr(relForm->relname), true); + + /* db_schema:{add_name remove_name}, if necessary */ + if (newName || OidIsValid(newNsp)) + { + if (!OidIsValid(newNsp)) + sepgsql_schema_common(relForm->relnamespace, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + else + { + sepgsql_schema_common(relForm->relnamespace, + SEPG_DB_SCHEMA__REMOVE_NAME, true); + sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true); + } + } + ReleaseSysCache(tuple); + } + + void + sepgsql_relation_drop(Oid relOid) + { + if (!sepgsqlIsEnabled()) + return; + + if (get_rel_relkind(relOid) == RELKIND_TOASTVALUE) + return; + + sepgsql_relation_common(relOid, SEPG_DB_TABLE__DROP, true); + } + + void + sepgsql_relation_grant(Oid relOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); + } + + Oid + sepgsql_relation_relabel(Oid relOid, DefElem *newLabel) + { + sepgsql_sid_t sid; + char relkind; + + if (!sepgsqlIsEnabled()) + { + if (newLabel) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux is disabled now"))); + return InvalidOid; + } + + relkind = get_rel_relkind(relOid); + if (relkind != RELKIND_RELATION && relkind != RELKIND_SEQUENCE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Unable to set security label on \"%s\"", + get_rel_name(relOid)))); + + /* input security context */ + sid.relid = RelationRelationId; + sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); + + /* db_table/db_sequence:{setattr relabelfrom} */ + sepgsql_relation_common(relOid, + SEPG_DB_TABLE__SETATTR | + SEPG_DB_TABLE__RELABELFROM, true); + + /* db_table/db_sequence:{relabelto} */ + sepgsqlClientHasPerms(sid, + (relkind == RELKIND_RELATION + ? SEPG_CLASS_DB_TABLE + : SEPG_CLASS_DB_SEQUENCE), + SEPG_DB_TABLE__RELABELTO, + get_rel_name(relOid), true); + + return sid.secid; + } + + void + sepgsql_relation_get_transaction_id(Oid relOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_relation_common(relOid, SEPG_DB_TABLE__GETATTR, true); + } + + void + sepgsql_relation_copy_definition(Oid relOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_relation_common(relOid, SEPG_DB_TABLE__GETATTR, true); + } + + void + sepgsql_relation_truncate(Relation rel) + { + HeapScanDesc scan; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION); + + if (!sepgsqlIsEnabled()) + return; + + /* check db_table:{delete} permission */ + sepgsql_relation_common(RelationGetRelid(rel), + SEPG_DB_TABLE__DELETE, true); + + /* row-level access control is enabled? */ + if (!sepostgresql_row_level) + return; + + /* check db_tuple:{delete} permission */ + scan = heap_beginscan(rel, SnapshotNow, 0, NULL); + + while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + { + sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__DELETE, + NULL, true); + } + heap_endscan(scan); + } + + void + sepgsql_relation_references(Relation rel, int16 *attnums, int natts) + { + Oid relOid = RelationGetRelid(rel); + int i; + + Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION); + + if (!sepgsqlIsEnabled()) + return; + + /* db_table:{reference} */ + sepgsql_relation_common(relOid, SEPG_DB_TABLE__REFERENCE, true); + + for (i=0; i < natts; i++) + sepgsql_attribute_common(relOid, attnums[i], + SEPG_DB_COLUMN__REFERENCE, true); + } + + void + sepgsql_relation_lock(Oid relOid) + { + if (!sepgsqlIsEnabled()) + return; + + if (get_rel_relkind(relOid) != RELKIND_RELATION) + return; + + sepgsql_relation_common(relOid, SEPG_DB_TABLE__LOCK, true); + } + + void + sepgsql_view_replace(Oid viewOid) + { + if (!sepgsqlIsEnabled()) + return; + + Assert(get_rel_relkind(viewOid) == RELKIND_VIEW); + + sepgsql_relation_common(viewOid, SEPG_DB_TABLE__SETATTR, true); + } + + void + sepgsql_index_create(Oid relOid, Oid nspOid, bool check_rights) + { + if (!sepgsqlIsEnabled()) + return; + + if (check_rights) + { + /* db_table:{setattr} */ + sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); + + /* db_schema:{add_name} */ + sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); + } + } + + void + sepgsql_sequence_get_value(Oid seqOid) + { + if (!sepgsqlIsEnabled()) + return; + + Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE); + + sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__GET_VALUE, true); + } + + void + sepgsql_sequence_next_value(Oid seqOid) + { + if (!sepgsqlIsEnabled()) + return; + + Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE); + + sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__NEXT_VALUE, true); + } + + void + sepgsql_sequence_set_value(Oid seqOid) + { + if (!sepgsqlIsEnabled()) + return; + + Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE); + + sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__SET_VALUE, true); + } + + /* ------------------------------------------------------------ * + * + * Pg_proc related security hooks + * + * ------------------------------------------------------------ */ + static bool + sepgsql_proc_common(Oid procOid, uint32 required, bool abort) + { + sepgsql_sid_t sid; + HeapTuple tuple; + uint16 tclass; + const char *auname; + bool rc; + + tuple = SearchSysCache(PROCOID, + ObjectIdGetDatum(procOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for procedure: %u", procOid); + + auname = NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname); + sid = sepgsqlGetTupleSecid(ProcedureRelationId, tuple, &tclass); + + rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); + + ReleaseSysCache(tuple); + + return rc; + } + + Oid + sepgsql_proc_create(const char *procName, HeapTuple oldTup, + Oid nspOid, Oid langOid, DefElem *newLabel) + { + sepgsql_sid_t sid; + HeapTuple tuple; + uint32 required; + bool trusted; + + if (!sepgsqlIsEnabled()) + return InvalidOid; + + if (!HeapTupleIsValid(oldTup)) + { + /* create a new function */ + required = SEPG_DB_PROCEDURE__CREATE; + if (!newLabel) + sid = sepgsqlGetDefaultProcedureSecid(nspOid); + else + { + sid.relid = ProcedureRelationId; + sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); + } + } + else if (!newLabel) + { + /* replace an existing function, without any label */ + required = SEPG_DB_PROCEDURE__SETATTR; + sid = sepgsqlGetTupleSecid(ProcedureRelationId, oldTup, NULL); + } + else + { + /* replace an existing function, with relabeling */ + sepgsql_proc_common(HeapTupleGetOid(oldTup), + SEPG_DB_PROCEDURE__SETATTR | + SEPG_DB_PROCEDURE__RELABELFROM, true); + + required = SEPG_DB_PROCEDURE__RELABELTO; + sid = sepgsqlGetTupleSecid(ProcedureRelationId, oldTup, NULL); + } + + /* Procedural language is trusted? */ + tuple = SearchSysCache(LANGOID, + ObjectIdGetDatum(langOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for procedural langugage: %u", langOid); + + trusted = ((Form_pg_language) GETSTRUCT(tuple))->lanpltrusted; + if (!trusted) + required |= SEPG_DB_PROCEDURE__UNTRUSTED; + + ReleaseSysCache(tuple); + + /* check it */ + sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_PROCEDURE, + required, procName, true); + + return sid.secid; + } + + void + sepgsql_proc_alter(Oid procOid, const char *newName, Oid newNsp) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); + if (newName || OidIsValid(newNsp)) + { + HeapTuple tuple; + Oid oldNsp; + + tuple = SearchSysCache(PROCOID, + ObjectIdGetDatum(procOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for function %u", procOid); + + oldNsp = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; + + ReleaseSysCache(tuple); + + if (!OidIsValid(newNsp)) + { + sepgsql_schema_common(oldNsp, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + } + else + { + sepgsql_schema_common(oldNsp, SEPG_DB_SCHEMA__REMOVE_NAME, true); + sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true); + } + } + } + + void + sepgsql_proc_drop(Oid procOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__DROP, true); + } + + void + sepgsql_proc_grant(Oid procOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); + } + + Oid + sepgsql_proc_relabel(Oid procOid, DefElem *newLabel) + { + sepgsql_sid_t sid; + + if (!sepgsqlIsEnabled()) + { + if (newLabel) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux is disabled now"))); + return InvalidOid; + } + + sid.relid = ProcedureRelationId; + sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); + + /* db_procedure:{setattr relabelfrom} for older seclabel */ + sepgsql_proc_common(procOid, + SEPG_DB_PROCEDURE__SETATTR | + SEPG_DB_PROCEDURE__RELABELFROM, true); + /* db_procedure:{relabelto} for newer seclabel */ + sepgsqlClientHasPerms(sid, + SEPG_CLASS_DB_PROCEDURE, + SEPG_DB_PROCEDURE__RELABELTO, + get_func_name(procOid), true); + return sid.secid; + } + + void + sepgsql_proc_execute(Oid procOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__EXECUTE, true); + } + + bool + sepgsql_proc_hint_inlined(HeapTuple protup) + { + security_context_t newcon; + sepgsql_sid_t sid; + + if (!sepgsqlIsEnabled()) + return true; + + if (!sepgsql_proc_common(HeapTupleGetOid(protup), + SEPG_DB_PROCEDURE__EXECUTE, false)) + return false; + /* + * 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. + */ + sid = sepgsqlGetTupleSecid(ProcedureRelationId, protup, NULL); + + newcon = sepgsqlClientCreateLabel(sid, SEPG_CLASS_PROCESS); + + if (strcmp(sepgsqlGetClientLabel(), newcon) == 0) + return true; + + return false; + } + + /* + * 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 + sepgsql_proc_entrypoint(FmgrInfo *flinfo, HeapTuple protup) + { + struct TrustedProcedureCache *tcache; + security_context_t newcon; + sepgsql_sid_t proSid; + + if (!sepgsqlIsEnabled()) + return; + + proSid = sepgsqlGetTupleSecid(ProcedureRelationId, + protup, NULL); + + newcon = sepgsqlClientCreateLabel(proSid, SEPG_CLASS_PROCESS); + + /* Do nothing, if it is not a trusted procedure */ + if (strcmp(newcon, sepgsqlGetClientLabel()) == 0) + return; + + /* check db_procedure:{entrypoint} */ + sepgsqlClientHasPerms(proSid, + SEPG_CLASS_DB_PROCEDURE, + SEPG_DB_PROCEDURE__ENTRYPOINT, + NULL, 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; + } + + /* ------------------------------------------------------------ * + * + * Pg_cast related security hooks + * + * ------------------------------------------------------------ */ + Oid + sepgsql_cast_create(Oid sourceTypOid, Oid targetTypOid, Oid funcOid) + { + sepgsql_sid_t sid; + char audit_buffer[2*NAMEDATALEN+10]; + + if (!sepgsqlIsEnabled()) + return InvalidOid; + + sid = sepgsqlGetDefaultTupleSecid(CastRelationId); + + snprintf(audit_buffer, sizeof(audit_buffer), "%s::%s", + format_type_be(sourceTypOid), format_type_be(targetTypOid)); + + sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + audit_buffer, true); + + if (OidIsValid(funcOid)) + sepgsql_proc_common(funcOid, SEPG_DB_PROCEDURE__INSTALL, true); + + return sid.secid; + } + + void + sepgsql_cast_drop(Oid castOid) + { + Form_pg_cast castForm; + Relation rel; + HeapTuple tuple; + ScanKeyData skey; + SysScanDesc scan; + sepgsql_sid_t sid; + uint16 tclass; + char audit_buffer[2*NAMEDATALEN+10]; + + if (!sepgsqlIsEnabled()) + return; + + rel = heap_open(CastRelationId, AccessShareLock); + + ScanKeyInit(&skey, + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(castOid)); + + scan = systable_beginscan(rel, CastOidIndexId, true, + SnapshotNow, 1, &skey); + tuple = systable_getnext(scan); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "could not find tuple for cast: %u", castOid); + + castForm = (Form_pg_cast) GETSTRUCT(tuple); + + snprintf(audit_buffer, sizeof(audit_buffer), "%s::%s", + format_type_be(castForm->castsource), + format_type_be(castForm->casttarget)); + + sid = sepgsqlGetTupleSecid(CastRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__DELETE, + audit_buffer, true); + + systable_endscan(scan); + + heap_close(rel, AccessShareLock); + } + + /* ------------------------------------------------------------ * + * + * Pg_conversion related security hooks + * + * ------------------------------------------------------------ */ + Oid + sepgsql_conversion_create(const char *convName, Oid nspOid, Oid procOid) + { + sepgsql_sid_t sid; + + if (!sepgsqlIsEnabled()) + return InvalidOid; + + sid = sepgsqlGetDefaultTupleSecid(ConversionRelationId); + sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + convName, true); + + /* db_schema:{add_name} */ + sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_procedure:{install} */ + sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true); + + return sid.secid; + } + + void + sepgsql_conversion_alter(Oid convOid, const char *newName) + { + Form_pg_conversion convForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(CONVOID, + ObjectIdGetDatum(convOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for conversion %u", convOid); + convForm = (Form_pg_conversion) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(ConversionRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__UPDATE, + NameStr(convForm->conname), true); + if (newName) + { + Oid nspOid = convForm->connamespace; + + sepgsql_schema_common(nspOid, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + } + ReleaseSysCache(tuple); + } + + void + sepgsql_conversion_drop(Oid convOid) + { + Form_pg_conversion convForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(CONVOID, + ObjectIdGetDatum(convOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for conversion %u", convOid); + convForm = (Form_pg_conversion) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(ConversionRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__UPDATE, + NameStr(convForm->conname), true); + + /* db_schema:{remove_name} */ + sepgsql_schema_common(convForm->connamespace, + SEPG_DB_SCHEMA__REMOVE_NAME, true); + + ReleaseSysCache(tuple); + } + + /* ------------------------------------------------------------ * + * + * Pg_foreign_data_wrapper related security hooks + * + * ------------------------------------------------------------ */ + static bool + sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort) + { + Form_pg_foreign_data_wrapper fdwForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + bool rc; + + tuple = SearchSysCache(FOREIGNDATAWRAPPEROID, + ObjectIdGetDatum(fdwOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for FDW: %u", fdwOid); + fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(ForeignDataWrapperRelationId, tuple, &tclass); + rc = sepgsqlClientHasPerms(sid, tclass, required, + NameStr(fdwForm->fdwname), abort); + ReleaseSysCache(tuple); + + return rc; + } + + Oid + sepgsql_fdw_create(const char *fdwName, Oid fdwValidator) + { + sepgsql_sid_t sid; + + if (!sepgsqlIsEnabled()) + return InvalidOid; + + sid = sepgsqlGetDefaultTupleSecid(ForeignDataWrapperRelationId); + sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + fdwName, true); + + /* db_procedure:{install} */ + if (OidIsValid(fdwValidator)) + sepgsql_proc_common(fdwValidator, SEPG_DB_PROCEDURE__INSTALL, true); + + return sid.secid; + } + + void + sepgsql_fdw_alter(Oid fdwOid, Oid newValidator) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); + + /* db_procedure:{install} */ + if (OidIsValid(newValidator)) + sepgsql_proc_common(newValidator, SEPG_DB_PROCEDURE__INSTALL, true); + } + + void + sepgsql_fdw_drop(Oid fdwOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__DELETE, true); + } + + void + sepgsql_fdw_grant(Oid fdwOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); + } + + /* ------------------------------------------------------------ * + * + * Pg_foreign_server related security hooks + * + * ------------------------------------------------------------ */ + static bool + sepgsql_foreign_server_common(Oid fsrvOid, uint32 required, bool abort) + { + Form_pg_foreign_server fsrvForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + bool rc; + + tuple = SearchSysCache(FOREIGNSERVEROID, + ObjectIdGetDatum(fsrvOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for foreign server %u", fsrvOid); + fsrvForm = (Form_pg_foreign_server) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(ForeignServerRelationId, tuple, &tclass); + rc = sepgsqlClientHasPerms(sid, tclass, required, + NameStr(fsrvForm->srvname), abort); + ReleaseSysCache(tuple); + + return rc; + } + + Oid + sepgsql_foreign_server_create(const char *fsrvName) + { + sepgsql_sid_t sid; + + if (!sepgsqlIsEnabled()) + return InvalidOid; + + sid = sepgsqlGetDefaultTupleSecid(ForeignServerRelationId); + sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + fsrvName, true); + + return sid.secid; + } + + void + sepgsql_foreign_server_alter(Oid fsrvOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__UPDATE, true); + } + + void + sepgsql_foreign_server_drop(Oid fsrvOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__DELETE, true); + } + + void + sepgsql_foreign_server_grant(Oid fsrvOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__UPDATE, true); + } + + /* ------------------------------------------------------------ * + * + * Pg_language related security hooks + * + * ------------------------------------------------------------ */ + static bool + sepgsql_language_common(Oid langOid, uint32 required, bool abort) + { + Form_pg_language langForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + bool rc; + + tuple = SearchSysCache(LANGOID, + ObjectIdGetDatum(langOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for language %u", langOid); + langForm = (Form_pg_language) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(LanguageRelationId, tuple, &tclass); + rc = sepgsqlClientHasPerms(sid, tclass, required, + NameStr(langForm->lanname), abort); + + ReleaseSysCache(tuple); + + return rc; + } + + Oid + sepgsql_language_create(const char *langName, Oid handlerOid, Oid validatorOid) + { + sepgsql_sid_t sid; + + if (!sepgsqlIsEnabled()) + return InvalidOid; + + sid = sepgsqlGetDefaultTupleSecid(LanguageRelationId); + sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, langName, true); + + /* db_procedure:{install} */ + if (OidIsValid(handlerOid)) + sepgsql_proc_common(handlerOid, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(validatorOid)) + sepgsql_proc_common(validatorOid, SEPG_DB_PROCEDURE__INSTALL, true); + + return sid.secid; + } + + void + sepgsql_language_alter(Oid langOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_language_common(langOid, SEPG_DB_TUPLE__UPDATE, true); + } + + void + sepgsql_language_drop(Oid langOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_language_common(langOid, SEPG_DB_TUPLE__DELETE, true); + } + + void + sepgsql_language_grant(Oid langOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_language_common(langOid, SEPG_DB_TUPLE__UPDATE, true); + } + + /* ------------------------------------------------------------ * + * + * Pg_largeobject related security hooks + * (need to backport v8.5 feature) + * ------------------------------------------------------------ */ + Oid + sepgsql_largeobject_create(Oid loid, DefElem *newLabel) + { + return InvalidOid; + } + + void + sepgsql_largeobject_alter(Oid loid, Oid newOwner) + {} + + void + sepgsql_largeobject_drop(Oid loid, bool dacSkip) + {} + + void + sepgsql_largeobject_read(Oid loid) + {} + + void + sepgsql_largeobject_write(Oid loid) + {} + + void + sepgsql_largeobject_export(Oid loid, const char *filename) + {} + + Oid + sepgsql_largeobject_import(Oid loid, const char *filename, DefElem *newLabel) + {} + + /* ------------------------------------------------------------ * + * + * Pg_opclass related security hooks + * + * ------------------------------------------------------------ */ + Oid + sepgsql_opclass_create(const char *opcName, Oid nspOid) + { + sepgsql_sid_t sid; + + if (!sepgsqlIsEnabled()) + return InvalidOid; + + sid = sepgsqlGetDefaultTupleSecid(OperatorClassRelationId); + sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + opcName, true); + + /* db_schema:{add_name} */ + sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); + + return sid.secid; + } + + void + sepgsql_opclass_alter(Oid opcOid, const char *newName) + { + Form_pg_opclass opcForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(CLAOID, + ObjectIdGetDatum(opcOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for opclass %u", opcOid); + opcForm = (Form_pg_opclass) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(OperatorClassRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__UPDATE, + NameStr(opcForm->opcname), true); + + /* db_schema:{add_name remove_name} */ + if (newName) + { + sepgsql_schema_common(opcForm->opcnamespace, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + } + ReleaseSysCache(tuple); + } + + void + sepgsql_opclass_drop(Oid opcOid) + { + Form_pg_opclass opcForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(CLAOID, + ObjectIdGetDatum(opcOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for opclass %u", opcOid); + opcForm = (Form_pg_opclass) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(OperatorClassRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__UPDATE, + NameStr(opcForm->opcname), true); + + /* db_schema:{remove_name} */ + sepgsql_schema_common(opcForm->opcnamespace, + SEPG_DB_SCHEMA__REMOVE_NAME, true); + + ReleaseSysCache(tuple); + } + + /* ------------------------------------------------------------ * + * + * Pg_opfamily related security hooks + * + * ------------------------------------------------------------ */ + Oid + sepgsql_opfamily_create(const char *opfName, Oid nspOid) + { + sepgsql_sid_t sid; + + if (!sepgsqlIsEnabled()) + return InvalidOid; + + sid = sepgsqlGetDefaultTupleSecid(OperatorFamilyRelationId); + sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + opfName, true); + + /* db_schema:{add_name} */ + sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); + + return sid.secid; + } + + void + sepgsql_opfamily_alter(Oid opfOid, const char *newName) + { + Form_pg_opfamily opfForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(OPFAMILYOID, + ObjectIdGetDatum(opfOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for operator family: %u", opfOid); + opfForm = (Form_pg_opfamily) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__UPDATE, + NameStr(opfForm->opfname), true); + if (newName) + { + sepgsql_schema_common(opfForm->opfnamespace, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + } + ReleaseSysCache(tuple); + } + + void + sepgsql_opfamily_drop(Oid opfOid) + { + Form_pg_opfamily opfForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(OPFAMILYOID, + ObjectIdGetDatum(opfOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for operator family: %u", opfOid); + opfForm = (Form_pg_opfamily) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__DELETE, + NameStr(opfForm->opfname), true); + + /* db_schema:{remove_name} */ + sepgsql_schema_common(opfForm->opfnamespace, + SEPG_DB_SCHEMA__REMOVE_NAME, true); + + ReleaseSysCache(tuple); + } + + void + sepgsql_opfamily_add_operator(Oid opfOid, Oid operOid) + { + if (!sepgsqlIsEnabled()) + return; + + /* currently, do nothing here */ + } + + void + sepgsql_opfamily_add_procedure(Oid opfOid, Oid procOid) + { + if (!sepgsqlIsEnabled()) + return; + + /* + * Note that db_tuple:{setattr} is already checked at the + * earlier phase, so db_procedure:{install} is only needed. + */ + if (OidIsValid(procOid)) + sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true); + } + + /* ------------------------------------------------------------ * + * + * Pg_operator related security hooks + * + * ------------------------------------------------------------ */ + static bool + sepgsql_operator_common(Oid oprOid, uint32 required, bool abort) + { + Form_pg_operator oprForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + bool rc; + + tuple = SearchSysCache(OPEROID, + ObjectIdGetDatum(oprOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for operator: %u", oprOid); + oprForm = (Form_pg_operator) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(OperatorRelationId, tuple, &tclass); + rc = sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__DELETE, + NameStr(oprForm->oprname), abort); + + ReleaseSysCache(tuple); + + return rc; + } + + Oid + sepgsql_operator_create(const char *oprName, Oid oprOid, Oid nspOid, + Oid codeFn, Oid restFn, Oid joinFn) + { + HeapTuple tuple; + sepgsql_sid_t sid; + uint32 required; + + if (!sepgsqlIsEnabled()) + return InvalidOid; + + if (!OidIsValid(oprOid)) + { + sid = sepgsqlGetDefaultTupleSecid(OperatorRelationId); + required = SEPG_DB_TUPLE__INSERT; + } + else + { + tuple = SearchSysCache(OPEROID, + ObjectIdGetDatum(oprOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for operator %u", oprOid); + + sid = sepgsqlGetTupleSecid(OperatorRelationId, tuple, NULL); + + ReleaseSysCache(tuple); + + required = SEPG_DB_TUPLE__UPDATE; + } + + sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, + required, oprName, true); + + /* db_schema:{add_name} checks */ + if (!OidIsValid(oprOid)) + sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_procedure:{install} checks */ + if (OidIsValid(codeFn)) + sepgsql_proc_common(codeFn, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(restFn)) + sepgsql_proc_common(restFn, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(joinFn)) + sepgsql_proc_common(joinFn, SEPG_DB_PROCEDURE__INSTALL, true); + + return sid.secid; + } + + void + sepgsql_operator_alter(Oid oprOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_operator_common(oprOid, SEPG_DB_TUPLE__UPDATE, true); + } + + void + sepgsql_operator_drop(Oid oprOid) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_operator_common(oprOid, SEPG_DB_TUPLE__DELETE, true); + } + + /* ------------------------------------------------------------ * + * + * Pg_rewrite related security hooks + * + * ------------------------------------------------------------ */ + void + sepgsql_rule_create(Oid relOid, const char *ruleName) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); + } + + void + sepgsql_rule_drop(Oid relOid, const char *ruleName) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); + } + + /* ------------------------------------------------------------ * + * + * Pg_trigger related security hooks + * + * ------------------------------------------------------------ */ + void + sepgsql_trigger_create(Oid relOid, const char *trigName, Oid procOid) + { + if (!sepgsqlIsEnabled()) + return; + + /* db_table:{setattr} */ + sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); + + /* db_procedure:{install} */ + sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true); + } + + void + sepgsql_trigger_alter(Oid relOid, const char *trigName) + { + if (!sepgsqlIsEnabled()) + return; + + /* db_table:{setattr} */ + sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); + } + + void + sepgsql_trigger_drop(Oid relOid, const char *trigName) + { + if (!sepgsqlIsEnabled()) + return; + + /* db_table:{setattr} */ + sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); + } + + /* ------------------------------------------------------------ * + * + * Pg_type related security hooks + * + * ------------------------------------------------------------ */ + Oid + sepgsql_ts_config_create(const char *cfgName, Oid nspOid) + { + sepgsql_sid_t sid; + + if (!sepgsqlIsEnabled()) + return InvalidOid; + + sid = sepgsqlGetDefaultTupleSecid(TSConfigRelationId); + sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + cfgName, true); + + /* db_schema:{add_name} */ + sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); + + return sid.secid; + } + + void + sepgsql_ts_config_alter(Oid cfgOid, const char *newName) + { + Form_pg_ts_config cfgForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(TSCONFIGOID, + ObjectIdGetDatum(cfgOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for text search dictionary %u", cfgOid); + cfgForm = (Form_pg_ts_config) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(TSConfigRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__UPDATE, + NameStr(cfgForm->cfgname), true); + if (newName) + { + sepgsql_schema_common(cfgForm->cfgnamespace, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + } + ReleaseSysCache(tuple); + } + + void + sepgsql_ts_config_drop(Oid cfgOid) + { + Form_pg_ts_config cfgForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(TSCONFIGOID, + ObjectIdGetDatum(cfgOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for text search dictionary %u", cfgOid); + cfgForm = (Form_pg_ts_config) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(TSConfigRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__DELETE, + NameStr(cfgForm->cfgname), true); + + /* db_schema:{remove_name} */ + sepgsql_schema_common(cfgForm->cfgnamespace, + SEPG_DB_SCHEMA__REMOVE_NAME, true); + + ReleaseSysCache(tuple); + } + + /* ------------------------------------------------------------ * + * + * Pg_type related security hooks + * + * ------------------------------------------------------------ */ + Oid + sepgsql_ts_dict_create(const char *dictName, Oid nspOid) + { + sepgsql_sid_t sid; + + if (!sepgsqlIsEnabled()) + return InvalidOid; + + sid = sepgsqlGetDefaultTupleSecid(TSDictionaryRelationId); + sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + dictName, true); + + /* db_schema:{add_name} */ + sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); + + return sid.secid; + } + + void + sepgsql_ts_dict_alter(Oid dictOid, const char *newName) + { + Form_pg_ts_dict dictForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(TSDICTOID, + ObjectIdGetDatum(dictOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for text search dictionary %u", dictOid); + dictForm = (Form_pg_ts_dict) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(TSDictionaryRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__UPDATE, + NameStr(dictForm->dictname), true); + + /* db_schema:{add_name remove_name} */ + if (newName) + { + sepgsql_schema_common(dictForm->dictnamespace, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + } + ReleaseSysCache(tuple); + } + + void + sepgsql_ts_dict_drop(Oid dictOid) + { + Form_pg_ts_dict dictForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(TSDICTOID, + ObjectIdGetDatum(dictOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for text search dictionary %u", dictOid); + dictForm = (Form_pg_ts_dict) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(TSDictionaryRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__DELETE, + NameStr(dictForm->dictname), true); + + /* db_schema:{remove_name} */ + sepgsql_schema_common(dictForm->dictnamespace, + SEPG_DB_SCHEMA__REMOVE_NAME, true); + + ReleaseSysCache(tuple); + } + + /* ------------------------------------------------------------ * + * + * Pg_type related security hooks + * + * ------------------------------------------------------------ */ + Oid + sepgsql_ts_parser_create(const char *prsName, Oid nspOid, + Oid startFn, Oid tokenFn, Oid sendFn, + Oid headlineFn, Oid lextypeFn) + { + sepgsql_sid_t sid; + + if (!sepgsqlIsEnabled()) + return InvalidOid; + + sid = sepgsqlGetDefaultTupleSecid(TSParserRelationId); + sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + prsName, true); + + /* db_schema:{add_name} */ + sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_procedure:{install} */ + if (OidIsValid(startFn)) + sepgsql_proc_common(startFn, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(tokenFn)) + sepgsql_proc_common(tokenFn, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(sendFn)) + sepgsql_proc_common(sendFn, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(headlineFn)) + sepgsql_proc_common(headlineFn, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(lextypeFn)) + sepgsql_proc_common(lextypeFn, SEPG_DB_PROCEDURE__INSTALL, true); + + return sid.secid; + } + + void + sepgsql_ts_parser_alter(Oid prsOid, const char *newName) + { + Form_pg_ts_parser prsForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(TSPARSEROID, + ObjectIdGetDatum(prsOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for text search parser %u", prsOid); + + prsForm = (Form_pg_ts_parser) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(TSParserRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__UPDATE, + NameStr(prsForm->prsname), true); + if (newName) + { + sepgsql_schema_common(prsForm->prsnamespace, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + } + ReleaseSysCache(tuple); + } + + void + sepgsql_ts_parser_drop(Oid prsOid) + { + Form_pg_ts_parser prsForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(TSPARSEROID, + ObjectIdGetDatum(prsOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for text search parser %u", prsOid); + + prsForm = (Form_pg_ts_parser) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(TSParserRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__DELETE, + NameStr(prsForm->prsname), true); + + /* db_schema:{remove_name} */ + sepgsql_schema_common(prsForm->prsnamespace, + SEPG_DB_SCHEMA__REMOVE_NAME, true); + + ReleaseSysCache(tuple); + } + + /* ------------------------------------------------------------ * + * + * Pg_type related security hooks + * + * ------------------------------------------------------------ */ + Oid + sepgsql_ts_template_create(const char *tmplName, Oid nspOid, + Oid initFn, Oid lexizeFn) + { + sepgsql_sid_t sid; + + if (!sepgsqlIsEnabled()) + return InvalidOid; + + sid = sepgsqlGetDefaultTupleSecid(TSTemplateRelationId); + sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + tmplName, true); + + /* db_schema:{add_name} */ + sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_procedure:{install} */ + if (OidIsValid(initFn)) + sepgsql_proc_common(initFn, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(lexizeFn)) + sepgsql_proc_common(lexizeFn, SEPG_DB_PROCEDURE__INSTALL, true); + + return sid.secid; + } + + void + sepgsql_ts_template_alter(Oid tmplOid, const char *newName) + { + Form_pg_ts_template tmplForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(TSTEMPLATEOID, + ObjectIdGetDatum(tmplOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for text search template %u", tmplOid); + tmplForm = (Form_pg_ts_template) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(TSTemplateRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__UPDATE, + NameStr(tmplForm->tmplname), true); + if (newName) + { + sepgsql_schema_common(tmplForm->tmplnamespace, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + } + ReleaseSysCache(tuple); + } + + void + sepgsql_ts_template_drop(Oid tmplOid) + { + Form_pg_ts_template tmplForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(TSTEMPLATEOID, + ObjectIdGetDatum(tmplOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for text search template %u", tmplOid); + tmplForm = (Form_pg_ts_template) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(TSTemplateRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__DELETE, + NameStr(tmplForm->tmplname), true); + + /* db_schema:{remove_name} */ + sepgsql_schema_common(tmplForm->tmplnamespace, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + + ReleaseSysCache(tuple); + } + + /* ------------------------------------------------------------ * + * + * Pg_type related security hooks + * + * ------------------------------------------------------------ */ + Oid + sepgsql_type_create(const char *typName, HeapTuple oldTup, Oid nspOid, + Oid inputProc, Oid outputProc, Oid recvProc, Oid sendProc, + Oid modinProc, Oid modoutProc, Oid analyzeProc) + { + sepgsql_sid_t sid; + uint32 required; + + if (!sepgsqlIsEnabled()) + return InvalidOid; + + if (!HeapTupleIsValid(oldTup)) + { + sid = sepgsqlGetDefaultTupleSecid(TypeRelationId); + required = SEPG_DB_TUPLE__INSERT; + } + else + { + sid = sepgsqlGetTupleSecid(TypeRelationId, oldTup, NULL); + required = SEPG_DB_TUPLE__UPDATE; + } + sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, + required, typName, true); + /* db_schema:{add_name} */ + sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_procedure:{install} */ + if (OidIsValid(inputProc)) + sepgsql_proc_common(inputProc, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(outputProc)) + sepgsql_proc_common(outputProc, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(recvProc)) + sepgsql_proc_common(recvProc, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(sendProc)) + sepgsql_proc_common(sendProc, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(modinProc)) + sepgsql_proc_common(modinProc, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(modoutProc)) + sepgsql_proc_common(modoutProc, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(analyzeProc)) + sepgsql_proc_common(analyzeProc, SEPG_DB_PROCEDURE__INSTALL, true); + + return sid.secid; + } + + void + sepgsql_type_alter(Oid typOid, const char *newName, Oid newNsp) + { + Form_pg_type typForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for type: %u", typOid); + typForm = (Form_pg_type) GETSTRUCT(tuple); + + sid = sepgsqlGetTupleSecid(TypeRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__UPDATE, + NameStr(typForm->typname), true); + + if (newName || OidIsValid(newNsp)) + { + Oid oldNsp = typForm->typnamespace; + + if (!OidIsValid(newNsp)) + { + sepgsql_schema_common(oldNsp, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + } + else + { + sepgsql_schema_common(oldNsp, SEPG_DB_SCHEMA__REMOVE_NAME, true); + sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true); + } + } + ReleaseSysCache(tuple); + } + + void + sepgsql_type_drop(Oid typOid) + { + Form_pg_type typForm; + HeapTuple tuple; + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + tuple = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typOid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for type: %u", typOid); + typForm = (Form_pg_type) GETSTRUCT(tuple); + + if (typForm->typtype == TYPTYPE_COMPOSITE || + (typForm->typtype == TYPTYPE_BASE && OidIsValid(typForm->typarray))) + { + /* + * No need to check for composite type and implicitly + * declared array type here. + */ + ReleaseSysCache(tuple); + return; + } + + sid = sepgsqlGetTupleSecid(TypeRelationId, tuple, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__DELETE, + NameStr(typForm->typname), true); + + /* db_schema:{remove_name} */ + sepgsql_schema_common(typForm->typnamespace, + SEPG_DB_SCHEMA__REMOVE_NAME, true); + + ReleaseSysCache(tuple); + } + + /* ------------------------------------------------------------ * + * + * Misc system object related security hooks + * + * ------------------------------------------------------------ */ + + void + sepgsql_sysobj_drop(const ObjectAddress *object) + { + switch (object->classId) + { + case RelationRelationId: + if (object->objectSubId == 0) + sepgsql_relation_drop(object->objectId); + else + sepgsql_attribute_drop(object->objectId, + object->objectSubId); + break; + + case ProcedureRelationId: + sepgsql_proc_drop(object->objectId); + break; + + case TypeRelationId: + sepgsql_type_drop(object->objectId); + break; + + case CastRelationId: + sepgsql_cast_drop(object->objectId); + break; + + case ConversionRelationId: + sepgsql_conversion_drop(object->objectId); + break; + + case LanguageRelationId: + sepgsql_language_drop(object->objectId); + break; + + case OperatorRelationId: + sepgsql_operator_drop(object->objectId); + break; + + case OperatorClassRelationId: + sepgsql_opclass_drop(object->objectId); + break; + + case OperatorFamilyRelationId: + sepgsql_opfamily_drop(object->objectId); + break; + + case NamespaceRelationId: + sepgsql_schema_drop(object->objectId); + break; + + case TSParserRelationId: + sepgsql_ts_parser_drop(object->objectId); + break; + + case TSDictionaryRelationId: + sepgsql_ts_dict_drop(object->objectId); + break; + + case TSTemplateRelationId: + sepgsql_ts_template_drop(object->objectId); + break; + + case TSConfigRelationId: + sepgsql_ts_config_drop(object->objectId); + break; + + case AuthIdRelationId: + break; + + case DatabaseRelationId: + sepgsql_database_drop(object->objectId); + break; + + case TableSpaceRelationId: + break; + + case ForeignDataWrapperRelationId: + sepgsql_fdw_drop(object->objectId); + break; + + case ForeignServerRelationId: + sepgsql_foreign_server_drop(object->objectId); + break; + + case UserMappingRelationId: + break; + + default: + /* do nothing */ + break; + } + } + + /* ------------------------------------------------------------ * + * + * Filesystem object related security hooks + * + * ------------------------------------------------------------ */ + static char * + sepgsql_getfilecon(const char *path) + { + security_context_t context; + char *result; + + if (getfilecon_raw(path, &context) < 0) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not get context of \"%s\": %m", path))); + + PG_TRY(); + { + result = pstrdup(context); + } + PG_CATCH(); + { + freecon(context); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(context); + + return result; + } + + static void + sepgsql_file_common(const char *filename, uint32 required, bool may_create) + { + struct stat stbuf; + + if (stat(filename, &stbuf) == 0) + { + uint16 tclass; + + /* + * Get file object class + */ + if (S_ISDIR(stbuf.st_mode)) + tclass = SEPG_CLASS_DIR; + else if (S_ISCHR(stbuf.st_mode)) + tclass = SEPG_CLASS_CHR_FILE; + else if (S_ISBLK(stbuf.st_mode)) + tclass = SEPG_CLASS_BLK_FILE; + else if (S_ISFIFO(stbuf.st_mode)) + tclass = SEPG_CLASS_FIFO_FILE; + else if (S_ISLNK(stbuf.st_mode)) + tclass = SEPG_CLASS_LNK_FILE; + else if (S_ISSOCK(stbuf.st_mode)) + tclass = SEPG_CLASS_SOCK_FILE; + else + tclass = SEPG_CLASS_FILE; + + /* + * Check permission (no cached operation) + */ + sepgsqlComputePerms(sepgsqlGetClientLabel(), + sepgsql_getfilecon(filename), + tclass, required, + filename, true); + } + else if (may_create) + { + /* + * If the required file is not found, we check permission to + * create a new file and required permission on the new file. + */ + security_context_t dcontext; + security_context_t ncontext; + char *copy = pstrdup(filename); + + /* + * Compute a security context for the new file + */ + dcontext = sepgsql_getfilecon(dirname(copy)); + + ncontext = sepgsqlComputeCreate(sepgsqlGetServerLabel(), + dcontext, + SEPG_CLASS_FILE); + /* + * Check permission (no cached operation) + */ + required |= SEPG_FILE__CREATE; + + sepgsqlComputePerms(sepgsqlGetClientLabel(), + sepgsql_getfilecon(filename), + SEPG_CLASS_FILE, + required, filename, true); + } + else + { + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not stat file \"%s\": %m", filename))); + } + } + + void + sepgsql_file_stat(const char *filename) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_file_common(filename, SEPG_FILE__GETATTR, false); + } + + void + sepgsql_file_read(const char *filename) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_file_common(filename, SEPG_FILE__READ, false); + } + + void + sepgsql_file_write(const char *filename) + { + if (!sepgsqlIsEnabled()) + return; + + sepgsql_file_common(filename, SEPG_FILE__WRITE, true); + } + + /* + * TODO: add check for pg_ls_dir() + */ diff -Nrpc base/src/backend/security/sepgsql/checker.c sepgsql/src/backend/security/sepgsql/checker.c *** base/src/backend/security/sepgsql/checker.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/checker.c Thu Oct 8 09:29:32 2009 *************** *** 0 **** --- 1,424 ---- + /* + * 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 "access/sysattr.h" + #include "catalog/catalog.h" + #include "catalog/pg_security.h" + #include "miscadmin.h" + #include "security/sepgsql.h" + #include "storage/bufmgr.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; + Form_pg_class relForm; + HeapTuple reltup; + sepgsql_sid_t relsid; + sepgsql_sid_t attsid; + AttrNumber attno; + uint16 tclass; + + /* + * Hardwired Policy: + * SE-PostgreSQL enforces that clients cannot modify system + * catalogs and access toast values using DML statements, + * except initial setting up phase. + */ + if (sepgsqlGetEnforce()) + { + if (IsSystemNamespace(get_rel_namespace(relid)) && + (required & (SEPG_DB_TABLE__UPDATE | + SEPG_DB_TABLE__INSERT | + SEPG_DB_TABLE__DELETE)) != 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SE-PostgreSQL prevents to modidy \"%s\"", + get_rel_name(relid)))); + if (get_rel_relkind(relid) == RELKIND_TOASTVALUE) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SE-PostgreSQL prevents to access \"%s\"", + get_rel_name(relid)))); + } + + /* + * Check db_table:{...} or db_sequence permissions + */ + reltup = SearchSysCache(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (!HeapTupleIsValid(reltup)) + elog(ERROR, "SELinux: cache lookup failed for relation %u", relid); + + relForm = (Form_pg_class) GETSTRUCT(reltup); + + relsid = sepgsqlGetTupleSecid(RelationRelationId, reltup, &tclass); + + if (tclass != SEPG_CLASS_DB_TABLE) + { + /* check db_sequence:{xxx} permission */ + if (tclass == SEPG_CLASS_DB_SEQUENCE) + { + if (required & SEPG_DB_TABLE__SELECT) + { + sepgsqlClientHasPerms(relsid, tclass, + SEPG_DB_SEQUENCE__GET_VALUE, + NameStr(relForm->relname), true); + } + } + ReleaseSysCache(reltup); + return; + } + sepgsqlClientHasPerms(relsid, tclass, required, + NameStr(relForm->relname), true); + + /* + * Check db_column:{...} permissions + */ + selected_ex = fixupWholeRowReference(relid, relForm->relnatts, selected); + modified_ex = fixupWholeRowReference(relid, relForm->relnatts, modified); + columns = bms_union(selected_ex, modified_ex); + + while ((attno = bms_first_member(columns)) >= 0) + { + Form_pg_attribute attForm; + HeapTuple atttup; + uint32 attperms = 0; + char auname[2 * NAMEDATALEN + 3]; + + 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; + atttup = SearchSysCache(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attno), + 0, 0); + if (!HeapTupleIsValid(atttup)) + elog(ERROR, "cache lookup failed for attribute %d of relation %u", + attno, relid); + + attForm = (Form_pg_attribute) GETSTRUCT(atttup); + if (attForm->attisdropped) + elog(ERROR, "attribute %d of relation %u does not exist", + attno, relid); + + snprintf(auname, sizeof(auname), "%s.%s", + NameStr(relForm->relname), + NameStr(attForm->attname)); + attsid = sepgsqlGetTupleSecid(AttributeRelationId, + atttup, &tclass); + sepgsqlClientHasPerms(attsid, tclass, attperms, auname, true); + + ReleaseSysCache(atttup); + } + + ReleaseSysCache(reltup); + + 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); + } + + /* + * sepgsqlExecScan + * makes a decision on the given tuple. + */ + bool + sepgsqlExecScan(Relation rel, HeapTuple tuple, uint32 required, bool abort) + { + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled() || + !required || + RelationGetForm(rel)->relkind != RELKIND_RELATION || + RelationGetRelid(rel) == SecurityRelationId) + return true; + + sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), tuple, &tclass); + /* + * Insert/Delete to an external attribute is equivalent to + * the set-attribute on the master + */ + if (sid.relid != RelationGetRelid(rel) && + (required & (SEPG_DB_TUPLE__INSERT | SEPG_DB_TUPLE__DELETE))) + { + required &= ~(SEPG_DB_TUPLE__INSERT | SEPG_DB_TUPLE__DELETE); + required |= SEPG_DB_TUPLE__UPDATE; + } + + return sepgsqlClientHasPerms(sid, tclass, required, NULL, 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; + } + + /* + * sepgsqlHeapTupleInsert + * It assigns a default security label, if no explicit security labels + * were given. In addition, it also checks db_tuple:{insert} for the + * tuple newly inserted, when it invoked from user's query. + */ + void + sepgsqlHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal) + { + sepgsql_sid_t sid; + uint16 tclass; + + if (!sepgsqlIsEnabled()) + return; + + /* + * assigns a default security label, if not explicit one + */ + if (!OidIsValid(HeapTupleGetSecid(newtup))) + { + if (HeapTupleHasSecid(newtup)) + sepgsqlSetDefaultSecid(rel, newtup); + } + + /* + * It does not check permission for the new tuples + * inserted by system internal stuff using + * simple_heap_insert(); + */ + if (internal) + return; + + sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), + newtup, &tclass); + sepgsqlClientHasPerms(sid, tclass, SEPG_DB_TUPLE__INSERT, NULL, true); + } + + /* + * sepgsqlHeapTupleUpdate + * It checks db_tuple:{relabelfrom relabelto} permission on + * the user queries. (Please note that it does not check + * system internal stuff via simple_heap_update) + */ + void + sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup) + { + Oid secid; + HeapTupleData oldtup; + Buffer oldbuf; + + if (!sepgsqlIsEnabled()) + return; + + /* + * heap_update() preserves the original security label + * of the given tuple, if no explicit security label + * is assigned on the newer version. + * In this case, db_tuple:{update} is already checked + * at the sepgsqlExecScan() hook, so we don't need to + * check anything more. + */ + secid = HeapTupleGetSecid(newtup); + if (!OidIsValid(secid)) + return; + + /* + * User gave an explicit security label + */ + ItemPointerCopy(otid, &oldtup.t_self); + if (!heap_fetch(rel, SnapshotAny, &oldtup, &oldbuf, false, NULL)) + elog(ERROR, "failed to fetch old version of the tuple"); + + if (secid != HeapTupleGetSecid(&oldtup)) + { + sepgsql_sid_t sid; + uint16 tclass; + + /* db_tuple:{relabelfrom} for older security context */ + sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), + &oldtup, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__RELABELFROM, + NULL, true); + + /* db_tuple:{relabelto} for newer security label */ + sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), + newtup, &tclass); + sepgsqlClientHasPerms(sid, tclass, + SEPG_DB_TUPLE__RELABELTO, + NULL, true); + } + ReleaseBuffer(oldbuf); + } diff -Nrpc base/src/backend/security/sepgsql/dummy.c sepgsql/src/backend/security/sepgsql/dummy.c *** base/src/backend/security/sepgsql/dummy.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/dummy.c Wed Jul 15 19:39:56 2009 *************** *** 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_server_getcon(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 -Nrpc base/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/security/sepgsql/hooks.c *** base/src/backend/security/sepgsql/hooks.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/hooks.c Mon Sep 28 10:07:19 2009 *************** *** 0 **** --- 1,239 ---- + /* + * 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 "catalog/indexing.h" + #include "catalog/namespace.h" + #include "catalog/pg_database.h" + #include "catalog/pg_foreign_data_wrapper.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 "catalog/pg_security.h" + #include "commands/dbcommands.h" + #include "miscadmin.h" + #include "security/sepgsql.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" + #include "utils/lsyscache.h" + #include "utils/syscache.h" + #include "utils/tqual.h" + + /* ------------------------------------------------------------ * + * Hooks corresponding to db_blob object class + * ------------------------------------------------------------ */ + + /* + * sepgsqlCheckBlobCreate + * assigns a default security label and checks db_blob:{create} + */ + void + sepgsqlCheckBlobCreate(Relation rel, HeapTuple lotup) + { + sepgsql_sid_t loSid; + Oid relid = RelationGetRelid(rel); + + if (!sepgsqlIsEnabled()) + return; + + /* set a default security context */ + sepgsqlSetDefaultSecid(rel, lotup); + + loSid = sepgsqlGetTupleSecid(relid, lotup, NULL); + sepgsqlClientHasPerms(loSid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__CREATE, + NULL, true); + } + + /* + * sepgsqlCheckBlobDrop + * checks db_blob:{drop} permission + */ + void + sepgsqlCheckBlobDrop(Relation rel, HeapTuple lotup) + { + sepgsql_sid_t loSid; + Oid relid = RelationGetRelid(rel); + + if (!sepgsqlIsEnabled()) + return; + + loSid = sepgsqlGetTupleSecid(relid, lotup, NULL); + sepgsqlClientHasPerms(loSid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__DROP, + NULL, true); + } + + /* + * sepgsqlCheckBlobRead + * checks db_blob:{read} permission + */ + void + sepgsqlCheckBlobRead(LargeObjectDesc *lobj) + { + sepgsql_sid_t loSid; + + if (!sepgsqlIsEnabled()) + return; + + loSid.relid = LargeObjectRelationId; + loSid.secid = lobj->secid; + sepgsqlClientHasPerms(loSid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__READ, + NULL, true); + } + + /* + * sepgsqlCheckBlobWrite + * check db_blob:{write} permission + */ + void + sepgsqlCheckBlobWrite(LargeObjectDesc *lobj) + { + sepgsql_sid_t loSid; + + if (!sepgsqlIsEnabled()) + return; + + loSid.relid = LargeObjectRelationId; + loSid.secid = lobj->secid; + sepgsqlClientHasPerms(loSid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__WRITE, + NULL, true); + } + + /* + * sepgsqlCheckBlobGetattr + * check db_blob:{getattr} permission + */ + void + sepgsqlCheckBlobGetattr(HeapTuple tuple) + { + sepgsql_sid_t loSid; + + if (!sepgsqlIsEnabled()) + return; + + loSid.relid = LargeObjectRelationId; + loSid.secid = HeapTupleGetSecid(tuple); + sepgsqlClientHasPerms(loSid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__GETATTR, + NULL, true); + } + + /* + * sepgsqlCheckBlobSetattr + * check db_blob:{setattr} permission + */ + void + sepgsqlCheckBlobSetattr(HeapTuple tuple) + { + sepgsql_sid_t loSid; + + if (!sepgsqlIsEnabled()) + return; + + loSid.relid = LargeObjectRelationId; + loSid.secid = HeapTupleGetSecid(tuple); + sepgsqlClientHasPerms(loSid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__SETATTR, + NULL, true); + } + + /* + * sepgsqlCheckBlobExport + * check db_blob:{read export} and file:{write} permission + */ + void + sepgsqlCheckBlobExport(LargeObjectDesc *lobj, const char *filename) + { + sepgsql_sid_t loSid; + + if (!sepgsqlIsEnabled()) + return; + + /* db_blob:{read export} */ + loSid.relid = LargeObjectRelationId; + loSid.secid = lobj->secid; + sepgsqlClientHasPerms(loSid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__READ | SEPG_DB_BLOB__EXPORT, + NULL, true); + /* file:{write} */ + sepgsql_file_write(filename); + } + + /* + * sepgsqlCheckBlobImport + * check db_blob:{write import} and file:{read} permission + */ + void + sepgsqlCheckBlobImport(LargeObjectDesc *lobj, const char *filename) + { + sepgsql_sid_t loSid; + + if (!sepgsqlIsEnabled()) + return; + + /* db_blob:{write import} */ + loSid.relid = LargeObjectRelationId; + loSid.secid = lobj->secid; + sepgsqlClientHasPerms(loSid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__WRITE | SEPG_DB_BLOB__IMPORT, + NULL, true); + /* file:{read} */ + sepgsql_file_read(filename); + } + + /* + * sepgsqlCheckBlobRelabel + * check db_blob:{setattr relabelfrom relabelto} + */ + void + sepgsqlCheckBlobRelabel(HeapTuple oldtup, HeapTuple newtup) + { + sepgsql_sid_t loSid; + access_vector_t required = SEPG_DB_BLOB__SETATTR; + + if (HeapTupleGetSecid(oldtup) != HeapTupleGetSecid(newtup)) + required |= SEPG_DB_BLOB__RELABELFROM; + + /* db_blob:{setattr relabelfrom} */ + loSid = sepgsqlGetTupleSecid(LargeObjectRelationId, oldtup, NULL); + sepgsqlClientHasPerms(loSid, + SEPG_CLASS_DB_BLOB, + required, + NULL, true); + + if ((required & SEPG_DB_BLOB__RELABELFROM) == 0) + return; + + /* db_blob:{relabelto} */ + loSid = sepgsqlGetTupleSecid(LargeObjectRelationId, newtup, NULL); + sepgsqlClientHasPerms(loSid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__RELABELTO, + NULL, true); + } diff -Nrpc base/src/backend/security/sepgsql/label.c sepgsql/src/backend/security/sepgsql/label.c *** base/src/backend/security/sepgsql/label.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/label.c Fri Sep 25 17:27:05 2009 *************** *** 0 **** --- 1,1140 ---- + /* + * 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/sysattr.h" + #include "access/xact.h" + #include "catalog/catalog.h" + #include "catalog/pg_constraint.h" + #include "catalog/heap.h" + #include "catalog/indexing.h" + #include "catalog/namespace.h" + #include "catalog/pg_aggregate.h" + #include "catalog/pg_amop.h" + #include "catalog/pg_amproc.h" + #include "catalog/pg_attrdef.h" + #include "catalog/pg_attribute.h" + #include "catalog/pg_auth_members.h" + #include "catalog/pg_authid.h" + #include "catalog/pg_cast.h" + #include "catalog/pg_class.h" + #include "catalog/pg_conversion.h" + #include "catalog/pg_database.h" + #include "catalog/pg_description.h" + #include "catalog/pg_enum.h" + #include "catalog/pg_foreign_data_wrapper.h" + #include "catalog/pg_foreign_server.h" + #include "catalog/pg_inherits.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_rewrite.h" + #include "catalog/pg_security.h" + #include "catalog/pg_shdescription.h" + #include "catalog/pg_statistic.h" + #include "catalog/pg_tablespace.h" + #include "catalog/pg_trigger.h" + #include "catalog/pg_ts_config.h" + #include "catalog/pg_ts_config_map.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 "catalog/pg_user_mapping.h" + #include "miscadmin.h" + #include "nodes/makefuncs.h" + #include "security/sepgsql.h" + #include "storage/fd.h" + #include "utils/fmgroids.h" + #include "utils/lsyscache.h" + #include "utils/syscache.h" + #include "utils/tqual.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; + + /* + * sepgsqlTupleDescHasSecid + * + * returns a hint whether we should allocate a field to store + * security label on the given relation, or not. + */ + bool + sepgsqlTupleDescHasSecid(Oid relid, char relkind) + { + if (!sepgsqlIsEnabled()) + return false; + + if (!OidIsValid(relid)) + return sepostgresql_row_level; /* Target of SELECT INTO */ + + /* These system catalogs always have its secid */ + if (relid == DatabaseRelationId || + relid == NamespaceRelationId || + relid == RelationRelationId || + relid == AttributeRelationId || + relid == ProcedureRelationId) + return true; + + /* These system catalogs are an external attributes */ + if (relid == AggregateRelationId || + relid == AccessMethodOperatorRelationId || + relid == AccessMethodProcedureRelationId || + relid == AttrDefaultRelationId || + relid == AuthMemRelationId || + relid == ConstraintRelationId || + relid == DescriptionRelationId || + relid == EnumRelationId || + relid == IndexRelationId || + relid == InheritsRelationId || + relid == RewriteRelationId || + relid == SecurityRelationId || + relid == SharedDescriptionRelationId || + relid == StatisticRelationId || + relid == TriggerRelationId) + return false; + + return sepostgresql_row_level; + } + + /* + * sepgsqlGetDefaultDatabaseSecid + * It returns the default security label of a database object. + */ + sepgsql_sid_t + sepgsqlGetDefaultDatabaseSecid(void) + { + security_context_t seclabel; + sepgsql_sid_t sid; + char filename[MAXPGPATH]; + char buffer[1024], *policy_type, *tmp; + FILE *filp; + + /* + * NOTE: when the security policy provide a configuration to + * specify the default security context of database object, + * we apply is as a default one. + * If the configuration is unavailable, we compute the + * default security context without any parent object. + */ + if (selinux_getpolicytype(&policy_type) < 0) + goto fallback; + + snprintf(filename, sizeof(filename), + "%s%s/contexts/sepgsql_context", selinux_path(), policy_type); + filp = AllocateFile(filename, PG_BINARY_R); + if (!filp) + goto fallback; + + while (fgets(buffer, sizeof(buffer), filp) != NULL) + { + tmp = strchr(buffer, '#'); + if (tmp) + *tmp = '\0'; + + seclabel = strtok(buffer, " \t\n\r"); + if (!seclabel) + continue; + + /* An entry found */ + FreeFile(filp); + + sid.relid = DatabaseRelationId; + sid.secid = securityTransSecLabelIn(sid.relid, seclabel); + + return sid; + } + FreeFile(filp); + + fallback: + seclabel = sepgsqlComputeCreate(sepgsqlGetClientLabel(), + sepgsqlGetClientLabel(), + SEPG_CLASS_DB_DATABASE); + sid.relid = DatabaseRelationId; + sid.secid = securityTransSecLabelIn(sid.relid, seclabel); + + return sid; + } + + static sepgsql_sid_t + defaultSecidWithDatabase(Oid relid, Oid datoid, uint16 tclass) + { + HeapTuple tuple; + sepgsql_sid_t datsid; + + if (IsBootstrapProcessingMode()) + { + static sepgsql_sid_t cached = { InvalidOid, InvalidOid }; + + if (!SidIsValid(cached)) + cached = sepgsqlGetDefaultDatabaseSecid(); + datsid = cached; + } + else + { + tuple = SearchSysCache(DATABASEOID, + ObjectIdGetDatum(datoid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for database: %u", datoid); + datsid.relid = DatabaseRelationId; + datsid.secid = HeapTupleGetSecid(tuple); + ReleaseSysCache(tuple); + } + + return sepgsqlClientCreateSecid(datsid, tclass, relid); + } + + sepgsql_sid_t + sepgsqlGetDefaultSchemaSecid(Oid database_oid) + { + return defaultSecidWithDatabase(NamespaceRelationId, + database_oid, + SEPG_CLASS_DB_SCHEMA); + } + + static sepgsql_sid_t + defaultSecidWithSchema(Oid relid, Oid nspoid, uint16 tclass) + { + HeapTuple tuple; + sepgsql_sid_t nspsid; + + if (IsBootstrapProcessingMode()) + { + static sepgsql_sid_t cached = { InvalidOid, InvalidOid }; + + if (!SidIsValid(cached)) + cached = sepgsqlGetDefaultSchemaSecid(MyDatabaseId); + + nspsid = sepgsqlGetDefaultSchemaSecid(MyDatabaseId); + } + else + { + tuple = SearchSysCache(NAMESPACEOID, + ObjectIdGetDatum(nspoid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for namespace: %u", nspoid); + nspsid.relid = NamespaceRelationId; + nspsid.secid = HeapTupleGetSecid(tuple); + ReleaseSysCache(tuple); + } + + return sepgsqlClientCreateSecid(nspsid, tclass, relid); + } + + sepgsql_sid_t + sepgsqlGetDefaultTableSecid(Oid namespace_oid) + { + return defaultSecidWithSchema(RelationRelationId, + namespace_oid, + SEPG_CLASS_DB_TABLE); + } + + sepgsql_sid_t + sepgsqlGetDefaultSequenceSecid(Oid namespace_oid) + { + return defaultSecidWithSchema(RelationRelationId, + namespace_oid, + SEPG_CLASS_DB_SEQUENCE); + } + + sepgsql_sid_t + sepgsqlGetDefaultProcedureSecid(Oid namespace_oid) + { + return defaultSecidWithSchema(ProcedureRelationId, + namespace_oid, + SEPG_CLASS_DB_PROCEDURE); + } + + static sepgsql_sid_t + defaultSecidWithTable(Oid relid, Oid tbloid, security_class_t tclass) + { + HeapTuple tuple; + sepgsql_sid_t relsid; + + if (IsBootstrapProcessingMode() + && (tbloid == TypeRelationId || + tbloid == ProcedureRelationId || + tbloid == AttributeRelationId || + tbloid == RelationRelationId)) + { + static sepgsql_sid_t cached = { InvalidOid, InvalidOid }; + + if (!SidIsValid(cached)) + cached = sepgsqlGetDefaultTableSecid(PG_CATALOG_NAMESPACE); + + relsid = cached; + } + else + { + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(tbloid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation: %u", tbloid); + relsid.relid = RelationRelationId; + relsid.secid = HeapTupleGetSecid(tuple); + ReleaseSysCache(tuple); + } + + return sepgsqlClientCreateSecid(relsid, tclass, relid); + } + + sepgsql_sid_t + sepgsqlGetDefaultColumnSecid(Oid table_oid) + { + return defaultSecidWithTable(AttributeRelationId, + table_oid, + SEPG_CLASS_DB_COLUMN); + } + + sepgsql_sid_t + sepgsqlGetDefaultTupleSecid(Oid table_oid) + { + return defaultSecidWithTable(table_oid, + table_oid, + SEPG_CLASS_DB_TUPLE); + } + + sepgsql_sid_t + sepgsqlGetDefaultBlobSecid(Oid database_oid) + { + return defaultSecidWithDatabase(LargeObjectRelationId, + MyDatabaseId, + SEPG_CLASS_DB_BLOB); + } + + void + sepgsqlSetDefaultSecid(Relation rel, HeapTuple tuple) + { + sepgsql_sid_t newSid; + Oid relOid = RelationGetRelid(rel); + Oid nspOid, tblOid; + char relkind; + + if (!sepgsqlIsEnabled()) + return; + + if (!HeapTupleHasSecid(tuple)) + return; + + /* initialize */ + newSid.relid = relOid; + newSid.secid = InvalidOid; + + switch (relOid) + { + case DatabaseRelationId: + newSid = sepgsqlGetDefaultDatabaseSecid(); + break; + + case NamespaceRelationId: + newSid = sepgsqlGetDefaultSchemaSecid(MyDatabaseId); + break; + + case RelationRelationId: + nspOid = ((Form_pg_class) GETSTRUCT(tuple))->relnamespace; + relkind = ((Form_pg_class) GETSTRUCT(tuple))->relkind; + + switch (relkind) + { + case RELKIND_RELATION: + newSid = sepgsqlGetDefaultTableSecid(nspOid); + break; + + case RELKIND_SEQUENCE: + newSid = sepgsqlGetDefaultSequenceSecid(nspOid); + break; + + default: + newSid = sepgsqlGetDefaultTupleSecid(relOid); + break; + } + break; + + case ProcedureRelationId: + nspOid = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; + newSid = sepgsqlGetDefaultProcedureSecid(nspOid); + break; + + case AttributeRelationId: + tblOid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; + + /* + * We cannot refer system cache in the very early initdb + * phase, because pg_class is not constructed yet. + */ + if ((IsBootstrapProcessingMode() && + (tblOid == TypeRelationId || + tblOid == ProcedureRelationId || + tblOid == AttributeRelationId || + tblOid == RelationRelationId)) + || get_rel_relkind(tblOid) == RELKIND_RELATION) + { + newSid = sepgsqlGetDefaultColumnSecid(tblOid); + } + /* otherwise, it does not have individual security context */ + break; + + case LargeObjectRelationId: + newSid = sepgsqlGetDefaultBlobSecid(MyDatabaseId); + break; + + default: + newSid = sepgsqlGetDefaultTupleSecid(relOid); + break; + } + + HeapTupleSetSecid(tuple, newSid.secid); + } + + /* + * sepgsqlGetSysobjSecid + * + * It returns a pair of relid/secid for the given OID. + */ + static sepgsql_sid_t + getSysobjSecidDirect(Oid classOid, Oid indexOid, Oid objectId, uint16 *tclass) + { + sepgsql_sid_t sid; + Relation rel; + HeapTuple tup; + ScanKeyData skey; + SysScanDesc scan; + + rel = heap_open(CastRelationId, AccessShareLock); + + ScanKeyInit(&skey, + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(objectId)); + + scan = systable_beginscan(rel, CastOidIndexId, true, + SnapshotNow, 1, &skey); + tup = systable_getnext(scan); + + if (!HeapTupleIsValid(tup)) + elog(ERROR, "system object lookup failed for oid %u on relation %u", + objectId, classOid); + + sid = sepgsqlGetTupleSecid(classOid, tup, tclass); + + systable_endscan(scan); + + heap_close(rel, AccessShareLock); + + return sid; + } + + sepgsql_sid_t + sepgsqlGetSysobjSecid(Oid classOid, Oid objectId, int32 objsubId, uint16 *tclass) + { + sepgsql_sid_t sid; + HeapTuple tup; + + switch (classOid) + { + case AccessMethodRelationId: + tup = SearchSysCache(AMOID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for access method: %u", objectId); + break; + + case AccessMethodOperatorRelationId: + return getSysobjSecidDirect(AccessMethodOperatorRelationId, + AccessMethodOperatorOidIndexId, + objectId, tclass); + + case AccessMethodProcedureRelationId: + return getSysobjSecidDirect(AccessMethodProcedureRelationId, + AccessMethodProcedureOidIndexId, + objectId, tclass); + + case AuthIdRelationId: + tup = SearchSysCache(AUTHOID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for role: %u", objectId); + break; + + case CastRelationId: + return getSysobjSecidDirect(CastRelationId, + CastOidIndexId, + objectId, tclass); + + case ConstraintRelationId: + tup = SearchSysCache(CONSTROID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for constraint: %u", objectId); + break; + + case ConversionRelationId: + tup = SearchSysCache(CONVOID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for conversion: %u", objectId); + break; + + case DatabaseRelationId: + tup = SearchSysCache(DATABASEOID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for database: %u", objectId); + break; + + case ForeignDataWrapperRelationId: + tup = SearchSysCache(FOREIGNDATAWRAPPEROID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for FDW: %u", objectId); + break; + + case ForeignServerRelationId: + tup = SearchSysCache(FOREIGNSERVEROID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for foreign server: %u", objectId); + break; + + case LanguageRelationId: + tup = SearchSysCache(LANGOID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + break; + + case LargeObjectRelationId: + /* to be replaced by LargeObjectMetaRelationId! */ + { + Relation rel; + ScanKeyData skey; + SysScanDesc scan; + + rel = heap_open(LargeObjectRelationId, AccessShareLock); + + ScanKeyInit(&skey, + Anum_pg_largeobject_loid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(objectId)); + + scan = systable_beginscan(rel, LargeObjectLOidPNIndexId, + true, SnapshotNow, 1, &skey); + + tup = systable_getnext(scan); + + if (!HeapTupleIsValid(tup)) + elog(ERROR, "largeobject %u lookup failed", objectId); + + sid = sepgsqlGetTupleSecid(classOid, tup, tclass); + systable_endscan(scan); + + heap_close(rel, AccessShareLock); + } + return sid; + + case RelationRelationId: + if (objsubId != 0) + { + classOid = AttributeRelationId; + tup = SearchSysCache(ATTNUM, + ObjectIdGetDatum(objectId), + Int16GetDatum(objsubId), + 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for attribute %d of relation %u", + objsubId, objectId); + } + else + { + classOid = RelationRelationId; + tup = SearchSysCache(RELOID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for relation %u", objectId); + } + break; + + case NamespaceRelationId: + tup = SearchSysCache(NAMESPACEOID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for schema %u", objectId); + break; + + case OperatorClassRelationId: + tup = SearchSysCache(CLAOID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for opclass %u", objectId); + break; + + case OperatorFamilyRelationId: + tup = SearchSysCache(OPFAMILYOID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for opfamily %u", objectId); + break; + + case OperatorRelationId: + tup = SearchSysCache(OPEROID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for operator %u", objectId); + break; + + case ProcedureRelationId: + tup = SearchSysCache(PROCOID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for procedure %u", objectId); + break; + + case RewriteRelationId: + return getSysobjSecidDirect(RewriteRelationId, + RewriteOidIndexId, + objectId, tclass); + + case TableSpaceRelationId: + return getSysobjSecidDirect(TableSpaceRelationId, + TablespaceOidIndexId, + objectId, tclass); + + case TriggerRelationId: + return getSysobjSecidDirect(TriggerRelationId, + TriggerOidIndexId, + objectId, tclass); + + case TSConfigRelationId: + tup = SearchSysCache(TSCONFIGOID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for text search configuration %u", objectId); + break; + + case TSDictionaryRelationId: + tup = SearchSysCache(TSDICTOID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for text search dictionary %u", objectId); + break; + + case TSParserRelationId: + tup = SearchSysCache(TSPARSEROID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for text search parser %u", objectId); + break; + + case TSTemplateRelationId: + tup = SearchSysCache(TSTEMPLATEOID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for text search template %u", objectId); + break; + + case TypeRelationId: + tup = SearchSysCache(TYPEOID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for type %u", objectId); + break; + + case UserMappingRelationId: + tup = SearchSysCache(USERMAPPINGOID, + ObjectIdGetDatum(objectId), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for user mapping %u", objectId); + break; + + default: + elog(ERROR, "unexpected class OID: %u", classOid); + tup = NULL; /* for compiler quiet */ + break; + } + + Assert(HeapTupleIsValid(tup)); + + sid = sepgsqlGetTupleSecid(classOid, tup, tclass); + + ReleaseSysCache(tup); + + return sid; + } + + /* + * sepgsqlGetTupleSecid + * + * It returns a pair of relid/secid for the given HeapTuple. + * A few system catalogs is handled as an attribute of other + * system objects. + * E.g) pg_attrdef is an attribute of a certain pg_attribute + */ + sepgsql_sid_t + sepgsqlGetTupleSecid(Oid tableOid, HeapTuple tuple, uint16 *tclass) + { + sepgsql_sid_t sid; + HeapTuple exttup; + Oid extid; + Oid extcls; + AttrNumber extsub; + + /* initialize (unlabeled security context) */ + sid.relid = tableOid; + sid.secid = InvalidOid; + if (tclass) + *tclass = SEPG_CLASS_DB_TUPLE; + + switch (tableOid) + { + case AggregateRelationId: + extid = ((Form_pg_aggregate) GETSTRUCT(tuple))->aggfnoid; + exttup = SearchSysCache(PROCOID, + ObjectIdGetDatum(extid), + 0, 0, 0); + if (HeapTupleIsValid(exttup)) + { + sid = sepgsqlGetTupleSecid(ProcedureRelationId, + exttup, tclass); + ReleaseSysCache(exttup); + } + break; + + case AccessMethodOperatorRelationId: + extid = ((Form_pg_amop) GETSTRUCT(tuple))->amopfamily; + exttup = SearchSysCache(OPFAMILYOID, + ObjectIdGetDatum(extid), + 0, 0, 0); + if (HeapTupleIsValid(exttup)) + { + sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, + exttup, tclass); + ReleaseSysCache(exttup); + } + break; + + case AccessMethodProcedureRelationId: + extid = ((Form_pg_amproc) GETSTRUCT(tuple))->amprocfamily; + exttup = SearchSysCache(OPFAMILYOID, + ObjectIdGetDatum(extid), + 0, 0, 0); + if (HeapTupleIsValid(exttup)) + { + sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, + exttup, tclass); + ReleaseSysCache(exttup); + } + break; + + case AttrDefaultRelationId: + extid = ((Form_pg_attrdef) GETSTRUCT(tuple))->adrelid; + extsub = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum; + exttup = SearchSysCache(ATTNUM, + ObjectIdGetDatum(extid), + Int16GetDatum(extsub), + 0, 0); + if (HeapTupleIsValid(exttup)) + { + sid = sepgsqlGetTupleSecid(AttributeRelationId, + exttup, tclass); + ReleaseSysCache(exttup); + } + break; + + case AttributeRelationId: + extid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; + exttup = SearchSysCache(RELOID, + ObjectIdGetDatum(extid), + 0, 0, 0); + if (HeapTupleIsValid(exttup)) + { + char relkind = ((Form_pg_class) GETSTRUCT(exttup))->relkind; + + if (relkind == RELKIND_RELATION) + { + if (tclass) + *tclass = SEPG_CLASS_DB_COLUMN; + sid.secid = HeapTupleGetSecid(tuple); + } + else + sid = sepgsqlGetTupleSecid(RelationRelationId, + exttup, tclass); + ReleaseSysCache(exttup); + } + break; + + case AuthMemRelationId: + extid = ((Form_pg_auth_members) GETSTRUCT(tuple))->roleid; + exttup = SearchSysCache(AUTHOID, + ObjectIdGetDatum(extid), + 0, 0, 0); + if (HeapTupleIsValid(exttup)) + { + sid = sepgsqlGetTupleSecid(AuthIdRelationId, + exttup, tclass); + ReleaseSysCache(exttup); + } + break; + + case ConstraintRelationId: + /* CHECK constraint is an attribute of the relation */ + extid = ((Form_pg_constraint) GETSTRUCT(tuple))->conrelid; + if (OidIsValid(extid)) + { + exttup = SearchSysCache(RELOID, + ObjectIdGetDatum(extid), + 0, 0, 0); + if (HeapTupleIsValid(exttup)) + { + sid = sepgsqlGetTupleSecid(RelationRelationId, + exttup, tclass); + ReleaseSysCache(exttup); + } + break; + } + /* DOMAIN constraint is an attribute of the domain type */ + extid = ((Form_pg_constraint) GETSTRUCT(tuple))->contypid; + if (OidIsValid(extid)) + { + sid.relid = TypeRelationId; + exttup = SearchSysCache(TYPEOID, + ObjectIdGetDatum(extid), + 0, 0, 0); + if (HeapTupleIsValid(exttup)) + { + sid = sepgsqlGetTupleSecid(TypeRelationId, + exttup, tclass); + ReleaseSysCache(exttup); + } + break; + } + /* Database's context for global assertion */ + exttup = SearchSysCache(DATABASEOID, + ObjectIdGetDatum(MyDatabaseId), + 0, 0, 0); + if (HeapTupleIsValid(exttup)) + { + sid = sepgsqlGetTupleSecid(DatabaseRelationId, + exttup, tclass); + ReleaseSysCache(exttup); + } + break; + + case DatabaseRelationId: + sid.secid = HeapTupleGetSecid(tuple); + if (tclass) + *tclass = SEPG_CLASS_DB_DATABASE; + break; + + case DescriptionRelationId: + /* recursive call */ + extid = ((Form_pg_description) GETSTRUCT(tuple))->objoid; + extcls = ((Form_pg_description) GETSTRUCT(tuple))->classoid; + return sepgsqlGetSysobjSecid(extcls, extid, 0, tclass); + + case EnumRelationId: + extid = ((Form_pg_enum) GETSTRUCT(tuple))->enumtypid; + exttup = SearchSysCache(TYPEOID, + ObjectIdGetDatum(extid), + 0, 0, 0); + if (HeapTupleIsValid(exttup)) + { + sid = sepgsqlGetTupleSecid(TypeRelationId, + exttup, tclass); + ReleaseSysCache(exttup); + } + break; + + case IndexRelationId: + extid = ((Form_pg_index) GETSTRUCT(tuple))->indrelid; + exttup = SearchSysCache(RELOID, + ObjectIdGetDatum(extid), + 0, 0, 0); + if (HeapTupleIsValid(exttup)) + { + sid = sepgsqlGetTupleSecid(RelationRelationId, + exttup, tclass); + ReleaseSysCache(exttup); + } + break; + + case InheritsRelationId: + extid = ((Form_pg_inherits) GETSTRUCT(tuple))->inhrelid; + exttup = SearchSysCache(RELOID, + ObjectIdGetDatum(extid), + 0, 0, 0); + if (HeapTupleIsValid(exttup)) + { + sid = sepgsqlGetTupleSecid(RelationRelationId, + exttup, tclass); + ReleaseSysCache(exttup); + } + break; + + case LargeObjectRelationId: + sid.secid = HeapTupleGetSecid(tuple); + if (tclass) + *tclass = SEPG_CLASS_DB_BLOB; + break; + + case NamespaceRelationId: + sid.secid = HeapTupleGetSecid(tuple); + if (tclass) + *tclass = SEPG_CLASS_DB_SCHEMA; + break; + + case ProcedureRelationId: + sid.secid = HeapTupleGetSecid(tuple); + if (tclass) + *tclass = SEPG_CLASS_DB_PROCEDURE; + break; + + case RelationRelationId: + sid.secid = HeapTupleGetSecid(tuple); + if (tclass) + { + char relkind = ((Form_pg_class) GETSTRUCT(tuple))->relkind; + + switch (relkind) + { + case RELKIND_RELATION: + *tclass = SEPG_CLASS_DB_TABLE; + break; + + case RELKIND_SEQUENCE: + *tclass = SEPG_CLASS_DB_SEQUENCE; + break; + + default: + *tclass = SEPG_CLASS_DB_TUPLE; + break; + } + } + break; + + case RewriteRelationId: + extid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class; + exttup = SearchSysCache(RELOID, + ObjectIdGetDatum(extid), + 0, 0, 0); + if (HeapTupleIsValid(exttup)) + { + sid = sepgsqlGetTupleSecid(RelationRelationId, + exttup, tclass); + ReleaseSysCache(exttup); + } + break; + + case SharedDescriptionRelationId: + /* recursive invocation */ + extid = ((Form_pg_shdescription) GETSTRUCT(tuple))->objoid; + extcls = ((Form_pg_shdescription) GETSTRUCT(tuple))->classoid; + return sepgsqlGetSysobjSecid(extcls, extid, 0, tclass); + + case StatisticRelationId: + extid = ((Form_pg_statistic) GETSTRUCT(tuple))->starelid; + extsub = ((Form_pg_statistic) GETSTRUCT(tuple))->staattnum; + exttup = SearchSysCache(ATTNUM, + ObjectIdGetDatum(extid), + Int16GetDatum(extsub), + 0, 0); + if (HeapTupleIsValid(exttup)) + { + sid = sepgsqlGetTupleSecid(AttributeRelationId, + exttup, tclass); + ReleaseSysCache(exttup); + } + break; + + case TriggerRelationId: + extid = ((Form_pg_trigger) GETSTRUCT(tuple))->tgrelid; + exttup = SearchSysCache(RELOID, + ObjectIdGetDatum(extid), + 0, 0, 0); + if (HeapTupleIsValid(exttup)) + { + sid = sepgsqlGetTupleSecid(RelationRelationId, + exttup, tclass); + ReleaseSysCache(exttup); + } + break; + + case TSConfigMapRelationId: + extid = ((Form_pg_ts_config_map) GETSTRUCT(tuple))->mapcfg; + exttup = SearchSysCache(TSCONFIGOID, + ObjectIdGetDatum(extid), + 0, 0, 0); + if (HeapTupleIsValid(exttup)) + { + sid = sepgsqlGetTupleSecid(TSConfigRelationId, + exttup, tclass); + ReleaseSysCache(exttup); + } + break; + + default: + /* No external lookups (normal case) */ + sid.secid = HeapTupleGetSecid(tuple); + break; + } + + return sid; + } + + /* + * sepgsqlRawSecLabelIn + * correctness checks for the given security context + */ + char * + sepgsqlRawSecLabelIn(char *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 + */ + char * + sepgsqlRawSecLabelOut(char *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 + */ + char * + sepgsqlTransSecLabelIn(char *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; + } + + char * + sepgsqlTransSecLabelOut(char *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; + } + + char * + sepgsqlSysattSecLabelOut(Oid relid, HeapTuple tuple) + { + sepgsql_sid_t sid; + + sid = sepgsqlGetTupleSecid(relid, tuple, NULL); + + return securityTransSecLabelOut(sid.relid, sid.secid); + } diff -Nrpc base/src/backend/security/sepgsql/misc.c sepgsql/src/backend/security/sepgsql/misc.c *** base/src/backend/security/sepgsql/misc.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/misc.c Sun Aug 23 23:05:48 2009 *************** *** 0 **** --- 1,304 ---- + /* + * src/backend/security/sepgsql/misc.c + * Miscellaneous facilities in 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 "libpq/libpq-be.h" + #include "miscadmin.h" + #include "security/sepgsql.h" + #include "utils/builtins.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(clientLabel); + } + PG_CATCH(); + { + clientLabel = old_client; + PG_RE_THROW(); + } + PG_END_TRY(); + + return old_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; + } + + /* + * 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_server_getcon(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 -Nrpc base/src/backend/security/sepgsql/perms.c sepgsql/src/backend/security/sepgsql/perms.c *** base/src/backend/security/sepgsql/perms.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/perms.c Mon Sep 28 09:29:32 2009 *************** *** 0 **** --- 1,597 ---- + /* + * 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/pg_database.h" + #include "catalog/pg_proc.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/lsyscache.h" + + /* + * 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 }, + {"create", SEPG_FILE__CREATE }, + {"getattr", SEPG_FILE__GETATTR }, + {NULL, 0} + } + }, + { + "dir", SEPG_CLASS_DIR, + { + {"read", SEPG_DIR__READ }, + {"write", SEPG_DIR__WRITE }, + {"create", SEPG_DIR__CREATE }, + {"getattr", SEPG_DIR__GETATTR }, + {NULL,0} + } + }, + { + "lnk_file", SEPG_CLASS_LNK_FILE, + { + {"read", SEPG_LNK_FILE__READ }, + {"write", SEPG_LNK_FILE__WRITE }, + {"create", SEPG_LNK_FILE__CREATE }, + {"getattr", SEPG_LNK_FILE__GETATTR }, + {NULL,0} + } + }, + { + "chr_file", SEPG_CLASS_CHR_FILE, + { + {"read", SEPG_CHR_FILE__READ }, + {"write", SEPG_CHR_FILE__WRITE }, + {"create", SEPG_CHR_FILE__CREATE }, + {"getattr", SEPG_CHR_FILE__GETATTR }, + {NULL,0} + } + }, + { + "blk_file", SEPG_CLASS_BLK_FILE, + { + {"read", SEPG_BLK_FILE__READ }, + {"write", SEPG_BLK_FILE__WRITE }, + {"create", SEPG_BLK_FILE__CREATE }, + {"getattr", SEPG_BLK_FILE__GETATTR }, + {NULL,0} + } + }, + { + "sock_file", SEPG_CLASS_SOCK_FILE, + { + {"read", SEPG_SOCK_FILE__READ }, + {"write", SEPG_SOCK_FILE__WRITE }, + {"create", SEPG_SOCK_FILE__CREATE }, + {"getattr", SEPG_SOCK_FILE__GETATTR }, + {NULL,0} + } + }, + { + "fifo_file", SEPG_CLASS_FIFO_FILE, + { + {"read", SEPG_FIFO_FILE__READ }, + {"write", SEPG_FIFO_FILE__WRITE }, + {"create", SEPG_FIFO_FILE__CREATE }, + {"getattr", SEPG_FIFO_FILE__GETATTR }, + {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_name", SEPG_DB_SCHEMA__ADD_NAME }, + { "remove_name", SEPG_DB_SCHEMA__REMOVE_NAME }, + { 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 }, + { "untrusted", SEPG_DB_PROCEDURE__UNTRUSTED }, + { 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(uint16 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(uint16 tclass) + { + Assert(tclass < SEPG_CLASS_MAX); + + return selinux_catalog[tclass].class_name; + } + + const char * + sepgsqlGetPermString(uint16 tclass, uint32 permission) + { + 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 == permission) + return selinux_catalog[tclass].av[i].perm_name; + } + return NULL; + } + + #if 0 + + /* + * sepgsqlFileObjectClass + * + * It returns proper object class of filesystem object already opened. + * It is necessary to check privileges voluntarily. + */ + uint16 + 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 + */ + uint16 + sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple) + { + Form_pg_class clsForm; + Form_pg_attribute attForm; + + switch (relid) + { + case DatabaseRelationId: + return SEPG_CLASS_DB_DATABASE; + + case NamespaceRelationId: + 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; + } + + /* + * sepgsqlTupleNamespace + * + * It returns an OID of the namespace, if the given system object is + * deployed under a certain namespace. + */ + Oid + sepgsqlTupleNamespace(Oid relOid, HeapTuple tuple) + { + Oid nspOid; + + switch (relOid) + { + case RelationRelationId: + nspOid = ((Form_pg_class) GETSTRUCT(tuple))->relnamespace; + break; + + case ConstraintRelationId: + nspOid = ((Form_pg_constraint) GETSTRUCT(tuple))->connamespace; + break; + + case ConversionRelationId: + nspOid = ((Form_pg_conversion) GETSTRUCT(tuple))->connamespace; + break; + + case OperatorClassRelationId: + nspOid = ((Form_pg_opclass) GETSTRUCT(tuple))->opcnamespace; + break; + + case OperatorRelationId: + nspOid = ((Form_pg_operator) GETSTRUCT(tuple))->oprnamespace; + break; + + case OperatorFamilyRelationId: + nspOid = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfnamespace; + break; + + case ProcedureRelationId: + nspOid = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; + break; + + case TSConfigRelationId: + nspOid = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgnamespace; + break; + + case TSDictionaryRelationId: + nspOid = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictnamespace; + break; + + case TSParserRelationId: + nspOid = ((Form_pg_ts_parser) GETSTRUCT(tuple))->prsnamespace; + break; + + case TSTemplateRelationId: + nspOid = ((Form_pg_ts_template) GETSTRUCT(tuple))->tmplnamespace; + break; + + default: + /* no specific namespace */ + nspOid = InvalidOid; + break; + } + + return nspOid; + } + + /* + * sepgsqlTupleAuditName + * + * It returns an OID of the namespace, if the given system object is + * deployed under a certain namespace. + */ + void + sepgsqlTupleAuditName(Oid relid, HeapTuple tuple, char *auname_buf) + { + char *name; + Oid extid; + + switch (relid) + { + case AccessMethodRelationId: + name = NameStr(((Form_pg_am) GETSTRUCT(tuple))->amname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + case AttributeRelationId: + name = NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname); + extid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; + sprintf(audit_name, "%s.%s", name, extid); + return; + + case AuthIdRelationId: + name = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + case ConversionRelationId: + name = NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + case DatabaseRelationId: + name = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + case ForeignDataWrapperRelationId: + name = NameStr(((Form_pg_foreign_data_wrapper) GETSTRUCT(tuple))->fdwname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + case ForeignServerRelationId: + name = NameStr(((Form_pg_foreign_server) GETSTRUCT(tuple))->srvname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + case LanguageRelationId: + name = NameStr(((Form_pg_language) GETSTRUCT(tuple))->lanname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + case NamespaceRelationId: + name = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + case OperatorClassRelationId: + name = NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + case OperatorRelationId: + name = NameStr(((Form_pg_operator) GETSTRUCT(tuple))->oprname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + case OperatorFamilyRelationId: + name = NameStr(((Form_pg_opfamily) GETSTRUCT(tuple))->opfname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + case ProcedureRelationId: + name = NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + case RelationRelationId: + name = NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + case TableSpaceRelationId: + name = NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + case TSConfigRelationId: + name = NameStr(((Form_pg_ts_config) GETSTRUCT(tuple))->cfgname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + case TSDictionaryRelationId: + name = NameStr(((Form_pg_ts_dict) GETSTRUCT(tuple))->dictname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + case TSParserRelationId: + name = NameStr(((Form_pg_ts_parser) GETSTRUCT(tuple))->prsname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + case TSTemplateRelationId: + name = NameStr(((Form_pg_templace) GETSTRUCT(tuple))->tmplname); + strncpy(auname_buf, name, NAMEDATALEN); + break; + + default: + /* no auditable name */ + auname_buf[0] = '\0'; + break; + } + } + #endif diff -Nrpc base/src/backend/security/sepgsql/policy/Makefile sepgsql/src/backend/security/sepgsql/policy/Makefile *** base/src/backend/security/sepgsql/policy/Makefile Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/policy/Makefile Wed Jul 15 19:35:52 2009 *************** *** 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_INSTDIR := $(POLICY_BASEDIR)/packages + 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: sepostgresql-devel.pp + + install: all + test -d $(POLICY_INSTDIR) || mkdir -p $(POLICY_INSTDIR) + install -p -m 0644 sepostgresql-devel.pp $(POLICY_INSTDIR) + + sepostgresql-devel.pp: sepostgresql-devel.te sepostgresql-devel.fc + $(MAKE) -f $(POLICY_MAKEFILE) + + 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 *.fc diff -Nrpc base/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template *** base/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template Wed Jul 15 19:35:52 2009 *************** *** 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 -Nrpc base/src/backend/security/sepgsql/policy/sepostgresql-devel.te sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.te *** base/src/backend/security/sepgsql/policy/sepostgresql-devel.te Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.te Tue Dec 1 17:11:40 2009 *************** *** 0 **** --- 1,123 ---- + policy_module(sepostgresql-devel, 3.29) + + 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_exec_t; + + attribute tmpfile; + ') + + ################################# + # + # Domain for Testcases + # + + role sepgsql_test_r; + + userdom_unpriv_user_template(sepgsql_test) + postgresql_role(sepgsql_test_r, sepgsql_test_t) + + allow sepgsql_test_t tmpfile : dir search_dir_perms; + allow sepgsql_test_t tmpfile : file rw_file_perms; + + optional_policy(` + term_write_all_terms(sepgsql_test_t) + ') + + optional_policy(` + gen_require(` + type unconfined_t; + role unconfined_r; + ') + + tunable_policy(`sepgsql_regression_test_mode',` + allow unconfined_t sepgsql_test_t : process transition; + ') + unconfined_rw_pipes(sepgsql_test_t) + 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_sysobj_table_type : db_tuple *; + 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_sysobj_table_type : db_tuple *; + 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 -Nrpc base/src/backend/storage/file/fd.c sepgsql/src/backend/storage/file/fd.c *** base/src/backend/storage/file/fd.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/storage/file/fd.c Wed Jul 15 19:48:58 2009 *************** FileTruncate(File file, off_t offset) *** 1319,1324 **** --- 1319,1331 ---- 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 -Nrpc base/src/backend/storage/ipc/ipci.c sepgsql/src/backend/storage/ipc/ipci.c *** base/src/backend/storage/ipc/ipci.c Thu May 7 08:49:32 2009 --- sepgsql/src/backend/storage/ipc/ipci.c Wed Jul 15 19:35:52 2009 *************** *** 25,30 **** --- 25,31 ---- #include "postmaster/autovacuum.h" #include "postmaster/bgwriter.h" #include "postmaster/postmaster.h" + #include "security/sepgsql.h" #include "storage/bufmgr.h" #include "storage/ipc.h" #include "storage/pg_shmem.h" *************** CreateSharedMemoryAndSemaphores(bool mak *** 119,124 **** --- 120,126 ---- #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 -Nrpc base/src/backend/storage/large_object/inv_api.c sepgsql/src/backend/storage/large_object/inv_api.c *** base/src/backend/storage/large_object/inv_api.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/storage/large_object/inv_api.c Tue Sep 8 23:55:48 2009 *************** *** 39,44 **** --- 39,45 ---- #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/rel.h" *************** close_lo_relation(bool isCommit) *** 137,148 **** * read with can be specified. */ static bool ! myLargeObjectExists(Oid loid, Snapshot snapshot) { bool retval = false; Relation pg_largeobject; ScanKeyData skey[1]; SysScanDesc sd; /* * See if we can find any tuples belonging to the specified LO --- 138,150 ---- * read with can be specified. */ static bool ! 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 *************** myLargeObjectExists(Oid loid, Snapshot s *** 150,164 **** ScanKeyInit(&skey[0], Anum_pg_largeobject_loid, BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(loid)); pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock); sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true, ! snapshot, 1, skey); ! if (systable_getnext(sd) != NULL) retval = true; systable_endscan(sd); --- 152,170 ---- ScanKeyInit(&skey[0], Anum_pg_largeobject_loid, BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(lobj->id)); pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock); sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true, ! lobj->snapshot, 1, skey); ! tuple = systable_getnext(sd); ! if (HeapTupleIsValid(tuple)) ! { retval = true; + lobj->secid = HeapTupleGetSecid(tuple); + } systable_endscan(sd); *************** inv_open(Oid lobjId, int flags, MemoryCo *** 260,266 **** elog(ERROR, "invalid flags: %d", flags); /* Can't use LargeObjectExists here because it always uses SnapshotNow */ ! if (!myLargeObjectExists(lobjId, retval->snapshot)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("large object %u does not exist", lobjId))); --- 266,272 ---- elog(ERROR, "invalid flags: %d", flags); /* Can't use LargeObjectExists here because it always uses SnapshotNow */ ! if (!myLargeObjectExists(retval)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("large object %u does not exist", lobjId))); *************** inv_write(LargeObjectDesc *obj_desc, con *** 642,647 **** --- 648,655 ---- replace[Anum_pg_largeobject_data - 1] = true; newtup = heap_modify_tuple(oldtuple, RelationGetDescr(lo_heap_r), values, nulls, replace); + if (HeapTupleHasSecid(newtup)) + HeapTupleSetSecid(newtup, obj_desc->secid); simple_heap_update(lo_heap_r, &newtup->t_self, newtup); CatalogIndexInsert(indstate, newtup); heap_freetuple(newtup); *************** inv_write(LargeObjectDesc *obj_desc, con *** 685,690 **** --- 693,700 ---- values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno); values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf); newtup = heap_form_tuple(lo_heap_r->rd_att, values, nulls); + if (HeapTupleHasSecid(newtup)) + HeapTupleSetSecid(newtup, obj_desc->secid); simple_heap_insert(lo_heap_r, newtup); CatalogIndexInsert(indstate, newtup); heap_freetuple(newtup); *************** inv_truncate(LargeObjectDesc *obj_desc, *** 845,850 **** --- 855,862 ---- values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno); values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf); newtup = heap_form_tuple(lo_heap_r->rd_att, values, nulls); + if (HeapTupleHasSecid(newtup)) + HeapTupleSetSecid(newtup, obj_desc->secid); simple_heap_insert(lo_heap_r, newtup); CatalogIndexInsert(indstate, newtup); heap_freetuple(newtup); *************** inv_truncate(LargeObjectDesc *obj_desc, *** 868,870 **** --- 880,978 ---- */ 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 = HeapTupleGetSecid(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 (!HeapTupleHasSecid(newtuple)) + elog(ERROR, "Unable to assign security label on \"%s\"", + RelationGetRelationName(rel)); + HeapTupleSetSecid(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 -Nrpc base/src/backend/tcop/fastpath.c sepgsql/src/backend/tcop/fastpath.c *** base/src/backend/tcop/fastpath.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/tcop/fastpath.c Thu Sep 17 17:04:16 2009 *************** *** 26,31 **** --- 26,32 ---- #include "libpq/pqformat.h" #include "mb/pg_wchar.h" #include "miscadmin.h" + #include "security/sepgsql.h" #include "tcop/fastpath.h" #include "tcop/tcopprot.h" #include "utils/acl.h" *************** HandleFunctionRequest(StringInfo msgBuf) *** 343,353 **** --- 344,356 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(fip->namespace)); + sepgsql_schema_search(fip->namespace, true); aclresult = pg_proc_aclcheck(fid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(fid)); + sepgsql_proc_execute(fid); /* * Prepare function call info block and insert arguments. diff -Nrpc base/src/backend/tcop/pquery.c sepgsql/src/backend/tcop/pquery.c *** base/src/backend/tcop/pquery.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/tcop/pquery.c Wed Jul 15 19:30:50 2009 *************** PortalStart(Portal portal, ParamListInfo *** 573,579 **** Assert(pstmt->returningLists); portal->tupDesc = ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), ! false); } /* --- 573,579 ---- Assert(pstmt->returningLists); portal->tupDesc = ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), ! false, false); } /* diff -Nrpc base/src/backend/tcop/utility.c sepgsql/src/backend/tcop/utility.c *** base/src/backend/tcop/utility.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/tcop/utility.c Wed Jul 15 21:17:18 2009 *************** *** 50,55 **** --- 50,56 ---- #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" *************** check_xact_readonly(Node *parsetree) *** 162,167 **** --- 163,169 ---- case T_AlterRoleSetStmt: case T_AlterObjectSchemaStmt: case T_AlterOwnerStmt: + case T_AlterSecLabelStmt: case T_AlterSeqStmt: case T_AlterTableStmt: case T_RenameStmt: *************** ProcessUtility(Node *parsetree, *** 612,617 **** --- 614,623 ---- ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree); break; + case T_AlterSecLabelStmt: + ExecAlterSecLabelStmt((AlterSecLabelStmt *) parsetree); + break; + case T_AlterTableStmt: { List *stmts; *************** ProcessUtility(Node *parsetree, *** 893,898 **** --- 899,905 ---- LoadStmt *stmt = (LoadStmt *) parsetree; closeAllVfds(); /* probably not necessary... */ + /* Allowed names are restricted if you're not superuser */ load_file(stmt->filename, !superuser()); } *************** CreateCommandTag(Node *parsetree) *** 1635,1640 **** --- 1642,1672 ---- } 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; + default: + tag = "???"; + break; + } + break; + case T_AlterTableStmt: switch (((AlterTableStmt *) parsetree)->relkind) { *************** GetCommandLogLevel(Node *parsetree) *** 2213,2218 **** --- 2245,2254 ---- lev = LOGSTMT_DDL; break; + case T_AlterSecLabelStmt: + lev = LOGSTMT_DDL; + break; + case T_AlterTableStmt: lev = LOGSTMT_DDL; break; diff -Nrpc base/src/backend/utils/adt/genfile.c sepgsql/src/backend/utils/adt/genfile.c *** base/src/backend/utils/adt/genfile.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/utils/adt/genfile.c Mon Sep 28 09:29:32 2009 *************** *** 24,29 **** --- 24,30 ---- #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" *************** pg_read_file(PG_FUNCTION_ARGS) *** 99,104 **** --- 100,108 ---- filename = convert_and_check_filename(filename_t); + /* SELinux: check file:{read} permission */ + sepgsql_file_read(filename); + if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL) ereport(ERROR, (errcode_for_file_access(), *************** pg_stat_file(PG_FUNCTION_ARGS) *** 159,164 **** --- 163,170 ---- (errmsg("must be superuser to get file information")))); filename = convert_and_check_filename(filename_t); + /* SELinux: check file:{getattr} permission */ + sepgsql_file_stat(filename); if (stat(filename, &fst) < 0) ereport(ERROR, diff -Nrpc base/src/backend/utils/adt/ri_triggers.c sepgsql/src/backend/utils/adt/ri_triggers.c *** base/src/backend/utils/adt/ri_triggers.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/utils/adt/ri_triggers.c Wed Jul 15 19:39:56 2009 *************** *** 39,44 **** --- 39,45 ---- #include "parser/parse_coerce.h" #include "parser/parse_relation.h" #include "miscadmin.h" + #include "security/rowlevel.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" *************** RI_Initial_Check(Trigger *trigger, Relat *** 2627,2632 **** --- 2628,2634 ---- const char *sep; int i; int old_work_mem; + int save_rowlv; char workmembuf[32]; int spi_result; SPIPlanPtr qplan; *************** RI_Initial_Check(Trigger *trigger, Relat *** 2759,2764 **** --- 2761,2771 ---- 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 don't need to register the *************** RI_Initial_Check(Trigger *trigger, Relat *** 2771,2776 **** --- 2778,2786 ---- 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); *************** ri_PerformCheck(RI_QueryKey *qkey, SPIPl *** 3264,3269 **** --- 3274,3280 ---- 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]; *************** ri_PerformCheck(RI_QueryKey *qkey, SPIPl *** 3346,3357 **** --- 3357,3375 ---- 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 -Nrpc base/src/backend/utils/adt/tid.c sepgsql/src/backend/utils/adt/tid.c *** base/src/backend/utils/adt/tid.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/utils/adt/tid.c Fri Sep 18 14:51:00 2009 *************** currtid_byreloid(PG_FUNCTION_ARGS) *** 347,352 **** --- 347,354 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(rel)); + /* SELinux checks */ + sepgsql_relation_get_transaction_id(RelationGetRelid(rel)); if (rel->rd_rel->relkind == RELKIND_VIEW) return currtid_for_view(rel, tid); *************** currtid_byrelname(PG_FUNCTION_ARGS) *** 377,382 **** --- 379,386 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(rel)); + /* SELinux checks */ + sepgsql_relation_get_transaction_id(RelationGetRelid(rel)); if (rel->rd_rel->relkind == RELKIND_VIEW) return currtid_for_view(rel, tid); diff -Nrpc base/src/backend/utils/adt/trigfuncs.c sepgsql/src/backend/utils/adt/trigfuncs.c *** base/src/backend/utils/adt/trigfuncs.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/utils/adt/trigfuncs.c Tue Sep 8 23:55:48 2009 *************** suppress_redundant_updates_trigger(PG_FU *** 76,81 **** --- 76,85 ---- !OidIsValid(HeapTupleHeaderGetOid(newheader))) HeapTupleHeaderSetOid(newheader, HeapTupleHeaderGetOid(oldheader)); + if (HeapTupleHeaderHasSecid(newheader) && + !OidIsValid(HeapTupleHeaderGetSecid(newheader))) + HeapTupleHeaderSetSecid(newheader, HeapTupleHeaderGetSecid(oldheader)); + /* if the tuple payload is the same ... */ if (newtuple->t_len == oldtuple->t_len && newheader->t_hoff == oldheader->t_hoff && diff -Nrpc base/src/backend/utils/cache/plancache.c sepgsql/src/backend/utils/cache/plancache.c *** base/src/backend/utils/cache/plancache.c Sun Sep 6 19:40:49 2009 --- sepgsql/src/backend/utils/cache/plancache.c Sun Sep 6 19:53:10 2009 *************** PlanCacheComputeResultDesc(List *stmt_li *** 859,870 **** if (IsA(node, Query)) { query = (Query *) node; ! return ExecCleanTypeFromTL(query->targetList, false); } if (IsA(node, PlannedStmt)) { pstmt = (PlannedStmt *) node; ! return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false); } /* other cases shouldn't happen, but return NULL */ break; --- 859,870 ---- if (IsA(node, Query)) { query = (Query *) node; ! return ExecCleanTypeFromTL(query->targetList, false, false); } if (IsA(node, PlannedStmt)) { pstmt = (PlannedStmt *) node; ! return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false, false); } /* other cases shouldn't happen, but return NULL */ break; *************** PlanCacheComputeResultDesc(List *stmt_li *** 875,887 **** { query = (Query *) node; Assert(query->returningList); ! return ExecCleanTypeFromTL(query->returningList, false); } if (IsA(node, PlannedStmt)) { pstmt = (PlannedStmt *) node; Assert(pstmt->returningLists); ! return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false); } /* other cases shouldn't happen, but return NULL */ break; --- 875,888 ---- { query = (Query *) node; Assert(query->returningList); ! return ExecCleanTypeFromTL(query->returningList, false, false); } if (IsA(node, PlannedStmt)) { pstmt = (PlannedStmt *) node; Assert(pstmt->returningLists); ! return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), ! false, false); } /* other cases shouldn't happen, but return NULL */ break; diff -Nrpc base/src/backend/utils/cache/relcache.c sepgsql/src/backend/utils/cache/relcache.c *** base/src/backend/utils/cache/relcache.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/utils/cache/relcache.c Wed Sep 9 13:14:37 2009 *************** *** 47,52 **** --- 47,53 ---- #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" *************** RelationBuildDesc(Oid targetRelId, Relat *** 865,870 **** --- 866,875 ---- /* extract reloptions if any */ RelationParseRelOptions(relation, pg_class_tuple); + /* Fixup relation->rd_att->tdhassecid */ + RelationGetDescr(relation)->tdhassecid + = securityTupleDescHasSecid(relid, relp->relkind); + /* * initialize the relation lock manager information */ *************** formrdesc(const char *relationName, Oid *** 1458,1463 **** --- 1463,1473 ---- RelationGetRelid(relation) = relation->rd_att->attrs[0]->attrelid; relation->rd_rel->relfilenode = RelationGetRelid(relation); + /* Fixup relation->rd_att->tdhassecid */ + RelationGetDescr(relation)->tdhassecid + = securityTupleDescHasSecid(RelationGetRelid(relation), + RELKIND_RELATION); + /* * initialize the relation lock manager information */ *************** BuildHardcodedDescriptor(int natts, Form *** 2699,2704 **** --- 2709,2721 ---- result = CreateTemplateTupleDesc(natts, hasoids); result->tdtypeid = RECORDOID; /* not right, but we don't care */ result->tdtypmod = -1; + /* + * 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->tdhassecid don't give us + * any effect. + * We omit to invoke securityTupleDescHasSecid() here. + */ for (i = 0; i < natts; i++) { *************** load_relcache_init_file(void) *** 3453,3458 **** --- 3470,3480 ---- rel->rd_options = NULL; } + /* Fixup rel->rd_att->tdhassecid */ + RelationGetDescr(rel)->tdhassecid + = securityTupleDescHasSecid(RelationGetRelid(rel), + RelationGetForm(rel)->relkind); + /* mark not-null status */ if (has_not_null) { diff -Nrpc base/src/backend/utils/cache/syscache.c sepgsql/src/backend/utils/cache/syscache.c *** base/src/backend/utils/cache/syscache.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/utils/cache/syscache.c Wed Sep 9 21:29:33 2009 *************** *** 41,46 **** --- 41,47 ---- #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" *************** static const struct cachedesc cacheinfo[ *** 584,589 **** --- 585,614 ---- }, 1024 }, + {SecurityRelationId, /* SECURITYATTR */ + SecuritySecattrIndexId, + Anum_pg_security_relid, + 4, + { + Anum_pg_security_datid, + Anum_pg_security_relid, + Anum_pg_security_seckind, + Anum_pg_security_secattr + }, + 128, + }, + {SecurityRelationId, /* SECURITYSECID */ + SecuritySecidIndexId, + Anum_pg_security_relid, + 2, + { + Anum_pg_security_secid, + Anum_pg_security_datid, + 0, + 0 + }, + 128 + }, {StatisticRelationId, /* STATRELATT */ StatisticRelidAttnumIndexId, Anum_pg_statistic_starelid, *************** GetSysCacheOid(int cacheId, *** 931,937 **** return result; } - /* * SearchSysCacheAttName * --- 956,961 ---- diff -Nrpc base/src/backend/utils/fmgr/dfmgr.c sepgsql/src/backend/utils/fmgr/dfmgr.c *** base/src/backend/utils/fmgr/dfmgr.c Sun Sep 6 19:40:49 2009 --- sepgsql/src/backend/utils/fmgr/dfmgr.c Thu Sep 17 17:04:16 2009 *************** *** 23,28 **** --- 23,29 ---- #endif #include "lib/stringinfo.h" #include "miscadmin.h" + #include "security/sepgsql.h" #include "utils/dynamic_loader.h" #include "utils/hsearch.h" *************** load_external_function(char *filename, c *** 109,114 **** --- 110,118 ---- /* Expand the possibly-abbreviated filename to an exact path name */ fullname = expand_dynamic_library_name(filename); + /* SELinux checks db_database:{load_module} */ + sepgsql_database_load_module(MyDatabaseId, fullname); + /* Load the shared library, unless we already did */ lib_handle = internal_load_library(fullname); *************** load_file(const char *filename, bool res *** 149,154 **** --- 153,161 ---- /* Expand the possibly-abbreviated filename to an exact path name */ fullname = expand_dynamic_library_name(filename); + /* SELinux checks db_database:{load_module} */ + sepgsql_database_load_module(MyDatabaseId, fullname); + /* Unload the library if currently loaded */ internal_unload_library(fullname); diff -Nrpc base/src/backend/utils/fmgr/fmgr.c sepgsql/src/backend/utils/fmgr/fmgr.c *** base/src/backend/utils/fmgr/fmgr.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/backend/utils/fmgr/fmgr.c Thu Sep 17 17:04:16 2009 *************** *** 24,29 **** --- 24,30 ---- #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "pgstat.h" + #include "security/sepgsql.h" #include "utils/builtins.h" #include "utils/fmgrtab.h" #include "utils/guc.h" *************** fmgr_info_cxt_security(Oid functionId, F *** 289,294 **** --- 290,296 ---- } finfo->fn_oid = functionId; + sepgsql_proc_entrypoint(finfo, procedureTuple); ReleaseSysCache(procedureTuple); } diff -Nrpc base/src/backend/utils/init/postinit.c sepgsql/src/backend/utils/init/postinit.c *** base/src/backend/utils/init/postinit.c Sun Sep 6 19:40:49 2009 --- sepgsql/src/backend/utils/init/postinit.c Thu Sep 17 17:04:16 2009 *************** *** 32,37 **** --- 32,38 ---- #include "pgstat.h" #include "postmaster/autovacuum.h" #include "postmaster/postmaster.h" + #include "security/sepgsql.h" #include "storage/backendid.h" #include "storage/bufmgr.h" #include "storage/fd.h" *************** CheckMyDatabase(const char *name, bool a *** 201,207 **** 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.) */ --- 202,208 ---- 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.) */ *************** CheckMyDatabase(const char *name, bool a *** 213,218 **** --- 214,222 ---- errmsg("permission denied for database \"%s\"", name), errdetail("User does not have CONNECT privilege."))); + /* SELinux: db_database:{access} */ + sepgsql_database_access(MyDatabaseId); + /* * Check connection limit for this database. * *************** InitPostgres(const char *in_dbname, Oid *** 575,580 **** --- 579,587 ---- */ RelationCacheInitializePhase2(); + /* Initialize SE-PostgreSQL internal facilities */ + sepgsqlAvcInitialize(); + /* * Figure out our postgres user id, and see if we are a superuser. * diff -Nrpc base/src/backend/utils/misc/guc.c sepgsql/src/backend/utils/misc/guc.c *** base/src/backend/utils/misc/guc.c Sun Sep 6 19:40:49 2009 --- sepgsql/src/backend/utils/misc/guc.c Sun Sep 6 19:53:10 2009 *************** *** 56,61 **** --- 56,62 ---- #include "postmaster/syslogger.h" #include "postmaster/walwriter.h" #include "regex/regex.h" + #include "security/sepgsql.h" #include "storage/bufmgr.h" #include "storage/fd.h" #include "tcop/tcopprot.h" *************** static struct config_bool ConfigureNames *** 1220,1225 **** --- 1221,1252 ---- &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 -Nrpc base/src/backend/utils/misc/postgresql.conf.sample sepgsql/src/backend/utils/misc/postgresql.conf.sample *** base/src/backend/utils/misc/postgresql.conf.sample Sun Sep 6 19:40:49 2009 --- sepgsql/src/backend/utils/misc/postgresql.conf.sample Tue Dec 1 17:11:40 2009 *************** *** 51,57 **** #------------------------------------------------------------------------------ ! # CONNECTIONS AND AUTHENTICATION #------------------------------------------------------------------------------ # - Connection Settings - --- 51,57 ---- #------------------------------------------------------------------------------ ! # CONNECTIONS, AUTHENTICATION AND SECURITY #------------------------------------------------------------------------------ # - Connection Settings - *************** *** 95,101 **** # 0 selects the system default #tcp_keepalives_count = 0 # TCP_KEEPCNT; # 0 selects the system default ! #------------------------------------------------------------------------------ # RESOURCE USAGE (except WAL) --- 95,101 ---- # 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 -Nrpc base/src/backend/utils/misc/superuser.c sepgsql/src/backend/utils/misc/superuser.c *** base/src/backend/utils/misc/superuser.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/utils/misc/superuser.c Thu Sep 17 17:04:16 2009 *************** *** 21,26 **** --- 21,27 ---- #include "postgres.h" #include "catalog/pg_authid.h" + #include "security/sepgsql.h" #include "utils/inval.h" #include "utils/syscache.h" #include "miscadmin.h" *************** superuser_arg(Oid roleid) *** 60,66 **** /* Quick out for cache hit */ if (OidIsValid(last_roleid) && last_roleid == roleid) ! return last_roleid_is_super; /* Special escape path in case you deleted all your users. */ if (!IsUnderPostmaster && roleid == BOOTSTRAP_SUPERUSERID) --- 61,70 ---- /* Quick out for cache hit */ if (OidIsValid(last_roleid) && last_roleid == roleid) ! { ! result = last_roleid_is_super; ! goto out; ! } /* Special escape path in case you deleted all your users. */ if (!IsUnderPostmaster && roleid == BOOTSTRAP_SUPERUSERID) *************** superuser_arg(Oid roleid) *** 94,99 **** --- 98,107 ---- last_roleid = roleid; last_roleid_is_super = result; + out: + if (result) + result = sepgsql_database_superuser(MyDatabaseId); + return result; } diff -Nrpc base/src/bin/initdb/initdb.c sepgsql/src/bin/initdb/initdb.c *** base/src/bin/initdb/initdb.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/bin/initdb/initdb.c Wed Jul 15 19:35:52 2009 *************** static bool debug = false; *** 87,92 **** --- 87,93 ---- static bool noclean = false; static bool show_setting = false; static char *xlog_dir = ""; + static bool enable_selinux = false; /* internal vars */ *************** setup_config(void) *** 1205,1210 **** --- 1206,1218 ---- "#default_text_search_config = 'pg_catalog.simple'", repltok); + if (enable_selinux) + { + strcpy(repltok, "sepostgresql = on"); + conflines = replace_token(conflines, + "#sepostgresql = off", repltok); + } + snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data); writefile(path, conflines); *************** usage(const char *progname) *** 2443,2448 **** --- 2451,2457 ---- printf(_(" -U, --username=NAME database superuser name\n")); printf(_(" -W, --pwprompt prompt for a password for the new superuser\n")); printf(_(" -X, --xlogdir=XLOGDIR location for the transaction log directory\n")); + printf(_(" --enable-selinux enables SELinux support, if compiled\n")); printf(_("\nLess commonly used options:\n")); printf(_(" -d, --debug generate lots of debugging output\n")); printf(_(" -L DIRECTORY where to find the input files\n")); *************** main(int argc, char *argv[]) *** 2478,2483 **** --- 2487,2493 ---- {"auth", required_argument, NULL, 'A'}, {"pwprompt", no_argument, NULL, 'W'}, {"pwfile", required_argument, NULL, 9}, + {"enable-selinux", no_argument, NULL, 10}, {"username", required_argument, NULL, 'U'}, {"help", no_argument, NULL, '?'}, {"version", no_argument, NULL, 'V'}, *************** main(int argc, char *argv[]) *** 2594,2599 **** --- 2604,2612 ---- case 9: pwfilename = xstrdup(optarg); break; + case 10: + enable_selinux = true; + break; case 's': show_setting = true; break; diff -Nrpc base/src/bin/pg_dump/pg_dump.c sepgsql/src/bin/pg_dump/pg_dump.c *** base/src/bin/pg_dump/pg_dump.c Sun Sep 6 19:40:49 2009 --- sepgsql/src/bin/pg_dump/pg_dump.c Tue Sep 8 17:13:41 2009 *************** static int disable_dollar_quoting = 0; *** 112,117 **** --- 112,119 ---- static int dump_inserts = 0; static int column_inserts = 0; + /* flag to turn on/off security_context */ + static int security_context = 0; static void help(const char *progname); static void expand_schema_name_patterns(SimpleStringList *patterns, *************** main(int argc, char **argv) *** 277,282 **** --- 279,285 ---- {"no-tablespaces", no_argument, &outputNoTablespaces, 1}, {"role", required_argument, NULL, 3}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, + {"security-context", no_argument, &security_context, 1}, {NULL, 0, NULL, 0} }; *************** main(int argc, char **argv) *** 425,430 **** --- 428,435 ---- outputNoTablespaces = 1; else if (strcmp(optarg, "use-set-session-authorization") == 0) use_setsessauth = 1; + else if (strcmp(optarg, "security-context") == 0) + security_context = 1; else { fprintf(stderr, *************** main(int argc, char **argv) *** 573,578 **** --- 578,605 ---- std_strings = PQparameterStatus(g_conn, "standard_conforming_strings"); g_fout->std_strings = (std_strings && strcmp(std_strings, "on") == 0); + /* Check availability of SE-PostgreSQL */ + if (security_context > 0) + { + PGresult *res; + + res = PQexec(g_conn, "SHOW sepostgresql"); + if (PQresultStatus(res) != PGRES_TUPLES_OK || + PQntuples(res) != 1 || + strcmp(PQgetvalue(res, 0, 0), "on") != 0) + { + write_msg(NULL, "SE-PostgreSQL is not available now."); + exit(1); + } + } + + /* + * It needs to force column insertion mode, when --inserts + * and either --security-label or --security-acl is given. + */ + if (security_context > 0 && dump_inserts) + column_inserts = 1; + /* Set the role if requested */ if (use_role && g_fout->remoteVersion >= 80100) { *************** help(const char *progname) *** 826,831 **** --- 853,860 ---- printf(_(" --use-set-session-authorization\n" " use SET SESSION AUTHORIZATION commands instead of\n" " ALTER OWNER commands to set ownership\n")); + printf(_(" --security-label dump SE-PostgreSQL security labels\n")); + printf(_(" --security-acl dump row-level database ACLs\n")); printf(_("\nConnection options:\n")); printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); *************** dumpTableData_insert(Archive *fout, void *** 1227,1233 **** if (fout->remoteVersion >= 70100) { appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " ! "SELECT * FROM ONLY %s", fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, classname)); } --- 1256,1263 ---- if (fout->remoteVersion >= 70100) { appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " ! "SELECT %s* FROM ONLY %s", ! (security_context > 0 ? "security_context, " : ""), fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, classname)); } *************** dumpDatabase(Archive *AH) *** 1583,1589 **** i_collate, i_ctype, i_frozenxid, ! i_tablespace; CatalogId dbCatId; DumpId dbDumpId; const char *datname, --- 1613,1620 ---- i_collate, i_ctype, i_frozenxid, ! i_tablespace, ! i_seclabel; CatalogId dbCatId; DumpId dbDumpId; const char *datname, *************** dumpDatabase(Archive *AH) *** 1591,1597 **** *encoding, *collate, *ctype, ! *tablespace; uint32 frozenxid; datname = PQdb(g_conn); --- 1622,1629 ---- *encoding, *collate, *ctype, ! *tablespace, ! *seclabel; uint32 frozenxid; datname = PQdb(g_conn); *************** dumpDatabase(Archive *AH) *** 1610,1620 **** "pg_encoding_to_char(encoding) AS encoding, " "datcollate, datctype, datfrozenxid, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " ! "shobj_description(oid, 'pg_database') AS description " ! "FROM pg_database " "WHERE datname = ", ! username_subquery); appendStringLiteralAH(dbQry, datname, AH); } else if (g_fout->remoteVersion >= 80200) --- 1642,1653 ---- "pg_encoding_to_char(encoding) AS encoding, " "datcollate, datctype, datfrozenxid, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " ! "shobj_description(oid, 'pg_database') AS description, " ! "%s as security_context " "FROM pg_database " "WHERE datname = ", ! username_subquery, ! security_context ? "security_context" : "NULL"); appendStringLiteralAH(dbQry, datname, AH); } else if (g_fout->remoteVersion >= 80200) *************** dumpDatabase(Archive *AH) *** 1624,1631 **** "pg_encoding_to_char(encoding) AS encoding, " "NULL AS datcollate, NULL AS datctype, datfrozenxid, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " ! "shobj_description(oid, 'pg_database') AS description " ! "FROM pg_database " "WHERE datname = ", username_subquery); --- 1657,1664 ---- "pg_encoding_to_char(encoding) AS encoding, " "NULL AS datcollate, NULL AS datctype, datfrozenxid, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " ! "shobj_description(oid, 'pg_database') AS description, " ! "NULL as security_context " "FROM pg_database " "WHERE datname = ", username_subquery); *************** dumpDatabase(Archive *AH) *** 1637,1643 **** "(%s datdba) AS dba, " "pg_encoding_to_char(encoding) AS encoding, " "NULL AS datcollate, NULL AS datctype, datfrozenxid, " ! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace " "FROM pg_database " "WHERE datname = ", username_subquery); --- 1670,1677 ---- "(%s datdba) AS dba, " "pg_encoding_to_char(encoding) AS encoding, " "NULL AS datcollate, NULL AS datctype, datfrozenxid, " ! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " ! "NULL as security_context " "FROM pg_database " "WHERE datname = ", username_subquery); *************** dumpDatabase(Archive *AH) *** 1650,1656 **** "pg_encoding_to_char(encoding) AS encoding, " "NULL AS datcollate, NULL AS datctype, " "0 AS datfrozenxid, " ! "NULL AS tablespace " "FROM pg_database " "WHERE datname = ", username_subquery); --- 1684,1691 ---- "pg_encoding_to_char(encoding) AS encoding, " "NULL AS datcollate, NULL AS datctype, " "0 AS datfrozenxid, " ! "NULL AS tablespace, " ! "NULL AS security_context " "FROM pg_database " "WHERE datname = ", username_subquery); *************** dumpDatabase(Archive *AH) *** 1665,1671 **** "pg_encoding_to_char(encoding) AS encoding, " "NULL AS datcollate, NULL AS datctype, " "0 AS datfrozenxid, " ! "NULL AS tablespace " "FROM pg_database " "WHERE datname = ", username_subquery); --- 1700,1707 ---- "pg_encoding_to_char(encoding) AS encoding, " "NULL AS datcollate, NULL AS datctype, " "0 AS datfrozenxid, " ! "NULL AS tablespace, " ! "NULL as security_context " "FROM pg_database " "WHERE datname = ", username_subquery); *************** dumpDatabase(Archive *AH) *** 1699,1704 **** --- 1735,1741 ---- i_ctype = PQfnumber(res, "datctype"); i_frozenxid = PQfnumber(res, "datfrozenxid"); i_tablespace = PQfnumber(res, "tablespace"); + i_seclabel = PQfnumber(res, "security_context"); dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid)); dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid)); *************** dumpDatabase(Archive *AH) *** 1708,1713 **** --- 1745,1751 ---- ctype = PQgetvalue(res, 0, i_ctype); frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid)); tablespace = PQgetvalue(res, 0, i_tablespace); + seclabel = PQgetvalue(res, 0, i_seclabel); appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0", fmtId(datname)); *************** dumpDatabase(Archive *AH) *** 1729,1734 **** --- 1767,1775 ---- if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0) appendPQExpBuffer(creaQry, " TABLESPACE = %s", fmtId(tablespace)); + if (strlen(seclabel) > 0) + appendPQExpBuffer(creaQry, " SECURITY_CONTEXT = '%s'", seclabel); + appendPQExpBuffer(creaQry, ";\n"); if (binary_upgrade) *************** getTables(int *numTables) *** 3190,3195 **** --- 3231,3237 ---- int i_reltablespace; int i_reloptions; int i_toastreloptions; + int i_relseclabel; /* Make sure we are in proper schema */ selectSourceSchema("pg_catalog"); *************** getTables(int *numTables) *** 3231,3237 **** "d.refobjsubid AS owning_col, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " "array_to_string(c.reloptions, ', ') AS reloptions, " ! "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " --- 3273,3280 ---- "d.refobjsubid AS owning_col, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " "array_to_string(c.reloptions, ', ') AS reloptions, " ! "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions, " ! "%s as security_context " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " *************** getTables(int *numTables) *** 3242,3247 **** --- 3285,3291 ---- "WHERE c.relkind in ('%c', '%c', '%c', '%c') " "ORDER BY c.oid", username_subquery, + security_context ? "c.security_context" : "NULL", RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); *************** getTables(int *numTables) *** 3263,3269 **** "d.refobjsubid AS owning_col, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " "array_to_string(c.reloptions, ', ') AS reloptions, " ! "NULL AS toast_reloptions " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " --- 3307,3314 ---- "d.refobjsubid AS owning_col, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " "array_to_string(c.reloptions, ', ') AS reloptions, " ! "NULL AS toast_reloptions, " ! "NULL as security_context " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " *************** getTables(int *numTables) *** 3294,3300 **** "d.refobjsubid AS owning_col, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " "NULL AS reloptions, " ! "NULL AS toast_reloptions " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " --- 3339,3346 ---- "d.refobjsubid AS owning_col, " "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " "NULL AS reloptions, " ! "NULL AS toast_reloptions, " ! "NULL as security_context " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " *************** getTables(int *numTables) *** 3325,3331 **** "d.refobjsubid AS owning_col, " "NULL AS reltablespace, " "NULL AS reloptions, " ! "NULL AS toast_reloptions " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " --- 3371,3378 ---- "d.refobjsubid AS owning_col, " "NULL AS reltablespace, " "NULL AS reloptions, " ! "NULL AS toast_reloptions, " ! "NULL as security_context " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " *************** getTables(int *numTables) *** 3352,3358 **** "NULL::int4 AS owning_col, " "NULL AS reltablespace, " "NULL AS reloptions, " ! "NULL AS toast_reloptions " "FROM pg_class " "WHERE relkind IN ('%c', '%c', '%c') " "ORDER BY oid", --- 3399,3406 ---- "NULL::int4 AS owning_col, " "NULL AS reltablespace, " "NULL AS reloptions, " ! "NULL AS toast_reloptions, " ! "NULL AS security_context " "FROM pg_class " "WHERE relkind IN ('%c', '%c', '%c') " "ORDER BY oid", *************** getTables(int *numTables) *** 3374,3380 **** "NULL::int4 AS owning_col, " "NULL AS reltablespace, " "NULL AS reloptions, " ! "NULL AS toast_reloptions " "FROM pg_class " "WHERE relkind IN ('%c', '%c', '%c') " "ORDER BY oid", --- 3422,3429 ---- "NULL::int4 AS owning_col, " "NULL AS reltablespace, " "NULL AS reloptions, " ! "NULL AS toast_reloptions, " ! "NULL AS security_context " "FROM pg_class " "WHERE relkind IN ('%c', '%c', '%c') " "ORDER BY oid", *************** getTables(int *numTables) *** 3406,3412 **** "NULL::int4 AS owning_col, " "NULL AS reltablespace, " "NULL AS reloptions, " ! "NULL AS toast_reloptions " "FROM pg_class c " "WHERE relkind IN ('%c', '%c') " "ORDER BY oid", --- 3455,3462 ---- "NULL::int4 AS owning_col, " "NULL AS reltablespace, " "NULL AS reloptions, " ! "NULL AS toast_reloptions, " ! "NULL as security_context " "FROM pg_class c " "WHERE relkind IN ('%c', '%c') " "ORDER BY oid", *************** getTables(int *numTables) *** 3451,3456 **** --- 3501,3507 ---- i_reltablespace = PQfnumber(res, "reltablespace"); i_reloptions = PQfnumber(res, "reloptions"); i_toastreloptions = PQfnumber(res, "toast_reloptions"); + i_relseclabel = PQfnumber(res, "security_context"); if (lockWaitTimeout && g_fout->remoteVersion >= 70300) { *************** getTables(int *numTables) *** 3498,3503 **** --- 3549,3555 ---- tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace)); tblinfo[i].reloptions = strdup(PQgetvalue(res, i, i_reloptions)); tblinfo[i].toast_reloptions = strdup(PQgetvalue(res, i, i_toastreloptions)); + tblinfo[i].relseclabel = strdup(PQgetvalue(res, i, i_relseclabel)); /* other fields were zeroed above */ *************** getTableAttrs(TableInfo *tblinfo, int nu *** 4697,4702 **** --- 4749,4755 ---- int i_attlen; int i_attalign; int i_attislocal; + int i_attseclabel; PGresult *res; int ntups; bool hasdefaults; *************** getTableAttrs(TableInfo *tblinfo, int nu *** 4741,4752 **** "a.attstattarget, a.attstorage, t.typstorage, " "a.attnotnull, a.atthasdef, a.attisdropped, " "a.attlen, a.attalign, a.attislocal, " ! "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname " "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " "ON a.atttypid = t.oid " "WHERE a.attrelid = '%u'::pg_catalog.oid " "AND a.attnum > 0::pg_catalog.int2 " "ORDER BY a.attrelid, a.attnum", tbinfo->dobj.catId.oid); } else if (g_fout->remoteVersion >= 70100) --- 4794,4807 ---- "a.attstattarget, a.attstorage, t.typstorage, " "a.attnotnull, a.atthasdef, a.attisdropped, " "a.attlen, a.attalign, a.attislocal, " ! "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " ! "%s as security_context " "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " "ON a.atttypid = t.oid " "WHERE a.attrelid = '%u'::pg_catalog.oid " "AND a.attnum > 0::pg_catalog.int2 " "ORDER BY a.attrelid, a.attnum", + security_context ? "a.security_context" : "NULL", tbinfo->dobj.catId.oid); } else if (g_fout->remoteVersion >= 70100) *************** getTableAttrs(TableInfo *tblinfo, int nu *** 4761,4767 **** "t.typstorage, a.attnotnull, a.atthasdef, " "false AS attisdropped, a.attlen, " "a.attalign, false AS attislocal, " ! "format_type(t.oid,a.atttypmod) AS atttypname " "FROM pg_attribute a LEFT JOIN pg_type t " "ON a.atttypid = t.oid " "WHERE a.attrelid = '%u'::oid " --- 4816,4823 ---- "t.typstorage, a.attnotnull, a.atthasdef, " "false AS attisdropped, a.attlen, " "a.attalign, false AS attislocal, " ! "format_type(t.oid,a.atttypmod) AS atttypname, " ! "NULL as security_context " "FROM pg_attribute a LEFT JOIN pg_type t " "ON a.atttypid = t.oid " "WHERE a.attrelid = '%u'::oid " *************** getTableAttrs(TableInfo *tblinfo, int nu *** 4778,4784 **** "attnotnull, atthasdef, false AS attisdropped, " "attlen, attalign, " "false AS attislocal, " ! "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname " "FROM pg_attribute a " "WHERE attrelid = '%u'::oid " "AND attnum > 0::int2 " --- 4834,4841 ---- "attnotnull, atthasdef, false AS attisdropped, " "attlen, attalign, " "false AS attislocal, " ! "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname, " ! "NULL as security_context " "FROM pg_attribute a " "WHERE attrelid = '%u'::oid " "AND attnum > 0::int2 " *************** getTableAttrs(TableInfo *tblinfo, int nu *** 4804,4809 **** --- 4861,4867 ---- i_attlen = PQfnumber(res, "attlen"); i_attalign = PQfnumber(res, "attalign"); i_attislocal = PQfnumber(res, "attislocal"); + i_attseclabel = PQfnumber(res, "security_context"); tbinfo->numatts = ntups; tbinfo->attnames = (char **) malloc(ntups * sizeof(char *)); *************** getTableAttrs(TableInfo *tblinfo, int nu *** 4816,4821 **** --- 4874,4880 ---- tbinfo->attlen = (int *) malloc(ntups * sizeof(int)); tbinfo->attalign = (char *) malloc(ntups * sizeof(char)); tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool)); + tbinfo->attseclabel = (char **) malloc(ntups * sizeof(char *)); tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool)); tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *)); tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool)); *************** getTableAttrs(TableInfo *tblinfo, int nu *** 4841,4846 **** --- 4900,4906 ---- tbinfo->attlen[j] = atoi(PQgetvalue(res, j, i_attlen)); tbinfo->attalign[j] = *(PQgetvalue(res, j, i_attalign)); tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't'); + tbinfo->attseclabel[j] = strdup(PQgetvalue(res, j, i_attseclabel)); tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't'); tbinfo->attrdefs[j] = NULL; /* fix below */ if (PQgetvalue(res, j, i_atthasdef)[0] == 't') *************** dumpFunc(Archive *fout, FuncInfo *finfo) *** 7091,7096 **** --- 7151,7157 ---- char *proconfig; char *procost; char *prorows; + char *proseclabel; char *lanname; char *rettypename; int nallargs; *************** dumpFunc(Archive *fout, FuncInfo *finfo) *** 7127,7135 **** "pg_catalog.pg_get_function_result(oid) AS funcresult, " "proiswindow, provolatile, proisstrict, prosecdef, " "proconfig, procost, prorows, " ! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); } else if (g_fout->remoteVersion >= 80300) --- 7188,7198 ---- "pg_catalog.pg_get_function_result(oid) AS funcresult, " "proiswindow, provolatile, proisstrict, prosecdef, " "proconfig, procost, prorows, " ! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " ! "%s as security_context " "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", + security_context ? "security_context" : "NULL", finfo->dobj.catId.oid); } else if (g_fout->remoteVersion >= 80300) *************** dumpFunc(Archive *fout, FuncInfo *finfo) *** 7140,7146 **** "false AS proiswindow, " "provolatile, proisstrict, prosecdef, " "proconfig, procost, prorows, " ! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); --- 7203,7210 ---- "false AS proiswindow, " "provolatile, proisstrict, prosecdef, " "proconfig, procost, prorows, " ! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " ! "NULL AS security_context " "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); *************** dumpFunc(Archive *fout, FuncInfo *finfo) *** 7153,7159 **** "false AS proiswindow, " "provolatile, proisstrict, prosecdef, " "null AS proconfig, 0 AS procost, 0 AS prorows, " ! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); --- 7217,7224 ---- "false AS proiswindow, " "provolatile, proisstrict, prosecdef, " "null AS proconfig, 0 AS procost, 0 AS prorows, " ! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " ! "NULL AS security_context " "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); *************** dumpFunc(Archive *fout, FuncInfo *finfo) *** 7168,7174 **** "false AS proiswindow, " "provolatile, proisstrict, prosecdef, " "null AS proconfig, 0 AS procost, 0 AS prorows, " ! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); --- 7233,7240 ---- "false AS proiswindow, " "provolatile, proisstrict, prosecdef, " "null AS proconfig, 0 AS procost, 0 AS prorows, " ! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " ! "NULL AS security_context " "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); *************** dumpFunc(Archive *fout, FuncInfo *finfo) *** 7183,7189 **** "false AS proiswindow, " "provolatile, proisstrict, prosecdef, " "null AS proconfig, 0 AS procost, 0 AS prorows, " ! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); --- 7249,7256 ---- "false AS proiswindow, " "provolatile, proisstrict, prosecdef, " "null AS proconfig, 0 AS procost, 0 AS prorows, " ! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " ! "NULL AS security_context " "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); *************** dumpFunc(Archive *fout, FuncInfo *finfo) *** 7200,7206 **** "proisstrict, " "false AS prosecdef, " "null AS proconfig, 0 AS procost, 0 AS prorows, " ! "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " "FROM pg_proc " "WHERE oid = '%u'::oid", finfo->dobj.catId.oid); --- 7267,7274 ---- "proisstrict, " "false AS prosecdef, " "null AS proconfig, 0 AS procost, 0 AS prorows, " ! "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname, " ! "NULL AS security_context " "FROM pg_proc " "WHERE oid = '%u'::oid", finfo->dobj.catId.oid); *************** dumpFunc(Archive *fout, FuncInfo *finfo) *** 7217,7223 **** "false AS proisstrict, " "false AS prosecdef, " "NULL AS proconfig, 0 AS procost, 0 AS prorows, " ! "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " "FROM pg_proc " "WHERE oid = '%u'::oid", finfo->dobj.catId.oid); --- 7285,7292 ---- "false AS proisstrict, " "false AS prosecdef, " "NULL AS proconfig, 0 AS procost, 0 AS prorows, " ! "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname, " ! "NULL AS security_context " "FROM pg_proc " "WHERE oid = '%u'::oid", finfo->dobj.catId.oid); *************** dumpFunc(Archive *fout, FuncInfo *finfo) *** 7261,7266 **** --- 7330,7336 ---- proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig")); procost = PQgetvalue(res, 0, PQfnumber(res, "procost")); prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows")); + proseclabel = PQgetvalue(res, 0, PQfnumber(res, "security_context")); lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname")); /* *************** dumpFunc(Archive *fout, FuncInfo *finfo) *** 7419,7424 **** --- 7489,7497 ---- if (prosecdef[0] == 't') appendPQExpBuffer(q, " SECURITY DEFINER"); + if (security_context > 0 && strlen(proseclabel) > 0) + appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'", proseclabel); + /* * COST and ROWS are emitted only if present and not default, so as not to * break backwards-compatibility of the dump without need. Keep this code *************** dumpTableSchema(Archive *fout, TableInfo *** 9877,9882 **** --- 9950,9966 ---- if (tbinfo->notnull[j] && (!tbinfo->inhNotNull[j] || binary_upgrade)) appendPQExpBuffer(q, " NOT NULL"); + + /* + * Security label -- if SE-PostgreSQL enabled + */ + if (security_context > 0 && + strlen(tbinfo->attseclabel[j]) > 0 && + strcmp(tbinfo->relseclabel, tbinfo->attseclabel[j]) != 0) + appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'", + tbinfo->attseclabel[j]); + + actual_atts++; } } *************** dumpTableSchema(Archive *fout, TableInfo *** 9939,9944 **** --- 10023,10031 ---- appendPQExpBuffer(q, ")"); } + if (security_context > 0 && strlen(tbinfo->relseclabel) > 0) + appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'", tbinfo->relseclabel); + appendPQExpBuffer(q, ";\n"); /* *************** fmtCopyColumnList(const TableInfo *ti) *** 11508,11513 **** --- 11595,11607 ---- appendPQExpBuffer(q, "("); needComma = false; + + if (security_context > 0) + { + appendPQExpBuffer(q, "security_context"); + needComma = true; + } + for (i = 0; i < numatts; i++) { if (attisdropped[i]) diff -Nrpc base/src/bin/pg_dump/pg_dump.h sepgsql/src/bin/pg_dump/pg_dump.h *** base/src/bin/pg_dump/pg_dump.h Thu Jun 18 10:20:52 2009 --- sepgsql/src/bin/pg_dump/pg_dump.h Wed Jul 15 20:03:59 2009 *************** typedef struct _tableInfo *** 228,233 **** --- 228,234 ---- bool hasoids; /* does it have OIDs? */ uint32 frozenxid; /* for restore frozen xid */ int ncheck; /* # of CHECK expressions */ + char *relseclabel; /* security labels of relation */ /* these two are set only if table is a sequence owned by a column: */ Oid owning_tab; /* OID of table owning sequence */ int owning_col; /* attr # of column owning sequence */ *************** typedef struct _tableInfo *** 249,254 **** --- 250,256 ---- int *attlen; /* attribute length, used by binary_upgrade */ char *attalign; /* attribute align, used by binary_upgrade */ bool *attislocal; /* true if attr has local definition */ + char **attseclabel; /* security labels of attributes */ /* * Note: we need to store per-attribute notnull, default, and constraint diff -Nrpc base/src/bin/pg_dump/pg_dumpall.c sepgsql/src/bin/pg_dump/pg_dumpall.c *** base/src/bin/pg_dump/pg_dumpall.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/bin/pg_dump/pg_dumpall.c Wed Jul 15 20:03:59 2009 *************** static int no_tablespaces = 0; *** 69,74 **** --- 69,77 ---- static int use_setsessauth = 0; static int server_version; + static int security_label = 0; + static int security_acl = 0; + static FILE *OPF; static char *filename = NULL; *************** main(int argc, char *argv[]) *** 130,135 **** --- 133,140 ---- {"no-tablespaces", no_argument, &no_tablespaces, 1}, {"role", required_argument, NULL, 3}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, + {"security-label", no_argument, &security_label, 1}, + {"security-acl", no_argument, &security_acl, 1}, {NULL, 0, NULL, 0} }; *************** main(int argc, char *argv[]) *** 283,288 **** --- 288,297 ---- no_tablespaces = 1; else if (strcmp(optarg, "use-set-session-authorization") == 0) use_setsessauth = 1; + else if (strcmp(optarg, "security-label") == 0) + security_label = 1; + else if (strcmp(optarg, "security-acl") == 0) + security_acl = 1; else { fprintf(stderr, *************** main(int argc, char *argv[]) *** 328,333 **** --- 337,346 ---- appendPQExpBuffer(pgdumpopts, " --no-tablespaces"); if (use_setsessauth) appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization"); + if (security_label) + appendPQExpBuffer(pgdumpopts, " --security-label"); + if (security_acl) + appendPQExpBuffer(pgdumpopts, " --security-acl"); if (optind < argc) { *************** main(int argc, char *argv[]) *** 403,408 **** --- 416,434 ---- } } + if (security_label > 0) + { + PGresult *res + = PQexec(conn, "SHOW sepostgresql"); + if (PQresultStatus(res) != PGRES_TUPLES_OK || + PQntuples(res) != 1 || + strcmp(PQgetvalue(res, 0, 0), "on") != 0) + { + fprintf(stderr, "SE-PostgreSQL is not available now."); + exit(1); + } + } + /* * Open the output file if required, otherwise use stdout */ *************** dumpCreateDB(PGconn *conn) *** 1130,1184 **** /* Now collect all the information about databases to dump */ if (server_version >= 80400) ! res = executeQuery(conn, ! "SELECT datname, " "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "datcollate, datctype, datfrozenxid, " "datistemplate, datacl, datconnlimit, " ! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " ! "WHERE datallowconn ORDER BY 1"); else if (server_version >= 80100) ! res = executeQuery(conn, ! "SELECT datname, " "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, datfrozenxid, " "datistemplate, datacl, datconnlimit, " ! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " "WHERE datallowconn ORDER BY 1"); else if (server_version >= 80000) ! res = executeQuery(conn, ! "SELECT datname, " "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, datfrozenxid, " "datistemplate, datacl, -1 as datconnlimit, " ! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " "WHERE datallowconn ORDER BY 1"); else if (server_version >= 70300) ! res = executeQuery(conn, ! "SELECT datname, " "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, datfrozenxid, " "datistemplate, datacl, -1 as datconnlimit, " ! "'pg_default' AS dattablespace " "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " "WHERE datallowconn ORDER BY 1"); else if (server_version >= 70100) ! res = executeQuery(conn, ! "SELECT datname, " "coalesce(" "(select usename from pg_shadow where usesysid=datdba), " "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " "datistemplate, '' as datacl, -1 as datconnlimit, " ! "'pg_default' AS dattablespace " "FROM pg_database d " "WHERE datallowconn ORDER BY 1"); else --- 1156,1211 ---- /* Now collect all the information about databases to dump */ if (server_version >= 80400) ! appendPQExpBuffer(buf, "SELECT datname, " "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "datcollate, datctype, datfrozenxid, " "datistemplate, datacl, datconnlimit, " ! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, " ! "%s AS security_label " "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " ! "WHERE datallowconn ORDER BY 1", ! security_label ? "sepgsql_raw_to_trans(datselabel)" : "null::text"); else if (server_version >= 80100) ! appendPQExpBuffer(buf, "SELECT datname, " "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, datfrozenxid, " "datistemplate, datacl, datconnlimit, " ! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, " ! "null::text " "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " "WHERE datallowconn ORDER BY 1"); else if (server_version >= 80000) ! appendPQExpBuffer(buf, "SELECT datname, " "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, datfrozenxid, " "datistemplate, datacl, -1 as datconnlimit, " ! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, " ! "null::text " "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " "WHERE datallowconn ORDER BY 1"); else if (server_version >= 70300) ! appendPQExpBuffer(buf, "SELECT datname, " "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, datfrozenxid, " "datistemplate, datacl, -1 as datconnlimit, " ! "'pg_default' AS dattablespace, " ! "null::text " "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " "WHERE datallowconn ORDER BY 1"); else if (server_version >= 70100) ! appendPQExpBuffer(buf, "SELECT datname, " "coalesce(" "(select usename from pg_shadow where usesysid=datdba), " "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " "datistemplate, '' as datacl, -1 as datconnlimit, " ! "'pg_default' AS dattablespace, " ! "null::text " "FROM pg_database d " "WHERE datallowconn ORDER BY 1"); else *************** dumpCreateDB(PGconn *conn) *** 1187,1204 **** * Note: 7.0 fails to cope with sub-select in COALESCE, so just deal * with getting a NULL by not printing any OWNER clause. */ ! res = executeQuery(conn, ! "SELECT datname, " "(select usename from pg_shadow where usesysid=datdba), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " "'f' as datistemplate, " "'' as datacl, -1 as datconnlimit, " ! "'pg_default' AS dattablespace " "FROM pg_database d " "ORDER BY 1"); } for (i = 0; i < PQntuples(res); i++) { char *dbname = PQgetvalue(res, i, 0); --- 1214,1233 ---- * Note: 7.0 fails to cope with sub-select in COALESCE, so just deal * with getting a NULL by not printing any OWNER clause. */ ! appendPQExpBuffer(buf, "SELECT datname, " "(select usename from pg_shadow where usesysid=datdba), " "pg_encoding_to_char(d.encoding), " "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " "'f' as datistemplate, " "'' as datacl, -1 as datconnlimit, " ! "'pg_default' AS dattablespace, " ! "null::text " "FROM pg_database d " "ORDER BY 1"); } + res = PQexec(conn, buf->data); + for (i = 0; i < PQntuples(res); i++) { char *dbname = PQgetvalue(res, i, 0); *************** dumpCreateDB(PGconn *conn) *** 1211,1216 **** --- 1240,1246 ---- char *dbacl = PQgetvalue(res, i, 7); char *dbconnlimit = PQgetvalue(res, i, 8); char *dbtablespace = PQgetvalue(res, i, 9); + char *dbseclabel = PQgetvalue(res, i, 9); char *fdbname; fdbname = strdup(fmtId(dbname)); *************** dumpCreateDB(PGconn *conn) *** 1266,1271 **** --- 1296,1305 ---- appendPQExpBuffer(buf, " CONNECTION LIMIT = %s", dbconnlimit); + if (security_label > 0 && strlen(dbseclabel) > 0) + appendPQExpBuffer(buf, " SECURITY_LABEL = '%s'", + dbseclabel); + appendPQExpBuffer(buf, ";\n"); if (strcmp(dbistemplate, "t") == 0) diff -Nrpc base/src/include/access/htup.h sepgsql/src/include/access/htup.h *** base/src/include/access/htup.h Thu Jun 18 10:20:52 2009 --- sepgsql/src/include/access/htup.h Tue Sep 8 23:55:48 2009 *************** typedef HeapTupleHeaderData *HeapTupleHe *** 163,169 **** #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_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 */ --- 163,169 ---- #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 */ ! #define HEAP_HASSECID 0x0010 /* has an security-id 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 */ *************** do { \ *** 290,295 **** --- 290,298 ---- (tup)->t_choice.t_datum.datum_typmod = (typmod) \ ) + #define HeapTupleHeaderHasOid(tup) \ + ((tup)->t_infomask & HEAP_HASOID) + #define HeapTupleHeaderGetOid(tup) \ ( \ ((tup)->t_infomask & HEAP_HASOID) ? \ *************** do { \ *** 349,354 **** --- 352,376 ---- (tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \ ) + #define HeapTupleHeaderHasSecid(tup) \ + ((tup)->t_infomask & HEAP_HASSECID) + + #define HeapTupleHeaderGetSecid(tup) \ + ( \ + HeapTupleHeaderHasSecid(tup) \ + ? (*(Oid *)((char *)(tup) + (tup)->t_hoff \ + - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ + - sizeof(Oid))) \ + : InvalidOid \ + ) + + #define HeapTupleHeaderSetSecid(tup, secid) \ + do { \ + Assert(HeapTupleHeaderHasSecid(tup)); \ + *((Oid *)((char *)(tup) + (tup)->t_hoff \ + - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ + - sizeof(Oid))) = (secid); \ + } while(0) /* * BITMAPLEN(NATTS) - *************** typedef HeapTupleData *HeapTuple; *** 549,554 **** --- 571,584 ---- #define HeapTupleSetOid(tuple, oid) \ HeapTupleHeaderSetOid((tuple)->t_data, (oid)) + #define HeapTupleHasSecid(tuple) \ + HeapTupleHeaderHasSecid((tuple)->t_data) + + #define HeapTupleGetSecid(tuple) \ + HeapTupleHeaderGetSecid((tuple)->t_data) + + #define HeapTupleSetSecid(tuple, secid) \ + HeapTupleHeaderSetSecid((tuple)->t_data, (secid)) /* * WAL record definitions for heapam.c's WAL operations diff -Nrpc base/src/include/access/sysattr.h sepgsql/src/include/access/sysattr.h *** base/src/include/access/sysattr.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/access/sysattr.h Wed Sep 9 16:47:01 2009 *************** *** 25,31 **** #define MaxTransactionIdAttributeNumber (-5) #define MaxCommandIdAttributeNumber (-6) #define TableOidAttributeNumber (-7) ! #define FirstLowInvalidHeapAttributeNumber (-8) #endif /* SYSATTR_H */ --- 25,43 ---- #define MaxTransactionIdAttributeNumber (-5) #define MaxCommandIdAttributeNumber (-6) #define TableOidAttributeNumber (-7) ! #define SecurityAttributeNumber (-8) ! #define FirstLowInvalidHeapAttributeNumber (-9) + /* + * Attribute names for the system-defined attributes + */ + #define SelfItemPointerAttributeName "ctid" + #define ObjectIdAttributeName "oid" + #define MinTransactionIdAttributeName "xmin" + #define MinCommandIdAttributeName "cmin" + #define MaxTransactionIdAttributeName "xmax" + #define MaxCommandIdAttributeName "cmax" + #define TableOidAttributeName "tableoid" + #define SecurityAttributeName "security_context" #endif /* SYSATTR_H */ diff -Nrpc base/src/include/access/tupdesc.h sepgsql/src/include/access/tupdesc.h *** base/src/include/access/tupdesc.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/access/tupdesc.h Wed Sep 9 13:14:37 2009 *************** typedef struct tupleDesc *** 75,80 **** --- 75,81 ---- Oid tdtypeid; /* composite type ID for tuple type */ int32 tdtypmod; /* typmod for tuple type */ bool tdhasoid; /* tuple has oid attribute in its header */ + bool tdhassecid; /* tuple has secid attribute in its header */ int tdrefcount; /* reference count, or -1 if not counting */ } *TupleDesc; diff -Nrpc base/src/include/bootstrap/bootstrap.h sepgsql/src/include/bootstrap/bootstrap.h *** base/src/include/bootstrap/bootstrap.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/bootstrap/bootstrap.h Tue Dec 8 14:04:25 2009 *************** typedef enum *** 70,76 **** BootstrapProcess, StartupProcess, BgWriterProcess, ! WalWriterProcess } AuxProcType; #endif /* BOOTSTRAP_H */ --- 70,77 ---- BootstrapProcess, StartupProcess, BgWriterProcess, ! WalWriterProcess, ! SelinuxReceiverProcess, } AuxProcType; #endif /* BOOTSTRAP_H */ diff -Nrpc base/src/include/catalog/dependency.h sepgsql/src/include/catalog/dependency.h *** base/src/include/catalog/dependency.h Thu Jun 18 10:20:52 2009 --- sepgsql/src/include/catalog/dependency.h Thu Jul 16 17:22:29 2009 *************** typedef enum ObjectClass *** 155,160 **** --- 155,163 ---- extern void performDeletion(const ObjectAddress *object, DropBehavior behavior); + extern void performDeletionNoPerms(const ObjectAddress *object, + DropBehavior behavior); + extern void performMultipleDeletions(const ObjectAddresses *objects, DropBehavior behavior); diff -Nrpc base/src/include/catalog/heap.h sepgsql/src/include/catalog/heap.h *** base/src/include/catalog/heap.h Thu Jun 18 10:20:52 2009 --- sepgsql/src/include/catalog/heap.h Wed Jul 15 19:38:52 2009 *************** extern Oid heap_create_with_catalog(cons *** 56,62 **** int oidinhcount, OnCommitAction oncommit, Datum reloptions, ! bool allow_system_table_mods); extern void heap_drop_with_catalog(Oid relid); --- 56,63 ---- int oidinhcount, OnCommitAction oncommit, Datum reloptions, ! bool allow_system_table_mods, ! Oid *secLabels); extern void heap_drop_with_catalog(Oid relid); *************** extern List *heap_truncate_find_FKs(List *** 68,79 **** extern void InsertPgAttributeTuple(Relation pg_attribute_rel, Form_pg_attribute new_attribute, ! CatalogIndexState indstate); extern void InsertPgClassTuple(Relation pg_class_desc, Relation new_rel_desc, Oid new_rel_oid, ! Datum reloptions); extern List *AddRelationNewConstraints(Relation rel, List *newColDefaults, --- 69,82 ---- extern void InsertPgAttributeTuple(Relation pg_attribute_rel, Form_pg_attribute new_attribute, ! CatalogIndexState indstate, ! Oid new_att_secid); extern void InsertPgClassTuple(Relation pg_class_desc, Relation new_rel_desc, Oid new_rel_oid, ! Datum reloptions, ! Oid new_rel_secid); extern List *AddRelationNewConstraints(Relation rel, List *newColDefaults, *************** extern Form_pg_attribute SystemAttribute *** 103,108 **** --- 106,113 ---- 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 -Nrpc base/src/include/catalog/indexing.h sepgsql/src/include/catalog/indexing.h *** base/src/include/catalog/indexing.h Thu Jun 18 10:20:52 2009 --- sepgsql/src/include/catalog/indexing.h Wed Jul 15 19:30:50 2009 *************** DECLARE_UNIQUE_INDEX(pg_type_oid_index, *** 249,254 **** --- 249,259 ---- 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_secid_index, 3401, on pg_security using btree(secid oid_ops, datid oid_ops, relid oid_ops)); + #define SecuritySecidIndexId 3401 + DECLARE_UNIQUE_INDEX(pg_security_secattr_index, 3402, on pg_security using btree(datid oid_ops, relid oid_ops, seckind char_ops, secattr text_ops)); + #define SecuritySecattrIndexId 3402 + DECLARE_UNIQUE_INDEX(pg_foreign_data_wrapper_oid_index, 112, on pg_foreign_data_wrapper using btree(oid oid_ops)); #define ForeignDataWrapperOidIndexId 112 diff -Nrpc base/src/include/catalog/pg_attribute.h sepgsql/src/include/catalog/pg_attribute.h *** base/src/include/catalog/pg_attribute.h Thu Jun 18 10:20:52 2009 --- sepgsql/src/include/catalog/pg_attribute.h Thu Sep 10 15:29:52 2009 *************** DATA(insert ( 1247 cmin 29 0 4 -4 0 *** 276,281 **** --- 276,282 ---- DATA(insert ( 1247 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); DATA(insert ( 1247 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); DATA(insert ( 1247 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1247 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); /* ---------------- * pg_proc *************** DATA(insert ( 1255 cmin 29 0 4 -4 0 *** 340,345 **** --- 341,347 ---- DATA(insert ( 1255 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); DATA(insert ( 1255 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); DATA(insert ( 1255 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1255 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); /* ---------------- * pg_attribute *************** DATA(insert ( 1249 cmin 29 0 4 -4 0 *** 390,395 **** --- 392,398 ---- DATA(insert ( 1249 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); DATA(insert ( 1249 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1249 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); /* ---------------- * pg_class *************** DATA(insert ( 1259 cmin 29 0 4 -4 0 *** 454,459 **** --- 457,463 ---- DATA(insert ( 1259 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); DATA(insert ( 1259 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1259 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); /* ---------------- * pg_index diff -Nrpc base/src/include/catalog/pg_conversion_fn.h sepgsql/src/include/catalog/pg_conversion_fn.h *** base/src/include/catalog/pg_conversion_fn.h Thu Jun 18 10:20:52 2009 --- sepgsql/src/include/catalog/pg_conversion_fn.h Thu Sep 17 22:10:19 2009 *************** *** 17,23 **** extern Oid ConversionCreate(const char *conname, Oid connamespace, Oid conowner, int32 conforencoding, int32 contoencoding, ! Oid conproc, bool def); extern void RemoveConversionById(Oid conversionOid); extern Oid FindConversion(const char *conname, Oid connamespace); extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding); --- 17,23 ---- extern Oid ConversionCreate(const char *conname, Oid connamespace, Oid conowner, int32 conforencoding, int32 contoencoding, ! Oid conproc, Oid consecid, bool def); extern void RemoveConversionById(Oid conversionOid); extern Oid FindConversion(const char *conname, Oid connamespace); extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding); diff -Nrpc base/src/include/catalog/pg_namespace.h sepgsql/src/include/catalog/pg_namespace.h *** base/src/include/catalog/pg_namespace.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/catalog/pg_namespace.h Wed Jul 15 19:35:52 2009 *************** DESCR("standard public schema"); *** 77,82 **** /* * prototypes for functions in pg_namespace.c */ ! extern Oid NamespaceCreate(const char *nspName, Oid ownerId); #endif /* PG_NAMESPACE_H */ --- 77,82 ---- /* * prototypes for functions in pg_namespace.c */ ! extern Oid NamespaceCreate(const char *nspName, Oid ownerId, Oid nspsecid); #endif /* PG_NAMESPACE_H */ diff -Nrpc base/src/include/catalog/pg_proc.h sepgsql/src/include/catalog/pg_proc.h *** base/src/include/catalog/pg_proc.h Thu Jun 18 10:20:52 2009 --- sepgsql/src/include/catalog/pg_proc.h Wed Jul 15 20:03:59 2009 *************** DATA(insert OID = 958 ( lo_tell PGN *** 1226,1231 **** --- 1226,1235 ---- DESCR("large object position"); DATA(insert OID = 1004 ( lo_truncate PGNSP PGUID 12 1 0 0 f f f t f v 2 0 23 "23 23" _null_ _null_ _null_ _null_ lo_truncate _null_ _null_ _null_ )); DESCR("truncate large object"); + DATA(insert OID = 1029 ( lo_get_security PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "26" _null_ _null_ _null_ _null_ lo_get_security _null_ _null_ _null_ )); + DESCR("get security label of large object"); + DATA(insert OID = 1030 ( lo_set_security PGNSP PGUID 12 1 0 0 f f f t f v 2 0 16 "26 25" _null_ _null_ _null_ _null_ lo_set_security _null_ _null_ _null_ )); + DESCR("set security label of large object"); DATA(insert OID = 959 ( on_pl PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "600 628" _null_ _null_ _null_ _null_ on_pl _null_ _null_ _null_ )); DESCR("point on line?"); *************** DESCR("I/O"); *** 4335,4340 **** --- 4339,4361 ---- DATA(insert OID = 2963 ( uuid_hash PGNSP PGUID 12 1 0 0 f f f t f i 1 0 23 "2950" _null_ _null_ _null_ _null_ uuid_hash _null_ _null_ _null_ )); DESCR("hash"); + /* Security attribute reclaimer */ + DATA(insert OID = 3411 ( security_reclaim_label PGNSP PGUID 12 1 0 0 f f f t f v 0 0 23 "" _null_ _null_ _null_ _null_ security_reclaim_label _null_ _null_ _null_ )); + DATA(insert OID = 3413 ( security_reclaim_label PGNSP PGUID 12 1 0 0 f f f t f v 1 0 23 "2205" _null_ _null_ _null_ _null_ security_reclaim_table_label _null_ _null_ _null_ )); + DATA(insert OID = 3415 ( security_label_to_secid PGNSP PGUID 12 1 0 0 f f f t f v 1 0 26 "2249" _null_ _null_ _null_ _null_ security_label_to_secid _null_ _null_ _null_ )); + + /* SE-PostgreSQL related functions */ + DATA(insert OID = 3416 ( sepgsql_getcon PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ sepgsql_getcon _null_ _null_ _null_ )); + DATA(insert OID = 3417 ( sepgsql_server_getcon PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ sepgsql_server_getcon _null_ _null_ _null_ )); + DATA(insert OID = 3418 ( sepgsql_get_user PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_user _null_ _null_ _null_ )); + DATA(insert OID = 3419 ( sepgsql_set_user PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_user _null_ _null_ _null_ )); + DATA(insert OID = 3420 ( sepgsql_get_role PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_role _null_ _null_ _null_ )); + DATA(insert OID = 3421 ( sepgsql_set_role PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_role _null_ _null_ _null_ )); + DATA(insert OID = 3422 ( sepgsql_get_type PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_type _null_ _null_ _null_ )); + DATA(insert OID = 3423 ( sepgsql_set_type PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_type _null_ _null_ _null_ )); + DATA(insert OID = 3424 ( sepgsql_get_range PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_range _null_ _null_ _null_ )); + DATA(insert OID = 3425 ( sepgsql_set_range PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_range _null_ _null_ _null_ )); + /* enum related procs */ DATA(insert OID = 3504 ( anyenum_in PGNSP PGUID 12 1 0 0 f f f t f i 1 0 3500 "2275" _null_ _null_ _null_ _null_ anyenum_in _null_ _null_ _null_ )); DESCR("I/O"); diff -Nrpc base/src/include/catalog/pg_proc_fn.h sepgsql/src/include/catalog/pg_proc_fn.h *** base/src/include/catalog/pg_proc_fn.h Thu Jun 18 10:20:52 2009 --- sepgsql/src/include/catalog/pg_proc_fn.h Wed Jul 15 19:37:35 2009 *************** extern Oid ProcedureCreate(const char *p *** 37,43 **** List *parameterDefaults, Datum proconfig, float4 procost, ! float4 prorows); extern bool function_parse_error_transpose(const char *prosrc); --- 37,44 ---- List *parameterDefaults, Datum proconfig, float4 procost, ! float4 prorows, ! Node *proseclabel); extern bool function_parse_error_transpose(const char *prosrc); diff -Nrpc base/src/include/catalog/pg_security.h sepgsql/src/include/catalog/pg_security.h *** base/src/include/catalog/pg_security.h Thu Jan 1 09:00:00 1970 --- sepgsql/src/include/catalog/pg_security.h Thu Sep 10 15:18:03 2009 *************** *** 0 **** --- 1,101 ---- + /* + * src/include/catalog/pg_security.h + * Definition of the security label relation (pg_security) + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ + #ifndef PG_SECURITY_H + #define PG_SECURITY_H + + #include "catalog/genbki.h" + + #include "access/htup.h" + #include "nodes/parsenodes.h" + #include "utils/acl.h" + #include "utils/relcache.h" + + #define SecurityRelationId 3400 + + CATALOG(pg_security,3400) BKI_SHARED_RELATION BKI_WITHOUT_OIDS + { + /* Identifier of the security attribute */ + Oid secid; + + /* OID of the database which referes the entry */ + Oid datid; + + /* OID of the table which refers the entry */ + Oid relid; + + /* See the SECKIND_SECURITY_* definition */ + char seckind; + + /* Text representation of security attribute */ + text secattr; + } 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_security + */ + #define Natts_pg_security 5 + #define Anum_pg_security_secid 1 + #define Anum_pg_security_datid 2 + #define Anum_pg_security_relid 3 + #define Anum_pg_security_seckind 4 + #define Anum_pg_security_secattr 5 + + /* + * Compiler constants for pg_security.seckind + */ + #define SECKIND_SECURITY_LABEL 'l' + + /* + * Functions to translate between security label and identifier + */ + extern void + securityPostBootstrapingMode(void); + + extern void + securityOnCreateDatabase(Oid src_datid, Oid dst_datid); + + extern void + securityOnDropDatabase(Oid datid); + + extern bool + securityTupleDescHasSecid(Oid relid, char relkind); + + 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 + securitySysattSecLabelOut(Oid relid, HeapTuple tuple); + + extern void + securityReclaimOnDropTable(Oid relid); + + extern Datum + security_reclaim_label(PG_FUNCTION_ARGS); + + extern Datum + security_reclaim_table_label(PG_FUNCTION_ARGS); + + extern Datum + security_label_to_secid(PG_FUNCTION_ARGS); + + #endif /* PG_SECURITY_H */ diff -Nrpc base/src/include/catalog/toasting.h sepgsql/src/include/catalog/toasting.h *** base/src/include/catalog/toasting.h Thu Jun 18 10:20:52 2009 --- sepgsql/src/include/catalog/toasting.h Wed Jul 15 19:30:50 2009 *************** DECLARE_TOAST(pg_database, 2844, 2845); *** 58,62 **** --- 58,65 ---- DECLARE_TOAST(pg_shdescription, 2846, 2847); #define PgShdescriptionToastTable 2846 #define PgShdescriptionToastIndex 2847 + DECLARE_TOAST(pg_security, 3403, 3404); + #define PgSecurityToastTable 3403 + #define PgSecurityToastIndex 3404 #endif /* TOASTING_H */ diff -Nrpc base/src/include/commands/alter.h sepgsql/src/include/commands/alter.h *** base/src/include/commands/alter.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/commands/alter.h Wed Jul 15 19:37:35 2009 *************** *** 19,23 **** --- 19,24 ---- extern void ExecRenameStmt(RenameStmt *stmt); extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt); extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt); + extern void ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt); #endif /* ALTER_H */ diff -Nrpc base/src/include/commands/dbcommands.h sepgsql/src/include/commands/dbcommands.h *** base/src/include/commands/dbcommands.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/commands/dbcommands.h Wed Jul 15 19:37:35 2009 *************** extern void RenameDatabase(const char *o *** 58,63 **** --- 58,64 ---- extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel); 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 -Nrpc base/src/include/commands/defrem.h sepgsql/src/include/commands/defrem.h *** base/src/include/commands/defrem.h Thu Apr 9 00:13:21 2009 --- sepgsql/src/include/commands/defrem.h Wed Jul 15 19:37:35 2009 *************** extern void SetFunctionArgType(Oid funcO *** 53,58 **** --- 53,59 ---- 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 -Nrpc base/src/include/commands/schemacmds.h sepgsql/src/include/commands/schemacmds.h *** base/src/include/commands/schemacmds.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/commands/schemacmds.h Wed Jul 15 19:37:35 2009 *************** extern void RemoveSchemaById(Oid schemaO *** 26,30 **** --- 26,31 ---- 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 -Nrpc base/src/include/commands/tablecmds.h sepgsql/src/include/commands/tablecmds.h *** base/src/include/commands/tablecmds.h Thu Jun 18 10:20:52 2009 --- sepgsql/src/include/commands/tablecmds.h Wed Jul 15 19:37:35 2009 *************** extern void AlterRelationNamespaceIntern *** 35,40 **** --- 35,43 ---- 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 -Nrpc base/src/include/executor/executor.h sepgsql/src/include/executor/executor.h *** base/src/include/executor/executor.h Sun Sep 6 19:40:49 2009 --- sepgsql/src/include/executor/executor.h Wed Sep 9 13:14:37 2009 *************** extern TupleHashEntry FindTupleHashEntry *** 130,136 **** /* * prototypes from functions in execJunk.c */ ! extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot); extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, TupleDesc cleanTupType, --- 130,136 ---- /* * prototypes from functions in execJunk.c */ ! extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, bool hasseclabel, TupleTableSlot *slot); extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, TupleDesc cleanTupType, *************** extern void InitResultRelInfo(ResultRelI *** 163,168 **** --- 163,169 ---- bool doInstrument); extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); + extern bool ExecContextForcesSecids(PlanState *planstate, bool *hassecids); extern void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate); extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, *************** extern void ExecInitScanTupleSlot(EState *** 216,223 **** 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 ExecTypeFromExprList(List *exprList); extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); --- 217,224 ---- extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate); extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, TupleDesc tupType); ! 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 -Nrpc base/src/include/executor/tuptable.h sepgsql/src/include/executor/tuptable.h *** base/src/include/executor/tuptable.h Thu Jun 18 10:20:52 2009 --- sepgsql/src/include/executor/tuptable.h Wed Jul 15 19:38:52 2009 *************** typedef struct TupleTableSlot *** 127,132 **** --- 127,133 ---- 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 -Nrpc base/src/include/libpq/be-fsstubs.h sepgsql/src/include/libpq/be-fsstubs.h *** base/src/include/libpq/be-fsstubs.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/libpq/be-fsstubs.h Wed Jul 15 19:48:58 2009 *************** extern Datum lo_tell(PG_FUNCTION_ARGS); *** 37,42 **** --- 37,45 ---- 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 -Nrpc base/src/include/nodes/nodes.h sepgsql/src/include/nodes/nodes.h *** base/src/include/nodes/nodes.h Thu Jun 18 10:20:52 2009 --- sepgsql/src/include/nodes/nodes.h Wed Jul 15 19:37:35 2009 *************** typedef enum NodeTag *** 337,342 **** --- 337,343 ---- T_CreateUserMappingStmt, T_AlterUserMappingStmt, T_DropUserMappingStmt, + T_AlterSecLabelStmt, /* * TAGS FOR PARSE TREE NODES (parsenodes.h) diff -Nrpc base/src/include/nodes/parsenodes.h sepgsql/src/include/nodes/parsenodes.h *** base/src/include/nodes/parsenodes.h Fri Jun 19 13:40:37 2009 --- sepgsql/src/include/nodes/parsenodes.h Wed Jul 15 19:37:35 2009 *************** typedef struct ColumnDef *** 464,469 **** --- 464,470 ---- Node *raw_default; /* default value (untransformed parse tree) */ char *cooked_default; /* nodeToString representation */ List *constraints; /* other constraints on column */ + Node *secLabel; /* security label of column */ } ColumnDef; /* *************** typedef struct CreateSchemaStmt *** 1070,1075 **** --- 1071,1077 ---- 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; *************** typedef struct CreateStmt *** 1335,1340 **** --- 1337,1343 ---- 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; /* ---------- *************** typedef struct CreateSeqStmt *** 1639,1644 **** --- 1642,1648 ---- NodeTag type; RangeVar *sequence; /* the sequence to create */ List *options; + Node *secLabel; } CreateSeqStmt; typedef struct AlterSeqStmt *************** typedef struct AlterOwnerStmt *** 1993,1998 **** --- 1997,2016 ---- 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 -Nrpc base/src/include/nodes/plannodes.h sepgsql/src/include/nodes/plannodes.h *** base/src/include/nodes/plannodes.h Thu Jun 18 10:20:52 2009 --- sepgsql/src/include/nodes/plannodes.h Wed Jul 15 19:39:56 2009 *************** *** 16,21 **** --- 16,22 ---- #include "access/sdir.h" #include "nodes/bitmapset.h" + #include "nodes/parsenodes.h" #include "nodes/primnodes.h" #include "storage/itemptr.h" *************** typedef struct Scan *** 239,244 **** --- 240,251 ---- { 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 -Nrpc base/src/include/nodes/relation.h sepgsql/src/include/nodes/relation.h *** base/src/include/nodes/relation.h Thu Jun 18 10:20:52 2009 --- sepgsql/src/include/nodes/relation.h Wed Jul 15 19:39:56 2009 *************** typedef struct RelOptInfo *** 383,388 **** --- 383,397 ---- * 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 -Nrpc base/src/include/pg_config.h.in sepgsql/src/include/pg_config.h.in *** base/src/include/pg_config.h.in Thu Apr 9 00:13:21 2009 --- sepgsql/src/include/pg_config.h.in Wed Jul 15 19:35:52 2009 *************** *** 391,396 **** --- 391,399 ---- /* 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 -Nrpc base/src/include/security/rowlevel.h sepgsql/src/include/security/rowlevel.h *** base/src/include/security/rowlevel.h Thu Jan 1 09:00:00 1970 --- sepgsql/src/include/security/rowlevel.h Thu Jul 16 17:22:29 2009 *************** *** 0 **** --- 1,44 ---- + /* + * 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 void + rowlvHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal); + + extern void + rowlvHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup); + + extern bool + rowlvCopyToTuple(Relation rel, HeapTuple tuple); + + #endif /* ROWLEVEL_H */ diff -Nrpc base/src/include/security/sepgsql.h sepgsql/src/include/security/sepgsql.h *** base/src/include/security/sepgsql.h Thu Jan 1 09:00:00 1970 --- sepgsql/src/include/security/sepgsql.h Tue Dec 8 14:04:25 2009 *************** *** 0 **** --- 1,777 ---- + /* + * src/include/security/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 "catalog/dependency.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_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_FILE__CREATE (1<<2) + #define SEPG_FILE__GETATTR (1<<3) + + #define SEPG_DIR__READ (SEPG_FILE__READ) + #define SEPG_DIR__WRITE (SEPG_FILE__WRITE) + #define SEPG_DIR__CREATE (SEPG_FILE__CREATE) + #define SEPG_DIR__GETATTR (SEPG_FILE__GETATTR) + + #define SEPG_LNK_FILE__READ (SEPG_FILE__READ) + #define SEPG_LNK_FILE__WRITE (SEPG_FILE__WRITE) + #define SEPG_LNK_FILE__CREATE (SEPG_FILE__CREATE) + #define SEPG_LNK_FILE__GETATTR (SEPG_FILE__GETATTR) + + #define SEPG_CHR_FILE__READ (SEPG_FILE__READ) + #define SEPG_CHR_FILE__WRITE (SEPG_FILE__WRITE) + #define SEPG_CHR_FILE__CREATE (SEPG_FILE__CREATE) + #define SEPG_CHR_FILE__GETATTR (SEPG_FILE__GETATTR) + + #define SEPG_BLK_FILE__READ (SEPG_FILE__READ) + #define SEPG_BLK_FILE__WRITE (SEPG_FILE__WRITE) + #define SEPG_BLK_FILE__CREATE (SEPG_FILE__CREATE) + #define SEPG_BLK_FILE__GETATTR (SEPG_FILE__GETATTR) + + #define SEPG_SOCK_FILE__READ (SEPG_FILE__READ) + #define SEPG_SOCK_FILE__WRITE (SEPG_FILE__WRITE) + #define SEPG_SOCK_FILE__CREATE (SEPG_FILE__CREATE) + #define SEPG_SOCK_FILE__GETATTR (SEPG_FILE__GETATTR) + + #define SEPG_FIFO_FILE__READ (SEPG_FILE__READ) + #define SEPG_FIFO_FILE__WRITE (SEPG_FILE__WRITE) + #define SEPG_FIFO_FILE__CREATE (SEPG_FILE__CREATE) + #define SEPG_FIFO_FILE__GETATTR (SEPG_FILE__GETATTR) + + #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_NAME (1<<7) + #define SEPG_DB_SCHEMA__REMOVE_NAME (1<<8) + + #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_PROCEDURE__UNTRUSTED (1<<9) + + #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) + + /* + * sepgsql_sid_t : alternative representation of security context + */ + typedef struct { + Oid relid; + Oid secid; + } sepgsql_sid_t; + + #define SidIsValid(sid) (OidIsValid((sid).relid) && OidIsValid((sid).secid)) + + /* + * avc.c : userspace access vector caches + */ + + /* Hook to record audit logs */ + typedef void (*sepgsqlAvcAuditHook_t)(bool denied, + const char *scontext, + const char *tcontext, + const char *tclass, + const char *permissions, + const char *audit_name); + extern PGDLLIMPORT sepgsqlAvcAuditHook_t sepgsqlAvcAuditHook; + + extern Size sepgsqlShmemSize(void); + extern void sepgsqlAvcInitialize(void); + + extern bool sepgsqlGetEnforce(void); + extern int sepgsqlSetEnforce(int new_mode); + extern void sepgsqlAvcReset(void); + extern void sepgsqlAvcSwitchClient(const char *scontext); + + extern bool + sepgsqlClientHasPerms(sepgsql_sid_t tsid, + uint16 tclass, uint32 required, + const char *audit_name, bool abort); + extern sepgsql_sid_t + sepgsqlClientCreateSecid(sepgsql_sid_t tsid, uint16 tclass, Oid nrelid); + + extern security_context_t + sepgsqlClientCreateLabel(sepgsql_sid_t tsid, uint16 tclass); + + extern bool + sepgsqlComputePerms(security_context_t scontext, + security_context_t tcontext, + uint16 tclass, uint32 required, + const char *audit_name, bool abort); + + extern security_context_t + sepgsqlComputeCreate(security_context_t scontext, + security_context_t tcontext, + uint16 tclass); + + extern void sepgsqlReceiverMain(void); + + /* + * bridge.c : new style security hooks + */ + + /* pg_attribute */ + extern Oid + sepgsql_attribute_create(Oid relOid, ColumnDef *cdef); + extern void + sepgsql_attribute_alter(Oid relOid, const char *attname); + extern void + sepgsql_attribute_drop(Oid relOid, AttrNumber attnum); + extern void + sepgsql_attribute_grant(Oid relOid, AttrNumber attnum); + extern Oid + sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, DefElem *newLabel); + + /* pg_cast */ + extern Oid + sepgsql_cast_create(Oid sourceTypOid, Oid targetTypOid, Oid funcOid); + extern void + sepgsql_cast_drop(Oid castOid); + + /* pg_class */ + extern Oid * + sepgsql_relation_create(const char *relName, + char relkind, + TupleDesc tupDesc, + Oid nspOid, + DefElem *relLabel, + List *colList, + bool createAs, + bool permission); + extern Oid * + sepgsql_relation_copy(Relation src); + extern void + sepgsql_relation_alter(Oid relOid, const char *newName, Oid newNsp); + extern void + sepgsql_relation_drop(Oid relOid); + extern void + sepgsql_relation_grant(Oid relOid); + extern Oid + sepgsql_relation_relabel(Oid relOid, DefElem *newLabel); + extern void + sepgsql_relation_get_transaction_id(Oid relOid); + extern void + sepgsql_relation_copy_definition(Oid relOid); + extern void + sepgsql_relation_truncate(Relation rel); + extern void + sepgsql_relation_references(Relation rel, int16 *attnums, int natts); + extern void + sepgsql_relation_lock(Oid relOid); + extern void + sepgsql_view_replace(Oid viewOid); + extern void + sepgsql_index_create(Oid relOid, Oid nspOid, bool check_rights); + extern void + sepgsql_sequence_get_value(Oid seqOid); + extern void + sepgsql_sequence_next_value(Oid seqOid); + extern void + sepgsql_sequence_set_value(Oid seqOid); + + /* pg_conversion */ + extern Oid + sepgsql_conversion_create(const char *convName, Oid nspOid, Oid procOid); + extern void + sepgsql_conversion_alter(Oid convOid, const char *newName); + extern void + sepgsql_conversion_drop(Oid convOid); + + /* pg_database */ + extern Oid + sepgsql_database_create(const char *datName, DefElem *newLabel); + extern void + sepgsql_database_alter(Oid datOid); + extern void + sepgsql_database_drop(Oid datOid); + extern Oid + sepgsql_database_relabel(Oid datOid, DefElem *newLabel); + extern void + sepgsql_database_grant(Oid datOid); + extern void + sepgsql_database_access(Oid datOid); + extern bool + sepgsql_database_superuser(Oid datOid); + extern void + sepgsql_database_load_module(Oid datOid, const char *filename); + + /* pg_foreign_data_wrapper */ + extern Oid + sepgsql_fdw_create(const char *fdwName, Oid fdwValidator); + extern void + sepgsql_fdw_alter(Oid fdwOid, Oid newValidator); + extern void + sepgsql_fdw_drop(Oid fdwOid); + extern void + sepgsql_fdw_grant(Oid fdwOid); + + /* pg_foreign_server */ + extern Oid + sepgsql_foreign_server_create(const char *fsrvName); + extern void + sepgsql_foreign_server_alter(Oid fsrvOid); + extern void + sepgsql_foreign_server_drop(Oid fsrvOid); + extern void + sepgsql_foreign_server_grant(Oid fsrvOid); + + /* pg_language */ + extern Oid + sepgsql_language_create(const char *langName, Oid handlerOid, Oid validatorOid); + extern void + sepgsql_language_alter(Oid langOid); + extern void + sepgsql_language_drop(Oid langOid); + extern void + sepgsql_language_grant(Oid langOid); + + /* pg_largeobject */ + extern Oid + sepgsql_largeobject_create(Oid loid, DefElem *newLabel); + extern void + sepgsql_largeobject_alter(Oid loid, Oid newOwner); + extern void + sepgsql_largeobject_drop(Oid loid, bool dacSkip); + extern void + sepgsql_largeobject_read(Oid loid); + extern void + sepgsql_largeobject_write(Oid loid); + extern void + sepgsql_largeobject_export(Oid loid, const char *filename); + extern Oid + sepgsql_largeobject_import(Oid loid, const char *filename, DefElem *newLabel); + + /* pg_namespace */ + extern Oid + sepgsql_schema_create(const char *nspName, bool isTemp, DefElem *newLabel); + extern void + sepgsql_schema_alter(Oid nspOid); + extern void + sepgsql_schema_drop(Oid nspOid); + extern Oid + sepgsql_schema_relabel(Oid nspOid, DefElem *newLabel); + extern void + sepgsql_schema_grant(Oid nspOid); + extern bool + sepgsql_schema_search(Oid nspOid, bool abort); + + /* pg_opclass */ + extern Oid + sepgsql_opclass_create(const char *opcName, Oid nspOid); + extern void + sepgsql_opclass_alter(Oid opcOid, const char *newName); + extern void + sepgsql_opclass_drop(Oid opcOid); + + /* pg_opfamily */ + extern Oid + sepgsql_opfamily_create(const char *opfName, Oid nspOid); + extern void + sepgsql_opfamily_alter(Oid opfOid, const char *newName); + extern void + sepgsql_opfamily_drop(Oid opfOid); + extern void + sepgsql_opfamily_add_operator(Oid opfOid, Oid operOid); + extern void + sepgsql_opfamily_add_procedure(Oid opfOid, Oid procOid); + + /* pg_operator */ + extern Oid + sepgsql_operator_create(const char *oprName, Oid oprOid, Oid nspOid, + Oid codeFn, Oid restFn, Oid joinFn); + extern void + sepgsql_operator_alter(Oid oprOid); + extern void + sepgsql_operator_drop(Oid oprOid); + + /* pg_proc */ + extern Oid + sepgsql_proc_create(const char *procName, HeapTuple oldTup, + Oid nspOid, Oid langOid, DefElem *newLabel); + extern void + sepgsql_proc_alter(Oid procOid, const char *newName, Oid newNsp); + extern void + sepgsql_proc_drop(Oid procOid); + extern Oid + sepgsql_proc_relabel(Oid procOid, DefElem *newLabel); + extern void + sepgsql_proc_grant(Oid procOid); + extern void + sepgsql_proc_execute(Oid procOid); + extern bool + sepgsql_proc_hint_inlined(HeapTuple protup); + extern void + sepgsql_proc_entrypoint(FmgrInfo *flinfo, HeapTuple protup); + + /* pg_rewrite */ + extern void + sepgsql_rule_create(Oid relOid, const char *ruleName); + extern void + sepgsql_rule_drop(Oid relOid, const char *ruleName); + + /* pg_trigger */ + extern void + sepgsql_trigger_create(Oid relOid, const char *trigName, Oid procOid); + extern void + sepgsql_trigger_alter(Oid relOid, const char *trigName); + extern void + sepgsql_trigger_drop(Oid relOid, const char *trigName); + + /* pg_ts_config */ + extern Oid + sepgsql_ts_config_create(const char *cfgName, Oid nspOid); + extern void + sepgsql_ts_config_alter(Oid cfgOid, const char *newName); + extern void + sepgsql_ts_config_drop(Oid cfgOid); + + /* pg_ts_dict */ + extern Oid + sepgsql_ts_dict_create(const char *dictName, Oid nspOid); + extern void + sepgsql_ts_dict_alter(Oid dictOid, const char *newName); + extern void + sepgsql_ts_dict_drop(Oid dictOid); + + /* pg_ts_parser */ + extern Oid + sepgsql_ts_parser_create(const char *prsName, Oid nspOid, + Oid startFn, Oid tokenFn, Oid sendFn, + Oid headlineFn, Oid lextypeFn); + extern void + sepgsql_ts_parser_alter(Oid prsOid, const char *newName); + extern void + sepgsql_ts_parser_drop(Oid prsOid); + + /* pg_ts_templace */ + extern Oid + sepgsql_ts_template_create(const char *tmplName, Oid nspOid, + Oid initFn, Oid lexizeFn); + extern void + sepgsql_ts_template_alter(Oid tmplOid, const char *newName); + extern void + sepgsql_ts_template_drop(Oid tmplOid); + + /* pg_type */ + extern Oid + sepgsql_type_create(const char *typName, HeapTuple oldTup, Oid nspOid, + Oid inputProc, Oid outputProc, Oid recvProc, Oid sendProc, + Oid modinProc, Oid modoutProc, Oid analyzeProc); + extern void + sepgsql_type_alter(Oid typOid, const char *newName, Oid newNsp); + extern void + sepgsql_type_drop(Oid typOid); + + /* misc objects */ + extern void + sepgsql_sysobj_drop(const ObjectAddress *object); + + /* filesystem objects */ + void + sepgsql_file_stat(const char *filename); + void + sepgsql_file_read(const char *filename); + void + sepgsql_file_write(const char *filename); + + /* + * 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 void + sepgsqlHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal); + + extern void + sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup); + + /* + * 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 : routines to check certain permissions + */ + 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, const char *filename); + extern void + sepgsqlCheckBlobImport(LargeObjectDesc *lobj, const char *filename); + extern void + sepgsqlCheckBlobRelabel(HeapTuple oldtup, HeapTuple newtup); + + /* + * label.c : security label management + */ + extern bool + sepgsqlTupleDescHasSecid(Oid relid, char relkind); + + extern void sepgsqlSetDefaultSecid(Relation rel, HeapTuple tuple); + extern sepgsql_sid_t sepgsqlGetDefaultDatabaseSecid(void); + extern sepgsql_sid_t sepgsqlGetDefaultSchemaSecid(Oid database_oid); + extern sepgsql_sid_t sepgsqlGetDefaultSchemaTempSecid(Oid database_oid); + extern sepgsql_sid_t sepgsqlGetDefaultTableSecid(Oid namespace_oid); + extern sepgsql_sid_t sepgsqlGetDefaultSequenceSecid(Oid namespace_oid); + extern sepgsql_sid_t sepgsqlGetDefaultProcedureSecid(Oid namespace_oid); + extern sepgsql_sid_t sepgsqlGetDefaultColumnSecid(Oid table_oid); + extern sepgsql_sid_t sepgsqlGetDefaultTupleSecid(Oid table_oid); + extern sepgsql_sid_t sepgsqlGetDefaultBlobSecid(Oid database_oid); + + extern Oid *sepgsqlCreateTableColumns(CreateStmt *stmt, + const char *relname, Oid namespace_oid, + TupleDesc tupdesc, char relkind); + extern Oid *sepgsqlCopyTableColumns(Relation source); + + extern sepgsql_sid_t + sepgsqlGetTupleSecid(Oid tableOid, HeapTuple tuple, uint16 *tclass); + extern sepgsql_sid_t + sepgsqlGetSysobjSecid(Oid tableOid, Oid objectId, int32 objsubId, uint16 *tclass); + + extern char *sepgsqlTransSecLabelIn(char *seclabel); + extern char *sepgsqlTransSecLabelOut(char *seclabel); + extern char *sepgsqlRawSecLabelIn(char *seclabel); + extern char *sepgsqlRawSecLabelOut(char *seclabel); + extern char *sepgsqlSysattSecLabelOut(Oid relid, HeapTuple tuple); + + /* + * perms.c : SELinux permission related stuff + */ + extern uint16 sepgsqlFileObjectClass(int fdesc); + + extern uint16 sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple); + + extern security_class_t sepgsqlTransToExternalClass(uint16 tclass_in); + + extern void sepgsqlTransToInternalPerms(security_class_t tclass_ex, + struct av_decision *avd); + extern const char *sepgsqlGetClassString(uint16 tclass); + extern const char *sepgsqlGetPermString(uint16 tclass, uint32 permission); + + #else /* HAVE_SELINUX */ + + /* avc.c */ + #define sepgsqlShmemSize() (0) + + /* checker.c */ + #define sepgsqlCheckRTEPerms(a) do {} while(0) + #define sepgsqlCheckCopyTable(a,b,c) do {} while(0) + #define sepgsqlCheckSelectInto(a) do {} while(0) + #define sepgsqlExecScan(a,b,c) (true) + #define sepgsqlSetupTuplePerms(a) (0) + #define sepgsqlHeapTupleInsert(a,b,c) do {} while(0) + #define sepgsqlHeapTupleUpdate(a,b,c) do {} while(0) + + /* core.c */ + #define sepgsqlIsEnabled() (false) + #define sepgsqlInitialize() do {} while(0) + + /* bridge.c */ + #define sepgsql_attribute_create(a,b) (InvalidOid) + #define sepgsql_attribute_alter(a,b) do {} while(0) + #define sepgsql_attribute_drop(a,b) do {} while(0) + #define sepgsql_attribute_grant(a,b) do {} while(0) + #define sepgsql_attribute_relabel(a,b,c) (InvalidOid) + + #define sepgsql_cast_create(a,b,c) (InvalidOid) + #define sepgsql_cast_drop(a) (InvalidOid) + + #define sepgsql_relation_create(a,b,c,d,e,f) (NULL) + #define sepgsql_relation_copy(a) (NULL) + #define sepgsql_relation_alter(a,b,c) do {} while(0) + #define sepgsql_relation_drop(a) do {} while(0) + #define sepgsql_relation_grant(a) do {} while(0) + #define sepgsql_relation_relabel(a,b) do {} while(0) + #define sepgsql_relation_get_transaction_id(a) do {} while(0) + #define sepgsql_relation_copy_definition(a) do {} while(0) + #define sepgsql_relation_truncate(a) do {} while(0) + #define sepgsql_relation_references(a,b,c) do {} while(0) + #define sepgsql_relation_lock(a) do {} while(0) + #define sepgsql_view_replace(a) do {} while(0) + #define sepgsql_index_create(a,b,c) do {} while(0) + #define sepgsql_sequence_get_value(a) do {} while(0) + #define sepgsql_sequence_next_value(a) do {} while(0) + #define sepgsql_sequence_set_value(a) do {} while(0) + + #define sepgsql_conversion_create(a,b,c) do {} while(0) + #define sepgsql_conversion_alter(a,b) do {} while(0) + #define sepgsql_conversion_drop(a) do {} while(0) + + #define sepgsql_database_create(a,b) (InvalidOid) + #define sepgsql_database_alter(a) do {} while(0) + #define sepgsql_database_drop(a) do {} while(0) + #define sepgsql_database_relabel(a,b) (InvalidOid) + #define sepgsql_database_grant(a) do {} while(0) + #define sepgsql_database_access(a) do {} while(0) + #define sepgsql_database_superuser(a) (true) + #define sepgsql_database_load_module(a,b) do {} while(0) + + #define sepgsql_fdw_create(a,b) (InvalidOid) + #define sepgsql_fdw_alter(a,b) do {} while(0) + #define sepgsql_fdw_drop(a) do {} while(0) + #define sepgsql_fdw_grant(a) do {} while(0) + + #define sepgsql_foreign_server_create(a) (InvalidOid) + #define sepgsql_foreign_server_alter(a) do {} while(0) + #define sepgsql_foreign_server_drop(a) do {} while(0) + #define sepgsql_foreign_server_grant(a) do {} while(0) + + #define sepgsql_language_create(a,b,c) (InvalidOid) + #define sepgsql_language_alter(a) do {} while(0) + #define sepgsql_language_drop(a) do {} while(0) + #define sepgsql_language_grant(a) do {} while(0) + + #define sepgsql_largeobject_create(a,b) (InvalidOid) + #define sepgsql_largeobject_alter(a,b) do {} while(0) + #define sepgsql_largeobject_drop(a) do {} while(0) + #define sepgsql_largeobject_read(a) do {} while(0) + #define sepgsql_largeobject_write(a) do {} while(0) + #define sepgsql_largeobject_export(a,b) do {} while(0) + #define sepgsql_largeobject_import(a,b) (InvalidOid) + + #define sepgsql_schema_create(a,b,c) (InvalidOid) + #define sepgsql_schema_alter(a) do {} while(0) + #define sepgsql_schema_drop(a) do {} while(0) + #define sepgsql_schema_relabel(a,b) (InvalidOid) + #define sepgsql_schema_grant(a) do {} while(0) + #define sepgsql_schema_search(a,b) (true) + + #define sepgsql_opclass_create(a,b) (InvalidOid) + #define sepgsql_opclass_alter(a,b) do {} while(0) + #define sepgsql_opclass_drop(a) do {} while(0) + + #define sepgsql_opfamily_create(a,b) (InvalidOid) + #define sepgsql_opfamily_alter(a,b) do {} while(0) + #define sepgsql_opfamily_drop(a) do {} while(0) + #define sepgsql_opfamily_add_operator(a,b) do {} while(0) + #define sepgsql_opfamily_add_procedure(a,b) do {} while(0) + + #define sepgsql_operator_create(a,b,c,d,e,f) (InvalidOid) + #define sepgsql_operator_alter(a) do {} while(0) + #define sepgsql_operator_drop(a) do {} while(0) + + #define sepgsql_proc_create(a,b,c,d,e) (InvalidOid) + #define sepgsql_proc_alter(a,b,c) do {} while(0) + #define sepgsql_proc_drop(a) do {} while(0) + #define sepgsql_proc_relabel(a,b) (InvalidOid) + #define sepgsql_proc_grant(a) do {} while(0) + #define sepgsql_proc_execute(a) do {} while(0) + #define sepgsql_proc_hint_inlined(a) (true) + #define sepgsql_proc_entrypoint(a,b) do {} while(0) + + #define sepgsql_rule_create(a,b) do {} while(0) + #define sepgsql_rule_drop(a,b) do {} while(0) + + #define sepgsql_trigger_create(a,b,c) do {} while(0) + #define sepgsql_trigger_alter(a,b) do {} while(0) + #define sepgsql_trigger_drop(a,b) do {} while(0) + + #define sepgsql_ts_config_create(a,b) (InvalidOid) + #define sepgsql_ts_config_alter(a,b) do {} while(0) + #define sepgsql_ts_config_drop(a) do {} while(0) + + #define sepgsql_ts_config_create(a,b) (InvalidOid) + #define sepgsql_ts_config_alter(a,b) do {} while(0) + #define sepgsql_ts_config_drop(a) do {} while(0) + + #define sepgsql_ts_dict_create(a,b) (InvalidOid) + #define sepgsql_ts_dict_alter(a,b) do {} while(0) + #define sepgsql_ts_dict_drop(a) do {} while(0) + + #define sepgsql_ts_parser_create(a,b,c,d,e,f,g) (InvalidOid) + #define sepgsql_ts_parser_alter(a,b) do {} while(0) + #define sepgsql_ts_parser_drop(a) do {} while(0) + + #define sepgsql_ts_template_create(a,b,c,d) (InvalidOid) + #define sepgsql_ts_template_alter(a,b) do {} while(0) + #define sepgsql_ts_template_drop(a) do {} while(0) + + #define sepgsql_type_create(a,b,c,d,e,f,g,h,i,j) (InvalidOid) + #define sepgsql_type_alter(a,b,c) do {} while(0) + #define sepgsql_type_drop(a) do {} while(0) + + #define sepgsql_sysobj_drop(a) do {} while(0) + + #define sepgsql_file_stat(a) do {} while(0) + #define sepgsql_file_read(a) do {} while(0) + #define sepgsql_file_write(a) do {} while(0) + + /* label.c */ + #define sepgsqlTupleDescHasSecLabel(a,b) (false) + #define sepgsqlSetDefaultSecLabel(a,b) do {} while(0) + #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_server_getcon(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 -Nrpc base/src/include/storage/fd.h sepgsql/src/include/storage/fd.h *** base/src/include/storage/fd.h Tue Jan 13 09:22:28 2009 --- sepgsql/src/include/storage/fd.h Wed Jul 15 19:48:58 2009 *************** extern int FileWrite(File file, char *bu *** 68,73 **** --- 68,74 ---- extern int FileSync(File file); extern off_t FileSeek(File file, off_t offset, int whence); extern int FileTruncate(File file, off_t 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 -Nrpc base/src/include/storage/large_object.h sepgsql/src/include/storage/large_object.h *** base/src/include/storage/large_object.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/storage/large_object.h Wed Jul 15 19:48:58 2009 *************** typedef struct LargeObjectDesc *** 44,49 **** --- 44,50 ---- #define IFS_RDLOCK (1 << 0) #define IFS_WRLOCK (1 << 1) + Oid secid; /* security identifier of largeobject */ } LargeObjectDesc; *************** extern int inv_tell(LargeObjectDesc *obj *** 79,83 **** --- 80,86 ---- 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 -Nrpc base/src/include/storage/lwlock.h sepgsql/src/include/storage/lwlock.h *** base/src/include/storage/lwlock.h Fri Mar 6 09:45:33 2009 --- sepgsql/src/include/storage/lwlock.h Wed Jul 15 19:35:52 2009 *************** typedef enum LWLockId *** 67,72 **** --- 67,73 ---- AutovacuumLock, AutovacuumScheduleLock, SyncScanLock, + SepgsqlAvcLock, /* Individual lock IDs end here */ FirstBufMappingLock, FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS, diff -Nrpc base/src/include/storage/proc.h sepgsql/src/include/storage/proc.h *** base/src/include/storage/proc.h Thu Feb 26 10:18:55 2009 --- sepgsql/src/include/storage/proc.h Tue Dec 8 14:04:25 2009 *************** typedef struct PROC_HDR *** 143,150 **** * normal operation. Startup process also consumes one slot, but WAL * writer and autovacuum launcher are launched only after it has * exited. */ ! #define NUM_AUXILIARY_PROCS 3 /* configurable options */ --- 143,152 ---- * normal operation. Startup process also consumes one slot, but WAL * writer and autovacuum launcher are launched only after it has * exited. + * In addition, a netlink receiver process may be launched, if SELinux + * support is enabled. */ ! #define NUM_AUXILIARY_PROCS 4 /* configurable options */ diff -Nrpc base/src/include/utils/errcodes.h sepgsql/src/include/utils/errcodes.h *** base/src/include/utils/errcodes.h Fri Mar 6 09:45:33 2009 --- sepgsql/src/include/utils/errcodes.h Wed Jul 15 19:35:52 2009 *************** *** 347,352 **** --- 347,357 ---- #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 -Nrpc base/src/include/utils/syscache.h sepgsql/src/include/utils/syscache.h *** base/src/include/utils/syscache.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/utils/syscache.h Wed Sep 9 21:29:33 2009 *************** enum SysCacheIdentifier *** 69,74 **** --- 69,76 ---- RELNAMENSP, RELOID, RULERELNAME, + SECURITYATTR, + SECURITYSECID, STATRELATT, TSCONFIGMAP, TSCONFIGNAMENSP, diff -Nrpc base/src/test/regress/GNUmakefile sepgsql/src/test/regress/GNUmakefile *** base/src/test/regress/GNUmakefile Sat Jan 3 13:01:35 2009 --- sepgsql/src/test/regress/GNUmakefile Tue Dec 1 17:11:40 2009 *************** ifdef NO_LOCALE *** 38,43 **** --- 38,49 ---- NOLOCALE += --no-locale endif + # SELinux support + ENABLE_SELINUX = + ifdef SELINUX + ENABLE_SELINUX += --enable-selinux + endif + # stuff to pass into build of pg_regress EXTRADEFS = '-DHOST_TUPLE="$(host_tuple)"' \ '-DMAKEPROG="$(MAKE)"' \ *************** tablespace-setup: *** 138,144 **** ## Run tests ## ! pg_regress_call = ./pg_regress --inputdir=$(srcdir) --dlpath=. --multibyte=$(MULTIBYTE) --load-language=plpgsql $(NOLOCALE) check: all $(pg_regress_call) --temp-install=./tmp_check --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(TEMP_CONF) --- 144,150 ---- ## Run tests ## ! pg_regress_call = ./pg_regress --inputdir=$(srcdir) --dlpath=. --multibyte=$(MULTIBYTE) --load-language=plpgsql $(NOLOCALE) $(ENABLE_SELINUX) check: all $(pg_regress_call) --temp-install=./tmp_check --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(TEMP_CONF) diff -Nrpc base/src/test/regress/expected/sanity_check.out sepgsql/src/test/regress/expected/sanity_check.out *** base/src/test/regress/expected/sanity_check.out Tue Feb 10 10:10:02 2009 --- sepgsql/src/test/regress/expected/sanity_check.out Thu Jul 16 23:18:11 2009 *************** SELECT relname, relhasindex *** 112,117 **** --- 112,118 ---- pg_pltemplate | t pg_proc | t pg_rewrite | t + pg_security | t pg_shdepend | t pg_shdescription | t pg_statistic | t *************** SELECT relname, relhasindex *** 151,157 **** timetz_tbl | f tinterval_tbl | f varchar_tbl | f ! (140 rows) -- -- another sanity check: every system catalog that has OIDs should have --- 152,158 ---- timetz_tbl | f tinterval_tbl | f varchar_tbl | f ! (141 rows) -- -- another sanity check: every system catalog that has OIDs should have diff -Nrpc base/src/test/regress/pg_regress.c sepgsql/src/test/regress/pg_regress.c *** base/src/test/regress/pg_regress.c Thu Jun 18 10:20:52 2009 --- sepgsql/src/test/regress/pg_regress.c Tue Dec 1 17:11:40 2009 *************** static _stringlist *schedulelist = NULL; *** 82,87 **** --- 82,88 ---- static _stringlist *extra_tests = NULL; static char *temp_install = NULL; static char *temp_config = NULL; + static bool enable_selinux = false; static char *top_builddir = NULL; static bool nolocale = false; static char *hostname = NULL; *************** help(void) *** 1863,1868 **** --- 1864,1870 ---- printf(_(" --top-builddir=DIR (relative) path to top level build directory\n")); printf(_(" --port=PORT start postmaster on PORT\n")); printf(_(" --temp-config=PATH append contents of PATH to temporary config\n")); + printf(_(" --enable-selinux enables SELinux support, if available\n")); printf(_("\n")); printf(_("Options for using an existing installation:\n")); printf(_(" --host=HOST use postmaster running on HOST\n")); *************** regression_main(int argc, char *argv[], *** 1907,1912 **** --- 1909,1915 ---- {"dlpath", required_argument, NULL, 17}, {"create-role", required_argument, NULL, 18}, {"temp-config", required_argument, NULL, 19}, + {"enable-selinux", optional_argument, NULL, 20}, {NULL, 0, NULL, 0} }; *************** regression_main(int argc, char *argv[], *** 1997,2002 **** --- 2000,2008 ---- case 19: temp_config = strdup(optarg); break; + case 20: + enable_selinux = true; + break; default: /* getopt_long already emitted a complaint */ fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"), *************** regression_main(int argc, char *argv[], *** 2086,2095 **** /* initdb */ header(_("initializing database system")); snprintf(buf, sizeof(buf), ! SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE, bindir, temp_install, datadir, debug ? " --debug" : "", nolocale ? " --no-locale" : "", outputdir); if (system(buf)) { --- 2092,2102 ---- /* initdb */ header(_("initializing database system")); snprintf(buf, sizeof(buf), ! SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean%s%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE, bindir, temp_install, datadir, debug ? " --debug" : "", nolocale ? " --no-locale" : "", + enable_selinux ? " --enable-selinux" : "", outputdir); if (system(buf)) {