diff --git a/configure b/configure index 7e34c4f..7bfa5ff 100755 --- a/configure +++ b/configure @@ -707,6 +707,7 @@ LDFLAGS_SL ELF_SYS EGREP GREP +enable_selinux with_zlib with_system_tzdata with_libxslt @@ -842,6 +843,7 @@ with_libxml with_libxslt with_system_tzdata with_zlib +enable_selinux with_gnu_ld enable_largefile enable_float4_byval @@ -1498,6 +1500,7 @@ Optional Features: --enable-depend turn on automatic dependency tracking --enable-cassert enable assertion checks (for debugging) --disable-thread-safety disable thread-safety in client libraries + --enable-selinux build with SELinux support --disable-largefile omit support for large files --disable-float4-byval disable float4 passed by value --disable-float8-byval disable float8 passed by value @@ -5608,6 +5611,201 @@ fi # +# SELinux support +# + + +# Check whether --enable-selinux was given. +if test "${enable_selinux+set}" = set; then + enableval=$enable_selinux; + case $enableval in + yes) + : + ;; + no) + : + ;; + *) + { { $as_echo "$as_me:$LINENO: error: no argument expected for --enable-selinux option" >&5 +$as_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 + +{ $as_echo "$as_me:$LINENO: checking for avc_open in -lselinux" >&5 +$as_echo_n "checking for avc_open in -lselinux... " >&6; } +if test "${ac_cv_lib_selinux_avc_open+set}" = set; then + $as_echo_n "(cached) " >&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_open (); +int +main () +{ +return avc_open (); + ; + 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 ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$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 + $as_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 && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_lib_selinux_avc_open=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_selinux_avc_open=no +fi + +rm -rf conftest.dSYM +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 +{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_avc_open" >&5 +$as_echo "$ac_cv_lib_selinux_avc_open" >&6; } +if test "x$ac_cv_lib_selinux_avc_open" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBSELINUX 1 +_ACEOF + + LIBS="-lselinux $LIBS" + +else + { { $as_echo "$as_me:$LINENO: error: \"SELinux support requires libselinux.\"" >&5 +$as_echo "$as_me: error: \"SELinux support requires libselinux.\"" >&2;} + { (exit 1); exit 1; }; } +fi + + +{ $as_echo "$as_me:$LINENO: checking for audit_open in -laudit" >&5 +$as_echo_n "checking for audit_open in -laudit... " >&6; } +if test "${ac_cv_lib_audit_audit_open+set}" = set; then + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-laudit $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 audit_open (); +int +main () +{ +return audit_open (); + ; + 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 ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$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 + $as_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 && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then + ac_cv_lib_audit_audit_open=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_audit_audit_open=no +fi + +rm -rf conftest.dSYM +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 +{ $as_echo "$as_me:$LINENO: result: $ac_cv_lib_audit_audit_open" >&5 +$as_echo "$ac_cv_lib_audit_audit_open" >&6; } +if test "x$ac_cv_lib_audit_audit_open" = x""yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBAUDIT 1 +_ACEOF + + LIBS="-laudit $LIBS" + +else + { { $as_echo "$as_me:$LINENO: error: \"SELinux support requires libaudit.\"" >&5 +$as_echo "$as_me: error: \"SELinux support requires libaudit.\"" >&2;} + { (exit 1); exit 1; }; } +fi + + +cat >>confdefs.h <<_ACEOF +#define HAVE_SELINUX 1 +_ACEOF + + +fi + +# # Elf # diff --git a/configure.in b/configure.in index bbeea97..e499e10 100644 --- a/configure.in +++ b/configure.in @@ -755,6 +755,19 @@ PGAC_ARG_BOOL(with, zlib, yes, AC_SUBST(with_zlib) # +# SELinux support +# +PGAC_ARG_BOOL(enable, selinux, no, [build with SELinux support]) +if test "$enable_selinux" = yes; then + AC_CHECK_LIB(selinux, avc_open,, + AC_MSG_ERROR("SELinux support requires libselinux.")) + AC_CHECK_LIB(audit, audit_open,, + AC_MSG_ERROR("SELinux support requires libaudit.")) + AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1, [SE-PostgreSQL feature is enabled]) + AC_SUBST(enable_selinux) +fi + +# # Elf # diff --git a/src/Makefile.global.in b/src/Makefile.global.in index 1c38ac2..0e3bed5 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -164,6 +164,7 @@ enable_nls = @enable_nls@ enable_debug = @enable_debug@ enable_dtrace = @enable_dtrace@ enable_coverage = @enable_coverage@ +enable_selinux = @enable_selinux@ enable_thread_safety = @enable_thread_safety@ python_includespec = @python_includespec@ diff --git a/src/backend/Makefile b/src/backend/Makefile index 218544e..0b1dd31 100644 --- a/src/backend/Makefile +++ b/src/backend/Makefile @@ -16,7 +16,7 @@ include $(top_builddir)/src/Makefile.global SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \ main nodes optimizer port postmaster regex replication rewrite \ - storage tcop tsearch utils $(top_builddir)/src/timezone + sepgsql storage tcop tsearch utils $(top_builddir)/src/timezone include $(srcdir)/common.mk @@ -40,6 +40,11 @@ LIBS := $(filter-out -lpgport, $(LIBS)) $(LDAP_LIBS_BE) # The backend doesn't need everything that's in LIBS, however LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS)) +# SELinux needs libselinux and libaudit +ifeq ($(enable_selinux), yes) +LIBS := $(filter-out -lselinux -laudit, $(LIBS)) -lselinux -laudit +endif + ########################################################################## all: submake-libpgport submake-schemapg postgres $(POSTGRES_IMP) diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index 6ec73f0..db0023f 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -60,6 +60,7 @@ #include "access/heapam.h" #include "access/sysattr.h" #include "access/tuptoaster.h" +#include "catalog/pg_seclabel.h" #include "executor/tuptable.h" @@ -559,6 +560,9 @@ heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull) case TableOidAttributeNumber: result = ObjectIdGetDatum(tup->t_tableOid); break; + case SecurityLabelAttributeNumber: + result = seclabelSysattOutput(tup->t_tableOid, tup); + break; default: elog(ERROR, "invalid attnum: %d", attnum); result = 0; /* keep compiler quiet */ @@ -682,6 +686,8 @@ heap_form_tuple(TupleDesc tupleDescriptor, if (tupleDescriptor->tdhasoid) len += sizeof(Oid); + if (tupleDescriptor->tdhassecid) + len += sizeof(Oid)+1; hoff = len = MAXALIGN(len); /* align user data safely */ @@ -713,6 +719,8 @@ heap_form_tuple(TupleDesc tupleDescriptor, 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, @@ -824,6 +832,8 @@ heap_modify_tuple(HeapTuple tuple, newTuple->t_tableOid = tuple->t_tableOid; if (tupleDesc->tdhasoid) HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); + if (tupleDesc->tdhassecid) + HeapTupleSetSecid(newTuple, HeapTupleGetSecid(tuple)); return newTuple; } @@ -1434,6 +1444,8 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor, if (tupleDescriptor->tdhasoid) len += sizeof(Oid); + if (tupleDescriptor->tdhassecid) + len += sizeof(Oid); hoff = len = MAXALIGN(len); /* align user data safely */ @@ -1455,6 +1467,8 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor, 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 --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 9a8611f..325dbde 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -34,7 +34,7 @@ * caller can overwrite this if needed. */ TupleDesc -CreateTemplateTupleDesc(int natts, bool hasoid) +CreateTemplateTupleDesc(int natts, bool hasoid, bool hassecid) { TupleDesc desc; char *stg; @@ -88,6 +88,7 @@ CreateTemplateTupleDesc(int natts, bool hasoid) desc->tdtypeid = RECORDOID; desc->tdtypmod = -1; desc->tdhasoid = hasoid; + desc->tdhassecid = hassecid; desc->tdrefcount = -1; /* assume not reference-counted */ return desc; @@ -105,7 +106,8 @@ CreateTemplateTupleDesc(int natts, bool hasoid) * caller can overwrite this if needed. */ TupleDesc -CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs) +CreateTupleDesc(int natts, bool hasoid, bool hassecid, + Form_pg_attribute *attrs) { TupleDesc desc; @@ -121,6 +123,7 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs) desc->tdtypeid = RECORDOID; desc->tdtypmod = -1; desc->tdhasoid = hasoid; + desc->tdhassecid = hassecid; desc->tdrefcount = -1; /* assume not reference-counted */ return desc; @@ -139,7 +142,8 @@ CreateTupleDescCopy(TupleDesc tupdesc) TupleDesc desc; int i; - desc = CreateTemplateTupleDesc(tupdesc->natts, tupdesc->tdhasoid); + desc = CreateTemplateTupleDesc(tupdesc->natts, + tupdesc->tdhasoid, tupdesc->tdhassecid); for (i = 0; i < desc->natts; i++) { @@ -150,6 +154,7 @@ CreateTupleDescCopy(TupleDesc tupdesc) desc->tdtypeid = tupdesc->tdtypeid; desc->tdtypmod = tupdesc->tdtypmod; + desc->tdhassecid = tupdesc->tdhassecid; return desc; } @@ -166,7 +171,8 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc) TupleConstr *constr = tupdesc->constr; int i; - desc = CreateTemplateTupleDesc(tupdesc->natts, tupdesc->tdhasoid); + desc = CreateTemplateTupleDesc(tupdesc->natts, + tupdesc->tdhasoid, tupdesc->tdhassecid); for (i = 0; i < desc->natts; i++) { @@ -314,6 +320,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) return false; if (tupdesc1->tdhasoid != tupdesc2->tdhasoid) return false; + if (tupdesc1->tdhassecid != tupdesc2->tdhassecid) + return false; for (i = 0; i < tupdesc1->natts; i++) { @@ -519,7 +527,7 @@ BuildDescForRelation(List *schema) * allocate a new tuple descriptor */ natts = list_length(schema); - desc = CreateTemplateTupleDesc(natts, false); + desc = CreateTemplateTupleDesc(natts, false, false); has_not_null = false; attnum = 0; @@ -604,7 +612,7 @@ BuildDescFromLists(List *names, List *types, List *typmods) /* * allocate a new tuple descriptor */ - desc = CreateTemplateTupleDesc(natts, false); + desc = CreateTemplateTupleDesc(natts, false, false); attnum = 0; diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c index f01ed1e..4e8dfcb 100644 --- a/src/backend/access/gin/ginutil.c +++ b/src/backend/access/gin/ginutil.c @@ -33,7 +33,7 @@ initGinState(GinState *state, Relation index) for (i = 0; i < index->rd_att->natts; i++) { - state->tupdesc[i] = CreateTemplateTupleDesc(2, false); + state->tupdesc[i] = CreateTemplateTupleDesc(2, false, false); TupleDescInitEntry(state->tupdesc[i], (AttrNumber) 1, NULL, INT2OID, -1, 0); diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index bb57cb9..d17274f 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -52,6 +52,7 @@ #include "access/xlogutils.h" #include "catalog/catalog.h" #include "catalog/namespace.h" +#include "catalog/pg_seclabel.h" #include "miscadmin.h" #include "pgstat.h" #include "storage/bufmgr.h" @@ -1862,6 +1863,22 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid, Assert(!(tup->t_data->t_infomask & HEAP_HASOID)); } + /* + * If this tuple has a capability to store its security id, but it has + * not been assigned yet, the default security id should be assigned. + * Note that this step does not apply any permission checks. All the + * caller of heap_insert() is trusted. + */ + if (relation->rd_rel->relhassecids) + { + if (!OidIsValid(HeapTupleGetSecid(tup))) + HeapTupleSetSecid(tup, seclabelGetNewSecid(relation, tup)); + } + else + { + Assert(!HeapTupleHasSecid(tup)); + } + tup->t_data->t_infomask &= ~(HEAP_XACT_MASK); tup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK); tup->t_data->t_infomask |= HEAP_XMAX_INVALID; @@ -2560,6 +2577,20 @@ l2: Assert(!(newtup->t_data->t_infomask & HEAP_HASOID)); } + /* + * Preserve security-id, if not changed + */ + if (relation->rd_rel->relhassecids) + { + if (!OidIsValid(HeapTupleGetSecid(newtup))) + HeapTupleSetSecid(newtup, HeapTupleGetSecid(&oldtup)); + } + else + { + /* check there is not space for a security-id */ + Assert(!HeapTupleHasSecid(newtup)); + } + 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); @@ -3501,6 +3532,10 @@ heap_inplace_update(Relation relation, HeapTuple tuple) memcpy((char *) htup + htup->t_hoff, (char *) tuple->t_data + tuple->t_data->t_hoff, newlen); + if (HeapTupleHeaderHasOid(htup)) + HeapTupleHeaderSetOid(htup, HeapTupleGetOid(tuple)); + if (HeapTupleHeaderHasSecid(htup)) + HeapTupleHeaderSetSecid(htup, HeapTupleGetSecid(tuple)); MarkBufferDirty(buffer); diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c index 2af81df..18341f4 100644 --- a/src/backend/access/heap/tuptoaster.c +++ b/src/backend/access/heap/tuptoaster.c @@ -591,6 +591,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, 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 */ @@ -868,6 +870,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, 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, @@ -1019,6 +1023,8 @@ toast_flatten_tuple_attribute(Datum value, 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, diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c index e2566a4..c21054b 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -605,7 +605,7 @@ pg_prepared_xact(PG_FUNCTION_ARGS) /* build tupdesc for result tuples */ /* this had better match pg_prepared_xacts view in system_views.sql */ - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(5, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "transaction", XIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "gid", diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index b88cff2..d6636f7 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -36,6 +36,7 @@ #include "libpq/be-fsstubs.h" #include "miscadmin.h" #include "pgstat.h" +#include "sepgsql/hooks.h" #include "storage/bufmgr.h" #include "storage/fd.h" #include "storage/lmgr.h" @@ -140,6 +141,8 @@ typedef struct TransactionStateData int maxChildXids; /* allocated size of childXids[] */ Oid prevUser; /* previous CurrentUserId setting */ int prevSecContext; /* previous SecurityRestrictionContext */ + char *prevSecLabel; /* previous security label of client */ + int prevRowlvMode; /* previous row-level access control mode */ bool prevXactReadOnly; /* entry-time xact r/o state */ bool startedInRecovery; /* did we start in recovery? */ struct TransactionStateData *parent; /* back link to parent */ @@ -169,6 +172,8 @@ static TransactionStateData TopTransactionStateData = { 0, /* allocated size of childXids[] */ InvalidOid, /* previous CurrentUserId setting */ 0, /* previous SecurityRestrictionContext */ + NULL, /* previous security label of the client */ + 0, /* previous row-level access control mode */ false, /* entry-time xact r/o state */ false, /* startedInRecovery */ NULL /* link to parent state block */ @@ -1658,6 +1663,10 @@ StartTransaction(void) /* SecurityRestrictionContext should never be set outside a transaction */ Assert(s->prevSecContext == 0); + /* Save status of SELinux */ + s->prevSecLabel = sepgsql_get_client_label(); + s->prevRowlvMode = sepgsql_rowlv_get_mode(); + /* * initialize other subsystems for new transaction */ @@ -2162,6 +2171,10 @@ AbortTransaction(void) */ SetUserIdAndSecContext(s->prevUser, s->prevSecContext); + /* Reset SELinux status */ + sepgsql_set_client_label(s->prevSecLabel); + sepgsql_rowlv_set_mode(s->prevRowlvMode); + /* * do abort processing */ @@ -4006,6 +4019,10 @@ AbortSubTransaction(void) */ SetUserIdAndSecContext(s->prevUser, s->prevSecContext); + /* Reset SELinux status */ + sepgsql_set_client_label(s->prevSecLabel); + sepgsql_rowlv_set_mode(s->prevRowlvMode); + /* * We can skip all this stuff if the subxact failed before creating a * ResourceOwner... @@ -4145,6 +4162,8 @@ PushTransaction(void) s->state = TRANS_DEFAULT; s->blockState = TBLOCK_SUBBEGIN; GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext); + s->prevSecLabel = sepgsql_get_client_label(); + s->prevRowlvMode = sepgsql_rowlv_get_mode(); s->prevXactReadOnly = XactReadOnly; CurrentTransactionState = s; diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index abdf4d8..1b5b84c 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -8427,7 +8427,7 @@ pg_xlogfile_name_offset(PG_FUNCTION_ARGS) * Construct a tuple descriptor for the result row. This must match this * function's pg_proc entry! */ - resultTupleDesc = CreateTemplateTupleDesc(2, false); + resultTupleDesc = CreateTemplateTupleDesc(2, false, false); TupleDescInitEntry(resultTupleDesc, (AttrNumber) 1, "file_name", TEXTOID, -1, 0); TupleDescInitEntry(resultTupleDesc, (AttrNumber) 2, "file_offset", diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y index 387d43e..aecb449 100644 --- a/src/backend/bootstrap/bootparse.y +++ b/src/backend/bootstrap/bootparse.y @@ -32,6 +32,7 @@ #include "catalog/pg_authid.h" #include "catalog/pg_class.h" #include "catalog/pg_namespace.h" +#include "catalog/pg_seclabel.h" #include "catalog/pg_tablespace.h" #include "catalog/toasting.h" #include "commands/defrem.h" @@ -187,10 +188,11 @@ Boot_CreateStmt: TupleDesc tupdesc; bool shared_relation; bool mapped_relation; + bool hassecid = seclabelCatalogHasSysAttr($3); do_start(); - tupdesc = CreateTupleDesc(numattr, !($6), attrtypes); + tupdesc = CreateTupleDesc(numattr, !($6), hassecid, attrtypes); shared_relation = $5; @@ -245,7 +247,8 @@ Boot_CreateStmt: ONCOMMIT_NOOP, (Datum) 0, false, - true); + true, + NULL); elog(DEBUG4, "relation created with oid %u", id); } do_end(); diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index 46e8bae..5f8c824 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -33,6 +33,7 @@ #include "postmaster/bgwriter.h" #include "postmaster/walwriter.h" #include "replication/walreceiver.h" +#include "sepgsql/hooks.h" #include "storage/bufmgr.h" #include "storage/ipc.h" #include "storage/proc.h" @@ -319,6 +320,9 @@ AuxiliaryProcessMain(int argc, char *argv[]) case WalReceiverProcess: statmsg = "wal receiver process"; break; + case SecurityWorkerProcess: + statmsg = "security worker process"; + break; default: statmsg = "??? process"; break; @@ -429,6 +433,10 @@ AuxiliaryProcessMain(int argc, char *argv[]) WalReceiverMain(); proc_exit(1); /* should never return */ + case SecurityWorkerProcess: + sepgsql_worker_main(); + proc_exit(1); /* should never return */ + default: elog(PANIC, "unrecognized process type: %d", auxType); proc_exit(1); @@ -493,6 +501,11 @@ BootstrapModeMain(void) boot_yyparse(); /* + * Initial security labeling + */ + sepgsql_post_bootstraping(); + + /* * We should now know about all mapped relations, so it's okay to write * out the initial relation mapping files. */ @@ -794,6 +807,7 @@ InsertOneTuple(Oid objectid) tupDesc = CreateTupleDesc(numattr, RelationGetForm(boot_reldesc)->relhasoids, + RelationGetForm(boot_reldesc)->relhassecids, attrtypes); tuple = heap_form_tuple(tupDesc, values, Nulls); if (objectid != (Oid) 0) diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index dafae3f..c2aea7a 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -13,7 +13,7 @@ include $(top_builddir)/src/Makefile.global 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_db_role_setting.o pg_shdepend.o pg_type.o storage.o toasting.o + pg_db_role_setting.o pg_seclabel.o pg_shdepend.o pg_type.o storage.o toasting.o BKIFILES = postgres.bki postgres.description postgres.shdescription @@ -34,7 +34,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ pg_database.h pg_db_role_setting.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_seclabel.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 \ pg_default_acl.h \ diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index d16b03b..a9d5184 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -38,6 +38,7 @@ #include "catalog/pg_operator.h" #include "catalog/pg_opfamily.h" #include "catalog/pg_proc.h" +#include "catalog/pg_seclabel.h" #include "catalog/pg_tablespace.h" #include "catalog/pg_type.h" #include "catalog/pg_ts_config.h" @@ -46,6 +47,7 @@ #include "foreign/foreign.h" #include "miscadmin.h" #include "parser/parse_func.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" @@ -1460,6 +1462,10 @@ expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids) continue; + /* Skip security label column, if it doesn't exist */ + if (curr_att == SecurityLabelAttributeNumber && !classForm->relhassecids) + continue; + /* Views don't have any system columns at all */ if (classForm->relkind == RELKIND_VIEW && curr_att < 0) continue; @@ -1560,6 +1566,8 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname, relOid, grantorId, ACL_KIND_COLUMN, relname, attnum, NameStr(pg_attribute_tuple->attname)); + /* SELinux checks */ + sepgsql_attribute_grant(relOid, attnum); /* * Generate new ACL. @@ -1813,6 +1821,8 @@ ExecGrant_Relation(InternalGrant *istmt) ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS, NameStr(pg_class_tuple->relname), 0, NULL); + /* SELinux checks */ + sepgsql_relation_grant(relOid); /* * Generate new ACL. @@ -1999,6 +2009,8 @@ ExecGrant_Database(InternalGrant *istmt) datId, grantorId, ACL_KIND_DATABASE, NameStr(pg_database_tuple->datname), 0, NULL); + /* SELinux checks */ + sepgsql_database_grant(datId); /* * Generate new ACL. @@ -2116,6 +2128,8 @@ ExecGrant_Fdw(InternalGrant *istmt) fdwid, grantorId, ACL_KIND_FDW, NameStr(pg_fdw_tuple->fdwname), 0, NULL); + /* SELinux checks */ + sepgsql_fdw_grant(fdwid); /* * Generate new ACL. @@ -2233,6 +2247,8 @@ ExecGrant_ForeignServer(InternalGrant *istmt) srvid, grantorId, ACL_KIND_FOREIGN_SERVER, NameStr(pg_server_tuple->srvname), 0, NULL); + /* SELinux checks */ + sepgsql_fserver_grant(srvid); /* * Generate new ACL. @@ -2719,6 +2735,8 @@ ExecGrant_Namespace(InternalGrant *istmt) nspid, grantorId, ACL_KIND_NAMESPACE, NameStr(pg_namespace_tuple->nspname), 0, NULL); + /* SELinux checks */ + sepgsql_schema_grant(nspid); /* * Generate new ACL. @@ -2835,6 +2853,8 @@ ExecGrant_Tablespace(InternalGrant *istmt) tblId, grantorId, ACL_KIND_TABLESPACE, NameStr(pg_tablespace_tuple->spcname), 0, NULL); + /* SELinux checks */ + sepgsql_tablespace_grant(tblId); /* * Generate new ACL. diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c index 7efdf67..9376f6c 100644 --- a/src/backend/catalog/catalog.c +++ b/src/backend/catalog/catalog.c @@ -32,6 +32,7 @@ #include "catalog/pg_namespace.h" #include "catalog/pg_pltemplate.h" #include "catalog/pg_db_role_setting.h" +#include "catalog/pg_seclabel.h" #include "catalog/pg_shdepend.h" #include "catalog/pg_shdescription.h" #include "catalog/pg_tablespace.h" @@ -306,6 +307,7 @@ IsSharedRelation(Oid relationId) relationId == AuthMemRelationId || relationId == DatabaseRelationId || relationId == PLTemplateRelationId || + relationId == SecLabelRelationId || relationId == SharedDescriptionRelationId || relationId == SharedDependRelationId || relationId == TableSpaceRelationId || @@ -319,6 +321,8 @@ IsSharedRelation(Oid relationId) relationId == DatabaseNameIndexId || relationId == DatabaseOidIndexId || relationId == PLTemplateNameIndexId || + relationId == SecLabelSecidIndexId || + relationId == SecLabelLabelIndexId || relationId == SharedDescriptionObjIndexId || relationId == SharedDependDependerIndexId || relationId == SharedDependReferenceIndexId || @@ -331,6 +335,8 @@ IsSharedRelation(Oid relationId) relationId == PgAuthidToastIndex || relationId == PgDatabaseToastTable || relationId == PgDatabaseToastIndex || + relationId == PgSecLabelToastTable || + relationId == PgSecLabelToastIndex || relationId == PgShdescriptionToastTable || relationId == PgShdescriptionToastIndex || relationId == PgDbRoleSettingToastTable || diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl index 9eb805d..30a0779 100644 --- a/src/backend/catalog/genbki.pl +++ b/src/backend/catalog/genbki.pl @@ -216,7 +216,8 @@ foreach my $catname ( @{ $catalogs->{names} } ) {cmin => 'cid'}, {xmax => 'xid'}, {cmax => 'cid'}, - {tableoid => 'oid'} + {tableoid => 'oid'}, + {security_label => 'text'} ); foreach my $attr (@SYS_ATTRS) { diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index ec89e49..35bc618 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -43,6 +43,7 @@ #include "catalog/pg_constraint.h" #include "catalog/pg_inherits.h" #include "catalog/pg_namespace.h" +#include "catalog/pg_seclabel.h" #include "catalog/pg_statistic.h" #include "catalog/pg_tablespace.h" #include "catalog/pg_type.h" @@ -82,14 +83,16 @@ static void AddNewRelationTuple(Relation pg_class_desc, Oid relowner, char relkind, Datum relacl, - Datum reloptions); + Datum reloptions, + Oid *secLabels); static Oid AddNewRelationType(const char *typeName, Oid typeNamespace, Oid new_rel_oid, char new_rel_kind, Oid ownerid, Oid new_row_type, - Oid new_array_type); + Oid new_array_type, + Oid securityId); static void RelationRemoveInheritance(Oid relid); static void StoreRelCheck(Relation rel, char *ccname, Node *expr, bool is_local, int inhcount); @@ -173,7 +176,16 @@ static FormData_pg_attribute a7 = { true, 'p', 'i', true, false, false, true, 0 }; -static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; +/* + * Security Label system column + */ +static FormData_pg_attribute a8 = { + 0, {"security_label"}, TEXTOID, 0, -1, + SecurityLabelAttributeNumber, 0, -1, -1, + false, 'x', 'i', true, false, false, true, 0 +}; + +static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8}; /* * This function returns a Form_pg_attribute pointer for a system attribute. @@ -181,12 +193,14 @@ static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; * happen if there's a problem upstream. */ Form_pg_attribute -SystemAttributeDefinition(AttrNumber attno, bool relhasoids) +SystemAttributeDefinition(AttrNumber attno, bool relhasoids, bool relhassecids) { if (attno >= 0 || attno < -(int) lengthof(SysAtt)) elog(ERROR, "invalid system attribute number %d", attno); if (attno == ObjectIdAttributeNumber && !relhasoids) elog(ERROR, "invalid system attribute number %d", attno); + if (attno == SecurityLabelAttributeNumber && !relhassecids) + elog(ERROR, "invalid system attribute number %d", attno); return SysAtt[-attno - 1]; } @@ -195,7 +209,7 @@ SystemAttributeDefinition(AttrNumber attno, bool relhasoids) * pointer for a prototype definition. If not, return NULL. */ Form_pg_attribute -SystemAttributeByName(const char *attname, bool relhasoids) +SystemAttributeByName(const char *attname, bool relhasoids, bool relhassecids) { int j; @@ -203,16 +217,29 @@ SystemAttributeByName(const char *attname, bool relhasoids) { Form_pg_attribute att = SysAtt[j]; - if (relhasoids || att->attnum != ObjectIdAttributeNumber) - { - if (strcmp(NameStr(att->attname), attname) == 0) - return att; - } + if (!relhasoids && att->attnum == ObjectIdAttributeNumber) + continue; + if (!relhassecids && att->attnum == SecurityLabelAttributeNumber) + continue; + + if (strcmp(NameStr(att->attname), attname) == 0) + return att; } return NULL; } +/* + * If the given attribute is writable system attribute, it returns true. + */ +bool +SystemAttributeWritable(AttrNumber attnum, bool hasoids, bool hassecids) +{ + if (hassecids && attnum == SecurityLabelAttributeNumber) + return true; + + return false; +} /* ---------------------------------------------------------------- * XXX END OF UGLY HARD CODED BADNESS XXX @@ -391,7 +418,8 @@ CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind, for (i = 0; i < natts; i++) { if (SystemAttributeByName(NameStr(tupdesc->attrs[i]->attname), - tupdesc->tdhasoid) != NULL) + tupdesc->tdhasoid, + tupdesc->tdhassecid) != NULL) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_COLUMN), errmsg("column name \"%s\" conflicts with a system column name", @@ -509,7 +537,8 @@ CheckAttributeType(const char *attname, Oid atttypid, void InsertPgAttributeTuple(Relation pg_attribute_rel, Form_pg_attribute new_attribute, - CatalogIndexState indstate) + CatalogIndexState indstate, + Oid securityId) { Datum values[Natts_pg_attribute]; bool nulls[Natts_pg_attribute]; @@ -542,6 +571,8 @@ InsertPgAttributeTuple(Relation pg_attribute_rel, nulls[Anum_pg_attribute_attoptions - 1] = true; tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, securityId); /* finally insert the new tuple, update the indexes, and clean up */ simple_heap_insert(pg_attribute_rel, tup); @@ -566,13 +597,15 @@ AddNewAttributeTuples(Oid new_rel_oid, TupleDesc tupdesc, char relkind, bool oidislocal, - int oidinhcount) + int oidinhcount, + Oid *secLabels) { Form_pg_attribute attr; int i; Relation rel; CatalogIndexState indstate; int natts = tupdesc->natts; + Oid secid; ObjectAddress myself, referenced; @@ -596,7 +629,10 @@ AddNewAttributeTuples(Oid new_rel_oid, attr->attstattarget = -1; attr->attcacheoff = -1; - InsertPgAttributeTuple(rel, attr, indstate); + secid = (!secLabels ? InvalidOid : + secLabels[i - FirstLowInvalidHeapAttributeNumber]); + + InsertPgAttributeTuple(rel, attr, indstate, secid); /* Add dependency info */ myself.classId = RelationRelationId; @@ -624,6 +660,11 @@ AddNewAttributeTuples(Oid new_rel_oid, SysAtt[i]->attnum == ObjectIdAttributeNumber) continue; + /* skip security label where appropriate */ + if (!tupdesc->tdhassecid && + SysAtt[i]->attnum == SecurityLabelAttributeNumber) + continue; + memcpy(&attStruct, (char *) SysAtt[i], sizeof(FormData_pg_attribute)); /* Fill in the correct relation OID in the copied tuple */ @@ -636,7 +677,10 @@ AddNewAttributeTuples(Oid new_rel_oid, attStruct.attinhcount = oidinhcount; } - InsertPgAttributeTuple(rel, &attStruct, indstate); + secid = (!secLabels ? InvalidOid + : secLabels[SysAtt[i]->attnum - FirstLowInvalidHeapAttributeNumber]); + + InsertPgAttributeTuple(rel, &attStruct, indstate, secid); } } @@ -666,7 +710,8 @@ InsertPgClassTuple(Relation pg_class_desc, Relation new_rel_desc, Oid new_rel_oid, Datum relacl, - Datum reloptions) + Datum reloptions, + Oid securityId) { Form_pg_class rd_rel = new_rel_desc->rd_rel; Datum values[Natts_pg_class]; @@ -696,6 +741,7 @@ InsertPgClassTuple(Relation pg_class_desc, values[Anum_pg_class_relnatts - 1] = Int16GetDatum(rd_rel->relnatts); values[Anum_pg_class_relchecks - 1] = Int16GetDatum(rd_rel->relchecks); values[Anum_pg_class_relhasoids - 1] = BoolGetDatum(rd_rel->relhasoids); + values[Anum_pg_class_relhassecids - 1] = BoolGetDatum(rd_rel->relhassecids); values[Anum_pg_class_relhaspkey - 1] = BoolGetDatum(rd_rel->relhaspkey); values[Anum_pg_class_relhasexclusion - 1] = BoolGetDatum(rd_rel->relhasexclusion); values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules); @@ -719,6 +765,9 @@ InsertPgClassTuple(Relation pg_class_desc, */ HeapTupleSetOid(tup, new_rel_oid); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, securityId); + /* finally insert the new tuple, update the indexes, and clean up */ simple_heap_insert(pg_class_desc, tup); @@ -743,9 +792,11 @@ AddNewRelationTuple(Relation pg_class_desc, Oid relowner, char relkind, Datum relacl, - Datum reloptions) + Datum reloptions, + Oid *secLabels) { Form_pg_class new_rel_reltup; + Oid secid; /* * first we update some of the information in our uncataloged relation's @@ -803,9 +854,11 @@ AddNewRelationTuple(Relation pg_class_desc, new_rel_desc->rd_att->tdtypeid = new_type_oid; + secid = (!secLabels ? InvalidOid : secLabels[0]); + /* Now build and insert the tuple */ InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, - relacl, reloptions); + relacl, reloptions, secid); } @@ -822,7 +875,8 @@ AddNewRelationType(const char *typeName, char new_rel_kind, Oid ownerid, Oid new_row_type, - Oid new_array_type) + Oid new_array_type, + Oid securityId) { return TypeCreate(new_row_type, /* optional predetermined OID */ @@ -854,7 +908,8 @@ AddNewRelationType(const char *typeName, 'x', /* fully TOASTable */ -1, /* typmod */ 0, /* array dimensions for typBaseType */ - false); /* Type NOT NULL */ + false, /* Type NOT NULL */ + securityId); /* security-id of the type */ } /* -------------------------------- @@ -903,7 +958,8 @@ heap_create_with_catalog(const char *relname, OnCommitAction oncommit, Datum reloptions, bool use_user_acl, - bool allow_system_table_mods) + bool allow_system_table_mods, + Oid *secLabels) { Relation pg_class_desc; Relation new_rel_desc; @@ -911,6 +967,7 @@ heap_create_with_catalog(const char *relname, Oid old_type_oid; Oid new_type_oid; Oid new_array_oid = InvalidOid; + Oid type_secid = InvalidOid; pg_class_desc = heap_open(RelationRelationId, RowExclusiveLock); @@ -1030,6 +1087,11 @@ heap_create_with_catalog(const char *relname, relkind == RELKIND_COMPOSITE_TYPE)) new_array_oid = AssignTypeArrayOid(); + /* security context of the relation type */ + if (secLabels) + type_secid = seclabelMoveSecid(TypeRelationId, + RelationRelationId, secLabels[0]); + /* * Since defining a relation also defines a complex type, we add a new * system type corresponding to the new relation. The OID of the type can @@ -1046,7 +1108,8 @@ heap_create_with_catalog(const char *relname, relkind, ownerid, reltypeid, - new_array_oid); + new_array_oid, + type_secid); /* * Now make the array type if wanted. @@ -1086,7 +1149,8 @@ heap_create_with_catalog(const char *relname, 'x', /* fully TOASTable */ -1, /* typmod */ 0, /* array dimensions for typBaseType */ - false); /* Type NOT NULL */ + false, /* Type NOT NULL */ + type_secid); /* security-id of the type */ pfree(relarrayname); } @@ -1106,13 +1170,14 @@ heap_create_with_catalog(const char *relname, ownerid, relkind, PointerGetDatum(relacl), - reloptions); + 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); + oidislocal, oidinhcount, secLabels); /* * Make a dependency link to force the relation to be deleted if its @@ -1590,6 +1655,11 @@ heap_drop_with_catalog(Oid relid) * delete relation tuple */ DeleteRelationTuple(relid); + + /* + * delete orphan pg_seclabel entries + */ + seclabelOnDropTable(relid); } diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 69946fe..23fb19b 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -39,6 +39,7 @@ #include "catalog/pg_constraint.h" #include "catalog/pg_operator.h" #include "catalog/pg_opclass.h" +#include "catalog/pg_seclabel.h" #include "catalog/pg_tablespace.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -90,7 +91,8 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation, Oid *classObjectId); static void InitializeAttributeOids(Relation indexRelation, int numatts, Oid indexoid); -static void AppendAttributeTuples(Relation indexRelation, int numatts); +static void AppendAttributeTuples(Relation indexRelation, + int numatts, Oid securityId); static void UpdateIndexRelation(Oid indexoid, Oid heapoid, IndexInfo *indexInfo, Oid *classOids, @@ -155,7 +157,7 @@ ConstructTupleDescriptor(Relation heapRelation, /* * allocate the new tuple descriptor */ - indexTupDesc = CreateTemplateTupleDesc(numatts, false); + indexTupDesc = CreateTemplateTupleDesc(numatts, false, false); /* * For simple index columns, we copy the pg_attribute row from the parent @@ -182,7 +184,8 @@ ConstructTupleDescriptor(Relation heapRelation, * here we are indexing on a system attribute (-1...-n) */ from = SystemAttributeDefinition(atnum, - heapRelation->rd_rel->relhasoids); + heapRelation->rd_rel->relhasoids, + heapRelation->rd_rel->relhassecids); } else { @@ -339,13 +342,16 @@ InitializeAttributeOids(Relation indexRelation, * ---------------------------------------------------------------- */ static void -AppendAttributeTuples(Relation indexRelation, int numatts) +AppendAttributeTuples(Relation indexRelation, int numatts, Oid securityId) { Relation pg_attribute; CatalogIndexState indstate; TupleDesc indexTupDesc; int i; + /* copy security id */ + securityId = seclabelMoveSecid(AttributeRelationId, + RelationRelationId, securityId); /* * open the attribute relation and its indexes */ @@ -367,7 +373,8 @@ AppendAttributeTuples(Relation indexRelation, int numatts) Assert(indexTupDesc->attrs[i]->attnum == i + 1); Assert(indexTupDesc->attrs[i]->attcacheoff == -1); - InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], indstate); + InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], + indstate, securityId); } CatalogCloseIndexes(indstate); @@ -545,6 +552,7 @@ index_create(Oid heapRelationId, bool mapped_relation; bool is_exclusion; Oid namespaceId; + Oid securityId; int i; is_exclusion = (indexInfo->ii_ExclusionOps != NULL); @@ -682,15 +690,21 @@ index_create(Oid heapRelationId, indexRelation->rd_rel->relam = accessMethodObjectId; indexRelation->rd_rel->relkind = RELKIND_INDEX; indexRelation->rd_rel->relhasoids = false; + indexRelation->rd_rel->relhassecids = false; indexRelation->rd_rel->relhasexclusion = is_exclusion; /* + * Index always has same security id of the relation to be indexed on. + */ + securityId = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(heapRelationId)); + + /* * store index's pg_class entry */ InsertPgClassTuple(pg_class, indexRelation, RelationGetRelid(indexRelation), (Datum) 0, - reloptions); + reloptions, securityId); /* done with pg_class */ heap_close(pg_class, RowExclusiveLock); @@ -706,7 +720,7 @@ index_create(Oid heapRelationId, /* * append ATTRIBUTE tuples for the index */ - AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs); + AppendAttributeTuples(indexRelation, indexInfo->ii_NumIndexAttrs, securityId); /* ---------------- * update pg_index diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 5581346..cf1910a 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -40,6 +40,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "parser/parse_func.h" +#include "sepgsql/hooks.h" #include "storage/backendid.h" #include "storage/ipc.h" #include "utils/acl.h" @@ -2311,6 +2312,9 @@ LookupExplicitNamespace(const char *nspname) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, nspname); + /* SELinux checks */ + sepgsql_schema_search(namespaceId, true); + return namespaceId; } @@ -2903,7 +2907,8 @@ recomputeNamespacePath(void) if (OidIsValid(namespaceId) && !list_member_oid(oidlist, namespaceId) && pg_namespace_aclcheck(namespaceId, roleid, - ACL_USAGE) == ACLCHECK_OK) + ACL_USAGE) == ACLCHECK_OK && + sepgsql_schema_search(namespaceId, false)) oidlist = lappend_oid(oidlist, namespaceId); } } @@ -2930,7 +2935,8 @@ recomputeNamespacePath(void) if (OidIsValid(namespaceId) && !list_member_oid(oidlist, namespaceId) && pg_namespace_aclcheck(namespaceId, roleid, - ACL_USAGE) == ACLCHECK_OK) + ACL_USAGE) == ACLCHECK_OK && + sepgsql_schema_search(namespaceId, false)) oidlist = lappend_oid(oidlist, namespaceId); } } @@ -2996,9 +3002,12 @@ InitTempTableNamespace(void) char namespaceName[NAMEDATALEN]; Oid namespaceId; Oid toastspaceId; + Oid secid; Assert(!OidIsValid(myTempNamespace)); + snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId); + /* * First, do permission check to see if we are authorized to make temp * tables. We use a nonstandard error message here since "databasename: @@ -3016,6 +3025,9 @@ InitTempTableNamespace(void) errmsg("permission denied to create temporary tables in database \"%s\"", get_database_name(MyDatabaseId)))); + /* SELinux checks */ + secid = sepgsql_schema_create(namespaceName, true); + /* * Do not allow a Hot Standby slave session to make temp tables. Aside * from problems with modifying the system catalogs, there is a naming @@ -3031,8 +3043,6 @@ InitTempTableNamespace(void) (errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION), errmsg("cannot create temporary tables during recovery"))); - snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId); - namespaceId = GetSysCacheOid1(NAMESPACENAME, CStringGetDatum(namespaceName)); if (!OidIsValid(namespaceId)) @@ -3045,7 +3055,9 @@ InitTempTableNamespace(void) * temp tables. This works because the places that access the temp * namespace for my own backend skip permissions checks on it. */ - namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); + namespaceId = NamespaceCreate(namespaceName, + BOOTSTRAP_SUPERUSERID, + secid); /* Advance command counter to make namespace visible */ CommandCounterIncrement(); } @@ -3070,7 +3082,9 @@ InitTempTableNamespace(void) CStringGetDatum(namespaceName)); if (!OidIsValid(toastspaceId)) { - toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); + toastspaceId = NamespaceCreate(namespaceName, + BOOTSTRAP_SUPERUSERID, + secid); /* Advance command counter to make namespace visible */ CommandCounterIncrement(); } diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 582d894..d7b6bd1 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -27,6 +27,7 @@ #include "parser/parse_coerce.h" #include "parser/parse_func.h" #include "parser/parse_oper.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -67,6 +68,7 @@ AggregateCreate(const char *aggName, Oid *fnArgs; int nargs_transfn; Oid procOid; + Oid procSecid; TupleDesc tupDesc; int i; ObjectAddress myself, @@ -161,6 +163,10 @@ AggregateCreate(const char *aggName, } Assert(OidIsValid(finaltype)); + /* SELinux checks */ + procSecid = sepgsql_aggregate_create(aggName, aggNamespace, + transfn, finalfn); + /* * If finaltype (i.e. aggregate return type) is polymorphic, inputs must * be polymorphic also, else parser will fail to deduce result type. @@ -229,7 +235,8 @@ AggregateCreate(const char *aggName, NIL, /* parameterDefaults */ PointerGetDatum(NULL), /* proconfig */ 1, /* procost */ - 0); /* prorows */ + 0, /* prorows */ + procSecid); /* security-id */ /* * Okay to create the pg_aggregate entry. diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c index 99085c9..b4c0b3a 100644 --- a/src/backend/catalog/pg_conversion.c +++ b/src/backend/catalog/pg_conversion.c @@ -40,7 +40,7 @@ Oid ConversionCreate(const char *conname, Oid connamespace, Oid conowner, int32 conforencoding, int32 contoencoding, - Oid conproc, bool def) + Oid conproc, bool def, Oid securityId) { int i; Relation rel; @@ -104,6 +104,8 @@ ConversionCreate(const char *conname, Oid connamespace, tup = heap_form_tuple(tupDesc, values, nulls); + HeapTupleSetSecid(tup, securityId); + /* insert a new tuple */ oid = simple_heap_insert(rel, tup); Assert(OidIsValid(oid)); diff --git a/src/backend/catalog/pg_largeobject.c b/src/backend/catalog/pg_largeobject.c index e3f18bf..572ce01 100644 --- a/src/backend/catalog/pg_largeobject.c +++ b/src/backend/catalog/pg_largeobject.c @@ -21,10 +21,13 @@ #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_authid.h" +#include "catalog/pg_description.h" #include "catalog/pg_largeobject.h" #include "catalog/pg_largeobject_metadata.h" +#include "catalog/pg_seclabel.h" #include "catalog/toasting.h" #include "miscadmin.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/bytea.h" #include "utils/fmgroids.h" @@ -40,7 +43,7 @@ * will appear to exist with size 0. */ Oid -LargeObjectCreate(Oid loid) +LargeObjectCreate(Oid loid, Oid securityId) { Relation pg_lo_meta; HeapTuple ntup; @@ -66,6 +69,8 @@ LargeObjectCreate(Oid loid) if (OidIsValid(loid)) HeapTupleSetOid(ntup, loid); + HeapTupleSetSecid(ntup, securityId); + loid_new = simple_heap_insert(pg_lo_meta, ntup); Assert(!OidIsValid(loid) || loid == loid_new); @@ -245,6 +250,64 @@ LargeObjectAlterOwner(Oid loid, Oid newOwnerId) } /* + * LargeObjectAlterSecLabel + * + * Implementation of ALTER LARGE OBJECT xxx SECURITY LABEL + */ +void +LargeObjectAlterSecLabel(Oid loid, char *new_label) +{ + Relation pg_lo_meta; + ScanKeyData skey; + SysScanDesc scan; + HeapTuple oldtup; + HeapTuple newtup; + Oid securityId; + + pg_lo_meta = heap_open(LargeObjectMetadataRelationId, + RowExclusiveLock); + + ScanKeyInit(&skey, + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(loid)); + + scan = systable_beginscan(pg_lo_meta, + LargeObjectMetadataOidIndexId, true, + SnapshotNow, 1, &skey); + + oldtup = systable_getnext(scan); + if (!HeapTupleIsValid(oldtup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("large object %u does not exist", loid))); + + /* Must be owner of the large object */ + if (!pg_largeobject_ownercheck(loid, GetUserId())) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be owner of large object %u", loid))); + + /* SELinux checks */ + securityId = sepgsql_largeobject_relabel(loid, new_label); + + /* update the tuple */ + newtup = heap_copytuple(oldtup); + + HeapTupleSetSecid(newtup, securityId); + + simple_heap_update(pg_lo_meta, &newtup->t_self, newtup); + + CatalogUpdateIndexes(pg_lo_meta, newtup); + + heap_freetuple(newtup); + + systable_endscan(scan); + + heap_close(pg_lo_meta, RowExclusiveLock); +} + +/* * LargeObjectExists * * We don't use the system cache to for large object metadata, for fear of diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c index 22111a3..cba7274 100644 --- a/src/backend/catalog/pg_namespace.c +++ b/src/backend/catalog/pg_namespace.c @@ -28,7 +28,7 @@ * --------------- */ Oid -NamespaceCreate(const char *nspName, Oid ownerId) +NamespaceCreate(const char *nspName, Oid ownerId, Oid secid) { Relation nspdesc; HeapTuple tup; @@ -64,6 +64,8 @@ NamespaceCreate(const char *nspName, Oid ownerId) tupDesc = nspdesc->rd_att; tup = heap_form_tuple(tupDesc, values, nulls); + if (HeapTupleHasSecid(tup)) + HeapTupleSetSecid(tup, secid); nspoid = simple_heap_insert(nspdesc, tup); Assert(OidIsValid(nspoid)); diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index 2362268..f6df55e 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -28,6 +28,7 @@ #include "catalog/pg_type.h" #include "miscadmin.h" #include "parser/parse_oper.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -204,6 +205,7 @@ OperatorShellMake(const char *operatorName, { Relation pg_operator_desc; Oid operatorObjectId; + Oid securityId; int i; HeapTuple tup; Datum values[Natts_pg_operator]; @@ -220,6 +222,12 @@ OperatorShellMake(const char *operatorName, errmsg("\"%s\" is not a valid operator name", operatorName))); + /* SELinux checks */ + securityId = sepgsql_operator_create(operatorName, InvalidOid, + operatorNamespace, + InvalidOid, InvalidOid, InvalidOid, + InvalidOid, InvalidOid); + /* * initialize our *nulls and *values arrays */ @@ -261,6 +269,8 @@ OperatorShellMake(const char *operatorName, */ tup = heap_form_tuple(tupDesc, values, nulls); + HeapTupleSetSecid(tup, securityId); + /* * insert our "shell" operator tuple */ @@ -340,6 +350,7 @@ OperatorCreate(const char *operatorName, bool replaces[Natts_pg_operator]; Datum values[Natts_pg_operator]; Oid operatorObjectId; + Oid securityId; bool operatorAlreadyDefined; Oid operResultType; Oid commutatorId, @@ -476,6 +487,12 @@ OperatorCreate(const char *operatorName, else negatorId = InvalidOid; + /* SELinux checks */ + securityId = sepgsql_operator_create(operatorName, operatorObjectId, + operatorNamespace, + procedureId, restrictionId, joinId, + commutatorId, negatorId); + /* * set up values in the operator tuple */ @@ -516,6 +533,8 @@ OperatorCreate(const char *operatorName, if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for operator %u", operatorObjectId); + if (securityId != HeapTupleHasSecid(tup)) + elog(ERROR, "Bug? security-id was mismatched."); tup = heap_modify_tuple(tup, RelationGetDescr(pg_operator_desc), @@ -530,6 +549,8 @@ OperatorCreate(const char *operatorName, tupDesc = pg_operator_desc->rd_att; tup = heap_form_tuple(tupDesc, values, nulls); + HeapTupleSetSecid(tup, securityId); + operatorObjectId = simple_heap_insert(pg_operator_desc, tup); } diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 3772c32..d99c351 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -84,7 +84,8 @@ ProcedureCreate(const char *procedureName, List *parameterDefaults, Datum proconfig, float4 procost, - float4 prorows) + float4 prorows, + Oid prosecid) { Oid retval; int parameterCount; @@ -364,6 +365,8 @@ ProcedureCreate(const char *procedureName, if (!pg_proc_ownercheck(HeapTupleGetOid(oldtup), proowner)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, procedureName); + if (prosecid != HeapTupleGetSecid(oldtup)) + elog(ERROR, "Bug? security-id was tried to be changed."); /* * Not okay to change the return type of the existing proc, since @@ -548,6 +551,9 @@ ProcedureCreate(const char *procedureName, nulls[Anum_pg_proc_proacl - 1] = true; tup = heap_form_tuple(tupDesc, values, nulls); + + HeapTupleSetSecid(tup, prosecid); + simple_heap_insert(rel, tup); is_update = false; } diff --git a/src/backend/catalog/pg_seclabel.c b/src/backend/catalog/pg_seclabel.c new file mode 100644 index 0000000..4816635 --- /dev/null +++ b/src/backend/catalog/pg_seclabel.c @@ -0,0 +1,646 @@ +/* + * pg_seclabel.c + * + * Portions Copyright (c) 1996-2010, 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/tupdesc.h" +#include "catalog/catalog.h" +#include "catalog/indexing.h" +#include "catalog/pg_aggregate.h" +#include "catalog/pg_amop.h" +#include "catalog/pg_amproc.h" +#include "catalog/pg_attrdef.h" +#include "catalog/pg_auth_members.h" +#include "catalog/pg_constraint.h" +#include "catalog/pg_db_role_setting.h" +#include "catalog/pg_depend.h" +#include "catalog/pg_description.h" +#include "catalog/pg_enum.h" +#include "catalog/pg_index.h" +#include "catalog/pg_inherits.h" +#include "catalog/pg_largeobject.h" +#include "catalog/pg_namespace.h" +#include "catalog/pg_rewrite.h" +#include "catalog/pg_seclabel.h" +#include "catalog/pg_shdepend.h" +#include "catalog/pg_shdescription.h" +#include "catalog/pg_trigger.h" +#include "catalog/pg_ts_config_map.h" +#include "executor/spi.h" +#include "miscadmin.h" +#include "sepgsql/sepgsql.h" +#include "sepgsql/hooks.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/tqual.h" + +bool ignore_security_label_input; + +void +seclabelOnCreateDatabase(Oid src_datid, Oid dst_datid) +{ + Relation rel; + ScanKeyData keys[1]; + SysScanDesc scan; + HeapTuple oldtup, newtup; + Datum values[Natts_pg_seclabel]; + bool nulls[Natts_pg_seclabel]; + bool replaces[Natts_pg_seclabel]; + + /* Scan all entries with pg_seclabel.datid = src_datid */ + ScanKeyInit(&keys[0], + Anum_pg_seclabel_datid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(src_datid)); + + rel = heap_open(SecLabelRelationId, RowExclusiveLock); + + scan = systable_beginscan(rel, SecLabelSecidIndexId, true, + SnapshotNow, 1, keys); + + /* corresponding entries will be inserted with new datid */ + memset(values, 0, sizeof(values)); + memset(nulls, false, sizeof(nulls)); + memset(replaces, false, sizeof(replaces)); + + values[Anum_pg_seclabel_datid - 1] = ObjectIdGetDatum(dst_datid); + replaces[Anum_pg_seclabel_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); +} + +void +seclabelOnDropDatabase(Oid datid) +{ + Relation rel; + ScanKeyData keys[1]; + SysScanDesc scan; + HeapTuple tuple; + + /* Scan all entries with pg_seclabel.datid = datid */ + ScanKeyInit(&keys[0], + Anum_pg_seclabel_datid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(datid)); + + rel = heap_open(SecLabelRelationId, RowExclusiveLock); + + scan = systable_beginscan(rel, SecLabelSecidIndexId, true, + SnapshotNow, 1, keys); + + while (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + simple_heap_delete(rel, &tuple->t_self); + } + + systable_endscan(scan); + + heap_close(rel, RowExclusiveLock); +} + +void +seclabelOnDropTable(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_seclabel_datid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(database_oid)); + ScanKeyInit(&key[1], + Anum_pg_seclabel_relid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + + rel = heap_open(SecLabelRelationId, RowExclusiveLock); + scan = systable_beginscan(rel, SecLabelLabelIndexId, true, + SnapshotNow, 2, key); + + while (HeapTupleIsValid(tuple = systable_getnext(scan))) + simple_heap_delete(rel, &tuple->t_self); + + systable_endscan(scan); + + heap_close(rel, RowExclusiveLock); +} + +/* + * XXX - it should be replaced by BKI scripts + */ +bool +seclabelCatalogHasSysAttr(Oid relOid) +{ + switch (relOid) + { + case AggregateRelationId: + /* pg_aggregate is property of pg_proc */ + case AccessMethodOperatorRelationId: + /* pg_amop is property of pg_opfamily */ + case AccessMethodProcedureRelationId: + /* pg_amproc is property of pg_opfamily */ + case AttrDefaultRelationId: + /* pg_attrdef is property of pg_attribute */ + case AuthMemRelationId: + /* pg_auth_members is property of pg_auth */ + case ConstraintRelationId: + /* + * CHECK constraint is property of pg_class + * DOMAIN constraint is property of pg_type + * Global assertion is property of pg_database + */ + case DbRoleSettingRelationId: + /* pg_db_role_setting is property of pg_auth or pg_database */ + case DependRelationId: + /* property of the depending object */ + case DescriptionRelationId: + /* property of the object commented on */ + case EnumRelationId: + /* pg_enum is property of pg_type */ + case IndexRelationId: + /* pg_index is property of pg_class with RELKIND_INDEX */ + case InheritsRelationId: + /* pg_inherits is property of the child relation */ + case LargeObjectRelationId: + /* pg_largeobject is data chunk of pg_largeobject_metadata */ + case RewriteRelationId: + /* pg_rewrite is property of pg_class */ + case SecLabelRelationId: + /* No security attribute has no security label */ + case SharedDependRelationId: + /* property of the depending shared object */ + case SharedDescriptionRelationId: + /* property of the shared object commented on */ + case TriggerRelationId: + /* pg_trigger is property of pg_class */ + case TSConfigMapRelationId: + /* pg_ts_config_map is property of pg_ts_config */ + return false; + + default: + return true; + } +} + +Oid * +seclabelMakeRelationDefaults(TupleDesc tupdesc, List *supOids) +{ + ListCell *l; + Oid *secLabels; + Oid securityId; + int index, attno, nitems; + + nitems = tupdesc->natts - FirstLowInvalidHeapAttributeNumber; + secLabels = palloc0(sizeof(Oid) * nitems); + + foreach (l, supOids) + { + Oid relOid = lfirst_oid(l); + + securityId = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); + + if (!OidIsValid(secLabels[0])) + secLabels[0] = securityId; + else if (!seclabelCompareSecid(RelationRelationId, secLabels[0], + RelationRelationId, securityId)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot inherit relations with different label"))); + + for (index = 1; index < nitems; index++) + { + attno = index + FirstLowInvalidHeapAttributeNumber; + + if (attno < 0) + securityId = GetSysCacheSecid2(ATTNUM, + ObjectIdGetDatum(relOid), + Int16GetDatum(attno)); + else + { + const char *attname = NameStr(tupdesc->attrs[attno]->attname); + securityId = GetSysCacheSecid2(ATTNAME, + ObjectIdGetDatum(relOid), + PointerGetDatum(attname)); + } + + if (!OidIsValid(securityId)) + continue; + + if (!OidIsValid(secLabels[index])) + secLabels[index] = securityId; + else if (!seclabelCompareSecid(AttributeRelationId, secLabels[index], + AttributeRelationId, securityId)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot inherit attribute with different label"))); + } + } + return secLabels; +} + +Oid * +seclabelMakeToastDefaults(TupleDesc tupdesc, Oid relOid) +{ + Oid *secLabels; + Oid securityId; + int index, nitems; + + nitems = tupdesc->natts + 1 - FirstLowInvalidHeapAttributeNumber; + secLabels = palloc0(sizeof(Oid) * nitems); + + securityId = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); + secLabels[0] = securityId; + + securityId = seclabelMoveSecid(AttributeRelationId, + RelationRelationId, + securityId); + for (index = 1; index < nitems; index++) + secLabels[index] = securityId; + + return secLabels; +} + +Oid +seclabelGetNewSecid(Relation rel, HeapTuple tuple) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled() && + !IsBootstrapProcessingMode()) + { + return sepgsql_get_default_secid(rel, tuple); + } +#endif + return InvalidOid; +} + +static Oid +inputSecurityLabel(Oid relid, const char *seclabel) +{ + LOCKMODE lockmode = AccessShareLock; + Relation rel; + ScanKeyData skey[3]; + SysScanDesc scan; + HeapTuple tuple; + Oid datid; + Oid secid; + Datum values[Natts_pg_seclabel]; + bool nulls[Natts_pg_seclabel]; + + datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); + +retry: + /* + * Lookup pg_seclabel first, then insert a new entry if not found. + * An exclusive lock is not necessary for the first read-only path, + * and we assume most of trials are read-only. + */ + rel = heap_open(SecLabelRelationId, lockmode); + + ScanKeyInit(&skey[0], + Anum_pg_seclabel_datid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(datid)); + ScanKeyInit(&skey[1], + Anum_pg_seclabel_relid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[2], + Anum_pg_seclabel_label, + BTEqualStrategyNumber, F_TEXTEQ, + CStringGetTextDatum(seclabel)); + + scan = systable_beginscan(rel, SecLabelLabelIndexId, true, + SnapshotToast, 3, skey); + + tuple = systable_getnext(scan); + if (HeapTupleIsValid(tuple)) + { + secid = ((Form_pg_seclabel) GETSTRUCT(tuple))->secid; + systable_endscan(scan); + heap_close(rel, lockmode); + return secid; + } + + /* + * If not exist, try to insert a new entry. + */ + if (lockmode == AccessShareLock) + { + systable_endscan(scan); + heap_close(rel, lockmode); + lockmode = RowExclusiveLock; + goto retry; + } + + memset(nulls, false, sizeof(nulls)); + secid = GetNewOidWithIndex(rel, SecLabelSecidIndexId, + Anum_pg_seclabel_secid); + values[Anum_pg_seclabel_secid - 1] = ObjectIdGetDatum(secid); + values[Anum_pg_seclabel_datid - 1] = ObjectIdGetDatum(datid); + values[Anum_pg_seclabel_relid - 1] = ObjectIdGetDatum(relid); + values[Anum_pg_seclabel_label - 1] = CStringGetTextDatum(seclabel); + + tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); + + simple_heap_insert(rel, tuple); + + CatalogUpdateIndexes(rel, tuple); + + systable_endscan(scan); + + heap_close(rel, lockmode); + + return secid; +} + +static char * +outputSecurityLabel(Oid relid, Oid secid) +{ + Relation rel; + ScanKeyData skey[3]; + SysScanDesc scan; + HeapTuple tuple; + Oid datid; + char *result = NULL; + + datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); + + /* + * Lookup pg_seclabel for the given datid/relid/secid + */ + rel = heap_open(SecLabelRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], + Anum_pg_seclabel_secid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(secid)); + ScanKeyInit(&skey[1], + Anum_pg_seclabel_datid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(datid)); + ScanKeyInit(&skey[2], + Anum_pg_seclabel_relid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relid)); + + scan = systable_beginscan(rel, SecLabelSecidIndexId, true, + SnapshotToast, 3, skey); + tuple = systable_getnext(scan); + if (HeapTupleIsValid(tuple)) + { + Datum datum; + bool isnull; + + datum = heap_getattr(tuple, + Anum_pg_seclabel_label, + RelationGetDescr(rel), &isnull); + if (!isnull) + result = TextDatumGetCString(datum); + } + systable_endscan(scan); + + heap_close(rel, AccessShareLock); + + return result; +} + +Oid +seclabelRawInput(Oid relid, char *seclabel) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + seclabel = sepgsql_rawlabel_in(seclabel); +#endif + return inputSecurityLabel(relid, seclabel); +} + +char * +seclabelRawOutput(Oid relid, Oid secid) +{ + char *seclabel = outputSecurityLabel(relid, secid); +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + seclabel = sepgsql_rawlabel_out(seclabel); +#endif + return seclabel; +} + +Oid +seclabelTransInput(Oid relid, char *seclabel) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + seclabel = sepgsql_mcstrans_in(seclabel); +#endif + return seclabelRawInput(relid, seclabel); +} + +char * +seclabelTransOutput(Oid relid, Oid secid) +{ + char *seclabel = seclabelRawOutput(relid, secid); +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + seclabel = sepgsql_mcstrans_out(seclabel); +#endif + return seclabel; +} + +Oid +seclabelMoveSecid(Oid dst_relid, Oid src_relid, Oid secid) +{ + char *label = seclabelRawOutput(src_relid, secid); + + if (!label) + return InvalidOid; + + secid = seclabelRawInput(dst_relid, label); + + pfree(label); + + return secid; +} + +bool +seclabelCompareSecid(Oid relid1, Oid secid1, Oid relid2, Oid secid2) +{ + char *label1 = seclabelRawOutput(relid1, secid1); + char *label2 = seclabelRawOutput(relid2, secid2); + bool retval = false; + + if (label1 && label2 && strcmp(label1, label2) == 0) + retval = true; + else if (!label1 && !label2) + retval = false; + + if (label1) + pfree(label1); + if (label2) + pfree(label2); + + return retval; +} + +Datum +seclabelSysattOutput(Oid relid, HeapTuple tuple) +{ + Oid secid; + char *seclabel; + + secid = HeapTupleGetSecid(tuple); + + seclabel = seclabelTransOutput(relid, secid); + if (!seclabel) + seclabel = "unlabeled"; + + return CStringGetTextDatum(seclabel); +} + +/* + * seclabelRelationReclaim + * + * It reclaims security labels already referenced to. + * It has to be called under the VACUUM FULL context that means the relation + * to be reclaimed is already locked exclusively. + */ +void +seclabelRelationReclaim(Oid relOid) +{ + StringInfoData query; + const char *nspname_reclaimed; + const char *relname_reclaimed; + const char *nspname_pg_seclabel; + const char *relname_pg_seclabel; + const char *attname_secid; + const char *attname_datid; + const char *attname_relid; + const char *attname_label; + const char *nspname_to_secid; + const char *proname_to_secid; + Oid databaseId; + Oid namespaceId; + int index; + int save_sepgsql_mode; + + if (SPI_connect() != SPI_OK_CONNECT) + elog(ERROR, "SPI_connect() failed"); + + /* + * DELETE orphan entries + */ + databaseId = (IsSharedRelation(relOid) ? InvalidOid : MyDatabaseId); + + namespaceId = get_rel_namespace(relOid); + nspname_reclaimed = get_namespace_name(namespaceId); + relname_reclaimed = get_rel_name(relOid); + + namespaceId = get_rel_namespace(SecLabelRelationId); + nspname_pg_seclabel = get_namespace_name(namespaceId); + relname_pg_seclabel = get_rel_name(SecLabelRelationId); + + attname_secid = get_attname(SecLabelRelationId, Anum_pg_seclabel_secid); + attname_datid = get_attname(SecLabelRelationId, Anum_pg_seclabel_datid); + attname_relid = get_attname(SecLabelRelationId, Anum_pg_seclabel_relid); + attname_label = get_attname(SecLabelRelationId, Anum_pg_seclabel_label); + + namespaceId = get_func_namespace(F_SECLABEL_TO_SECID); + nspname_to_secid = get_namespace_name(namespaceId); + proname_to_secid = get_func_name(F_SECLABEL_TO_SECID); + + initStringInfo(&query); + appendStringInfo(&query, + "DELETE FROM %s.%s " + "WHERE %s = %u AND %s = %u AND %s NOT IN " + "(SELECT %s.%s(%s) FROM ONLY %s.%s) " + "RETURNING %s,%s", + quote_identifier(nspname_pg_seclabel), + quote_identifier(relname_pg_seclabel), + quote_identifier(attname_datid), + databaseId, + quote_identifier(attname_relid), + relOid, + quote_identifier(attname_secid), + quote_identifier(nspname_to_secid), + quote_identifier(proname_to_secid), + quote_identifier(relname_reclaimed), + quote_identifier(nspname_reclaimed), + quote_identifier(relname_reclaimed), + quote_identifier(attname_secid), + quote_identifier(attname_label)); + /* + * Run the query + */ + elog(DEBUG1, "query: %s", query.data); + + save_sepgsql_mode = sepostgresql_mode; + + PG_TRY(); + { + sepostgresql_mode = SEPGSQL_MODE_INTERNAL; + + if (SPI_execute(query.data, false, 0) != SPI_OK_DELETE_RETURNING) + elog(ERROR, "Failed to run: %s", query.data); + } + PG_CATCH(); + { + sepostgresql_mode = save_sepgsql_mode; + + PG_RE_THROW(); + } + PG_END_TRY(); + + sepostgresql_mode = save_sepgsql_mode; + + for (index = 0; index < SPI_processed; index++) + { + HeapTuple tuple = SPI_tuptable->vals[index]; + char *label; + Oid secid; + Datum datum; + bool isnull; + + datum = heap_getattr(tuple, 1, SPI_tuptable->tupdesc, &isnull); + secid = (isnull ? InvalidOid : DatumGetObjectId(datum)); + + datum = heap_getattr(tuple, 2, SPI_tuptable->tupdesc, &isnull); + label = (isnull ? NULL : TextDatumGetCString(datum)); + + elog(DEBUG1, "seclabel: \"%s\"was reclaimed (secid=%u)", + label, secid); + } + + if (SPI_finish() != SPI_OK_FINISH) + elog(ERROR, "SPI_finish() failed"); +} + +Datum +seclabel_to_secid(PG_FUNCTION_ARGS) +{ + HeapTupleHeader htup = PG_GETARG_HEAPTUPLEHEADER(0); + + PG_RETURN_OID(HeapTupleHeaderGetSecid(htup)); +} diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index 76f9e06..0b4dcc3 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -25,6 +25,7 @@ #include "commands/typecmds.h" #include "miscadmin.h" #include "parser/scansup.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" @@ -57,10 +58,17 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) Datum values[Natts_pg_type]; bool nulls[Natts_pg_type]; Oid typoid; + Oid secid; NameData name; Assert(PointerIsValid(typeName)); + /* SELinux checks */ + secid = sepgsql_type_create(typeName, InvalidOid, + typeNamespace, TYPTYPE_PSEUDO, + F_SHELL_IN, F_SHELL_OUT, + InvalidOid, InvalidOid, + InvalidOid, InvalidOid, InvalidOid); /* * open pg_type */ @@ -126,6 +134,8 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) binary_upgrade_next_pg_type_oid = InvalidOid; } + HeapTupleSetSecid(tup, secid); + /* * insert the tuple in the relation and get the tuple's oid. */ @@ -204,7 +214,8 @@ TypeCreate(Oid newTypeOid, char storage, int32 typeMod, int32 typNDims, /* Array dimensions for baseType */ - bool typeNotNull) + bool typeNotNull, + Oid securityId) { Relation pg_type_desc; Oid typeObjectId; @@ -389,6 +400,8 @@ TypeCreate(Oid newTypeOid, */ if (((Form_pg_type) GETSTRUCT(tup))->typowner != ownerId) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, typeName); + if (HeapTupleGetSecid(tup) != securityId) + elog(ERROR, "Bug? security-id was mismatched"); /* trouble if caller wanted to force the OID */ if (OidIsValid(newTypeOid)) @@ -425,6 +438,8 @@ TypeCreate(Oid newTypeOid, } /* else allow system to assign oid */ + HeapTupleSetSecid(tup, securityId); + typeObjectId = simple_heap_insert(pg_type_desc, tup); } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 435dfdd..5d86354 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -24,6 +24,7 @@ #include "catalog/namespace.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" +#include "catalog/pg_seclabel.h" #include "catalog/pg_type.h" #include "catalog/toasting.h" #include "miscadmin.h" @@ -127,6 +128,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio int16 coloptions[2]; ObjectAddress baseobject, toastobject; + Oid *secLabels; /* * Toast table is shared if and only if its parent is. @@ -168,7 +170,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio "pg_toast_%u_index", relOid); /* this is pretty painful... need a tuple descriptor */ - tupdesc = CreateTemplateTupleDesc(3, false); + tupdesc = CreateTemplateTupleDesc(3, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "chunk_id", OIDOID, @@ -206,6 +208,11 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio binary_upgrade_next_pg_type_toast_oid = InvalidOid; } + /* + * Toast inherits security-id from the heap relation + */ + secLabels = seclabelMakeToastDefaults(tupdesc, relOid); + toast_relid = heap_create_with_catalog(toast_relname, namespaceid, rel->rd_rel->reltablespace, @@ -223,7 +230,8 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio ONCOMMIT_NOOP, reloptions, false, - true); + true, + secLabels); /* make the toast relation visible, else index creation will fail */ CommandCounterIncrement(); diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c index 4964fb3..390a1c1 100644 --- a/src/backend/commands/aggregatecmds.c +++ b/src/backend/commands/aggregatecmds.c @@ -32,6 +32,7 @@ #include "miscadmin.h" #include "parser/parse_func.h" #include "parser/parse_type.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -307,6 +308,9 @@ RenameAggregate(List *name, List *args, const char *newname) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* SELinux checks */ + sepgsql_proc_alter_rename(procOid, newname); + /* rename */ namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname); simple_heap_update(rel, &tup->t_self, tup); diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index 17e1e77..c1c8bfd 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -289,3 +289,64 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) (int) stmt->objectType); } } + +/* + * ExecAlterSecLabelStmt + * + * Execute ALTER xxx SECURITY LABEL TO statement + */ +void +ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt) +{ + const char *name; + char *seclabel; + + Assert(IsA(stmt->secLabel, String)); + seclabel = strVal(stmt->secLabel); + + switch (stmt->objectType) + { + case OBJECT_DATABASE: + name = strVal(linitial(stmt->object)); + AlterDatabaseSecLabel(name, seclabel); + break; + + case OBJECT_SCHEMA: + name = strVal(linitial(stmt->object)); + AlterSchemaSecLabel(name, seclabel); + break; + + case OBJECT_TABLE: + case OBJECT_SEQUENCE: + case OBJECT_VIEW: + case OBJECT_COLUMN: + AlterRelationSecLabel(stmt->relation, stmt->addname, + stmt->objectType, seclabel); + break; + + case OBJECT_AGGREGATE: + AlterFunctionSecLabel(stmt->object, stmt->objarg, true, seclabel); + break; + + case OBJECT_FUNCTION: + AlterFunctionSecLabel(stmt->object, stmt->objarg, false, seclabel); + break; + + case OBJECT_LARGEOBJECT: + LargeObjectAlterSecLabel(intVal(linitial(stmt->object)), seclabel); + break; + + case OBJECT_TYPE: + case OBJECT_DOMAIN: + AlterTypeSecLabel(stmt->object, seclabel); + break; + + case OBJECT_TABLESPACE: + AlterTableSpaceSecLabel(strVal(linitial(stmt->object)), seclabel); + break; + + default: + elog(ERROR, "unrecognized AlterSecLabelStmt type: %d", + (int) stmt->objectType); + } +} diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 30a00ab..da39665 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -30,12 +30,14 @@ #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_namespace.h" +#include "catalog/pg_seclabel.h" #include "catalog/toasting.h" #include "commands/cluster.h" #include "commands/tablecmds.h" #include "commands/trigger.h" #include "commands/vacuum.h" #include "miscadmin.h" +#include "sepgsql/hooks.h" #include "storage/bufmgr.h" #include "storage/procarray.h" #include "storage/smgr.h" @@ -116,6 +118,9 @@ cluster(ClusterStmt *stmt, bool isTopLevel) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(rel)); + /* SELinux checks */ + sepgsql_relation_cluster(tableOid, true); + /* * Reject clustering a remote temp table ... their local buffer * manager is not going to cope. @@ -290,7 +295,8 @@ cluster_rel(Oid tableOid, Oid indexOid, bool recheck, bool verbose, Form_pg_index indexForm; /* Check that the user still owns the relation */ - if (!pg_class_ownercheck(tableOid, GetUserId())) + if (!pg_class_ownercheck(tableOid, GetUserId()) || + !sepgsql_relation_cluster(tableOid, false)) { relation_close(OldHeap, AccessExclusiveLock); return; @@ -632,6 +638,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace) HeapTuple tuple; Datum reloptions; bool isNull; + Oid *secLabels; OldHeap = heap_open(OIDOldHeap, AccessExclusiveLock); OldHeapDesc = RelationGetDescr(OldHeap); @@ -657,6 +664,11 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace) reloptions = (Datum) 0; /* + * The new heap copies all the security-id from the original + */ + secLabels = seclabelMakeRelationDefaults(tupdesc, + list_make1_oid(OIDOldHeap)); + /* * Create the new heap, using a temporary name in the same namespace as * the existing table. NOTE: there is some risk of collision with user * relnames. Working around this seems more trouble than it's worth; in @@ -687,7 +699,8 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace) ONCOMMIT_NOOP, reloptions, false, - true); + true, + secLabels); ReleaseSysCache(tuple); @@ -994,6 +1007,9 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex, /* Preserve OID, if any */ if (NewHeap->rd_rel->relhasoids) HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple)); + /* Preserve security-id, if any */ + if (NewHeap->rd_rel->relhassecids) + HeapTupleSetSecid(copiedTuple, HeapTupleGetSecid(tuple)); /* The heap rewrite module does the rest */ rewrite_heap_tuple(rwstate, tuple, copiedTuple); @@ -1481,7 +1497,8 @@ get_tables_to_cluster(MemoryContext cluster_context) { index = (Form_pg_index) GETSTRUCT(indexTuple); - if (!pg_class_ownercheck(index->indrelid, GetUserId())) + if (!pg_class_ownercheck(index->indrelid, GetUserId()) || + !sepgsql_relation_cluster(index->indrelid, false)) continue; /* diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c index 64792f2..6d71642 100644 --- a/src/backend/commands/comment.c +++ b/src/backend/commands/comment.c @@ -49,6 +49,7 @@ #include "parser/parse_func.h" #include "parser/parse_oper.h" #include "parser/parse_type.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" @@ -548,6 +549,9 @@ CommentRelation(int objtype, List *relname, char *comment) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(relation)); + /* SELinux checks */ + sepgsql_relation_comment(RelationGetRelid(relation)); + /* Next, verify that the relation type matches the intent */ switch (objtype) @@ -651,6 +655,9 @@ CommentAttribute(List *qualname, char *comment) errmsg("column \"%s\" of relation \"%s\" does not exist", attrname, RelationGetRelationName(relation)))); + /* SELinux checks */ + sepgsql_attribute_comment(RelationGetRelid(relation), attnum); + /* Create the comment using the relation's oid */ CreateComments(RelationGetRelid(relation), RelationRelationId, (int32) attnum, comment); @@ -704,6 +711,9 @@ CommentDatabase(List *qualname, char *comment) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, database); + /* SELinux checks */ + sepgsql_database_comment(oid); + /* Call CreateSharedComments() to create/drop the comments */ CreateSharedComments(oid, DatabaseRelationId, comment); } @@ -742,6 +752,9 @@ CommentTablespace(List *qualname, char *comment) if (!pg_tablespace_ownercheck(oid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, tablespace); + /* SELinux checks */ + sepgsql_tablespace_comment(oid); + /* Call CreateSharedComments() to create/drop the comments */ CreateSharedComments(oid, TableSpaceRelationId, comment); } @@ -774,6 +787,9 @@ CommentRole(List *qualname, char *comment) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be member of role \"%s\" to comment upon it", role))); + /* SELinux checks */ + sepgsql_role_comment(oid); + /* Call CreateSharedComments() to create/drop the comments */ CreateSharedComments(oid, AuthIdRelationId, comment); } @@ -810,6 +826,9 @@ CommentNamespace(List *qualname, char *comment) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, namespace); + /* SELinux checks */ + sepgsql_schema_comment(oid); + /* Call CreateComments() to create/drop the comments */ CreateComments(oid, NamespaceRelationId, 0, comment); } @@ -919,6 +938,9 @@ CommentRule(List *qualname, char *comment) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, get_rel_name(reloid)); + /* SELinux checks */ + sepgsql_rule_comment(reloid, rulename); + /* Call CreateComments() to create/drop the comments */ CreateComments(ruleoid, RewriteRelationId, 0, comment); @@ -953,6 +975,9 @@ CommentType(List *typename, char *comment) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, format_type_be(oid)); + /* SELinux checks */ + sepgsql_type_comment(oid); + /* Call CreateComments() to create/drop the comments */ CreateComments(oid, TypeRelationId, 0, comment); } @@ -977,6 +1002,9 @@ CommentAggregate(List *aggregate, List *arguments, char *comment) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(aggregate)); + /* SELinux checks */ + sepgsql_proc_comment(oid); + /* Call CreateComments() to create/drop the comments */ CreateComments(oid, ProcedureRelationId, 0, comment); } @@ -1005,6 +1033,9 @@ CommentProc(List *function, List *arguments, char *comment) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(function)); + /* SELinux checks */ + sepgsql_proc_comment(oid); + /* Call CreateComments() to create/drop the comments */ CreateComments(oid, ProcedureRelationId, 0, comment); } @@ -1036,6 +1067,9 @@ CommentOperator(List *opername, List *arguments, char *comment) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, NameListToString(opername)); + /* SELinux checks */ + sepgsql_operator_comment(oid); + /* Call CreateComments() to create/drop the comments */ CreateComments(oid, OperatorRelationId, 0, comment); } @@ -1080,6 +1114,9 @@ CommentTrigger(List *qualname, char *comment) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(relation)); + /* SELinux checks */ + sepgsql_trigger_comment(RelationGetRelid(relation), trigname); + /* * Fetch the trigger tuple from pg_trigger. There can be only one because * of the unique index. @@ -1153,6 +1190,9 @@ CommentConstraint(List *qualname, char *comment) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(relation)); + /* SELinux checks */ + sepgsql_constraint_comment(RelationGetRelid(relation), conName); + conOid = GetConstraintByName(RelationGetRelid(relation), conName); /* Call CreateComments() to create/drop the comments */ @@ -1188,6 +1228,9 @@ CommentConversion(List *qualname, char *comment) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, NameListToString(qualname)); + /* SELinux checks */ + sepgsql_conversion_comment(conversionOid); + /* Call CreateComments() to create/drop the comments */ CreateComments(conversionOid, ConversionRelationId, 0, comment); } @@ -1304,6 +1347,9 @@ CommentOpClass(List *qualname, List *arguments, char *comment) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, NameListToString(qualname)); + /* SELinux checks */ + sepgsql_opclass_comment(opcID); + ReleaseSysCache(tuple); /* Call CreateComments() to create/drop the comments */ @@ -1385,6 +1431,9 @@ CommentOpFamily(List *qualname, List *arguments, char *comment) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, NameListToString(qualname)); + /* SELinux checks */ + sepgsql_opfamily_comment(opfID); + ReleaseSysCache(tuple); /* Call CreateComments() to create/drop the comments */ @@ -1505,6 +1554,9 @@ CommentCast(List *qualname, List *arguments, char *comment) format_type_be(sourcetypeid), format_type_be(targettypeid)))); + /* SELinux checks */ + sepgsql_cast_comment(sourcetypeid, targettypeid); + ReleaseSysCache(tuple); /* Call CreateComments() to create/drop the comments */ @@ -1522,6 +1574,8 @@ CommentTSParser(List *qualname, char *comment) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to comment on text search parser"))); + /* SELinux checks */ + sepgsql_ts_parser_comment(prsId); CreateComments(prsId, TSParserRelationId, 0, comment); } @@ -1536,6 +1590,8 @@ CommentTSDictionary(List *qualname, char *comment) if (!pg_ts_dict_ownercheck(dictId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, NameListToString(qualname)); + /* SELinux checks */ + sepgsql_ts_dict_comment(dictId); CreateComments(dictId, TSDictionaryRelationId, 0, comment); } @@ -1551,6 +1607,8 @@ CommentTSTemplate(List *qualname, char *comment) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to comment on text search template"))); + /* SELinux checks */ + sepgsql_ts_template_comment(tmplId); CreateComments(tmplId, TSTemplateRelationId, 0, comment); } @@ -1565,6 +1623,8 @@ CommentTSConfiguration(List *qualname, char *comment) if (!pg_ts_config_ownercheck(cfgId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, NameListToString(qualname)); + /* SELinux checks */ + sepgsql_ts_config_comment(cfgId); CreateComments(cfgId, TSConfigRelationId, 0, comment); } diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c index 57ddab0..0c10a64 100644 --- a/src/backend/commands/conversioncmds.c +++ b/src/backend/commands/conversioncmds.c @@ -24,6 +24,7 @@ #include "mb/pg_wchar.h" #include "miscadmin.h" #include "parser/parse_func.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -45,6 +46,7 @@ CreateConversionCommand(CreateConversionStmt *stmt) 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; @@ -96,6 +98,10 @@ CreateConversionCommand(CreateConversionStmt *stmt) 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 @@ -114,7 +120,7 @@ CreateConversionCommand(CreateConversionStmt *stmt) * name) */ ConversionCreate(conversion_name, namespaceId, GetUserId(), - from_encoding, to_encoding, funcoid, stmt->def); + from_encoding, to_encoding, funcoid, stmt->def, secid); } /* @@ -174,6 +180,9 @@ DropConversionsCommand(DropStmt *drop) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, NameStr(con->conname)); + /* SELinux checks */ + sepgsql_conversion_drop(conversionOid, false); + object.classId = ConversionRelationId; object.objectId = conversionOid; object.objectSubId = 0; @@ -235,6 +244,9 @@ RenameConversion(List *name, const char *newname) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* SELinux checks */ + sepgsql_conversion_alter_rename(conversionOid, newname); + /* rename */ namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname); simple_heap_update(rel, &tup->t_self, tup); @@ -329,6 +341,8 @@ AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(convForm->connamespace)); } + /* SELinux checks */ + sepgsql_conversion_alter(HeapTupleGetOid(tup)); /* * Modify the owner --- okay to scribble on tup because it's a copy diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 84a83f1..7cf7a89 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -22,7 +22,10 @@ #include "access/heapam.h" #include "access/xact.h" +#include "access/sysattr.h" +#include "catalog/heap.h" #include "catalog/namespace.h" +#include "catalog/pg_seclabel.h" #include "catalog/pg_type.h" #include "commands/copy.h" #include "commands/defrem.h" @@ -35,6 +38,7 @@ #include "optimizer/planner.h" #include "parser/parse_relation.h" #include "rewrite/rewriteHandler.h" +#include "sepgsql/hooks.h" #include "storage/fd.h" #include "tcop/tcopprot.h" #include "utils/acl.h" @@ -161,6 +165,12 @@ typedef struct CopyStateData char *raw_buf; int raw_buf_index; /* next byte to process */ int raw_buf_len; /* total # of bytes stored */ + + /* + * Dump/Restore support for security_label + */ + FmgrInfo seclabel_out_function; + bool seclabel_force_quot; } CopyStateData; typedef CopyStateData *CopyState; @@ -244,7 +254,7 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0"; /* non-export function prototypes */ static void DoCopyTo(CopyState cstate); static void CopyTo(CopyState cstate); -static void CopyOneRowTo(CopyState cstate, Oid tupleOid, +static void CopyOneRowTo(CopyState cstate, HeapTuple tuple, Datum *values, bool *nulls); static void CopyFrom(CopyState cstate); static bool CopyReadLine(CopyState cstate); @@ -988,6 +998,10 @@ DoCopy(const CopyStmt *stmt, const char *queryString) if (stmt->relation) { + Bitmapset *columnsSet = NULL; + List *attnums; + ListCell *cur; + Assert(!stmt->query); cstate->queryDesc = NULL; @@ -998,16 +1012,20 @@ DoCopy(const CopyStmt *stmt, const char *queryString) tupDesc = RelationGetDescr(cstate->rel); /* Check relation permissions. */ + attnums = CopyGetAttnums(tupDesc, cstate->rel, attnamelist); + foreach(cur, attnums) + { + int index = lfirst_int(cur) + - FirstLowInvalidHeapAttributeNumber; + columnsSet = bms_add_member(columnsSet, index); + } + relPerms = pg_class_aclmask(RelationGetRelid(cstate->rel), GetUserId(), required_access, ACLMASK_ALL); remainingPerms = required_access & ~relPerms; if (remainingPerms != 0) { /* We don't have table permissions, check per-column permissions */ - List *attnums; - ListCell *cur; - - attnums = CopyGetAttnums(tupDesc, cstate->rel, attnamelist); foreach(cur, attnums) { int attnum = lfirst_int(cur); @@ -1021,6 +1039,13 @@ DoCopy(const CopyStmt *stmt, const char *queryString) } } + if (is_from) + sepgsql_relation_perms(RelationGetRelid(cstate->rel), + ACL_INSERT, NULL, columnsSet, true); + else + sepgsql_relation_perms(RelationGetRelid(cstate->rel), + ACL_SELECT, columnsSet, NULL, true); + /* check read-only transaction */ if (XactReadOnly && is_from && !cstate->rel->rd_islocaltemp) PreventCommandIfReadOnly("COPY FROM"); @@ -1130,11 +1155,24 @@ DoCopy(const CopyStmt *stmt, const char *queryString) int attnum = lfirst_int(cur); if (!list_member_int(cstate->attnumlist, attnum)) + { + Form_pg_attribute attForm; + + if (attnum > 0) + attForm = tupDesc->attrs[attnum - 1]; + else + attForm = SystemAttributeDefinition(attnum, true, true); + ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", - NameStr(tupDesc->attrs[attnum - 1]->attname)))); - cstate->force_quote_flags[attnum - 1] = true; + NameStr(attForm->attname)))); + } + + if (attnum == SecurityLabelAttributeNumber) + cstate->seclabel_force_quot = true; + else + cstate->force_quote_flags[attnum - 1] = true; } } @@ -1152,10 +1190,24 @@ DoCopy(const CopyStmt *stmt, const char *queryString) int attnum = lfirst_int(cur); if (!list_member_int(cstate->attnumlist, attnum)) + { + Form_pg_attribute attForm; + + if (attnum > 0) + attForm = tupDesc->attrs[attnum - 1]; + else + attForm = SystemAttributeDefinition(attnum, + tupDesc->tdhasoid, + tupDesc->tdhassecid); + ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", - NameStr(tupDesc->attrs[attnum - 1]->attname)))); + NameStr(attForm->attname)))); + } + /* ignore system columns, if specified */ + if (attnum <= 0) + continue; cstate->force_notnull_flags[attnum - 1] = true; } } @@ -1347,16 +1399,29 @@ CopyTo(CopyState cstate) int attnum = lfirst_int(cur); Oid out_func_oid; bool isvarlena; + FmgrInfo *out_fmgr; + Form_pg_attribute attForm; + + if (attnum == SecurityLabelAttributeNumber) + { + attForm = SystemAttributeDefinition(attnum, true, true); + out_fmgr = &cstate->seclabel_out_function; + } + else + { + attForm = attr[attnum - 1]; + out_fmgr = &cstate->out_functions[attnum - 1]; + } if (cstate->binary) - getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid, + getTypeBinaryOutputInfo(attForm->atttypid, &out_func_oid, &isvarlena); else - getTypeOutputInfo(attr[attnum - 1]->atttypid, + getTypeOutputInfo(attForm->atttypid, &out_func_oid, &isvarlena); - fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); + fmgr_info(out_func_oid, out_fmgr); } /* @@ -1411,7 +1476,17 @@ CopyTo(CopyState cstate) CopySendChar(cstate, cstate->delim[0]); hdr_delim = true; - colname = NameStr(attr[attnum - 1]->attname); + if (SystemAttributeWritable(attnum, + tupDesc->tdhasoid, + tupDesc->tdhassecid)) + { + Form_pg_attribute attForm + = SystemAttributeDefinition(attnum, true, true); + + colname = NameStr(attForm->attname); + } + else + colname = NameStr(attr[attnum - 1]->attname); CopyAttributeOutCSV(cstate, colname, false, list_length(cstate->attnumlist) == 1); @@ -1441,7 +1516,7 @@ CopyTo(CopyState cstate) heap_deform_tuple(tuple, tupDesc, values, nulls); /* Format and send the data */ - CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls); + CopyOneRowTo(cstate, tuple, values, nulls); } heap_endscan(scandesc); @@ -1467,13 +1542,17 @@ CopyTo(CopyState cstate) * Emit one row during CopyTo(). */ static void -CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) +CopyOneRowTo(CopyState cstate, HeapTuple tuple, Datum *values, bool *nulls) { bool need_delim = false; FmgrInfo *out_functions = cstate->out_functions; MemoryContext oldcontext; ListCell *cur; char *string; + Oid tupleOid = InvalidOid; + + if (HeapTupleIsValid(tuple)) + tupleOid = HeapTupleGetOid(tuple); MemoryContextReset(cstate->rowcontext); oldcontext = MemoryContextSwitchTo(cstate->rowcontext); @@ -1506,8 +1585,10 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); - Datum value = values[attnum - 1]; - bool isnull = nulls[attnum - 1]; + Datum value; + bool isnull; + bool force_quot; + FmgrInfo *out_fmgr; if (!cstate->binary) { @@ -1516,6 +1597,21 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) need_delim = true; } + if (attnum == SecurityLabelAttributeNumber) + { + value = seclabelSysattOutput(RelationGetRelid(cstate->rel), tuple); + isnull = false; + force_quot = cstate->seclabel_force_quot; + out_fmgr = &cstate->seclabel_out_function; + } + else + { + value = values[attnum - 1]; + isnull = nulls[attnum - 1]; + force_quot = cstate->force_quote_flags[attnum - 1]; + out_fmgr = &out_functions[attnum - 1]; + } + if (isnull) { if (!cstate->binary) @@ -1527,11 +1623,9 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) { if (!cstate->binary) { - string = OutputFunctionCall(&out_functions[attnum - 1], - value); + string = OutputFunctionCall(out_fmgr, value); if (cstate->csv_mode) - CopyAttributeOutCSV(cstate, string, - cstate->force_quote_flags[attnum - 1], + CopyAttributeOutCSV(cstate, string, force_quot, list_length(cstate->attnumlist) == 1); else CopyAttributeOutText(cstate, string); @@ -1540,8 +1634,7 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) { bytea *outputbytes; - outputbytes = SendFunctionCall(&out_functions[attnum - 1], - value); + outputbytes = SendFunctionCall(out_fmgr, value); CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); CopySendData(cstate, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); @@ -1675,8 +1768,10 @@ CopyFrom(CopyState cstate) num_defaults; FmgrInfo *in_functions; FmgrInfo oid_in_function; + FmgrInfo seclabel_in_function; Oid *typioparams; Oid oid_typioparam; + Oid seclabel_typioparam; int attnum; int i; Oid in_func_oid; @@ -1919,6 +2014,19 @@ CopyFrom(CopyState cstate) fmgr_info(in_func_oid, &oid_in_function); } + if (list_member_int(cstate->attnumlist, SecurityLabelAttributeNumber)) + { + if (!cstate->binary) + getTypeInputInfo(TEXTOID, &in_func_oid, &seclabel_typioparam); + else + getTypeBinaryInputInfo(TEXTOID, &in_func_oid, &seclabel_typioparam); + + fmgr_info(in_func_oid, &seclabel_in_function); + } + + + + values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); nulls = (bool *) palloc(num_phys_attrs * sizeof(bool)); @@ -1953,6 +2061,7 @@ CopyFrom(CopyState cstate) { bool skip_tuple; Oid loaded_oid = InvalidOid; + Oid loaded_secid = InvalidOid; CHECK_FOR_INTERRUPTS(); @@ -2024,14 +2133,20 @@ CopyFrom(CopyState cstate) /* Loop to read the user attributes on the line. */ foreach(cur, cstate->attnumlist) { + Form_pg_attribute attForm; int attnum = lfirst_int(cur); int m = attnum - 1; + if (attnum == SecurityLabelAttributeNumber) + attForm = SystemAttributeDefinition(attnum, true, true); + else + attForm = attr[m]; + if (fieldno >= fldct) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("missing data for column \"%s\"", - NameStr(attr[m]->attname)))); + NameStr(attForm->attname)))); string = field_strings[fieldno++]; if (cstate->csv_mode && string == NULL && @@ -2041,14 +2156,38 @@ CopyFrom(CopyState cstate) string = cstate->null_print; } - cstate->cur_attname = NameStr(attr[m]->attname); + cstate->cur_attname = NameStr(attForm->attname); cstate->cur_attval = string; - values[m] = InputFunctionCall(&in_functions[m], - string, - typioparams[m], - attr[m]->atttypmod); - if (string != NULL) - nulls[m] = false; + + if (attnum == SecurityLabelAttributeNumber) + { + if (string && !ignore_security_label_input) + { + Datum datum = InputFunctionCall(&seclabel_in_function, + string, + seclabel_typioparam, + attForm->atttypmod); + loaded_secid + = seclabelTransInput(RelationGetRelid(cstate->rel), + TextDatumGetCString(datum)); + } + } + else + { + 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; + } cstate->cur_attname = NULL; cstate->cur_attval = NULL; } @@ -2094,17 +2233,38 @@ CopyFrom(CopyState cstate) i = 0; foreach(cur, cstate->attnumlist) { + Form_pg_attribute attForm; int attnum = lfirst_int(cur); int m = attnum - 1; - cstate->cur_attname = NameStr(attr[m]->attname); + if (attnum == SecurityLabelAttributeNumber) + attForm = SystemAttributeDefinition(attnum, true, true); + else + attForm = attr[m]; + + cstate->cur_attname = NameStr(attForm->attname); i++; - values[m] = CopyReadBinaryAttribute(cstate, - i, - &in_functions[m], - typioparams[m], - attr[m]->atttypmod, - &nulls[m]); + + if (attnum == SecurityLabelAttributeNumber) + { + Datum datum = CopyReadBinaryAttribute(cstate, i, + &seclabel_in_function, + seclabel_typioparam, + attForm->atttypmod, + &isnull); + if (!isnull && !ignore_security_label_input) + loaded_secid + = seclabelTransInput(RelationGetRelid(cstate->rel), + TextDatumGetCString(datum)); + } + else + { + values[m] = CopyReadBinaryAttribute(cstate, i, + &in_functions[m], + typioparams[m], + attr[m]->atttypmod, + &nulls[m]); + } cstate->cur_attname = NULL; } } @@ -2125,6 +2285,8 @@ CopyFrom(CopyState cstate) if (cstate->oids && file_has_oids) HeapTupleSetOid(tuple, loaded_oid); + if (HeapTupleHasSecid(tuple)) + HeapTupleSetSecid(tuple, loaded_secid); /* Triggers and stuff need to be invoked in query context. */ MemoryContextSwitchTo(oldcontext); @@ -2149,6 +2311,9 @@ CopyFrom(CopyState cstate) } if (!skip_tuple) + sepgsql_tuple_insert(cstate->rel, tuple); + + if (!skip_tuple) { List *recheckIndexes = NIL; @@ -3442,6 +3607,17 @@ CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist) } if (attnum == InvalidAttrNumber) { + Form_pg_attribute attForm; + bool hasoid = tupDesc->tdhasoid; + bool hassecid = tupDesc->tdhassecid; + + attForm = SystemAttributeByName(name, hasoid, hassecid); + if (attForm && + SystemAttributeWritable(attForm->attnum, hasoid, hassecid)) + attnum = attForm->attnum; + } + if (attnum == InvalidAttrNumber) + { if (rel != NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), @@ -3489,7 +3665,7 @@ copy_dest_receive(TupleTableSlot *slot, DestReceiver *self) slot_getallattrs(slot); /* And send the data */ - CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull); + CopyOneRowTo(cstate, slot->tts_tuple, slot->tts_values, slot->tts_isnull); } /* diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index e7dac22..16360db 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -35,6 +35,8 @@ #include "catalog/pg_authid.h" #include "catalog/pg_database.h" #include "catalog/pg_db_role_setting.h" +#include "catalog/pg_seclabel.h" +#include "catalog/pg_shdescription.h" #include "catalog/pg_tablespace.h" #include "commands/comment.h" #include "commands/dbcommands.h" @@ -43,6 +45,7 @@ #include "miscadmin.h" #include "pgstat.h" #include "postmaster/bgwriter.h" +#include "sepgsql/hooks.h" #include "storage/bufmgr.h" #include "storage/fd.h" #include "storage/lmgr.h" @@ -111,6 +114,7 @@ createdb(const CreatedbStmt *stmt) Datum new_record[Natts_pg_database]; bool new_record_nulls[Natts_pg_database]; Oid dboid; + Oid dbsecid; Oid datdba; ListCell *option; DefElem *dtablespacename = NULL; @@ -486,6 +490,9 @@ createdb(const CreatedbStmt *stmt) /* Note there is no additional permission check in this path */ } + /* SELinux permission checks */ + dbsecid = sepgsql_database_create(dbname, src_dboid); + /* * Check for db name conflict. This is just to give a more friendly error * message than "unique index violation". There's a race condition but @@ -560,6 +567,8 @@ createdb(const CreatedbStmt *stmt) new_record, new_record_nulls); HeapTupleSetOid(tuple, dboid); + if (HeapTupleHasSecid(tuple)) + HeapTupleSetSecid(tuple, dbsecid); simple_heap_insert(pg_database_rel, tuple); @@ -576,6 +585,9 @@ createdb(const CreatedbStmt *stmt) /* Create pg_shdepend entries for objects within database */ copyTemplateDependencies(src_dboid, dboid); + /* Create pg_seclabel entries for objects within database */ + seclabelOnCreateDatabase(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 @@ -777,6 +789,9 @@ dropdb(const char *dbname, bool missing_ok) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname); + /* SELinux checks */ + sepgsql_database_drop(db_id, false); + /* * Disallow dropping a DB that is marked istemplate. This is just to * prevent people from accidentally dropping template0 or template1; they @@ -833,6 +848,11 @@ dropdb(const char *dbname, bool missing_ok) dropDatabaseDependencies(db_id); /* + * Remove pg_seclabel entries for the database + */ + seclabelOnDropDatabase(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... @@ -915,6 +935,9 @@ RenameDatabase(const char *oldname, const char *newname) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to rename database"))); + /* SELinux checks */ + sepgsql_database_alter(db_id); + /* * Make sure the new name doesn't exist. See notes for same error in * CREATE DATABASE. @@ -1053,6 +1076,9 @@ movedb(const char *dbname, const char *tblspcname) (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("pg_global cannot be used as default tablespace"))); + /* SELinux checks */ + sepgsql_database_alter(db_id); + /* * No-op if same tablespace */ @@ -1369,6 +1395,9 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, stmt->dbname); + /* SELinux checks */ + sepgsql_database_alter(HeapTupleGetOid(tuple)); + /* * Build an updated tuple, perusing the information just obtained */ @@ -1419,6 +1448,9 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, stmt->dbname); + /* SELinux checks */ + sepgsql_database_alter(datid); + AlterSetting(datid, InvalidOid, stmt->setstmt); UnlockSharedObject(DatabaseRelationId, datid, 0, AccessShareLock); @@ -1494,6 +1526,9 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to change owner of database"))); + /* SELinux checks */ + sepgsql_database_alter(HeapTupleGetOid(tuple)); + memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); @@ -1533,6 +1568,58 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) heap_close(rel, NoLock); } +/* + * ALTER DATABASE SECURITY LABEL TO + */ +void +AlterDatabaseSecLabel(const char *dbname, char *new_label) +{ + Relation rel; + HeapTuple oldtup; + HeapTuple newtup; + ScanKeyData skey; + SysScanDesc scan; + Oid databaseId; + Oid securityId; + + /* Fetch the old tuple */ + rel = heap_open(DatabaseRelationId, RowExclusiveLock); + ScanKeyInit(&skey, + Anum_pg_database_datname, + BTEqualStrategyNumber, F_NAMEEQ, + NameGetDatum(dbname)); + scan = systable_beginscan(rel, DatabaseNameIndexId, true, + SnapshotNow, 1, &skey); + oldtup = systable_getnext(scan); + if (!HeapTupleIsValid(oldtup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_DATABASE), + errmsg("database \"%s\" does not exist", dbname))); + + newtup = heap_copytuple(oldtup); + + systable_endscan(scan); + + databaseId = HeapTupleGetOid(newtup); + + /* DAC permission checks */ + if (!pg_database_ownercheck(databaseId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname); + + /* SELinux checks */ + securityId = sepgsql_database_relabel(databaseId, new_label); + + /* Update it */ + HeapTupleSetSecid(newtup, securityId); + + simple_heap_update(rel, &newtup->t_self, newtup); + + CatalogUpdateIndexes(rel, newtup); + + heap_freetuple(newtup); + + heap_close(rel, RowExclusiveLock); +} /* * Helper functions diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 0bda24a..334b123 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -257,7 +257,7 @@ ExplainResultDesc(ExplainStmt *stmt) } /* Need a tuple descriptor representing a single TEXT or XML column */ - tupdesc = CreateTemplateTupleDesc(1, false); + tupdesc = CreateTemplateTupleDesc(1, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN", xml ? XMLOID : TEXTOID, -1, 0); return tupdesc; diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c index 14356a2..d1f255e 100644 --- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -27,6 +27,7 @@ #include "foreign/foreign.h" #include "miscadmin.h" #include "parser/parse_func.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -230,6 +231,9 @@ AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId) fdwId = HeapTupleGetOid(tup); form = (Form_pg_foreign_data_wrapper) GETSTRUCT(tup); + /* SELinux checks */ + sepgsql_fdw_alter(fdwId, InvalidOid); + if (form->fdwowner != newOwnerId) { form->fdwowner = newOwnerId; @@ -294,6 +298,8 @@ AlterForeignServerOwner(const char *name, Oid newOwnerId) aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); } } + /* SELinux checks */ + sepgsql_fserver_alter(srvId); form->srvowner = newOwnerId; @@ -339,6 +345,7 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) Oid fdwvalidator; Datum fdwoptions; Oid ownerId; + Oid securityId; /* Must be super user */ if (!superuser()) @@ -391,8 +398,13 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) else nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true; + /* SELinux checks */ + securityId = sepgsql_fdw_create(stmt->fdwname, fdwvalidator); + tuple = heap_form_tuple(rel->rd_att, values, nulls); + HeapTupleSetSecid(tuple, securityId); + fdwId = simple_heap_insert(rel, tuple); CatalogUpdateIndexes(rel, tuple); @@ -511,6 +523,8 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt) repl_repl[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true; } + /* SELinux checks */ + sepgsql_fdw_alter(fdwId, fdwvalidator); /* Everything looks good - update the tuple */ @@ -559,6 +573,8 @@ RemoveForeignDataWrapper(DropFdwStmt *stmt) stmt->fdwname))); return; } + /* SELinux checks */ + sepgsql_fdw_drop(fdwId, false); /* * Do the deletion @@ -608,6 +624,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt) HeapTuple tuple; Oid srvId; Oid ownerId; + Oid securityId; AclResult aclresult; ObjectAddress myself; ObjectAddress referenced; @@ -635,6 +652,9 @@ CreateForeignServer(CreateForeignServerStmt *stmt) if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); + /* SELinux checks */ + securityId = sepgsql_fserver_create(stmt->servername, fdw->fdwid); + /* * Insert tuple into pg_foreign_server. */ @@ -678,6 +698,8 @@ CreateForeignServer(CreateForeignServerStmt *stmt) tuple = heap_form_tuple(rel->rd_att, values, nulls); + HeapTupleSetSecid(tuple, securityId); + srvId = simple_heap_insert(rel, tuple); CatalogUpdateIndexes(rel, tuple); @@ -732,6 +754,9 @@ AlterForeignServer(AlterForeignServerStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, stmt->servername); + /* SELinux checks */ + sepgsql_fserver_alter(srvId); + memset(repl_val, 0, sizeof(repl_val)); memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); @@ -823,6 +848,8 @@ RemoveForeignServer(DropForeignServerStmt *stmt) if (!pg_foreign_server_ownercheck(srvId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, stmt->servername); + /* SELinux checks */ + sepgsql_fserver_drop(srvId, false); object.classId = ForeignServerRelationId; object.objectId = srvId; @@ -896,6 +923,7 @@ CreateUserMapping(CreateUserMappingStmt *stmt) HeapTuple tuple; Oid useId; Oid umId; + Oid securityId; ObjectAddress myself; ObjectAddress referenced; ForeignServer *srv; @@ -908,6 +936,9 @@ CreateUserMapping(CreateUserMappingStmt *stmt) user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername); + /* SELinux checks */ + securityId = sepgsql_user_mapping_create(useId, srv->serverid); + /* * Check that the user mapping is unique within server. */ @@ -947,6 +978,8 @@ CreateUserMapping(CreateUserMappingStmt *stmt) tuple = heap_form_tuple(rel->rd_att, values, nulls); + HeapTupleSetSecid(tuple, securityId); + umId = simple_heap_insert(rel, tuple); CatalogUpdateIndexes(rel, tuple); @@ -1000,6 +1033,9 @@ AlterUserMapping(AlterUserMappingStmt *stmt) user_mapping_ddl_aclcheck(useId, srv->serverid, stmt->servername); + /* SELinux checks */ + sepgsql_user_mapping_alter(umId); + tp = SearchSysCacheCopy1(USERMAPPINGOID, ObjectIdGetDatum(umId)); if (!HeapTupleIsValid(tp)) @@ -1114,6 +1150,9 @@ RemoveUserMapping(DropUserMappingStmt *stmt) user_mapping_ddl_aclcheck(useId, srv->serverid, srv->servername); + /* SELinux checks */ + sepgsql_user_mapping_drop(umId, false); + /* * Do the deletion */ diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 9a584ed..f914280 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -43,6 +43,7 @@ #include "catalog/pg_namespace.h" #include "catalog/pg_proc.h" #include "catalog/pg_proc_fn.h" +#include "catalog/pg_seclabel.h" #include "catalog/pg_type.h" #include "catalog/pg_type_fn.h" #include "commands/defrem.h" @@ -53,6 +54,7 @@ #include "parser/parse_expr.h" #include "parser/parse_func.h" #include "parser/parse_type.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" @@ -788,6 +790,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) ArrayType *parameterNames; List *parameterDefaults; Oid requiredResultType; + Oid replacedFunc = InvalidOid; + Oid prosecid; bool isWindowFunc, isStrict, security; @@ -933,6 +937,18 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) errmsg("ROWS is not applicable when function does not return a set"))); /* + * SELinux checks + */ + if (stmt->replace) + replacedFunc = GetSysCacheOid3(PROCNAMEARGSNSP, + PointerGetDatum(funcname), + PointerGetDatum(parameterTypes), + ObjectIdGetDatum(namespaceId)); + + prosecid = sepgsql_proc_create(funcname, replacedFunc, + namespaceId, languageOid); + + /* * And now that we have all the parameters, and know we're permitted to do * so, go ahead and create the function. */ @@ -957,7 +973,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) parameterDefaults, PointerGetDatum(proconfig), procost, - prorows); + prorows, + prosecid); } @@ -999,6 +1016,9 @@ RemoveFunction(RemoveFuncStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(functionName)); + /* SELinux checks */ + sepgsql_proc_drop(funcOid, false); + if (((Form_pg_proc) GETSTRUCT(tup))->proisagg) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), @@ -1135,6 +1155,9 @@ RenameFunction(List *name, List *argtypes, const char *newname) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* SELinux checks */ + sepgsql_proc_alter_rename(procOid, newname); + /* rename */ namestrcpy(&(procForm->proname), newname); simple_heap_update(rel, &tup->t_self, tup); @@ -1239,6 +1262,8 @@ AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(procForm->pronamespace)); } + /* SELinux checks */ + sepgsql_proc_alter(procOid); memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); @@ -1277,6 +1302,49 @@ AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) } /* + * ALTER FUNCTION f(...) SECURITY LABEL TO + */ +void +AlterFunctionSecLabel(List *name, List *argtypes, bool isagg, char *new_label) +{ + Relation rel; + HeapTuple tuple; + Oid procOid; + Oid securityId; + + /* open pg_proc system catalog */ + rel = heap_open(ProcedureRelationId, RowExclusiveLock); + + /* get function OID */ + if (isagg) + procOid = LookupAggNameTypeNames(name, argtypes, false); + else + procOid = LookupFuncNameTypeNames(name, argtypes, false); + + tuple = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for function %u", procOid); + + /* Must be owner */ + if (!pg_proc_ownercheck(procOid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + get_func_name(procOid)); + + /* SELinux checks */ + securityId = sepgsql_proc_relabel(procOid, new_label); + + /* Update it */ + HeapTupleSetSecid(tuple, securityId); + + simple_heap_update(rel, &tuple->t_self, tuple); + CatalogUpdateIndexes(rel, tuple); + + heap_freetuple(tuple); + + 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). @@ -1313,6 +1381,9 @@ AlterFunction(AlterFunctionStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(stmt->func->funcname)); + /* SELinux checks */ + sepgsql_proc_alter(funcOid); + if (procForm->proisagg) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), @@ -1659,6 +1730,9 @@ CreateCast(CreateCastStmt *stmt) errmsg("array data types are not binary-compatible"))); } + /* SELinux checks */ + sepgsql_cast_create(sourcetypeid, targettypeid, castmethod, funcid); + /* * Allow source and target types to be same only for length coercion * functions. We assume a multi-arg function does length coercion. @@ -1795,6 +1869,9 @@ DropCast(DropCastStmt *stmt) format_type_be(sourcetypeid), format_type_be(targettypeid)))); + /* SELinux checks */ + sepgsql_cast_drop(sourcetypeid, targettypeid, false); + /* * Do the deletion */ @@ -1873,6 +1950,9 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg, /* get schema OID and check its permissions */ nspOid = LookupCreationNamespace(newschema); + /* SELinux checks */ + sepgsql_proc_alter_schema(procOid, nspOid); + if (oldNspOid == nspOid) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 94657b8..7cb3634 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -39,6 +39,7 @@ #include "parser/parse_func.h" #include "parser/parse_oper.h" #include "parser/parsetree.h" +#include "sepgsql/hooks.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/procarray.h" @@ -242,6 +243,10 @@ DefineIndex(RangeVar *heapRelation, get_tablespace_name(tablespaceId)); } + /* SELinux checks */ + if (check_rights) + sepgsql_index_create(relationId, namespaceId); + /* * Force shared indexes into the pg_global tablespace. This is a bit of a * hack but seems simpler than marking them in the BKI commands. On the @@ -363,7 +368,9 @@ DefineIndex(RangeVar *heapRelation, errmsg("primary keys cannot be expressions"))); /* System attributes are never null, so no problem */ - if (SystemAttributeByName(key->name, rel->rd_rel->relhasoids)) + if (SystemAttributeByName(key->name, + rel->rd_rel->relhasoids, + rel->rd_rel->relhassecids)) continue; atttuple = SearchSysCacheAttName(relationId, key->name); @@ -1572,6 +1579,9 @@ ReindexIndex(RangeVar *indexRelation) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, indexRelation->relname); + /* SELinux checks */ + sepgsql_index_reindex(indOid); + ReleaseSysCache(tuple); reindex_index(indOid, false); @@ -1604,6 +1614,9 @@ ReindexTable(RangeVar *relation) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, relation->relname); + /* SELinux checks */ + sepgsql_relation_reindex(heapOid); + ReleaseSysCache(tuple); if (!reindex_relation(heapOid, true, false)) @@ -1642,6 +1655,9 @@ ReindexDatabase(const char *databaseName, bool do_system, bool do_user) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, databaseName); + /* SELinux checks */ + sepgsql_database_reindex(MyDatabaseId); + /* * Create a memory context that will survive forced transaction commits we * do below. Since it is a child of PortalContext, it will go away diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c index 283947a..657ff95 100644 --- a/src/backend/commands/lockcmds.c +++ b/src/backend/commands/lockcmds.c @@ -20,6 +20,7 @@ #include "commands/lockcmds.h" #include "miscadmin.h" #include "parser/parse_clause.h" +#include "sepgsql/hooks.h" #include "storage/lmgr.h" #include "utils/acl.h" #include "utils/lsyscache.h" @@ -149,6 +150,9 @@ LockTableRecurse(Oid reloid, RangeVar *rv, errmsg("\"%s\" is not a table", RelationGetRelationName(rel)))); + /* SELinux checks */ + sepgsql_relation_lock(rel); + /* * If requested, recurse to children. We use find_inheritance_children * not find_all_inheritors to avoid taking locks far in advance of diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c index ac0270f..25b84fd 100644 --- a/src/backend/commands/opclasscmds.c +++ b/src/backend/commands/opclasscmds.c @@ -35,6 +35,7 @@ #include "parser/parse_func.h" #include "parser/parse_oper.h" #include "parser/parse_type.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" @@ -164,7 +165,8 @@ OpClassCacheLookup(Oid amID, List *opclassname) * Caller must have done permissions checks etc. already. */ static Oid -CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid) +CreateOpFamily(char *amname, char *opfname, + Oid namespaceoid, Oid amoid, Oid securityId) { Oid opfamilyoid; Relation rel; @@ -204,6 +206,8 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid) tup = heap_form_tuple(rel->rd_att, values, nulls); + HeapTupleSetSecid(tup, securityId); + opfamilyoid = simple_heap_insert(rel, tup); CatalogUpdateIndexes(rel, tup); @@ -369,11 +373,16 @@ DefineOpClass(CreateOpClassStmt *stmt) } else { + Oid securityId; + + /* SELinux checks */ + securityId = sepgsql_opfamily_create(opcname, namespaceoid, amoid); + /* * Create it ... again no need for more permissions ... */ opfamilyoid = CreateOpFamily(stmt->amname, opcname, - namespaceoid, amoid); + namespaceoid, amoid, securityId); } } @@ -505,6 +514,12 @@ DefineOpClass(CreateOpClassStmt *stmt) stmt->amname))); } + /* SELinux checks */ + sepgsql_opfamily_alter(opfamilyoid, false, amoid, + operators, procedures); + sepgsql_opclass_create(opcname, namespaceoid, + typeoid, opfamilyoid, storageoid); + rel = heap_open(OperatorClassRelationId, RowExclusiveLock); /* @@ -650,6 +665,7 @@ DefineOpFamily(CreateOpFamilyStmt *stmt) NameData opfName; ObjectAddress myself, referenced; + Oid securityId; /* Convert list of names to a name and namespace */ namespaceoid = QualifiedNameGetCreationNamespace(stmt->opfamilyname, @@ -701,6 +717,9 @@ DefineOpFamily(CreateOpFamilyStmt *stmt) errmsg("operator family \"%s\" for access method \"%s\" already exists", opfname, stmt->amname))); + /* SELinux checks */ + securityId = sepgsql_opfamily_create(opfname, namespaceoid, amoid); + /* * Okay, let's create the pg_opfamily entry. */ @@ -715,6 +734,8 @@ DefineOpFamily(CreateOpFamilyStmt *stmt) tup = heap_form_tuple(rel->rd_att, values, nulls); + HeapTupleSetSecid(tup, securityId); + opfamilyoid = simple_heap_insert(rel, tup); CatalogUpdateIndexes(rel, tup); @@ -925,6 +946,9 @@ AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid, break; } } + /* SELinux checks */ + sepgsql_opfamily_alter(opfamilyoid, false, amoid, + operators, procedures); /* * Add tuples to pg_amop and pg_amproc tying in the operators and @@ -1002,6 +1026,9 @@ AlterOpFamilyDrop(List *opfamilyname, Oid amoid, Oid opfamilyoid, } } + /* SELinux checks */ + sepgsql_opfamily_alter(opfamilyoid, true, amoid, operators, procedures); + /* * Remove tuples from pg_amop and pg_amproc. */ @@ -1522,6 +1549,9 @@ RemoveOpClass(RemoveOpClassStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, NameListToString(stmt->opclassname)); + /* SELinux checks */ + sepgsql_opclass_drop(opcID, false); + ReleaseSysCache(tuple); /* @@ -1583,6 +1613,9 @@ RemoveOpFamily(RemoveOpFamilyStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPFAMILY, NameListToString(stmt->opfamilyname)); + /* SELinux checks */ + sepgsql_opfamily_drop(opfID, false); + ReleaseSysCache(tuple); /* @@ -1781,6 +1814,9 @@ RenameOpClass(List *name, const char *access_method, const char *newname) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* SELinux checks */ + sepgsql_opclass_alter_rename(opcOid, newname); + /* rename */ namestrcpy(&(((Form_pg_opclass) GETSTRUCT(tup))->opcname), newname); simple_heap_update(rel, &tup->t_self, tup); @@ -1875,6 +1911,9 @@ RenameOpFamily(List *name, const char *access_method, const char *newname) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* SELinux checks */ + sepgsql_opfamily_alter_rename(opfOid, newname); + /* rename */ namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname); simple_heap_update(rel, &tup->t_self, tup); @@ -1990,6 +2029,8 @@ AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } + /* SELinux checks */ + sepgsql_opclass_alter(HeapTupleGetOid(tup)); /* * Modify the owner --- okay to scribble on tup because it's a copy @@ -2112,7 +2153,8 @@ AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } - + /* SELinux checks */ + sepgsql_opfamily_alter_owner(HeapTupleGetOid(tup), newOwnerId); /* * Modify the owner --- okay to scribble on tup because it's a copy */ diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index 9c07cf3..2e19448 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -45,6 +45,7 @@ #include "parser/parse_func.h" #include "parser/parse_oper.h" #include "parser/parse_type.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/lsyscache.h" #include "utils/rel.h" @@ -319,6 +320,9 @@ RemoveOperator(RemoveFuncStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPER, NameListToString(operatorName)); + /* SELinux checks */ + sepgsql_operator_drop(operOid, false); + ReleaseSysCache(tup); /* @@ -426,6 +430,8 @@ AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(oprForm->oprnamespace)); } + /* SELinux checks */ + sepgsql_operator_alter(operOid); /* * Modify the owner --- okay to scribble on tup because it's a copy diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index e765382..a54dd3f 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -759,7 +759,7 @@ pg_prepared_statement(PG_FUNCTION_ARGS) * build tupdesc for result tuples. This must match the definition of the * pg_prepared_statements view in system_views.sql */ - tupdesc = CreateTemplateTupleDesc(5, false); + tupdesc = CreateTemplateTupleDesc(5, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement", diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index 633a093..a3375b2 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -29,6 +29,7 @@ #include "miscadmin.h" #include "parser/parse_func.h" #include "parser/parser.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" @@ -145,7 +146,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) NIL, PointerGetDatum(NULL), 1, - 0); + 0, + InvalidOid); } /* @@ -180,7 +182,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) NIL, PointerGetDatum(NULL), 1, - 0); + 0, + InvalidOid); } } else @@ -218,7 +221,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) NIL, PointerGetDatum(NULL), 1, - 0); + 0, + InvalidOid); } } else diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c index b0a9a22..57951fe 100644 --- a/src/backend/commands/schemacmds.c +++ b/src/backend/commands/schemacmds.c @@ -21,10 +21,12 @@ #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_namespace.h" +#include "catalog/pg_seclabel.h" #include "commands/dbcommands.h" #include "commands/schemacmds.h" #include "miscadmin.h" #include "parser/parse_utilcmd.h" +#include "sepgsql/hooks.h" #include "tcop/utility.h" #include "utils/acl.h" #include "utils/builtins.h" @@ -49,6 +51,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) Oid owner_uid; Oid saved_uid; int save_sec_context; + Oid secid; AclResult aclresult; GetUserIdAndSecContext(&saved_uid, &save_sec_context); @@ -75,6 +78,9 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) check_is_member_of_role(saved_uid, owner_uid); + /* SELinux checks */ + secid = sepgsql_schema_create(schemaName, false); + /* Additional check to protect reserved schema names */ if (!allowSystemTableMods && IsReservedName(schemaName)) ereport(ERROR, @@ -95,7 +101,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) save_sec_context | SECURITY_LOCAL_USERID_CHANGE); /* Create the schema's namespace */ - namespaceId = NamespaceCreate(schemaName, owner_uid); + namespaceId = NamespaceCreate(schemaName, owner_uid, secid); /* Advance cmd counter to make the namespace visible */ CommandCounterIncrement(); @@ -204,6 +210,9 @@ RemoveSchemas(DropStmt *drop) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, namespaceName); + /* SELinux checks */ + sepgsql_schema_drop(namespaceId, false); + object.classId = NamespaceRelationId; object.objectId = namespaceId; object.objectSubId = 0; @@ -288,6 +297,9 @@ RenameSchema(const char *oldname, const char *newname) errmsg("unacceptable schema name \"%s\"", newname), errdetail("The prefix \"pg_\" is reserved for system schemas."))); + /* SELinux checks */ + sepgsql_schema_alter(HeapTupleGetOid(tup)); + /* rename */ namestrcpy(&(((Form_pg_namespace) GETSTRUCT(tup))->nspname), newname); simple_heap_update(rel, &tup->t_self, tup); @@ -389,6 +401,9 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) aclcheck_error(aclresult, ACL_KIND_DATABASE, get_database_name(MyDatabaseId)); + /* SELinux checks */ + sepgsql_schema_alter(HeapTupleGetOid(tup)); + memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); @@ -423,3 +438,43 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) } } + +/* + * ALTER SCHEMA SECURITY LABEL TO + */ +void +AlterSchemaSecLabel(const char *name, char *new_label) +{ + Relation rel; + HeapTuple tuple; + Oid namespaceId; + Oid securityId; + + /* open pg_namespace relation */ + rel = heap_open(NamespaceRelationId, RowExclusiveLock); + tuple = SearchSysCacheCopy1(NAMESPACENAME, + CStringGetDatum(name)); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_SCHEMA), + errmsg("schema \"%s\" does not exist", name))); + namespaceId = HeapTupleGetOid(tuple); + + /* DAC permission check */ + if (!pg_namespace_ownercheck(namespaceId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, name); + + /* SELinux checks */ + securityId = sepgsql_schema_relabel(namespaceId, new_label); + + /* Update it */ + HeapTupleSetSecid(tuple, securityId); + + simple_heap_update(rel, &tuple->t_self, tuple); + + CatalogUpdateIndexes(rel, tuple); + + heap_freetuple(tuple); + + heap_close(rel, RowExclusiveLock); +} diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index f52e1d8..ecff05d 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -26,6 +26,7 @@ #include "commands/tablecmds.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "sepgsql/hooks.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/proc.h" @@ -331,6 +332,9 @@ AlterSequence(AlterSeqStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, stmt->sequence->relname); + /* SELinux checks */ + sepgsql_relation_alter(relid); + /* do the work */ AlterSequenceInternal(relid, stmt->options); } @@ -469,6 +473,9 @@ nextval_internal(Oid relid) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); + /* SELinux checks */ + sepgsql_sequence_next_value(elm->relid); + /* read-only transactions may only modify temp sequences */ if (!seqrel->rd_islocaltemp) PreventCommandIfReadOnly("nextval()"); @@ -668,6 +675,9 @@ currval_oid(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); + /* SELinux checks */ + sepgsql_sequence_get_value(elm->relid); + if (!elm->last_valid) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), @@ -710,6 +720,9 @@ lastval(PG_FUNCTION_ARGS) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); + /* SELinux checks */ + sepgsql_sequence_get_value(last_used_seq->relid); + result = last_used_seq->last; relation_close(seqrel, NoLock); @@ -746,6 +759,9 @@ do_setval(Oid relid, int64 next, bool iscalled) errmsg("permission denied for sequence %s", RelationGetRelationName(seqrel)))); + /* SELinux checks */ + sepgsql_sequence_set_value(elm->relid); + /* read-only transactions may only modify temp sequences */ if (!seqrel->rd_islocaltemp) PreventCommandIfReadOnly("setval()"); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 25b2807..cbe483b 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -32,6 +32,7 @@ #include "catalog/pg_inherits_fn.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" +#include "catalog/pg_seclabel.h" #include "catalog/pg_tablespace.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" @@ -62,12 +63,14 @@ #include "parser/parser.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" +#include "sepgsql/hooks.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/smgr.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" +#include "utils/guc.h" #include "utils/inval.h" #include "utils/lsyscache.h" #include "utils/memutils.h" @@ -223,7 +226,7 @@ static const struct dropmsgstrings dropmsgstringarray[] = { static void truncate_check_rel(Relation rel); static List *MergeAttributes(List *schema, List *supers, bool istemp, - List **supOids, List **supconstr, int *supOidCount); + List **supOids, List **supconstr, int *supOidCount, int *supSecidCount); static bool MergeCheckConstraint(List *constraints, char *name, Node *expr); static bool change_varattnos_walker(Node *node, const AttrNumber *newattno); static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel); @@ -271,10 +274,12 @@ static void ATOneLevelRecursion(List **wqueue, Relation rel, static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd); static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel, - ColumnDef *colDef, bool isOid); + ColumnDef *colDef, bool isOid, bool isSecid); static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid); static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd); +static void ATPrepAddSecLabel(List **wqueue, Relation rel, bool recurse, + AlterTableCmd *cmd); static void ATExecDropNotNull(Relation rel, const char *colName); static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, const char *colName); @@ -355,6 +360,7 @@ DefineRelation(CreateStmt *stmt, char relkind) List *old_constraints; bool localHasOids; int parentOidCount; + int parentSecidCount; List *rawDefaults; List *cookedDefaults; Datum reloptions; @@ -362,6 +368,7 @@ DefineRelation(CreateStmt *stmt, char relkind) AttrNumber attnum; static char *validnsps[] = HEAP_RELOPT_NAMESPACES; Oid ofTypeId; + Oid *secLabels; /* * Truncate relname to appropriate length (probably a waste of time, as @@ -461,7 +468,8 @@ DefineRelation(CreateStmt *stmt, char relkind) */ schema = MergeAttributes(schema, stmt->inhRelations, stmt->relation->istemp, - &inheritOids, &old_constraints, &parentOidCount); + &inheritOids, &old_constraints, + &parentOidCount, &parentSecidCount); /* * Create a tuple descriptor from the relation schema. Note that this @@ -473,6 +481,16 @@ DefineRelation(CreateStmt *stmt, char relkind) localHasOids = interpretOidsOption(stmt->options); descriptor->tdhasoid = (localHasOids || parentOidCount > 0); + if ((relkind == RELKIND_RELATION && default_with_secids) || parentSecidCount > 0) + descriptor->tdhassecid = true; + + /* SELinux permission checks */ + secLabels = sepgsql_relation_create(relname, + relkind, + descriptor, + namespaceId, + inheritOids, + false); /* * Find columns with default values and prepare for insertion of the * defaults. Pre-cooked (that is, inherited) defaults go into a list of @@ -546,7 +564,8 @@ DefineRelation(CreateStmt *stmt, char relkind) stmt->oncommit, reloptions, true, - allowSystemTableMods); + allowSystemTableMods, + secLabels); StoreCatalogInheritance(relationId, inheritOids); @@ -755,6 +774,9 @@ RemoveRelations(DropStmt *drop) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, rel->relname); + /* SELinux checks */ + sepgsql_relation_drop(relOid, false); + if (!allowSystemTableMods && IsSystemClass(classform)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), @@ -918,6 +940,9 @@ ExecuteTruncate(TruncateStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(seq_rel)); + /* SELinux checks */ + sepgsql_relation_alter(seq_relid); + seq_relids = lappend_oid(seq_relids, seq_relid); relation_close(seq_rel, NoLock); @@ -1086,6 +1111,8 @@ truncate_check_rel(Relation rel) if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(rel)); + /* SELinux checks */ + sepgsql_relation_truncate(rel); if (!allowSystemTableMods && IsSystemRelation(rel)) ereport(ERROR, @@ -1146,6 +1173,7 @@ storage_name(char c) * 'supconstr' receives a list of constraints belonging to the parents, * updated as necessary to be valid for the child. * 'supOidCount' is set to the number of parents that have OID columns. + * 'supSecidCount' is set to the number of parents that have SID columns. * * Return value: * Completed schema list. @@ -1191,13 +1219,15 @@ storage_name(char c) */ static List * MergeAttributes(List *schema, List *supers, bool istemp, - List **supOids, List **supconstr, int *supOidCount) + List **supOids, List **supconstr, + int *supOidCount, int *supSecidCount) { ListCell *entry; List *inhSchema = NIL; List *parentOids = NIL; List *constraints = NIL; int parentsWithOids = 0; + int parentsWithSecids = 0; bool have_bogus_defaults = false; int child_attno; static Node bogus_marker = {0}; /* marks conflicting defaults */ @@ -1325,6 +1355,8 @@ MergeAttributes(List *schema, List *supers, bool istemp, if (relation->rd_rel->relhasoids) parentsWithOids++; + if (relation->rd_rel->relhassecids) + parentsWithSecids++; tupleDesc = RelationGetDescr(relation); constr = tupleDesc->constr; @@ -1626,6 +1658,7 @@ MergeAttributes(List *schema, List *supers, bool istemp, *supOids = parentOids; *supconstr = constraints; *supOidCount = parentsWithOids; + *supSecidCount = parentsWithSecids; return schema; } @@ -1985,6 +2018,9 @@ renameatt(Oid myrelid, errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(targetrelation)))); + /* SELinux checks */ + sepgsql_attribute_alter(myrelid, oldattname); + /* * if the 'recurse' flag is set then we are supposed to rename this * attribute in all classes that inherit from 'relname' (as well as in @@ -2136,6 +2172,9 @@ RenameRelation(Oid myrelid, const char *newrelname, ObjectType reltype) errmsg("\"%s\" is not a view", RelationGetRelationName(targetrelation)))); + /* SELinux checks */ + sepgsql_relation_alter_rename(myrelid, newrelname); + /* * Don't allow ALTER TABLE on composite types. We want people to use ALTER * TYPE for that. @@ -2566,6 +2605,27 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, } pass = AT_PASS_DROP; break; + case AT_AddSecLabel: + ATSimplePermissions(rel, false); + /* Performs own recursion */ + if (!rel->rd_rel->relhassecids || recursing) + ATPrepAddSecLabel(wqueue, rel, recurse, cmd); + pass = AT_PASS_ADD_COL; + break; + case AT_DropSecLabel: + ATSimplePermissions(rel, false); + /* Performs own recursion */ + if (rel->rd_rel->relhassecids) + { + AlterTableCmd *dropCmd = makeNode(AlterTableCmd); + + dropCmd->subtype = AT_DropColumn; + dropCmd->name = pstrdup("security_label"); + dropCmd->behavior = cmd->behavior; + ATPrepCmd(wqueue, rel, dropCmd, recurse, false); + } + pass = AT_PASS_DROP; + break; case AT_SetTableSpace: /* SET TABLESPACE */ ATSimplePermissionsRelationOrIndex(rel); /* This command never recurses */ @@ -2690,7 +2750,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, case AT_AddColumn: /* ADD COLUMN */ case AT_AddColumnToView: /* add column via CREATE OR REPLACE * VIEW */ - ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false); + ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false, false); break; case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */ ATExecColumnDefault(rel, cmd->name, cmd->def); @@ -2762,17 +2822,22 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, case AT_AddOids: /* SET WITH OIDS */ /* Use the ADD COLUMN code, unless prep decided to do nothing */ if (cmd->def != NULL) - ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, true); + ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, true, false); + break; + case AT_AddSecLabel: /* SET WITH SECURITY LABEL */ + /* Use the ADD COLUMN code, unless prep decided to do nothing */ + if (cmd->def != NULL) + ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false, true); break; case AT_DropOids: /* SET WITHOUT OIDS */ - + case AT_DropSecLabel: /* SET WITHOUT SECURITY LABEL */ /* * Nothing to do here; we'll have generated a DropColumn * subcommand to do the real work */ break; case AT_SetTableSpace: /* SET TABLESPACE */ - + sepgsql_relation_alter(RelationGetRelid(rel)); /* * Nothing to do here; Phase 3 does the work */ @@ -3126,6 +3191,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) MemoryContext oldCxt; List *dropped_attrs = NIL; ListCell *lc; + Oid tupSecidInherit = InvalidOid; econtext = GetPerTupleExprContext(estate); @@ -3156,6 +3222,19 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) } /* + * If this routine is called due to ALTER TABLE SET WITH SECURITY LABEL, + * security label of the relation shall be assigned tuples. + */ + if (!oldTupDesc->tdhassecid && newTupDesc->tdhassecid) + { + Oid relSecid = GetSysCacheSecid1(RELOID, + ObjectIdGetDatum(tab->relid)); + + tupSecidInherit = seclabelMoveSecid(RelationGetRelid(oldrel), + RelationRelationId, relSecid); + } + + /* * Scan through the rows, generating a new row if needed and then * checking all the constraints. */ @@ -3172,11 +3251,16 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) 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 (oldTupDesc->tdhassecid) + tupSecid = HeapTupleGetSecid(tuple); + else + tupSecid = tupSecidInherit; /* Set dropped attributes to null in new tuple */ foreach(lc, dropped_attrs) @@ -3208,6 +3292,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) /* Preserve OID, if any */ if (newTupDesc->tdhasoid) HeapTupleSetOid(tuple, tupOid); + /* Preserve security-id, if any */ + if (newTupDesc->tdhassecid) + HeapTupleSetSecid(tuple, tupSecid); } /* Now check any constraints on the possibly-changed tuple */ @@ -3608,7 +3695,7 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel, - ColumnDef *colDef, bool isOid) + ColumnDef *colDef, bool isOid, bool isSecid) { Oid myrelid = RelationGetRelid(rel); Relation pgclass, @@ -3622,12 +3709,16 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, int32 typmod; Form_pg_type tform; Expr *defval; + Oid securityId; if (rel->rd_rel->reloftype) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot add column to typed table"))); + /* SELinux permission check */ + securityId = sepgsql_attribute_create(myrelid, colDef->colname); + attrdesc = heap_open(AttributeRelationId, RowExclusiveLock); /* @@ -3655,6 +3746,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, errmsg("child table \"%s\" has different type for column \"%s\"", RelationGetRelationName(rel), colDef->colname))); + if (!seclabelCompareSecid(AttributeRelationId, securityId, + AttributeRelationId, HeapTupleGetSecid(tuple))) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("child table \"%s\" has different label for column \"%s\"", + RelationGetRelationName(rel), colDef->colname))); + /* If it's OID, child column must actually be OID */ if (isOid && childatt->attnum != ObjectIdAttributeNumber) ereport(ERROR, @@ -3662,6 +3760,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, errmsg("child table \"%s\" has a conflicting \"%s\" column", RelationGetRelationName(rel), colDef->colname))); + /* If it's SecID, child column must actually be SecID */ + if (isSecid && childatt->attnum != SecurityLabelAttributeNumber) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("child table \"%s\" has a conflicting \"%s\" column", + RelationGetRelationName(rel), colDef->colname))); + /* Bump the existing child att's inhcount */ childatt->attinhcount++; simple_heap_update(attrdesc, &tuple->t_self, tuple); @@ -3701,6 +3806,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, /* Determine the new attribute's number */ if (isOid) newattnum = ObjectIdAttributeNumber; + else if (isSecid) + newattnum = SecurityLabelAttributeNumber; else { newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1; @@ -3740,7 +3847,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, ReleaseSysCache(typeTuple); - InsertPgAttributeTuple(attrdesc, &attribute, NULL); + InsertPgAttributeTuple(attrdesc, &attribute, NULL, securityId); heap_close(attrdesc, RowExclusiveLock); @@ -3749,6 +3856,8 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, */ if (isOid) ((Form_pg_class) GETSTRUCT(reltup))->relhasoids = true; + else if (isSecid) + ((Form_pg_class) GETSTRUCT(reltup))->relhassecids = true; else ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum; @@ -3860,7 +3969,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, * If we are adding an OID column, we have to tell Phase 3 to rewrite the * table to fix that. */ - if (isOid) + if (isOid || isSecid) tab->new_changeoids = true; /* @@ -3913,6 +4022,31 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd) } /* + * ALTER TABLE SET WITH SECURITY LABEL + * + * Basically this is an ADD COLUMN for the special SecLabel column. + * We have to cons up a ColumnDef node because the ADD COLUMN code needs one. + */ +static void +ATPrepAddSecLabel(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd) +{ + /* If we're recursing to a child table, the ColumnDef is already set up */ + if (cmd->def == NULL) + { + ColumnDef *cdef = makeNode(ColumnDef); + + cdef->colname = pstrdup("security_label"); + cdef->typeName = makeTypeNameFromOid(TEXTOID, -1); + cdef->inhcount = 0; + cdef->is_local = true; + cdef->is_not_null = true; + cdef->storage = 0; + cmd->def = (Node *) cdef; + } + ATPrepAddColumn(wqueue, rel, recurse, cmd); +} + +/* * ALTER TABLE ALTER COLUMN DROP NOT NULL */ static void @@ -3924,6 +4058,9 @@ ATExecDropNotNull(Relation rel, const char *colName) List *indexoidlist; ListCell *indexoidscan; + /* SELinux checks */ + sepgsql_attribute_alter(RelationGetRelid(rel), colName); + /* * lookup the attribute */ @@ -4014,6 +4151,9 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, AttrNumber attnum; Relation attr_rel; + /* SELinux checks */ + sepgsql_attribute_alter(RelationGetRelid(rel), colName); + /* * lookup the attribute */ @@ -4064,6 +4204,9 @@ ATExecColumnDefault(Relation rel, const char *colName, { AttrNumber attnum; + /* SELinux checks */ + sepgsql_attribute_alter(RelationGetRelid(rel), colName); + /* * get the number of the attribute */ @@ -4138,6 +4281,9 @@ ATExecSetStatistics(Relation rel, const char *colName, Node *newValue) HeapTuple tuple; Form_pg_attribute attrtuple; + /* SELinux checks */ + sepgsql_attribute_alter(RelationGetRelid(rel), colName); + Assert(IsA(newValue, Integer)); newtarget = intVal(newValue); @@ -4204,6 +4350,9 @@ ATExecSetOptions(Relation rel, const char *colName, Node *options, bool repl_null[Natts_pg_attribute]; bool repl_repl[Natts_pg_attribute]; + /* SELinux checks */ + sepgsql_attribute_alter(RelationGetRelid(rel), colName); + attrelation = heap_open(AttributeRelationId, RowExclusiveLock); tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName); @@ -4263,6 +4412,9 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue) HeapTuple tuple; Form_pg_attribute attrtuple; + /* SELinux checks */ + sepgsql_attribute_alter(RelationGetRelid(rel), colName); + Assert(IsA(newValue, String)); storagemode = strVal(newValue); @@ -4353,6 +4505,9 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, if (recursing) ATSimplePermissions(rel, false); + /* SELinux checks */ + sepgsql_attribute_drop(RelationGetRelid(rel), colName, false); + /* * get the number of the attribute */ @@ -4378,8 +4533,10 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, attnum = targetatt->attnum; - /* Can't drop a system attribute, except OID */ - if (attnum <= 0 && attnum != ObjectIdAttributeNumber) + /* Can't drop a system attribute, except OID/SecID */ + if (attnum <= 0 && + attnum != ObjectIdAttributeNumber && + attnum != SecurityLabelAttributeNumber) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot drop system column \"%s\"", @@ -4495,7 +4652,8 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, * If we dropped the OID column, must adjust pg_class.relhasoids and tell * Phase 3 to physically get rid of the column. */ - if (attnum == ObjectIdAttributeNumber) + if (attnum == ObjectIdAttributeNumber || + attnum == SecurityLabelAttributeNumber) { Relation class_rel; Form_pg_class tuple_class; @@ -4510,7 +4668,11 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, RelationGetRelid(rel)); tuple_class = (Form_pg_class) GETSTRUCT(tuple); - tuple_class->relhasoids = false; + if (attnum == ObjectIdAttributeNumber) + tuple_class->relhasoids = false; + if (attnum == SecurityLabelAttributeNumber) + tuple_class->relhassecids = false; + simple_heap_update(class_rel, &tuple->t_self, tuple); /* Keep the catalog indexes up to date */ @@ -4657,6 +4819,9 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, if (recursing) ATSimplePermissions(rel, false); + /* SELinux checks */ + sepgsql_relation_alter(RelationGetRelid(rel)); + /* * Call AddRelationNewConstraints to do the work, making sure it works on * a copy of the Constraint so transformExpr can't modify the original. It @@ -4854,6 +5019,9 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, checkFkeyPermissions(pkrel, pkattnum, numpks); checkFkeyPermissions(rel, fkattnum, numfks); + /* SELinux checks */ + sepgsql_relation_alter(RelationGetRelid(rel)); + /* * Look up the equality operators to use in the constraint. * @@ -5599,6 +5767,9 @@ ATExecDropConstraint(Relation rel, const char *constrName, if (recursing) ATSimplePermissions(rel, false); + /* SELinux checks */ + sepgsql_relation_alter(RelationGetRelid(rel)); + conrel = heap_open(ConstraintRelationId, RowExclusiveLock); /* @@ -5931,6 +6102,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, SysScanDesc scan; HeapTuple depTup; + /* SELinux checks */ + sepgsql_attribute_alter(RelationGetRelid(rel), colName); + attrelation = heap_open(AttributeRelationId, RowExclusiveLock); /* Look up the target column */ @@ -6544,6 +6718,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } + /* SELinux checks */ + sepgsql_relation_alter(relationOid); } memset(repl_null, false, sizeof(repl_null)); @@ -6709,6 +6885,9 @@ ATExecClusterOn(Relation rel, const char *indexName) { Oid indexOid; + /* SELinux checks */ + sepgsql_relation_alter(RelationGetRelid(rel)); + indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace); if (!OidIsValid(indexOid)) @@ -6733,6 +6912,9 @@ ATExecClusterOn(Relation rel, const char *indexName) static void ATExecDropCluster(Relation rel) { + /* SELinux checks */ + sepgsql_relation_alter(RelationGetRelid(rel)); + mark_index_clustered(rel, InvalidOid); } @@ -6783,6 +6965,9 @@ ATExecSetRelOptions(Relation rel, List *defList, bool isReset) bool repl_repl[Natts_pg_class]; static char *validnsps[] = HEAP_RELOPT_NAMESPACES; + /* SELinux checks */ + sepgsql_relation_alter(RelationGetRelid(rel)); + if (defList == NIL) return; /* nothing to do */ @@ -7115,6 +7300,9 @@ static void ATExecEnableDisableTrigger(Relation rel, char *trigname, char fires_when, bool skip_system) { + /* SELinux checks */ + sepgsql_relation_alter(RelationGetRelid(rel)); + EnableDisableTrigger(rel, trigname, fires_when, skip_system); } @@ -7127,6 +7315,9 @@ static void ATExecEnableDisableRule(Relation rel, char *trigname, char fires_when) { + /* SELinux checks */ + sepgsql_relation_alter(RelationGetRelid(rel)); + EnableDisableRule(rel, trigname, fires_when); } @@ -7160,6 +7351,10 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent) */ ATSimplePermissions(parent_rel, false); + /* SELinux checks */ + sepgsql_relation_alter_inherit(RelationGetRelid(child_rel), + RelationGetRelid(parent_rel)); + /* Permanent rels cannot inherit from temporary ones */ if (parent_rel->rd_istemp && !child_rel->rd_istemp) ereport(ERROR, @@ -7512,6 +7707,9 @@ ATExecDropInherit(Relation rel, RangeVar *parent) List *connames; bool found = false; + /* SELinux checks */ + sepgsql_relation_alter(RelationGetRelid(rel)); + /* * AccessShareLock on the parent is probably enough, seeing that DROP * TABLE doesn't lock parent tables at all. We need some lock since we'll @@ -7809,6 +8007,9 @@ AlterTableNamespace(RangeVar *relation, const char *newschema, /* get schema OID and check its permissions */ nspOid = LookupCreationNamespace(newschema); + /* SELinux checks */ + sepgsql_relation_alter_schema(RelationGetRelid(rel), nspOid); + if (oldNspOid == nspOid) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_TABLE), @@ -8006,6 +8207,282 @@ AlterSeqNamespaces(Relation classRel, Relation rel, relation_close(depRel, AccessShareLock); } +/* + * ALTER TABLE/SEQUENCE/VIEW SECURITY LABEL TO + */ +void +AlterRelationSecLabelInternal(Oid relOid, Oid securityId, int expected_parents) +{ + Form_pg_class classForm; + Relation targetRel; + Relation inhRel; + Relation classRel; + SysScanDesc scan; + ScanKeyData skey; + HeapTuple tuple; + List *indexList; + ListCell *cell; + int inhcount = 0; + + /* + * Grab an exclusive lock on the target table + */ + targetRel = relation_open(relOid, AccessExclusiveLock); + + /* + * Check num of inheritors + */ + inhRel = heap_open(InheritsRelationId, AccessShareLock); + + ScanKeyInit(&skey, + Anum_pg_inherits_inhrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(relOid)); + + scan = systable_beginscan(inhRel, InheritsRelidSeqnoIndexId, + true, SnapshotNow, 1, &skey); + while (HeapTupleIsValid(systable_getnext(scan))) + inhcount++; + + systable_endscan(scan); + + heap_close(inhRel, AccessShareLock); + + if (inhcount != expected_parents) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("cannot relabel inherited relation"))); + + /* + * Update pg_class relation + */ + classRel = heap_open(RelationRelationId, RowExclusiveLock); + + tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", relOid); + + classForm = (Form_pg_class) GETSTRUCT(tuple); + + HeapTupleSetSecid(tuple, securityId); + + simple_heap_update(classRel, &tuple->t_self, tuple); + + CatalogUpdateIndexes(classRel, tuple); + + heap_close(classRel, RowExclusiveLock); + + /* + * Also update TOAST and INDEX + */ + if (OidIsValid(classForm->reltoastrelid)) + AlterRelationSecLabelInternal(classForm->reltoastrelid, + securityId, 0); + + indexList = RelationGetIndexList(targetRel); + foreach (cell, indexList) + AlterRelationSecLabelInternal(lfirst_oid(cell), securityId, 0); + + /* + * Also update pg_attribute, if not RELKIND_RELATION + */ + if (classForm->relkind != RELKIND_RELATION) + { + Relation attRel; + HeapTuple oldtup, newtup; + Oid attsecid; + + /* move security-id to pg_attribtue catalog */ + attsecid = seclabelMoveSecid(AttributeRelationId, + RelationRelationId, + securityId); + + attRel = heap_open(AttributeRelationId, RowExclusiveLock); + + ScanKeyInit(&skey, + Anum_pg_attribute_attrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(RelationGetRelid(targetRel))); + + scan = systable_beginscan(attRel, AttributeRelidNumIndexId, true, + SnapshotNow, 1, &skey); + while (HeapTupleIsValid(oldtup = systable_getnext(scan))) + { + Form_pg_attribute attForm + = (Form_pg_attribute) GETSTRUCT(oldtup); + + if (attForm->attinhcount > 0) + elog(ERROR, "Bug? attinhcount is %d at %s of %s", + attForm->attinhcount, NameStr(attForm->attname), + RelationGetRelationName(targetRel)); + + newtup = heap_copytuple(oldtup); + + HeapTupleSetSecid(newtup, attsecid); + + simple_heap_update(attRel, &newtup->t_self, newtup); + + CatalogUpdateIndexes(attRel, newtup); + } + systable_endscan(scan); + + heap_close(attRel, RowExclusiveLock); + } + + heap_close(targetRel, NoLock); /* close rel but keep lock */ +} + +void +AlterAttributeSecLabelInternal(Oid relOid, const char *attname, + Oid securityId, int expected_parents) +{ + Form_pg_attribute attForm; + Relation targetRel; + Relation attRel; + HeapTuple tuple; + + /* + * Grab an exclusive lock on the target table, which we will NOT + * release until end of transaction. + */ + targetRel = heap_open(relOid, AccessExclusiveLock); + + attRel = heap_open(AttributeRelationId, RowExclusiveLock); + + tuple = SearchSysCacheCopyAttName(relOid, attname); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" does not exist", attname))); + + attForm = (Form_pg_attribute) GETSTRUCT(tuple); +#if 0 + /* + * XXX - here is no active reason why we forbid to relabel + * system columns. + */ + if (attForm->attnum <= 0) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot relabel system column \"%s\"", attname))); +#endif + if (attForm->attinhcount > expected_parents) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TABLE_DEFINITION), + errmsg("cannot relabel inherited column \"%s\"", attname))); + + /* update pg_attribute */ + HeapTupleSetSecid(tuple, securityId); + + simple_heap_update(attRel, &tuple->t_self, tuple); + + CatalogUpdateIndexes(attRel, tuple); + + heap_close(attRel, RowExclusiveLock); + + heap_close(targetRel, NoLock); /* close rel but keep lock */ +} + +void +AlterRelationSecLabel(RangeVar *relation, const char *attname, + ObjectType objtype, char *new_label) +{ + Oid relOid = RangeVarGetRelid(relation, false); + Oid securityId; + char relkind; + List *child_oids, *child_numparents; + ListCell *lo, *li; + + /* + * Sanity checks for relation types + */ + relkind = get_rel_relkind(relOid); + switch (objtype) + { + case OBJECT_TABLE: + Assert(attname == NULL); + if (relkind != RELKIND_RELATION && + relkind != RELKIND_SEQUENCE && + relkind != RELKIND_VIEW) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table, sequence or view", + get_rel_name(relOid)))); + break; + + case OBJECT_SEQUENCE: + Assert(attname == NULL); + if (relkind != RELKIND_SEQUENCE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a sequence", + get_rel_name(relOid)))); + break; + + case OBJECT_VIEW: + Assert(attname == NULL); + if (relkind != RELKIND_VIEW) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a view", + get_rel_name(relOid)))); + break; + + case OBJECT_COLUMN: + Assert(attname != NULL); + if (relkind != RELKIND_RELATION) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a table", + get_rel_name(relOid)))); + break; + + default: + elog(ERROR, "Bug? unexpected object type %d", objtype); + break; + } + + /* + * Recursive calls to child relations including myself + */ + child_oids = find_all_inheritors(relOid, + AccessExclusiveLock, + &child_numparents); + forboth (lo, child_oids, li, child_numparents) + { + Oid childOid = lfirst_oid(lo); + int numParents = lfirst_int(li); + Oid relnsp = get_rel_namespace(childOid); + + /* Permission checks */ + if (!pg_class_ownercheck(childOid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + get_rel_name(childOid)); + + if (!allowSystemTableMods && + (IsSystemNamespace(relnsp) || IsToastNamespace(relnsp))) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied: \"%s\" is a system catalog", + get_rel_name(childOid)))); + + /* SELinux checks */ + if (objtype != OBJECT_COLUMN) + securityId = sepgsql_relation_relabel(childOid, + new_label); + else + securityId = sepgsql_attribute_relabel(childOid, attname, + new_label); + + /* Do work */ + if (objtype != OBJECT_COLUMN) + AlterRelationSecLabelInternal(childOid, + securityId, numParents); + else + AlterAttributeSecLabelInternal(childOid, attname, + securityId, numParents); + } +} /* * This code supports diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index 862cd2d..8ea4118 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -65,6 +65,7 @@ #include "commands/tablespace.h" #include "miscadmin.h" #include "postmaster/bgwriter.h" +#include "sepgsql/hooks.h" #include "storage/fd.h" #include "storage/procarray.h" #include "storage/standby.h" @@ -235,6 +236,7 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) Oid tablespaceoid; char *location; Oid ownerId; + Oid securityId; /* Must be super user */ if (!superuser()) @@ -244,6 +246,9 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) stmt->tablespacename), errhint("Must be superuser to create a tablespace."))); + /* SELinux check */ + securityId = sepgsql_tablespace_create(stmt->tablespacename); + /* However, the eventual owner of the tablespace need not be */ if (stmt->owner) ownerId = get_roleid_checked(stmt->owner); @@ -324,6 +329,8 @@ CreateTableSpace(CreateTableSpaceStmt *stmt) tuple = heap_form_tuple(rel->rd_att, values, nulls); + HeapTupleSetSecid(tuple, securityId); + tablespaceoid = simple_heap_insert(rel, tuple); CatalogUpdateIndexes(rel, tuple); @@ -429,6 +436,9 @@ DropTableSpace(DropTableSpaceStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TABLESPACE, tablespacename); + /* SELinux checks */ + sepgsql_tablespace_drop(tablespaceoid, false); + /* Disallow drop of the standard tablespaces, even by superuser */ if (tablespaceoid == GLOBALTABLESPACE_OID || tablespaceoid == DEFAULTTABLESPACE_OID) @@ -787,6 +797,9 @@ RenameTableSpace(const char *oldname, const char *newname) if (!pg_tablespace_ownercheck(HeapTupleGetOid(newtuple), GetUserId())) aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, oldname); + /* SELinux checks */ + sepgsql_tablespace_alter(HeapTupleGetOid(newtuple)); + /* Validate new name */ if (!allowSystemTableMods && IsReservedName(newname)) ereport(ERROR, @@ -868,6 +881,9 @@ AlterTableSpaceOwner(const char *name, Oid newOwnerId) /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); + /* SELinux checks */ + sepgsql_tablespace_alter(HeapTupleGetOid(tup)); + /* * Normally we would also check for create permissions here, but there * are none for tablespaces so we follow what rename tablespace does @@ -985,6 +1001,54 @@ AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt) } /* + * ALTER TABLESPACE xxx SECURITY LABEL TO ... + */ +void +AlterTableSpaceSecLabel(const char *tspaceName, char *newLabel) +{ + Relation rel; + ScanKeyData skey; + HeapScanDesc scan; + HeapTuple oldtup; + HeapTuple newtup; + Oid securityId; + + rel = heap_open(TableSpaceRelationId, RowExclusiveLock); + + /* scan pg_tablespace catalog */ + ScanKeyInit(&skey, + Anum_pg_tablespace_spcname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(tspaceName)); + scan = heap_beginscan(rel, SnapshotNow, 1, &skey); + oldtup = heap_getnext(scan, ForwardScanDirection); + if (!HeapTupleIsValid(oldtup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("tablespace \"%s\" does not exist", tspaceName))); + + /* Must be owner */ + if (!pg_tablespace_ownercheck(HeapTupleGetOid(oldtup), GetUserId())) + aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_TABLESPACE, tspaceName); + + /* SELinux checks */ + securityId = sepgsql_tablespace_relabel(HeapTupleGetSecid(oldtup), newLabel); + + /* update it */ + newtup = heap_copytuple(oldtup); + + HeapTupleSetSecid(newtup, securityId); + + simple_heap_update(rel, &newtup->t_self, newtup); + + CatalogUpdateIndexes(rel, newtup); + + heap_endscan(scan); + + heap_close(rel, RowExclusiveLock); +} + +/* * Routines for handling the GUC variable 'default_tablespace'. */ diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 2cbc192..2ea9e6e 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -40,6 +40,7 @@ #include "parser/parsetree.h" #include "pgstat.h" #include "rewrite/rewriteManip.h" +#include "sepgsql/hooks.h" #include "storage/bufmgr.h" #include "tcop/utility.h" #include "utils/acl.h" @@ -330,6 +331,10 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, NameListToString(stmt->funcname)))); } + /* SELinux checks */ + sepgsql_trigger_create(RelationGetRelid(rel), stmt->trigname, + constrrelid, 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 @@ -1007,6 +1012,9 @@ DropTrigger(Oid relid, const char *trigname, DropBehavior behavior, aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, get_rel_name(relid)); + /* SELinux checks */ + sepgsql_trigger_drop(relid, trigname, false); + object.classId = TriggerRelationId; object.objectId = HeapTupleGetOid(tup); object.objectSubId = 0; @@ -1116,6 +1124,9 @@ renametrig(Oid relid, SysScanDesc tgscan; ScanKeyData key[2]; + /* SELinux checks */ + sepgsql_trigger_alter(relid, oldname); + /* * Grab an exclusive lock on the target table, which we will NOT release * until end of transaction. diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c index ba3de63..19bebb4 100644 --- a/src/backend/commands/tsearchcmds.c +++ b/src/backend/commands/tsearchcmds.c @@ -35,6 +35,7 @@ #include "miscadmin.h" #include "nodes/makefuncs.h" #include "parser/parse_func.h" +#include "sepgsql/hooks.h" #include "tsearch/ts_cache.h" #include "tsearch/ts_public.h" #include "tsearch/ts_utils.h" @@ -171,6 +172,7 @@ DefineTSParser(List *names, List *parameters) NameData pname; Oid prsOid; Oid namespaceoid; + Oid securityId; if (!superuser()) ereport(ERROR, @@ -250,6 +252,14 @@ DefineTSParser(List *names, List *parameters) (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("text search parser lextypes method is required"))); + /* SELinux checks */ + securityId = 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 */ @@ -257,6 +267,8 @@ DefineTSParser(List *names, List *parameters) tup = heap_form_tuple(prsRel->rd_att, values, nulls); + HeapTupleSetSecid(tup, securityId); + prsOid = simple_heap_insert(prsRel, tup); CatalogUpdateIndexes(prsRel, tup); @@ -314,6 +326,8 @@ RemoveTSParsers(DropStmt *drop) } continue; } + /* SELinux checks */ + sepgsql_ts_parser_drop(prsOid, false); object.classId = TSParserRelationId; object.objectId = prsOid; @@ -366,10 +380,13 @@ RenameTSParser(List *oldname, const char *newname) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to rename text search parsers"))); - rel = heap_open(TSParserRelationId, RowExclusiveLock); - prsId = TSParserGetPrsid(oldname, false); + /* SELinux checks */ + sepgsql_ts_parser_alter_rename(prsId, newname); + + rel = heap_open(TSParserRelationId, RowExclusiveLock); + tup = SearchSysCacheCopy1(TSPARSEROID, ObjectIdGetDatum(prsId)); if (!HeapTupleIsValid(tup)) /* should not happen */ @@ -496,6 +513,7 @@ DefineTSDictionary(List *names, List *parameters) List *dictoptions = NIL; Oid dictOid; Oid namespaceoid; + Oid securityId; AclResult aclresult; char *dictname; @@ -508,6 +526,9 @@ DefineTSDictionary(List *names, List *parameters) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceoid)); + /* SELinux checks */ + securityId = sepgsql_ts_dict_create(dictname, namespaceoid); + /* * loop over the definition list and extract the information we need. */ @@ -557,6 +578,8 @@ DefineTSDictionary(List *names, List *parameters) tup = heap_form_tuple(dictRel->rd_att, values, nulls); + HeapTupleSetSecid(tup, securityId); + dictOid = simple_heap_insert(dictRel, tup); CatalogUpdateIndexes(dictRel, tup); @@ -610,6 +633,8 @@ RenameTSDictionary(List *oldname, const char *newname) if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* SELinux checks */ + sepgsql_ts_dict_alter_rename(dictId, newname); namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname); simple_heap_update(rel, &tup->t_self, tup); @@ -674,6 +699,8 @@ RemoveTSDictionaries(DropStmt *drop) !pg_namespace_ownercheck(namespaceId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, NameListToString(names)); + /* SELinux checks */ + sepgsql_ts_dict_drop(dictOid, false); object.classId = TSDictionaryRelationId; object.objectId = dictOid; @@ -745,6 +772,8 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt) if (!pg_ts_dict_ownercheck(dictId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, NameListToString(stmt->dictname)); + /* SELinux checks */ + sepgsql_ts_dict_alter(dictId); /* deserialize the existing set of options */ opt = SysCacheGetAttr(TSDICTOID, tup, @@ -871,6 +900,8 @@ AlterTSDictionaryOwner(List *name, Oid newOwnerId) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } + /* SELinux checks */ + sepgsql_ts_dict_alter(dictId); form->dictowner = newOwnerId; @@ -982,6 +1013,7 @@ DefineTSTemplate(List *names, List *parameters) int i; Oid dictOid; Oid namespaceoid; + Oid securityId; char *tmplname; if (!superuser()) @@ -1036,6 +1068,10 @@ DefineTSTemplate(List *names, List *parameters) (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("text search template lexize method is required"))); + /* SELinux checks */ + securityId = 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 */ @@ -1044,6 +1080,8 @@ DefineTSTemplate(List *names, List *parameters) tup = heap_form_tuple(tmplRel->rd_att, values, nulls); + HeapTupleSetSecid(tup, securityId); + dictOid = simple_heap_insert(tmplRel, tup); CatalogUpdateIndexes(tmplRel, tup); @@ -1075,6 +1113,9 @@ RenameTSTemplate(List *oldname, const char *newname) tmplId = TSTemplateGetTmplid(oldname, false); + /* SELinux checks */ + sepgsql_ts_template_alter_rename(tmplId, newname); + tup = SearchSysCacheCopy1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId)); if (!HeapTupleIsValid(tup)) /* should not happen */ @@ -1145,6 +1186,8 @@ RemoveTSTemplates(DropStmt *drop) } continue; } + /* SELinux checks */ + sepgsql_ts_template_drop(tmplOid, false); object.classId = TSTemplateRelationId; object.objectId = tmplOid; @@ -1305,6 +1348,7 @@ DefineTSConfiguration(List *names, List *parameters) bool nulls[Natts_pg_ts_config]; AclResult aclresult; Oid namespaceoid; + Oid securityId; char *cfgname; NameData cname; Oid sourceOid = InvalidOid; @@ -1321,6 +1365,9 @@ DefineTSConfiguration(List *names, List *parameters) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceoid)); + /* SELinux checks */ + securityId = sepgsql_ts_config_create(cfgname, namespaceoid); + /* * loop over the definition list and extract the information we need. */ @@ -1388,6 +1435,8 @@ DefineTSConfiguration(List *names, List *parameters) tup = heap_form_tuple(cfgRel->rd_att, values, nulls); + HeapTupleSetSecid(tup, securityId); + cfgOid = simple_heap_insert(cfgRel, tup); CatalogUpdateIndexes(cfgRel, tup); @@ -1489,6 +1538,9 @@ RenameTSConfiguration(List *oldname, const char *newname) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); + /* SELinux checks */ + sepgsql_ts_config_alter_rename(cfgId, newname); + namestrcpy(&(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname), newname); simple_heap_update(rel, &tup->t_self, tup); CatalogUpdateIndexes(rel, tup); @@ -1549,6 +1601,9 @@ RemoveTSConfigurations(DropStmt *drop) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, NameListToString(names)); + /* SELinux checks */ + sepgsql_ts_config_drop(cfgOid, false); + object.classId = TSConfigRelationId; object.objectId = cfgOid; object.objectSubId = 0; @@ -1656,6 +1711,8 @@ AlterTSConfigurationOwner(List *name, Oid newOwnerId) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } + /* SELinux checks */ + sepgsql_ts_config_alter(cfgId); form->cfgowner = newOwnerId; @@ -1693,6 +1750,9 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, NameListToString(stmt->cfgname)); + /* SELinux checks */ + sepgsql_ts_config_alter(HeapTupleGetOid(tup)); + relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock); /* Add or drop mappings */ diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 1e14dca..f7429f0 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -56,6 +56,7 @@ #include "parser/parse_expr.h" #include "parser/parse_func.h" #include "parser/parse_type.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" @@ -143,6 +144,8 @@ DefineType(List *names, List *parameters) char *array_type; Oid array_oid; Oid typoid; + Oid type_replaced; + Oid securityId; Oid resulttype; ListCell *pl; @@ -520,6 +523,15 @@ DefineType(List *names, List *parameters) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(analyzeName)); #endif + /* SELinux checks */ + type_replaced = GetSysCacheOid2(TYPENAMENSP, + CStringGetDatum(typeName), + ObjectIdGetDatum(typeNamespace)); + securityId = sepgsql_type_create(typeName, type_replaced, + typeNamespace, TYPTYPE_BASE, + inputOid, outputOid, + receiveOid, sendOid, + typmodinOid, typmodoutOid, analyzeOid); array_oid = AssignTypeArrayOid(); @@ -562,7 +574,8 @@ DefineType(List *names, List *parameters) storage, /* TOAST strategy */ -1, /* typMod (Domains only) */ 0, /* Array Dimensions of typbasetype */ - false); /* Type NOT NULL */ + false, /* Type NOT NULL */ + securityId); /* security-id of the type */ /* * Create the array type that goes with it. @@ -601,7 +614,8 @@ DefineType(List *names, List *parameters) 'x', /* ARRAY is always toastable */ -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ - false); /* Type NOT NULL */ + false, /* Type NOT NULL */ + securityId); /* security-id of the type */ pfree(array_type); } @@ -668,6 +682,9 @@ RemoveTypes(DropStmt *drop) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, format_type_be(typeoid)); + /* SELinux checks */ + sepgsql_type_drop(typeoid, false); + if (drop->removeType == OBJECT_DOMAIN) { /* Check that this is actually a domain */ @@ -766,6 +783,7 @@ DefineDomain(CreateDomainStmt *stmt) Oid basetypeoid; Oid domainoid; Oid old_type_oid; + Oid securityId; Form_pg_type baseType; int32 basetypeMod; @@ -1011,6 +1029,13 @@ DefineDomain(CreateDomainStmt *stmt) } } + /* SELinux checks */ + securityId = sepgsql_type_create(domainName, InvalidOid, + domainNamespace, TYPTYPE_DOMAIN, + inputProcedure, outputProcedure, + receiveProcedure, sendProcedure, + InvalidOid, InvalidOid, analyzeProcedure); + /* * Have TypeCreate do all the real work. */ @@ -1044,7 +1069,8 @@ DefineDomain(CreateDomainStmt *stmt) storage, /* TOAST strategy */ basetypeMod, /* typeMod value */ typNDims, /* Array dimensions for base type */ - typNotNull); /* Type NOT NULL */ + typNotNull, /* Type NOT NULL */ + securityId); /* security-id of the type */ /* * Process constraints which refer to the domain ID returned by TypeCreate @@ -1094,6 +1120,7 @@ DefineEnum(CreateEnumStmt *stmt) AclResult aclresult; Oid old_type_oid; Oid enumArrayOid; + Oid securityId; /* Convert list of names to a name and namespace */ enumNamespace = QualifiedNameGetCreationNamespace(stmt->typeName, @@ -1120,6 +1147,13 @@ DefineEnum(CreateEnumStmt *stmt) errmsg("type \"%s\" already exists", enumName))); } + /* SELinux checks */ + securityId = sepgsql_type_create(enumName, old_type_oid, + enumNamespace, TYPTYPE_ENUM, + F_ENUM_IN, F_ENUM_OUT, + F_ENUM_RECV, F_ENUM_SEND, + InvalidOid, InvalidOid, InvalidOid); + enumArrayOid = AssignTypeArrayOid(); /* Create the pg_type entry */ @@ -1153,7 +1187,8 @@ DefineEnum(CreateEnumStmt *stmt) 'p', /* TOAST strategy always plain */ -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ - false); /* Type NOT NULL */ + false, /* Type NOT NULL */ + securityId); /* security-id of the type */ /* Enter the enum's values into pg_enum */ EnumValuesCreate(enumTypeOid, stmt->vals, InvalidOid); @@ -1192,7 +1227,8 @@ DefineEnum(CreateEnumStmt *stmt) 'x', /* ARRAY is always toastable */ -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ - false); /* Type NOT NULL */ + false, /* Type NOT NULL */ + securityId); /* security-id of the type */ pfree(enumArrayName); } @@ -1585,6 +1621,8 @@ AlterDomainDefault(List *names, Node *defaultRaw) /* Check it's a domain and check user has permission for ALTER DOMAIN */ checkDomainOwner(tup, typename); + sepgsql_type_alter(domainoid); + /* Setup new tuple */ MemSet(new_record, (Datum) 0, sizeof(new_record)); MemSet(new_record_nulls, false, sizeof(new_record_nulls)); @@ -1711,6 +1749,8 @@ AlterDomainNotNull(List *names, bool notNull) /* Check it's a domain and check user has permission for ALTER DOMAIN */ checkDomainOwner(tup, typename); + sepgsql_type_alter(domainoid); + /* Is the domain already set to the desired constraint? */ if (typTup->typnotnull == notNull) { @@ -1810,6 +1850,8 @@ AlterDomainDropConstraint(List *names, const char *constrName, /* Check it's a domain and check user has permission for ALTER DOMAIN */ checkDomainOwner(tup, typename); + sepgsql_type_alter(domainoid); + /* Grab an appropriate lock on the pg_constraint relation */ conrel = heap_open(ConstraintRelationId, RowExclusiveLock); @@ -1884,6 +1926,8 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) /* Check it's a domain and check user has permission for ALTER DOMAIN */ checkDomainOwner(tup, typename); + sepgsql_type_alter(domainoid); + if (!IsA(newConstraint, Constraint)) elog(ERROR, "unrecognized node type: %d", (int) nodeTag(newConstraint)); @@ -2507,6 +2551,9 @@ RenameType(List *names, const char *newTypeName) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, format_type_be(typeOid)); + /* SELinux checks */ + sepgsql_type_alter_rename(typeOid, newTypeName); + /* * 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 @@ -2627,6 +2674,8 @@ AlterTypeOwner(List *names, Oid newOwnerId) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(typTup->typnamespace)); } + /* SELinux checks */ + sepgsql_type_alter(HeapTupleGetOid(tup)); /* * If it's a composite type, invoke ATExecChangeOwner so that we fix @@ -2731,6 +2780,9 @@ AlterTypeNamespace(List *names, const char *newschema) /* get schema OID and check its permissions */ nspOid = LookupCreationNamespace(newschema); + /* SELinux checks */ + sepgsql_type_alter_schema(typeOid, nspOid); + /* don't allow direct alteration of array types */ elemOid = get_element_type(typeOid); if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid) @@ -2882,3 +2934,100 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, if (OidIsValid(arrayOid)) AlterTypeNamespaceInternal(arrayOid, nspOid, true, true); } + +/* + * ALTER TYPE xxx SECURITY LABEL TO ... + */ +void +AlterTypeSecLabelInternal(Oid typeOid, Oid securityId) +{ + Relation typeRel; + Form_pg_type typeForm; + HeapTuple tuple; + + typeRel = heap_open(TypeRelationId, RowExclusiveLock); + + tuple = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(typeOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for type %u", typeOid); + typeForm = (Form_pg_type) GETSTRUCT(tuple); + + /* update it */ + HeapTupleSetSecid(tuple, securityId); + + simple_heap_update(typeRel, &tuple->t_self, tuple); + + CatalogUpdateIndexes(typeRel, tuple); + + /* if it is a composit type, update pg_class too */ + if (OidIsValid(typeForm->typrelid)) + { + Oid classSecId = seclabelMoveSecid(RelationRelationId, + TypeRelationId, + securityId); + AlterRelationSecLabelInternal(typeForm->typrelid, classSecId, 0); + } + + /* if it has an array type, update that too */ + if (OidIsValid(typeForm->typarray)) + AlterTypeSecLabelInternal(typeForm->typarray, securityId); + + heap_freetuple(tuple); + + heap_close(typeRel, RowExclusiveLock); +} + +void +AlterTypeSecLabel(List *names, char *new_label) +{ + Form_pg_type typeForm; + HeapTuple typtup; + TypeName *typename; + Oid typeOid; + Oid securityId; + + /* resolve type name */ + typename = makeTypeNameFromNameList(names); + typtup = LookupTypeName(NULL, typename, NULL); + if (!HeapTupleIsValid(typtup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("type \"%s\" does not exist", + TypeNameToString(typename)))); + + typeForm = (Form_pg_type) GETSTRUCT(typtup); + typeOid = HeapTupleGetOid(typtup); + + /* + * 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 + * to use ALTER TABLE not ALTER TYPE for that case. + */ + if (typeForm->typtype == TYPTYPE_COMPOSITE && + get_rel_relkind(typeForm->typrelid) != RELKIND_COMPOSITE_TYPE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("%s is a table's row type", + format_type_be(typeOid)), + errhint("Use ALTER TABLE instead."))); + + /* don't allow direct alteration of array types, either */ + if (OidIsValid(typeForm->typelem) && + get_array_type(typeForm->typelem) == typeOid) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot alter array type %s", + format_type_be(typeOid)))); + + /* DAC permission checks */ + if (!pg_type_ownercheck(typeOid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, + format_type_be(typeOid)); + + /* SELinux checks */ + securityId = sepgsql_type_relabel(typeOid, new_label); + + AlterTypeSecLabelInternal(typeOid, securityId); + + ReleaseSysCache(typtup); +} diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 2f0788e..8de31ae 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -21,11 +21,13 @@ #include "catalog/pg_authid.h" #include "catalog/pg_database.h" #include "catalog/pg_db_role_setting.h" +#include "catalog/pg_seclabel.h" #include "commands/comment.h" #include "commands/dbcommands.h" #include "commands/user.h" #include "libpq/md5.h" #include "miscadmin.h" +#include "sepgsql/hooks.h" #include "storage/lmgr.h" #include "utils/acl.h" #include "utils/builtins.h" @@ -111,6 +113,7 @@ CreateRole(CreateRoleStmt *stmt) DefElem *drolemembers = NULL; DefElem *dadminmembers = NULL; DefElem *dvalidUntil = NULL; + Oid securityId; /* The defaults can vary depending on the original statement type */ switch (stmt->stmt_type) @@ -279,6 +282,9 @@ CreateRole(CreateRoleStmt *stmt) errmsg("permission denied to create role"))); } + /* SELinux checks */ + securityId = sepgsql_role_create(stmt->role); + if (strcmp(stmt->role, "public") == 0 || strcmp(stmt->role, "none") == 0) ereport(ERROR, @@ -365,6 +371,8 @@ CreateRole(CreateRoleStmt *stmt) tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls); + HeapTupleSetSecid(tuple, securityId); + /* * Insert new record in the pg_authid table */ @@ -606,6 +614,8 @@ AlterRole(AlterRoleStmt *stmt) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied"))); } + /* SELinux checks */ + sepgsql_role_alter(roleid); /* Convert validuntil to internal form */ if (validUntil) @@ -791,6 +801,8 @@ AlterRoleSet(AlterRoleSetStmt *stmt) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied"))); } + /* SELinux checks */ + sepgsql_role_alter(HeapTupleGetOid(roletuple)); /* look up and lock the database, if specified */ if (stmt->database != NULL) @@ -886,6 +898,9 @@ DropRole(DropRoleStmt *stmt) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to drop superusers"))); + /* SELinux checks */ + sepgsql_role_drop(roleid, false); + /* * Lock the role, so nobody can add dependencies to her while we drop * her. We keep the lock until the end of transaction. @@ -1282,6 +1297,9 @@ AddRoleMems(const char *rolename, Oid roleid, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to set grantor"))); + /* SELinux checks */ + sepgsql_role_grant(roleid, true, memberIds); + pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock); pg_authmem_dsc = RelationGetDescr(pg_authmem_rel); @@ -1412,6 +1430,8 @@ DelRoleMems(const char *rolename, Oid roleid, errmsg("must have admin option on role \"%s\"", rolename))); } + /* SELinux checks */ + sepgsql_role_grant(roleid, false, memberIds); pg_authmem_rel = heap_open(AuthMemRelationId, RowExclusiveLock); pg_authmem_dsc = RelationGetDescr(pg_authmem_rel); diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 49a206e..4646c41 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -980,6 +980,12 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound, relation_close(onerel, NoLock); /* + * VACUUM FULL also reclaim orphan security labels, if exist + */ + if (vacstmt->options & VACOPT_FULL) + seclabelRelationReclaim(relid); + + /* * Complete the transaction and free all temporary memory used. */ PopActiveSnapshot(); diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index d7a06bc..5480c83 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -28,6 +28,7 @@ #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" #include "rewrite/rewriteSupport.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -167,6 +168,9 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace) 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 --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c index 5e555ad..963096d 100644 --- a/src/backend/executor/execJunk.c +++ b/src/backend/executor/execJunk.c @@ -58,7 +58,8 @@ * An optional resultSlot can be passed as well. */ JunkFilter * -ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) +ExecInitJunkFilter(List *targetList, bool hasoid, bool hassecid, + TupleTableSlot *slot) { JunkFilter *junkfilter; TupleDesc cleanTupType; @@ -70,7 +71,7 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) /* * Compute the tuple descriptor for the cleaned tuple. */ - cleanTupType = ExecCleanTypeFromTL(targetList, hasoid); + cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, hassecid); /* * Use the given slot, or make a new slot if we weren't given one. diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index d5e7e3a..96d4ba8 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -38,6 +38,7 @@ #include "access/xact.h" #include "catalog/heap.h" #include "catalog/namespace.h" +#include "catalog/pg_seclabel.h" #include "catalog/toasting.h" #include "commands/tablespace.h" #include "commands/trigger.h" @@ -47,11 +48,13 @@ #include "optimizer/clauses.h" #include "parser/parse_clause.h" #include "parser/parsetree.h" +#include "sepgsql/hooks.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/smgr.h" #include "tcop/utility.h" #include "utils/acl.h" +#include "utils/guc.h" #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/snapmgr.h" @@ -414,7 +417,16 @@ ExecCheckRTPerms(List *rangeTable) foreach(l, rangeTable) { - ExecCheckRTEPerms((RangeTblEntry *) lfirst(l)); + RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); + + ExecCheckRTEPerms(rte); + + if (rte->rtekind == RTE_RELATION) + sepgsql_relation_perms(rte->relid, + rte->requiredPerms, + rte->selectedCols, + rte->modifiedCols, + true); } } @@ -829,6 +841,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) j = ExecInitJunkFilter(planstate->plan->targetlist, tupType->tdhasoid, + tupType->tdhassecid, ExecInitExtraTupleSlot(estate)); estate->es_junkFilter = j; @@ -1065,6 +1078,37 @@ ExecContextForcesOids(PlanState *planstate, bool *hasoids) return false; } +/* + * ExecContextForcesSecids + * + * It is same with ExecContextForcesOids, except for it checks space + * for security id of the tuples. + */ +bool +ExecContextForcesSecids(PlanState *planstate, bool *hassecid) +{ + ResultRelInfo *ri = planstate->state->es_result_relation_info; + + if (ri != NULL) + { + Relation rel = ri->ri_RelationDesc; + + if (rel != NULL) + { + *hassecid = RelationGetForm(rel)->relhassecids; + return true; + } + } + + if (planstate->state->es_select_into) + { + *hassecid = default_with_secids; + return true; + } + + return false; +} + /* ---------------------------------------------------------------- * ExecEndPlan * @@ -2073,6 +2117,7 @@ OpenIntoRel(QueryDesc *queryDesc) Oid intoRelationId; TupleDesc tupdesc; DR_intorel *myState; + Oid *secLabels; static char *validnsps[] = HEAP_RELOPT_NAMESPACES; Assert(into); @@ -2144,6 +2189,14 @@ OpenIntoRel(QueryDesc *queryDesc) get_tablespace_name(tablespaceId)); } + /* SELinux checks */ + secLabels = sepgsql_relation_create(intoName, + RELKIND_RELATION, + queryDesc->tupDesc, + namespaceId, + NIL, + true); + /* Parse and validate any reloptions */ reloptions = transformRelOptions((Datum) 0, into->options, @@ -2174,7 +2227,8 @@ OpenIntoRel(QueryDesc *queryDesc) into->onCommit, reloptions, true, - allowSystemTableMods); + allowSystemTableMods, + secLabels); FreeTupleDesc(tupdesc); @@ -2305,6 +2359,11 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self) */ if (myState->rel->rd_rel->relhasoids) HeapTupleSetOid(tuple, InvalidOid); + if (myState->rel->rd_rel->relhassecids) + HeapTupleSetSecid(tuple, InvalidOid); + + /* SELinux checks */ + sepgsql_tuple_insert(myState->rel, tuple); heap_insert(myState->rel, tuple, diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 005e15e..6e93932 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -49,6 +49,7 @@ #include "optimizer/planner.h" #include "parser/parse_coerce.h" #include "pgstat.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -1177,6 +1178,9 @@ init_fcache(Oid foid, FuncExprState *fcache, if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid)); + /* SELinux checks */ + sepgsql_proc_execute(foid); + /* * Safety check on nargs. Under normal circumstances this should never * fail, as parser should check sooner. But possibly it might fail if @@ -1221,7 +1225,7 @@ init_fcache(Oid foid, FuncExprState *fcache, else if (functypclass == TYPEFUNC_SCALAR) { /* Base data type, i.e. scalar */ - tupdesc = CreateTemplateTupleDesc(1, false); + tupdesc = CreateTemplateTupleDesc(1, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, NULL, @@ -2111,7 +2115,7 @@ ExecMakeTableFunctionResult(ExprState *funcexpr, /* * Scalar type, so make a single-column descriptor */ - tupdesc = CreateTemplateTupleDesc(1, false); + tupdesc = CreateTemplateTupleDesc(1, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "column", @@ -4108,6 +4112,9 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(acoerce->elemfuncid)); + /* SELinux checks */ + sepgsql_proc_execute(acoerce->elemfuncid); + /* Set up the primary fmgr lookup information */ fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc), econtext->ecxt_per_query_memory); diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index 53fe195..cb63671 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -258,6 +258,7 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc int numattrs = tupdesc->natts; int attrno; bool hasoid; + bool hassecid; ListCell *tlist_item = list_head(tlist); /* Check the tlist attributes */ @@ -307,6 +308,9 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc if (ExecContextForcesOids(ps, &hasoid) && hasoid != tupdesc->tdhasoid) return false; + if (ExecContextForcesSecids(ps, &hassecid) && + hassecid != tupdesc->tdhassecid) + return false; return true; } diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 1caf976..64f6468 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -98,7 +98,7 @@ static TupleDesc ExecTypeFromTLInternal(List *targetList, - bool hasoid, bool skipjunk); + bool hasoid, bool hassecid, bool skipjunk); /* ---------------------------------------------------------------- @@ -899,9 +899,9 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType) * ---------------------------------------------------------------- */ TupleDesc -ExecTypeFromTL(List *targetList, bool hasoid) +ExecTypeFromTL(List *targetList, bool hasoid, bool hassecid) { - return ExecTypeFromTLInternal(targetList, hasoid, false); + return ExecTypeFromTLInternal(targetList, hasoid, hassecid, false); } /* ---------------------------------------------------------------- @@ -911,13 +911,14 @@ ExecTypeFromTL(List *targetList, bool hasoid) * ---------------------------------------------------------------- */ TupleDesc -ExecCleanTypeFromTL(List *targetList, bool hasoid) +ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hassecid) { - return ExecTypeFromTLInternal(targetList, hasoid, true); + return ExecTypeFromTLInternal(targetList, hasoid, hassecid, true); } static TupleDesc -ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) +ExecTypeFromTLInternal(List *targetList, + bool hasoid, bool hassecid, bool skipjunk) { TupleDesc typeInfo; ListCell *l; @@ -928,7 +929,7 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) len = ExecCleanTargetListLength(targetList); else len = ExecTargetListLength(targetList); - typeInfo = CreateTemplateTupleDesc(len, hasoid); + typeInfo = CreateTemplateTupleDesc(len, hasoid, hassecid); foreach(l, targetList) { @@ -960,7 +961,7 @@ ExecTypeFromExprList(List *exprList) int cur_resno = 1; char fldname[NAMEDATALEN]; - typeInfo = CreateTemplateTupleDesc(list_length(exprList), false); + typeInfo = CreateTemplateTupleDesc(list_length(exprList), false, false); foreach(l, exprList) { diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 98e4a64..14f9404 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -444,6 +444,7 @@ void ExecAssignResultTypeFromTL(PlanState *planstate) { bool hasoid; + bool hassecid; TupleDesc tupDesc; if (ExecContextForcesOids(planstate, &hasoid)) @@ -456,12 +457,15 @@ ExecAssignResultTypeFromTL(PlanState *planstate) 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); + tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid, hassecid); ExecAssignResultType(planstate, tupDesc); } diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index d552698..ed1913f 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -1153,7 +1153,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, /* Set up junk filter if needed */ if (junkFilter) - *junkFilter = ExecInitJunkFilter(tlist, false, NULL); + *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); } else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) { @@ -1192,7 +1192,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, } /* Set up junk filter if needed */ if (junkFilter) - *junkFilter = ExecInitJunkFilter(tlist, false, NULL); + *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); return false; /* NOT returning whole tuple */ } } @@ -1205,7 +1205,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, * what the caller expects will happen at runtime. */ if (junkFilter) - *junkFilter = ExecInitJunkFilter(tlist, false, NULL); + *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); return true; } Assert(tupdesc); diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index ddd91fc..cfb685e 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -89,6 +89,7 @@ #include "optimizer/tlist.h" #include "parser/parse_agg.h" #include "parser/parse_coerce.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -1641,6 +1642,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) get_func_name(finalfn_oid)); } } + /* SELinux checks */ + sepgsql_aggregate_execute(aggref->aggfnoid); /* resolve actual type of transition state, if polymorphic */ aggtranstype = aggform->aggtranstype; @@ -1722,7 +1725,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) * Get a tupledesc corresponding to the inputs (including sort * expressions) of the agg. */ - peraggstate->evaldesc = ExecTypeFromTL(aggref->args, false); + peraggstate->evaldesc = ExecTypeFromTL(aggref->args, false, false); /* Create slot we're going to do argument evaluation in */ peraggstate->evalslot = ExecInitExtraTupleSlot(estate); diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index 66e6b74..a31907c 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -178,7 +178,7 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) /* Base data type, i.e. scalar */ char *attname = strVal(linitial(node->funccolnames)); - tupdesc = CreateTemplateTupleDesc(1, false); + tupdesc = CreateTemplateTupleDesc(1, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, attname, diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index ca6b0f8..b5c79d0 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -98,6 +98,7 @@ #include "executor/execdefs.h" #include "executor/nodeMergejoin.h" #include "miscadmin.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/lsyscache.h" #include "utils/memutils.h" @@ -216,6 +217,9 @@ MJExamineQuals(List *mergeclauses, aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(cmpproc)); + /* SELinux permissions */ + sepgsql_proc_execute(cmpproc); + /* Set up the fmgr lookup information */ fmgr_info(cmpproc, &(clause->cmpfinfo)); diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 7856b66..eee2739 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -38,11 +38,13 @@ #include "postgres.h" #include "access/xact.h" +#include "catalog/pg_seclabel.h" #include "commands/trigger.h" #include "executor/executor.h" #include "executor/nodeModifyTable.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" +#include "sepgsql/hooks.h" #include "storage/bufmgr.h" #include "utils/builtins.h" #include "utils/memutils.h" @@ -160,7 +162,8 @@ ExecProcessReturning(ProjectionInfo *projectReturning, static TupleTableSlot * ExecInsert(TupleTableSlot *slot, TupleTableSlot *planSlot, - EState *estate) + EState *estate, + Oid securityId) { HeapTuple tuple; ResultRelInfo *resultRelInfo; @@ -194,6 +197,8 @@ ExecInsert(TupleTableSlot *slot, */ if (resultRelationDesc->rd_rel->relhasoids) HeapTupleSetOid(tuple, InvalidOid); + if (resultRelationDesc->rd_rel->relhassecids) + HeapTupleSetSecid(tuple, securityId); /* BEFORE ROW INSERT Triggers */ if (resultRelInfo->ri_TrigDesc && @@ -224,6 +229,8 @@ ExecInsert(TupleTableSlot *slot, tuple = newtuple; } } + /* SELinux checks */ + sepgsql_tuple_insert(resultRelationDesc, tuple); /* * Check the constraints of the tuple @@ -421,7 +428,8 @@ ExecUpdate(ItemPointer tupleid, TupleTableSlot *slot, TupleTableSlot *planSlot, EPQState *epqstate, - EState *estate) + EState *estate, + Oid securityId) { HeapTuple tuple; ResultRelInfo *resultRelInfo; @@ -449,6 +457,13 @@ ExecUpdate(ItemPointer tupleid, resultRelInfo = estate->es_result_relation_info; resultRelationDesc = resultRelInfo->ri_RelationDesc; + /* + * If the result relation has writable system attributes, + * we store user given value (or InvalidOid) on the tuple. + */ + if (resultRelationDesc->rd_rel->relhassecids) + HeapTupleSetSecid(tuple, securityId); + /* BEFORE ROW UPDATE Triggers */ if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) @@ -479,6 +494,8 @@ ExecUpdate(ItemPointer tupleid, tuple = newtuple; } } + /* SELinux checks */ + sepgsql_tuple_update(resultRelationDesc, tupleid, tuple); /* * Check the constraints of the tuple @@ -635,6 +652,42 @@ fireASTriggers(ModifyTableState *node) } } +/* + * FetchWritableSecLabel + * + * It moves user given security label into slot-> + */ +static Oid +FetchWritableSecLabel(Relation relation, + JunkFilter *junkfilter, + TupleTableSlot *slot) +{ + Oid securityId = InvalidOid; + AttrNumber attno; + Datum datum; + bool isnull; + char *label; + + /* + * If no explicit label was given, set a default label later + */ + attno = ExecFindJunkAttribute(junkfilter, "security_label"); + if (attno != InvalidAttrNumber && !ignore_security_label_input) + { + datum = ExecGetJunkAttribute(slot, attno, &isnull); + if (isnull) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Unable to set NULL on \"security_label\""))); + + label = TextDatumGetCString(datum); + + securityId = seclabelTransInput(RelationGetRelid(relation), label); + + pfree(label); + } + return securityId; +} /* ---------------------------------------------------------------- * ExecModifyTable @@ -682,6 +735,8 @@ ExecModifyTable(ModifyTableState *node) */ for (;;) { + Oid securityId = InvalidOid; + planSlot = ExecProcNode(subplanstate); if (TupIsNull(planSlot)) @@ -705,6 +760,14 @@ ExecModifyTable(ModifyTableState *node) if (junkfilter != NULL) { + Relation targetRel + = estate->es_result_relation_info->ri_RelationDesc; + + /* + * extract writable system column + */ + securityId = FetchWritableSecLabel(targetRel, junkfilter, slot); + /* * extract the 'ctid' junk attribute. */ @@ -734,11 +797,11 @@ ExecModifyTable(ModifyTableState *node) switch (operation) { case CMD_INSERT: - slot = ExecInsert(slot, planSlot, estate); + slot = ExecInsert(slot, planSlot, estate, securityId); break; case CMD_UPDATE: slot = ExecUpdate(tupleid, slot, planSlot, - &node->mt_epqstate, estate); + &node->mt_epqstate, estate, securityId); break; case CMD_DELETE: slot = ExecDelete(tupleid, planSlot, @@ -850,7 +913,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) * RETURNING list. We assume the rest will look the same. */ tupDesc = ExecTypeFromTL((List *) linitial(node->returningLists), - false); + false, false); /* Set up a slot for the output of the RETURNING projection(s) */ ExecInitResultTupleSlot(estate, &mtstate->ps); @@ -884,7 +947,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) * We still must construct a dummy result tuple type, because InitPlan * expects one (maybe should change that?). */ - tupDesc = ExecTypeFromTL(NIL, false); + tupDesc = ExecTypeFromTL(NIL, false, false); ExecInitResultTupleSlot(estate, &mtstate->ps); ExecAssignResultType(&mtstate->ps, tupDesc); @@ -976,6 +1039,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) j = ExecInitJunkFilter(subplan->targetlist, resultRelInfo->ri_RelationDesc->rd_att->tdhasoid, + resultRelInfo->ri_RelationDesc->rd_att->tdhassecid, ExecInitExtraTupleSlot(estate)); if (operation == CMD_UPDATE || operation == CMD_DELETE) diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index 9f1ff16..bef704b 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -859,7 +859,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. */ - tupDesc = ExecTypeFromTL(leftptlist, false); + tupDesc = ExecTypeFromTL(leftptlist, false, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); sstate->projLeft = ExecBuildProjectionInfo(lefttlist, @@ -867,7 +867,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) slot, NULL); - tupDesc = ExecTypeFromTL(rightptlist, false); + tupDesc = ExecTypeFromTL(rightptlist, false, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); sstate->projRight = ExecBuildProjectionInfo(righttlist, diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index 712bab1..e69f3c7 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -43,6 +43,7 @@ #include "optimizer/clauses.h" #include "parser/parse_agg.h" #include "parser/parse_coerce.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/datum.h" @@ -1554,6 +1555,12 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(wfunc->winfnoid)); + /* SELinux checks */ + if (wfunc->winagg) + sepgsql_aggregate_execute(wfunc->winfnoid); + else + sepgsql_proc_execute(wfunc->winfnoid); + /* Fill in the perfuncstate data */ perfuncstate->wfuncstate = wfuncstate; perfuncstate->wfunc = wfunc; diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 7f0b5e4..2e2c63b 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -767,6 +767,8 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum, mtuple->t_tableOid = tuple->t_tableOid; if (rel->rd_att->tdhasoid) HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple)); + if (rel->rd_att->tdhassecid) + HeapTupleSetSecid(mtuple, HeapTupleGetSecid(tuple)); } else { @@ -795,7 +797,8 @@ SPI_fnumber(TupleDesc tupdesc, const char *fname) return res + 1; } - sysatt = SystemAttributeByName(fname, true /* "oid" will be accepted */ ); + /* "oid" and "security_label" will be accepted */ + sysatt = SystemAttributeByName(fname, true, true); if (sysatt != NULL) return sysatt->attnum; @@ -820,7 +823,7 @@ SPI_fname(TupleDesc tupdesc, int fnumber) if (fnumber > 0) att = tupdesc->attrs[fnumber - 1]; else - att = SystemAttributeDefinition(fnumber, true); + att = SystemAttributeDefinition(fnumber, true, true); return pstrdup(NameStr(att->attname)); } @@ -852,7 +855,7 @@ SPI_getvalue(HeapTuple tuple, TupleDesc tupdesc, int fnumber) if (fnumber > 0) typoid = tupdesc->attrs[fnumber - 1]->atttypid; else - typoid = (SystemAttributeDefinition(fnumber, true))->atttypid; + typoid = (SystemAttributeDefinition(fnumber, true, true))->atttypid; getTypeOutputInfo(typoid, &foutoid, &typisvarlena); @@ -909,7 +912,7 @@ SPI_gettype(TupleDesc tupdesc, int fnumber) if (fnumber > 0) typoid = tupdesc->attrs[fnumber - 1]->atttypid; else - typoid = (SystemAttributeDefinition(fnumber, true))->atttypid; + typoid = (SystemAttributeDefinition(fnumber, true, true))->atttypid; typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typoid)); @@ -939,7 +942,7 @@ SPI_gettypeid(TupleDesc tupdesc, int fnumber) if (fnumber > 0) return tupdesc->attrs[fnumber - 1]->atttypid; else - return (SystemAttributeDefinition(fnumber, true))->atttypid; + return (SystemAttributeDefinition(fnumber, true, true))->atttypid; } char * diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c index f4f50f8..4ab5698 100644 --- a/src/backend/libpq/be-fsstubs.c +++ b/src/backend/libpq/be-fsstubs.c @@ -46,6 +46,7 @@ #include "libpq/be-fsstubs.h" #include "libpq/libpq-fs.h" #include "miscadmin.h" +#include "sepgsql/hooks.h" #include "storage/fd.h" #include "storage/large_object.h" #include "utils/acl.h" @@ -172,6 +173,8 @@ lo_read(int fd, char *buf, int len) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied for large object %u", cookies[fd]->id))); + /* SELinux checks */ + sepgsql_largeobject_read(cookies[fd]->id, cookies[fd]->snapshot); status = inv_read(cookies[fd], buf, len); @@ -204,6 +207,8 @@ lo_write(int fd, const char *buf, int len) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied for large object %u", cookies[fd]->id))); + /* SELinux checks */ + sepgsql_largeobject_write(cookies[fd]->id, cookies[fd]->snapshot); status = inv_write(cookies[fd], buf, len); @@ -233,6 +238,7 @@ Datum lo_creat(PG_FUNCTION_ARGS) { Oid lobjId; + Oid securityId; /* * We don't actually need to store into fscxt, but create it anyway to @@ -240,7 +246,10 @@ lo_creat(PG_FUNCTION_ARGS) */ CreateFSContext(); - lobjId = inv_create(InvalidOid); + /* SELinux checks */ + securityId = sepgsql_largeobject_create(InvalidOid); + + lobjId = inv_create(InvalidOid, securityId); PG_RETURN_OID(lobjId); } @@ -249,6 +258,7 @@ Datum lo_create(PG_FUNCTION_ARGS) { Oid lobjId = PG_GETARG_OID(0); + Oid securityId; /* * We don't actually need to store into fscxt, but create it anyway to @@ -256,7 +266,10 @@ lo_create(PG_FUNCTION_ARGS) */ CreateFSContext(); - lobjId = inv_create(lobjId); + /* SELinux checks */ + securityId = sepgsql_largeobject_create(lobjId); + + lobjId = inv_create(lobjId, securityId); PG_RETURN_OID(lobjId); } @@ -286,6 +299,9 @@ lo_unlink(PG_FUNCTION_ARGS) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be owner of large object %u", lobjId))); + /* SELinux checks */ + sepgsql_largeobject_drop(lobjId, false); + /* * If there are any open LO FDs referencing that ID, close 'em. */ @@ -381,9 +397,10 @@ lo_import_internal(text *filename, Oid lobjOid) int nbytes, tmp; char buf[BUFSIZE]; - char fnamebuf[MAXPGPATH]; + char *fnamebuf = text_to_cstring(filename); LargeObjectDesc *lobj; Oid oid; + Oid securityId; #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS if (!superuser()) @@ -392,13 +409,14 @@ lo_import_internal(text *filename, Oid lobjOid) errmsg("must be superuser to use server-side lo_import()"), errhint("Anyone can use the client-side lo_import() provided by libpq."))); #endif - CreateFSContext(); + /* SELinux checks */ + securityId = sepgsql_largeobject_import(lobjOid, fnamebuf); + /* * open the file to be read in */ - text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, 0666); if (fd < 0) ereport(ERROR, @@ -409,7 +427,7 @@ lo_import_internal(text *filename, Oid lobjOid) /* * create an inversion object */ - oid = inv_create(lobjOid); + oid = inv_create(lobjOid, securityId); /* * read in from the filesystem and write to the inversion object @@ -447,7 +465,7 @@ lo_export(PG_FUNCTION_ARGS) int nbytes, tmp; char buf[BUFSIZE]; - char fnamebuf[MAXPGPATH]; + char *fnamebuf = text_to_cstring(filename); LargeObjectDesc *lobj; mode_t oumask; @@ -458,7 +476,6 @@ lo_export(PG_FUNCTION_ARGS) errmsg("must be superuser to use server-side lo_export()"), errhint("Anyone can use the client-side lo_export() provided by libpq."))); #endif - CreateFSContext(); /* @@ -466,6 +483,9 @@ lo_export(PG_FUNCTION_ARGS) */ lobj = inv_open(lobjId, INV_READ, fscxt); + /* SELinux checks */ + sepgsql_largeobject_export(lobj->id, lobj->snapshot, fnamebuf); + /* * open the file to be written to * @@ -473,7 +493,6 @@ lo_export(PG_FUNCTION_ARGS) * 022. This code used to drop it all the way to 0, but creating * world-writable export files doesn't seem wise. */ - text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); oumask = umask((mode_t) 0022); fd = PathNameOpenFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666); umask(oumask); @@ -528,6 +547,9 @@ lo_truncate(PG_FUNCTION_ARGS) errmsg("permission denied for large object %u", cookies[fd]->id))); + /* SELinux checks */ + sepgsql_largeobject_write(cookies[fd]->id, cookies[fd]->snapshot); + inv_truncate(cookies[fd], len); PG_RETURN_INT32(0); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index e770e89..9642a66 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1823,6 +1823,7 @@ _copyRangeTblEntry(RangeTblEntry *from) COPY_SCALAR_FIELD(checkAsUser); COPY_BITMAPSET_FIELD(selectedCols); COPY_BITMAPSET_FIELD(modifiedCols); + COPY_SCALAR_FIELD(rowlvPerms); return newnode; } @@ -2755,6 +2756,21 @@ _copyAlterOwnerStmt(AlterOwnerStmt *from) return newnode; } +static AlterSecLabelStmt * +_copyAlterSecLabelStmt(AlterSecLabelStmt *from) +{ + AlterSecLabelStmt *newnode = makeNode(AlterSecLabelStmt); + + COPY_SCALAR_FIELD(objectType); + COPY_NODE_FIELD(relation); + COPY_NODE_FIELD(object); + COPY_NODE_FIELD(objarg); + COPY_STRING_FIELD(addname); + COPY_NODE_FIELD(secLabel); + + return newnode; +} + static RuleStmt * _copyRuleStmt(RuleStmt *from) { @@ -3968,6 +3984,9 @@ copyObject(void *from) case T_AlterOwnerStmt: retval = _copyAlterOwnerStmt(from); break; + case T_AlterSecLabelStmt: + retval = _copyAlterSecLabelStmt(from); + break; case T_RuleStmt: retval = _copyRuleStmt(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 5d83727..fd4071d 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1308,6 +1308,19 @@ _equalAlterOwnerStmt(AlterOwnerStmt *a, AlterOwnerStmt *b) } 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(addname); + COMPARE_NODE_FIELD(secLabel); + + return true; +} + +static bool _equalRuleStmt(RuleStmt *a, RuleStmt *b) { COMPARE_NODE_FIELD(relation); @@ -2186,6 +2199,7 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b) COMPARE_SCALAR_FIELD(checkAsUser); COMPARE_BITMAPSET_FIELD(selectedCols); COMPARE_BITMAPSET_FIELD(modifiedCols); + COMPARE_SCALAR_FIELD(rowlvPerms); return true; } @@ -2657,6 +2671,9 @@ equal(void *a, void *b) case T_AlterOwnerStmt: retval = _equalAlterOwnerStmt(a, b); break; + case T_AlterSecLabelStmt: + retval = _equalAlterSecLabelStmt(a, b); + break; case T_RuleStmt: retval = _equalRuleStmt(a, b); break; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index e7dae4b..4e8b350 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -2135,6 +2135,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) WRITE_OID_FIELD(checkAsUser); WRITE_BITMAPSET_FIELD(selectedCols); WRITE_BITMAPSET_FIELD(modifiedCols); + WRITE_UINT_FIELD(rowlvPerms); } static void diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index bc6e2a6..792b9ff 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1171,6 +1171,7 @@ _readRangeTblEntry(void) READ_OID_FIELD(checkAsUser); READ_BITMAPSET_FIELD(selectedCols); READ_BITMAPSET_FIELD(modifiedCols); + READ_UINT_FIELD(rowlvPerms); READ_DONE(); } diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index a0e31a2..086b313 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -32,6 +32,7 @@ #include "optimizer/var.h" #include "parser/parse_clause.h" #include "parser/parsetree.h" +#include "sepgsql/hooks.h" #include "utils/lsyscache.h" @@ -305,6 +306,9 @@ create_scan_plan(PlannerInfo *root, Path *best_path) break; } + /* Append row-level access control policy */ + sepgsql_rowlv_add_policy(root, (Scan *)plan); + /* * If there are any pseudoconstant clauses attached to this node, insert a * gating Result node that evaluates the pseudoconstants as one-time diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index e525ba6..6474fbc 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -38,6 +38,7 @@ #include "parser/parse_coerce.h" #include "parser/parse_func.h" #include "rewrite/rewriteManip.h" +#include "sepgsql/hooks.h" #include "tcop/tcopprot.h" #include "utils/acl.h" #include "utils/builtins.h" @@ -3712,6 +3713,10 @@ inline_function(Oid funcid, Oid result_type, List *args, if (pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK) return NULL; + /* SELinux checks */ + if (!sepgsql_proc_be_inlined(func_tuple)) + return NULL; + /* * Make a temporary memory context, so that we don't leak all the stuff * that parsing might create. @@ -4164,7 +4169,8 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) funcform->provolatile == PROVOLATILE_VOLATILE || funcform->prosecdef || !funcform->proretset || - !heap_attisnull(func_tuple, Anum_pg_proc_proconfig)) + !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || + !sepgsql_proc_be_inlined(func_tuple)) { ReleaseSysCache(func_tuple); return NULL; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 6b99a10..4eb9d24 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -25,6 +25,7 @@ #include "postgres.h" #include "access/sysattr.h" +#include "catalog/heap.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" @@ -40,6 +41,7 @@ #include "parser/parse_target.h" #include "parser/parsetree.h" #include "rewrite/rewriteManip.h" +#include "utils/guc.h" #include "utils/rel.h" @@ -651,7 +653,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) tle = makeTargetEntry(expr, attr_num, col->name, - false); + attr_num < 0 ? true : false); qry->targetList = lappend(qry->targetList, tle); rte->modifiedCols = bms_add_member(rte->modifiedCols, diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index b793c4d..866e0ba 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -183,8 +183,8 @@ static TypeName *TableFuncTypeName(List *columns); %type stmt schema_stmt AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt AlterForeignServerStmt AlterGroupStmt - AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt - AlterUserStmt AlterUserMappingStmt AlterUserSetStmt + AlterObjectSchemaStmt AlterOwnerStmt AlterSecLabelStmt AlterSeqStmt + AlterTableStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt AlterDefaultPrivilegesStmt DefACLAction AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt @@ -422,6 +422,8 @@ static TypeName *TableFuncTypeName(List *columns); %type OptTableSpace OptConsTableSpace OptTableSpaceOwner %type opt_check_option +%type SecLabelItem + %type xml_attribute_el %type xml_attribute_list xml_attributes %type xml_root_version opt_xml_root_standalone @@ -498,7 +500,7 @@ static TypeName *TableFuncTypeName(List *columns); KEY - LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING + LABEL LANGUAGE LARGE_P LAST_P LC_COLLATE_P LC_CTYPE_P LEADING LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOGIN_P @@ -654,6 +656,7 @@ stmt : | AlterGroupStmt | AlterObjectSchemaStmt | AlterOwnerStmt + | AlterSecLabelStmt | AlterSeqStmt | AlterTableStmt | AlterRoleSetStmt @@ -1758,6 +1761,20 @@ alter_table_cmd: n->subtype = AT_DropOids; $$ = (Node *)n; } + /* ALTER TABLE SET WITH SECURITY LABEL */ + | SET WITH SECURITY LABEL + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_AddSecLabel; + $$ = (Node *)n; + } + /* ALTER TABLE SET WITHOUT SECURITY LABEL */ + | SET WITHOUT SECURITY LABEL + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropSecLabel; + $$ = (Node *)n; + } /* ALTER TABLE CLUSTER ON */ | CLUSTER ON name { @@ -6022,6 +6039,102 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId } ; +/***************************************************************************** + * + * ALTER THING name SECURITY LABEL TO new_label + * + *****************************************************************************/ + +AlterSecLabelStmt: ALTER DATABASE database_name SecLabelItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_DATABASE; + n->object = list_make1(makeString($3)); + n->secLabel = $4; + $$ = (Node *)n; + } + | ALTER SCHEMA name SecLabelItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_SCHEMA; + n->object = list_make1(makeString($3)); + n->secLabel = $4; + $$ = (Node *)n; + } + | ALTER TABLE relation_expr SecLabelItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_TABLE; + n->relation = $3; + n->secLabel = $4; + $$ = (Node *)n; + } + | ALTER TABLE relation_expr ALTER opt_column ColId SecLabelItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_COLUMN; + n->relation = $3; + n->addname = $6; + n->secLabel = $7; + $$ = (Node *)n; + } + | ALTER SEQUENCE qualified_name SecLabelItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_SEQUENCE; + n->relation = $3; + n->secLabel = $4; + $$ = (Node *)n; + } + | ALTER VIEW qualified_name SecLabelItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_VIEW; + n->relation = $3; + n->secLabel = $4; + $$ = (Node *)n; + } + | ALTER FUNCTION function_with_argtypes SecLabelItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_FUNCTION; + n->object = $3->funcname; + n->objarg = $3->funcargs; + n->secLabel = $4; + $$ = (Node *)n; + } + | ALTER AGGREGATE func_name aggr_args SecLabelItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_AGGREGATE; + n->object = $3; + n->objarg = $4; + n->secLabel = $5; + $$ = (Node *)n; + } + | ALTER LARGE_P OBJECT_P Iconst SecLabelItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_LARGEOBJECT; + n->object = list_make1(makeInteger($4)); + n->secLabel = $5; + $$ = (Node *)n; + } + | ALTER TYPE_P any_name SecLabelItem + { + AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); + n->objectType = OBJECT_TYPE; + n->object = $3; + n->secLabel = $4; + $$ = (Node *)n; + } + ; + +SecLabelItem: SECURITY LABEL TO Sconst + { + $$ = makeString($4); + } + ; /***************************************************************************** * @@ -10921,6 +11034,7 @@ unreserved_keyword: | INVOKER | ISOLATION | KEY + | LABEL | LANGUAGE | LARGE_P | LAST_P diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index eb6505a..1962055 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -2302,8 +2302,8 @@ specialAttNum(const char *attname) { Form_pg_attribute sysatt; - sysatt = SystemAttributeByName(attname, - true /* "oid" will be accepted */ ); + /* "oid" and "security_label" will be accepted */ + sysatt = SystemAttributeByName(attname, true, true); if (sysatt != NULL) return sysatt->attnum; return InvalidAttrNumber; @@ -2324,7 +2324,9 @@ attnumAttName(Relation rd, int attid) { Form_pg_attribute sysatt; - sysatt = SystemAttributeDefinition(attid, rd->rd_rel->relhasoids); + sysatt = SystemAttributeDefinition(attid, + rd->rd_rel->relhasoids, + rd->rd_rel->relhassecids); return &sysatt->attname; } if (attid > rd->rd_att->natts) @@ -2346,7 +2348,9 @@ attnumTypeId(Relation rd, int attid) { Form_pg_attribute sysatt; - sysatt = SystemAttributeDefinition(attid, rd->rd_rel->relhasoids); + sysatt = SystemAttributeDefinition(attid, + rd->rd_rel->relhasoids, + rd->rd_rel->relhassecids); return sysatt->atttypid; } if (attid > rd->rd_att->natts) diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index e542dc0..8191f94 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -14,6 +14,7 @@ */ #include "postgres.h" +#include "catalog/heap.h" #include "catalog/pg_type.h" #include "commands/dbcommands.h" #include "funcapi.h" @@ -365,16 +366,34 @@ transformAssignedExpr(ParseState *pstate, Oid attrtype; /* type of target column */ int32 attrtypmod; Relation rd = pstate->p_target_relation; + bool relhasoids = RelationGetForm(rd)->relhasoids; + bool relhassecids = RelationGetForm(rd)->relhassecids; Assert(rd != NULL); - if (attrno <= 0) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot assign to system column \"%s\"", - colname), - parser_errposition(pstate, location))); - attrtype = attnumTypeId(rd, attrno); - attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; + if (attrno > 0) + { + attrtype = attnumTypeId(rd, attrno); + attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; + } + else + { + Form_pg_attribute attr; + + attr = SystemAttributeDefinition(attrno, relhasoids, relhassecids); + if (attr && SystemAttributeWritable(attrno, relhasoids, relhassecids)) + { + attrtype = attr->atttypid; + attrtypmod = attr->atttypmod; + } + else + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot assign to system column \"%s\"", colname), + parser_errposition(pstate, location))); + return NULL; + } + } /* * If the expression is a DEFAULT placeholder, insert the attribute's @@ -503,6 +522,10 @@ updateTargetListEntry(ParseState *pstate, List *indirection, int location) { + Relation rel = pstate->p_target_relation; + bool relhasoids = RelationGetForm(rel)->relhasoids; + bool relhassecids = RelationGetForm(rel)->relhassecids; + /* Fix up expression as needed */ tle->expr = transformAssignedExpr(pstate, tle->expr, @@ -519,6 +542,9 @@ updateTargetListEntry(ParseState *pstate, */ tle->resno = (AttrNumber) attrno; tle->resname = colname; + + if (SystemAttributeWritable(attrno, relhasoids, relhassecids)) + tle->resjunk = true; } @@ -793,6 +819,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) Bitmapset *wholecols = NULL; Bitmapset *partialcols = NULL; ListCell *tl; + uint32 system_attrs = 0UL; foreach(tl, cols) { @@ -801,14 +828,42 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) int attrno; /* Lookup column name, ereport on failure */ - attrno = attnameAttNum(pstate->p_target_relation, name, false); + attrno = attnameAttNum(pstate->p_target_relation, name, true); if (attrno == InvalidAttrNumber) + { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", name, RelationGetRelationName(pstate->p_target_relation)), parser_errposition(pstate, col->location))); + } + else if (attrno < 0) + { + Relation rel = pstate->p_target_relation; + bool relhasoids = RelationGetForm(rel)->relhasoids; + bool relhassecids = RelationGetForm(rel)->relhassecids; + + if (SystemAttributeWritable(attrno, relhasoids, relhassecids)) + { + 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(rel)), + parser_errposition(pstate, col->location))); + } /* * Check for duplicates, but only of whole columns --- we allow @@ -1263,7 +1318,7 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup) expandRTE(rte, var->varno, 0, var->location, false, &names, &vars); - tupleDesc = CreateTemplateTupleDesc(list_length(vars), false); + tupleDesc = CreateTemplateTupleDesc(list_length(vars), false, false); i = 1; forboth(lname, names, lvar, vars) { diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 90d5c76..b78f9ba 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -53,8 +53,10 @@ #include "parser/parse_utilcmd.h" #include "parser/parser.h" #include "rewrite/rewriteManip.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/guc.h" #include "utils/lsyscache.h" #include "utils/relcache.h" #include "utils/syscache.h" @@ -70,6 +72,7 @@ typedef struct List *inhRelations; /* relations to inherit from */ bool isalter; /* true if altering existing table */ bool hasoids; /* does relation have an OID column? */ + bool hassecids; /* does relation have an security label? */ List *columns; /* ColumnDef items */ List *ckconstraints; /* CHECK constraints */ List *fkconstraints; /* FOREIGN KEY constraints */ @@ -185,6 +188,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) cxt.alist = NIL; cxt.pkey = NULL; cxt.hasoids = interpretOidsOption(stmt->options); + cxt.hassecids = default_with_secids; Assert(!stmt->ofTypename || !stmt->inhRelations); /* grammar enforces */ @@ -587,6 +591,9 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(relation)); + /* SELinux checks */ + sepgsql_relation_getattr(RelationGetRelid(relation)); + tupleDesc = RelationGetDescr(relation); constr = tupleDesc->constr; @@ -1368,7 +1375,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) if (constraint->contype == CONSTR_PRIMARY) column->is_not_null = TRUE; } - else if (SystemAttributeByName(key, cxt->hasoids) != NULL) + else if (SystemAttributeByName(key, cxt->hasoids, cxt->hassecids) != NULL) { /* * column will be a system column in the new table, so accept it. @@ -1945,6 +1952,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) cxt.inhRelations = NIL; cxt.isalter = true; cxt.hasoids = false; /* need not be right */ + cxt.hassecids = false; /* need not be right */ cxt.columns = NIL; cxt.ckconstraints = NIL; cxt.fkconstraints = NIL; diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 98ab484..44c7a56 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -109,6 +109,7 @@ #include "postmaster/postmaster.h" #include "postmaster/syslogger.h" #include "replication/walsender.h" +#include "sepgsql/hooks.h" #include "storage/fd.h" #include "storage/ipc.h" #include "storage/pg_shmem.h" @@ -212,7 +213,8 @@ static pid_t StartupPID = 0, AutoVacPID = 0, PgArchPID = 0, PgStatPID = 0, - SysLoggerPID = 0; + SysLoggerPID = 0, + SecWorkerPID = 0; /* Startup/shutdown state */ #define NoShutdown 0 @@ -466,6 +468,7 @@ static void ShmemBackendArrayRemove(Backend *bn); #define StartBackgroundWriter() StartChildProcess(BgWriterProcess) #define StartWalWriter() StartChildProcess(WalWriterProcess) #define StartWalReceiver() StartChildProcess(WalReceiverProcess) +#define StartSecurityWorker() StartChildProcess(SecurityWorkerProcess) /* Macros to check exit status of a child process */ #define EXIT_STATUS_0(st) ((st) == 0) @@ -1473,6 +1476,11 @@ ServerLoop(void) if (PgStatPID == 0 && pmState == PM_RUN) PgStatPID = pgstat_start(); + /* If we have lost security worker, try to start a new one */ + if (SecWorkerPID == 0 && pmState == PM_RUN && + sepgsql_worker_needed()) + SecWorkerPID = StartSecurityWorker(); + /* If we need to signal the autovacuum launcher, do so now */ if (avlauncher_needs_signal) { @@ -2113,6 +2121,8 @@ SIGHUP_handler(SIGNAL_ARGS) signal_child(SysLoggerPID, SIGHUP); if (PgStatPID != 0) signal_child(PgStatPID, SIGHUP); + if (SecWorkerPID != 0) + signal_child(SecWorkerPID, SIGHUP); /* Reload authentication config files too */ if (!load_hba()) @@ -2173,6 +2183,9 @@ pmdie(SIGNAL_ARGS) /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); + /* and the security worker too */ + if (SecWorkerPID != 0) + signal_child(SecWorkerPID, SIGTERM); pmState = PM_WAIT_BACKUP; } @@ -2223,6 +2236,9 @@ pmdie(SIGNAL_ARGS) /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); + /* and the security worker too */ + if (SecWorkerPID != 0) + signal_child(SecWorkerPID, SIGTERM); pmState = PM_WAIT_BACKENDS; } @@ -2258,6 +2274,8 @@ pmdie(SIGNAL_ARGS) signal_child(PgArchPID, SIGQUIT); if (PgStatPID != 0) signal_child(PgStatPID, SIGQUIT); + if (SecWorkerPID != 0) + signal_child(SecWorkerPID, SIGQUIT); ExitPostmaster(0); break; } @@ -2529,6 +2547,16 @@ reaper(SIGNAL_ARGS) continue; } + /* Was it the security worker process? */ + if (pid == SecWorkerPID) + { + SecWorkerPID = 0; + if (!EXIT_STATUS_0(exitstatus)) + LogChildExit(LOG, _("security worker process"), + pid, exitstatus); + continue; + } + /* * Else do standard backend child cleanup. */ @@ -2732,6 +2760,18 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT)); } + /* Take care of the security worker process too */ + if (pid == SecWorkerPID) + SecWorkerPID = 0; + else if (SecWorkerPID != 0 && !FatalError) + { + ereport(DEBUG2, + (errmsg_internal("sending %s to process %d", + (SendStop ? "SIGSTOP" : "SIGQUIT"), + (int) SecWorkerPID))); + signal_child(SecWorkerPID, (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 @@ -2867,7 +2907,8 @@ PostmasterStateMachine(void) WalReceiverPID == 0 && (BgWriterPID == 0 || !FatalError) && WalWriterPID == 0 && - AutoVacPID == 0) + AutoVacPID == 0 && + SecWorkerPID == 0) { if (FatalError) { diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 68be146..6ba1d2f 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -27,6 +27,7 @@ #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteManip.h" #include "rewrite/rewriteSupport.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/inval.h" @@ -265,6 +266,9 @@ DefineQueryRewrite(char *rulename, 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 --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 25b44dd..e35f7c5 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -23,6 +23,7 @@ #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" +#include "sepgsql/hooks.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "commands/trigger.h" @@ -1938,6 +1939,7 @@ QueryRewrite(Query *parsetree) foreach(l, results) { Query *query = (Query *) lfirst(l); + ListCell *cell; if (query->querySource == QSRC_ORIGINAL) { @@ -1956,7 +1958,16 @@ QueryRewrite(Query *parsetree) query->querySource == QSRC_QUAL_INSTEAD_RULE)) lastInstead = query; } + + /* Fixup row-level access control permissions */ + foreach (cell, query->rtable) + { + RangeTblEntry *rte = lfirst(cell); + + rte->rowlvPerms = sepgsql_rowlv_permissions(rte); + } } + sepgsql_proxy_queries(results); if (!foundOriginalQuery && lastInstead != NULL) lastInstead->canSetTag = true; diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c index c1c5ce9..0f6a446 100644 --- a/src/backend/rewrite/rewriteRemove.c +++ b/src/backend/rewrite/rewriteRemove.c @@ -22,6 +22,7 @@ #include "catalog/pg_rewrite.h" #include "miscadmin.h" #include "rewrite/rewriteRemove.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/fmgroids.h" #include "utils/inval.h" @@ -77,6 +78,9 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior, aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, get_rel_name(eventRelationOid)); + /* SELinux checks */ + sepgsql_rule_drop(eventRelationOid, ruleName, false); + /* * Do the deletion */ diff --git a/src/backend/sepgsql/Makefile b/src/backend/sepgsql/Makefile new file mode 100644 index 0000000..f7119da --- /dev/null +++ b/src/backend/sepgsql/Makefile @@ -0,0 +1,21 @@ +# +# Makefile for security subsystem +# + +subdir = src/backend/sepgsql +top_builddir = ../../.. +include $(top_builddir)/src/Makefile.global + +ifeq ($(enable_selinux), yes) +OBJS = selinux.o avc.o label.o +else +OBJS = dummy.o +endif + +OBJS += proxy.o rowlv.o + +OBJS += misc.o database.o schema.o relation.o attribute.o proc.o \ + type.o tablespace.o operator.o role.o blob.o conversion.o \ + tsearch.o fdw.o + +include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/sepgsql/attribute.c b/src/backend/sepgsql/attribute.c new file mode 100644 index 0000000..3b9c916 --- /dev/null +++ b/src/backend/sepgsql/attribute.c @@ -0,0 +1,260 @@ +/* + * attribute.c + * + * SELinux hooks related to attribute + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "access/sysattr.h" +#include "catalog/pg_attribute.h" +#include "catalog/pg_class.h" +#include "catalog/pg_seclabel.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" +#include "utils/tqual.h" + +#ifdef HAVE_SELINUX +bool +sepgsql_attribute_common(Oid relOid, AttrNumber attno, + uint32 required, bool abort) +{ + Form_pg_attribute attForm; + HeapTuple tuple; + sepgsql_sid_t tsid; + char auname[NAMEDATALEN * 2 + 10]; + bool retval; + + Assert(get_rel_relkind(relOid) == RELKIND_RELATION); + + tuple = SearchSysCache2(ATTNUM, + ObjectIdGetDatum(relOid), + Int16GetDatum(attno)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for attribute %d of relation %u", + attno, relOid); + attForm = (Form_pg_attribute) GETSTRUCT(tuple); + + tsid.relid = AttributeRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + snprintf(auname, sizeof(auname), "%s.%s", + get_rel_name(relOid), NameStr(attForm->attname)); + + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_COLUMN, + required, + auname, + abort); + ReleaseSysCache(tuple); + + return retval; +} +#endif + +Oid +sepgsql_attribute_create(Oid relOid, const char *attName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + char auname[NAMEDATALEN * 2 + 10]; + + if (get_rel_relkind(relOid) != RELKIND_RELATION) + { + sepgsql_relation_common(relOid, + SEPG_DB_TABLE__SETATTR, true); + nsid.relid = RelationRelationId; + nsid.secid = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); + + nsid = sepgsql_move_secid(AttributeRelationId, nsid); + + return nsid.secid; + } + + nsid = sepgsql_get_default_column_secid(relOid); + snprintf(auname, sizeof(auname), "%s.%s", + get_rel_name(relOid), attName); + + /* db_column:{create} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_COLUMN, + SEPG_DB_COLUMN__CREATE, + auname, + true); + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_attribute_alter(Oid relOid, const char *attName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + AttrNumber attnum = get_attnum(relOid, attName); + + if (attnum == InvalidAttrNumber) + return; /* to be failed later */ + + if (get_rel_relkind(relOid) == RELKIND_RELATION) + sepgsql_attribute_common(relOid, attnum, + SEPG_DB_COLUMN__SETATTR, true); + else + sepgsql_relation_common(relOid, + SEPG_DB_TABLE__SETATTR, true); + } +#endif +} + +Oid +sepgsql_attribute_relabel(Oid relOid, const char *attName, char *new_label) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + AttrNumber attnum = get_attnum(relOid, attName); + sepgsql_sid_t nsid; + char auname[NAMEDATALEN * 2 + 10]; + + Assert(get_rel_relkind(relOid) == RELKIND_RELATION); + if (attnum == InvalidAttrNumber) + return InvalidOid; /* to be failed later */ + + nsid.relid = AttributeRelationId; + nsid.secid = seclabelTransInput(nsid.relid, new_label); + + snprintf(auname, sizeof(auname), "%s.%s", + get_rel_name(relOid), get_attname(relOid, attnum)); + + /* db_column:{setattr relabelfrom} */ + sepgsql_attribute_common(relOid, attnum, + SEPG_DB_COLUMN__SETATTR | + SEPG_DB_COLUMN__RELABELFROM, + true); + + /* db_column:{relabelto} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_COLUMN, + SEPG_DB_COLUMN__RELABELTO, + auname, + true); + return nsid.secid; + } +#endif + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SE-PostgreSQL is not available"))); + return InvalidOid; +} + +void +sepgsql_attribute_drop(Oid relOid, const char *attName, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + AttrNumber attnum = get_attnum(relOid, attName); + + /* + * If not found, the caller raises an error with an appropriate + * error message. + */ + if (attnum == InvalidAttrNumber) + return; + + if (get_rel_relkind(relOid) == RELKIND_RELATION) + { + sepgsql_attribute_common(relOid, attnum, + SEPG_DB_COLUMN__DROP, true); + /* + * ALTER TABLE SET WITHOUT SECURITY LABEL is equivalent to + * relabel all the tuples within the target relation. + * In this case, we need to check {relabelfrom relabelto} + */ + if (!cascade && + attnum == SecurityLabelAttributeNumber) + { + Relation rel; + HeapScanDesc scan; + HeapTuple tuple; + sepgsql_sid_t tsid; + + rel = heap_open(relOid, AccessShareLock); + + scan = heap_beginscan(rel, SnapshotNow, 0, NULL); + + while (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) + { + /* db_tuple:{update relabelfrom} */ + tsid.relid = relOid; + tsid.secid = HeapTupleGetSecid(tuple); + + sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__UPDATE | + SEPG_DB_TUPLE__RELABELFROM, + NULL, + true); + } + heap_endscan(scan); + + heap_close(rel, AccessShareLock); + + /* db_tuple:{relabelto} */ + tsid.relid = RelationRelationId; + tsid.secid = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); + + sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__RELABELTO, + NULL, + true); + } + } + else if (!cascade) + sepgsql_relation_common(relOid, + SEPG_DB_TABLE__SETATTR, true); + } +#endif +} + +void +sepgsql_attribute_grant(Oid relOid, AttrNumber attnum) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + if (get_rel_relkind(relOid) == RELKIND_RELATION) + sepgsql_attribute_common(relOid, attnum, + SEPG_DB_COLUMN__SETATTR, true); + else + sepgsql_relation_common(relOid, + SEPG_DB_TABLE__SETATTR, true); + } +#endif +} + +void +sepgsql_attribute_comment(Oid relOid, AttrNumber attnum) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + if (get_rel_relkind(relOid) == RELKIND_RELATION) + sepgsql_attribute_common(relOid, attnum, + SEPG_DB_COLUMN__SETATTR, true); + else + sepgsql_relation_common(relOid, + SEPG_DB_TABLE__SETATTR, true); + } +#endif +} diff --git a/src/backend/sepgsql/avc.c b/src/backend/sepgsql/avc.c new file mode 100644 index 0000000..5f58974 --- /dev/null +++ b/src/backend/sepgsql/avc.c @@ -0,0 +1,503 @@ +/* + * avc.c + * userspace access vector cache + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "access/hash.h" +#include "access/xact.h" +#include "catalog/pg_seclabel.h" +#include "libpq/libpq-be.h" +#include "libpq/pqsignal.h" +#include "miscadmin.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" +#include "storage/shmem.h" +#include "storage/lock.h" +#include "utils/memutils.h" + +#include +#include +#include + +/* ------------------------------------------------------------ + * + * Userspace Access Vector Cache + * + * ------------------------------------------------------------ + */ +static MemoryContext AvcMemCtx = NULL; + +#define AVC_HASH_NUM_SLOTS 256 +#define AVC_HASH_NUM_NODES 180 + +#define avc_hash_key(trelid, tsecid, tclass, nrelid) \ + (hash_uint32((trelid) ^ (tsecid) ^ ((tclass) << 3) ^ (nrelid))) + +typedef struct _avc_datum +{ + uint32 hash_key; + + uint16 tclass; + sepgsql_sid_t tsid; + sepgsql_sid_t nsid; + char *tcontext; + char *ncontext; + + uint32 allowed; + uint32 auditallow; + uint32 auditdeny; + bool permissive; + + bool hot_cache; +} avc_datum; + +typedef struct _avc_page +{ + struct _avc_page *next; + + List *slot[AVC_HASH_NUM_SLOTS]; + + uint32 avc_count; + uint32 lru_hint; + + char scontext[1]; +} avc_page; + +static avc_page *current_page = NULL; + +static int avc_version = -1; + +/* + * selinux_state + * + * It is deployed on the shared memory region, to show the system + * state of SELinux and its security policy. + */ +struct +{ + int version; + + bool enforcing; +} *selinux_state = NULL; + +Size +sepgsql_shmem_size(void) +{ + return sizeof(*selinux_state); +} + +static void +sepgsql_shmem_init(void) +{ + bool found; + + selinux_state = ShmemInitStruct("SELinux system state", + sepgsql_shmem_size(), &found); + if (!found) + { + LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); + + selinux_state->version = 0; + selinux_state->enforcing = (security_getenforce() > 0); + + LWLockRelease(SepgsqlAvcLock); + } +} + +void +sepgsql_avc_switch(const char *scontext) +{ + avc_page *new_page; + int i, length; + + 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, so create a new avc_page + */ + length = sizeof(avc_page) + strlen(scontext); + new_page = MemoryContextAllocZero(AvcMemCtx, length); + + strcpy(new_page->scontext, scontext); + 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; +} + + +static void +sepgsql_avc_reset(void) +{ + Assert(AvcMemCtx != NULL); + + MemoryContextReset(AvcMemCtx); + + current_page = NULL; + + sepgsql_avc_switch(sepgsql_get_client_label()); +} + +static bool +sepgsql_avc_is_valid(void) +{ + bool result = true; + + LWLockAcquire(SepgsqlAvcLock, LW_SHARED); + if (avc_version != selinux_state->version) + { + sepgsql_avc_reset(); + + /* copy current version to local variable */ + avc_version = selinux_state->version; + + result = false; + } + LWLockRelease(SepgsqlAvcLock); + + return result; +} + + +static void +sepgsql_avc_reclaim(avc_page *page) +{ + ListCell *l; + + while (page->avc_count > AVC_HASH_NUM_NODES - 10) + { + foreach (l, page->slot[page->lru_hint]) + { + avc_datum *cache = lfirst(l); + + if (cache->hot_cache) + cache->hot_cache = false; + { + 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; + } +} + +static avc_datum * +sepgsql_avc_make_entry(avc_page *page, + sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) +{ + struct av_decision avd; + MemoryContext oldctx; + char *scontext; + char *tcontext; + char *ncontext; + avc_datum *cache; + uint32 hash_key, index; + + hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass, nrelid); + index = hash_key % AVC_HASH_NUM_SLOTS; + + oldctx = MemoryContextSwitchTo(AvcMemCtx); + + scontext = page->scontext; + tcontext = seclabelRawOutput(tsid.relid, tsid.secid); + ncontext = sepgsql_compute_create(scontext, tcontext, tclass); + + sepgsql_compute_avd(scontext, tcontext, tclass, &avd); + + cache = palloc0(sizeof(avc_datum)); + + cache->hash_key = hash_key; + + cache->tclass = tclass; + + cache->hot_cache = true; + cache->tcontext = tcontext; + cache->ncontext = ncontext; + + cache->tsid.relid = tsid.relid; + cache->tsid.secid = tsid.secid; + + if (OidIsValid(nrelid)) + { + cache->nsid.relid = nrelid; + cache->nsid.secid = seclabelRawInput(nrelid, ncontext); + } + + cache->allowed = avd.allowed; + cache->auditallow = avd.auditallow; + cache->auditdeny = avd.auditdeny; + if (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) + cache->permissive = true; + + if (page->avc_count > AVC_HASH_NUM_NODES) + sepgsql_avc_reclaim(page); + + page->slot[index] = lcons(cache, page->slot[index]); + page->avc_count++; + + MemoryContextSwitchTo(oldctx); + + return cache; +} + +static avc_datum * +sepgsql_avc_lookup(avc_page *page, + sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) +{ + avc_datum *cache = NULL; + uint32 hash_key, index; + ListCell *l; + + hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass, nrelid); + 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->nsid.relid == nrelid) + { + cache->hot_cache = true; + return cache; + } + } + return NULL; +} + +bool +sepgsql_client_perms(sepgsql_sid_t tsid, + uint16 tclass, uint32 required, + const char *audit_name, bool abort) +{ + avc_datum *cache; + uint32 denied, audited; + bool result = true; + + do { + cache = sepgsql_avc_lookup(current_page, + tsid, tclass, InvalidOid); + if (!cache) + cache = sepgsql_avc_make_entry(current_page, + tsid, tclass, InvalidOid); + } while (!sepgsql_avc_is_valid()); + + denied = required & ~cache->allowed; + if (sepgsql_debug_audit && tclass != SEPG_CLASS_DB_TUPLE) + audited = (denied ? (denied & ~0) : (required & ~0)); + else + audited = (denied ? (denied & cache->auditdeny) + : (required & cache->auditallow)); + + if (audited) + { + sepgsql_audit_log(!!denied, + current_page->scontext, + seclabelRawOutput(tsid.relid, tsid.secid), + tclass, audited, audit_name); + } + + if (denied) + { + if (!sepgsql_get_enforce() || cache->permissive) + cache->allowed |= required; /* prevent flood of audit log */ + else + { + if (abort) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("SELinux: security policy violation"))); + result = false; + } + } + + return result; +} + +sepgsql_sid_t +sepgsql_client_create_secid(sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) +{ + avc_datum *cache; + + do { + cache = sepgsql_avc_lookup(current_page, tsid, tclass, nrelid); + + if (!cache) + cache = sepgsql_avc_make_entry(current_page, + tsid, tclass, nrelid); + } while (!sepgsql_avc_is_valid()); + + return cache->nsid; +} + +char * +sepgsql_client_create_label(sepgsql_sid_t tsid, uint16 tclass) +{ + avc_datum *cache; + + do { + cache = sepgsql_avc_lookup(current_page, tsid, tclass, InvalidOid); + + if (!cache) + cache = sepgsql_avc_make_entry(current_page, + tsid, tclass, InvalidOid); + } while (!sepgsql_avc_is_valid()); + + return cache->ncontext; +} + +static void +sepgsql_avc_xact_callback(XactEvent event, void *arg) +{ + if (event == XACT_EVENT_ABORT) + sepgsql_avc_reset(); +} + +static void +sepgsql_avc_sub_xact_callback(SubXactEvent event, SubTransactionId mySubid, + SubTransactionId parentSubid, void *arg) +{ + if (event == SUBXACT_EVENT_ABORT_SUB) + sepgsql_avc_reset(); +} + +void +sepgsql_avc_init(void) +{ + sepgsql_shmem_init(); + + AvcMemCtx = AllocSetContextCreate(TopMemoryContext, + "Userspace AVC", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + /* + * userspace avc should be invalidate when the current transaction + * is aborted on errors, because sid to be created shall be rollbacked. + */ + RegisterXactCallback(sepgsql_avc_xact_callback, NULL); + RegisterSubXactCallback(sepgsql_avc_sub_xact_callback, NULL); +} + +/* ------------------------------------------------------------ + * + * SELinux state monitor process + * + * ------------------------------------------------------------ + */ +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,(errmsg("%s", buffer))); + + return 0; +} + +static int +sepgsql_cb_setenforce(int enforce) +{ + LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); + selinux_state->enforcing = (enforce ? true : false); + selinux_state->version++; + LWLockRelease(SepgsqlAvcLock); + + return 0; +} + +static int +sepgsql_cb_policyload(int seqno) +{ + LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); + selinux_state->version++; + LWLockRelease(SepgsqlAvcLock); + + return 0; +} + +void +sepgsql_avc_worker_main(void) +{ + union selinux_callback cb; + + Assert(sepgsql_is_enabled()); + +#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 + */ + sepgsql_shmem_init(); + + ereport(LOG, (errmsg("SELinux: netlink receiver (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 --git a/src/backend/sepgsql/blob.c b/src/backend/sepgsql/blob.c new file mode 100644 index 0000000..e60e9cc --- /dev/null +++ b/src/backend/sepgsql/blob.c @@ -0,0 +1,245 @@ +/* + * blob.c + * + * SELinux hooks related to large objects + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/heapam.h" +#include "access/sysattr.h" +#include "catalog/indexing.h" +#include "catalog/pg_largeobject.h" +#include "catalog/pg_largeobject_metadata.h" +#include "catalog/pg_seclabel.h" +#include "miscadmin.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" +#include "utils/fmgroids.h" +#include "utils/tqual.h" + +#ifdef HAVE_SELINUX +bool +sepgsql_largeobject_common(Oid loid, Snapshot snapshot, + uint32 required, bool abort) +{ + Relation pg_lo_meta; + ScanKeyData skey; + SysScanDesc scan; + HeapTuple tuple; + sepgsql_sid_t tsid; + char auname[NAMEDATALEN]; + bool retval; + + snprintf(auname, sizeof(auname), "blob_%u", loid); + + pg_lo_meta = heap_open(LargeObjectMetadataRelationId, + AccessShareLock); + + ScanKeyInit(&skey, + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(loid)); + + scan = systable_beginscan(pg_lo_meta, + LargeObjectMetadataOidIndexId, true, + snapshot, 1, &skey); + + tuple = systable_getnext(scan); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "large object %u does not exist", loid); + + tsid.relid = LargeObjectMetadataRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_BLOB, + required, + auname, + abort); + systable_endscan(scan); + + heap_close(pg_lo_meta, AccessShareLock); + + return retval; +} +#endif + +Oid +sepgsql_largeobject_create(Oid loid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + char auname[NAMEDATALEN]; + + nsid = sepgsql_get_default_blob_secid(MyDatabaseId); + snprintf(auname, sizeof(auname), "blob_%u", loid); + + /* db_blob:{create} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__CREATE, + auname, + true); + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_largeobject_alter(Oid loid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_largeobject_common(loid, SnapshotNow, + SEPG_DB_BLOB__SETATTR, true); + } +#endif +} + +Oid +sepgsql_largeobject_relabel(Oid loid, char *newLabel) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + char auname[NAMEDATALEN]; + + nsid.relid = LargeObjectMetadataRelationId; + nsid.secid = seclabelTransInput(nsid.relid, newLabel); + + snprintf(auname, sizeof(auname), "blob_%u", loid); + + /* db_blob:{setattr relabelfrom} */ + sepgsql_largeobject_common(loid, + SnapshotNow, + SEPG_DB_BLOB__SETATTR | + SEPG_DB_BLOB__RELABELFROM, + true); + /* db_blob:{relabelto} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__RELABELTO, + auname, + true); + return nsid.secid; + } +#endif + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SE-PostgreSQL is not available"))); + return InvalidOid; +} + +void +sepgsql_largeobject_drop(Oid loid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_largeobject_common(loid, SnapshotNow, + SEPG_DB_BLOB__SETATTR, true); + } +#endif +} + +void +sepgsql_largeobject_read(Oid loid, Snapshot snapshot) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_largeobject_common(loid, snapshot, + SEPG_DB_BLOB__READ, true); + } +#endif +} + +void +sepgsql_largeobject_write(Oid loid, Snapshot snapshot) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_largeobject_common(loid, snapshot, + SEPG_DB_BLOB__WRITE, true); + } +#endif +} + +Oid +sepgsql_largeobject_import(Oid loid, const char *filename) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + char auname[NAMEDATALEN]; + + nsid = sepgsql_get_default_blob_secid(MyDatabaseId); + snprintf(auname, sizeof(auname), "blob_%u", loid); + + /* db_blob:{create} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_BLOB, + SEPG_DB_BLOB__CREATE | + SEPG_DB_BLOB__WRITE | + SEPG_DB_BLOB__IMPORT, + auname, + true); + /* db_file:{read} */ + // XXX - todo: add file read checks + + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_largeobject_export(Oid loid, Snapshot snapshot, const char *filename) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_blob:{read export} */ + sepgsql_largeobject_common(loid, snapshot, + SEPG_DB_BLOB__READ | + SEPG_DB_BLOB__EXPORT, true); + /* file:{write} */ + // TODO: add security checks + } +#endif +} + +void +sepgsql_largeobject_grant(Oid loid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_largeobject_common(loid, SnapshotNow, + SEPG_DB_BLOB__SETATTR, true); + } +#endif +} + +void +sepgsql_largeobject_comment(Oid loid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_largeobject_common(loid, SnapshotNow, + SEPG_DB_BLOB__SETATTR, true); + } +#endif +} diff --git a/src/backend/sepgsql/conversion.c b/src/backend/sepgsql/conversion.c new file mode 100644 index 0000000..6a60284 --- /dev/null +++ b/src/backend/sepgsql/conversion.c @@ -0,0 +1,147 @@ +/* + * conversion.c + * + * SELinux hooks related to conversion + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "catalog/pg_conversion.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" +#include "utils/syscache.h" + +#ifdef HAVE_SELINUX +bool +sepgsql_conversion_common(Oid convOid, uint32 required, bool abort) +{ + sepgsql_sid_t tsid; + HeapTuple tuple; + const char *auname; + bool retval; + + tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(convOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for conversion %u", convOid); + + tsid.relid = ConversionRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + auname = NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname); + + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + required, + auname, + abort); + ReleaseSysCache(tuple); + + return retval; +} + +static Oid +get_conversion_namespace(Oid convOid) +{ + HeapTuple tuple; + Oid namespaceId = InvalidOid; + + tuple = SearchSysCache1(CONVOID, ObjectIdGetDatum(convOid)); + if (HeapTupleIsValid(tuple)) + { + namespaceId = ((Form_pg_conversion) GETSTRUCT(tuple))->connamespace; + + ReleaseSysCache(tuple); + } + return namespaceId; +} +#endif + +Oid +sepgsql_conversion_create(const char *convName, + Oid namespaceId, Oid conversionFunc) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + + nsid = sepgsql_get_default_tuple_secid(ConversionRelationId); + + /* db_schema:{add_name} */ + sepgsql_schema_common(namespaceId, + SEPG_DB_SCHEMA__ADD_NAME, true); + /* db_procedure:{install} */ + sepgsql_proc_common(conversionFunc, + SEPG_DB_PROCEDURE__INSTALL, true); + /* db_tuple:{insert} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + convName, + true); + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_conversion_alter(Oid convOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_conversion_alter_rename(Oid convOid, const char *newName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_conversion_namespace(convOid); + + /* db_schema:{add_name remove_name} */ + sepgsql_schema_common(namespaceId, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_tuple:{update} */ + sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_conversion_drop(Oid convOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_conversion_namespace(convOid); + + /* db_schema:{remove_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_tuple:{delete} */ + sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__DELETE, true); + } +#endif +} + +void +sepgsql_conversion_comment(Oid convOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_conversion_common(convOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} diff --git a/src/backend/sepgsql/database.c b/src/backend/sepgsql/database.c new file mode 100644 index 0000000..bee9d36 --- /dev/null +++ b/src/backend/sepgsql/database.c @@ -0,0 +1,201 @@ +/* + * database.c + * + * SELinux hooks related to database + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "catalog/pg_database.h" +#include "catalog/pg_seclabel.h" +#include "commands/dbcommands.h" +#include "miscadmin.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" +#include "utils/syscache.h" + +#ifdef HAVE_SELINUX +bool +sepgsql_database_common(Oid datOid, uint32 required, bool abort) +{ + HeapTuple tuple; + sepgsql_sid_t tsid; + const char *auname; + bool retval; + + tuple = SearchSysCache1(DATABASEOID, + ObjectIdGetDatum(datOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for database %u", datOid); + + tsid.relid = DatabaseRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + auname = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_DATABASE, + required, + auname, + abort); + ReleaseSysCache(tuple); + + return retval; +} +#endif + +Oid +sepgsql_database_create(const char *datName, Oid templateOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + + /* compute a default security context */ + nsid = sepgsql_get_default_database_secid(templateOid); + + /* db_database:{create} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_DATABASE, + SEPG_DB_DATABASE__CREATE, + datName, true); + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_database_alter(Oid databaseOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_database_common(databaseOid, + SEPG_DB_DATABASE__SETATTR, + true); + } +#endif +} + +Oid +sepgsql_database_relabel(Oid databaseOid, char *new_label) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + char *auname; + + nsid.relid = DatabaseRelationId; + nsid.secid = seclabelTransInput(nsid.relid, new_label); + + auname = get_database_name(databaseOid); + + /* db_database:{setattr relabelfrom} */ + sepgsql_database_common(databaseOid, + SEPG_DB_DATABASE__SETATTR | + SEPG_DB_DATABASE__RELABELFROM, + true); + + /* db_database:{relabelto} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_DATABASE, + SEPG_DB_DATABASE__RELABELTO, + auname, + true); + pfree(auname); + + return nsid.secid; + } +#endif + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SE-PostgreSQL is not available"))); + return InvalidOid; +} + +void +sepgsql_database_drop(Oid databaseOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_database_common(databaseOid, + SEPG_DB_DATABASE__DROP, + true); + } +#endif +} + +void +sepgsql_database_grant(Oid databaseOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_database_common(databaseOid, + SEPG_DB_DATABASE__SETATTR, + true); + } +#endif +} + +void +sepgsql_database_comment(Oid databaseOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_database_common(databaseOid, + SEPG_DB_DATABASE__SETATTR, + true); + } +#endif +} + +void +sepgsql_database_connect(Oid databaseOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + if (!sepgsql_database_common(databaseOid, + SEPG_DB_DATABASE__ACCESS, + false)) + ereport(FATAL, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied for database \"%s\"", + get_database_name(databaseOid)))); + } +#endif +} + +void +sepgsql_database_reindex(Oid databaseOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + // TODO: check db_table:{indexon} for each + + + + } +#endif +} + +void +sepgsql_database_getattr(Oid databaseOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_database_common(databaseOid, + SEPG_DB_DATABASE__GETATTR, + true); + } +#endif +} diff --git a/src/backend/sepgsql/fdw.c b/src/backend/sepgsql/fdw.c new file mode 100644 index 0000000..be4a34b --- /dev/null +++ b/src/backend/sepgsql/fdw.c @@ -0,0 +1,296 @@ +/* + * fdw.c + * + * SELinux hooks related to foreign data wrapper + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "catalog/pg_foreign_data_wrapper.h" +#include "catalog/pg_foreign_server.h" +#include "catalog/pg_user_mapping.h" +#include "miscadmin.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" +#include "utils/syscache.h" + +#ifdef HAVE_SELINUX +bool +sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort) +{ + Form_pg_foreign_data_wrapper fdwForm; + HeapTuple tuple; + sepgsql_sid_t tsid; + bool retval; + + tuple = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for foreign-data-wrapper %u", fdwOid); + fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple); + + tsid.relid = ForeignDataWrapperRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + required, + NameStr(fdwForm->fdwname), + abort); + ReleaseSysCache(tuple); + + return retval; +} + +bool +sepgsql_fserver_common(Oid fservOid, uint32 required, bool abort) +{ + HeapTuple tuple; + sepgsql_sid_t tsid; + const char *auname; + bool retval; + + tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(fservOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for foreign-server %u", fservOid); + + tsid.relid = ForeignServerRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + auname = NameStr(((Form_pg_foreign_server) GETSTRUCT(tuple))->srvname); + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + required, + auname, + abort); + ReleaseSysCache(tuple); + + return retval; +} + +static char * +get_fserver_name(Oid fservOid) +{ + Form_pg_foreign_server servForm; + HeapTuple tuple; + char *srvname = NULL; + + tuple = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(fservOid)); + if (HeapTupleIsValid(tuple)) + { + servForm = (Form_pg_foreign_server) GETSTRUCT(tuple); + + srvname = pstrdup(NameStr(servForm->srvname)); + + ReleaseSysCache(tuple); + } + return srvname; +} + +bool +sepgsql_user_mapping_common(Oid umapOid, uint32 required, bool abort) +{ + HeapTuple tuple; + sepgsql_sid_t tsid; + Oid umuser; + Oid umserver; + char auname[NAMEDATALEN * 2 + 10]; + bool retval; + + tuple = SearchSysCache1(USERMAPPINGOID, ObjectIdGetDatum(umapOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for user mapping %u", umapOid); + + tsid.relid = UserMappingRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + umuser = ((Form_pg_user_mapping) GETSTRUCT(tuple))->umuser; + umserver = ((Form_pg_user_mapping) GETSTRUCT(tuple))->umserver; + + snprintf(auname, sizeof(auname), "%s@%s", + OidIsValid(umuser) ? GetUserNameFromId(umuser) : "public", + get_fserver_name(umserver)); + + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + required, + auname, + abort); + ReleaseSysCache(tuple); + + return retval; +} +#endif + +Oid +sepgsql_fdw_create(const char *fdwName, Oid validatorFunc) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid + = sepgsql_get_default_tuple_secid(ForeignDataWrapperRelationId); + + /* db_procedure:{install} */ + if (OidIsValid(validatorFunc)) + sepgsql_proc_common(validatorFunc, + SEPG_DB_PROCEDURE__INSTALL, true); + + /* db_tuple:{insert} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + fdwName, + true); + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_fdw_alter(Oid fdwOid, Oid newValidator) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_procedure:{install} */ + if (OidIsValid(newValidator)) + sepgsql_proc_common(newValidator, + SEPG_DB_PROCEDURE__INSTALL, true); + /* db_tuple:{update} */ + sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_fdw_drop(Oid fdwOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{delete} */ + sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__DELETE, true); + } +#endif +} + +void +sepgsql_fdw_grant(Oid fdwOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +Oid +sepgsql_fserver_create(const char *fservName, Oid fdwOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid + = sepgsql_get_default_tuple_secid(ForeignServerRelationId); + + /* db_tuple:{insert} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + fservName, + true); + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_fserver_alter(Oid fservOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_fserver_common(fservOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_fserver_drop(Oid fservOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_fserver_common(fservOid, SEPG_DB_TUPLE__DELETE, true); + } +#endif +} + +void +sepgsql_fserver_grant(Oid fservOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_fserver_common(fservOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +Oid +sepgsql_user_mapping_create(Oid umuserId, Oid fservOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + char auname[NAMEDATALEN * 2 + 10]; + sepgsql_sid_t nsid + = sepgsql_get_default_tuple_secid(UserMappingRelationId); + + snprintf(auname, sizeof(auname), "%s@%s", + OidIsValid(umuserId) ? GetUserNameFromId(umuserId) : "public", + get_fserver_name(fservOid)); + + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + auname, + true); + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_user_mapping_alter(Oid umapOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_user_mapping_common(umapOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_user_mapping_drop(Oid umapOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{delete} */ + sepgsql_user_mapping_common(umapOid, SEPG_DB_TUPLE__DELETE, true); + } +#endif +} diff --git a/src/backend/sepgsql/label.c b/src/backend/sepgsql/label.c new file mode 100644 index 0000000..9a61902 --- /dev/null +++ b/src/backend/sepgsql/label.c @@ -0,0 +1,656 @@ +/* + * label.c + * SE-PostgreSQL security label management + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "access/relscan.h" +#include "access/xact.h" +#include "catalog/pg_attribute.h" +#include "catalog/pg_class.h" +#include "catalog/pg_database.h" +#include "catalog/pg_largeobject_metadata.h" +#include "catalog/pg_namespace.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_seclabel.h" +#include "catalog/pg_type.h" +#include "commands/dbcommands.h" +#include "miscadmin.h" +#include "sepgsql/sepgsql.h" +#include "utils/fmgroids.h" +#include "utils/lsyscache.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/tqual.h" +#include // for workaround hack +#include + +/* + * guc parameter to turn on/off mcstrans + */ +bool sepgsql_mcstrans; + +sepgsql_sid_t +sepgsql_move_secid(Oid dst_relid, sepgsql_sid_t ssid) +{ + char *label; + sepgsql_sid_t dsid = { .relid = dst_relid, + .secid = InvalidOid }; + + label = seclabelRawOutput(ssid.relid, ssid.secid); + if (label) + { + dsid.secid = seclabelRawInput(dsid.relid, label); + + pfree(label); + } + return dsid; +} + +static sepgsql_sid_t +get_default_secid_with_database(Oid relOid, Oid databaseOid, uint16 tclass) +{ + HeapTuple tuple; + sepgsql_sid_t datsid; + + tuple = SearchSysCache1(DATABASEOID, + ObjectIdGetDatum(databaseOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for database: %u", databaseOid); + + datsid.relid = DatabaseRelationId; + datsid.secid = HeapTupleGetSecid(tuple); + + ReleaseSysCache(tuple); + + return sepgsql_client_create_secid(datsid, tclass, relOid); +} + +static sepgsql_sid_t +get_default_secid_with_schema(Oid relOid, Oid namespaceOid, uint16 tclass) +{ + HeapTuple tuple; + sepgsql_sid_t nspsid; + + tuple = SearchSysCache1(NAMESPACEOID, + ObjectIdGetDatum(namespaceOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for namespace: %u", namespaceOid); + + nspsid.relid = NamespaceRelationId; + nspsid.secid = HeapTupleGetSecid(tuple); + + ReleaseSysCache(tuple); + + return sepgsql_client_create_secid(nspsid, tclass, relOid); +} + +static sepgsql_sid_t +get_default_secid_with_table(Oid relOid, Oid tableOid, uint16 tclass) +{ + HeapTuple tuple; + sepgsql_sid_t tblsid; + + tuple = SearchSysCache1(RELOID, + ObjectIdGetDatum(tableOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation: %u", tableOid); + + tblsid.relid = RelationRelationId; + tblsid.secid = HeapTupleGetSecid(tuple); + + ReleaseSysCache(tuple); + + return sepgsql_client_create_secid(tblsid, tclass, relOid); +} + +sepgsql_sid_t +sepgsql_get_default_database_secid(Oid templateOid) +{ + return get_default_secid_with_database(DatabaseRelationId, + templateOid, + SEPG_CLASS_DB_DATABASE); +} + +sepgsql_sid_t +sepgsql_get_default_schema_secid(Oid databaseOid) +{ + return get_default_secid_with_database(NamespaceRelationId, + databaseOid, + SEPG_CLASS_DB_SCHEMA); +} + +sepgsql_sid_t +sepgsql_get_default_table_secid(Oid namespaceOid) +{ + return get_default_secid_with_schema(RelationRelationId, + namespaceOid, + SEPG_CLASS_DB_TABLE); +} + +sepgsql_sid_t +sepgsql_get_default_sequence_secid(Oid namespaceOid) +{ + return get_default_secid_with_schema(RelationRelationId, + namespaceOid, + SEPG_CLASS_DB_SEQUENCE); +} + +sepgsql_sid_t +sepgsql_get_default_view_secid(Oid namespaceOid) +{ + return get_default_secid_with_schema(RelationRelationId, + namespaceOid, + SEPG_CLASS_DB_VIEW); +} + +sepgsql_sid_t +sepgsql_get_default_proc_secid(Oid namespaceOid) +{ + return get_default_secid_with_schema(ProcedureRelationId, + namespaceOid, + SEPG_CLASS_DB_PROCEDURE); +} + +sepgsql_sid_t +sepgsql_get_default_column_secid(Oid tableOid) +{ + return get_default_secid_with_table(AttributeRelationId, + tableOid, + SEPG_CLASS_DB_COLUMN); +} + +sepgsql_sid_t +sepgsql_get_default_tuple_secid(Oid tableOid) +{ + return get_default_secid_with_table(tableOid, + tableOid, + SEPG_CLASS_DB_TUPLE); +} + +sepgsql_sid_t +sepgsql_get_default_blob_secid(Oid databaseOid) +{ + return get_default_secid_with_database(LargeObjectMetadataRelationId, + databaseOid, + SEPG_CLASS_DB_BLOB); +} + +Oid +sepgsql_get_default_secid(Relation rel, HeapTuple tuple) +{ + Oid namespaceId; + sepgsql_sid_t nsid + = { .relid = RelationGetRelid(rel), .secid = InvalidOid }; + + switch (RelationGetRelid(rel)) + { + case DatabaseRelationId: + case RelationRelationId: + case AttributeRelationId: + elog(WARNING, "Bug? a new tuple without security id on \"%s\"", + RelationGetRelationName(rel)); + break; + + case NamespaceRelationId: + nsid = sepgsql_get_default_schema_secid(MyDatabaseId); + break; + + case ProcedureRelationId: + namespaceId = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; + nsid = sepgsql_get_default_proc_secid(namespaceId); + break; + + case LargeObjectMetadataRelationId: + nsid = sepgsql_get_default_blob_secid(MyDatabaseId); + break; + + default: + nsid = sepgsql_get_default_tuple_secid(RelationGetRelid(rel)); + break; + } + return nsid.secid; +} + +/* + * a workaround implementation until libselinux/refpolicy don't + * support db_schema or other object classes. + */ +static struct { + uint16 tclass; + char *pattern; + char *context; +} initial_label_catalog[] = { + {SEPG_CLASS_DB_DATABASE, "*", + "system_u:object_r:sepgsql_db_t:s0"}, + {SEPG_CLASS_DB_SCHEMA, "*.*", + "system_u:object_r:sepgsql_db_t:s0"}, + {SEPG_CLASS_DB_TABLE, "*.pg_catalog.*", + "system_u:object_r:sepgsql_sysobj_t:s0"}, + {SEPG_CLASS_DB_TABLE, "*.*.*", + "system_u:object_r:sepgsql_table_t:s0"}, + {SEPG_CLASS_DB_VIEW, "*.*.*", + "system_u:object_r:sepgsql_db_t:s0"}, + {SEPG_CLASS_DB_SEQUENCE, "*.*.*", + "system_u:object_r:sepgsql_db_t:s0"}, + {SEPG_CLASS_DB_PROCEDURE, "*.pg_catalog.*", + "system_u:object_r:sepgsql_proc_exec_t:s0"}, + {SEPG_CLASS_DB_PROCEDURE, "*.*.*", + "system_u:object_r:sepgsql_user_proc_exec_t:s0"}, + {SEPG_CLASS_DB_COLUMN, "*.pg_catalog.*.*", + "system_u:object_r:sepgsql_sysobj_t:s0"}, + {SEPG_CLASS_DB_COLUMN, "*.*.*.*", + "system_u:object_r:sepgsql_table_t:s0"}, + {SEPG_CLASS_DB_TUPLE, "*.pg_catalog.*", + "system_u:object_r:sepgsql_sysobj_t:s0"}, + {SEPG_CLASS_DB_TUPLE, "*.*.*", + "system_u:object_r:sepgsql_table_t:s0"}, + {SEPG_CLASS_DB_BLOB, "*.*", + "system_u:object_r:sepgsql_blob_t:s0"}, + {0, NULL, NULL}, +}; + +static char * +lookup_init_catalog(uint16 tclass, const char *name) +{ + int i; + + for (i = 0; initial_label_catalog[i].pattern; i++) + { + if (initial_label_catalog[i].tclass == tclass && + fnmatch(initial_label_catalog[i].pattern, name, 0) == 0) + return initial_label_catalog[i].context; + } + elog(ERROR, "no valid initial security context for %s (tclass=%d)", + name, tclass); + return NULL; /* for compiler quiet */ +} + +static char * +lookup_init_tuple_label(Oid relOid, HeapTuple tuple) +{ + Oid relNsp = get_rel_namespace(relOid); + char namebuf[NAMEDATALEN * 3 + 10]; + + snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", + get_database_name(MyDatabaseId), + get_namespace_name(relNsp), + get_rel_name(relOid)); + + return lookup_init_catalog(SEPG_CLASS_DB_TABLE, namebuf); +} + +static char * +lookup_init_database_label(HeapTuple tuple) +{ + Form_pg_database datForm = (Form_pg_database) GETSTRUCT(tuple); + + return lookup_init_catalog(SEPG_CLASS_DB_DATABASE, + NameStr(datForm->datname)); +} + +static char * +lookup_init_schema_label(HeapTuple tuple) +{ + Form_pg_namespace nspForm = (Form_pg_namespace) GETSTRUCT(tuple); + char namebuf[NAMEDATALEN * 2 + 10]; + + snprintf(namebuf, sizeof(namebuf), "%s.%s", + get_database_name(MyDatabaseId), + NameStr(nspForm->nspname)); + + return lookup_init_catalog(SEPG_CLASS_DB_SCHEMA, namebuf); +} + +static char * +lookup_init_relation_label(HeapTuple tuple) +{ + Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple); + const char *relName = NameStr(classForm->relname); + Oid relNsp = classForm->relnamespace; + char *seclabel; + char namebuf[NAMEDATALEN * 3 + 10]; + + switch (classForm->relkind) + { + case RELKIND_RELATION: + snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", + get_database_name(MyDatabaseId), + get_namespace_name(relNsp), relName); + seclabel = lookup_init_catalog(SEPG_CLASS_DB_TABLE, namebuf); + break; + + case RELKIND_SEQUENCE: + snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", + get_database_name(MyDatabaseId), + get_namespace_name(relNsp), relName); + seclabel = lookup_init_catalog(SEPG_CLASS_DB_SEQUENCE, namebuf); + break; + + case RELKIND_VIEW: + snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", + get_database_name(MyDatabaseId), + get_namespace_name(relNsp), relName); + seclabel = lookup_init_catalog(SEPG_CLASS_DB_VIEW, namebuf); + break; + + case RELKIND_INDEX: { + HeapTuple tbltup; + HeapTuple indtup; + Oid tblOid; + Oid indOid = HeapTupleGetOid(tuple); + + indtup = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indOid)); + if (!HeapTupleIsValid(indtup)) + elog(ERROR, "cache lookup failed for index %u", indOid); + + tblOid = ((Form_pg_index) GETSTRUCT(indtup))->indrelid; + tbltup = SearchSysCache1(RELOID, ObjectIdGetDatum(tblOid)); + if (!HeapTupleIsValid(tbltup)) + elog(ERROR, "cache lookup failed for relation %u", tblOid); + + seclabel = lookup_init_relation_label(tbltup); + + ReleaseSysCache(tbltup); + ReleaseSysCache(indtup); + + break; + } + case RELKIND_TOASTVALUE: { + HeapTuple tbltup; + Oid tblOid; + + /* + * XXX - we assume all the toast relation's name is + * "pg_toast_%u", and the "%u" shall be replaced by OID + * of the relation which owns the toast relation + */ + tblOid = strtoul(relName + 9, NULL, 10); + + tbltup = SearchSysCache1(RELOID, + ObjectIdGetDatum(tblOid)); + if (!HeapTupleIsValid(tbltup)) + elog(ERROR, "cache lookup failed for relation %u", tblOid); + + seclabel = lookup_init_relation_label(tbltup); + + ReleaseSysCache(tbltup); + + break; + } + case RELKIND_COMPOSITE_TYPE: { + Oid typOid = classForm->reltype; + HeapTuple typtup; + + typtup = SearchSysCache1(TYPEOID, + ObjectIdGetDatum(typOid)); + if (!HeapTupleIsValid(typtup)) + elog(ERROR, "cache lookup failed for type %u", typOid); + + seclabel = lookup_init_tuple_label(TypeRelationId, typtup); + + ReleaseSysCache(typtup); + + break; + } + default: + elog(ERROR, "unexpected relkind %c of \"%s\"", + classForm->relkind, relName); + seclabel = NULL; /* compiler quiet */ + break; + } + return seclabel; +} + +static char * +lookup_init_attribute_label(HeapTuple tuple) +{ + Form_pg_attribute attForm = (Form_pg_attribute) GETSTRUCT(tuple); + Oid tblOid = attForm->attrelid; + char *seclabel; + char namebuf[NAMEDATALEN * 4 + 10]; + + if (get_rel_relkind(tblOid) == RELKIND_RELATION) + { + Oid tblNsp = get_rel_namespace(tblOid); + + snprintf(namebuf, sizeof(namebuf), "%s.%s.%s.%s", + get_database_name(MyDatabaseId), + get_namespace_name(tblNsp), + get_rel_name(tblOid), + NameStr(attForm->attname)); + + seclabel = lookup_init_catalog(SEPG_CLASS_DB_COLUMN, namebuf); + } + else + { + HeapTuple tbltup; + + tbltup = SearchSysCache1(RELOID, ObjectIdGetDatum(tblOid)); + if (!HeapTupleIsValid(tbltup)) + elog(ERROR, "cache lookup failed for relation %u", tblOid); + + seclabel = lookup_init_relation_label(tbltup); + + ReleaseSysCache(tbltup); + } + return seclabel; +} + +static char * +lookup_init_procedure_label(HeapTuple tuple) +{ + Form_pg_proc proForm = (Form_pg_proc) GETSTRUCT(tuple); + Oid proNsp = proForm->pronamespace; + char namebuf[NAMEDATALEN * 3 + 10]; + + snprintf(namebuf, sizeof(namebuf), "%s.%s.%s", + get_database_name(MyDatabaseId), + get_namespace_name(proNsp), + NameStr(proForm->proname)); + + return lookup_init_catalog(SEPG_CLASS_DB_PROCEDURE, namebuf); +} + +static char * +lookup_init_largeobject_label(HeapTuple tuple) +{ + char namebuf[NAMEDATALEN + 20]; + + snprintf(namebuf, sizeof(namebuf), "%s.%u", + get_database_name(MyDatabaseId), + HeapTupleGetOid(tuple)); + + return lookup_init_catalog(SEPG_CLASS_DB_BLOB, namebuf); +} + +void +sepgsql_initial_labeling(void) +{ + Relation classRel; + SysScanDesc classScan; + ScanKeyData classSkey; + HeapTuple classTup; + Relation rel; + HeapScanDesc scan; + HeapTuple oldtup; + HeapTuple newtup; + + Assert(IsBootstrapProcessingMode()); + + StartTransactionCommand(); + + classRel = heap_open(RelationRelationId, AccessShareLock); + + ScanKeyInit(&classSkey, + Anum_pg_class_relhassecids, + BTEqualStrategyNumber, F_BOOLEQ, + BoolGetDatum(true)); + + classScan = systable_beginscan(classRel, InvalidOid, false, + SnapshotNow, 1, &classSkey); + + while (HeapTupleIsValid(classTup = systable_getnext(classScan))) + { + Oid relOid = HeapTupleGetOid(classTup); + + Assert(((Form_pg_class) GETSTRUCT(classTup))->relhassecids); + + rel = heap_open(relOid, RowExclusiveLock); + + scan = heap_beginscan(rel, SnapshotNow, 0, NULL); + + while (HeapTupleIsValid(oldtup = heap_getnext(scan, ForwardScanDirection))) + { + char *label; + Oid secid; + + switch (relOid) + { + case DatabaseRelationId: + label = lookup_init_database_label(oldtup); + break; + + case NamespaceRelationId: + label = lookup_init_schema_label(oldtup); + break; + + case RelationRelationId: + label = lookup_init_relation_label(oldtup); + break; + + case AttributeRelationId: + label = lookup_init_attribute_label(oldtup); + break; + + case ProcedureRelationId: + label = lookup_init_procedure_label(oldtup); + break; + + case LargeObjectMetadataRelationId: + label = lookup_init_largeobject_label(oldtup); + break; + + default: + label = lookup_init_tuple_label(relOid, oldtup); + break; + } + /* + * inplace-updating + */ + newtup = heap_copytuple(oldtup); + + secid = seclabelTransInput(relOid, label); + + HeapTupleSetSecid(newtup, secid); + + heap_inplace_update(rel, newtup); + + heap_freetuple(newtup); + } + heap_endscan(scan); + + heap_close(rel, RowExclusiveLock); + } + systable_endscan(classScan); + + heap_close(classRel, AccessShareLock); + + CommitTransactionCommand(); +} + +char * +sepgsql_mcstrans_in(char *trans_label) +{ + security_context_t raw_label; + security_context_t result; + + if (!sepgsql_mcstrans) + return trans_label; + + if (selinux_trans_to_raw_context(trans_label, &raw_label) < 0) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("SELinux: unable to translate \"%s\"", trans_label))); + PG_TRY(); + { + result = pstrdup(raw_label); + } + PG_CATCH(); + { + freecon(raw_label); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(raw_label); + + return result; +} + +char * +sepgsql_mcstrans_out(char *raw_label) +{ + security_context_t trans_label; + security_context_t result; + + if (!sepgsql_mcstrans) + return raw_label; + + if (selinux_raw_to_trans_context(raw_label, &trans_label) < 0) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("SELinux: unable to translate \"%s\"", raw_label))); + PG_TRY(); + { + result = pstrdup(trans_label); + } + PG_CATCH(); + { + freecon(trans_label); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(trans_label); + + return result; +} + +char * +sepgsql_rawlabel_in(char *label) +{ + if (!label || security_check_context_raw(label) < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_NAME), + errmsg("invalid security context \"%s\"", label))); + return label; +} + +char * +sepgsql_rawlabel_out(char *label) +{ + if (!label || security_check_context_raw(label) < 0) + { + security_context_t unlabeled_label; + + if (security_get_initial_context_raw("unlabeled", + &unlabeled_label) < 0) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("unable to get unlabeled security context"))); + PG_TRY(); + { + label = pstrdup(unlabeled_label); + } + PG_CATCH(); + { + freecon(unlabeled_label); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(unlabeled_label); + } + return label; +} diff --git a/src/backend/sepgsql/misc.c b/src/backend/sepgsql/misc.c new file mode 100644 index 0000000..4f29cd7 --- /dev/null +++ b/src/backend/sepgsql/misc.c @@ -0,0 +1,123 @@ +/* + * misc.c + * + * SELinux hooks related to misc features + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "libpq/libpq-be.h" +#include "miscadmin.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" +#include "utils/guc.h" + +#include + +/* + * sepgsql_client_label + * + * security context of the peer process + */ +static char *sepgsql_client_label = NULL; + +char * +sepgsql_get_client_label(void) +{ + return sepgsql_client_label; +} + +char * +sepgsql_set_client_label(char *new_label) +{ + char *old_label = sepgsql_client_label; + + sepgsql_client_label = new_label; + +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_avc_switch(new_label); + } +#endif + return old_label; +} + +void +sepgsql_post_bootstraping(void) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + sepgsql_initial_labeling(); +#endif +} + +void +sepgsql_initialize(void) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + security_context_t context; + + /* init userspace avc */ + sepgsql_avc_init(); + + /* init privilege of the client */ + if (!MyProcPort) + { + /* + * SE-PgSQL does not prevent anything in single-user mode. + */ + sepostgresql_mode = SEPGSQL_MODE_INTERNAL; + + if (getprevcon_raw(&context) < 0) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("SELinux: could not get server context"))); + } + else + { + if (getpeercon_raw(MyProcPort->sock, &context) < 0) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("SELinux: could not get client context"))); + } + sepgsql_set_client_label(context); + + return; + } +#endif + if (default_with_secids) + { + default_with_secids = false; + elog(LOG, "guc: default_with_secid was turned off " + "because no label based access control is availabel now"); + } +} + +bool +sepgsql_worker_needed(void) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + return true; +#endif + return false; +} + +void +sepgsql_worker_main(void) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_avc_worker_main(); + return; + } +#endif + elog(FATAL, "Bug? try to launch worker process without security provider"); +} + diff --git a/src/backend/sepgsql/operator.c b/src/backend/sepgsql/operator.c new file mode 100644 index 0000000..7960525 --- /dev/null +++ b/src/backend/sepgsql/operator.c @@ -0,0 +1,454 @@ +/* + * operator.c + * + * SELinux hooks related to operators + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "catalog/pg_operator.h" +#include "catalog/pg_opclass.h" +#include "catalog/pg_opfamily.h" +#include "catalog/pg_seclabel.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + +#ifdef HAVE_SELINUX +bool +sepgsql_operator_common(Oid operOid, uint32 required, bool abort) +{ + sepgsql_sid_t tsid; + char *auname; + bool retval; + + tsid.relid = OperatorRelationId; + tsid.secid = GetSysCacheSecid1(OPEROID, ObjectIdGetDatum(operOid)); + + auname = format_operator(operOid); + + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + required, + auname, + abort); + pfree(auname); + + return retval; +} + +bool +sepgsql_opclass_common(Oid opcOid, uint32 required, bool abort) +{ + sepgsql_sid_t tsid; + HeapTuple tuple; + const char *auname; + bool retval; + + tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for operator class %u", opcOid); + + tsid.relid = OperatorClassRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + auname = NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname); + + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + required, + auname, + abort); + ReleaseSysCache(tuple); + + return retval; +} + +bool +sepgsql_opfamily_common(Oid opfOid, uint32 required, bool abort) +{ + sepgsql_sid_t tsid; + HeapTuple tuple; + const char *auname; + bool retval; + + tuple = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for operator family %u", opfOid); + + tsid.relid = OperatorFamilyRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + auname = NameStr(((Form_pg_opfamily) GETSTRUCT(tuple))->opfname); + + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + required, + auname, + abort); + ReleaseSysCache(tuple); + + return retval; +} + +static Oid +get_operator_namespace(Oid operOid) +{ + HeapTuple tuple; + Oid namespaceId; + + tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid)); + if (HeapTupleIsValid(tuple)) + { + namespaceId = ((Form_pg_operator) GETSTRUCT(tuple))->oprnamespace; + + ReleaseSysCache(tuple); + } + return namespaceId; +} + +static Oid +get_opclass_namespace(Oid opcOid) +{ + HeapTuple tuple; + Oid namespaceId = InvalidOid; + + tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcOid)); + if (HeapTupleIsValid(tuple)) + { + namespaceId = ((Form_pg_opclass) GETSTRUCT(tuple))->opcnamespace; + + ReleaseSysCache(tuple); + } + return namespaceId; +} + +static Oid +get_opfamily_namespace(Oid opfOid) +{ + HeapTuple tuple; + Oid namespaceId = InvalidOid; + + tuple = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfOid)); + if (HeapTupleIsValid(tuple)) + { + namespaceId = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfnamespace; + + ReleaseSysCache(tuple); + } + return namespaceId; +} + +#endif + +Oid +sepgsql_operator_create(const char *operName, Oid replaced, Oid namespaceId, + Oid codeFunc, Oid restrictFunc, Oid joinFunc, + Oid commutatorOp, Oid negatorOp) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + uint32 required; + + /* db_schema:{add_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_tuple:{insert} */ + if (OidIsValid(replaced)) + { + nsid.relid = OperatorRelationId; + nsid.secid = GetSysCacheSecid1(OPEROID, + ObjectIdGetDatum(replaced)); + required = SEPG_DB_TUPLE__UPDATE; + } + else + { + nsid = sepgsql_get_default_tuple_secid(OperatorRelationId); + required = SEPG_DB_TUPLE__INSERT; + } + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_TUPLE, + required, + operName, + true); + + /* db_procedure:{install} */ + if (OidIsValid(codeFunc)) + sepgsql_proc_common(codeFunc, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(restrictFunc)) + sepgsql_proc_common(restrictFunc, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(joinFunc)) + sepgsql_proc_common(joinFunc, SEPG_DB_PROCEDURE__INSTALL, true); + + /* + * XXX - we should check anything on he commutatorOp/negatorOp + */ + + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_operator_alter(Oid operOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_operator_common(operOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +Oid +sepgsql_operator_relabel(Oid operOid, char *newLabel) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + + nsid.relid = OperatorRelationId; + nsid.secid = seclabelTransInput(OperatorRelationId, newLabel); + + /* db_tuple:{update relabelfrom} */ + sepgsql_operator_common(operOid, + SEPG_DB_TUPLE__UPDATE | + SEPG_DB_TUPLE__RELABELFROM, + true); + + /* db_tuple:{relabelto} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__RELABELTO, + format_operator(operOid), + true); + return nsid.secid; + } +#endif + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SE-PostgreSQL is not available"))); + return InvalidOid; +} + +void +sepgsql_operator_drop(Oid operOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_operator_namespace(operOid); + + /* db_namespace:{remove_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_tuple:{delete} */ + sepgsql_operator_common(operOid, SEPG_DB_TUPLE__DELETE, true); + } +#endif +} + +void +sepgsql_operator_comment(Oid operOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_operator_common(operOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +Oid +sepgsql_opclass_create(const char *opcName, Oid namespaceId, + Oid typeOid, Oid opfamilyOid, Oid storageOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + + /* db_schema:{add_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_TUPLE__UPDATE, true); + + /* db_tuple:{insert} */ + nsid = sepgsql_get_default_tuple_secid(OperatorRelationId); + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + opcName, + true); + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_opclass_alter(Oid opcOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_opclass_alter_rename(Oid opcOid, const char *newName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_opclass_namespace(opcOid); + + /* db_schema:{add_name remove_name} */ + sepgsql_schema_common(namespaceId, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_tuple:{update} */ + sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_opclass_drop(Oid opcOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_opclass_namespace(opcOid); + + /* db_schema:{remove_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_tuple:{drop} */ + sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__DELETE, true); + } +#endif +} + +void +sepgsql_opclass_comment(Oid opcOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_opclass_common(opcOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +Oid +sepgsql_opfamily_create(const char *opfName, Oid namespaceId, Oid amOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + + /* db_schema:{add_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_tuple:{insert} */ + nsid = sepgsql_get_default_tuple_secid(OperatorFamilyRelationId); + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + opfName, + true); + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_opfamily_alter(Oid opfOid, bool isDrop, Oid amOid, + List *operators, List *procedures) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true); + + /* XXX - to do we should install checks? */ + } +#endif +} + +void +sepgsql_opfamily_alter_rename(Oid opfOid, const char *newName) +{ + +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_opfamily_namespace(opfOid); + + /* db_schema:{add_name remove_name} */ + sepgsql_schema_common(namespaceId, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_tuple:{update} */ + sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_opfamily_alter_owner(Oid opfOid, Oid newOwner) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_opfamily_drop(Oid opfOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_opfamily_namespace(opfOid); + + /* db_schema:{remove_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_tuple:{delete} */ + sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__DELETE, true); + } +#endif +} + +void +sepgsql_opfamily_comment(Oid opfOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_opfamily_common(opfOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} diff --git a/src/backend/sepgsql/proc.c b/src/backend/sepgsql/proc.c new file mode 100644 index 0000000..1149571 --- /dev/null +++ b/src/backend/sepgsql/proc.c @@ -0,0 +1,366 @@ +/* + * proc.c + * + * SELinux hooks related to procedures + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "catalog/pg_aggregate.h" +#include "catalog/pg_language.h" +#include "catalog/pg_namespace.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_seclabel.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" +#include "utils/builtins.h" +#include "utils/syscache.h" +#include "utils/lsyscache.h" + +#ifdef HAVE_SELINUX +bool +sepgsql_proc_common(Oid procOid, uint32 required, bool abort) +{ + sepgsql_sid_t tsid; + char *auname; + bool retval; + + tsid.relid = ProcedureRelationId; + tsid.secid = GetSysCacheSecid1(PROCOID, + ObjectIdGetDatum(procOid)); + + auname = format_procedure(procOid); + + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_PROCEDURE, + required, + auname, + abort); + pfree(auname); + + return retval; +} +#endif + +Oid +sepgsql_proc_create(const char *proName, Oid replaced, + Oid namespaceId, Oid languageId) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + HeapTuple tuple; + sepgsql_sid_t nsid; + char *scontext; + char *tcontext; + uint32 required; + + /* db_schema:{add_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_procedure:{create or setattr} */ + if (OidIsValid(replaced)) + { + nsid.relid = ProcedureRelationId; + nsid.secid = GetSysCacheSecid1(PROCOID, + ObjectIdGetDatum(replaced)); + required = SEPG_DB_PROCEDURE__SETATTR; + } + else + { + nsid = sepgsql_get_default_proc_secid(namespaceId); + required = SEPG_DB_PROCEDURE__CREATE; + } + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_PROCEDURE, + required, + proName, + true); + + /* db_language:{implemente} */ + tuple = SearchSysCache1(LANGOID, ObjectIdGetDatum(languageId)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for language %u", languageId); + + scontext = seclabelRawOutput(LanguageRelationId, + HeapTupleGetSecid(tuple)); + tcontext = seclabelRawOutput(nsid.relid, nsid.secid); + + sepgsql_compute_perms(scontext, tcontext, + SEPG_CLASS_DB_LANGUAGE, + SEPG_DB_LANGUAGE__IMPLEMENTE, + proName, true); + + ReleaseSysCache(tuple); + + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_proc_alter(Oid procOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); + } +#endif +} + +void +sepgsql_proc_alter_rename(Oid procOid, const char *newName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_func_namespace(procOid); + + /* db_schema:{remove_name add_name} */ + sepgsql_schema_common(namespaceId, + SEPG_DB_SCHEMA__REMOVE_NAME | + SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_procedure:{setattr} */ + sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); + } +#endif +} + +void +sepgsql_proc_alter_schema(Oid procOid, Oid newSchema) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_func_namespace(procOid); + + /* db_schema:{remove_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_schema:{add_name} */ + sepgsql_schema_common(newSchema, SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_procedure:{setattr} */ + sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); + } +#endif +} + +Oid +sepgsql_proc_relabel(Oid procOid, char *new_label) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t tsid; + + tsid.relid = ProcedureRelationId; + tsid.secid = seclabelTransInput(tsid.relid, new_label); + + /* db_procedure:{setattr relabelfrom} */ + sepgsql_proc_common(procOid, + SEPG_DB_PROCEDURE__SETATTR | + SEPG_DB_PROCEDURE__RELABELFROM, true); + + /* db_procedure:{relabelto} */ + sepgsql_client_perms(tsid, + SEPG_CLASS_DB_PROCEDURE, + SEPG_DB_PROCEDURE__RELABELTO, + format_procedure(procOid), + true); + return tsid.secid; + } +#endif + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SE-PostgreSQL is not available"))); + return InvalidOid; +} + +void +sepgsql_proc_drop(Oid procOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_func_namespace(procOid); + + /* db_schema:{remove_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_procedure:{proc} */ + sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__DROP, true); + } +#endif +} + +void +sepgsql_proc_grant(Oid procOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); + } +#endif +} + +void +sepgsql_proc_comment(Oid procOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); + } +#endif +} + +void +sepgsql_proc_execute(Oid procOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__EXECUTE, true); + } +#endif +} + +bool +sepgsql_proc_be_inlined(HeapTuple protup) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t tsid; + char *old_label; + char *new_label; + const char *auname + = NameStr(((Form_pg_proc) GETSTRUCT(protup))->proname); + + tsid.relid = ProcedureRelationId; + tsid.secid = HeapTupleGetSecid(protup); + + if (!sepgsql_client_perms(tsid, + SEPG_CLASS_DB_PROCEDURE, + SEPG_DB_PROCEDURE__EXECUTE, + auname, false)) + return false; + + old_label = sepgsql_get_client_label(); + new_label = sepgsql_client_create_label(tsid, SEPG_CLASS_PROCESS); + if (strcmp(old_label, new_label) != 0) + return false; + + return true; + } +#endif + return true; +} + +char * +sepgsql_proc_domtrans(HeapTuple protup, MemoryContext mcxt) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t tsid; + char *old_label = sepgsql_get_client_label(); + char *new_label; + char *auname + = NameStr(((Form_pg_proc) GETSTRUCT(protup))->proname); + + tsid.relid = ProcedureRelationId; + tsid.secid = HeapTupleGetSecid(protup); + + new_label = sepgsql_client_create_label(tsid, SEPG_CLASS_PROCESS); + + if (strcmp(old_label, new_label) == 0) + return NULL; + + /* db_procedure:{entrypoint} */ + sepgsql_client_perms(tsid, + SEPG_CLASS_DB_PROCEDURE, + SEPG_DB_PROCEDURE__ENTRYPOINT, + auname, + true); + + /* db_process:{transition} */ + sepgsql_compute_perms(old_label, + new_label, + SEPG_CLASS_PROCESS, + SEPG_PROCESS__TRANSITION, + NULL, + true); + + return MemoryContextStrdup(mcxt, new_label); + } +#endif + return NULL; +} + +Oid +sepgsql_aggregate_create(const char *aggName, Oid namespaceId, + Oid transFunc, Oid finalFunc) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid securityId; + + /* check normal creation permission */ + securityId = sepgsql_proc_create(aggName, InvalidOid, + namespaceId, INTERNALlanguageId); + + /* db_procedure:{install} */ + if (OidIsValid(transFunc)) + sepgsql_proc_common(transFunc, SEPG_DB_PROCEDURE__INSTALL, true); + + /* db_procedure:{install} */ + if (OidIsValid(finalFunc)) + sepgsql_proc_common(finalFunc, SEPG_DB_PROCEDURE__INSTALL, true); + + return securityId; + } +#endif + return InvalidOid; +} + +void +sepgsql_aggregate_execute(Oid aggOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Form_pg_aggregate aggForm; + HeapTuple tuple; + + tuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for aggregate %u", aggOid); + + aggForm = (Form_pg_aggregate) GETSTRUCT(tuple); + + /* pg_proc:{execute} */ + sepgsql_proc_common(aggOid, SEPG_DB_PROCEDURE__EXECUTE, true); + + /* pg_proc:{execute} */ + if (OidIsValid(aggForm->aggtransfn)) + sepgsql_proc_common(aggForm->aggtransfn, + SEPG_DB_PROCEDURE__EXECUTE, true); + + /* pg_proc:{execute} */ + if (OidIsValid(aggForm->aggfinalfn)) + sepgsql_proc_common(aggForm->aggfinalfn, + SEPG_DB_PROCEDURE__EXECUTE, true); + + ReleaseSysCache(tuple); + } +#endif +} diff --git a/src/backend/sepgsql/proxy.c b/src/backend/sepgsql/proxy.c new file mode 100644 index 0000000..32bb57a --- /dev/null +++ b/src/backend/sepgsql/proxy.c @@ -0,0 +1,129 @@ +/* + * proxy.c + * + * mandatory query rewriting support + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "catalog/namespace.h" +#include "lib/stringinfo.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "tcop/tcopprot.h" + +#ifdef HAVE_SELINUX +/* + * If row-level access control is configured, COPY TO statement + * shall be rewritten to SELECT * statement. + */ +static void +sepgsql_proxy_copy_stmt(CopyStmt *stmt) +{ + Oid relOid; + Oid namespaceId; + List *queries; + ListCell *l, *p = NULL; + bool with_oids = false; + StringInfoData qbuf; + + /* no need to do nothing */ + if (stmt->is_from || !stmt->relation) + return; + + /* obtain relaion ID */ + relOid = RangeVarGetRelid(stmt->relation, false); + + namespaceId = get_rel_namespace(relOid); + + /* Is there WITH OID option? */ +retry: + foreach (l, stmt->options) + { + DefElem *defel = (DefElem *) lfirst(l); + + Assert(IsA(defel, DefElem)); + if (strcmp(defel->defname, "oids") == 0) + { + with_oids = true; + stmt->options = list_delete_cell(stmt->options, l, p); + goto retry; + } + p = l; + } + + /* Make a query */ + initStringInfo(&qbuf); + + appendStringInfo(&qbuf, "SELECT %s", with_oids ? "oid" : ""); + + if (stmt->attlist == NIL) + appendStringInfo(&qbuf, "%s*", with_oids ? "," : ""); + else + { + bool need_comma = with_oids; + + foreach (l, stmt->attlist) + { + appendStringInfo(&qbuf, "%s%s", + need_comma ? "," : "", + strVal(lfirst(l))); + need_comma = true; + } + } + + appendStringInfo(&qbuf, " FROM ONLY %s.%s", + quote_identifier(get_namespace_name(namespaceId)), + quote_identifier(get_rel_name(relOid))); + + queries = pg_parse_query(qbuf.data); + + Assert(list_length(queries) == 1); + + /* update CopyStmt */ + stmt->query = lfirst(list_head(queries)); + stmt->relation = NULL; +} +#endif + +void +sepgsql_proxy_queries(List *queryList) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + ListCell *l; + + foreach (l, queryList) + { + Query *qry = lfirst(l); + + switch (qry->commandType) + { + case CMD_SELECT: + break; + + case CMD_UPDATE: + case CMD_INSERT: + case CMD_DELETE: + /* we have no rewrite policy */ + break; + + case CMD_UTILITY: + Assert(qry->utilityStmt != NULL); + if (IsA(qry->utilityStmt, CopyStmt)) + sepgsql_proxy_copy_stmt((CopyStmt *)qry->utilityStmt); + break; + + default: + /* CMD_UNKNOWN or CMD_NOTHING */ + break; + } + } + } +#endif +} diff --git a/src/backend/sepgsql/relation.c b/src/backend/sepgsql/relation.c new file mode 100644 index 0000000..2604749 --- /dev/null +++ b/src/backend/sepgsql/relation.c @@ -0,0 +1,819 @@ +/* + * relation.c + * + * SELinux hooks related to relation + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "access/sysattr.h" +#include "catalog/catalog.h" +#include "catalog/heap.h" +#include "catalog/pg_attribute.h" +#include "catalog/pg_class.h" +#include "catalog/pg_seclabel.h" +#include "catalog/pg_type.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + +#ifdef HAVE_SELINUX +bool +sepgsql_relation_common(Oid relOid, uint32 required, bool abort) +{ + Form_pg_class classForm; + HeapTuple tuple; + sepgsql_sid_t tsid; + uint16 tclass; + bool retval; + + tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", relOid); + classForm = (Form_pg_class) GETSTRUCT(tuple); + + tsid.relid = RelationRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + switch (classForm->relkind) + { + case RELKIND_SEQUENCE: + tclass = SEPG_CLASS_DB_SEQUENCE; + break; + + case RELKIND_VIEW: + tclass = SEPG_CLASS_DB_VIEW; + break; + + case RELKIND_COMPOSITE_TYPE: + tclass = SEPG_CLASS_DB_TUPLE; + break; + + default: + tclass = SEPG_CLASS_DB_TABLE; + break; + } + + retval = sepgsql_client_perms(tsid, + tclass, + required, + NameStr(classForm->relname), + abort); + ReleaseSysCache(tuple); + + return retval; +} + +static Bitmapset * +fixup_whole_row_reference(Oid relOid, int natts, 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 <= natts; attno++) + { + Form_pg_attribute attForm; + HeapTuple atttup; + + atttup = SearchSysCache2(ATTNUM, + ObjectIdGetDatum(relOid), + Int16GetDatum(attno)); + 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; +} +#endif + +bool +sepgsql_relation_perms(Oid relOid, AclMode aclmask, + Bitmapset *selectedCols, + Bitmapset *modifiedCols, bool abort) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Form_pg_class relForm; + HeapTuple tuple; + sepgsql_sid_t rsid; + Bitmapset *selColsEx; + Bitmapset *modColsEx; + Bitmapset *columns; + AttrNumber nattrs; + AttrNumber attno; + const char *auname; + char relkind; + uint16 tclass = 0; + uint32 required = 0; + bool rc = true; + + /* + * Hardwired policy: + * SE-PostgreSQL enforces clients cannot modify system catalogs + * and access toast values using DML statements in enforcing mode. + * Note that it performs in permissive mode during initdb phase. + */ + if (sepgsql_get_enforce()) + { + if (IsSystemNamespace(get_rel_namespace(relOid)) && + (aclmask & (ACL_UPDATE | ACL_INSERT | ACL_DELETE)) != 0) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("not allowed to modify system catalog \"%s\"", + get_rel_name(relOid)))); + + if (get_rel_relkind(relOid) == RELKIND_TOASTVALUE) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("not allowed to access toast values \"%s\"", + get_rel_name(relOid)))); + } + + /* + * check relation's permissions + */ + tuple = SearchSysCache1(RELOID, + ObjectIdGetDatum(relOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", relOid); + + rsid.relid = RelationRelationId; + rsid.secid = HeapTupleGetSecid(tuple); + + relForm = (Form_pg_class) GETSTRUCT(tuple); + auname = NameStr(relForm->relname); + relkind = relForm->relkind; + nattrs = relForm->relnatts; + + switch (relkind) + { + case RELKIND_RELATION: + tclass = SEPG_CLASS_DB_TABLE; + + if (aclmask & ACL_SELECT) + required |= SEPG_DB_TABLE__SELECT; + if (aclmask & ACL_INSERT) + required |= SEPG_DB_TABLE__INSERT; + if (aclmask & ACL_UPDATE) + required |= (!modifiedCols + ? SEPG_DB_TABLE__LOCK + : SEPG_DB_TABLE__UPDATE); + if (aclmask & ACL_DELETE) + required |= SEPG_DB_TABLE__DELETE; + break; + + case RELKIND_SEQUENCE: + tclass = SEPG_CLASS_DB_SEQUENCE; + if (aclmask & ACL_SELECT) + required |= SEPG_DB_SEQUENCE__GET_VALUE; + break; + + case RELKIND_VIEW: + tclass = SEPG_CLASS_DB_VIEW; + if (aclmask != 0) + required |= SEPG_DB_VIEW__EXPAND; + break; + + default: + elog(ERROR, "Bug? unexpected relkind %c", relkind); + return false; + } + + if (required != 0) + rc = sepgsql_client_perms(rsid, tclass, required, auname, abort); + + ReleaseSysCache(tuple); + + if (!rc || relkind != RELKIND_RELATION) + return rc; + + /* + * Check column's permissions + */ + selColsEx = fixup_whole_row_reference(relOid, nattrs, + selectedCols); + modColsEx = fixup_whole_row_reference(relOid, nattrs, + modifiedCols); + columns = bms_union(selColsEx, modColsEx); + + while ((attno = bms_first_member(columns)) >= 0) + { + required = 0; + + if (bms_is_member(attno, selColsEx)) + required |= SEPG_DB_COLUMN__SELECT; + if (bms_is_member(attno, modColsEx)) + { + if (aclmask & ACL_UPDATE) + required |= SEPG_DB_COLUMN__UPDATE; + if (aclmask & ACL_INSERT) + required |= SEPG_DB_COLUMN__INSERT; + } + if (required == 0) + continue; + + attno += FirstLowInvalidHeapAttributeNumber; + rc = sepgsql_attribute_common(relOid, attno, required, abort); + if (!rc) + break; + } + + if (selColsEx != selectedCols) + bms_free(selColsEx); + if (modColsEx != modifiedCols) + bms_free(modColsEx); + bms_free(columns); + + return rc; + } +#endif + return true; +} + +Oid * +sepgsql_relation_create(const char *relName, + char relkind, + TupleDesc tupDesc, + Oid namespaceId, + List *supOids, + bool createAs) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t tsid; + sepgsql_sid_t csid; + Oid *secLabels; + uint16 tclass; + uint32 perms; + AttrNumber index, attno, nitems; + + /* + * The secLabeld array stores security identifiers to be + * assigned on the new table and columns. + * + * secLabels[0] is security-id of the relation. + * secLabels[attnum - FirstLowInvalidHeapAttributeNumber] + * is security-id of the columns. + */ + secLabels = seclabelMakeRelationDefaults(tupDesc, supOids); + nitems = tupDesc->natts - FirstLowInvalidHeapAttributeNumber; + + switch (relkind) + { + case RELKIND_RELATION: + if (!OidIsValid(secLabels[0])) + { + tsid = sepgsql_get_default_table_secid(namespaceId); + secLabels[0] = tsid.secid; + } + tclass = SEPG_CLASS_DB_TABLE; + perms = SEPG_DB_TABLE__CREATE; + if (createAs) + perms |= SEPG_DB_TABLE__INSERT; + break; + + case RELKIND_SEQUENCE: + if (!OidIsValid(secLabels[0])) + { + tsid = sepgsql_get_default_sequence_secid(namespaceId); + secLabels[0] = tsid.secid; + } + tclass = SEPG_CLASS_DB_SEQUENCE; + perms = SEPG_DB_SEQUENCE__CREATE; + break; + + case RELKIND_VIEW: + if (!OidIsValid(secLabels[0])) + { + tsid = sepgsql_get_default_view_secid(namespaceId); + secLabels[0] = tsid.secid; + } + tclass = SEPG_CLASS_DB_VIEW; + perms = SEPG_DB_VIEW__CREATE; + break; + + case RELKIND_COMPOSITE_TYPE: + if (!OidIsValid(secLabels[0])) + { + tsid = sepgsql_get_default_tuple_secid(TypeRelationId); + secLabels[0] = seclabelMoveSecid(RelationRelationId, + TypeRelationId, tsid.secid); + } + tclass = SEPG_CLASS_DB_TUPLE; + perms = SEPG_DB_TUPLE__INSERT; + break; + + default: + elog(ERROR, "Bug? unexpected relkind %c", relkind); + return NULL; + } + /* db_schema:{add_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_table:{create} or others */ + tsid.relid = RelationRelationId; + tsid.secid = secLabels[0]; + sepgsql_client_perms(tsid, tclass, perms, relName, true); + + /* no individual security-id except for RELKIND_RELATION */ + if (relkind != RELKIND_RELATION) + { + Oid securityId = seclabelMoveSecid(AttributeRelationId, + RelationRelationId, secLabels[0]); + + for (index = 1; index < nitems; index++) + secLabels[index] = securityId; + + return secLabels; + } + + /* + * security context of the columns + */ + for (index = 1; index < nitems; index++) + { + Form_pg_attribute attForm; + char auname[NAMEDATALEN * 2 + 10]; + + attno = index + FirstLowInvalidHeapAttributeNumber; + + /* skip unnecessary system columns */ + if ((attno == ObjectIdAttributeNumber && !tupDesc->tdhasoid) || + (attno == SecurityLabelAttributeNumber && !tupDesc->tdhassecid)) + continue; + + if (!OidIsValid(secLabels[index])) + { + csid = sepgsql_client_create_secid(tsid, + SEPG_CLASS_DB_COLUMN, + AttributeRelationId); + secLabels[index] = csid.secid; + } + + if (attno < 0) + attForm = SystemAttributeDefinition(attno, + tupDesc->tdhasoid, + tupDesc->tdhassecid); + else + attForm = tupDesc->attrs[attno]; + + /* db_column:{create (insert)} permission */ + csid.relid = AttributeRelationId; + csid.secid = secLabels[index]; + + perms = SEPG_DB_COLUMN__CREATE; + if (createAs && attno >= 0) + perms |= SEPG_DB_COLUMN__INSERT; + + snprintf(auname, sizeof(auname), "%s.%s", + relName, NameStr(attForm->attname)); + + sepgsql_client_perms(csid, + SEPG_CLASS_DB_COLUMN, + perms, + auname, + true); + } + return secLabels; + } +#endif + return NULL; +} + +void +sepgsql_relation_alter(Oid relationOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__SETATTR, true); + } +#endif +} + +void +sepgsql_relation_alter_schema(Oid relationOid, Oid newSchema) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__SETATTR, true); + } +#endif +} + +void +sepgsql_relation_alter_rename(Oid relationOid, const char *newName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__SETATTR, true); + } +#endif +} + +void +sepgsql_relation_alter_inherit(Oid childOid, Oid parentOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_relation_common(childOid, + SEPG_DB_TABLE__SETATTR, true); + } +#endif +} + +Oid +sepgsql_relation_relabel(Oid relationOid, char *new_label) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + char *auname; + char relkind; + uint16 tclass = 0; + + nsid.relid = RelationRelationId; + nsid.secid = seclabelTransInput(nsid.relid, new_label); + + auname = get_rel_name(relationOid); + relkind = get_rel_relkind(relationOid); + + switch (relkind) + { + case RELKIND_RELATION: + tclass = SEPG_CLASS_DB_TABLE; + break; + + case RELKIND_SEQUENCE: + tclass = SEPG_CLASS_DB_SEQUENCE; + break; + + case RELKIND_VIEW: + tclass = SEPG_CLASS_DB_VIEW; + break; + + case RELKIND_COMPOSITE_TYPE: + tclass = SEPG_CLASS_DB_TUPLE; + break; + + default: + elog(ERROR, "unexpected relkind %c", relkind); + break;; + } + /* db_xxx:{setattr relabelfrom} */ + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__SETATTR | + SEPG_DB_TABLE__RELABELFROM, true); + + /* db_xxx:{relabelto} */ + sepgsql_client_perms(nsid, + tclass, + SEPG_DB_TABLE__RELABELTO, + auname, true); + pfree(auname); + + return nsid.secid; + } +#endif + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SE-PostgreSQL is not available"))); + return InvalidOid; +} + +void +sepgsql_relation_drop(Oid relationOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__DROP, true); + } +#endif +} + +void +sepgsql_relation_getattr(Oid relationOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__GETATTR, true); + } +#endif +} + +void +sepgsql_relation_grant(Oid relationOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__SETATTR, true); + } +#endif +} + +void +sepgsql_relation_comment(Oid relationOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__SETATTR, true); + } +#endif +} + +bool +sepgsql_relation_cluster(Oid relationOid, bool abort) +{ +#ifdef HAVE_SELINUX + Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); + + if (sepgsql_is_enabled()) + { + bool retval = + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__INDEXON, true); + return retval; + } +#endif + return true; +} + +void +sepgsql_relation_truncate(Relation rel) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION); + + /* db_table:{delete} */ + sepgsql_relation_common(RelationGetRelid(rel), + SEPG_DB_TABLE__DELETE, true); + /* db_tuple:{delete} */ + } +#endif +} + +void +sepgsql_relation_lock(Relation rel) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION); + + /* db_table:{lock} */ + sepgsql_relation_common(RelationGetRelid(rel), + SEPG_DB_TABLE__LOCK, true); + } +#endif +} + +void +sepgsql_relation_reindex(Oid relationOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); + + /* db_table:{indexon} */ + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__INDEXON, true); + } +#endif +} + +void +sepgsql_view_replace(Oid viewOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Assert(get_rel_relkind(viewOid) == RELKIND_VIEW); + /* db_view:{setattr} */ + sepgsql_relation_common(viewOid, + SEPG_DB_VIEW__SETATTR, true); + } +#endif +} + +void +sepgsql_index_create(Oid relationOid, Oid namespaceOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_schema:{add_name} */ + sepgsql_schema_common(namespaceOid, + SEPG_DB_SCHEMA__ADD_NAME, true); + /* db_table:{setattr indexon} */ + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__SETATTR | + SEPG_DB_TABLE__INDEXON, true); + } +#endif +} + +void +sepgsql_index_reindex(Oid indexOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + HeapTuple tuple; + Oid relationOid; + + tuple = SearchSysCache1(INDEXRELID, indexOid); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for index %u", indexOid); + relationOid = ((Form_pg_index) GETSTRUCT(tuple))->indrelid; + ReleaseSysCache(tuple); + + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__INDEXON, true); + } +#endif +} + +void +sepgsql_sequence_get_value(Oid sequenceOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Assert(get_rel_relkind(sequenceOid) == RELKIND_SEQUENCE); + sepgsql_relation_common(sequenceOid, + SEPG_DB_SEQUENCE__GET_VALUE, true); + } +#endif +} + +void +sepgsql_sequence_next_value(Oid sequenceOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Assert(get_rel_relkind(sequenceOid) == RELKIND_SEQUENCE); + sepgsql_relation_common(sequenceOid, + SEPG_DB_SEQUENCE__NEXT_VALUE, true); + } +#endif +} + +void +sepgsql_sequence_set_value(Oid sequenceOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Assert(get_rel_relkind(sequenceOid) == RELKIND_SEQUENCE); + sepgsql_relation_common(sequenceOid, + SEPG_DB_SEQUENCE__SET_VALUE, true); + } +#endif +} + +void +sepgsql_rule_create(Oid relationOid, const char *ruleName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__SETATTR, true); + } +#endif +} + +void +sepgsql_rule_drop(Oid relationOid, const char *ruleName, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled() && !cascade) + { + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__SETATTR, true); + } +#endif +} + +void +sepgsql_rule_comment(Oid relationOid, const char *ruleName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__SETATTR, true); + } +#endif +} + +void +sepgsql_trigger_create(Oid relationOid, const char *triggerName, + Oid constrrelid, Oid funcOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); + + /* db_table:{setattr} */ + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__SETATTR, true); + /* db_procedure:{install} */ + // sepgsql_procedure_common... + } +#endif +} + +void +sepgsql_trigger_alter(Oid relationOid, const char *triggerName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__SETATTR, true); + } +#endif +} + +void +sepgsql_trigger_drop(Oid relationOid, const char *triggerName, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled() && !cascade) + { + Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__SETATTR, true); + } +#endif +} + +void +sepgsql_trigger_comment(Oid relationOid, const char *triggerName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__SETATTR, true); + } +#endif +} + +void +sepgsql_constraint_comment(Oid relationOid, const char *constName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Assert(get_rel_relkind(relationOid) == RELKIND_RELATION); + sepgsql_relation_common(relationOid, + SEPG_DB_TABLE__SETATTR, true); + } +#endif +} diff --git a/src/backend/sepgsql/role.c b/src/backend/sepgsql/role.c new file mode 100644 index 0000000..3c4891c --- /dev/null +++ b/src/backend/sepgsql/role.c @@ -0,0 +1,142 @@ +/* + * role.c + * + * SELinux hooks related to roles + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "catalog/pg_authid.h" +#include "catalog/pg_seclabel.h" +#include "miscadmin.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + +#ifdef HAVE_SELINUX +bool +sepgsql_role_common(Oid roleOid, uint32 required, bool abort) +{ + sepgsql_sid_t tsid; + HeapTuple tuple; + const char *auname; + bool retval; + + tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for role %u", roleOid); + + tsid.relid = AuthIdRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + auname = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname); + + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + required, + auname, + abort); + ReleaseSysCache(tuple); + + return retval; +} +#endif + +Oid +sepgsql_role_create(const char *roleName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + + nsid = sepgsql_get_default_tuple_secid(AuthIdRelationId); + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + roleName, + true); + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_role_alter(Oid roleOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_role_common(roleOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +Oid +sepgsql_role_relabel(Oid roleOid, char *newLabel) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t tsid; + + tsid.relid = AuthIdRelationId; + tsid.secid = seclabelTransInput(tsid.relid, newLabel); + + /* db_tuple:{update relabelfrom} */ + sepgsql_role_common(roleOid, + SEPG_DB_TUPLE__UPDATE | + SEPG_DB_TUPLE__RELABELFROM, + true); + + /* db_tuple:{relabelto} */ + sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__RELABELTO, + GetUserNameFromId(roleOid), + true); + return tsid.secid; + } +#endif + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SE-PostgreSQL is not available"))); + return InvalidOid; +} + +void +sepgsql_role_drop(Oid roleOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_role_common(roleOid, SEPG_DB_TUPLE__DELETE, true); + } +#endif +} + +void +sepgsql_role_grant(Oid roleOid, bool is_grant, List *memberIds) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_role_common(roleOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_role_comment(Oid roleOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_role_common(roleOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} diff --git a/src/backend/sepgsql/rowlv.c b/src/backend/sepgsql/rowlv.c new file mode 100644 index 0000000..9ace5e4 --- /dev/null +++ b/src/backend/sepgsql/rowlv.c @@ -0,0 +1,367 @@ +/* + * rowlv.c + * + * Row-level access control facilities + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "access/sysattr.h" +#include "catalog/pg_attribute.h" +#include "catalog/pg_class.h" +#include "catalog/pg_database.h" +#include "catalog/pg_language.h" +#include "catalog/pg_largeobject_metadata.h" +#include "catalog/pg_namespace.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_seclabel.h" +#include "catalog/pg_type.h" +#include "nodes/makefuncs.h" +#include "parser/parsetree.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" +#include "storage/bufmgr.h" +#include "utils/fmgroids.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" +#include "utils/tqual.h" + +static int sepgsql_rowlv_mode = SEPGSQL_ROWLV_FILTER; + +int +sepgsql_rowlv_get_mode(void) +{ + return sepgsql_rowlv_mode; +} + +int +sepgsql_rowlv_set_mode(int new_mode) +{ + int old_mode = sepgsql_rowlv_mode; + + Assert(new_mode == SEPGSQL_ROWLV_FILTER || + new_mode == SEPGSQL_ROWLV_ABORT || + new_mode == SEPGSQL_ROWLV_BYPASS); + + sepgsql_rowlv_mode = new_mode; + + return old_mode; +} + +void +sepgsql_rowlv_add_policy(PlannerInfo *root, Scan *scan) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled() && scan->scanrelid > 0) + { + RangeTblEntry *rte = planner_rt_fetch(scan->scanrelid, root); + Form_pg_class classForm; + HeapTuple tuple; + FuncExpr *func; + Var *v1; /* tableoid */ + Var *v2; /* row reference */ + Const *c3; /* required permissions */ + Const *c4; /* abort? or filter? */ + bool abort; + bool relhassecids; + Oid reltype; + + if (sepgsql_rowlv_mode == SEPGSQL_ROWLV_BYPASS) + return; + + Assert(IsA(rte, RangeTblEntry)); + if (rte->rowlvPerms == 0) + return; + + tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(rte->relid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", rte->relid); + + classForm = (Form_pg_class) GETSTRUCT(tuple); + reltype = classForm->reltype; + relhassecids = classForm->relhassecids; + + ReleaseSysCache(tuple); + + /* + * In the case when tuples have no individual security labels, + * sepgsql_relation_perms() checks permissions on the relation's + * label, as if it is security label of the whole tuples. + */ + if (!relhassecids) + return; + + /* 1st argument : tableoid */ + v1 = makeVar(scan->scanrelid, + TableOidAttributeNumber, + OIDOID, + -1, + 0); + + /* 2nd argument : whole row reference */ + v2 = makeVar(scan->scanrelid, + InvalidAttrNumber, + reltype, + -1, + 0); + + /* 3rd argument : required permissions */ + c3 = makeConst(INT4OID, + -1, + sizeof(int32), + Int32GetDatum(rte->rowlvPerms), + false, + true); + + /* 4th argument : abort/filter mode */ + abort = (sepgsql_rowlv_mode != SEPGSQL_ROWLV_FILTER); + c4 = makeConst(BOOLOID, + -1, + sizeof(bool), + BoolGetDatum(abort), + false, + true); + + /* sepgsql_tuple_perms(tableoid, , ) */ + func = makeFuncExpr(F_SEPGSQL_TUPLE_PERMS, + BOOLOID, + list_make4(v1, v2, c3, c4), + COERCE_DONTCARE); + + /* append row-level access control policy */ + if (abort) + scan->plan.qual = lappend(scan->plan.qual, func); + else + scan->plan.qual = lcons(func, scan->plan.qual); + } +#endif +} + +uint32 +sepgsql_rowlv_permissions(RangeTblEntry *rte) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + uint32 required = 0; + + if (!OidIsValid(rte->relid) || + get_rel_relkind(rte->relid) != RELKIND_RELATION) + return 0; + + if (rte->requiredPerms & ACL_SELECT) + required |= SEPG_DB_TUPLE__SELECT; + + if (rte->requiredPerms & ACL_UPDATE && + !bms_is_empty(rte->modifiedCols)) + required |= SEPG_DB_TUPLE__UPDATE; + + if (rte->requiredPerms & ACL_DELETE) + required |= SEPG_DB_TUPLE__DELETE; + + return required; + } +#endif + return 0; +} + +void +sepgsql_tuple_insert(Relation rel, HeapTuple tuple) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + Oid relOid = RelationGetRelid(rel); + + if (!RelationGetForm(rel)->relhassecids) + { + nsid.relid = RelationRelationId; + nsid.secid = GetSysCacheSecid1(RELOID, ObjectIdGetDatum(relOid)); + } + else if (OidIsValid(HeapTupleGetSecid(tuple))) + { + nsid.relid = relOid; + nsid.secid = HeapTupleGetSecid(tuple); + } + else + { + nsid = sepgsql_get_default_tuple_secid(relOid); + HeapTupleSetSecid(tuple, nsid.secid); + } + /* db_tuple:{insert} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + NULL, + true); + } +#endif +} + +void +sepgsql_tuple_update(Relation rel, ItemPointer otid, HeapTuple newtup) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t tsid; + HeapTupleData oldtup; + Buffer oldbuf; + Oid newSecId = HeapTupleGetSecid(newtup); + Oid oldSecId; + + /* + * heap_update() preserves security id of the original tuple, + * if no explicit security label was given, so we don't need + * to check anything. + * At this point, db_tuple:{update} is already checked. + */ + if (!OidIsValid(newSecId)) + 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"); + + tsid.relid = RelationGetRelid(rel); + oldSecId = HeapTupleGetSecid(&oldtup); + + if (!seclabelCompareSecid(tsid.relid, oldSecId, + tsid.relid, newSecId)) + { + /* db_tuple:{relabelfrom} */ + tsid.secid = oldSecId; + sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__RELABELFROM, + NULL, + true); + + /* db_tuple:{relabelto} */ + tsid.secid = newSecId; + sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__RELABELTO, + NULL, + true); + } + ReleaseBuffer(oldbuf); + } +#endif +} + +Datum +sepgsql_tuple_perms(PG_FUNCTION_ARGS) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Form_pg_class relForm; + Form_pg_attribute attForm; + sepgsql_sid_t tsid; + HeapTupleHeader htup; + HeapTupleData tuple; + uint16 tclass; + uint32 required; + Oid tableOid; + bool abort; + bool result; + + /* function arguments */ + tableOid = PG_GETARG_OID(0); + htup = PG_GETARG_HEAPTUPLEHEADER(1); + required = PG_GETARG_UINT32(2); + abort = PG_GETARG_BOOL(3); + + /* set up pseudo tuple */ + tuple.t_len = HeapTupleHeaderGetDatumLength(htup); + ItemPointerSetInvalid(&(tuple.t_self)); + tuple.t_tableOid = tableOid; + tuple.t_data = htup; + + /* object class? */ + switch (tableOid) + { + case DatabaseRelationId: + tclass = SEPG_CLASS_DB_DATABASE; + break; + + case NamespaceRelationId: + tclass = SEPG_CLASS_DB_SCHEMA; + break; + + case RelationRelationId: + relForm = (Form_pg_class) GETSTRUCT(&tuple); + switch (relForm->relkind) + { + case RELKIND_RELATION: + tclass = SEPG_CLASS_DB_TABLE; + break; + case RELKIND_SEQUENCE: + tclass = SEPG_CLASS_DB_SEQUENCE; + break; + case RELKIND_VIEW: + tclass = SEPG_CLASS_DB_VIEW; + break; + case RELKIND_COMPOSITE_TYPE: + tclass = SEPG_CLASS_DB_TUPLE; + break; + default: /* index, toast */ + tclass = SEPG_CLASS_DB_TABLE; + break; + } + break; + + case AttributeRelationId: + attForm = (Form_pg_attribute) GETSTRUCT(&tuple); + switch (get_rel_relkind(attForm->attrelid)) + { + case RELKIND_RELATION: + tclass = SEPG_CLASS_DB_COLUMN; + break; + case RELKIND_SEQUENCE: + tclass = SEPG_CLASS_DB_SEQUENCE; + break; + case RELKIND_VIEW: + tclass = SEPG_CLASS_DB_VIEW; + break; + case RELKIND_COMPOSITE_TYPE: + tclass = SEPG_CLASS_DB_TUPLE; + break; + default: /* index, toast */ + tclass = SEPG_CLASS_DB_TABLE; + break; + } + break; + + case LanguageRelationId: + tclass = SEPG_CLASS_DB_LANGUAGE; + break; + + case LargeObjectMetadataRelationId: + tclass = SEPG_CLASS_DB_BLOB; + break; + + default: + tclass = SEPG_CLASS_DB_TUPLE; + break; + } + + /* do permission check */ + tsid.relid = tableOid; + tsid.secid = HeapTupleGetSecid(&tuple); + + result = sepgsql_client_perms(tsid, tclass, required, NULL, abort); + + PG_RETURN_BOOL(result); + } +#endif + PG_RETURN_BOOL(true); +} diff --git a/src/backend/sepgsql/schema.c b/src/backend/sepgsql/schema.c new file mode 100644 index 0000000..24878f9 --- /dev/null +++ b/src/backend/sepgsql/schema.c @@ -0,0 +1,173 @@ +/* + * schema.c + * + * SELinux hooks related to schema + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "catalog/pg_namespace.h" +#include "catalog/pg_seclabel.h" +#include "miscadmin.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + +#ifdef HAVE_SELINUX +bool +sepgsql_schema_common(Oid namespaceOid, uint32 required, bool abort) +{ + HeapTuple tuple; + sepgsql_sid_t tsid; + const char *auname; + bool retval; + + tuple = SearchSysCache1(NAMESPACEOID, + ObjectIdGetDatum(namespaceOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for namespace %u", namespaceOid); + + tsid.relid = NamespaceRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + auname = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_SCHEMA, + required, + auname, + abort); + ReleaseSysCache(tuple); + + return retval; +} +#endif + +Oid +sepgsql_schema_create(const char *nspName, bool is_temp) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + + /* compute default security context */ + nsid = sepgsql_get_default_schema_secid(MyDatabaseId); + + /* db_schema:{create} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_SCHEMA, + SEPG_DB_SCHEMA__CREATE, + nspName, true); + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_schema_alter(Oid namespaceOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_schema_common(namespaceOid, + SEPG_DB_SCHEMA__SETATTR, + true); + } +#endif +} + +Oid +sepgsql_schema_relabel(Oid namespaceOid, char *new_label) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + char *auname; + + nsid.relid = NamespaceRelationId; + nsid.secid = seclabelTransInput(nsid.relid, new_label); + + auname = get_namespace_name(namespaceOid); + + /* db_schema:{setattr relabelfrom} */ + sepgsql_schema_common(namespaceOid, + SEPG_DB_SCHEMA__SETATTR | + SEPG_DB_SCHEMA__RELABELFROM, + true); + + /* db_schema:{relabelto} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_SCHEMA, + SEPG_DB_SCHEMA__RELABELTO, + auname, + true); + pfree(auname); + + return nsid.secid; + } +#endif + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SE-PostgreSQL is not available"))); + return InvalidOid; +} + +void +sepgsql_schema_drop(Oid namespaceOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_schema_common(namespaceOid, + SEPG_DB_SCHEMA__DROP, + true); + } +#endif +} + +void +sepgsql_schema_grant(Oid namespaceOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_schema_common(namespaceOid, + SEPG_DB_SCHEMA__SETATTR, + true); + } +#endif +} + +bool +sepgsql_schema_search(Oid namespaceOid, bool abort) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + bool retval = + sepgsql_schema_common(namespaceOid, + SEPG_DB_SCHEMA__SEARCH, + abort); + return retval; + } +#endif + return true; +} + +void +sepgsql_schema_comment(Oid namespaceOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_schema_common(namespaceOid, + SEPG_DB_SCHEMA__SETATTR, + true); + } +#endif +} diff --git a/src/backend/sepgsql/selinux.c b/src/backend/sepgsql/selinux.c new file mode 100644 index 0000000..ea9c9da --- /dev/null +++ b/src/backend/sepgsql/selinux.c @@ -0,0 +1,670 @@ +/* + * src/backend/security/sepgsql/selinux.c + * Routines to communicate with SELinux. + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "catalog/pg_seclabel.h" +#include "libpq/libpq.h" +#include "miscadmin.h" +#include "sepgsql/sepgsql.h" +#include "utils/guc.h" +#include "utils/memutils.h" + +#include + +/* + * selinux_catalog + * + * This static translation lookup table enables to associate a certain + * object class/permission name with its internal code, such as + * SEPG_CLASS_DB_SCHEMA. + * + * SELinux requires applications to represent object class and a set of + * permissions in code, instead of its name, when we ask SELinux's decision. + * + * See the definition of security_compute_av(3) API in libselinux. + * We need to gives a code of object class, and interpret what permissions + * are allowed on the object class from av_decision structure. + * Actual values of the code depend on the security policy. In other words, + * we cannot know what number is assigned on a certain object class and + * permissions. + * The string_to_security_class(3) and string_to_av_perm(3) APIs takes + * arguments with the name of object class/permission, and returns the + * code for the given object class/permissions. + * For example, we can know what code is assigned on the "db_table" class + * using these functions as follows: + * + * uint16 tclass_ex = string_to_security_class("db_table"); + * + * On the other hand, we use an alternative code internally to simplify + * the implementation, such as SEPG_CLASS_* for object class. + * The following selinux_catalog is used to translate the 'internal' + * code and the 'external' code. + * + * It allows to lookup name of the object class or permission corresponding + * to a certain 'internal' code. Then, we can give the name to SELinux's + * API to obtain 'external' code which can be used to ask in-kernel SELinux. + */ +static struct +{ + const char *class_name; + uint16 class_code; + struct + { + const char *perm_name; + uint32 perm_code; + } perms[32]; +} selinux_catalog[] = { + { + "process", SEPG_CLASS_PROCESS, + { + { "translation", SEPG_PROCESS__TRANSITION }, + { NULL, 0UL } + } + }, + { + "file", SEPG_CLASS_FILE, + { + { "read", SEPG_FILE__READ }, + { "write", SEPG_FILE__WRITE }, + { "create", SEPG_FILE__CREATE }, + { "getattr", SEPG_FILE__GETATTR }, + { NULL, 0UL } + } + }, + { + "dir", SEPG_CLASS_DIR, + { + { "read", SEPG_DIR__READ }, + { "write", SEPG_DIR__WRITE }, + { "create", SEPG_DIR__CREATE }, + { "getattr", SEPG_DIR__GETATTR }, + { NULL, 0UL } + } + }, + { + "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, 0UL } + } + }, + { + "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, 0UL } + } + }, + { + "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, 0UL } + } + }, + { + "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, 0UL } + } + }, + { + "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 }, + { "load_module", SEPG_DB_DATABASE__LOAD_MODULE }, + { 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 }, + { "indexon", SEPG_DB_TABLE__INDEXON }, + { NULL, 0UL }, + } + }, + { + "db_view", SEPG_CLASS_DB_VIEW, + { + { "create", SEPG_DB_VIEW__CREATE }, + { "drop", SEPG_DB_VIEW__DROP }, + { "getattr", SEPG_DB_VIEW__GETATTR }, + { "setattr", SEPG_DB_VIEW__SETATTR }, + { "relabelfrom", SEPG_DB_VIEW__RELABELFROM }, + { "relabelto", SEPG_DB_VIEW__RELABELTO }, + { "expand", SEPG_DB_VIEW__EXPAND }, + { NULL, 0UL }, + } + }, + { + "db_sequence", SEPG_CLASS_DB_SEQUENCE, + { + { "create", SEPG_DB_SEQUENCE__CREATE }, + { "drop", SEPG_DB_SEQUENCE__DROP }, + { "getattr", SEPG_DB_SEQUENCE__GETATTR }, + { "setattr", SEPG_DB_SEQUENCE__SETATTR }, + { "relabelfrom", SEPG_DB_SEQUENCE__RELABELFROM }, + { "relabelto", SEPG_DB_SEQUENCE__RELABELTO }, + { "get_value", SEPG_DB_SEQUENCE__GET_VALUE }, + { "next_value", SEPG_DB_SEQUENCE__NEXT_VALUE }, + { "set_value", SEPG_DB_SEQUENCE__SET_VALUE }, + { NULL, 0UL }, + } + }, + { + "db_procedure", SEPG_CLASS_DB_PROCEDURE, + { + { "create", SEPG_DB_PROCEDURE__CREATE }, + { "drop", SEPG_DB_PROCEDURE__DROP }, + { "getattr", SEPG_DB_PROCEDURE__GETATTR }, + { "setattr", SEPG_DB_PROCEDURE__SETATTR }, + { "relabelfrom", SEPG_DB_PROCEDURE__RELABELFROM }, + { "relabelto", SEPG_DB_PROCEDURE__RELABELTO }, + { "execute", SEPG_DB_PROCEDURE__EXECUTE }, + { "entrypoint", SEPG_DB_PROCEDURE__ENTRYPOINT }, + { "install", SEPG_DB_PROCEDURE__INSTALL }, + { NULL, 0UL }, + } + }, + { + "db_column", SEPG_CLASS_DB_COLUMN, + { + { "create", SEPG_DB_COLUMN__CREATE }, + { "drop", SEPG_DB_COLUMN__DROP }, + { "getattr", SEPG_DB_COLUMN__GETATTR }, + { "setattr", SEPG_DB_COLUMN__SETATTR }, + { "relabelfrom", SEPG_DB_COLUMN__RELABELFROM }, + { "relabelto", SEPG_DB_COLUMN__RELABELTO }, + { "select", SEPG_DB_COLUMN__SELECT }, + { "update", SEPG_DB_COLUMN__UPDATE }, + { "insert", SEPG_DB_COLUMN__INSERT }, + { 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 }, + } + }, + { + "db_language", SEPG_CLASS_DB_LANGUAGE, + { + { "create", SEPG_DB_LANGUAGE__CREATE }, + { "drop", SEPG_DB_LANGUAGE__DROP }, + { "getattr", SEPG_DB_LANGUAGE__GETATTR }, + { "setattr", SEPG_DB_LANGUAGE__SETATTR }, + { "relabelfrom", SEPG_DB_LANGUAGE__RELABELFROM }, + { "relabelto", SEPG_DB_LANGUAGE__RELABELTO }, + { "implement", SEPG_DB_LANGUAGE__IMPLEMENTE }, + { "execute", SEPG_DB_LANGUAGE__EXECUTE }, + { NULL, 0UL }, + } + }, +}; + +/* + * GUC option: sepostgresql = [default|enforcing|permissive|disabled] + * + * SEPGSQL_MODE_DEFAULT : It follows system setting + * SEPGSQL_MODE_ENFORCING : Use enforcing mode always + * SEPGSQL_MODE_PERMISSIVE : Use permissive mode always + * SEPGSQL_MODE_INTERNAL : Internally used mode. Same as permissive mode + * except for silence in audit logs + * SEPGSQL_MODE_DISABLED : It always disables SE-PgSQL configuration + */ +int sepostgresql_mode; + +/* + * sepgsql_is_enabled + * + * If it returns true, SE-PgSQL is enabled. Otherwise, it is disabled. + */ +bool +sepgsql_is_enabled(void) +{ + static int enabled = -1; + + /* + * If sepostgresql = disabled, it always returns FALSE + * independently from the system status. + */ + if (sepostgresql_mode == SEPGSQL_MODE_DISABLED) + return false; + + /* + * SE-PgSQL needs SELinux is enabled on the operating system. + * If it is disabled, SE-PgSQL has to be also disabled, even if + * 'enforcing' or 'permissive' are specified. + */ + if (enabled < 0) + enabled = is_selinux_enabled(); + + return enabled > 0 ? true : false; +} + +/* + * sepgsql_get_enforce + * + * It returns true, if SE-PgSQL performs in enforcing mode. + * + * In enforcing mode, SE-PgSQL performs as expected. It checks permissions + * on the required action, and it prevents them if violated. + * In permissive mode, SE-PgSQL also checks permissions, but it does not + * prevent anything, even if violated. It generates audit logs for access + * violations, so we can use this mode to debug security policy itself. + */ +bool +sepgsql_get_enforce(void) +{ + if (sepostgresql_mode == SEPGSQL_MODE_DEFAULT) + { + if (security_getenforce() == 1) + return true; + } + else if (sepostgresql_mode == SEPGSQL_MODE_ENFORCING) + return true; + + return false; +} + +/* + * sepgsql_show_mode + * + * It returns the current performing mode ('selinux_support') + * in human readable form. + */ +const char * +sepgsql_show_mode(void) +{ + if (!sepgsql_is_enabled()) + return "disabled"; + + if (!sepgsql_get_enforce()) + return "permissive"; + + return "enforcing"; +} + +/* + * GUC parameter to turn on/off debuging audit generation + */ +bool sepgsql_debug_audit; + +/* + * sepgsql_audit_log + * + * It generates a security audit record. In the default, it writes out + * audit records into standard PG's logfile. It also allows to set up + * external audit log receiver, such as auditd in Linux, using the + * sepgsql_audit_hook. + * + * SELinux can control what should be audited and should not using + * "auditdeny" and "auditallow" rules in the security policy. In the + * default, all the access violations are audited, and all the access + * allowed are not audited. But we can set up the security policy, so + * we can have exceptions. So, it is necessary to follow the suggestion + * come from the security policy. (av_decision.auditallow and auditdeny) + * + * Security audit is an important feature, because it enables us to check + * what was happen if we have a security incident. In fact, ISO/IEC15408 + * defines several security functionalities for audit features. + */ +void +sepgsql_audit_log(bool denied, char *scontext, char *tcontext, + uint16 tclass, uint32 audited, const char *audit_name) +{ + StringInfoData buf; + const char *tclass_name; + const char *perm_name; + int level = LOG; + int i; + + /* + * translation of security contexts to human readable format, + * if sepgsql_mcstrans is turned on. + */ + scontext = sepgsql_mcstrans_out(scontext); + tcontext = sepgsql_mcstrans_out(tcontext); + + /* lookup name of the object class */ + tclass_name = selinux_catalog[tclass].class_name; + + /* lookup name of the permissions */ + initStringInfo(&buf); + appendStringInfo(&buf, "{"); + + for (i=0; selinux_catalog[tclass].perms[i].perm_name; i++) + { + if (audited & (1UL << i)) + { + perm_name = selinux_catalog[tclass].perms[i].perm_name; + appendStringInfo(&buf, " %s", perm_name); + } + } + appendStringInfo(&buf, " }"); + + /* + * Call external audit module, if loaded + */ + appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s", + scontext, tcontext, tclass_name); + if (audit_name) + appendStringInfo(&buf, " name=%s", audit_name); + + if (sepgsql_debug_audit) + level = client_min_messages; + + ereport(level, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("SELinux: %s %s", + (denied ? "denied" : "allowed"), buf.data))); +} + +/* + * sepgsql_compute_avd + * + * It actually asks SELinux what permissions are allowed on a pair of + * the security contexts and object class. It also returns what permissions + * should be audited on access violation or allowed. + * In most cases, subject's security context (scontext) is a client, and + * target security context (tcontext) is a database object. + * + * The access control decision shall be set on the given av_decision. + * The av_decision.allowed has a bitmask of SEPG___ + * to suggest a set of allowed actions in this object class. + */ +void +sepgsql_compute_avd(char *scontext, char *tcontext, + uint16 tclass, struct av_decision *avd) +{ + const char *tclass_name; + security_class_t tclass_ex; + struct av_decision avd_ex; + int i, deny_unknown = security_deny_unknown(); + + /* Get external code of the object class*/ + Assert(tclass < SEPG_CLASS_MAX); + + tclass_name = selinux_catalog[tclass].class_name; + tclass_ex = string_to_security_class(tclass_name); + + if (tclass_ex == 0) + { + /* + * If the current security policy does not support permissions + * corresponding to database objects, we fill up them with dummy + * data. + * If security_deny_unknown() returns positive value, undefined + * permissions should be denied. Otherwise, allowed + */ + avd->allowed = (deny_unknown > 0 ? 0 : ~0U); + avd->auditallow = 0U; + avd->auditdeny = ~0U; + avd->flags = 0; + + return; + } + + /* + * Ask SELinux what is allowed set of permissions on a pair of the + * security contexts and the given object class. + */ + if (security_compute_av_flags_raw(scontext, tcontext, + tclass_ex, 0, &avd_ex) < 0) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("SELinux could not compute av_decision: " + "scontext=%s tcontext=%s tclass=%s", + scontext, tcontext, tclass_name))); + + /* + * SELinux returns its access control decision as a set of permissions + * represented in external code which depends on run-time environment. + * So, we need to translate it to the internal representation before + * returning results for the caller. + */ + memset(avd, 0, sizeof(struct av_decision)); + + for (i=0; selinux_catalog[tclass].perms[i].perm_name; i++) + { + access_vector_t perm_code_ex; + const char *perm_name = selinux_catalog[tclass].perms[i].perm_name; + uint32 perm_code = selinux_catalog[tclass].perms[i].perm_code; + + perm_code_ex = string_to_av_perm(tclass_ex, perm_name); + if (perm_code_ex == 0) + { + /* fill up undefined permissions */ + if (!deny_unknown) + avd->allowed |= perm_code; + avd->auditdeny |= perm_code; + + continue; + } + + if (avd_ex.allowed & perm_code_ex) + avd->allowed |= perm_code; + if (avd_ex.auditallow & perm_code_ex) + avd->auditallow |= perm_code; + if (avd_ex.auditdeny & perm_code_ex) + avd->auditdeny |= perm_code; + } + + return; +} + +/* + * sepgsql_compute_perms + * + * It makes access control decision communicating with SELinux. + * If SELinux does not allow required permissions on a pair of the security + * contexts, it raises an error or returns false. + * + * scontext : The security context of subject. In most cases, it is client. + * tcontext : The security context of target database object. + * tclass : One of the object class code (SEPG_CLASS_*) declared in the + * header file. + * required : A bitmap of the required permissions (SEPG___) + * declared in the header file. + * audit_name : A human readable name of the database object for auditing. + * abort : True, if caller want to raise an error on access violation. + */ +bool +sepgsql_compute_perms(char *scontext, char *tcontext, + uint16 tclass, uint32 required, + const char *audit_name, bool abort) +{ + struct av_decision avd; + uint32 denied; + uint32 audited; + + sepgsql_compute_avd(scontext, tcontext, tclass, &avd); + + /* + * It logs a security audit record for the given request, if necessary. + * When SE-PgSQL performs 'internal' mode, it needs to keep silent. + */ + denied = required & ~avd.allowed; + if (sepgsql_debug_audit && tclass != SEPG_CLASS_DB_TUPLE) + audited = (denied ? (denied & ~0) : (required & ~0)); + else + audited = (denied ? (denied & avd.auditdeny) + : (required & avd.auditallow)); + + if (audited && sepostgresql_mode != SEPGSQL_MODE_INTERNAL) + { + sepgsql_audit_log(!!denied, scontext, tcontext, + tclass, audited, audit_name); + } + + /* + * If here is no policy violations, or SE-PgSQL performs in permissive + * mode, or the client process peforms in permissive domain, it returns + * normally with 'true'. + */ + if (!denied || + !sepgsql_get_enforce() || + (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) != 0) + return true; + + /* + * Otherwise, it raises an error or returns 'false', depending on the + * caller's indication by 'abort'. + */ + if (abort) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("SELinux: security policy violation"))); + + return false; +} + +/* + * sepgsql_compute_create + * + * It returns a default security context to be assigned on a new database + * object. SELinux compute it based on a combination of client, upper object + * which owns the new object and object class. + * + * For example, when a client (staff_u:staff_r:staff_t:s0) tries to create + * a new table within a schema (system_u:object_r:sepgsql_schema_t:s0), + * SELinux looks-up its security policy. If it has a special rule on the + * combination of these security contexts and object class (db_table), + * it returns the security context suggested by the special rule. + * Otherwise, it returns the security context of schema, as is. + * + * We expect the caller already applies sanity/validation checks on the + * given security context. + * + * scontext : The security context of subject. In most cases, it is client. + * tcontext : The security context of the parent database object.. + * tclass : One of the object class code (SEPG_CLASS_*) declared in the + * header file. + */ +char * +sepgsql_compute_create(char *scontext, char *tcontext, uint16 tclass) +{ + security_context_t ncontext; + security_class_t tclass_ex; + const char *tclass_name; + char *result; + + /* Get external code of the object class*/ + Assert(tclass < SEPG_CLASS_MAX); + + tclass_name = selinux_catalog[tclass].class_name; + tclass_ex = string_to_security_class(tclass_name); + + /* + * Ask SELinux what is the default context for the given object class + * on a pair of security contexts + */ + if (security_compute_create_raw(scontext, tcontext, + tclass_ex, &ncontext)) + ereport(ERROR, + (errcode(ERRCODE_INTERNAL_ERROR), + errmsg("SELinux could not compute a new context: " + "scontext=%s tcontext=%s tclass=%s", + scontext, tcontext, tclass_name))); + + /* + * libselinux returns malloc()'ed string, so we need to copy it + * on the palloc()'ed region. + */ + PG_TRY(); + { + result = pstrdup(ncontext); + } + PG_CATCH(); + { + freecon(ncontext); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(ncontext); + + return result; +} diff --git a/src/backend/sepgsql/tablespace.c b/src/backend/sepgsql/tablespace.c new file mode 100644 index 0000000..1d3a35e --- /dev/null +++ b/src/backend/sepgsql/tablespace.c @@ -0,0 +1,157 @@ +/* + * tablespace.c + * + * SELinux hooks related to tablespaces + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "catalog/pg_tablespace.h" +#include "catalog/pg_seclabel.h" +#include "commands/tablespace.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" +#include "utils/syscache.h" +#include "utils/lsyscache.h" + +#ifdef HAVE_SELINUX +bool +sepgsql_tablespace_common(Oid tablespaceOid, uint32 required, bool abort) +{ + HeapTuple tuple; + sepgsql_sid_t tsid; + bool retval; + const char *auname; + + tuple = SearchSysCache1(TABLESPACEOID, ObjectIdGetDatum(tablespaceOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for tablespace %u", tablespaceOid); + + tsid.relid = TableSpaceRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + auname = NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname); + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + required, + auname, + abort); + ReleaseSysCache(tuple); + + return retval; +} + +#endif + +Oid +sepgsql_tablespace_create(const char *tablespaceName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + + nsid = sepgsql_get_default_tuple_secid(TableSpaceRelationId); + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + tablespaceName, + true); + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_tablespace_alter(Oid tablespaceOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_tablespace_common(tablespaceOid, + SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +Oid +sepgsql_tablespace_relabel(Oid tablespaceOid, char *newLabel) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t tsid; + + tsid.relid = TableSpaceRelationId; + tsid.secid = seclabelTransInput(tsid.relid, newLabel); + + /* db_tuple:{update relabelfrom} */ + sepgsql_tablespace_common(tablespaceOid, + SEPG_DB_TUPLE__UPDATE | + SEPG_DB_TUPLE__RELABELFROM, true); + + /* db_procedure:{relabelto} */ + sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__RELABELTO, + get_tablespace_name(tablespaceOid), + true); + return tsid.secid; + } +#endif + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SE-PostgreSQL is not available"))); + return InvalidOid; +} + +void +sepgsql_tablespace_drop(Oid tablespaceOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_tablespace_common(tablespaceOid, + SEPG_DB_TUPLE__DELETE, true); + } +#endif +} + +void +sepgsql_tablespace_grant(Oid tablespaceOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_tablespace_common(tablespaceOid, + SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_tablespace_getattr(Oid tablespaceOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_tablespace_common(tablespaceOid, + SEPG_DB_TUPLE__SELECT, true); + } +#endif +} + +void +sepgsql_tablespace_comment(Oid tablespaceOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_tablespace_common(tablespaceOid, + SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} diff --git a/src/backend/sepgsql/tsearch.c b/src/backend/sepgsql/tsearch.c new file mode 100644 index 0000000..3d4180d --- /dev/null +++ b/src/backend/sepgsql/tsearch.c @@ -0,0 +1,524 @@ +/* + * tsearch.c + * + * SELinux hooks related to text searches + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "catalog/pg_ts_config.h" +#include "catalog/pg_ts_dict.h" +#include "catalog/pg_ts_parser.h" +#include "catalog/pg_ts_template.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" +#include "utils/syscache.h" + +#ifdef HAVE_SELINUX +bool +sepgsql_ts_config_common(Oid confOid, uint32 required, bool abort) +{ + sepgsql_sid_t tsid; + HeapTuple tuple; + const char *auname; + bool retval; + + tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(confOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for text search configuration %u", confOid); + + tsid.relid = TSConfigRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + auname = NameStr(((Form_pg_ts_config) GETSTRUCT(tuple))->cfgname); + + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + required, + auname, + abort); + ReleaseSysCache(tuple); + + return retval; +} + +static Oid +get_ts_config_namespace(Oid confOid) +{ + HeapTuple tuple; + Oid namespaceId = InvalidOid; + + tuple = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(confOid)); + if (HeapTupleIsValid(tuple)) + { + namespaceId = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgnamespace; + + ReleaseSysCache(tuple); + } + return namespaceId; +} + +bool +sepgsql_ts_dict_common(Oid dictOid, uint32 required, bool abort) +{ + sepgsql_sid_t tsid; + HeapTuple tuple; + const char *auname; + bool retval; + + tuple = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for text search dictionary %u", dictOid); + + tsid.relid = TSDictionaryRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + auname = NameStr(((Form_pg_ts_dict) GETSTRUCT(tuple))->dictname); + + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + required, + auname, + abort); + ReleaseSysCache(tuple); + + return retval; +} + +static Oid +get_ts_dict_namespace(Oid dictOid) +{ + HeapTuple tuple; + Oid namespaceId = InvalidOid; + + tuple = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictOid)); + if (HeapTupleIsValid(tuple)) + { + namespaceId = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictnamespace; + + ReleaseSysCache(tuple); + } + return namespaceId; +} + +bool +sepgsql_ts_parser_common(Oid parseOid, uint32 required, bool abort) +{ + sepgsql_sid_t tsid; + HeapTuple tuple; + const char *auname; + bool retval; + + tuple = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(parseOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for text search parser %u", parseOid); + + tsid.relid = TSParserRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + auname = NameStr(((Form_pg_ts_parser) GETSTRUCT(tuple))->prsname); + + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + required, + auname, + abort); + ReleaseSysCache(tuple); + + return retval; +} + +static Oid +get_ts_parser_namespace(Oid parseOid) +{ + HeapTuple tuple; + Oid namespaceId = InvalidOid; + + tuple = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(parseOid)); + if (HeapTupleIsValid(tuple)) + { + namespaceId = ((Form_pg_ts_parser) GETSTRUCT(tuple))->prsnamespace; + + ReleaseSysCache(tuple); + } + return namespaceId; +} + +bool +sepgsql_ts_template_common(Oid templateOid, uint32 required, bool abort) +{ + sepgsql_sid_t tsid; + HeapTuple tuple; + const char *auname; + bool retval; + + tuple = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(templateOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for text search template %u", templateOid); + + tsid.relid = TSDictionaryRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + auname = NameStr(((Form_pg_ts_dict) GETSTRUCT(tuple))->dictname); + + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + required, + auname, + abort); + ReleaseSysCache(tuple); + + return retval; +} + +static Oid +get_ts_template_namespace(Oid templateOid) +{ + HeapTuple tuple; + Oid namespaceId = InvalidOid; + + tuple = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(templateOid)); + if (HeapTupleIsValid(tuple)) + { + namespaceId = ((Form_pg_ts_template) GETSTRUCT(tuple))->tmplnamespace; + + ReleaseSysCache(tuple); + } + return namespaceId; +} + +#endif + +Oid +sepgsql_ts_config_create(const char *confName, Oid namespaceId) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid + = sepgsql_get_default_tuple_secid(TSConfigRelationId); + + /* db_schema:{add_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_tuple:{insert} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + confName, + true); + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_ts_config_alter(Oid confOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_ts_config_alter_rename(Oid confOid, const char *newName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_ts_config_namespace(confOid); + + /* db_schema:{add_name remove_name} */ + sepgsql_schema_common(namespaceId, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_tuple:{update} */ + sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_ts_config_drop(Oid confOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_ts_config_namespace(confOid); + + /* db_schema:{remove_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_tuple:{delete} */ + sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__DELETE, true); + } +#endif +} + +void +sepgsql_ts_config_comment(Oid confOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_ts_config_common(confOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +Oid +sepgsql_ts_dict_create(const char *dictName, Oid namespaceId) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid + = sepgsql_get_default_tuple_secid(TSDictionaryRelationId); + + /* db_schema:{add_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_tuple:{insert} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + dictName, + true); + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_ts_dict_alter(Oid dictOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_ts_dict_alter_rename(Oid dictOid, const char *newName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_ts_dict_namespace(dictOid); + + /* db_schema:{add_name remove_name} */ + sepgsql_schema_common(namespaceId, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_tuple:{update} */ + sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_ts_dict_drop(Oid dictOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_ts_dict_namespace(dictOid); + + /* db_schema:{remove_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_tuple:{delete} */ + sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__DELETE, true); + } +#endif +} + +void +sepgsql_ts_dict_comment(Oid dictOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_ts_dict_common(dictOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +Oid +sepgsql_ts_parser_create(const char *parseName, Oid namespaceId, + Oid startFunc, Oid tokenFunc, Oid endFunc, + Oid headlineFunc, Oid lextypeFunc) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid + = sepgsql_get_default_tuple_secid(TSParserRelationId); + + /* db_schema:{add_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_procedure:{install} */ + if (OidIsValid(startFunc)) + sepgsql_proc_common(startFunc, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(tokenFunc)) + sepgsql_proc_common(tokenFunc, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(endFunc)) + sepgsql_proc_common(endFunc, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(headlineFunc)) + sepgsql_proc_common(headlineFunc, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(lextypeFunc)) + sepgsql_proc_common(lextypeFunc, SEPG_DB_PROCEDURE__INSTALL, true); + + /* db_tuple:{insert} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + parseName, + true); + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_ts_parser_alter_rename(Oid parseOid, const char *newName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_ts_parser_namespace(parseOid); + + /* db_schema:{add_name remove_name} */ + sepgsql_schema_common(namespaceId, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_tuple:{update} */ + sepgsql_ts_parser_common(parseOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_ts_parser_drop(Oid parseOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_ts_parser_namespace(parseOid); + + /* db_schema:{remove_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_tuple:{delete} */ + sepgsql_ts_parser_common(parseOid, SEPG_DB_TUPLE__DELETE, true); + } +#endif +} + +void +sepgsql_ts_parser_comment(Oid parseOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_ts_parser_common(parseOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +Oid +sepgsql_ts_template_create(const char *templateName, Oid namespaceId, + Oid initFunc, Oid lexizeFunc) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid + = sepgsql_get_default_tuple_secid(TSTemplateRelationId); + + /* db_schema:{add_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_procedure:{install} */ + if (OidIsValid(initFunc)) + sepgsql_proc_common(initFunc, SEPG_DB_PROCEDURE__INSTALL, true); + if (OidIsValid(lexizeFunc)) + sepgsql_proc_common(lexizeFunc, SEPG_DB_PROCEDURE__INSTALL, true); + + /* db_tuple:{insert} */ + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + templateName, + true); + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_ts_template_alter_rename(Oid templateOid, const char *newName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_ts_template_namespace(templateOid); + + /* db_schema:{add_name remove_name} */ + sepgsql_schema_common(namespaceId, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_tuple:{update} */ + sepgsql_ts_template_common(templateOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_ts_template_drop(Oid templateOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_ts_template_namespace(templateOid); + + /* db_schema:{remove_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_tuple:{delete} */ + sepgsql_ts_template_common(templateOid, SEPG_DB_TUPLE__DELETE, true); + } +#endif +} + +void +sepgsql_ts_template_comment(Oid templateOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_ts_template_common(templateOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} diff --git a/src/backend/sepgsql/type.c b/src/backend/sepgsql/type.c new file mode 100644 index 0000000..8cd65ae --- /dev/null +++ b/src/backend/sepgsql/type.c @@ -0,0 +1,314 @@ +/* + * type.c + * + * SELinux hooks related to types + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#include "postgres.h" + +#include "catalog/pg_cast.h" +#include "catalog/pg_seclabel.h" +#include "catalog/pg_type.h" +#include "sepgsql/hooks.h" +#include "sepgsql/sepgsql.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + +#ifdef HAVE_SELINUX +bool +sepgsql_type_common(Oid typeOid, uint32 required, bool abort) +{ + sepgsql_sid_t tsid; + char *auname; + bool retval; + + tsid.relid = TypeRelationId; + tsid.secid = GetSysCacheSecid1(TYPEOID, + ObjectIdGetDatum(typeOid)); + + auname = format_type_be(typeOid); + + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + required, + auname, + abort); + pfree(auname); + + return retval; +} + +bool +sepgsql_cast_common(Oid srcTypeOid, Oid dstTypeOid, + uint32 required, bool abort) +{ + HeapTuple tuple; + sepgsql_sid_t tsid; + bool retval; + + tuple = SearchSysCache2(CASTSOURCETARGET, + ObjectIdGetDatum(srcTypeOid), + ObjectIdGetDatum(dstTypeOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for cast (%u,%u)", + srcTypeOid, dstTypeOid); + + tsid.relid = CastRelationId; + tsid.secid = HeapTupleGetSecid(tuple); + + retval = sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + required, + NULL, + abort); + ReleaseSysCache(tuple); + + return retval; +} + +static Oid +get_type_namespace(Oid typeOid) +{ + HeapTuple tuple; + Oid namespaceId; + + tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typeOid)); + if (!HeapTupleIsValid(tuple)) + return InvalidOid; + + namespaceId = ((Form_pg_type) GETSTRUCT(tuple))->typnamespace; + + ReleaseSysCache(tuple); + + return namespaceId; +} +#endif + +Oid +sepgsql_type_create(const char *typeName, Oid replaced, + Oid namespaceId, char typeType, + Oid inputFunc, Oid outputFunc, + Oid recvFunc, Oid sendFunc, + Oid modinFunc, Oid modoutFunc, Oid analyzeFunc) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + uint32 required; + + /* db_schema:{add_name} */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_tuple:{insert or update} */ + if (!OidIsValid(replaced)) + { + nsid = sepgsql_get_default_tuple_secid(TypeRelationId); + required = SEPG_DB_TUPLE__INSERT; + } + else + { + nsid.relid = TypeRelationId; + nsid.secid = GetSysCacheSecid1(TYPEOID, + ObjectIdGetDatum(replaced)); + required = SEPG_DB_TUPLE__UPDATE; + } + + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_TUPLE, + required, + typeName, + true); + + /* db_procedure:{install} */ + if (OidIsValid(inputFunc)) + sepgsql_proc_common(inputFunc, SEPG_DB_PROCEDURE__INSTALL, true); + + /* db_procedure:{install} */ + if (OidIsValid(outputFunc)) + sepgsql_proc_common(outputFunc, SEPG_DB_PROCEDURE__INSTALL, true); + + /* db_procedure:{install} */ + if (OidIsValid(recvFunc)) + sepgsql_proc_common(recvFunc, SEPG_DB_PROCEDURE__INSTALL, true); + + /* db_procedure:{install} */ + if (OidIsValid(sendFunc)) + sepgsql_proc_common(sendFunc, SEPG_DB_PROCEDURE__INSTALL, true); + + /* db_procedure:{install} */ + if (OidIsValid(modinFunc)) + sepgsql_proc_common(modinFunc, SEPG_DB_PROCEDURE__INSTALL, true); + + /* db_procedure:{install} */ + if (OidIsValid(modoutFunc)) + sepgsql_proc_common(modoutFunc, SEPG_DB_PROCEDURE__INSTALL, true); + + /* db_procedure:{install} */ + if (OidIsValid(analyzeFunc)) + sepgsql_proc_common(analyzeFunc, SEPG_DB_PROCEDURE__INSTALL, true); + + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_type_alter(Oid typeOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_type_alter_rename(Oid typeOid, const char *newName) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_type_namespace(typeOid); + + /* db_schema:{add_name remove_name} */ + sepgsql_schema_common(namespaceId, + SEPG_DB_SCHEMA__ADD_NAME | + SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_tuple:{update} */ + sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +void +sepgsql_type_alter_schema(Oid typeOid, Oid newSchema) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + Oid namespaceId = get_type_namespace(typeOid); + + /* db_schema:{ remove_name } */ + sepgsql_schema_common(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true); + + /* db_schema:{ add_name } */ + sepgsql_schema_common(newSchema, SEPG_DB_SCHEMA__ADD_NAME, true); + + /* db_tuple:{update} */ + sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +Oid +sepgsql_type_relabel(Oid typeOid, char *newLabel) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t tsid; + + tsid.relid = TypeRelationId; + tsid.secid = seclabelTransInput(tsid.relid, newLabel); + + /* db_tuple:{update relabelfrom} */ + sepgsql_type_common(typeOid, + SEPG_DB_TUPLE__UPDATE | + SEPG_DB_TUPLE__RELABELFROM, true); + + /* db_procedure:{relabelto} */ + sepgsql_client_perms(tsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__RELABELTO, + format_type_be(typeOid), + true); + return tsid.secid; + } +#endif + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("SE-PostgreSQL is not available"))); + return InvalidOid; +} + +void +sepgsql_type_drop(Oid typeOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{delete} */ + sepgsql_type_common(typeOid, SEPG_DB_TUPLE__DELETE, true); + } +#endif +} + +void +sepgsql_type_comment(Oid typeOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + /* db_tuple:{update} */ + sepgsql_type_common(typeOid, SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} + +Oid +sepgsql_cast_create(Oid sourceTypeOid, Oid targetTypeOid, + char castMethod, Oid castFuncOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_sid_t nsid; + + /* db_tuple:{insert} */ + nsid = sepgsql_get_default_tuple_secid(CastRelationId); + sepgsql_client_perms(nsid, + SEPG_CLASS_DB_TUPLE, + SEPG_DB_TUPLE__INSERT, + NULL, + true); + /* db_procedure:{install} */ + if (OidIsValid(castFuncOid)) + sepgsql_proc_common(castFuncOid, + SEPG_DB_PROCEDURE__INSTALL, true); + + return nsid.secid; + } +#endif + return InvalidOid; +} + +void +sepgsql_cast_drop(Oid sourceTypeOid, Oid targetTypeOid, bool cascade) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_cast_common(sourceTypeOid, targetTypeOid, + SEPG_DB_TUPLE__DELETE, true); + } +#endif +} + +void +sepgsql_cast_comment(Oid sourceTypeOid, Oid targetTypeOid) +{ +#ifdef HAVE_SELINUX + if (sepgsql_is_enabled()) + { + sepgsql_cast_common(sourceTypeOid, targetTypeOid, + SEPG_DB_TUPLE__UPDATE, true); + } +#endif +} diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c index ca48cdd..bb0688f 100644 --- a/src/backend/storage/large_object/inv_api.c +++ b/src/backend/storage/large_object/inv_api.c @@ -197,14 +197,14 @@ getbytealen(bytea *data) * in use. */ Oid -inv_create(Oid lobjId) +inv_create(Oid lobjId, Oid securityId) { Oid lobjId_new; /* * Create a new largeobject with empty data pages */ - lobjId_new = LargeObjectCreate(lobjId); + lobjId_new = LargeObjectCreate(lobjId, securityId); /* * dependency on the owner of largeobject diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c index 575fa86..eb2b5e8 100644 --- a/src/backend/tcop/fastpath.c +++ b/src/backend/tcop/fastpath.c @@ -26,6 +26,7 @@ #include "libpq/pqformat.h" #include "mb/pg_wchar.h" #include "miscadmin.h" +#include "sepgsql/hooks.h" #include "tcop/fastpath.h" #include "tcop/tcopprot.h" #include "utils/acl.h" @@ -347,6 +348,10 @@ HandleFunctionRequest(StringInfo msgBuf) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(fid)); + /* SELinux checks */ + sepgsql_schema_search(fip->namespace, true); + sepgsql_proc_execute(fid); + /* * Prepare function call info block and insert arguments. */ diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index dd00b2d..ed76bca 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -699,6 +699,9 @@ pg_rewrite_query(Query *query) { /* don't rewrite utilities, just dump 'em into result list */ querytree_list = list_make1(query); + + /* SE-PostgreSQL may rewrite the query */ + sepgsql_proxy_queries(querytree_list); } else { diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index 8ad4915..54961d8 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -575,7 +575,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot) Assert(pstmt->hasReturning); portal->tupDesc = ExecCleanTypeFromTL(pstmt->planTree->targetlist, - false); + false, false); } /* diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 8960246..e8f8ea3 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -165,6 +165,7 @@ check_xact_readonly(Node *parsetree) case T_AlterRoleSetStmt: case T_AlterObjectSchemaStmt: case T_AlterOwnerStmt: + case T_AlterSecLabelStmt: case T_AlterSeqStmt: case T_AlterTableStmt: case T_RenameStmt: @@ -696,6 +697,10 @@ standard_ProcessUtility(Node *parsetree, ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree); break; + case T_AlterSecLabelStmt: + ExecAlterSecLabelStmt((AlterSecLabelStmt *) parsetree); + break; + case T_AlterTableStmt: { List *stmts; @@ -1760,6 +1765,46 @@ CreateCommandTag(Node *parsetree) } break; + case T_AlterSecLabelStmt: + switch (((AlterSecLabelStmt *) parsetree)->objectType) + { + case OBJECT_DATABASE: + tag = "ALTER DATABASE"; + break; + case OBJECT_SCHEMA: + tag = "ALTER SCHEMA"; + break; + case OBJECT_TABLE: + case OBJECT_COLUMN: + tag = "ALTER TABLE"; + break; + case OBJECT_SEQUENCE: + tag = "ALTER SEQUENCE"; + break; + case OBJECT_VIEW: + tag = "ALTER VIEW"; + break; + case OBJECT_FUNCTION: + tag = "ALTER FUNCTION"; + break; + case OBJECT_AGGREGATE: + tag = "ALTER AGGREGATE"; + break; + case OBJECT_LARGEOBJECT: + tag = "ALTER LARGE OBJECT"; + break; + case OBJECT_TYPE: + tag = "ALTER TYPE"; + break; + case OBJECT_DOMAIN: + tag = "ALTER DOMAIN"; + break; + default: + tag = "???"; + break; + } + break; + case T_AlterTableStmt: switch (((AlterTableStmt *) parsetree)->relkind) { @@ -2352,6 +2397,10 @@ GetCommandLogLevel(Node *parsetree) lev = LOGSTMT_DDL; break; + case T_AlterSecLabelStmt: + lev = LOGSTMT_DDL; + break; + case T_AlterTableStmt: lev = LOGSTMT_DDL; break; diff --git a/src/backend/tsearch/wparser.c b/src/backend/tsearch/wparser.c index 0fed35c..ca9e9d1 100644 --- a/src/backend/tsearch/wparser.c +++ b/src/backend/tsearch/wparser.c @@ -59,7 +59,7 @@ tt_setup_firstcall(FuncCallContext *funcctx, Oid prsid) (Datum) 0)); funcctx->user_fctx = (void *) st; - tupdesc = CreateTemplateTupleDesc(3, false); + tupdesc = CreateTemplateTupleDesc(3, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "tokid", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "alias", @@ -205,7 +205,7 @@ prs_setup_firstcall(FuncCallContext *funcctx, Oid prsid, text *txt) st->cur = 0; funcctx->user_fctx = (void *) st; - tupdesc = CreateTemplateTupleDesc(2, false); + tupdesc = CreateTemplateTupleDesc(2, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "tokid", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "token", diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index faad11e..53e6944 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -1695,7 +1695,7 @@ aclexplode(PG_FUNCTION_ARGS) * build tupdesc for result tuples (matches out parameters in pg_proc * entry) */ - tupdesc = CreateTemplateTupleDesc(4, false); + tupdesc = CreateTemplateTupleDesc(4, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "grantor", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "grantee", diff --git a/src/backend/utils/adt/datetime.c b/src/backend/utils/adt/datetime.c index ed81ced..f3d4b99 100644 --- a/src/backend/utils/adt/datetime.c +++ b/src/backend/utils/adt/datetime.c @@ -4193,7 +4193,7 @@ pg_timezone_abbrevs(PG_FUNCTION_ARGS) * build tupdesc for result tuples. This must match this function's * pg_proc entry! */ - tupdesc = CreateTemplateTupleDesc(3, false); + tupdesc = CreateTemplateTupleDesc(3, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "abbrev", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "utc_offset", @@ -4286,7 +4286,7 @@ pg_timezone_names(PG_FUNCTION_ARGS) * build tupdesc for result tuples. This must match this function's * pg_proc entry! */ - tupdesc = CreateTemplateTupleDesc(4, false); + tupdesc = CreateTemplateTupleDesc(4, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "abbrev", diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c index a4e0252..36d07b7 100644 --- a/src/backend/utils/adt/dbsize.c +++ b/src/backend/utils/adt/dbsize.c @@ -21,6 +21,7 @@ #include "commands/dbcommands.h" #include "commands/tablespace.h" #include "miscadmin.h" +#include "sepgsql/hooks.h" #include "storage/fd.h" #include "utils/acl.h" #include "utils/builtins.h" @@ -90,6 +91,9 @@ calculate_database_size(Oid dbOid) aclcheck_error(aclresult, ACL_KIND_DATABASE, get_database_name(dbOid)); + /* SELinux checks */ + sepgsql_database_getattr(dbOid); + /* Shared storage in pg_global is not counted */ /* Include pg_default storage */ @@ -178,6 +182,8 @@ calculate_tablespace_size(Oid tblspcOid) aclcheck_error(aclresult, ACL_KIND_TABLESPACE, get_tablespace_name(tblspcOid)); } + /* SELinux checks */ + sepgsql_tablespace_getattr(tblspcOid); if (tblspcOid == DEFAULTTABLESPACE_OID) snprintf(tblspcPath, MAXPGPATH, "base"); diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c index e074b79..0b58f1c 100644 --- a/src/backend/utils/adt/genfile.c +++ b/src/backend/utils/adt/genfile.c @@ -173,7 +173,7 @@ pg_stat_file(PG_FUNCTION_ARGS) * This record type had better match the output parameters declared for me * in pg_proc.h. */ - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(6, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "size", INT8OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c index 07e6aab..e5c8182 100644 --- a/src/backend/utils/adt/lockfuncs.c +++ b/src/backend/utils/adt/lockfuncs.c @@ -85,7 +85,7 @@ pg_lock_status(PG_FUNCTION_ARGS) /* build tupdesc for result tuples */ /* this had better match pg_locks view in system_views.sql */ - tupdesc = CreateTemplateTupleDesc(14, false); + tupdesc = CreateTemplateTupleDesc(14, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "locktype", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database", diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c index 11342b2..0ddf907 100644 --- a/src/backend/utils/adt/misc.c +++ b/src/backend/utils/adt/misc.c @@ -322,7 +322,7 @@ pg_get_keywords(PG_FUNCTION_ARGS) funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - tupdesc = CreateTemplateTupleDesc(3, false); + tupdesc = CreateTemplateTupleDesc(3, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "word", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "catcode", diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index 8379407..d44655b 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -419,7 +419,7 @@ pg_stat_get_activity(PG_FUNCTION_ARGS) oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); - tupdesc = CreateTemplateTupleDesc(11, false); + tupdesc = CreateTemplateTupleDesc(11, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid", OIDOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "procpid", INT4OID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid", OIDOID, -1, 0); diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 9db070d..9df61c6 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -30,6 +30,7 @@ #include "postgres.h" +#include "access/sysattr.h" #include "access/xact.h" #include "catalog/pg_constraint.h" #include "catalog/pg_operator.h" @@ -39,6 +40,7 @@ #include "parser/parse_coerce.h" #include "parser/parse_relation.h" #include "miscadmin.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" @@ -2624,6 +2626,8 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) char fkrelname[MAX_QUOTED_REL_NAME_LEN]; char pkattname[MAX_QUOTED_NAME_LEN + 3]; char fkattname[MAX_QUOTED_NAME_LEN + 3]; + Bitmapset *pkColumns = NULL; + Bitmapset *fkColumns = NULL; const char *sep; int i; int old_work_mem; @@ -2645,6 +2649,18 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) ri_FetchConstraintInfo(&riinfo, trigger, fk_rel, false); + for (i = 0; i < riinfo.nkeys; i++) + { + fkColumns = bms_add_member(fkColumns, riinfo.fk_attnums[i] + - FirstLowInvalidHeapAttributeNumber); + pkColumns = bms_add_member(pkColumns, riinfo.pk_attnums[i] + - FirstLowInvalidHeapAttributeNumber); + } + + if (!sepgsql_relation_perms(RelationGetRelid(pk_rel), + ACL_SELECT, pkColumns, NULL, false)) + return false; + /*---------- * The query string built is: * SELECT fk.keycols FROM ONLY relname fk @@ -3204,6 +3220,7 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, Relation query_rel; Oid save_userid; int save_sec_context; + int save_rowlv_mode; /* * The query is always run against the FK table except when this is an @@ -3220,6 +3237,12 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, GetUserIdAndSecContext(&save_userid, &save_sec_context); SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner, save_sec_context | SECURITY_LOCAL_USERID_CHANGE); + /* + * When we scan FK relation, switch row-level access control mode + * into abort-on-violation mode, to keep referencial integrity. + */ + if (query_rel == fk_rel) + save_rowlv_mode = sepgsql_rowlv_set_mode(SEPGSQL_ROWLV_ABORT); /* Create the plan */ qplan = SPI_prepare(querystr, nargs, argtypes); @@ -3229,6 +3252,8 @@ ri_PlanCheck(const char *querystr, int nargs, Oid *argtypes, /* Restore UID and security context */ SetUserIdAndSecContext(save_userid, save_sec_context); + if (query_rel == fk_rel) + sepgsql_rowlv_set_mode(save_rowlv_mode); /* Save the plan if requested */ if (cache_plan) diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c index c837e67..1bf4eb8 100644 --- a/src/backend/utils/adt/tid.c +++ b/src/backend/utils/adt/tid.c @@ -27,6 +27,7 @@ #include "libpq/pqformat.h" #include "miscadmin.h" #include "parser/parsetree.h" +#include "sepgsql/hooks.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/rel.h" @@ -347,6 +348,8 @@ currtid_byreloid(PG_FUNCTION_ARGS) if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(rel)); + /* SELinux checks */ + sepgsql_relation_getattr(RelationGetRelid(rel)); if (rel->rd_rel->relkind == RELKIND_VIEW) return currtid_for_view(rel, tid); @@ -377,6 +380,8 @@ currtid_byrelname(PG_FUNCTION_ARGS) if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, RelationGetRelationName(rel)); + /* SELinux checks */ + sepgsql_relation_getattr(RelationGetRelid(rel)); if (rel->rd_rel->relkind == RELKIND_VIEW) return currtid_for_view(rel, tid); diff --git a/src/backend/utils/adt/trigfuncs.c b/src/backend/utils/adt/trigfuncs.c index 70246fb..b996537 100644 --- a/src/backend/utils/adt/trigfuncs.c +++ b/src/backend/utils/adt/trigfuncs.c @@ -76,6 +76,10 @@ suppress_redundant_updates_trigger(PG_FUNCTION_ARGS) !OidIsValid(HeapTupleHeaderGetOid(newheader))) HeapTupleHeaderSetOid(newheader, HeapTupleHeaderGetOid(oldheader)); + if (trigdata->tg_relation->rd_rel->relhassecids && + !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 --git a/src/backend/utils/adt/tsvector_op.c b/src/backend/utils/adt/tsvector_op.c index 78f08f4..4a44810 100644 --- a/src/backend/utils/adt/tsvector_op.c +++ b/src/backend/utils/adt/tsvector_op.c @@ -975,7 +975,7 @@ ts_setup_firstcall(FunctionCallInfo fcinfo, FuncCallContext *funcctx, } Assert(stat->stackpos <= stat->maxdepth); - tupdesc = CreateTemplateTupleDesc(3, false); + tupdesc = CreateTemplateTupleDesc(3, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "word", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "ndoc", diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c index 88d8d8b..281acb0 100644 --- a/src/backend/utils/cache/plancache.c +++ b/src/backend/utils/cache/plancache.c @@ -924,12 +924,12 @@ PlanCacheComputeResultDesc(List *stmt_list) if (IsA(node, Query)) { query = (Query *) node; - return ExecCleanTypeFromTL(query->targetList, false); + return ExecCleanTypeFromTL(query->targetList, false, false); } if (IsA(node, PlannedStmt)) { pstmt = (PlannedStmt *) node; - return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false); + return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false, false); } /* other cases shouldn't happen, but return NULL */ break; @@ -940,13 +940,13 @@ PlanCacheComputeResultDesc(List *stmt_list) { query = (Query *) node; Assert(query->returningList); - return ExecCleanTypeFromTL(query->returningList, false); + return ExecCleanTypeFromTL(query->returningList, false, false); } if (IsA(node, PlannedStmt)) { pstmt = (PlannedStmt *) node; Assert(pstmt->hasReturning); - return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false); + return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false, false); } /* other cases shouldn't happen, but return NULL */ break; diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 073d25a..249b4c0 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -213,7 +213,7 @@ static void write_relcache_init_file(bool shared); static void write_item(const void *data, Size len, FILE *fp); static void formrdesc(const char *relationName, Oid relationReltype, - bool isshared, bool hasoids, + bool isshared, bool hasoids, bool hassecids, int natts, const FormData_pg_attribute *attrs); static HeapTuple ScanPgRelation(Oid targetRelId, bool indexOK); @@ -352,7 +352,8 @@ AllocateRelationDesc(Form_pg_class relp) /* and allocate attribute tuple form storage */ relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts, - relationForm->relhasoids); + relationForm->relhasoids, + relationForm->relhassecids); /* which we mark as a reference-counted tupdesc */ relation->rd_att->tdrefcount = 1; @@ -434,6 +435,7 @@ RelationBuildTupleDesc(Relation relation) relation->rd_att->tdtypeid = relation->rd_rel->reltype; relation->rd_att->tdtypmod = -1; /* unnecessary, but... */ relation->rd_att->tdhasoid = relation->rd_rel->relhasoids; + relation->rd_att->tdhassecid = relation->rd_rel->relhassecids; constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext, sizeof(TupleConstr)); @@ -1392,7 +1394,7 @@ LookupOpclassInfo(Oid operatorClassOid, */ static void formrdesc(const char *relationName, Oid relationReltype, - bool isshared, bool hasoids, + bool isshared, bool hasoids, bool hassecids, int natts, const FormData_pg_attribute *attrs) { Relation relation; @@ -1455,6 +1457,7 @@ formrdesc(const char *relationName, Oid relationReltype, relation->rd_rel->reltuples = 1; relation->rd_rel->relkind = RELKIND_RELATION; relation->rd_rel->relhasoids = hasoids; + relation->rd_rel->relhassecids = hassecids; relation->rd_rel->relnatts = (int16) natts; /* @@ -1464,7 +1467,7 @@ formrdesc(const char *relationName, Oid relationReltype, * because it will never be replaced. The input values must be correctly * defined by macros in src/include/catalog/ headers. */ - relation->rd_att = CreateTemplateTupleDesc(natts, hasoids); + relation->rd_att = CreateTemplateTupleDesc(natts, hasoids, hassecids); relation->rd_att->tdrefcount = 1; /* mark as refcounted */ relation->rd_att->tdtypeid = relationReltype; @@ -2527,6 +2530,7 @@ RelationBuildLocalRelation(const char *relname, rel->rd_rel->relkind = RELKIND_UNCATALOGED; rel->rd_rel->relhasoids = rel->rd_att->tdhasoid; + rel->rd_rel->relhassecids = rel->rd_att->tdhassecid; rel->rd_rel->relnatts = natts; rel->rd_rel->reltype = InvalidOid; /* needed when bootstrapping: */ @@ -2767,7 +2771,7 @@ RelationCacheInitializePhase2(void) if (!load_relcache_init_file(true)) { formrdesc("pg_database", DatabaseRelation_Rowtype_Id, true, - true, Natts_pg_database, Desc_pg_database); + true, true, Natts_pg_database, Desc_pg_database); #define NUM_CRITICAL_SHARED_RELS 1 /* fix if you change list above */ } @@ -2818,13 +2822,13 @@ RelationCacheInitializePhase3(void) needNewCacheFile = true; formrdesc("pg_class", RelationRelation_Rowtype_Id, false, - true, Natts_pg_class, Desc_pg_class); + true, true, Natts_pg_class, Desc_pg_class); formrdesc("pg_attribute", AttributeRelation_Rowtype_Id, false, - false, Natts_pg_attribute, Desc_pg_attribute); + false, true, Natts_pg_attribute, Desc_pg_attribute); formrdesc("pg_proc", ProcedureRelation_Rowtype_Id, false, - true, Natts_pg_proc, Desc_pg_proc); + true, true, Natts_pg_proc, Desc_pg_proc); formrdesc("pg_type", TypeRelation_Rowtype_Id, false, - true, Natts_pg_type, Desc_pg_type); + true, true, Natts_pg_type, Desc_pg_type); #define NUM_CRITICAL_LOCAL_RELS 4 /* fix if you change list above */ } @@ -2969,6 +2973,7 @@ RelationCacheInitializePhase3(void) Assert(relation->rd_att->tdtypeid == relp->reltype); Assert(relation->rd_att->tdtypmod == -1); Assert(relation->rd_att->tdhasoid == relp->relhasoids); + Assert(relation->rd_att->tdhassecid == relp->relhassecids); ReleaseSysCache(htup); @@ -3079,7 +3084,7 @@ load_critical_index(Oid indexoid, Oid heapoid) */ static TupleDesc BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs, - bool hasoids) + bool hasoids, bool hassecids) { TupleDesc result; MemoryContext oldcxt; @@ -3087,7 +3092,7 @@ BuildHardcodedDescriptor(int natts, const FormData_pg_attribute *attrs, oldcxt = MemoryContextSwitchTo(CacheMemoryContext); - result = CreateTemplateTupleDesc(natts, hasoids); + result = CreateTemplateTupleDesc(natts, hasoids, hassecids); result->tdtypeid = RECORDOID; /* not right, but we don't care */ result->tdtypmod = -1; @@ -3117,7 +3122,7 @@ GetPgClassDescriptor(void) if (pgclassdesc == NULL) pgclassdesc = BuildHardcodedDescriptor(Natts_pg_class, Desc_pg_class, - true); + true, true); return pgclassdesc; } @@ -3131,7 +3136,7 @@ GetPgIndexDescriptor(void) if (pgindexdesc == NULL) pgindexdesc = BuildHardcodedDescriptor(Natts_pg_index, Desc_pg_index, - false); + false, false); return pgindexdesc; } @@ -3947,7 +3952,8 @@ load_relcache_init_file(bool shared) /* initialize attribute tuple forms */ rel->rd_att = CreateTemplateTupleDesc(relform->relnatts, - relform->relhasoids); + relform->relhasoids, + relform->relhassecids); rel->rd_att->tdrefcount = 1; /* mark as refcounted */ rel->rd_att->tdtypeid = relform->reltype; diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index 61b06ac..b08654b 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -895,6 +895,30 @@ GetSysCacheOid(int cacheId, return result; } +/* + * GetSysCacheSecid + * + * A convenience routine that does SearchSysCache and returns the + * security-id of the found tuple, or InvalidOid if no tuple could + * be found. No lock is retained on the syscache entry. + */ +Oid +GetSysCacheSecid(int cacheId, + Datum key1, + Datum key2, + Datum key3, + Datum key4) +{ + HeapTuple tuple; + Oid result; + + tuple = SearchSysCache(cacheId, key1, key2, key3, key4); + if (!HeapTupleIsValid(tuple)) + return InvalidOid; + result = HeapTupleGetSecid(tuple); + ReleaseSysCache(tuple); + return result; +} /* * SearchSysCacheAttName diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index c3c0440..fb5b762 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -24,6 +24,7 @@ #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "pgstat.h" +#include "sepgsql/hooks.h" #include "utils/builtins.h" #include "utils/fmgrtab.h" #include "utils/guc.h" @@ -190,6 +191,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, finfo->fn_extra = NULL; finfo->fn_mcxt = mcxt; finfo->fn_expr = NULL; /* caller may set this later */ + finfo->fn_seclabel = NULL; if ((fbp = fmgr_isbuiltin(functionId)) != NULL) { @@ -228,15 +230,22 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, * ability to set the track_functions GUC as a local GUC parameter of an * interesting function and have the right things happen. */ - if (!ignore_security && - (procedureStruct->prosecdef || - !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig))) + if (!ignore_security) { - finfo->fn_addr = fmgr_security_definer; - finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ - finfo->fn_oid = functionId; - ReleaseSysCache(procedureTuple); - return; + char *seclabel + = sepgsql_proc_domtrans(procedureTuple, mcxt); + + if (procedureStruct->prosecdef || + !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig) || + seclabel != NULL) + { + finfo->fn_addr = fmgr_security_definer; + finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */ + finfo->fn_oid = functionId; + finfo->fn_seclabel = seclabel; + ReleaseSysCache(procedureTuple); + return; + } } switch (procedureStruct->prolang) @@ -877,6 +886,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS) FmgrInfo *save_flinfo; Oid save_userid; int save_sec_context; + char *save_seclabel; volatile int save_nestlevel; PgStat_FunctionCallUsage fcusage; @@ -939,6 +949,10 @@ fmgr_security_definer(PG_FUNCTION_ARGS) PGC_S_SESSION, GUC_ACTION_SAVE); } + if (fcinfo->flinfo->fn_seclabel) + save_seclabel = sepgsql_set_client_label(fcinfo->flinfo->fn_seclabel); + else + save_seclabel = NULL; /* * We don't need to restore GUC or userid settings on error, because the @@ -978,6 +992,8 @@ fmgr_security_definer(PG_FUNCTION_ARGS) AtEOXact_GUC(true, save_nestlevel); if (OidIsValid(fcache->userid)) SetUserIdAndSecContext(save_userid, save_sec_context); + if (fcinfo->flinfo->fn_seclabel) + sepgsql_set_client_label(save_seclabel); return result; } diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index 382008c..e6eba47 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -1105,7 +1105,7 @@ build_function_result_tupdesc_d(Datum proallargtypes, if (numoutargs < 2) return NULL; - desc = CreateTemplateTupleDesc(numoutargs, false); + desc = CreateTemplateTupleDesc(numoutargs, false, false); for (i = 0; i < numoutargs; i++) { TupleDescInitEntry(desc, i + 1, @@ -1220,7 +1220,7 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases) /* OK, get the column alias */ attname = strVal(linitial(colaliases)); - tupdesc = CreateTemplateTupleDesc(1, false); + tupdesc = CreateTemplateTupleDesc(1, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, attname, diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index ed99b91..2bc5127 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -37,6 +37,7 @@ #include "postmaster/autovacuum.h" #include "postmaster/postmaster.h" #include "replication/walsender.h" +#include "sepgsql/hooks.h" #include "storage/bufmgr.h" #include "storage/fd.h" #include "storage/ipc.h" @@ -304,6 +305,9 @@ CheckMyDatabase(const char *name, bool am_superuser) errmsg("permission denied for database \"%s\"", name), errdetail("User does not have CONNECT privilege."))); + /* SELinux checks */ + sepgsql_database_connect(MyDatabaseId); + /* * Check connection limit for this database. * @@ -735,6 +739,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, /* set up ACL framework (so CheckMyDatabase can check permissions) */ initialize_acl(); + /* set up enhanced security feature */ + sepgsql_initialize(); + /* Process pg_db_role_setting options */ process_settings(MyDatabaseId, GetSessionUserId()); diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index f198c9c..8a8d45d 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -31,6 +31,7 @@ #include "access/twophase.h" #include "access/xact.h" #include "catalog/namespace.h" +#include "catalog/pg_seclabel.h" #include "commands/async.h" #include "commands/prepare.h" #include "commands/vacuum.h" @@ -56,6 +57,7 @@ #include "postmaster/syslogger.h" #include "postmaster/walwriter.h" #include "replication/walsender.h" +#include "sepgsql/sepgsql.h" #include "storage/bufmgr.h" #include "storage/fd.h" #include "tcop/tcopprot.h" @@ -337,6 +339,18 @@ static const struct config_enum_entry constraint_exclusion_options[] = { {NULL, 0, false} }; +#ifdef HAVE_SELINUX +static const struct config_enum_entry sepostgresql_options[] = { + {"default", SEPGSQL_MODE_DEFAULT, false}, + {"enforcing", SEPGSQL_MODE_ENFORCING, false}, + {"permissive", SEPGSQL_MODE_PERMISSIVE, false}, + {"disabled", SEPGSQL_MODE_DISABLED, false}, + {"on", SEPGSQL_MODE_DEFAULT, true}, + {"off", SEPGSQL_MODE_DISABLED, true}, + {NULL, 0, false}, +}; +#endif + /* * Options for enum values stored in other modules */ @@ -365,6 +379,7 @@ bool log_btree_build_stats = false; bool check_function_bodies = true; bool default_with_oids = false; +bool default_with_secids = true; bool SQL_inheritance = true; bool Password_encryption = true; @@ -1085,6 +1100,14 @@ static struct config_bool ConfigureNamesBool[] = false, NULL, NULL }, { + {"default_with_secids", PGC_USERSET, CONN_AUTH_SETTINGS, + gettext_noop("Create new tables with security-ids by default."), + NULL + }, + &default_with_secids, + true, NULL, NULL + }, + { {"logging_collector", PGC_POSTMASTER, LOGGING_WHERE, gettext_noop("Start a subprocess to capture stderr output and/or csvlogs into log files."), NULL @@ -1254,6 +1277,36 @@ static struct config_bool ConfigureNamesBool[] = false, NULL, NULL }, + { + {"ignore_security_label_input", PGC_USERSET, CONN_AUTH_SECURITY, + gettext_noop("Disables to assign used provided security label"), + NULL, + GUC_NO_SHOW_ALL | GUC_NOT_IN_SAMPLE + }, + &ignore_security_label_input, + false, NULL, NULL + }, +#ifdef HAVE_SELINUX + { + {"sepostgresql_mcstrans", PGC_USERSET, CONN_AUTH_SECURITY, + gettext_noop("Enables to show security context in human-readable form"), + NULL, + GUC_NOT_IN_SAMPLE + }, + &sepgsql_mcstrans, + true, NULL, NULL + }, + { + {"sepostgresql_debug_audit", PGC_USERSET, CONN_AUTH_SECURITY, + gettext_noop("Enables to show audit logs for debugging"), + NULL, + GUC_NOT_IN_SAMPLE + }, + &sepgsql_debug_audit, + false, NULL, NULL + }, +#endif + /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL @@ -2795,7 +2848,16 @@ static struct config_enum ConfigureNamesEnum[] = &xmloption, XMLOPTION_CONTENT, xmloption_options, NULL, NULL }, - +#ifdef HAVE_SELINUX + { + {"sepostgresql", PGC_POSTMASTER, CONN_AUTH_SECURITY, + gettext_noop("Enables to set SE-PostgreSQL's mode"), + NULL + }, + &sepostgresql_mode, + SEPGSQL_MODE_DEFAULT, sepostgresql_options, NULL, sepgsql_show_mode + }, +#endif /* End-of-list marker */ { @@ -6070,7 +6132,7 @@ GetPGVariableResultDesc(const char *name) if (guc_name_compare(name, "all") == 0) { /* need a tuple descriptor representing three TEXT columns */ - tupdesc = CreateTemplateTupleDesc(3, false); + tupdesc = CreateTemplateTupleDesc(3, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", @@ -6086,7 +6148,7 @@ GetPGVariableResultDesc(const char *name) (void) GetConfigOptionByName(name, &varname); /* need a tuple descriptor representing a single TEXT column */ - tupdesc = CreateTemplateTupleDesc(1, false); + tupdesc = CreateTemplateTupleDesc(1, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname, TEXTOID, -1, 0); } @@ -6109,7 +6171,7 @@ ShowGUCConfigOption(const char *name, DestReceiver *dest) value = GetConfigOptionByName(name, &varname); /* need a tuple descriptor representing a single TEXT column */ - tupdesc = CreateTemplateTupleDesc(1, false); + tupdesc = CreateTemplateTupleDesc(1, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, varname, TEXTOID, -1, 0); @@ -6136,7 +6198,7 @@ ShowAllGUCConfig(DestReceiver *dest) bool isnull[3] = {false, false, false}; /* need a tuple descriptor representing three TEXT columns */ - tupdesc = CreateTemplateTupleDesc(3, false); + tupdesc = CreateTemplateTupleDesc(3, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", @@ -6531,7 +6593,7 @@ show_all_settings(PG_FUNCTION_ARGS) * need a tuple descriptor representing NUM_PG_SETTINGS_ATTS columns * of the appropriate types */ - tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS, false); + tupdesc = CreateTemplateTupleDesc(NUM_PG_SETTINGS_ATTS, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting", diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 02f1df0..f8a27c8 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -76,6 +76,8 @@ # - Security and Authentication - +#sepostgresql = disabled # default|enforcing|permissive|disabled + #authentication_timeout = 1min # 1s-600s #ssl = off # (change requires restart) #ssl_ciphers = 'ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH' # allowed SSL ciphers diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c index ac62d45..f05a094 100644 --- a/src/backend/utils/mmgr/portalmem.c +++ b/src/backend/utils/mmgr/portalmem.c @@ -894,7 +894,7 @@ pg_cursor(PG_FUNCTION_ARGS) * build tupdesc for result tuples. This must match the definition of the * pg_cursors view in system_views.sql */ - tupdesc = CreateTemplateTupleDesc(6, false); + tupdesc = CreateTemplateTupleDesc(6, false, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", TEXTOID, -1, 0); TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement", diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index f40ad87..497bdf0 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -87,6 +87,7 @@ static bool debug = false; static bool noclean = false; static bool show_setting = false; static char *xlog_dir = ""; +static bool enable_selinux = false; /* internal vars */ @@ -1163,6 +1164,13 @@ setup_config(void) "#default_text_search_config = 'pg_catalog.simple'", repltok); + if (enable_selinux) + { + strcpy(repltok, "sepostgresql = default"); + conflines = replace_token(conflines, + "#sepostgresql = disabled", repltok); + } + snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data); writefile(path, conflines); @@ -2394,6 +2402,7 @@ usage(const char *progname) 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\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")); @@ -2436,6 +2445,7 @@ main(int argc, char *argv[]) {"show", no_argument, NULL, 's'}, {"noclean", no_argument, NULL, 'n'}, {"xlogdir", required_argument, NULL, 'X'}, + {"enable-selinux", no_argument, NULL, 10}, {NULL, 0, NULL, 0} }; @@ -2545,6 +2555,9 @@ main(int argc, char *argv[]) case 9: pwfilename = xstrdup(optarg); break; + case 10: + enable_selinux = true; + break; case 's': show_setting = true; break; diff --git a/src/bin/pg_dump/pg_backup.h b/src/bin/pg_dump/pg_backup.h index 14fe652..40b7b40 100644 --- a/src/bin/pg_dump/pg_backup.h +++ b/src/bin/pg_dump/pg_backup.h @@ -103,6 +103,7 @@ typedef struct _restoreOptions * restore */ int use_setsessauth;/* Use SET SESSION AUTHORIZATION commands * instead of OWNER TO */ + int noSecLabel; /* Don't try to restore security labels */ char *superuser; /* Username to use as superuser */ char *use_role; /* Issue SET ROLE to this */ int dataOnly; @@ -167,7 +168,7 @@ extern void ArchiveEntry(Archive *AHX, CatalogId catalogId, DumpId dumpId, const char *tag, const char *namespace, const char *tablespace, - const char *owner, bool withOids, + const char *owner, bool withOids, bool withSecids, const char *desc, teSection section, const char *defn, const char *dropStmt, const char *copyStmt, diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c index d83f4dc..cc0cbae 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -540,6 +540,8 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, } else { + bool need_reset = false; + _disableTriggersIfNecessary(AH, te, ropt); /* Select owner and schema as necessary */ @@ -575,6 +577,16 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, "ONLY " : ""), fmtId(te->tag)); } + /* + * If data section has security_label, but pg_restore works + * with --no-security-label, we set server flag to ignore + * the security label input. + */ + if (ropt->noSecLabel && te->withSecids) + { + ahprintf(AH, "SET ignore_security_label_input = on;\n\n"); + need_reset = true; + } /* * If we have a copy statement, use it. As of V1.3, these @@ -595,6 +607,9 @@ restore_toc_entry(ArchiveHandle *AH, TocEntry *te, AH->writingCopyData = false; + if (need_reset) + ahprintf(AH, "RESET ignore_security_label_input;\n\n"); + /* close out the transaction started above */ if (is_parallel && te->created) CommitTransaction(AH); @@ -712,7 +727,7 @@ ArchiveEntry(Archive *AHX, const char *tag, const char *namespace, const char *tablespace, - const char *owner, bool withOids, + const char *owner, bool withOids, bool withSecids, const char *desc, teSection section, const char *defn, const char *dropStmt, const char *copyStmt, @@ -744,6 +759,7 @@ ArchiveEntry(Archive *AHX, newToc->tablespace = tablespace ? strdup(tablespace) : NULL; newToc->owner = strdup(owner); newToc->withOids = withOids; + newToc->withSecids = withSecids; newToc->desc = strdup(desc); newToc->defn = strdup(defn); newToc->dropStmt = strdup(dropStmt); @@ -2027,6 +2043,7 @@ WriteToc(ArchiveHandle *AH) WriteStr(AH, te->tablespace); WriteStr(AH, te->owner); WriteStr(AH, te->withOids ? "true" : "false"); + WriteStr(AH, te->withSecids ? "true" : "false"); /* Dump list of dependencies */ for (i = 0; i < te->nDeps; i++) @@ -2138,6 +2155,16 @@ ReadToc(ArchiveHandle *AH) else te->withOids = true; + if (AH->version >= K_VERS_1_13) + { + if (strcmp(ReadStr(AH), "true") == 0) + te->withSecids = true; + else + te->withSecids = false; + } + else + te->withSecids = false; + /* Read TOC entry dependencies */ if (AH->version >= K_VERS_1_5) { @@ -2256,6 +2283,9 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) if ((!include_acls || ropt->aclsSkip) && _tocEntryIsACL(te)) return 0; + if (ropt->noSecLabel && strcmp(te->desc, "LABEL") == 0) + return 0; + /* Ignore DATABASE entry unless we should create it */ if (!ropt->create && strcmp(te->desc, "DATABASE") == 0) return 0; @@ -2322,6 +2352,8 @@ _tocEntryRequired(TocEntry *te, RestoreOptions *ropt, bool include_acls) (strcmp(te->desc, "ACL") == 0 && strncmp(te->tag, "LARGE OBJECT ", 13) == 0) || (strcmp(te->desc, "COMMENT") == 0 && + strncmp(te->tag, "LARGE OBJECT ", 13) == 0) || + (strcmp(te->desc, "LABEL") == 0 && strncmp(te->tag, "LARGE OBJECT ", 13) == 0)) res = res & REQ_DATA; else @@ -2473,6 +2505,36 @@ _doSetWithOids(ArchiveHandle *AH, const bool withOids) destroyPQExpBuffer(cmd); } +/* + * Issue a SET default_with_secids command. Caller is responsible + * for updating state if appropriate. + */ +static void +_doSetWithSecids(ArchiveHandle *AH, const bool withSecids) +{ + PQExpBuffer cmd = createPQExpBuffer(); + + appendPQExpBuffer(cmd, "SET default_with_secids = %s;", withSecids ? + "true" : "false"); + + if (RestoringToDB(AH)) + { + PGresult *res; + + res = PQexec(AH->connection, cmd->data); + + if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) + warn_or_die_horribly(AH, modulename, + "could not set default_with_secids: %s", + PQerrorMessage(AH->connection)); + + PQclear(res); + } + else + ahprintf(AH, "%s\n\n", cmd->data); + + destroyPQExpBuffer(cmd); +} /* * Issue the commands to connect to the specified database. @@ -2571,6 +2633,18 @@ _setWithOids(ArchiveHandle *AH, TocEntry *te) } } +/* + * Set the proper default_with_secids value for the table. + */ +static void +_setWithSecids(ArchiveHandle *AH, TocEntry *te) +{ + if (AH->currWithSecids != te->withSecids) + { + _doSetWithSecids(AH, te->withSecids); + AH->currWithSecids = te->withSecids; + } +} /* * Issue the commands to select the specified schema as the current schema @@ -2808,9 +2882,12 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat _selectOutputSchema(AH, te->namespace); _selectTablespace(AH, te->tablespace); - /* Set up OID mode too */ + /* Set up OID/SECID mode too */ if (strcmp(te->desc, "TABLE") == 0) + { _setWithOids(AH, te); + _setWithSecids(AH, te); + } /* Emit header comment for item */ if (!AH->noTocComments) diff --git a/src/bin/pg_dump/pg_backup_archiver.h b/src/bin/pg_dump/pg_backup_archiver.h index 2e944c1..67f62ac 100644 --- a/src/bin/pg_dump/pg_backup_archiver.h +++ b/src/bin/pg_dump/pg_backup_archiver.h @@ -63,7 +63,7 @@ typedef z_stream *z_streamp; /* Current archive version number (the format we can output) */ #define K_VERS_MAJOR 1 -#define K_VERS_MINOR 12 +#define K_VERS_MINOR 13 #define K_VERS_REV 0 /* Data block types */ @@ -89,9 +89,10 @@ typedef z_stream *z_streamp; * indicator */ #define K_VERS_1_12 (( (1 * 256 + 12) * 256 + 0) * 256 + 0) /* add separate BLOB * entries */ - +#define K_VERS_1_13 (( (1 * 256 + 13) * 256 + 0) * 256 + 0) /* add security label + * support */ /* Newest format we can read */ -#define K_VERS_MAX (( (1 * 256 + 12) * 256 + 255) * 256 + 0) +#define K_VERS_MAX (( (1 * 256 + 13) * 256 + 255) * 256 + 0) /* Flags to indicate disposition of offsets stored in files */ @@ -278,6 +279,7 @@ typedef struct _archiveHandle char *currSchema; /* current schema, or NULL */ char *currTablespace; /* current tablespace, or NULL */ bool currWithOids; /* current default_with_oids setting */ + bool currWithSecids; /* current default_with_secids setting */ void *lo_buf; size_t lo_buf_used; @@ -305,6 +307,7 @@ typedef struct _tocEntry * means use database default */ char *owner; bool withOids; /* Used only by "TABLE" tags */ + bool withSecids; /* Used only by "TABLE" tags */ char *desc; char *defn; char *dropStmt; diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 2431d71..e7ef296 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -125,7 +125,7 @@ static int binary_upgrade = 0; static int disable_dollar_quoting = 0; static int dump_inserts = 0; static int column_inserts = 0; - +static int security_label = 0; static void help(const char *progname); static void expand_schema_name_patterns(SimpleStringList *patterns, @@ -183,6 +183,11 @@ static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, const char *tag, const char *nspname, const char *owner, const char *acls); +static void dumpSecLabel(Archive *fout, DumpId objDumpId, + const char *target, + const char *namespace, + const char *seclabel); + static void getDependencies(void); static void getDomainConstraints(TypeInfo *tyinfo); static void getTableData(TableInfo *tblinfo, int numTables, bool oids); @@ -215,7 +220,7 @@ static bool binary_upgrade_set_type_oids_by_rel_oid( static void binary_upgrade_set_relfilenodes(PQExpBuffer upgrade_buffer, Oid pg_class_oid, bool is_index); static const char *getAttrName(int attrnum, TableInfo *tblInfo); -static const char *fmtCopyColumnList(const TableInfo *ti); +static const char *fmtCopyColumnList(const TableInfo *ti, bool secids); static void do_sql_command(PGconn *conn, const char *query); static void check_sql_result(PGresult *res, PGconn *conn, const char *query, ExecStatusType expected); @@ -299,6 +304,7 @@ main(int argc, char **argv) {"no-tablespaces", no_argument, &outputNoTablespaces, 1}, {"role", required_argument, NULL, 3}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, + {"security-label", no_argument, &security_label, 1}, {NULL, 0, NULL, 0} }; @@ -447,6 +453,8 @@ main(int argc, char **argv) outputNoTablespaces = 1; else if (strcmp(optarg, "use-set-session-authorization") == 0) use_setsessauth = 1; + else if (strcmp(optarg, "security-label") == 0) + security_label = 1; else { fprintf(stderr, @@ -515,6 +523,10 @@ main(int argc, char **argv) exit(1); } + /* Force column insertion mode, when --security-label mode is given. */ + if (security_label && dump_inserts) + column_inserts = 1; + /* open the output file */ if (pg_strcasecmp(format, "a") == 0 || pg_strcasecmp(format, "append") == 0) { @@ -833,6 +845,7 @@ help(const char *progname) 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 schema/data with security label\n")); printf(_("\nConnection options:\n")); printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); @@ -1127,7 +1140,7 @@ dumpTableData_copy(Archive *fout, void *dcontext) * cases involving ADD COLUMN and inheritance.) */ if (g_fout->remoteVersion >= 70300) - column_list = fmtCopyColumnList(tbinfo); + column_list = fmtCopyColumnList(tbinfo, tdinfo->secids); else column_list = ""; /* can't select columns in COPY */ @@ -1251,14 +1264,16 @@ dumpTableData_insert(Archive *fout, void *dcontext) if (fout->remoteVersion >= 70100) { appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " - "SELECT * FROM ONLY %s", + "SELECT %s* FROM ONLY %s", + (tdinfo->secids ? "security_label," : ""), fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, classname)); } else { appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " - "SELECT * FROM %s", + "SELECT %s* FROM %s", + (tdinfo->secids ? "security_label," : ""), fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, classname)); } @@ -1398,7 +1413,7 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo) appendPQExpBuffer(copyBuf, "COPY %s ", fmtId(tbinfo->dobj.name)); appendPQExpBuffer(copyBuf, "%s %sFROM stdin;\n", - fmtCopyColumnList(tbinfo), + fmtCopyColumnList(tbinfo, tdinfo->secids), (tdinfo->oids && tbinfo->hasoids) ? "WITH OIDS " : ""); copyStmt = copyBuf->data; } @@ -1412,7 +1427,7 @@ dumpTableData(Archive *fout, TableDataInfo *tdinfo) ArchiveEntry(fout, tdinfo->dobj.catId, tdinfo->dobj.dumpId, tbinfo->dobj.name, tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, - false, "TABLE DATA", SECTION_DATA, + false, tdinfo->secids, "TABLE DATA", SECTION_DATA, "", "", copyStmt, tdinfo->dobj.dependencies, tdinfo->dobj.nDeps, dumpFn, tdinfo); @@ -1457,6 +1472,7 @@ getTableData(TableInfo *tblinfo, int numTables, bool oids) tdinfo->dobj.namespace = tblinfo[i].dobj.namespace; tdinfo->tdtable = &(tblinfo[i]); tdinfo->oids = oids; + tdinfo->secids = (security_label && tblinfo[i].hassecids) ? true : false; addObjectDependency(&tdinfo->dobj, tblinfo[i].dobj.dumpId); tblinfo[i].dataObj = tdinfo; @@ -1780,6 +1796,7 @@ dumpDatabase(Archive *AH) NULL, /* Tablespace */ dba, /* Owner */ false, /* with oids */ + false, /* with secids */ "DATABASE", /* Desc */ SECTION_PRE_DATA, /* Section */ creaQry->data, /* Create */ @@ -1825,7 +1842,7 @@ dumpDatabase(Archive *AH) LargeObjectRelationId); ArchiveEntry(AH, nilCatalogId, createDumpId(), "pg_largeobject", NULL, NULL, "", - false, "pg_largeobject", SECTION_PRE_DATA, + false, false, "pg_largeobject", SECTION_PRE_DATA, loOutQry->data, "", NULL, NULL, 0, NULL, NULL); @@ -1857,7 +1874,7 @@ dumpDatabase(Archive *AH) appendPQExpBuffer(dbQry, ";\n"); ArchiveEntry(AH, dbCatId, createDumpId(), datname, NULL, NULL, - dba, false, "COMMENT", SECTION_NONE, + dba, false, false, "COMMENT", SECTION_NONE, dbQry->data, "", NULL, &dbDumpId, 1, NULL, NULL); } @@ -1896,7 +1913,7 @@ dumpEncoding(Archive *AH) ArchiveEntry(AH, nilCatalogId, createDumpId(), "ENCODING", NULL, NULL, "", - false, "ENCODING", SECTION_PRE_DATA, + false, false, "ENCODING", SECTION_PRE_DATA, qry->data, "", NULL, NULL, 0, NULL, NULL); @@ -1923,7 +1940,7 @@ dumpStdStrings(Archive *AH) ArchiveEntry(AH, nilCatalogId, createDumpId(), "STDSTRINGS", NULL, NULL, "", - false, "STDSTRINGS", SECTION_PRE_DATA, + false, false, "STDSTRINGS", SECTION_PRE_DATA, qry->data, "", NULL, NULL, 0, NULL, NULL); @@ -1956,16 +1973,17 @@ getBlobs(Archive *AH) /* Fetch BLOB OIDs, and owner/ACL data if >= 9.0 */ if (AH->remoteVersion >= 90000) appendPQExpBuffer(blobQry, - "SELECT oid, (%s lomowner) AS rolname, lomacl" + "SELECT oid, (%s lomowner) AS rolname, lomacl, %s" " FROM pg_largeobject_metadata", - username_subquery); + username_subquery, + security_label ? "security_label" : "NULL"); else if (AH->remoteVersion >= 70100) appendPQExpBuffer(blobQry, - "SELECT DISTINCT loid, NULL::oid, NULL::oid" + "SELECT DISTINCT loid, NULL::oid, NULL::oid, NULL::text" " FROM pg_largeobject"); else appendPQExpBuffer(blobQry, - "SELECT oid, NULL::oid, NULL::oid" + "SELECT oid, NULL::oid, NULL::oid, NULL::text" " FROM pg_class WHERE relkind = 'l'"); res = PQexec(g_conn, blobQry->data); @@ -1995,6 +2013,11 @@ getBlobs(Archive *AH) binfo[i].blobacl = strdup(PQgetvalue(res, i, 2)); else binfo[i].blobacl = NULL; + + if (!PQgetisnull(res, i, 3)) + binfo[i].seclabel = strdup(PQgetvalue(res, i, 3)); + else + binfo[i].seclabel = NULL; } /* @@ -2034,7 +2057,7 @@ dumpBlob(Archive *AH, BlobInfo *binfo) ArchiveEntry(AH, binfo->dobj.catId, binfo->dobj.dumpId, binfo->dobj.name, NULL, NULL, - binfo->rolname, false, + binfo->rolname, false, false, "BLOB", SECTION_PRE_DATA, cquery->data, dquery->data, NULL, binfo->dobj.dependencies, binfo->dobj.nDeps, @@ -2049,6 +2072,10 @@ dumpBlob(Archive *AH, BlobInfo *binfo) NULL, binfo->rolname, binfo->dobj.catId, 0, binfo->dobj.dumpId); + /* Dump security label if any */ + dumpSecLabel(AH, binfo->dobj.dumpId, + cquery->data, NULL, binfo->seclabel); + /* Dump ACL if any */ if (binfo->blobacl) dumpACL(AH, binfo->dobj.catId, binfo->dobj.dumpId, "LARGE OBJECT", @@ -2356,6 +2383,7 @@ getNamespaces(int *numNamespaces) int i_nspname; int i_rolname; int i_nspacl; + int i_seclabel; /* * Before 7.3, there are no real namespaces; create two dummy entries, one @@ -2372,6 +2400,7 @@ getNamespaces(int *numNamespaces) nsinfo[0].dobj.name = strdup("public"); nsinfo[0].rolname = strdup(""); nsinfo[0].nspacl = strdup(""); + nsinfo[0].seclabel = strdup(""); selectDumpableNamespace(&nsinfo[0]); @@ -2382,6 +2411,7 @@ getNamespaces(int *numNamespaces) nsinfo[1].dobj.name = strdup("pg_catalog"); nsinfo[1].rolname = strdup(""); nsinfo[1].nspacl = strdup(""); + nsinfo[1].seclabel = strdup(""); selectDumpableNamespace(&nsinfo[1]); @@ -2402,8 +2432,8 @@ getNamespaces(int *numNamespaces) */ appendPQExpBuffer(query, "SELECT tableoid, oid, nspname, " "(%s nspowner) AS rolname, " - "nspacl FROM pg_namespace", - username_subquery); + "nspacl, %s AS seclabel FROM pg_namespace", + username_subquery, security_label ? "security_label" : "NULL"); res = PQexec(g_conn, query->data); check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); @@ -2417,6 +2447,7 @@ getNamespaces(int *numNamespaces) i_nspname = PQfnumber(res, "nspname"); i_rolname = PQfnumber(res, "rolname"); i_nspacl = PQfnumber(res, "nspacl"); + i_seclabel = PQfnumber(res, "seclabel"); for (i = 0; i < ntups; i++) { @@ -2427,6 +2458,7 @@ getNamespaces(int *numNamespaces) nsinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_nspname)); nsinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname)); nsinfo[i].nspacl = strdup(PQgetvalue(res, i, i_nspacl)); + nsinfo[i].seclabel = strdup(PQgetvalue(res, i, i_seclabel)); /* Decide whether to dump this namespace */ selectDumpableNamespace(&nsinfo[i]); @@ -2515,6 +2547,7 @@ getTypes(int *numTypes) int i_typtype; int i_typisdefined; int i_isarray; + int i_seclabel; /* * we include even the built-in types because those may be used as array @@ -2547,8 +2580,10 @@ getTypes(int *numTypes) "typtype, typisdefined, " "typname[0] = '_' AND typelem != 0 AND " "(SELECT typarray FROM pg_type te WHERE oid = pg_type.typelem) = oid AS isarray " + ",%s AS seclabel " "FROM pg_type", - username_subquery); + username_subquery, + security_label ? "security_label" : "NULL"); } else if (g_fout->remoteVersion >= 70300) { @@ -2561,6 +2596,7 @@ getTypes(int *numTypes) "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " "typtype, typisdefined, " "typname[0] = '_' AND typelem != 0 AS isarray " + ",NULL AS seclabel " "FROM pg_type", username_subquery); } @@ -2575,6 +2611,7 @@ getTypes(int *numTypes) "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " "typtype, typisdefined, " "typname[0] = '_' AND typelem != 0 AS isarray " + ",NULL AS seclabel " "FROM pg_type", username_subquery); } @@ -2591,6 +2628,7 @@ getTypes(int *numTypes) "ELSE (SELECT relkind FROM pg_class WHERE oid = typrelid) END AS typrelkind, " "typtype, typisdefined, " "typname[0] = '_' AND typelem != 0 AS isarray " + ",NULL AS seclabel " "FROM pg_type", username_subquery); } @@ -2615,6 +2653,7 @@ getTypes(int *numTypes) i_typtype = PQfnumber(res, "typtype"); i_typisdefined = PQfnumber(res, "typisdefined"); i_isarray = PQfnumber(res, "isarray"); + i_seclabel = PQfnumber(res, "seclabel"); for (i = 0; i < ntups; i++) { @@ -2642,6 +2681,8 @@ getTypes(int *numTypes) else tyinfo[i].isArray = false; + tyinfo[i].seclabel = strdup(PQgetvalue(res, i, i_seclabel)); + /* Decide whether we want to dump it */ selectDumpableType(&tyinfo[i]); @@ -3407,6 +3448,7 @@ getTables(int *numTables) int i_relhasindex; int i_relhasrules; int i_relhasoids; + int i_relhassecids; int i_relfrozenxid; int i_owning_tab; int i_owning_col; @@ -3414,6 +3456,7 @@ getTables(int *numTables) int i_reloptions; int i_toastreloptions; int i_reloftype; + int i_seclabel; /* Make sure we are in proper schema */ selectSourceSchema("pg_catalog"); @@ -3450,6 +3493,7 @@ getTables(int *numTables) "(%s c.relowner) AS rolname, " "c.relchecks, c.relhastriggers, " "c.relhasindex, c.relhasrules, c.relhasoids, " + "c.relhassecids, " "c.relfrozenxid, " "CASE WHEN c.reloftype <> 0 THEN c.reloftype::pg_catalog.regtype ELSE NULL END AS reloftype, " "d.refobjid AS owning_tab, " @@ -3457,6 +3501,7 @@ getTables(int *numTables) "(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 seclabel " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -3467,6 +3512,7 @@ getTables(int *numTables) "WHERE c.relkind in ('%c', '%c', '%c', '%c') " "ORDER BY c.oid", username_subquery, + security_label ? "c.security_label" : "NULL", RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); @@ -3483,6 +3529,7 @@ getTables(int *numTables) "(%s c.relowner) AS rolname, " "c.relchecks, c.relhastriggers, " "c.relhasindex, c.relhasrules, c.relhasoids, " + "false AS relhasecids, " "c.relfrozenxid, " "NULL AS reloftype, " "d.refobjid AS owning_tab, " @@ -3490,6 +3537,7 @@ getTables(int *numTables) "(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 seclabel " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -3500,6 +3548,7 @@ getTables(int *numTables) "WHERE c.relkind in ('%c', '%c', '%c', '%c') " "ORDER BY c.oid", username_subquery, + security_label ? "security_label" : "NULL", RELKIND_SEQUENCE, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); @@ -3516,6 +3565,7 @@ getTables(int *numTables) "(%s relowner) AS rolname, " "relchecks, (reltriggers <> 0) AS relhastriggers, " "relhasindex, relhasrules, relhasoids, " + "false AS relhassecids, " "relfrozenxid, " "NULL AS reloftype, " "d.refobjid AS owning_tab, " @@ -3523,6 +3573,7 @@ getTables(int *numTables) "(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 seclabel " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -3548,6 +3599,7 @@ getTables(int *numTables) "(%s relowner) AS rolname, " "relchecks, (reltriggers <> 0) AS relhastriggers, " "relhasindex, relhasrules, relhasoids, " + "false AS relhassecids, " "0 AS relfrozenxid, " "NULL AS reloftype, " "d.refobjid AS owning_tab, " @@ -3555,6 +3607,7 @@ getTables(int *numTables) "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " "NULL AS reloptions, " "NULL AS toast_reloptions " + ",NULL AS seclabel " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -3580,6 +3633,7 @@ getTables(int *numTables) "(%s relowner) AS rolname, " "relchecks, (reltriggers <> 0) AS relhastriggers, " "relhasindex, relhasrules, relhasoids, " + "false AS relhassecids, " "0 AS relfrozenxid, " "NULL AS reloftype, " "d.refobjid AS owning_tab, " @@ -3587,6 +3641,7 @@ getTables(int *numTables) "NULL AS reltablespace, " "NULL AS reloptions, " "NULL AS toast_reloptions " + ",NULL AS seclabel " "FROM pg_class c " "LEFT JOIN pg_depend d ON " "(c.relkind = '%c' AND " @@ -3608,6 +3663,7 @@ getTables(int *numTables) "(%s relowner) AS rolname, " "relchecks, (reltriggers <> 0) AS relhastriggers, " "relhasindex, relhasrules, relhasoids, " + "false AS relhassecids, " "0 AS relfrozenxid, " "NULL AS reloftype, " "NULL::oid AS owning_tab, " @@ -3615,6 +3671,7 @@ getTables(int *numTables) "NULL AS reltablespace, " "NULL AS reloptions, " "NULL AS toast_reloptions " + ",NULL AS seclabel " "FROM pg_class " "WHERE relkind IN ('%c', '%c', '%c') " "ORDER BY oid", @@ -3631,6 +3688,7 @@ getTables(int *numTables) "relchecks, (reltriggers <> 0) AS relhastriggers, " "relhasindex, relhasrules, " "'t'::bool AS relhasoids, " + "'f'::bool AS relhassecids, " "0 AS relfrozenxid, " "NULL AS reloftype, " "NULL::oid AS owning_tab, " @@ -3638,6 +3696,7 @@ getTables(int *numTables) "NULL AS reltablespace, " "NULL AS reloptions, " "NULL AS toast_reloptions " + ",NULL AS seclabel " "FROM pg_class " "WHERE relkind IN ('%c', '%c', '%c') " "ORDER BY oid", @@ -3671,6 +3730,7 @@ getTables(int *numTables) "NULL AS reltablespace, " "NULL AS reloptions, " "NULL AS toast_reloptions " + ",NULL AS seclabel " "FROM pg_class c " "WHERE relkind IN ('%c', '%c') " "ORDER BY oid", @@ -3709,6 +3769,7 @@ getTables(int *numTables) i_relhasindex = PQfnumber(res, "relhasindex"); i_relhasrules = PQfnumber(res, "relhasrules"); i_relhasoids = PQfnumber(res, "relhasoids"); + i_relhassecids = PQfnumber(res, "relhassecids"); i_relfrozenxid = PQfnumber(res, "relfrozenxid"); i_owning_tab = PQfnumber(res, "owning_tab"); i_owning_col = PQfnumber(res, "owning_col"); @@ -3716,6 +3777,7 @@ getTables(int *numTables) i_reloptions = PQfnumber(res, "reloptions"); i_toastreloptions = PQfnumber(res, "toast_reloptions"); i_reloftype = PQfnumber(res, "reloftype"); + i_seclabel = PQfnumber(res, "seclabel"); if (lockWaitTimeout && g_fout->remoteVersion >= 70300) { @@ -3748,6 +3810,7 @@ getTables(int *numTables) tblinfo[i].hasrules = (strcmp(PQgetvalue(res, i, i_relhasrules), "t") == 0); tblinfo[i].hastriggers = (strcmp(PQgetvalue(res, i, i_relhastriggers), "t") == 0); tblinfo[i].hasoids = (strcmp(PQgetvalue(res, i, i_relhasoids), "t") == 0); + tblinfo[i].hassecids = (strcmp(PQgetvalue(res, i, i_relhassecids), "t")==0); tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid)); if (PQgetisnull(res, i, i_reloftype)) tblinfo[i].reloftype = NULL; @@ -3767,6 +3830,7 @@ getTables(int *numTables) 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].rellabel = strdup(PQgetvalue(res, i, i_seclabel)); /* other fields were zeroed above */ @@ -5082,6 +5146,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) int i_attalign; int i_attislocal; int i_attoptions; + int i_seclabel; PGresult *res; int ntups; bool hasdefaults; @@ -5128,11 +5193,13 @@ getTableAttrs(TableInfo *tblinfo, int numTables) "a.attlen, a.attalign, a.attislocal, " "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " "array_to_string(attoptions, ', ') AS attoptions " + ",%s AS seclabel " "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_label ? "a.security_label" : "NULL", tbinfo->dobj.catId.oid); } else if (g_fout->remoteVersion >= 70300) @@ -5144,6 +5211,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) "a.attlen, a.attalign, a.attislocal, " "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " "'' AS attoptions " + ",NULL AS seclabel " "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 " @@ -5165,6 +5233,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) "a.attalign, false AS attislocal, " "format_type(t.oid,a.atttypmod) AS atttypname, " "'' AS attoptions " + ",NULL AS seclabel " "FROM pg_attribute a LEFT JOIN pg_type t " "ON a.atttypid = t.oid " "WHERE a.attrelid = '%u'::oid " @@ -5183,6 +5252,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) "false AS attislocal, " "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname, " "'' AS attoptions " + ",NULL AS seclabel " "FROM pg_attribute a " "WHERE attrelid = '%u'::oid " "AND attnum > 0::int2 " @@ -5209,6 +5279,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) i_attalign = PQfnumber(res, "attalign"); i_attislocal = PQfnumber(res, "attislocal"); i_attoptions = PQfnumber(res, "attoptions"); + i_seclabel = PQfnumber(res, "seclabel"); tbinfo->numatts = ntups; tbinfo->attnames = (char **) malloc(ntups * sizeof(char *)); @@ -5227,6 +5298,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool)); tbinfo->inhAttrDef = (bool *) malloc(ntups * sizeof(bool)); tbinfo->inhNotNull = (bool *) malloc(ntups * sizeof(bool)); + tbinfo->attlabels = (char **) malloc(ntups * sizeof(char *)); hasdefaults = false; for (j = 0; j < ntups; j++) @@ -5256,6 +5328,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) tbinfo->inhAttrs[j] = false; tbinfo->inhAttrDef[j] = false; tbinfo->inhNotNull[j] = false; + tbinfo->attlabels[j] = strdup(PQgetvalue(res, j, i_seclabel)); } PQclear(res); @@ -6146,7 +6219,7 @@ dumpComment(Archive *fout, const char *target, */ ArchiveEntry(fout, nilCatalogId, createDumpId(), target, namespace, NULL, owner, - false, "COMMENT", SECTION_NONE, + false, false, "COMMENT", SECTION_NONE, query->data, "", NULL, &(dumpId), 1, NULL, NULL); @@ -6207,7 +6280,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, target->data, tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, - false, "COMMENT", SECTION_NONE, + false, false, "COMMENT", SECTION_NONE, query->data, "", NULL, &(tbinfo->dobj.dumpId), 1, NULL, NULL); @@ -6229,7 +6302,7 @@ dumpTableComment(Archive *fout, TableInfo *tbinfo, target->data, tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, - false, "COMMENT", SECTION_NONE, + false, false, "COMMENT", SECTION_NONE, query->data, "", NULL, &(tbinfo->dobj.dumpId), 1, NULL, NULL); @@ -6509,7 +6582,7 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj) case DO_BLOB_DATA: ArchiveEntry(fout, dobj->catId, dobj->dumpId, dobj->name, NULL, NULL, "", - false, "BLOBS", SECTION_DATA, + false, false, "BLOBS", SECTION_DATA, "", "", NULL, dobj->dependencies, dobj->nDeps, dumpBlobs, NULL); @@ -6549,7 +6622,7 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) nspinfo->dobj.name, NULL, NULL, nspinfo->rolname, - false, "SCHEMA", SECTION_PRE_DATA, + false, false, "SCHEMA", SECTION_PRE_DATA, q->data, delq->data, NULL, nspinfo->dobj.dependencies, nspinfo->dobj.nDeps, NULL, NULL); @@ -6561,6 +6634,9 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) NULL, nspinfo->rolname, nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId); + dumpSecLabel(fout, nspinfo->dobj.dumpId, + q->data, NULL, nspinfo->seclabel); + dumpACL(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, "SCHEMA", qnspname, NULL, nspinfo->dobj.name, NULL, nspinfo->rolname, nspinfo->nspacl); @@ -6678,7 +6754,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, NULL, - tyinfo->rolname, false, + tyinfo->rolname, false, false, "TYPE", SECTION_PRE_DATA, q->data, delq->data, NULL, tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, @@ -6692,6 +6768,12 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + /* Dump type security label */ + dumpSecLabel(fout, tyinfo->dobj.dumpId, + q->data, + tyinfo->dobj.namespace->dobj.name, + tyinfo->seclabel); + PQclear(res); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); @@ -7054,7 +7136,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, NULL, - tyinfo->rolname, false, + tyinfo->rolname, false, false, "TYPE", SECTION_PRE_DATA, q->data, delq->data, NULL, tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, @@ -7068,6 +7150,12 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + /* Dump type security label */ + dumpSecLabel(fout, tyinfo->dobj.dumpId, + q->data, + tyinfo->dobj.namespace->dobj.name, + tyinfo->seclabel); + PQclear(res); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); @@ -7178,7 +7266,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, NULL, - tyinfo->rolname, false, + tyinfo->rolname, false, false, "DOMAIN", SECTION_PRE_DATA, q->data, delq->data, NULL, tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, @@ -7192,6 +7280,12 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + /* Dump domain security label */ + dumpSecLabel(fout, tyinfo->dobj.dumpId, + q->data, + tyinfo->dobj.namespace->dobj.name, + tyinfo->seclabel); + destroyPQExpBuffer(q); destroyPQExpBuffer(delq); destroyPQExpBuffer(query); @@ -7283,7 +7377,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, NULL, - tyinfo->rolname, false, + tyinfo->rolname, false, false, "TYPE", SECTION_PRE_DATA, q->data, delq->data, NULL, tyinfo->dobj.dependencies, tyinfo->dobj.nDeps, @@ -7298,6 +7392,12 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); + /* Dump type security label */ + dumpSecLabel(fout, tyinfo->dobj.dumpId, + q->data, + tyinfo->dobj.namespace->dobj.name, + tyinfo->seclabel); + PQclear(res); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); @@ -7402,7 +7502,7 @@ dumpCompositeTypeColComments(Archive *fout, TypeInfo *tyinfo) target->data, tyinfo->dobj.namespace->dobj.name, NULL, tyinfo->rolname, - false, "COMMENT", SECTION_NONE, + false, false, "COMMENT", SECTION_NONE, query->data, "", NULL, &(tyinfo->dobj.dumpId), 1, NULL, NULL); @@ -7454,7 +7554,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo) stinfo->dobj.name, stinfo->dobj.namespace->dobj.name, NULL, - stinfo->baseType->rolname, false, + stinfo->baseType->rolname, false, false, "SHELL TYPE", SECTION_PRE_DATA, q->data, "", NULL, stinfo->dobj.dependencies, stinfo->dobj.nDeps, @@ -7609,7 +7709,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId, plang->dobj.name, lanschema, NULL, plang->lanowner, - false, "PROCEDURAL LANGUAGE", SECTION_PRE_DATA, + false, false, "PROCEDURAL LANGUAGE", SECTION_PRE_DATA, defqry->data, delqry->data, NULL, plang->dobj.dependencies, plang->dobj.nDeps, NULL, NULL); @@ -7795,6 +7895,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) char **argmodes = NULL; char **argnames = NULL; char **configitems = NULL; + char *proseclabel; int nconfigitems = 0; int i; @@ -7825,8 +7926,10 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "proiswindow, provolatile, proisstrict, prosecdef, " "proconfig, procost, prorows, " "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " + ",%s AS seclabel " "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", + security_label ? "security_label" : "NULL", finfo->dobj.catId.oid); } else if (g_fout->remoteVersion >= 80300) @@ -7838,6 +7941,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "provolatile, proisstrict, prosecdef, " "proconfig, procost, prorows, " "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " + ",NULL AS seclabel " "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); @@ -7851,6 +7955,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "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 seclabel " "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); @@ -7866,6 +7971,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "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 seclabel " "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); @@ -7881,6 +7987,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "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 seclabel " "FROM pg_catalog.pg_proc " "WHERE oid = '%u'::pg_catalog.oid", finfo->dobj.catId.oid); @@ -7898,6 +8005,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "false AS prosecdef, " "null AS proconfig, 0 AS procost, 0 AS prorows, " "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " + ",NULL AS seclabel " "FROM pg_proc " "WHERE oid = '%u'::oid", finfo->dobj.catId.oid); @@ -7915,6 +8023,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) "false AS prosecdef, " "NULL AS proconfig, 0 AS procost, 0 AS prorows, " "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " + ",NULL AS seclabel " "FROM pg_proc " "WHERE oid = '%u'::oid", finfo->dobj.catId.oid); @@ -7959,6 +8068,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) procost = PQgetvalue(res, 0, PQfnumber(res, "procost")); prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows")); lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname")); + proseclabel = PQgetvalue(res, 0, PQfnumber(res, "seclabel")); /* * See backend/commands/functioncmds.c for details of how the 'AS' clause @@ -8169,7 +8279,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) funcsig_tag, finfo->dobj.namespace->dobj.name, NULL, - finfo->rolname, false, + finfo->rolname, false, false, "FUNCTION", SECTION_PRE_DATA, q->data, delqry->data, NULL, finfo->dobj.dependencies, finfo->dobj.nDeps, @@ -8182,6 +8292,12 @@ dumpFunc(Archive *fout, FuncInfo *finfo) finfo->dobj.namespace->dobj.name, finfo->rolname, finfo->dobj.catId, 0, finfo->dobj.dumpId); + /* Dump Function security label */ + dumpSecLabel(fout, finfo->dobj.dumpId, + q->data, + finfo->dobj.namespace->dobj.name, + proseclabel); + dumpACL(fout, finfo->dobj.catId, finfo->dobj.dumpId, "FUNCTION", funcsig, NULL, funcsig_tag, finfo->dobj.namespace->dobj.name, @@ -8323,7 +8439,7 @@ dumpCast(Archive *fout, CastInfo *cast) ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId, castsig->data, "pg_catalog", NULL, "", - false, "CAST", SECTION_PRE_DATA, + false, false, "CAST", SECTION_PRE_DATA, defqry->data, delqry->data, NULL, cast->dobj.dependencies, cast->dobj.nDeps, NULL, NULL); @@ -8567,7 +8683,7 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) oprinfo->dobj.namespace->dobj.name, NULL, oprinfo->rolname, - false, "OPERATOR", SECTION_PRE_DATA, + false, false, "OPERATOR", SECTION_PRE_DATA, q->data, delq->data, NULL, oprinfo->dobj.dependencies, oprinfo->dobj.nDeps, NULL, NULL); @@ -9026,7 +9142,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) opcinfo->dobj.namespace->dobj.name, NULL, opcinfo->rolname, - false, "OPERATOR CLASS", SECTION_PRE_DATA, + false, false, "OPERATOR CLASS", SECTION_PRE_DATA, q->data, delq->data, NULL, opcinfo->dobj.dependencies, opcinfo->dobj.nDeps, NULL, NULL); @@ -9307,7 +9423,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) opfinfo->dobj.namespace->dobj.name, NULL, opfinfo->rolname, - false, "OPERATOR FAMILY", SECTION_PRE_DATA, + false, false, "OPERATOR FAMILY", SECTION_PRE_DATA, q->data, delq->data, NULL, opfinfo->dobj.dependencies, opfinfo->dobj.nDeps, NULL, NULL); @@ -9423,7 +9539,7 @@ dumpConversion(Archive *fout, ConvInfo *convinfo) convinfo->dobj.namespace->dobj.name, NULL, convinfo->rolname, - false, "CONVERSION", SECTION_PRE_DATA, + false, false, "CONVERSION", SECTION_PRE_DATA, q->data, delq->data, NULL, convinfo->dobj.dependencies, convinfo->dobj.nDeps, NULL, NULL); @@ -9504,11 +9620,13 @@ dumpAgg(Archive *fout, AggInfo *agginfo) int i_aggtranstype; int i_agginitval; int i_convertok; + int i_seclabel; const char *aggtransfn; const char *aggfinalfn; const char *aggsortop; const char *aggtranstype; const char *agginitval; + const char *seclabel; bool convertok; /* Skip if not to be dumped */ @@ -9531,9 +9649,11 @@ dumpAgg(Archive *fout, AggInfo *agginfo) "aggsortop::pg_catalog.regoperator, " "agginitval, " "'t'::boolean AS convertok " + ",%s AS seclabel " "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " "WHERE a.aggfnoid = p.oid " "AND p.oid = '%u'::pg_catalog.oid", + security_label ? "p.security_label" : "NULL", agginfo->aggfn.dobj.catId.oid); } else if (g_fout->remoteVersion >= 70300) @@ -9543,6 +9663,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) "0 AS aggsortop, " "agginitval, " "'t'::boolean AS convertok " + ",NULL AS seclabel " "FROM pg_catalog.pg_aggregate a, pg_catalog.pg_proc p " "WHERE a.aggfnoid = p.oid " "AND p.oid = '%u'::pg_catalog.oid", @@ -9555,6 +9676,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) "0 AS aggsortop, " "agginitval, " "'t'::boolean AS convertok " + ",NULL AS seclabel " "FROM pg_aggregate " "WHERE oid = '%u'::oid", agginfo->aggfn.dobj.catId.oid); @@ -9567,6 +9689,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) "0 AS aggsortop, " "agginitval1 AS agginitval, " "(aggtransfn2 = 0 and aggtranstype2 = 0 and agginitval2 is null) AS convertok " + ",NULL AS seclabel " "FROM pg_aggregate " "WHERE oid = '%u'::oid", agginfo->aggfn.dobj.catId.oid); @@ -9592,6 +9715,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) i_aggtranstype = PQfnumber(res, "aggtranstype"); i_agginitval = PQfnumber(res, "agginitval"); i_convertok = PQfnumber(res, "convertok"); + i_seclabel = PQfnumber(res, "seclabel"); aggtransfn = PQgetvalue(res, 0, i_aggtransfn); aggfinalfn = PQgetvalue(res, 0, i_aggfinalfn); @@ -9599,6 +9723,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) aggtranstype = PQgetvalue(res, 0, i_aggtranstype); agginitval = PQgetvalue(res, 0, i_agginitval); convertok = (PQgetvalue(res, 0, i_convertok)[0] == 't'); + seclabel = PQgetvalue(res, 0, i_seclabel); aggsig = format_aggregate_signature(agginfo, fout, true); aggsig_tag = format_aggregate_signature(agginfo, fout, false); @@ -9667,7 +9792,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) agginfo->aggfn.dobj.namespace->dobj.name, NULL, agginfo->aggfn.rolname, - false, "AGGREGATE", SECTION_PRE_DATA, + false, false, "AGGREGATE", SECTION_PRE_DATA, q->data, delq->data, NULL, agginfo->aggfn.dobj.dependencies, agginfo->aggfn.dobj.nDeps, NULL, NULL); @@ -9679,6 +9804,12 @@ dumpAgg(Archive *fout, AggInfo *agginfo) agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname, agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId); + /* Dump Aggregate security label */ + dumpSecLabel(fout, agginfo->aggfn.dobj.dumpId, + q->data, + agginfo->aggfn.dobj.namespace->dobj.name, + seclabel); + /* * Since there is no GRANT ON AGGREGATE syntax, we have to make the ACL * command look like a function's GRANT; in particular this affects the @@ -9755,7 +9886,7 @@ dumpTSParser(Archive *fout, TSParserInfo *prsinfo) prsinfo->dobj.namespace->dobj.name, NULL, "", - false, "TEXT SEARCH PARSER", SECTION_PRE_DATA, + false, false, "TEXT SEARCH PARSER", SECTION_PRE_DATA, q->data, delq->data, NULL, prsinfo->dobj.dependencies, prsinfo->dobj.nDeps, NULL, NULL); @@ -9847,7 +9978,7 @@ dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo) dictinfo->dobj.namespace->dobj.name, NULL, dictinfo->rolname, - false, "TEXT SEARCH DICTIONARY", SECTION_PRE_DATA, + false, false, "TEXT SEARCH DICTIONARY", SECTION_PRE_DATA, q->data, delq->data, NULL, dictinfo->dobj.dependencies, dictinfo->dobj.nDeps, NULL, NULL); @@ -9907,7 +10038,7 @@ dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo) tmplinfo->dobj.namespace->dobj.name, NULL, "", - false, "TEXT SEARCH TEMPLATE", SECTION_PRE_DATA, + false, false, "TEXT SEARCH TEMPLATE", SECTION_PRE_DATA, q->data, delq->data, NULL, tmplinfo->dobj.dependencies, tmplinfo->dobj.nDeps, NULL, NULL); @@ -10040,7 +10171,7 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) cfginfo->dobj.namespace->dobj.name, NULL, cfginfo->rolname, - false, "TEXT SEARCH CONFIGURATION", SECTION_PRE_DATA, + false, false, "TEXT SEARCH CONFIGURATION", SECTION_PRE_DATA, q->data, delq->data, NULL, cfginfo->dobj.dependencies, cfginfo->dobj.nDeps, NULL, NULL); @@ -10096,7 +10227,7 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) NULL, NULL, fdwinfo->rolname, - false, "FOREIGN DATA WRAPPER", SECTION_PRE_DATA, + false, false, "FOREIGN DATA WRAPPER", SECTION_PRE_DATA, q->data, delq->data, NULL, fdwinfo->dobj.dependencies, fdwinfo->dobj.nDeps, NULL, NULL); @@ -10183,7 +10314,7 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) NULL, NULL, srvinfo->rolname, - false, "SERVER", SECTION_PRE_DATA, + false, false, "SERVER", SECTION_PRE_DATA, q->data, delq->data, NULL, srvinfo->dobj.dependencies, srvinfo->dobj.nDeps, NULL, NULL); @@ -10279,7 +10410,7 @@ dumpUserMappings(Archive *fout, const char *target, tag->data, namespace, NULL, - owner, false, + owner, false, false, "USER MAPPING", SECTION_PRE_DATA, q->data, delq->data, NULL, &dumpId, 1, @@ -10350,7 +10481,7 @@ dumpDefaultACL(Archive *fout, DefaultACLInfo *daclinfo) daclinfo->dobj.namespace ? daclinfo->dobj.namespace->dobj.name : NULL, NULL, daclinfo->defaclrole, - false, "DEFAULT ACL", SECTION_NONE, + false, false, "DEFAULT ACL", SECTION_NONE, q->data, "", NULL, daclinfo->dobj.dependencies, daclinfo->dobj.nDeps, NULL, NULL); @@ -10407,7 +10538,7 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, tag, nspname, NULL, owner ? owner : "", - false, "ACL", SECTION_NONE, + false, false, "ACL", SECTION_NONE, sql->data, "", NULL, &(objDumpId), 1, NULL, NULL); @@ -10416,6 +10547,123 @@ dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, } /* + * dumpSecLabel + * + * write out security label of the objects + */ +static void +dumpSecLabel(Archive *fout, DumpId objDumpId, + const char *target, const char *namespace, + const char *seclabel) +{ + PQExpBuffer qry; + + /* Do nothing, if security label dump is not given */ + if (!security_label || strlen(seclabel) == 0) + return; + + /* Do nothing, if --data-only for schemas, or --schema-only for blobs */ + if (strncmp(target, "LARGE OBJECT", 13) == 0) + { + if (schemaOnly) + return; + } + else + { + if (dataOnly) + return; + } + + /* Make ALTER xxx SECURITY LABEL TO command */ + qry = createPQExpBuffer(); + + appendPQExpBuffer(qry, "ALTER %s SECURITY LABEL TO '%s';", + target, seclabel); + + ArchiveEntry(fout, + nilCatalogId, /* catalog ID */ + createDumpId(), /* dump ID */ + target, /* name */ + namespace, /* namespace */ + NULL, /* tablespace */ + "", /* owner */ + false, /* with oids */ + false, /* with secids */ + "LABEL", /* desc */ + SECTION_NONE, /* section */ + qry->data, /* create */ + "", /* delete*/ + NULL, /* copy */ + &(objDumpId), /* dependency */ + 1, /* # deps */ + NULL, /* dumper Func */ + NULL); /* dumper Arg */ + + destroyPQExpBuffer(qry); +} + +/* + * dumpTableSecLabel + * + * write out security label of the table + */ +static void +dumpTableSecLabel(Archive *fout, DumpId objDumpId, + const char *namespace, + const char *table_name, + const char **column_names, int ncolumns, + const char *table_label, const char **column_labels) +{ + PQExpBuffer qry; + PQExpBuffer tag; + int i; + + if (!security_label || dataOnly) + return; + + /* Make ALTER xxx SECURITY LABEL TO command */ + qry = createPQExpBuffer(); + tag = createPQExpBuffer(); + + appendPQExpBuffer(tag, "TABLE %s", table_name); + + if (strlen(table_label) > 0) + appendPQExpBuffer(qry, "ALTER TABLE %s SECURITY LABEL TO '%s';\n", + table_name, table_label); + for (i = 0; i < ncolumns; i++) + { + if (strlen(column_labels[i]) > 0) + appendPQExpBuffer(qry, "ALTER TABLE %s ALTER %s SECURITY LABEL TO '%s';\n", + table_name, + column_names[i], + column_labels[i]); + } + + if (qry->len > 0) + ArchiveEntry(fout, + nilCatalogId, /* catalog ID */ + createDumpId(), /* dump ID */ + tag->data, /* name */ + namespace, /* namespace */ + NULL, /* tablespace */ + "", /* owner */ + false, /* with oids */ + false, /* with secids */ + "LABEL", /* desc */ + SECTION_NONE, /* section */ + qry->data, /* create */ + "", /* delete*/ + NULL, /* copy */ + &(objDumpId), /* dependency */ + 1, /* # deps */ + NULL, /* dumper Func */ + NULL); /* dumper Arg */ + + destroyPQExpBuffer(qry); + destroyPQExpBuffer(tag); +} + +/* * dumpTable * write out to fout the declarations (not data) of a user-defined table */ @@ -10571,7 +10819,6 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBuffer(q, "CREATE VIEW %s AS\n %s\n", fmtId(tbinfo->dobj.name), viewdef); - PQclear(res); } else @@ -10922,6 +11169,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) (tbinfo->relkind == RELKIND_VIEW) ? NULL : tbinfo->reltablespace, tbinfo->rolname, (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hasoids : false, + (strcmp(reltypename, "TABLE") == 0) ? tbinfo->hassecids : false, reltypename, SECTION_PRE_DATA, q->data, delq->data, NULL, tbinfo->dobj.dependencies, tbinfo->dobj.nDeps, @@ -10942,6 +11190,27 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) dumpTableConstraintComment(fout, constr); } + /* Dump relation/attribute's security labels */ + if (tbinfo->relkind == RELKIND_RELATION) + { + dumpTableSecLabel(fout, tbinfo->dobj.dumpId, + tbinfo->dobj.namespace->dobj.name, + tbinfo->dobj.name, + tbinfo->attnames, tbinfo->numatts, + tbinfo->rellabel, tbinfo->attlabels); + } + else + { + PQExpBuffer target = createPQExpBuffer(); + + appendPQExpBuffer(target, "%s %s", reltypename, fmtId(tbinfo->dobj.name)); + dumpSecLabel(fout, tbinfo->dobj.dumpId, + target->data, + tbinfo->dobj.namespace->dobj.name, + tbinfo->rellabel); + destroyPQExpBuffer(target); + } + destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); @@ -10990,7 +11259,7 @@ dumpAttrDef(Archive *fout, AttrDefInfo *adinfo) tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, - false, "DEFAULT", SECTION_PRE_DATA, + false, false, "DEFAULT", SECTION_PRE_DATA, q->data, delq->data, NULL, adinfo->dobj.dependencies, adinfo->dobj.nDeps, NULL, NULL); @@ -11086,7 +11355,7 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo) indxinfo->dobj.name, tbinfo->dobj.namespace->dobj.name, indxinfo->tablespace, - tbinfo->rolname, false, + tbinfo->rolname, false, false, "INDEX", SECTION_POST_DATA, q->data, delq->data, NULL, indxinfo->dobj.dependencies, indxinfo->dobj.nDeps, @@ -11211,7 +11480,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) coninfo->dobj.name, tbinfo->dobj.namespace->dobj.name, indxinfo->tablespace, - tbinfo->rolname, false, + tbinfo->rolname, false, false, "CONSTRAINT", SECTION_POST_DATA, q->data, delq->data, NULL, coninfo->dobj.dependencies, coninfo->dobj.nDeps, @@ -11244,7 +11513,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) coninfo->dobj.name, tbinfo->dobj.namespace->dobj.name, NULL, - tbinfo->rolname, false, + tbinfo->rolname, false, false, "FK CONSTRAINT", SECTION_POST_DATA, q->data, delq->data, NULL, coninfo->dobj.dependencies, coninfo->dobj.nDeps, @@ -11279,7 +11548,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) coninfo->dobj.name, tbinfo->dobj.namespace->dobj.name, NULL, - tbinfo->rolname, false, + tbinfo->rolname, false, false, "CHECK CONSTRAINT", SECTION_POST_DATA, q->data, delq->data, NULL, coninfo->dobj.dependencies, coninfo->dobj.nDeps, @@ -11315,7 +11584,7 @@ dumpConstraint(Archive *fout, ConstraintInfo *coninfo) coninfo->dobj.name, tyinfo->dobj.namespace->dobj.name, NULL, - tyinfo->rolname, false, + tyinfo->rolname, false, false, "CHECK CONSTRAINT", SECTION_POST_DATA, q->data, delq->data, NULL, coninfo->dobj.dependencies, coninfo->dobj.nDeps, @@ -11601,7 +11870,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, - false, "SEQUENCE", SECTION_PRE_DATA, + false, false, "SEQUENCE", SECTION_PRE_DATA, query->data, delqry->data, NULL, tbinfo->dobj.dependencies, tbinfo->dobj.nDeps, NULL, NULL); @@ -11637,7 +11906,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, - false, "SEQUENCE OWNED BY", SECTION_PRE_DATA, + false, false, "SEQUENCE OWNED BY", SECTION_PRE_DATA, query->data, "", NULL, &(tbinfo->dobj.dumpId), 1, NULL, NULL); @@ -11650,6 +11919,12 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) dumpComment(fout, query->data, tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId); + + /* Dump sequence security label */ + dumpSecLabel(fout, tbinfo->dobj.dumpId, + query->data, + tbinfo->dobj.namespace->dobj.name, + tbinfo->rellabel); } if (!schemaOnly) @@ -11665,7 +11940,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) tbinfo->dobj.namespace->dobj.name, NULL, tbinfo->rolname, - false, "SEQUENCE SET", SECTION_PRE_DATA, + false, false, "SEQUENCE SET", SECTION_PRE_DATA, query->data, "", NULL, &(tbinfo->dobj.dumpId), 1, NULL, NULL); @@ -11849,7 +12124,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) tginfo->dobj.name, tbinfo->dobj.namespace->dobj.name, NULL, - tbinfo->rolname, false, + tbinfo->rolname, false, false, "TRIGGER", SECTION_POST_DATA, query->data, delqry->data, NULL, tginfo->dobj.dependencies, tginfo->dobj.nDeps, @@ -11969,7 +12244,7 @@ dumpRule(Archive *fout, RuleInfo *rinfo) rinfo->dobj.name, tbinfo->dobj.namespace->dobj.name, NULL, - tbinfo->rolname, false, + tbinfo->rolname, false, false, "RULE", SECTION_POST_DATA, cmd->data, delcmd->data, NULL, rinfo->dobj.dependencies, rinfo->dobj.nDeps, @@ -12334,7 +12609,7 @@ fmtQualifiedId(const char *schema, const char *id) * "", not an invalid "()" column list. */ static const char * -fmtCopyColumnList(const TableInfo *ti) +fmtCopyColumnList(const TableInfo *ti, bool secids) { static PQExpBuffer q = NULL; int numatts = ti->numatts; @@ -12350,6 +12625,11 @@ fmtCopyColumnList(const TableInfo *ti) appendPQExpBuffer(q, "("); needComma = false; + if (secids) + { + appendPQExpBuffer(q, "security_label"); + needComma = true; + } for (i = 0; i < numatts; i++) { if (attisdropped[i]) diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index c93bada..78a50bd 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -137,6 +137,7 @@ typedef struct _namespaceInfo DumpableObject dobj; char *rolname; /* name of owner, or empty string */ char *nspacl; + char *seclabel; } NamespaceInfo; typedef struct _typeInfo @@ -153,6 +154,7 @@ typedef struct _typeInfo char typrelkind; /* 'r', 'v', 'c', etc */ char typtype; /* 'b', 'c', etc */ bool isArray; /* true if auto-generated array type */ + char *seclabel; bool isDefined; /* true if typisdefined */ /* If it's a dumpable base type, we create a "shell type" entry for it */ struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */ @@ -227,9 +229,11 @@ typedef struct _tableInfo bool hasrules; /* does it have any rules? */ bool hastriggers; /* does it have any triggers? */ bool hasoids; /* does it have OIDs? */ + bool hassecids; /* does it have security-Id? */ uint32 frozenxid; /* for restore frozen xid */ int ncheck; /* # of CHECK expressions */ char *reloftype; /* underlying type for typed table */ + char *rellabel; /* relation's security label */ /* 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 */ @@ -252,6 +256,7 @@ typedef struct _tableInfo char *attalign; /* attribute align, used by binary_upgrade */ bool *attislocal; /* true if attr has local definition */ char **attoptions; /* per-attribute options */ + char **attlabels; /* attribute's security label */ /* * Note: we need to store per-attribute notnull, default, and constraint @@ -287,6 +292,7 @@ typedef struct _tableDataInfo DumpableObject dobj; TableInfo *tdtable; /* link to table to dump */ bool oids; /* include OIDs in data? */ + bool secids; /* include SecIDs in data? */ } TableDataInfo; typedef struct _indxInfo @@ -448,6 +454,7 @@ typedef struct _blobInfo DumpableObject dobj; char *rolname; char *blobacl; + char *seclabel; } BlobInfo; /* global decls */ diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 0c3f63f..83f1678 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -69,6 +69,7 @@ static int disable_triggers = 0; static int inserts = 0; static int no_tablespaces = 0; static int use_setsessauth = 0; +static int security_label = 0; static int server_version; static FILE *OPF; @@ -132,6 +133,7 @@ main(int argc, char *argv[]) {"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}, {NULL, 0, NULL, 0} }; @@ -285,6 +287,8 @@ main(int argc, char *argv[]) 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 { fprintf(stderr, @@ -330,6 +334,8 @@ main(int argc, char *argv[]) appendPQExpBuffer(pgdumpopts, " --no-tablespaces"); if (use_setsessauth) appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization"); + if (security_label) + appendPQExpBuffer(pgdumpopts, " --security-label"); if (optind < argc) { @@ -561,6 +567,7 @@ help(void) 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 schema/data with security label\n")); printf(_("\nConnection options:\n")); printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); @@ -949,6 +956,7 @@ dropTablespaces(PGconn *conn) static void dumpTablespaces(PGconn *conn) { + PQExpBuffer qry = createPQExpBuffer(); PGresult *res; int i; @@ -957,31 +965,37 @@ dumpTablespaces(PGconn *conn) * pg_xxx) */ if (server_version >= 90000) - res = executeQuery(conn, "SELECT spcname, " + appendPQExpBuffer(qry, "SELECT spcname, " "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " "spclocation, spcacl, " "array_to_string(spcoptions, ', ')," "pg_catalog.shobj_description(oid, 'pg_tablespace') " + ",%s AS seclabel " "FROM pg_catalog.pg_tablespace " "WHERE spcname !~ '^pg_' " - "ORDER BY 1"); + "ORDER BY 1", + security_label ? "security_label" : "NULL"); else if (server_version >= 80200) - res = executeQuery(conn, "SELECT spcname, " + appendPQExpBuffer(qry, "SELECT spcname, " "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " "spclocation, spcacl, null, " "pg_catalog.shobj_description(oid, 'pg_tablespace') " + ",NULL AS seclabel" "FROM pg_catalog.pg_tablespace " "WHERE spcname !~ '^pg_' " "ORDER BY 1"); else - res = executeQuery(conn, "SELECT spcname, " + appendPQExpBuffer(qry, "SELECT spcname, " "pg_catalog.pg_get_userbyid(spcowner) AS spcowner, " "spclocation, spcacl, " "null, null " + ",NULL AS seclabel" "FROM pg_catalog.pg_tablespace " "WHERE spcname !~ '^pg_' " "ORDER BY 1"); + res = PQexec(conn, qry->data); + if (PQntuples(res) > 0) fprintf(OPF, "--\n-- Tablespaces\n--\n\n"); @@ -994,6 +1008,7 @@ dumpTablespaces(PGconn *conn) char *spcacl = PQgetvalue(res, i, 3); char *spcoptions = PQgetvalue(res, i, 4); char *spccomment = PQgetvalue(res, i, 5); + char *spcseclabel = PQgetvalue(res, i, 6); char *fspcname; /* needed for buildACLCommands() */ @@ -1010,6 +1025,10 @@ dumpTablespaces(PGconn *conn) appendPQExpBuffer(buf, "ALTER TABLESPACE %s SET (%s);\n", fspcname, spcoptions); + if (security_label && strlen(spcseclabel) > 0) + appendPQExpBuffer(buf, "ALTER TABLESPACE %s SECURITY LABEL TO '%s';\n", + fspcname, spcseclabel); + if (!skip_acls && !buildACLCommands(fspcname, NULL, "TABLESPACE", spcacl, spcowner, "", server_version, buf)) @@ -1153,48 +1172,48 @@ dumpCreateDB(PGconn *conn) /* Now collect all the information about databases to dump */ if (server_version >= 80400) - res = executeQuery(conn, - "SELECT datname, " + 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 seclabel " "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " - "WHERE datallowconn ORDER BY 1"); + "WHERE datallowconn ORDER BY 1", + security_label ? "d.security_label" : "NULL"); else if (server_version >= 80100) - res = executeQuery(conn, - "SELECT datname, " + 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 AS seclabel " "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, " + 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 AS seclabel " "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, " + 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 AS seclabel " "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, " + 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'))), " @@ -1202,6 +1221,7 @@ dumpCreateDB(PGconn *conn) "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " "datistemplate, '' as datacl, -1 as datconnlimit, " "'pg_default' AS dattablespace " + ",NULL AS seclabel " "FROM pg_database d " "WHERE datallowconn ORDER BY 1"); else @@ -1210,18 +1230,20 @@ dumpCreateDB(PGconn *conn) * Note: 7.0 fails to cope with sub-select in COALESCE, so just deal * with getting a NULL by not printing any OWNER clause. */ - res = executeQuery(conn, - "SELECT datname, " + 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 AS seclabel " "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); @@ -1234,6 +1256,7 @@ dumpCreateDB(PGconn *conn) char *dbacl = PQgetvalue(res, i, 7); char *dbconnlimit = PQgetvalue(res, i, 8); char *dbtablespace = PQgetvalue(res, i, 9); + char *dbseclabel = PQgetvalue(res, i, 10); char *fdbname; fdbname = strdup(fmtId(dbname)); @@ -1309,6 +1332,9 @@ dumpCreateDB(PGconn *conn) appendPQExpBuffer(buf, ";\n"); } } + if (security_label && strlen(dbseclabel) > 0) + appendPQExpBuffer(buf, "ALTER DATABASE %s SECURITY LABEL TO '%s';\n", + dbname, dbseclabel); if (!skip_acls && !buildACLCommands(fdbname, NULL, "DATABASE", dbacl, dbowner, diff --git a/src/bin/pg_dump/pg_restore.c b/src/bin/pg_dump/pg_restore.c index b0bcbc2..cdb2592 100644 --- a/src/bin/pg_dump/pg_restore.c +++ b/src/bin/pg_dump/pg_restore.c @@ -76,6 +76,7 @@ main(int argc, char **argv) static int no_data_for_failed_tables = 0; static int outputNoTablespaces = 0; static int use_setsessauth = 0; + static int no_security_label = 0; struct option cmdopts[] = { {"clean", 0, NULL, 'c'}, @@ -116,6 +117,7 @@ main(int argc, char **argv) {"no-tablespaces", no_argument, &outputNoTablespaces, 1}, {"role", required_argument, NULL, 2}, {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, + {"no-security-label", no_argument, &no_security_label, 1}, {NULL, 0, NULL, 0} }; @@ -262,6 +264,8 @@ main(int argc, char **argv) outputNoTablespaces = 1; else if (strcmp(optarg, "use-set-session-authorization") == 0) use_setsessauth = 1; + else if (strcmp(optarg, "no-security-label") == 0) + no_security_label = 1; else { fprintf(stderr, @@ -326,6 +330,7 @@ main(int argc, char **argv) opts->noDataForFailedTables = no_data_for_failed_tables; opts->noTablespace = outputNoTablespaces; opts->use_setsessauth = use_setsessauth; + opts->noSecLabel = no_security_label; if (opts->formatName) { @@ -437,6 +442,7 @@ usage(const char *progname) " ALTER OWNER commands to set ownership\n")); printf(_(" -1, --single-transaction\n" " restore as a single transaction\n")); + printf(_(" --no-security-label skip restoration of security labels\n")); printf(_("\nConnection options:\n")); printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); diff --git a/src/include/access/htup.h b/src/include/access/htup.h index d87e888..270ff67 100644 --- a/src/include/access/htup.h +++ b/src/include/access/htup.h @@ -163,7 +163,7 @@ typedef HeapTupleHeaderData *HeapTupleHeader; #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_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 */ @@ -292,6 +292,9 @@ do { \ (tup)->t_choice.t_datum.datum_typmod = (typmod) \ ) +#define HeapTupleHeaderHasOid(tup) \ + ((tup)->t_infomask & HEAP_HASOID) + #define HeapTupleHeaderGetOid(tup) \ ( \ ((tup)->t_infomask & HEAP_HASOID) ? \ @@ -351,6 +354,25 @@ do { \ (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) - @@ -545,12 +567,23 @@ typedef HeapTupleData *HeapTuple; #define HeapTupleClearHeapOnly(tuple) \ HeapTupleHeaderClearHeapOnly((tuple)->t_data) +#define HeapTupleHasOid(tuple) \ + HeapTupleHeaderHasOid((tuple)->t_data) + #define HeapTupleGetOid(tuple) \ HeapTupleHeaderGetOid((tuple)->t_data) #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 --git a/src/include/access/sysattr.h b/src/include/access/sysattr.h index 59cd2cd..ad5903b 100644 --- a/src/include/access/sysattr.h +++ b/src/include/access/sysattr.h @@ -25,7 +25,8 @@ #define MaxTransactionIdAttributeNumber (-5) #define MaxCommandIdAttributeNumber (-6) #define TableOidAttributeNumber (-7) -#define FirstLowInvalidHeapAttributeNumber (-8) +#define SecurityLabelAttributeNumber (-8) +#define FirstLowInvalidHeapAttributeNumber (-9) #endif /* SYSATTR_H */ diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h index 53e0be6..dea713b 100644 --- a/src/include/access/tupdesc.h +++ b/src/include/access/tupdesc.h @@ -75,13 +75,14 @@ typedef struct tupleDesc Oid tdtypeid; /* composite type ID for tuple type */ int32 tdtypmod; /* typmod for tuple type */ bool tdhasoid; /* tuple has oid attribute in its header */ + bool tdhassecid; /* tuple has security id in its header */ int tdrefcount; /* reference count, or -1 if not counting */ } *TupleDesc; -extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid); +extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid, bool hassecid); -extern TupleDesc CreateTupleDesc(int natts, bool hasoid, +extern TupleDesc CreateTupleDesc(int natts, bool hasoid, bool hassecid, Form_pg_attribute *attrs); extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc); diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h index 49a32e3..f088876 100644 --- a/src/include/bootstrap/bootstrap.h +++ b/src/include/bootstrap/bootstrap.h @@ -24,6 +24,7 @@ typedef enum BgWriterProcess, WalWriterProcess, WalReceiverProcess, + SecurityWorkerProcess, NUM_AUXPROCTYPES /* Must be last! */ } AuxProcType; diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index 8292273..e2a86e8 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -61,7 +61,8 @@ extern Oid heap_create_with_catalog(const char *relname, OnCommitAction oncommit, Datum reloptions, bool use_user_acl, - bool allow_system_table_mods); + bool allow_system_table_mods, + Oid *secLabels); extern void heap_drop_with_catalog(Oid relid); @@ -75,13 +76,15 @@ extern List *heap_truncate_find_FKs(List *relationIds); extern void InsertPgAttributeTuple(Relation pg_attribute_rel, Form_pg_attribute new_attribute, - CatalogIndexState indstate); + CatalogIndexState indstate, + Oid securityId); extern void InsertPgClassTuple(Relation pg_class_desc, Relation new_rel_desc, Oid new_rel_oid, Datum relacl, - Datum reloptions); + Datum reloptions, + Oid securityId); extern List *AddRelationNewConstraints(Relation rel, List *newColDefaults, @@ -106,10 +109,13 @@ extern void RemoveAttrDefaultById(Oid attrdefId); extern void RemoveStatistics(Oid relid, AttrNumber attnum); extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno, - bool relhasoids); + bool relhasoids, bool relhassecids); extern Form_pg_attribute SystemAttributeByName(const char *attname, - bool relhasoids); + bool relhasoids, bool relhassecids); + +extern bool SystemAttributeWritable(AttrNumber attno, + bool relhasoids, bool relhassecids); extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind, bool allow_system_table_mods); diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index 4f437fd..069256f 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -255,6 +255,11 @@ DECLARE_UNIQUE_INDEX(pg_type_oid_index, 2703, on pg_type using btree(oid oid_ops 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_seclabel_secid_index, 3401, on pg_seclabel using btree(secid oid_ops, datid oid_ops, relid oid_ops)); +#define SecLabelSecidIndexId 3401 +DECLARE_INDEX(pg_seclabel_label_index, 3402, on pg_seclabel using btree(datid oid_ops, relid oid_ops, label text_ops)); +#define SecLabelLabelIndexId 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 --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index 5ea514d..0f07482 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -60,6 +60,7 @@ CATALOG(pg_class,1259) BKI_BOOTSTRAP BKI_ROWTYPE_OID(83) BKI_SCHEMA_MACRO */ int2 relchecks; /* # of CHECK constraints for class */ bool relhasoids; /* T if we generate OIDs for rows of rel */ + bool relhassecids; /* T if we generate SIDs for rows of rel */ bool relhaspkey; /* has (or has had) PRIMARY KEY index */ bool relhasexclusion; /* has (or has had) exclusion constraint */ bool relhasrules; /* has (or has had) any rules */ @@ -93,7 +94,7 @@ typedef FormData_pg_class *Form_pg_class; * ---------------- */ -#define Natts_pg_class 27 +#define Natts_pg_class 28 #define Anum_pg_class_relname 1 #define Anum_pg_class_relnamespace 2 #define Anum_pg_class_reltype 3 @@ -113,14 +114,15 @@ typedef FormData_pg_class *Form_pg_class; #define Anum_pg_class_relnatts 17 #define Anum_pg_class_relchecks 18 #define Anum_pg_class_relhasoids 19 -#define Anum_pg_class_relhaspkey 20 -#define Anum_pg_class_relhasexclusion 21 -#define Anum_pg_class_relhasrules 22 -#define Anum_pg_class_relhastriggers 23 -#define Anum_pg_class_relhassubclass 24 -#define Anum_pg_class_relfrozenxid 25 -#define Anum_pg_class_relacl 26 -#define Anum_pg_class_reloptions 27 +#define Anum_pg_class_relhassecids 20 +#define Anum_pg_class_relhaspkey 21 +#define Anum_pg_class_relhasexclusion 22 +#define Anum_pg_class_relhasrules 23 +#define Anum_pg_class_relhastriggers 24 +#define Anum_pg_class_relhassubclass 25 +#define Anum_pg_class_relfrozenxid 26 +#define Anum_pg_class_relacl 27 +#define Anum_pg_class_reloptions 28 /* ---------------- * initial contents of pg_class @@ -132,13 +134,13 @@ typedef FormData_pg_class *Form_pg_class; */ /* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */ -DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t f f f f f 3 _null_ _null_ )); +DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t t f f f f f 3 _null_ _null_ )); DESCR(""); -DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ )); +DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f t f f f f f 3 _null_ _null_ )); DESCR(""); -DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f f r 25 0 t f f f f f 3 _null_ _null_ )); +DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f f r 25 0 t t f f f f f 3 _null_ _null_ )); DESCR(""); -DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f f r 27 0 t f f f f f 3 _null_ _null_ )); +DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t t f f f f f 3 _null_ _null_ )); DESCR(""); #define RELKIND_INDEX 'i' /* secondary index */ diff --git a/src/include/catalog/pg_conversion_fn.h b/src/include/catalog/pg_conversion_fn.h index d40dea6..add5fd9 100644 --- a/src/include/catalog/pg_conversion_fn.h +++ b/src/include/catalog/pg_conversion_fn.h @@ -17,7 +17,7 @@ extern Oid ConversionCreate(const char *conname, Oid connamespace, Oid conowner, int32 conforencoding, int32 contoencoding, - Oid conproc, bool def); + Oid conproc, bool def, Oid securityId); extern void RemoveConversionById(Oid conversionOid); extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding); diff --git a/src/include/catalog/pg_largeobject.h b/src/include/catalog/pg_largeobject.h index c4c4a26..6025f56 100644 --- a/src/include/catalog/pg_largeobject.h +++ b/src/include/catalog/pg_largeobject.h @@ -51,9 +51,10 @@ typedef FormData_pg_largeobject *Form_pg_largeobject; #define Anum_pg_largeobject_pageno 2 #define Anum_pg_largeobject_data 3 -extern Oid LargeObjectCreate(Oid loid); +extern Oid LargeObjectCreate(Oid loid, Oid securityId); extern void LargeObjectDrop(Oid loid); extern void LargeObjectAlterOwner(Oid loid, Oid newOwnerId); +extern void LargeObjectAlterSecLabel(Oid loid, char *new_label); extern bool LargeObjectExists(Oid loid); #endif /* PG_LARGEOBJECT_H */ diff --git a/src/include/catalog/pg_namespace.h b/src/include/catalog/pg_namespace.h index 425f039..0f8043d 100644 --- a/src/include/catalog/pg_namespace.h +++ b/src/include/catalog/pg_namespace.h @@ -77,6 +77,6 @@ DESCR("standard public schema"); /* * prototypes for functions in pg_namespace.c */ -extern Oid NamespaceCreate(const char *nspName, Oid ownerId); +extern Oid NamespaceCreate(const char *nspName, Oid ownerId, Oid secid); #endif /* PG_NAMESPACE_H */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index f2751a4..d7e7db8 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3719,6 +3719,10 @@ DESCR("current user privilege on role by role name"); DATA(insert OID = 2710 ( pg_has_role PGNSP PGUID 12 1 0 0 f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ pg_has_role_id _null_ _null_ _null_ )); DESCR("current user privilege on role by role oid"); +/* SE-PostgreSQL related stuff */ +DATA(insert OID = 3405 ( sepgsql_tuple_perms PGNSP PGUID 12 0 0 0 f f f t f v 4 0 16 "26 2249 23 16" _null_ _null_ _null_ _null_ sepgsql_tuple_perms _null_ _null_ _null_ )); +DATA(insert OID = 3406 ( seclabel_to_secid PGNSP PGUID 12 0 0 0 f f f t f v 1 0 26 "2249" _null_ _null_ _null_ _null_ seclabel_to_secid _null_ _null_ _null_ )); + DATA(insert OID = 1269 ( pg_column_size PGNSP PGUID 12 1 0 0 f f f t f s 1 0 23 "2276" _null_ _null_ _null_ _null_ pg_column_size _null_ _null_ _null_ )); DESCR("bytes required to store the value, perhaps with compression"); DATA(insert OID = 2322 ( pg_tablespace_size PGNSP PGUID 12 1 0 0 f f f t f v 1 0 20 "26" _null_ _null_ _null_ _null_ pg_tablespace_size_oid _null_ _null_ _null_ )); diff --git a/src/include/catalog/pg_proc_fn.h b/src/include/catalog/pg_proc_fn.h index c886f81..f2351c1 100644 --- a/src/include/catalog/pg_proc_fn.h +++ b/src/include/catalog/pg_proc_fn.h @@ -37,7 +37,8 @@ extern Oid ProcedureCreate(const char *procedureName, List *parameterDefaults, Datum proconfig, float4 procost, - float4 prorows); + float4 prorows, + Oid prosecid); extern bool function_parse_error_transpose(const char *prosrc); diff --git a/src/include/catalog/pg_seclabel.h b/src/include/catalog/pg_seclabel.h new file mode 100644 index 0000000..21b25de --- /dev/null +++ b/src/include/catalog/pg_seclabel.h @@ -0,0 +1,79 @@ +/* + * pg_seclabel.h + * Definition of the security label relation (pg_seclabel) + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ +#ifndef PG_SECLABEL_H +#define PG_SECLABEL_H + +#include "catalog/genbki.h" + +#include "access/htup.h" +#include "access/skey.h" +#include "utils/relcache.h" + +#define SecLabelRelationId 3400 + +CATALOG(pg_seclabel,3400) BKI_SHARED_RELATION BKI_WITHOUT_OIDS +{ + /* Identifier of the security label */ + Oid secid; + + /* OID of the database which referes the entry */ + Oid datid; + + /* OID of the table which refers the entry */ + Oid relid; + + /* Text representation of the security label */ + text label; +} FormData_pg_seclabel; + +/* + * Form_pg_seclabel corresponds to a pointer to a tuple with + * the format of pg_seclabel relation. + */ +typedef FormData_pg_seclabel *Form_pg_seclabel; + +/* + * Compiler constants for pg_seclabel + */ +#define Natts_pg_seclabel 4 +#define Anum_pg_seclabel_secid 1 +#define Anum_pg_seclabel_datid 2 +#define Anum_pg_seclabel_relid 3 +#define Anum_pg_seclabel_label 4 + +/* + * Functions to translate between security label and identifier + */ +extern bool ignore_security_label_input; + +extern bool seclabelCatalogHasSysAttr(Oid relOid); +extern void seclabelPostBootstrap(void); +extern void seclabelOnCreateDatabase(Oid src_datOid, Oid dst_datOid); +extern void seclabelOnDropDatabase(Oid datOid); +extern void seclabelOnDropTable(Oid relOid); + +extern Oid *seclabelMakeRelationDefaults(TupleDesc tupdesc, List *supOids); +extern Oid *seclabelMakeToastDefaults(TupleDesc tupdesc, Oid relOid); + +extern Oid seclabelGetNewSecid(Relation rel, HeapTuple tuple); + +extern Oid seclabelRawInput(Oid relOid, char *seclabel); +extern char *seclabelRawOutput(Oid relOid, Oid secid); +extern Oid seclabelTransInput(Oid relOid, char *seclabel); +extern char *seclabelTransOutput(Oid relOid, Oid secid); + +extern Oid seclabelMoveSecid(Oid dst_relid, Oid src_relid, Oid secid); +extern bool seclabelCompareSecid(Oid relid1, Oid secid1, + Oid relid2, Oid secid2); + +extern Datum seclabelSysattOutput(Oid relOid, HeapTuple tuple); + +extern void seclabelRelationReclaim(Oid relOid); +extern Datum seclabel_to_secid(PG_FUNCTION_ARGS); + +#endif /* PG_SECLABEL_H */ diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h index 5cf90af..1ca2dd4 100644 --- a/src/include/catalog/pg_type_fn.h +++ b/src/include/catalog/pg_type_fn.h @@ -50,7 +50,8 @@ extern Oid TypeCreate(Oid newTypeOid, char storage, int32 typeMod, int32 typNDims, - bool typeNotNull); + bool typeNotNull, + Oid securityId); extern void GenerateTypeDependencies(Oid typeNamespace, Oid typeObjectId, diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h index 95f86d3..ef9cd9b 100644 --- a/src/include/catalog/toasting.h +++ b/src/include/catalog/toasting.h @@ -61,5 +61,8 @@ DECLARE_TOAST(pg_shdescription, 2846, 2847); DECLARE_TOAST(pg_db_role_setting, 2966, 2967); #define PgDbRoleSettingToastTable 2966 #define PgDbRoleSettingToastIndex 2967 +DECLARE_TOAST(pg_seclabel, 3403, 3404); +#define PgSecLabelToastTable 3403 +#define PgSecLabelToastIndex 3404 #endif /* TOASTING_H */ diff --git a/src/include/commands/alter.h b/src/include/commands/alter.h index b1e04fb..6cd19ba 100644 --- a/src/include/commands/alter.h +++ b/src/include/commands/alter.h @@ -19,5 +19,6 @@ extern void ExecRenameStmt(RenameStmt *stmt); extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt); extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt); +extern void ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt); #endif /* ALTER_H */ diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h index 542fc27..874e07a 100644 --- a/src/include/commands/dbcommands.h +++ b/src/include/commands/dbcommands.h @@ -58,6 +58,7 @@ extern void RenameDatabase(const char *oldname, const char *newname); 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, char *new_label); extern Oid get_database_oid(const char *dbname); extern char *get_database_name(Oid dbid); diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index e8dbe81..7fc4922 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -60,6 +60,8 @@ extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType); 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, + bool isagg, char *new_label); extern void AlterFunction(AlterFunctionStmt *stmt); extern void CreateCast(CreateCastStmt *stmt); extern void DropCast(DropCastStmt *stmt); diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h index c914bd5..edd5abd 100644 --- a/src/include/commands/schemacmds.h +++ b/src/include/commands/schemacmds.h @@ -26,5 +26,6 @@ extern void RemoveSchemaById(Oid schemaOid); 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, char *new_label); #endif /* SCHEMACMDS_H */ diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index f9269cc..a531f45 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -35,6 +35,13 @@ extern void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry); +extern void AlterRelationSecLabel(RangeVar *relation, const char *attname, + ObjectType objtype, char *new_label); +extern void AlterRelationSecLabelInternal(Oid relOid, Oid securityId, + int expected_parents); +extern void AlterAttributeSecLabelInternal(Oid relOid, const char *attname, + Oid securityId, int expected_parents); + extern void CheckTableNotInUse(Relation rel, const char *stmt); extern void ExecuteTruncate(TruncateStmt *stmt); diff --git a/src/include/commands/tablespace.h b/src/include/commands/tablespace.h index cf005ee..e9e8a55 100644 --- a/src/include/commands/tablespace.h +++ b/src/include/commands/tablespace.h @@ -44,6 +44,7 @@ extern void DropTableSpace(DropTableSpaceStmt *stmt); extern void RenameTableSpace(const char *oldname, const char *newname); extern void AlterTableSpaceOwner(const char *name, Oid newOwnerId); extern void AlterTableSpaceOptions(AlterTableSpaceOptionsStmt *stmt); +extern void AlterTableSpaceSecLabel(const char *tspaceName, char *newLabel); extern void TablespaceCreateDbspace(Oid spcNode, Oid dbNode, bool isRedo); diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h index 8e5b610..f8c5872 100644 --- a/src/include/commands/typecmds.h +++ b/src/include/commands/typecmds.h @@ -43,5 +43,7 @@ extern void AlterTypeNamespace(List *names, const char *newschema); extern void AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool errorOnTableType); +extern void AlterTypeSecLabel(List *name, char *new_label); +extern void AlterTypeSecLabelInternal(Oid typeOid, Oid securityId); #endif /* TYPECMDS_H */ diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 820314c..7873b44 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -131,8 +131,8 @@ extern TupleHashEntry FindTupleHashEntry(TupleHashTable hashtable, /* * prototypes from functions in execJunk.c */ -extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, - TupleTableSlot *slot); +extern JunkFilter *ExecInitJunkFilter(List *targetList, + bool hasoid, bool hassecid, TupleTableSlot *slot); extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, TupleDesc cleanTupType, TupleTableSlot *slot); @@ -164,6 +164,7 @@ extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, int instrument_options); extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); +extern bool ExecContextForcesSecids(PlanState *planstate, bool *hassecid); extern void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate); extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate, @@ -234,8 +235,8 @@ extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate); extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate); extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, TupleDesc tupType); -extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid); -extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid); +extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid, bool hassecid); +extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hassecid); extern TupleDesc ExecTypeFromExprList(List *exprList); extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); diff --git a/src/include/fmgr.h b/src/include/fmgr.h index d1a0dc1..e9de809 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -51,6 +51,7 @@ typedef struct FmgrInfo bool fn_retset; /* function returns a set */ unsigned char fn_stats; /* collect stats if track_functions > this */ void *fn_extra; /* extra space for use by handler */ + char *fn_seclabel; /* function is trusted procedure, or NULL */ MemoryContext fn_mcxt; /* memory context to store fn_extra in */ fmNodePtr fn_expr; /* expression parse tree for call, or NULL */ } FmgrInfo; diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 1b5e476..37ffdfa 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -346,6 +346,7 @@ typedef enum NodeTag T_AlterUserMappingStmt, T_DropUserMappingStmt, T_AlterTableSpaceOptionsStmt, + T_AlterSecLabelStmt, /* * TAGS FOR PARSE TREE NODES (parsenodes.h) diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index b591073..53609e8 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -731,6 +731,7 @@ typedef struct RangeTblEntry Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *modifiedCols; /* columns needing INSERT/UPDATE permission */ + uint32 rowlvPerms; /* permissions for row-level access controls */ } RangeTblEntry; /* @@ -1134,6 +1135,8 @@ typedef enum AlterTableType AT_DropCluster, /* SET WITHOUT CLUSTER */ AT_AddOids, /* SET WITH OIDS */ AT_DropOids, /* SET WITHOUT OIDS */ + AT_AddSecLabel, /* SET WITH SECURITY LABEL */ + AT_DropSecLabel, /* SET WITHOUT SECURITY LABEL */ AT_SetTableSpace, /* SET TABLESPACE */ AT_SetRelOptions, /* SET (...) -- AM specific parameters */ AT_ResetRelOptions, /* RESET (...) -- AM specific parameters */ @@ -2073,6 +2076,20 @@ typedef struct AlterOwnerStmt char *newowner; /* the new owner */ } AlterOwnerStmt; +/* ---------------------- + * Alter Security Label Statement + * ---------------------- + */ +typedef struct AlterSecLabelStmt +{ + NodeTag type; + ObjectType objectType; /* OBJECT_TABLE, OBJECT_TYPE, 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 *addname; /* additional name if needed */ + Value *secLabel; /* the new security label */ +} AlterSecLabelStmt; /* ---------------------- * Create Rule Statement diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index 49d4b6c..13c88a1 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -208,6 +208,7 @@ PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD) PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD) PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD) PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD) +PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD) PG_KEYWORD("language", LANGUAGE, UNRESERVED_KEYWORD) PG_KEYWORD("large", LARGE_P, UNRESERVED_KEYWORD) PG_KEYWORD("last", LAST_P, UNRESERVED_KEYWORD) diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 684aed4..2080c95 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -412,6 +412,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SECURITY_PAM_APPL_H +/* Define to 1 if you enable SELinux support */ +#undef HAVE_SELINUX + /* Define to 1 if you have the `setproctitle' function. */ #undef HAVE_SETPROCTITLE diff --git a/src/include/sepgsql/hooks.h b/src/include/sepgsql/hooks.h new file mode 100644 index 0000000..4e983f8 --- /dev/null +++ b/src/include/sepgsql/hooks.h @@ -0,0 +1,284 @@ +/* + * sepgsql/hooks.h + * + * Header of SE-PostgreSQL Hooks + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#ifndef SEPGSQL_HOOKS_H +#define SEPGSQL_HOOKS_H + +#include "nodes/plannodes.h" +#include "nodes/relation.h" +#include "storage/fd.h" +#include "utils/acl.h" +#include "utils/rel.h" +#include "utils/snapshot.h" + +/* + * misc.c + */ +extern char *sepgsql_get_client_label(void); +extern char *sepgsql_set_client_label(char *new_label); +extern void sepgsql_post_bootstraping(void); +extern void sepgsql_initialize(void); +extern bool sepgsql_worker_needed(void); +extern void sepgsql_worker_main(void); + +/* + * database.c + */ +extern Oid sepgsql_database_create(const char *datName, Oid templateOid); +extern void sepgsql_database_alter(Oid databaseOid); +extern Oid sepgsql_database_relabel(Oid databaseOid, char *new_label); +extern void sepgsql_database_drop(Oid databaseOid, bool cascade); +extern void sepgsql_database_grant(Oid databaseOid); +extern void sepgsql_database_comment(Oid databaseOid); +extern void sepgsql_database_connect(Oid databaseOid); +extern void sepgsql_database_reindex(Oid databaseOid); +extern void sepgsql_database_getattr(Oid databaseOid); + +/* + * schema.c + */ +extern Oid sepgsql_schema_create(const char *nspName, bool is_temp); +extern void sepgsql_schema_alter(Oid namespaceOid); +extern Oid sepgsql_schema_relabel(Oid namespaceOid, char *new_label); +extern void sepgsql_schema_drop(Oid namespaceOid, bool cascade); +extern void sepgsql_schema_grant(Oid namespaceOid); +extern bool sepgsql_schema_search(Oid namespaceOid, bool abort); +extern void sepgsql_schema_comment(Oid namespaceOid); + +/* + * relation.c + */ +extern bool sepgsql_relation_perms(Oid relOid, AclMode aclmask, + Bitmapset *selectedCols, + Bitmapset *modifiedCols, bool abort); +extern Oid *sepgsql_relation_create(const char *relName, + char relkind, + TupleDesc tupDesc, + Oid namespaceId, + List *supOids, + bool createAs); +extern void sepgsql_relation_alter(Oid relationOid); +extern void sepgsql_relation_alter_schema(Oid relationOid, Oid newSchema); +extern void sepgsql_relation_alter_rename(Oid relationOid, + const char *newName); +extern void sepgsql_relation_alter_inherit(Oid childOid, Oid parentOid); +extern Oid sepgsql_relation_relabel(Oid relationOid, char *new_label); +extern void sepgsql_relation_drop(Oid relationOid, bool cascade); +extern void sepgsql_relation_getattr(Oid relationOid); +extern void sepgsql_relation_grant(Oid relationOid); +extern void sepgsql_relation_comment(Oid relationOid); +extern bool sepgsql_relation_cluster(Oid relationOid, bool abort); +extern void sepgsql_relation_truncate(Relation rel); +extern void sepgsql_relation_lock(Relation rel); +extern void sepgsql_relation_reindex(Oid relOid); +extern void sepgsql_view_replace(Oid viewOid); +extern void sepgsql_index_create(Oid relationOid, Oid namespaceOid); +extern void sepgsql_index_reindex(Oid indexOid); +extern void sepgsql_sequence_get_value(Oid sequenceOid); +extern void sepgsql_sequence_next_value(Oid sequenceOid); +extern void sepgsql_sequence_set_value(Oid sequenceOid); +extern void sepgsql_rule_create(Oid relationOid, + const char *ruleName); +extern void sepgsql_rule_drop(Oid relationOid, + const char *ruleName, bool cascade); +extern void sepgsql_rule_comment(Oid relationOid, const char *ruleName); +extern void sepgsql_trigger_create(Oid relationOid, const char *triggerName, + Oid constrrelid, Oid funcOid); +extern void sepgsql_trigger_alter(Oid relOid, const char *tgName); +extern void sepgsql_trigger_drop(Oid relOid, const char *tgName, bool cascade); +extern void sepgsql_trigger_comment(Oid relOid, const char *tgName); +extern void sepgsql_constraint_comment(Oid relOid, const char *constName); + +/* + * attribute.c + */ +extern Oid sepgsql_attribute_create(Oid relOid, const char *attName); +extern void sepgsql_attribute_alter(Oid relOid, const char *attName); +extern Oid sepgsql_attribute_relabel(Oid relOid, const char *attName, + char *new_label); +extern void sepgsql_attribute_drop(Oid relOid, const char *attName, bool cascade); +extern void sepgsql_attribute_grant(Oid relOid, AttrNumber attnum); +extern void sepgsql_attribute_comment(Oid relOid, AttrNumber attnum); + +/* + * proc.c + */ +extern Oid sepgsql_proc_create(const char *proName, Oid replaced, + Oid namespaceOid, Oid langageOid); +extern void sepgsql_proc_alter(Oid procOid); +extern void sepgsql_proc_alter_rename(Oid procOid, const char *newName); +extern void sepgsql_proc_alter_schema(Oid procOid, Oid newSchema); +extern Oid sepgsql_proc_relabel(Oid procOid, char *new_label); +extern void sepgsql_proc_drop(Oid procOid, bool cascade); +extern void sepgsql_proc_grant(Oid procOid); +extern void sepgsql_proc_comment(Oid procOid); +extern void sepgsql_proc_execute(Oid procOid); +extern bool sepgsql_proc_be_inlined(HeapTuple protup); +extern char *sepgsql_proc_domtrans(HeapTuple protup, MemoryContext mcxt); +extern Oid sepgsql_aggregate_create(const char *aggName, Oid namespaceId, + Oid transFunc, Oid finalFunc); +extern void sepgsql_aggregate_execute(Oid aggOid); + +/* + * type.c + */ +extern Oid sepgsql_type_create(const char *typeName, Oid replaced, + Oid namespaceId, char typeType, + Oid inputFunc, Oid outputFunc, + Oid recvFunc, Oid sendFunc, + Oid modinFunc, Oid modoutFunc, + Oid analyzeFunc); +extern void sepgsql_type_alter(Oid typeOid); +extern void sepgsql_type_alter_rename(Oid typeOid, const char *newName); +extern void sepgsql_type_alter_schema(Oid typeOid, Oid newSchema); +extern Oid sepgsql_type_relabel(Oid typeOid, char *newLabel); +extern void sepgsql_type_drop(Oid typeOid, bool cascade); +extern void sepgsql_type_comment(Oid typeOid); +extern Oid sepgsql_cast_create(Oid sourceTypeOid, Oid targetTypeOid, + char castMethod, Oid castFuncOid); +extern void sepgsql_cast_drop(Oid srcTypeOid, Oid dstTypeOid, bool cascade); +extern void sepgsql_cast_comment(Oid srcTypeOid, Oid dstTypeOid); + +/* + * tablespace.h + */ +extern Oid sepgsql_tablespace_create(const char *tablespaceName); +extern void sepgsql_tablespace_alter(Oid tablespaceOid); +extern Oid sepgsql_tablespace_relabel(Oid tablespaceOid, char *newLabel); +extern void sepgsql_tablespace_drop(Oid tablespaceOid, bool cascade); +extern void sepgsql_tablespace_grant(Oid tablespaceOid); +extern void sepgsql_tablespace_getattr(Oid tablespaceOid); +extern void sepgsql_tablespace_comment(Oid tablespaceOid); + +/* + * operator.h + */ +extern Oid sepgsql_operator_create(const char *operName, Oid replaced, + Oid namespaceId, + Oid codeFn, Oid restrictFn, Oid joinFn, + Oid commutatorOp, Oid negatorOp); +extern void sepgsql_operator_alter(Oid operOid); +extern Oid sepgsql_operator_relabel(Oid operOid, char *newLabel); +extern void sepgsql_operator_drop(Oid operOid, bool cascade); +extern void sepgsql_operator_comment(Oid operOid); + +extern Oid sepgsql_opclass_create(const char *opcName, Oid namespaceId, + Oid typeOid, Oid opfamilyOid, Oid storageOid); +extern void sepgsql_opclass_alter(Oid opcOid); +extern void sepgsql_opclass_alter_rename(Oid opcOid, const char *newName); +extern void sepgsql_opclass_drop(Oid opcOid, bool cascade); +extern void sepgsql_opclass_comment(Oid opcOid); + +extern Oid sepgsql_opfamily_create(const char *opfName, + Oid namespaceId, Oid amOid); +extern void sepgsql_opfamily_alter(Oid opfOid, bool isDrop, Oid amOid, + List *operators, List *procedures); +extern void sepgsql_opfamily_alter_rename(Oid opfOid, const char *newName); +extern void sepgsql_opfamily_alter_owner(Oid opfOid, Oid newOwner); +extern void sepgsql_opfamily_drop(Oid opfOid, bool cascade); +extern void sepgsql_opfamily_comment(Oid opfOid); + +/* + * role.c + */ +extern Oid sepgsql_role_create(const char *roleName); +extern void sepgsql_role_alter(Oid roleOid); +extern Oid sepgsql_role_relabel(Oid roleOid, char *newLabel); +extern void sepgsql_role_drop(Oid roleOid, bool cascade); +extern void sepgsql_role_grant(Oid roleOid, bool is_grant, List *memberIds); +extern void sepgsql_role_comment(Oid roleOid); + +/* + * blob.c + */ +extern Oid sepgsql_largeobject_create(Oid loid); +extern void sepgsql_largeobject_alter(Oid loid); +extern Oid sepgsql_largeobject_relabel(Oid loid, char *newLabel); +extern void sepgsql_largeobject_drop(Oid loid, bool cascade); +extern void sepgsql_largeobject_read(Oid loid, Snapshot snapshot); +extern void sepgsql_largeobject_write(Oid loid, Snapshot snapshot); +extern Oid sepgsql_largeobject_import(Oid loid, const char *filename); +extern void sepgsql_largeobject_export(Oid loid, Snapshot snapshot, + const char *filename); +extern void sepgsql_largeobject_grant(Oid loid); +extern void sepgsql_largeobject_comment(Oid loid); + +/* + * conversion.c + */ +extern Oid sepgsql_conversion_create(const char *convName, + Oid namespaceId, Oid conversionFunc); +extern void sepgsql_conversion_alter(Oid convOid); +extern void sepgsql_conversion_alter_rename(Oid convOid, const char *newName); +extern void sepgsql_conversion_drop(Oid convOid, bool cascade); +extern void sepgsql_conversion_comment(Oid convOid); + +/* + * tsearch.c + */ +extern Oid sepgsql_ts_config_create(const char *confName, Oid namespaceId); +extern void sepgsql_ts_config_alter(Oid confOid); +extern void sepgsql_ts_config_alter_rename(Oid confOid, const char *newName); +extern void sepgsql_ts_config_drop(Oid confOid, bool cascade); +extern void sepgsql_ts_config_comment(Oid confOid); +extern Oid sepgsql_ts_dict_create(const char *dictName, Oid namespaceId); +extern void sepgsql_ts_dict_alter(Oid dictOid); +extern void sepgsql_ts_dict_alter_rename(Oid dictOid, const char *newName); +extern void sepgsql_ts_dict_drop(Oid dictOid, bool cascade); +extern void sepgsql_ts_dict_comment(Oid dictOid); +extern Oid sepgsql_ts_parser_create(const char *parseName, Oid namespaceId, + Oid startFunc, Oid tokenFunc, Oid endFunc, + Oid headlineFunc, Oid lextypeFunc); +extern void sepgsql_ts_parser_alter_rename(Oid parseOid, const char *newName); +extern void sepgsql_ts_parser_drop(Oid parseOid, bool cascade); +extern void sepgsql_ts_parser_comment(Oid parseOid); +extern Oid sepgsql_ts_template_create(const char *templateName, + Oid namespaceId, + Oid initFunc, Oid lexizeFunc); +extern void sepgsql_ts_template_alter_rename(Oid templateOid, + const char *newName); +extern void sepgsql_ts_template_drop(Oid templateOid, bool cascade); +extern void sepgsql_ts_template_comment(Oid templateOid); + +/* + * fdw.c + */ +extern Oid sepgsql_fdw_create(const char *fdwName, Oid validatorFunc); +extern void sepgsql_fdw_alter(Oid fdwOid, Oid newValidator); +extern void sepgsql_fdw_drop(Oid fdwOid, bool cascade); +extern void sepgsql_fdw_grant(Oid fdwOid); + +extern Oid sepgsql_fserver_create(const char *fservName, Oid fdwOid); +extern void sepgsql_fserver_alter(Oid fservOid); +extern void sepgsql_fserver_drop(Oid fservOid, bool cascade); +extern void sepgsql_fserver_grant(Oid fservOid); + +extern Oid sepgsql_user_mapping_create(Oid mappedRoleId, Oid fservOid); +extern void sepgsql_user_mapping_alter(Oid umapOid); +extern void sepgsql_user_mapping_drop(Oid umapOid, bool cascade); + +/* + * row-level access controls + */ +#define SEPGSQL_ROWLV_FILTER 1 +#define SEPGSQL_ROWLV_ABORT 2 +#define SEPGSQL_ROWLV_BYPASS 3 + +extern int sepgsql_rowlv_get_mode(void); +extern int sepgsql_rowlv_set_mode(int new_mode); +extern void sepgsql_rowlv_add_policy(PlannerInfo *root, Scan *plan); +extern uint32 sepgsql_rowlv_permissions(RangeTblEntry *rte); + +extern void sepgsql_proxy_queries(List *queryList); + +extern void sepgsql_tuple_insert(Relation rel, HeapTuple tuple); +extern void sepgsql_tuple_update(Relation rel, ItemPointer otid, HeapTuple newtup); + +extern Datum sepgsql_tuple_perms(PG_FUNCTION_ARGS); + +#endif /* SEPGSQL_HOOKS_H */ diff --git a/src/include/sepgsql/sepgsql.h b/src/include/sepgsql/sepgsql.h new file mode 100644 index 0000000..ce65466 --- /dev/null +++ b/src/include/sepgsql/sepgsql.h @@ -0,0 +1,298 @@ +/* + * sepgsql/sepgsql.h + * + * Header of SE-PostgreSQL Internal + * + * Copyright (C) 2006-2010, NEC Corporation + * KaiGai Kohei + */ +#ifndef SEPGSQL_H +#define SEPGSQL_H + +#include "utils/snapshot.h" + +/* GUC : sepostgresql */ +extern int sepostgresql_mode; + +#define SEPGSQL_MODE_DEFAULT 1 +#define SEPGSQL_MODE_ENFORCING 2 +#define SEPGSQL_MODE_PERMISSIVE 3 +#define SEPGSQL_MODE_INTERNAL 4 +#define SEPGSQL_MODE_DISABLED 5 + +/* GUC: sepostgresql_mcstrans */ +extern bool sepgsql_mcstrans; + +/* GUC: sepostgresql_debug_audit */ +extern bool sepgsql_debug_audit; + +/* 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_VIEW, + SEPG_CLASS_DB_SEQUENCE, + SEPG_CLASS_DB_PROCEDURE, + SEPG_CLASS_DB_COLUMN, + SEPG_CLASS_DB_TUPLE, + SEPG_CLASS_DB_BLOB, + SEPG_CLASS_DB_LANGUAGE, + 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__LOAD_MODULE (1<<7) + +#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__INDEXON (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_VIEW__CREATE (SEPG_DB_DATABASE__CREATE) +#define SEPG_DB_VIEW__DROP (SEPG_DB_DATABASE__DROP) +#define SEPG_DB_VIEW__GETATTR (SEPG_DB_DATABASE__GETATTR) +#define SEPG_DB_VIEW__SETATTR (SEPG_DB_DATABASE__SETATTR) +#define SEPG_DB_VIEW__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) +#define SEPG_DB_VIEW__RELABELTO (SEPG_DB_DATABASE__RELABELTO) +#define SEPG_DB_VIEW__EXPAND (1<<6) + +#define SEPG_DB_PROCEDURE__CREATE (SEPG_DB_DATABASE__CREATE) +#define SEPG_DB_PROCEDURE__DROP (SEPG_DB_DATABASE__DROP) +#define SEPG_DB_PROCEDURE__GETATTR (SEPG_DB_DATABASE__GETATTR) +#define SEPG_DB_PROCEDURE__SETATTR (SEPG_DB_DATABASE__SETATTR) +#define SEPG_DB_PROCEDURE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) +#define SEPG_DB_PROCEDURE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) +#define SEPG_DB_PROCEDURE__EXECUTE (1<<6) +#define SEPG_DB_PROCEDURE__ENTRYPOINT (1<<7) +#define SEPG_DB_PROCEDURE__INSTALL (1<<8) + +#define SEPG_DB_COLUMN__CREATE (SEPG_DB_DATABASE__CREATE) +#define SEPG_DB_COLUMN__DROP (SEPG_DB_DATABASE__DROP) +#define SEPG_DB_COLUMN__GETATTR (SEPG_DB_DATABASE__GETATTR) +#define SEPG_DB_COLUMN__SETATTR (SEPG_DB_DATABASE__SETATTR) +#define SEPG_DB_COLUMN__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) +#define SEPG_DB_COLUMN__RELABELTO (SEPG_DB_DATABASE__RELABELTO) +#define SEPG_DB_COLUMN__SELECT (1<<6) +#define SEPG_DB_COLUMN__UPDATE (1<<7) +#define SEPG_DB_COLUMN__INSERT (1<<8) + +#define SEPG_DB_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) + +#define SEPG_DB_LANGUAGE__CREATE (SEPG_DB_DATABASE__CREATE) +#define SEPG_DB_LANGUAGE__DROP (SEPG_DB_DATABASE__DROP) +#define SEPG_DB_LANGUAGE__GETATTR (SEPG_DB_DATABASE__GETATTR) +#define SEPG_DB_LANGUAGE__SETATTR (SEPG_DB_DATABASE__SETATTR) +#define SEPG_DB_LANGUAGE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) +#define SEPG_DB_LANGUAGE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) +#define SEPG_DB_LANGUAGE__IMPLEMENTE (1<<6) +#define SEPG_DB_LANGUAGE__EXECUTE (1<<7) + +/* + * sepgsql_sid_t : alternative representation of security context + */ +typedef struct { + Oid relid; + Oid secid; +} sepgsql_sid_t; + +struct av_decision; + +/* + * selinux.c + */ +extern bool sepgsql_is_enabled(void); +extern bool sepgsql_get_enforce(void); +extern const char *sepgsql_show_mode(void); + +extern Size sepgsql_shmem_size(void); + +extern void sepgsql_audit_log(bool denied, + char *scontext, + char *tcontext, + uint16 tclass, + uint32 audited, + const char *audit_name); +extern void sepgsql_compute_avd(char *scontext, + char *tcontext, + uint16 tclass, + struct av_decision *avd); +extern bool sepgsql_compute_perms(char *scontext, + char *tcontext, + uint16 tclass, + uint32 required, + const char *audit_name, + bool abort); +extern char *sepgsql_compute_create(char *scontext, + char *tcontext, + uint16 tclass); +extern bool sepgsql_client_perms(sepgsql_sid_t tsid, + uint16 tclass, + uint32 required, + const char *audit_name, + bool abort); +extern sepgsql_sid_t sepgsql_client_create_secid(sepgsql_sid_t tsid, + uint16 tclass, + Oid nrelid); +extern char *sepgsql_client_create_label(sepgsql_sid_t tsid, + uint16 tclass); +extern void sepgsql_avc_worker_main(void); + +/* + * avc.c + */ +extern Size sepgsql_shmem_size(void); +extern void sepgsql_avc_init(void); +extern void sepgsql_avc_switch(const char *scontext); + + +/* + * label.c + */ +extern sepgsql_sid_t sepgsql_move_secid(Oid dst_relid, sepgsql_sid_t ssid); + +extern sepgsql_sid_t sepgsql_get_default_database_secid(Oid templateOid); +extern sepgsql_sid_t sepgsql_get_default_schema_secid(Oid databaseOid); +extern sepgsql_sid_t sepgsql_get_default_table_secid(Oid namespaceOid); +extern sepgsql_sid_t sepgsql_get_default_sequence_secid(Oid namespaceOid); +extern sepgsql_sid_t sepgsql_get_default_view_secid(Oid namespaceOid); +extern sepgsql_sid_t sepgsql_get_default_proc_secid(Oid namespaceOid); +extern sepgsql_sid_t sepgsql_get_default_column_secid(Oid tableOid); +extern sepgsql_sid_t sepgsql_get_default_tuple_secid(Oid tableOid); +extern sepgsql_sid_t sepgsql_get_default_blob_secid(Oid databaseOid); +extern Oid sepgsql_get_default_secid(Relation rel, HeapTuple tuple); + +extern void sepgsql_initial_labeling(void); + +extern char *sepgsql_mcstrans_out(char *label); +extern char *sepgsql_mcstrans_in(char *label); +extern char *sepgsql_rawlabel_out(char *label); +extern char *sepgsql_rawlabel_in(char *label); + +/* + * sepgsql_(object)_common + */ +extern bool sepgsql_database_common(Oid datOid, uint32 required, bool abort); +extern bool sepgsql_schema_common(Oid nspOid, uint32 required, bool abort); +extern bool sepgsql_relation_common(Oid relOid, uint32 required, bool abort); +extern bool sepgsql_attribute_common(Oid relOid, AttrNumber attno, + uint32 required, bool abort); +extern bool sepgsql_proc_common(Oid procOid, uint32 required, bool abort); +extern bool sepgsql_type_common(Oid typeOid, uint32 required, bool abort); +extern bool sepgsql_cast_common(Oid srcTypeOid, Oid dstTypeOid, + uint32 required, bool abort); +extern bool sepgsql_tablespace_common(Oid tspaceOid, uint32 required, bool abort); +extern bool sepgsql_operator_common(Oid operOid, uint32 required, bool abort); +extern bool sepgsql_opclass_common(Oid opcOid, uint32 required, bool abort); +extern bool sepgsql_opfamily_common(Oid opfOid, uint32 required, bool abort); +extern bool sepgsql_role_common(Oid roleOid, uint32 required, bool abort); +extern bool sepgsql_largeobejct_common(Oid loid, Snapshot snapshot, + uint32 required, bool abort); +extern bool sepgsql_conversion_common(Oid convOid, uint32 required, bool abort); +extern bool sepgsql_largeobject_common(Oid loid, Snapshot snapshot, + uint32 required, bool abort); +extern bool sepgsql_ts_config_common(Oid confOid, uint32 required, bool abort); +extern bool sepgsql_ts_dict_common(Oid dictOid, uint32 required, bool abort); +extern bool sepgsql_ts_parser_common(Oid parseOid, uint32 required, bool abort); +extern bool sepgsql_ts_template_common(Oid templateOid, uint32 required, bool abort); +extern bool sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort); +extern bool sepgsql_fserver_common(Oid fservOid, uint32 required, bool abort); +extern bool sepgsql_user_mapping_common(Oid umapOid, uint32 required, bool abort); + +#endif /* SEPGSQL_H */ diff --git a/src/include/storage/large_object.h b/src/include/storage/large_object.h index 43a61f3..6385b5c 100644 --- a/src/include/storage/large_object.h +++ b/src/include/storage/large_object.h @@ -70,7 +70,7 @@ typedef struct LargeObjectDesc /* inversion stuff in inv_api.c */ extern void close_lo_relation(bool isCommit); -extern Oid inv_create(Oid lobjId); +extern Oid inv_create(Oid lobjId, Oid securityId); extern LargeObjectDesc *inv_open(Oid lobjId, int flags, MemoryContext mcxt); extern void inv_close(LargeObjectDesc *obj_desc); extern int inv_drop(Oid lobjId); diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h index 4eece8b..4176eac 100644 --- a/src/include/storage/lwlock.h +++ b/src/include/storage/lwlock.h @@ -70,6 +70,7 @@ typedef enum LWLockId RelationMappingLock, AsyncCtlLock, AsyncQueueLock, + SepgsqlAvcLock, /* Individual lock IDs end here */ FirstBufMappingLock, FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS, diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index c22c65a..5b94283 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -166,6 +166,7 @@ extern bool log_btree_build_stats; extern PGDLLIMPORT bool check_function_bodies; extern bool default_with_oids; +extern bool default_with_secids; extern bool SQL_inheritance; extern int log_min_error_statement; diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h index 2f19e5c..6744782 100644 --- a/src/include/utils/syscache.h +++ b/src/include/utils/syscache.h @@ -101,6 +101,8 @@ extern bool SearchSysCacheExists(int cacheId, Datum key1, Datum key2, Datum key3, Datum key4); extern Oid GetSysCacheOid(int cacheId, Datum key1, Datum key2, Datum key3, Datum key4); +extern Oid GetSysCacheSecid(int cacheId, + Datum key1, Datum key2, Datum key3, Datum key4); extern HeapTuple SearchSysCacheAttName(Oid relid, const char *attname); extern HeapTuple SearchSysCacheCopyAttName(Oid relid, const char *attname); @@ -154,6 +156,15 @@ extern struct catclist *SearchSysCacheList(int cacheId, int nkeys, #define GetSysCacheOid4(cacheId, key1, key2, key3, key4) \ GetSysCacheOid(cacheId, key1, key2, key3, key4) +#define GetSysCacheSecid1(cacheId, key1) \ + GetSysCacheSecid(cacheId, key1, 0, 0, 0) +#define GetSysCacheSecid2(cacheId, key1, key2) \ + GetSysCacheSecid(cacheId, key1, key2, 0, 0) +#define GetSysCacheSecid3(cacheId, key1, key2, key3) \ + GetSysCacheSecid(cacheId, key1, key2, key3, 0) +#define GetSysCacheSecid4(cacheId, key1, key2, key3, key4) \ + GetSysCacheSecid(cacheId, key1, key2, key3, key4) + #define SearchSysCacheList1(cacheId, key1) \ SearchSysCacheList(cacheId, 1, key1, 0, 0, 0) #define SearchSysCacheList2(cacheId, key1, key2) \ diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 656ea73..2ce4e94 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -1936,7 +1936,7 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars) row = palloc0(sizeof(PLpgSQL_row)); row->dtype = PLPGSQL_DTYPE_ROW; - row->rowtupdesc = CreateTemplateTupleDesc(numvars, false); + row->rowtupdesc = CreateTemplateTupleDesc(numvars, false, false); row->nfields = numvars; row->fieldnames = palloc(numvars * sizeof(char *)); row->varnos = palloc(numvars * sizeof(int)); diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out index 1d9e110..9596b0b 100644 --- a/src/test/regress/expected/sanity_check.out +++ b/src/test/regress/expected/sanity_check.out @@ -114,6 +114,7 @@ SELECT relname, relhasindex pg_pltemplate | t pg_proc | t pg_rewrite | t + pg_seclabel | t pg_shdepend | t pg_shdescription | t pg_statistic | t @@ -153,7 +154,7 @@ SELECT relname, relhasindex timetz_tbl | f tinterval_tbl | f varchar_tbl | f -(142 rows) +(143 rows) -- -- another sanity check: every system catalog that has OIDs should have