diff -Nrpc base/configure sepgsql/configure *** base/configure Fri Jan 23 10:23:37 2009 --- sepgsql/configure Fri Jan 23 10:55:35 2009 *************** with_libxml *** 710,715 **** --- 710,716 ---- with_libxslt with_system_tzdata with_zlib + enable_selinux GREP EGREP ELF_SYS *************** Optional Features: *** 1378,1383 **** --- 1379,1385 ---- --enable-thread-safety make client libraries thread-safe --enable-thread-safety-force force thread-safety despite thread test failure + --enable-selinux enable to build with SELinux support --disable-float4-byval disable float4 passed by value --disable-float8-byval disable float8 passed by value --disable-largefile omit support for large files *************** fi *** 5531,5536 **** --- 5533,5644 ---- # + # SELinux support + # + + pgac_args="$pgac_args enable_selinux" + + # Check whether --enable-selinux was given. + if test "${enable_selinux+set}" = set; then + enableval=$enable_selinux; + case $enableval in + yes) + : + ;; + no) + : + ;; + *) + { { echo "$as_me:$LINENO: error: no argument expected for --enable-selinux option" >&5 + echo "$as_me: error: no argument expected for --enable-selinux option" >&2;} + { (exit 1); exit 1; }; } + ;; + esac + + else + enable_selinux=no + + fi + + + if test "$enable_selinux" = yes; then + { echo "$as_me:$LINENO: checking for getpeercon in -lselinux" >&5 + echo $ECHO_N "checking for getpeercon in -lselinux... $ECHO_C" >&6; } + if test "${ac_cv_lib_selinux_getpeercon+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 + else + ac_check_lib_save_LIBS=$LIBS + LIBS="-lselinux $LIBS" + cat >conftest.$ac_ext <<_ACEOF + /* confdefs.h. */ + _ACEOF + cat confdefs.h >>conftest.$ac_ext + cat >>conftest.$ac_ext <<_ACEOF + /* end confdefs.h. */ + + /* Override any 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 getpeercon (); + int + main () + { + return getpeercon (); + ; + return 0; + } + _ACEOF + rm -f conftest.$ac_objext conftest$ac_exeext + if { (ac_try="$ac_link" + case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; + esac + eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_selinux_getpeercon=yes + else + echo "$as_me: failed program was:" >&5 + sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_selinux_getpeercon=no + fi + + rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + LIBS=$ac_check_lib_save_LIBS + fi + { echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_getpeercon" >&5 + echo "${ECHO_T}$ac_cv_lib_selinux_getpeercon" >&6; } + if test $ac_cv_lib_selinux_getpeercon = yes; then + + cat >>confdefs.h <<_ACEOF + #define HAVE_SELINUX 1 + _ACEOF + + + else + { { echo "$as_me:$LINENO: error: \"--enable-selinux requires libselinux.\"" >&5 + echo "$as_me: error: \"--enable-selinux requires libselinux.\"" >&2;} + { (exit 1); exit 1; }; } + fi + + fi + + # # Elf # *************** with_libxml!$with_libxml$ac_delim *** 27803,27813 **** with_libxslt!$with_libxslt$ac_delim with_system_tzdata!$with_system_tzdata$ac_delim with_zlib!$with_zlib$ac_delim GREP!$GREP$ac_delim EGREP!$EGREP$ac_delim ELF_SYS!$ELF_SYS$ac_delim LDFLAGS_SL!$LDFLAGS_SL$ac_delim - LD!$LD$ac_delim _ACEOF if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then --- 27911,27921 ---- with_libxslt!$with_libxslt$ac_delim with_system_tzdata!$with_system_tzdata$ac_delim with_zlib!$with_zlib$ac_delim + enable_selinux!$enable_selinux$ac_delim GREP!$GREP$ac_delim EGREP!$EGREP$ac_delim ELF_SYS!$ELF_SYS$ac_delim LDFLAGS_SL!$LDFLAGS_SL$ac_delim _ACEOF if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then *************** _ACEOF *** 27849,27854 **** --- 27957,27963 ---- ac_delim='%!_!# ' for ac_last_try in false false false false false :; do cat >conf$$subs.sed <<_ACEOF + LD!$LD$ac_delim with_gnu_ld!$with_gnu_ld$ac_delim ld_R_works!$ld_R_works$ac_delim RANLIB!$RANLIB$ac_delim *************** vpath_build!$vpath_build$ac_delim *** 27911,27917 **** LTLIBOBJS!$LTLIBOBJS$ac_delim _ACEOF ! if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 60; then break elif $ac_last_try; then { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 --- 28020,28026 ---- LTLIBOBJS!$LTLIBOBJS$ac_delim _ACEOF ! if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 61; then break elif $ac_last_try; then { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 diff -Nrpc base/configure.in sepgsql/configure.in *** base/configure.in Fri Jan 23 10:23:37 2009 --- sepgsql/configure.in Fri Jan 23 10:55:35 2009 *************** PGAC_ARG_BOOL(with, zlib, yes, *** 763,768 **** --- 763,781 ---- AC_SUBST(with_zlib) # + # SELinux support + # + PGAC_ARG_BOOL(enable, selinux, no, + [enable to build with SELinux support]) + if test "$enable_selinux" = yes; then + AC_CHECK_LIB(selinux, getpeercon, + AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1, + [SE-PostgreSQL feature is enabled]) + AC_SUBST(enable_selinux), + AC_MSG_ERROR("--enable-selinux requires libselinux.")) + fi + + # # Elf # diff -Nrpc base/src/Makefile.global.in sepgsql/src/Makefile.global.in *** base/src/Makefile.global.in Fri Jan 23 10:23:37 2009 --- sepgsql/src/Makefile.global.in Fri Jan 23 10:55:35 2009 *************** enable_rpath = @enable_rpath@ *** 164,169 **** --- 164,170 ---- enable_nls = @enable_nls@ enable_debug = @enable_debug@ enable_dtrace = @enable_dtrace@ + enable_selinux = @enable_selinux@ enable_coverage = @enable_coverage@ enable_thread_safety = @enable_thread_safety@ diff -Nrpc base/src/backend/Makefile sepgsql/src/backend/Makefile *** base/src/backend/Makefile Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/Makefile Sat Jan 3 15:58:18 2009 *************** include $(top_builddir)/src/Makefile.glo *** 16,22 **** SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \ main nodes optimizer port postmaster regex rewrite \ ! storage tcop tsearch utils $(top_builddir)/src/timezone include $(srcdir)/common.mk --- 16,22 ---- SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \ main nodes optimizer port postmaster regex rewrite \ ! security storage tcop tsearch utils $(top_builddir)/src/timezone include $(srcdir)/common.mk *************** LIBS := $(filter-out -lpgport, $(LIBS)) *** 34,39 **** --- 34,44 ---- # The backend doesn't need everything that's in LIBS, however LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS)) + # SELinux support needs to link libselinux + ifeq ($(enable_selinux), yes) + LIBS += -lselinux + endif + ########################################################################## all: submake-libpgport postgres $(POSTGRES_IMP) diff -Nrpc base/src/backend/access/common/heaptuple.c sepgsql/src/backend/access/common/heaptuple.c *** base/src/backend/access/common/heaptuple.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/access/common/heaptuple.c Sat Jan 3 15:58:18 2009 *************** *** 61,66 **** --- 61,67 ---- #include "access/sysattr.h" #include "access/tuptoaster.h" #include "executor/tuptable.h" + #include "security/pgace.h" /* Does att's datatype allow packing into the 1-byte-header varlena format? */ *************** heap_attisnull(HeapTuple tup, int attnum *** 287,292 **** --- 288,295 ---- case MinCommandIdAttributeNumber: case MaxTransactionIdAttributeNumber: case MaxCommandIdAttributeNumber: + case SecurityAclAttributeNumber: + case SecurityLabelAttributeNumber: /* these are never null */ break; *************** heap_getsysattr(HeapTuple tup, int attnu *** 599,604 **** --- 602,613 ---- case TableOidAttributeNumber: result = ObjectIdGetDatum(tup->t_tableOid); break; + case SecurityAclAttributeNumber: + result = rowaclHeapGetSecurityAclSysattr(tup); + break; + case SecurityLabelAttributeNumber: + result = pgaceHeapGetSecurityLabelSysattr(tup); + break; default: elog(ERROR, "invalid attnum: %d", attnum); result = 0; /* keep compiler quiet */ *************** heap_form_tuple(TupleDesc tupleDescripto *** 723,728 **** --- 732,743 ---- if (tupleDescriptor->tdhasoid) len += sizeof(Oid); + if (tupleDescriptor->tdhasrowacl) + len += sizeof(Oid); + + if (tupleDescriptor->tdhasseclabel) + len += sizeof(Oid); + hoff = len = MAXALIGN(len); /* align user data safely */ data_len = heap_compute_data_size(tupleDescriptor, values, isnull); *************** heap_form_tuple(TupleDesc tupleDescripto *** 754,759 **** --- 769,779 ---- if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ td->t_infomask = HEAP_HASOID; + if (tupleDescriptor->tdhasrowacl) + td->t_infomask2 |= HEAP_HAS_ROWACL; + if (tupleDescriptor->tdhasseclabel) + td->t_infomask2 |= HEAP_HAS_SECLABEL; + heap_fill_tuple(tupleDescriptor, values, isnull, *************** heap_modify_tuple(HeapTuple tuple, *** 864,869 **** --- 884,893 ---- newTuple->t_tableOid = tuple->t_tableOid; if (tupleDesc->tdhasoid) HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); + if (HeapTupleHasRowAcl(newTuple)) + HeapTupleSetRowAcl(newTuple, HeapTupleGetRowAcl(tuple)); + if (HeapTupleHasSecLabel(newTuple)) + HeapTupleSetSecLabel(newTuple, HeapTupleGetSecLabel(tuple)); return newTuple; } *************** heap_form_minimal_tuple(TupleDesc tupleD *** 1475,1480 **** --- 1499,1510 ---- if (tupleDescriptor->tdhasoid) len += sizeof(Oid); + if (tupleDescriptor->tdhasrowacl) + len += sizeof(Oid); + + if (tupleDescriptor->tdhasseclabel) + len += sizeof(Oid); + hoff = len = MAXALIGN(len); /* align user data safely */ data_len = heap_compute_data_size(tupleDescriptor, values, isnull); *************** heap_form_minimal_tuple(TupleDesc tupleD *** 1496,1501 **** --- 1526,1536 ---- if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ tuple->t_infomask = HEAP_HASOID; + if (tupleDescriptor->tdhasrowacl) + tuple->t_infomask2 |= HEAP_HAS_ROWACL; + if (tupleDescriptor->tdhasseclabel) + tuple->t_infomask2 |= HEAP_HAS_SECLABEL; + heap_fill_tuple(tupleDescriptor, values, isnull, diff -Nrpc base/src/backend/access/common/reloptions.c sepgsql/src/backend/access/common/reloptions.c *** base/src/backend/access/common/reloptions.c Tue Jan 13 09:22:28 2009 --- sepgsql/src/backend/access/common/reloptions.c Tue Jan 13 10:00:14 2009 *************** *** 22,27 **** --- 22,28 ---- #include "catalog/pg_type.h" #include "commands/defrem.h" #include "nodes/makefuncs.h" + #include "security/pgace.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/guc.h" *************** *** 48,53 **** --- 49,62 ---- static relopt_bool boolRelOpts[] = { + { + { + "row_level_acl", + "Row-level ACLs validator", + RELOPT_KIND_HEAP + }, + false, + }, /* list terminator */ { { NULL } } }; *************** static relopt_real realRelOpts[] = *** 98,103 **** --- 107,121 ---- static relopt_string stringRelOpts[] = { + { + { + "default_row_acl", + "Default Row-level ACLs", + RELOPT_KIND_HEAP + }, + 0, true, rawaclValidateDefaultRowAclRelopt, + "", + }, /* list terminator */ { { NULL } } }; *************** default_reloptions(Datum reloptions, boo *** 877,883 **** StdRdOptions *rdopts; int numoptions; relopt_parse_elt tab[] = { ! {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)} }; options = parseRelOptions(reloptions, validate, kind, &numoptions); --- 895,903 ---- StdRdOptions *rdopts; int numoptions; relopt_parse_elt tab[] = { ! {"fillfactor", RELOPT_TYPE_INT, offsetof(StdRdOptions, fillfactor)}, ! {"row_level_acl", RELOPT_TYPE_BOOL, offsetof(StdRdOptions, row_level_acl)}, ! {"default_row_acl", RELOPT_TYPE_STRING, offsetof(StdRdOptions, default_row_acl)}, }; options = parseRelOptions(reloptions, validate, kind, &numoptions); diff -Nrpc base/src/backend/access/common/tupdesc.c sepgsql/src/backend/access/common/tupdesc.c *** base/src/backend/access/common/tupdesc.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/access/common/tupdesc.c Fri Jan 23 10:55:35 2009 *************** CreateTemplateTupleDesc(int natts, bool *** 88,93 **** --- 88,95 ---- desc->tdtypeid = RECORDOID; desc->tdtypmod = -1; desc->tdhasoid = hasoid; + desc->tdhasrowacl = false; /* set a proper bool, if necessary */ + desc->tdhasseclabel = false; /* set a proper bool, if necessary */ desc->tdrefcount = -1; /* assume not reference-counted */ return desc; *************** CreateTupleDesc(int natts, bool hasoid, *** 121,126 **** --- 123,130 ---- desc->tdtypeid = RECORDOID; desc->tdtypmod = -1; desc->tdhasoid = hasoid; + desc->tdhasrowacl = false; /* set a proper bool, if necessary */ + desc->tdhasseclabel = false; /* set a proper bool, if necessary */ desc->tdrefcount = -1; /* assume not reference-counted */ return desc; *************** CreateTupleDescCopy(TupleDesc tupdesc) *** 150,155 **** --- 154,161 ---- desc->tdtypeid = tupdesc->tdtypeid; desc->tdtypmod = tupdesc->tdtypmod; + desc->tdhasrowacl = tupdesc->tdhasrowacl; + desc->tdhasseclabel = tupdesc->tdhasseclabel; return desc; } *************** CreateTupleDescCopyConstr(TupleDesc tupd *** 208,213 **** --- 214,221 ---- desc->tdtypeid = tupdesc->tdtypeid; desc->tdtypmod = tupdesc->tdtypmod; + desc->tdhasrowacl = tupdesc->tdhasrowacl; + desc->tdhasseclabel = tupdesc->tdhasseclabel; return desc; } *************** equalTupleDescs(TupleDesc tupdesc1, Tupl *** 314,319 **** --- 322,331 ---- return false; if (tupdesc1->tdhasoid != tupdesc2->tdhasoid) return false; + if (tupdesc1->tdhasrowacl != tupdesc2->tdhasrowacl) + return false; + if (tupdesc1->tdhasseclabel != tupdesc2->tdhasseclabel) + return false; for (i = 0; i < tupdesc1->natts; i++) { diff -Nrpc base/src/backend/access/heap/heapam.c sepgsql/src/backend/access/heap/heapam.c *** base/src/backend/access/heap/heapam.c Thu Jan 22 14:34:54 2009 --- sepgsql/src/backend/access/heap/heapam.c Thu Jan 22 14:41:23 2009 *************** *** 54,59 **** --- 54,60 ---- #include "catalog/namespace.h" #include "miscadmin.h" #include "pgstat.h" + #include "security/pgace.h" #include "storage/bufmgr.h" #include "storage/freespace.h" #include "storage/lmgr.h" *************** heap_insert(Relation relation, HeapTuple *** 2056,2061 **** --- 2057,2068 ---- Oid simple_heap_insert(Relation relation, HeapTuple tup) { + if (!pgaceHeapTupleInsert(relation, tup, true, false)) + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("could not insert tuple on \"%s\" due to pgace security", + RelationGetRelationName(relation)))); + return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL); } *************** simple_heap_delete(Relation relation, It *** 2349,2354 **** --- 2356,2367 ---- ItemPointerData update_ctid; TransactionId update_xmax; + if (!pgaceHeapTupleDelete(relation, tid, true, false)) + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("could not delete tuple on \"%s\" due to pgace security", + RelationGetRelationName(relation)))); + result = heap_delete(relation, tid, &update_ctid, &update_xmax, GetCurrentCommandId(true), InvalidSnapshot, *************** simple_heap_update(Relation relation, It *** 3018,3023 **** --- 3031,3042 ---- ItemPointerData update_ctid; TransactionId update_xmax; + if (!pgaceHeapTupleUpdate(relation, otid, tup, true, false)) + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("could not update tuple on \"%s\" due to pgace security", + RelationGetRelationName(relation)))); + result = heap_update(relation, otid, tup, &update_ctid, &update_xmax, GetCurrentCommandId(true), InvalidSnapshot, diff -Nrpc base/src/backend/access/heap/tuptoaster.c sepgsql/src/backend/access/heap/tuptoaster.c *** base/src/backend/access/heap/tuptoaster.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/access/heap/tuptoaster.c Wed Jan 14 09:52:01 2009 *************** *** 35,40 **** --- 35,41 ---- #include "access/tuptoaster.h" #include "access/xact.h" #include "catalog/catalog.h" + #include "security/pgace.h" #include "utils/fmgroids.h" #include "utils/pg_lzcompress.h" #include "utils/rel.h" *************** toast_insert_or_update(Relation rel, Hea *** 591,596 **** --- 592,601 ---- hoff += BITMAPLEN(numAttrs); if (newtup->t_data->t_infomask & HEAP_HASOID) hoff += sizeof(Oid); + if (HeapTupleHasRowAcl(newtup)) + hoff += sizeof(Oid); + if (HeapTupleHasSecLabel(newtup)) + hoff += sizeof(Oid); hoff = MAXALIGN(hoff); Assert(hoff == newtup->t_data->t_hoff); /* now convert to a limit on the tuple data size */ *************** toast_insert_or_update(Relation rel, Hea *** 864,869 **** --- 869,878 ---- new_len += BITMAPLEN(numAttrs); if (olddata->t_infomask & HEAP_HASOID) new_len += sizeof(Oid); + if (HeapTupleHeaderHasRowAcl(olddata)) + new_len += sizeof(Oid); + if (HeapTupleHeaderHasSecLabel(olddata)) + new_len += sizeof(Oid); new_len = MAXALIGN(new_len); Assert(new_len == olddata->t_hoff); new_data_len = heap_compute_data_size(tupleDesc, *************** toast_flatten_tuple_attribute(Datum valu *** 1015,1020 **** --- 1024,1033 ---- new_len += BITMAPLEN(numAttrs); if (olddata->t_infomask & HEAP_HASOID) new_len += sizeof(Oid); + if (HeapTupleHeaderHasRowAcl(olddata)) + new_len += sizeof(Oid); + if (HeapTupleHeaderHasSecLabel(olddata)) + new_len += sizeof(Oid); new_len = MAXALIGN(new_len); Assert(new_len == olddata->t_hoff); new_data_len = heap_compute_data_size(tupleDesc, *************** toast_save_datum(Relation rel, Datum val *** 1213,1218 **** --- 1226,1237 ---- memcpy(VARDATA(&chunk_data), data_p, chunk_size); toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull); + if (!pgaceHeapTupleInsert(toastrel, toasttup, true, false)) + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("could not insert tuple \"%s\" due to pgace security", + RelationGetRelationName(toastrel)))); + heap_insert(toastrel, toasttup, mycid, options, NULL); /* diff -Nrpc base/src/backend/bootstrap/bootparse.y sepgsql/src/backend/bootstrap/bootparse.y *** base/src/backend/bootstrap/bootparse.y Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/bootstrap/bootparse.y Sat Jan 3 15:58:18 2009 *************** *** 42,47 **** --- 42,48 ---- #include "nodes/pg_list.h" #include "nodes/primnodes.h" #include "rewrite/prs2lock.h" + #include "security/pgace.h" #include "storage/block.h" #include "storage/fd.h" #include "storage/ipc.h" *************** Boot_CreateStmt: *** 206,211 **** --- 207,221 ---- RELKIND_RELATION, $3, true); + /* + * fixup boot_reldesc->rd_att->tdhassec(acl|label) via PGACE + */ + boot_reldesc->rd_rel->relkind = RELKIND_RELATION; + boot_reldesc->rd_att->tdhasrowacl + = pgaceTupleDescHasRowAcl(boot_reldesc, NIL); + boot_reldesc->rd_att->tdhasseclabel + = pgaceTupleDescHasSecLabel(boot_reldesc, NIL); + elog(DEBUG4, "bootstrap relation created"); } else *************** Boot_CreateStmt: *** 225,231 **** 0, ONCOMMIT_NOOP, (Datum) 0, ! true); elog(DEBUG4, "relation created with oid %u", id); } do_end(); --- 235,242 ---- 0, ONCOMMIT_NOOP, (Datum) 0, ! true, ! NIL); elog(DEBUG4, "relation created with oid %u", id); } do_end(); diff -Nrpc base/src/backend/bootstrap/bootstrap.c sepgsql/src/backend/bootstrap/bootstrap.c *** base/src/backend/bootstrap/bootstrap.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/bootstrap/bootstrap.c Fri Jan 23 10:55:35 2009 *************** *** 32,37 **** --- 32,38 ---- #include "nodes/makefuncs.h" #include "postmaster/bgwriter.h" #include "postmaster/walwriter.h" + #include "security/pgace.h" #include "storage/bufmgr.h" #include "storage/ipc.h" #include "storage/proc.h" *************** BootstrapModeMain(void) *** 500,505 **** --- 501,508 ---- */ boot_yyparse(); + pgacePostBootstrapingMode(); + /* Perform a checkpoint to ensure everything's down to disk */ SetProcessingMode(NormalProcessing); CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE); *************** InsertOneTuple(Oid objectid) *** 797,802 **** --- 800,810 ---- tupDesc = CreateTupleDesc(numattr, RelationGetForm(boot_reldesc)->relhasoids, attrtypes); + tupDesc->tdhasrowacl + = rowaclTupleDescHasRowAcl(boot_reldesc, NIL); + tupDesc->tdhasseclabel + = pgaceTupleDescHasSecLabel(boot_reldesc, NIL); + tuple = heap_form_tuple(tupDesc, values, Nulls); if (objectid != (Oid) 0) HeapTupleSetOid(tuple, objectid); diff -Nrpc base/src/backend/catalog/Makefile sepgsql/src/backend/catalog/Makefile *** base/src/backend/catalog/Makefile Wed Dec 24 11:45:25 2008 --- sepgsql/src/backend/catalog/Makefile Wed Dec 24 12:18:39 2008 *************** POSTGRES_BKI_SRCS = $(addprefix $(top_sr *** 34,39 **** --- 34,40 ---- pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ pg_database.h pg_tablespace.h pg_pltemplate.h \ pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ + pg_security.h \ pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ pg_ts_parser.h pg_ts_template.h \ pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ diff -Nrpc base/src/backend/catalog/aclchk.c sepgsql/src/backend/catalog/aclchk.c *** base/src/backend/catalog/aclchk.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/catalog/aclchk.c Fri Jan 23 10:55:35 2009 *************** dumpacl(Acl *acl) *** 105,111 **** * * NB: the original old_acl is pfree'd. */ ! static Acl * merge_acl_with_grant(Acl *old_acl, bool is_grant, bool grant_option, DropBehavior behavior, List *grantees, AclMode privileges, --- 105,111 ---- * * NB: the original old_acl is pfree'd. */ ! Acl * merge_acl_with_grant(Acl *old_acl, bool is_grant, bool grant_option, DropBehavior behavior, List *grantees, AclMode privileges, diff -Nrpc base/src/backend/catalog/catalog.c sepgsql/src/backend/catalog/catalog.c *** base/src/backend/catalog/catalog.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/catalog/catalog.c Sat Jan 3 15:58:18 2009 *************** *** 31,36 **** --- 31,37 ---- #include "catalog/pg_database.h" #include "catalog/pg_namespace.h" #include "catalog/pg_pltemplate.h" + #include "catalog/pg_security.h" #include "catalog/pg_shdepend.h" #include "catalog/pg_shdescription.h" #include "catalog/pg_tablespace.h" *************** IsSharedRelation(Oid relationId) *** 304,309 **** --- 305,311 ---- relationId == AuthMemRelationId || relationId == DatabaseRelationId || relationId == PLTemplateRelationId || + relationId == SecurityRelationId || relationId == SharedDescriptionRelationId || relationId == SharedDependRelationId || relationId == TableSpaceRelationId) *************** IsSharedRelation(Oid relationId) *** 316,321 **** --- 318,325 ---- relationId == DatabaseNameIndexId || relationId == DatabaseOidIndexId || relationId == PLTemplateNameIndexId || + relationId == SecurityOidIndexId || + relationId == SecuritySeclabelIndexId || relationId == SharedDescriptionObjIndexId || relationId == SharedDependDependerIndexId || relationId == SharedDependReferenceIndexId || diff -Nrpc base/src/backend/catalog/heap.c sepgsql/src/backend/catalog/heap.c *** base/src/backend/catalog/heap.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/catalog/heap.c Fri Jan 23 10:55:35 2009 *************** *** 56,61 **** --- 56,62 ---- #include "parser/parse_coerce.h" #include "parser/parse_expr.h" #include "parser/parse_relation.h" + #include "security/pgace.h" #include "storage/bufmgr.h" #include "storage/freespace.h" #include "storage/smgr.h" *************** static void AddNewRelationTuple(Relation *** 74,80 **** Oid new_rel_oid, Oid new_type_oid, Oid relowner, char relkind, ! Datum reloptions); static Oid AddNewRelationType(const char *typeName, Oid typeNamespace, Oid new_rel_oid, --- 75,82 ---- Oid new_rel_oid, Oid new_type_oid, Oid relowner, char relkind, ! Datum reloptions, ! List *pgaceAttrList); static Oid AddNewRelationType(const char *typeName, Oid typeNamespace, Oid new_rel_oid, *************** static List *insert_ordered_unique_oid(L *** 112,148 **** static FormData_pg_attribute a1 = { 0, {"ctid"}, TIDOID, 0, sizeof(ItemPointerData), SelfItemPointerAttributeNumber, 0, -1, -1, ! false, 'p', 's', true, false, false, true, 0, { 0 } }; static FormData_pg_attribute a2 = { 0, {"oid"}, OIDOID, 0, sizeof(Oid), ObjectIdAttributeNumber, 0, -1, -1, ! true, 'p', 'i', true, false, false, true, 0, { 0 } }; static FormData_pg_attribute a3 = { 0, {"xmin"}, XIDOID, 0, sizeof(TransactionId), MinTransactionIdAttributeNumber, 0, -1, -1, ! true, 'p', 'i', true, false, false, true, 0, { 0 } }; static FormData_pg_attribute a4 = { 0, {"cmin"}, CIDOID, 0, sizeof(CommandId), MinCommandIdAttributeNumber, 0, -1, -1, ! true, 'p', 'i', true, false, false, true, 0, { 0 } }; static FormData_pg_attribute a5 = { 0, {"xmax"}, XIDOID, 0, sizeof(TransactionId), MaxTransactionIdAttributeNumber, 0, -1, -1, ! true, 'p', 'i', true, false, false, true, 0, { 0 } }; static FormData_pg_attribute a6 = { 0, {"cmax"}, CIDOID, 0, sizeof(CommandId), MaxCommandIdAttributeNumber, 0, -1, -1, ! true, 'p', 'i', true, false, false, true, 0, { 0 } }; /* --- 114,150 ---- static FormData_pg_attribute a1 = { 0, {"ctid"}, TIDOID, 0, sizeof(ItemPointerData), SelfItemPointerAttributeNumber, 0, -1, -1, ! false, 'p', 's', 0, true, false, false, true, 0, { 0 } }; static FormData_pg_attribute a2 = { 0, {"oid"}, OIDOID, 0, sizeof(Oid), ObjectIdAttributeNumber, 0, -1, -1, ! true, 'p', 'i', 0, true, false, false, true, 0, { 0 } }; static FormData_pg_attribute a3 = { 0, {"xmin"}, XIDOID, 0, sizeof(TransactionId), MinTransactionIdAttributeNumber, 0, -1, -1, ! true, 'p', 'i', 0, true, false, false, true, 0, { 0 } }; static FormData_pg_attribute a4 = { 0, {"cmin"}, CIDOID, 0, sizeof(CommandId), MinCommandIdAttributeNumber, 0, -1, -1, ! true, 'p', 'i', 0, true, false, false, true, 0, { 0 } }; static FormData_pg_attribute a5 = { 0, {"xmax"}, XIDOID, 0, sizeof(TransactionId), MaxTransactionIdAttributeNumber, 0, -1, -1, ! true, 'p', 'i', 0, true, false, false, true, 0, { 0 } }; static FormData_pg_attribute a6 = { 0, {"cmax"}, CIDOID, 0, sizeof(CommandId), MaxCommandIdAttributeNumber, 0, -1, -1, ! true, 'p', 'i', 0, true, false, false, true, 0, { 0 } }; /* *************** static FormData_pg_attribute a6 = { *** 154,163 **** static FormData_pg_attribute a7 = { 0, {"tableoid"}, OIDOID, 0, sizeof(Oid), TableOidAttributeNumber, 0, -1, -1, ! true, 'p', 'i', true, false, false, true, 0, { 0 } }; ! static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; /* * This function returns a Form_pg_attribute pointer for a system attribute. --- 156,177 ---- static FormData_pg_attribute a7 = { 0, {"tableoid"}, OIDOID, 0, sizeof(Oid), TableOidAttributeNumber, 0, -1, -1, ! true, 'p', 'i', 0, true, false, false, true, 0, { 0 } }; ! static FormData_pg_attribute a8 = { ! 0, {SecurityAclAttributeName}, ACLITEMARRAYOID, 0, -1, ! SecurityAclAttributeNumber, 1, -1, -1, ! false, 'x', 'i', 0, true, false, false, true, 0, { 0 } ! }; ! ! static FormData_pg_attribute a9 = { ! 0, {SecurityLabelAttributeName}, TEXTOID, 0, -1, ! SecurityLabelAttributeNumber, 0, -1, -1, ! false, 'x', 'i', 0, true, false, false, true, 0, { 0 } ! }; ! ! static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9}; /* * This function returns a Form_pg_attribute pointer for a system attribute. *************** SystemAttributeByName(const char *attnam *** 197,202 **** --- 211,229 ---- return NULL; } + /* + * This function returns true, if the given attribute number is writable + * system column. If not, returns false. + */ + bool + SystemAttributeIsWritable(AttrNumber attnum) + { + if (attnum == SecurityAclAttributeNumber || + attnum == SecurityLabelAttributeNumber) + return true; + + return false; + } /* ---------------------------------------------------------------- * XXX END OF UGLY HARD CODED BADNESS XXX *************** CheckAttributeType(const char *attname, *** 486,492 **** void InsertPgAttributeTuple(Relation pg_attribute_rel, Form_pg_attribute new_attribute, ! CatalogIndexState indstate) { Datum values[Natts_pg_attribute]; bool nulls[Natts_pg_attribute]; --- 513,520 ---- void InsertPgAttributeTuple(Relation pg_attribute_rel, Form_pg_attribute new_attribute, ! CatalogIndexState indstate, ! List *pgaceAttrList) { Datum values[Natts_pg_attribute]; bool nulls[Natts_pg_attribute]; *************** InsertPgAttributeTuple(Relation pg_attri *** 508,513 **** --- 536,542 ---- values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(new_attribute->attbyval); values[Anum_pg_attribute_attstorage - 1] = CharGetDatum(new_attribute->attstorage); values[Anum_pg_attribute_attalign - 1] = CharGetDatum(new_attribute->attalign); + values[Anum_pg_attribute_attkind - 1] = CharGetDatum(new_attribute->attkind); values[Anum_pg_attribute_attnotnull - 1] = BoolGetDatum(new_attribute->attnotnull); values[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(new_attribute->atthasdef); values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(new_attribute->attisdropped); *************** InsertPgAttributeTuple(Relation pg_attri *** 519,524 **** --- 548,555 ---- tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls); + pgaceCreateAttributeCommon(pg_attribute_rel, tup, pgaceAttrList); + /* finally insert the new tuple, update the indexes, and clean up */ simple_heap_insert(pg_attribute_rel, tup); *************** AddNewAttributeTuples(Oid new_rel_oid, *** 541,547 **** TupleDesc tupdesc, char relkind, bool oidislocal, ! int oidinhcount) { Form_pg_attribute attr; int i; --- 572,579 ---- TupleDesc tupdesc, char relkind, bool oidislocal, ! int oidinhcount, ! List *pgaceAttrList) { Form_pg_attribute attr; int i; *************** AddNewAttributeTuples(Oid new_rel_oid, *** 565,577 **** for (i = 0; i < natts; i++) { attr = tupdesc->attrs[i]; ! /* Fill in the correct relation OID */ attr->attrelid = new_rel_oid; /* Make sure these are OK, too */ attr->attstattarget = -1; attr->attcacheoff = -1; ! InsertPgAttributeTuple(rel, attr, indstate); /* Add dependency info */ myself.classId = RelationRelationId; --- 597,610 ---- for (i = 0; i < natts; i++) { attr = tupdesc->attrs[i]; ! /* Fill in the correct relation OID and relkind */ attr->attrelid = new_rel_oid; + attr->attkind = relkind; /* Make sure these are OK, too */ attr->attstattarget = -1; attr->attcacheoff = -1; ! InsertPgAttributeTuple(rel, attr, indstate, pgaceAttrList); /* Add dependency info */ myself.classId = RelationRelationId; *************** AddNewAttributeTuples(Oid new_rel_oid, *** 603,608 **** --- 636,642 ---- /* Fill in the correct relation OID in the copied tuple */ attStruct.attrelid = new_rel_oid; + attStruct.attkind = relkind; /* Fill in correct inheritance info for the OID column */ if (attStruct.attnum == ObjectIdAttributeNumber) *************** AddNewAttributeTuples(Oid new_rel_oid, *** 611,617 **** attStruct.attinhcount = oidinhcount; } ! InsertPgAttributeTuple(rel, &attStruct, indstate); } } --- 645,651 ---- attStruct.attinhcount = oidinhcount; } ! InsertPgAttributeTuple(rel, &attStruct, indstate, pgaceAttrList); } } *************** void *** 639,645 **** InsertPgClassTuple(Relation pg_class_desc, Relation new_rel_desc, Oid new_rel_oid, ! Datum reloptions) { Form_pg_class rd_rel = new_rel_desc->rd_rel; Datum values[Natts_pg_class]; --- 673,680 ---- InsertPgClassTuple(Relation pg_class_desc, Relation new_rel_desc, Oid new_rel_oid, ! Datum reloptions, ! List *pgaceAttrList) { Form_pg_class rd_rel = new_rel_desc->rd_rel; Datum values[Natts_pg_class]; *************** InsertPgClassTuple(Relation pg_class_des *** 686,697 **** --- 721,736 ---- * be embarrassing to do this sort of thing in polite company. */ HeapTupleSetOid(tup, new_rel_oid); + pgaceCreateRelationCommon(pg_class_desc, tup, pgaceAttrList); /* finally insert the new tuple, update the indexes, and clean up */ simple_heap_insert(pg_class_desc, tup); CatalogUpdateIndexes(pg_class_desc, tup); + /* temporary use for this tuple */ + InsertSysCache(RelationGetRelid(pg_class_desc), tup); + heap_freetuple(tup); } *************** AddNewRelationTuple(Relation pg_class_de *** 709,715 **** Oid new_type_oid, Oid relowner, char relkind, ! Datum reloptions) { Form_pg_class new_rel_reltup; --- 748,755 ---- Oid new_type_oid, Oid relowner, char relkind, ! Datum reloptions, ! List *pgaceAttrList) { Form_pg_class new_rel_reltup; *************** AddNewRelationTuple(Relation pg_class_de *** 769,775 **** new_rel_desc->rd_att->tdtypeid = new_type_oid; /* Now build and insert the tuple */ ! InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions); } --- 809,816 ---- new_rel_desc->rd_att->tdtypeid = new_type_oid; /* Now build and insert the tuple */ ! InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, ! reloptions, pgaceAttrList); } *************** heap_create_with_catalog(const char *rel *** 838,844 **** int oidinhcount, OnCommitAction oncommit, Datum reloptions, ! bool allow_system_table_mods) { Relation pg_class_desc; Relation new_rel_desc; --- 879,886 ---- int oidinhcount, OnCommitAction oncommit, Datum reloptions, ! bool allow_system_table_mods, ! List *pgaceAttrList) { Relation pg_class_desc; Relation new_rel_desc; *************** heap_create_with_catalog(const char *rel *** 1012,1024 **** new_type_oid, ownerid, relkind, ! reloptions); /* * now add tuples to pg_attribute for the attributes in our new relation. */ AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind, ! oidislocal, oidinhcount); /* * Make a dependency link to force the relation to be deleted if its --- 1054,1075 ---- new_type_oid, ownerid, relkind, ! reloptions, ! pgaceAttrList); /* * now add tuples to pg_attribute for the attributes in our new relation. */ AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind, ! oidislocal, oidinhcount, pgaceAttrList); ! ! /* ! * Fixup rel->rd_att->tdhassecacl and rel->rd_att->tdhasseclabel ! */ ! new_rel_desc->rd_att->tdhasrowacl ! = pgaceTupleDescHasRowAcl(new_rel_desc, NIL); ! new_rel_desc->rd_att->tdhasseclabel ! = pgaceTupleDescHasSecLabel(new_rel_desc, NIL); /* * Make a dependency link to force the relation to be deleted if its diff -Nrpc base/src/backend/catalog/index.c sepgsql/src/backend/catalog/index.c *** base/src/backend/catalog/index.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/catalog/index.c Fri Jan 23 10:55:35 2009 *************** *** 48,53 **** --- 48,54 ---- #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/var.h" + #include "security/pgace.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/procarray.h" *************** InitializeAttributeOids(Relation indexRe *** 315,321 **** --- 316,325 ---- tupleDescriptor = RelationGetDescr(indexRelation); for (i = 0; i < numatts; i += 1) + { tupleDescriptor->attrs[i]->attrelid = indexoid; + tupleDescriptor->attrs[i]->attkind = RELKIND_INDEX; + } } /* ---------------------------------------------------------------- *************** AppendAttributeTuples(Relation indexRela *** 351,357 **** Assert(indexTupDesc->attrs[i]->attnum == i + 1); Assert(indexTupDesc->attrs[i]->attcacheoff == -1); ! InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], indstate); } CatalogCloseIndexes(indstate); --- 355,361 ---- Assert(indexTupDesc->attrs[i]->attnum == i + 1); Assert(indexTupDesc->attrs[i]->attcacheoff == -1); ! InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], indstate, NIL); } CatalogCloseIndexes(indstate); *************** index_create(Oid heapRelationId, *** 630,635 **** --- 634,647 ---- Assert(indexRelationId == RelationGetRelid(indexRelation)); /* + * Fixup rel->rd_att->tdhassecXXX + */ + indexRelation->rd_att->tdhasrowacl + = pgaceTupleDescHasRowAcl(indexRelation, NIL); + indexRelation->rd_att->tdhasseclabel + = pgaceTupleDescHasSecLabel(indexRelation, NIL); + + /* * Obtain exclusive lock on it. Although no other backends can see it * until we commit, this prevents deadlock-risk complaints from lock * manager in cases such as CLUSTER. *************** index_create(Oid heapRelationId, *** 652,658 **** */ InsertPgClassTuple(pg_class, indexRelation, RelationGetRelid(indexRelation), ! reloptions); /* done with pg_class */ heap_close(pg_class, RowExclusiveLock); --- 664,670 ---- */ InsertPgClassTuple(pg_class, indexRelation, RelationGetRelid(indexRelation), ! reloptions, NIL); /* done with pg_class */ heap_close(pg_class, RowExclusiveLock); diff -Nrpc base/src/backend/catalog/pg_aggregate.c sepgsql/src/backend/catalog/pg_aggregate.c *** base/src/backend/catalog/pg_aggregate.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/catalog/pg_aggregate.c Sat Jan 3 15:58:18 2009 *************** AggregateCreate(const char *aggName, *** 231,237 **** NIL, /* parameterDefaults */ PointerGetDatum(NULL), /* proconfig */ 1, /* procost */ ! 0); /* prorows */ /* * Okay to create the pg_aggregate entry. --- 231,238 ---- NIL, /* parameterDefaults */ PointerGetDatum(NULL), /* proconfig */ 1, /* procost */ ! 0, /* prorows */ ! NULL); /* PGACE opaque */ /* * Okay to create the pg_aggregate entry. diff -Nrpc base/src/backend/catalog/pg_largeobject.c sepgsql/src/backend/catalog/pg_largeobject.c *** base/src/backend/catalog/pg_largeobject.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/catalog/pg_largeobject.c Sat Jan 3 15:58:18 2009 *************** *** 18,23 **** --- 18,24 ---- #include "access/heapam.h" #include "catalog/indexing.h" #include "catalog/pg_largeobject.h" + #include "security/pgace.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/rel.h" *************** LargeObjectCreate(Oid loid) *** 59,64 **** --- 60,67 ---- ntup = heap_form_tuple(pg_largeobject->rd_att, values, nulls); + pgaceLargeObjectCreate(pg_largeobject, ntup); + /* * Insert it */ *************** LargeObjectDrop(Oid loid) *** 80,85 **** --- 83,89 ---- ScanKeyData skey[1]; SysScanDesc sd; HeapTuple tuple; + void *pgaceItem = NULL; ScanKeyInit(&skey[0], Anum_pg_largeobject_loid, *************** LargeObjectDrop(Oid loid) *** 93,98 **** --- 97,103 ---- while ((tuple = systable_getnext(sd)) != NULL) { + pgaceLargeObjectDrop(pg_largeobject, tuple, &pgaceItem); simple_heap_delete(pg_largeobject, &tuple->t_self); found = true; } diff -Nrpc base/src/backend/catalog/pg_proc.c sepgsql/src/backend/catalog/pg_proc.c *** base/src/backend/catalog/pg_proc.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/catalog/pg_proc.c Fri Jan 23 10:55:35 2009 *************** *** 29,34 **** --- 29,35 ---- #include "miscadmin.h" #include "nodes/nodeFuncs.h" #include "parser/parse_type.h" + #include "security/pgace.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" #include "utils/acl.h" *************** ProcedureCreate(const char *procedureNam *** 78,84 **** List *parameterDefaults, Datum proconfig, float4 procost, ! float4 prorows) { Oid retval; int parameterCount; --- 79,86 ---- List *parameterDefaults, Datum proconfig, float4 procost, ! float4 prorows, ! void *pgaceItem) { Oid retval; int parameterCount; *************** ProcedureCreate(const char *procedureNam *** 474,479 **** --- 476,482 ---- /* Okay, do it... */ tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces); + pgaceGramCreateFunction(rel, tup, (DefElem *)pgaceItem); simple_heap_update(rel, &tup->t_self, tup); ReleaseSysCache(oldtup); *************** ProcedureCreate(const char *procedureNam *** 483,488 **** --- 486,492 ---- { /* Creating a new procedure */ tup = heap_form_tuple(tupDesc, values, nulls); + pgaceGramCreateFunction(rel, tup, (DefElem *)pgaceItem); simple_heap_insert(rel, tup); is_update = false; } diff -Nrpc base/src/backend/catalog/toasting.c sepgsql/src/backend/catalog/toasting.c *** base/src/backend/catalog/toasting.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/catalog/toasting.c Sat Jan 3 15:58:18 2009 *************** create_toast_table(Relation rel, Oid toa *** 200,206 **** 0, ONCOMMIT_NOOP, (Datum) 0, ! true); /* make the toast relation visible, else index creation will fail */ CommandCounterIncrement(); --- 200,207 ---- 0, ONCOMMIT_NOOP, (Datum) 0, ! true, ! NIL); /* make the toast relation visible, else index creation will fail */ CommandCounterIncrement(); diff -Nrpc base/src/backend/commands/cluster.c sepgsql/src/backend/commands/cluster.c *** base/src/backend/commands/cluster.c Thu Jan 22 14:34:54 2009 --- sepgsql/src/backend/commands/cluster.c Thu Jan 22 14:41:23 2009 *************** make_new_heap(Oid OIDOldHeap, const char *** 711,717 **** 0, ONCOMMIT_NOOP, reloptions, ! allowSystemTableMods); ReleaseSysCache(tuple); --- 711,718 ---- 0, ONCOMMIT_NOOP, reloptions, ! allowSystemTableMods, ! NIL); ReleaseSysCache(tuple); *************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl *** 906,911 **** --- 907,920 ---- if (NewHeap->rd_rel->relhasoids) HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple)); + /* Preserve RowACL, if any */ + if (HeapTupleHasRowAcl(copiedTuple)) + HeapTupleSetRowAcl(copiedTuple, HeapTupleGetRowAcl(tuple)); + + /* Preserve SecLabel, if any */ + if (HeapTupleHasSecLabel(copiedTuple)) + HeapTupleSetSecLabel(copiedTuple, HeapTupleGetSecLabel(tuple)); + /* The heap rewrite module does the rest */ rewrite_heap_tuple(rwstate, tuple, copiedTuple); diff -Nrpc base/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c *** base/src/backend/commands/copy.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/commands/copy.c Wed Jan 14 15:02:53 2009 *************** *** 21,27 **** --- 21,29 ---- #include #include "access/heapam.h" + #include "access/sysattr.h" #include "access/xact.h" + #include "catalog/heap.h" #include "catalog/namespace.h" #include "catalog/pg_type.h" #include "commands/copy.h" *************** *** 34,39 **** --- 36,42 ---- #include "optimizer/planner.h" #include "parser/parse_relation.h" #include "rewrite/rewriteHandler.h" + #include "security/pgace.h" #include "storage/fd.h" #include "tcop/tcopprot.h" #include "utils/acl.h" *************** typedef struct CopyStateData *** 160,165 **** --- 163,176 ---- char *raw_buf; int raw_buf_index; /* next byte to process */ int raw_buf_len; /* total # of bytes stored */ + + /* dump/restore support for security_acl */ + FmgrInfo rowacl_out_function; + bool rowacl_force_quot; + + /* dump/restore support for security_label */ + FmgrInfo seclabel_out_function; + bool seclabel_force_quot; } CopyStateData; typedef CopyStateData *CopyState; *************** static const char BinarySignature[11] = *** 243,249 **** /* non-export function prototypes */ static void DoCopyTo(CopyState cstate); static void CopyTo(CopyState cstate); ! static void CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls); static void CopyFrom(CopyState cstate); static bool CopyReadLine(CopyState cstate); --- 254,260 ---- /* non-export function prototypes */ static void DoCopyTo(CopyState cstate); static void CopyTo(CopyState cstate); ! static void CopyOneRowTo(CopyState cstate, Oid tupleOid, Oid rowAclId, Oid secLabelId, Datum *values, bool *nulls); static void CopyFrom(CopyState cstate); static bool CopyReadLine(CopyState cstate); *************** DoCopy(const CopyStmt *stmt, const char *** 1072,1077 **** --- 1083,1090 ---- /* Generate or convert list of attributes to process */ cstate->attnumlist = CopyGetAttnums(tupDesc, cstate->rel, attnamelist); + pgaceCopyTable(cstate->rel, cstate->attnumlist, is_from); + num_phys_attrs = tupDesc->natts; /* Convert FORCE QUOTE name list to per-column flags, check validity */ *************** DoCopy(const CopyStmt *stmt, const char *** 1088,1098 **** int attnum = lfirst_int(cur); if (!list_member_int(cstate->attnumlist, attnum)) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", ! NameStr(tupDesc->attrs[attnum - 1]->attname)))); ! cstate->force_quote_flags[attnum - 1] = true; } } --- 1101,1136 ---- int attnum = lfirst_int(cur); if (!list_member_int(cstate->attnumlist, attnum)) + { + Form_pg_attribute attForm; + + if (SystemAttributeIsWritable(attnum)) + attForm = SystemAttributeDefinition(attnum, true); + else + attForm = tupDesc->attrs[attnum - 1]; + + Assert(attForm != NULL); + ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", ! NameStr(attForm->attname)))); ! } ! ! switch (attnum) ! { ! case SecurityAclAttributeNumber: ! cstate->rowacl_force_quot = true; ! break; ! ! case SecurityLabelAttributeNumber: ! cstate->seclabel_force_quot = true; ! break; ! ! default: ! cstate->force_quote_flags[attnum - 1] = true; ! break; ! } } } *************** DoCopy(const CopyStmt *stmt, const char *** 1110,1119 **** int attnum = lfirst_int(cur); if (!list_member_int(cstate->attnumlist, attnum)) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", ! NameStr(tupDesc->attrs[attnum - 1]->attname)))); cstate->force_notnull_flags[attnum - 1] = true; } } --- 1148,1171 ---- int attnum = lfirst_int(cur); if (!list_member_int(cstate->attnumlist, attnum)) + { + Form_pg_attribute attForm; + + if (SystemAttributeIsWritable(attnum)) + attForm = SystemAttributeDefinition(attnum, true); + else + attForm = tupDesc->attrs[attnum - 1]; + + Assert(attForm != NULL); + ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", ! NameStr(attForm->attname)))); ! } ! if (SystemAttributeIsWritable(attnum)) ! continue; /* ignore, if specified */ ! cstate->force_notnull_flags[attnum - 1] = true; } } *************** DoCopyTo(CopyState cstate) *** 1242,1247 **** --- 1294,1302 ---- ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is a directory", cstate->filename))); + + pgaceCopyFile(cstate->rel, fileno(cstate->copy_file), + cstate->filename, false); } PG_TRY(); *************** CopyTo(CopyState cstate) *** 1305,1320 **** int attnum = lfirst_int(cur); Oid out_func_oid; bool isvarlena; if (cstate->binary) ! getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid, &out_func_oid, &isvarlena); else ! getTypeOutputInfo(attr[attnum - 1]->atttypid, &out_func_oid, &isvarlena); ! fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); } /* --- 1360,1393 ---- int attnum = lfirst_int(cur); Oid out_func_oid; bool isvarlena; + FmgrInfo *out_fmgr; + Form_pg_attribute attForm; + + switch (attnum) + { + case SecurityAclAttributeNumber: + attForm = SystemAttributeDefinition(attnum, true); + out_fmgr = &cstate->rowacl_out_function; + break; + case SecurityLabelAttributeNumber: + attForm = SystemAttributeDefinition(attnum, true); + out_fmgr = &cstate->seclabel_out_function; + break; + default: /* user columns */ + attForm = attr[attnum - 1]; + out_fmgr = &cstate->out_functions[attnum - 1]; + break; + } if (cstate->binary) ! getTypeBinaryOutputInfo(attForm->atttypid, &out_func_oid, &isvarlena); else ! getTypeOutputInfo(attForm->atttypid, &out_func_oid, &isvarlena); ! fmgr_info(out_func_oid, out_fmgr); } /* *************** CopyTo(CopyState cstate) *** 1369,1375 **** CopySendChar(cstate, cstate->delim[0]); hdr_delim = true; ! colname = NameStr(attr[attnum - 1]->attname); CopyAttributeOutCSV(cstate, colname, false, list_length(cstate->attnumlist) == 1); --- 1442,1455 ---- CopySendChar(cstate, cstate->delim[0]); hdr_delim = true; ! if (SystemAttributeIsWritable(attnum)) ! { ! Form_pg_attribute attForm ! = SystemAttributeDefinition(attnum, true); ! colname = NameStr(attForm->attname); ! } ! else ! colname = NameStr(attr[attnum - 1]->attname); CopyAttributeOutCSV(cstate, colname, false, list_length(cstate->attnumlist) == 1); *************** CopyTo(CopyState cstate) *** 1395,1405 **** { CHECK_FOR_INTERRUPTS(); /* Deconstruct the tuple ... faster than repeated heap_getattr */ heap_deform_tuple(tuple, tupDesc, values, nulls); /* Format and send the data */ ! CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls); } heap_endscan(scandesc); --- 1475,1492 ---- { CHECK_FOR_INTERRUPTS(); + if (!pgaceCopyToTuple(cstate->rel, cstate->attnumlist, tuple)) + continue; + /* Deconstruct the tuple ... faster than repeated heap_getattr */ heap_deform_tuple(tuple, tupDesc, values, nulls); /* Format and send the data */ ! CopyOneRowTo(cstate, ! HeapTupleGetOid(tuple), ! HeapTupleGetRowAcl(tuple), ! HeapTupleGetSecLabel(tuple), ! values, nulls); } heap_endscan(scandesc); *************** CopyTo(CopyState cstate) *** 1425,1431 **** * Emit one row during CopyTo(). */ static void ! CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) { bool need_delim = false; FmgrInfo *out_functions = cstate->out_functions; --- 1512,1519 ---- * Emit one row during CopyTo(). */ static void ! CopyOneRowTo(CopyState cstate, Oid tupleOid, Oid rowAclId, Oid secLabelId, ! Datum *values, bool *nulls) { bool need_delim = false; FmgrInfo *out_functions = cstate->out_functions; *************** CopyOneRowTo(CopyState cstate, Oid tuple *** 1464,1471 **** foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); ! Datum value = values[attnum - 1]; ! bool isnull = nulls[attnum - 1]; if (!cstate->binary) { --- 1552,1561 ---- foreach(cur, cstate->attnumlist) { int attnum = lfirst_int(cur); ! Datum value; ! bool isnull; ! bool force_quot; ! FmgrInfo *out_fmgr; if (!cstate->binary) { *************** CopyOneRowTo(CopyState cstate, Oid tuple *** 1474,1479 **** --- 1564,1594 ---- need_delim = true; } + switch (attnum) + { + case SecurityAclAttributeNumber: + value = PointerGetDatum(rowaclSidToSecurityAcl(rowAclId, + RelationGetForm(cstate->rel)->relowner)); + isnull = false; + force_quot = cstate->rowacl_force_quot; + out_fmgr = &cstate->rowacl_out_function; + break; + + case SecurityLabelAttributeNumber: + value = CStringGetTextDatum(pgaceSidToSecurityLabel(secLabelId)); + isnull = false; + force_quot = cstate->seclabel_force_quot; + out_fmgr = &cstate->seclabel_out_function; + break; + + default: + value = values[attnum - 1]; + isnull = nulls[attnum - 1]; + force_quot = cstate->force_quote_flags[attnum - 1]; + out_fmgr = &out_functions[attnum - 1]; + break; + } + if (isnull) { if (!cstate->binary) *************** CopyOneRowTo(CopyState cstate, Oid tuple *** 1485,1495 **** { if (!cstate->binary) { ! string = OutputFunctionCall(&out_functions[attnum - 1], ! value); if (cstate->csv_mode) ! CopyAttributeOutCSV(cstate, string, ! cstate->force_quote_flags[attnum - 1], list_length(cstate->attnumlist) == 1); else CopyAttributeOutText(cstate, string); --- 1600,1608 ---- { if (!cstate->binary) { ! string = OutputFunctionCall(out_fmgr, value); if (cstate->csv_mode) ! CopyAttributeOutCSV(cstate, string, force_quot, list_length(cstate->attnumlist) == 1); else CopyAttributeOutText(cstate, string); *************** CopyOneRowTo(CopyState cstate, Oid tuple *** 1498,1505 **** { bytea *outputbytes; ! outputbytes = SendFunctionCall(&out_functions[attnum - 1], ! value); CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); CopySendData(cstate, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); --- 1611,1617 ---- { bytea *outputbytes; ! outputbytes = SendFunctionCall(out_fmgr, value); CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); CopySendData(cstate, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ); *************** CopyFrom(CopyState cstate) *** 1633,1642 **** --- 1745,1759 ---- num_defaults; FmgrInfo *in_functions; FmgrInfo oid_in_function; + FmgrInfo rowacl_in_function; + FmgrInfo seclabel_in_function; Oid *typioparams; Oid oid_typioparam; + Oid rowacl_typioparam; + Oid seclabel_typioparam; int attnum; int i; + ListCell *l; Oid in_func_oid; Datum *values; bool *nulls; *************** CopyFrom(CopyState cstate) *** 1737,1742 **** --- 1854,1862 ---- ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is a directory", cstate->filename))); + + pgaceCopyFile(cstate->rel, fileno(cstate->copy_file), + cstate->filename, true); } tupDesc = RelationGetDescr(cstate->rel); *************** CopyFrom(CopyState cstate) *** 1872,1877 **** --- 1992,2027 ---- fmgr_info(in_func_oid, &oid_in_function); } + foreach (l, cstate->attnumlist) + { + switch (lfirst_int(l)) + { + case SecurityAclAttributeNumber: + if (!cstate->binary) + getTypeInputInfo(ACLITEMARRAYOID, + &in_func_oid, + &rowacl_typioparam); + else + getTypeBinaryInputInfo(ACLITEMARRAYOID, + &in_func_oid, + &rowacl_typioparam); + fmgr_info(in_func_oid, &rowacl_in_function); + break; + + case 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); + break; + } + } + values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); nulls = (bool *) palloc(num_phys_attrs * sizeof(bool)); *************** CopyFrom(CopyState cstate) *** 1906,1911 **** --- 2056,2063 ---- { bool skip_tuple; Oid loaded_oid = InvalidOid; + Datum loaded_rowacl = PointerGetDatum(NULL); + Datum loaded_seclabel = PointerGetDatum(NULL); CHECK_FOR_INTERRUPTS(); *************** CopyFrom(CopyState cstate) *** 1980,1985 **** --- 2132,2176 ---- int attnum = lfirst_int(cur); int m = attnum - 1; + if (SystemAttributeIsWritable(attnum)) + { + Form_pg_attribute attForm + = SystemAttributeDefinition(attnum, true); + + if (fieldno >= fldct) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), + errmsg("missing data for column \"%s\"", + NameStr(attForm->attname)))); + string = field_strings[fieldno++]; + cstate->cur_attname = NameStr(attForm->attname); + cstate->cur_attval = string; + if (string) + { + switch (attnum) + { + case SecurityAclAttributeNumber: + loaded_rowacl + = InputFunctionCall(&rowacl_in_function, + string, + rowacl_typioparam, + attForm->atttypmod); + break; + + case SecurityLabelAttributeNumber: + loaded_seclabel + = InputFunctionCall(&seclabel_in_function, + string, + seclabel_typioparam, + attForm->atttypmod); + break; + } + } + cstate->cur_attname = NULL; + cstate->cur_attval = NULL; + continue; + } + if (fieldno >= fldct) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), *************** CopyFrom(CopyState cstate) *** 2050,2055 **** --- 2241,2281 ---- int attnum = lfirst_int(cur); int m = attnum - 1; + if (SystemAttributeIsWritable(attnum)) + { + Form_pg_attribute attForm + = SystemAttributeDefinition(attnum, false); + Datum tmp; + + cstate->cur_attname = NameStr(attForm->attname); + i++; + + switch (attnum) + { + case SecurityAclAttributeNumber: + tmp = CopyReadBinaryAttribute(cstate, i, + &rowacl_in_function, + rowacl_typioparam, + attForm->atttypmod, + &isnull); + if (!isnull) + loaded_seclabel = tmp; + break; + + case SecurityLabelAttributeNumber: + tmp = CopyReadBinaryAttribute(cstate, i, + &seclabel_in_function, + seclabel_typioparam, + attForm->atttypmod, + &isnull); + if (!isnull) + loaded_seclabel = tmp; + break; + } + cstate->cur_attname = NULL; + continue; + } + cstate->cur_attname = NameStr(attr[m]->attname); i++; values[m] = CopyReadBinaryAttribute(cstate, *************** CopyFrom(CopyState cstate) *** 2079,2084 **** --- 2305,2322 ---- if (cstate->oids && file_has_oids) HeapTupleSetOid(tuple, loaded_oid); + if (loaded_rowacl != PointerGetDatum(NULL)) + { + Acl *acl = DatumGetAclP(loaded_rowacl); + HeapTupleSetRowAcl(tuple, rowaclSecurityAclToSid(acl)); + } + + if (loaded_seclabel != PointerGetDatum(NULL)) + { + char *label = TextDatumGetCString(loaded_seclabel); + HeapTupleSetSecLabel(tuple, pgaceSecurityLabelToSid(label)); + } + /* Triggers and stuff need to be invoked in query context. */ MemoryContextSwitchTo(oldcontext); *************** CopyFrom(CopyState cstate) *** 2101,2106 **** --- 2339,2349 ---- } } + if (!skip_tuple && !rowaclHeapTupleInsert(cstate->rel, tuple, false, false)) + skip_tuple = true; + if (!skip_tuple && !pgaceHeapTupleInsert(cstate->rel, tuple, false, false)) + skip_tuple = true; + if (!skip_tuple) { /* Place tuple in tuple slot */ *************** CopyGetAttnums(TupleDesc tupDesc, Relati *** 3379,3384 **** --- 3622,3637 ---- break; } } + + /* Is it writable system column? */ + if (attnum == InvalidAttrNumber) + { + Form_pg_attribute attForm + = SystemAttributeByName(name, tupDesc->tdhasoid); + if (attForm && SystemAttributeIsWritable(attForm->attnum)) + attnum = attForm->attnum; + } + if (attnum == InvalidAttrNumber) { if (rel != NULL) *************** copy_dest_receive(TupleTableSlot *slot, *** 3428,3434 **** slot_getallattrs(slot); /* And send the data */ ! CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull); } /* --- 3681,3689 ---- slot_getallattrs(slot); /* And send the data */ ! CopyOneRowTo(cstate, ! InvalidOid, InvalidOid, InvalidOid, ! slot->tts_values, slot->tts_isnull); } /* diff -Nrpc base/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/dbcommands.c *** base/src/backend/commands/dbcommands.c Thu Jan 22 14:34:54 2009 --- sepgsql/src/backend/commands/dbcommands.c Thu Jan 22 14:41:23 2009 *************** *** 41,46 **** --- 41,47 ---- #include "miscadmin.h" #include "pgstat.h" #include "postmaster/bgwriter.h" + #include "security/pgace.h" #include "storage/bufmgr.h" #include "storage/fd.h" #include "storage/lmgr.h" *************** createdb(const CreatedbStmt *stmt) *** 119,124 **** --- 120,126 ---- DefElem *dcollate = NULL; DefElem *dctype = NULL; DefElem *dconnlimit = NULL; + DefElem *dpgace_item = NULL; char *dbname = stmt->dbname; char *dbowner = NULL; const char *dbtemplate = NULL; *************** createdb(const CreatedbStmt *stmt) *** 200,205 **** --- 202,214 ---- errmsg("LOCATION is not supported anymore"), errhint("Consider using tablespaces instead."))); } + else if (pgaceIsGramSecurityItem(defel)) { + if (dpgace_item) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dpgace_item = defel; + } else elog(ERROR, "option \"%s\" not recognized", defel->defname); *************** createdb(const CreatedbStmt *stmt) *** 532,537 **** --- 541,547 ---- new_record, new_record_nulls); HeapTupleSetOid(tuple, dboid); + pgaceGramCreateDatabase(pg_database_rel, tuple, dpgace_item); simple_heap_insert(pg_database_rel, tuple); *************** AlterDatabase(AlterDatabaseStmt *stmt, b *** 1278,1283 **** --- 1288,1294 ---- int connlimit = -1; DefElem *dconnlimit = NULL; DefElem *dtablespace = NULL; + DefElem *dpgace_item = NULL; Datum new_record[Natts_pg_database]; bool new_record_nulls[Natts_pg_database]; bool new_record_repl[Natts_pg_database]; *************** AlterDatabase(AlterDatabaseStmt *stmt, b *** 1303,1308 **** --- 1314,1327 ---- errmsg("conflicting or redundant options"))); dtablespace = defel; } + else if (pgaceIsGramSecurityItem(defel)) + { + if (dpgace_item) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + dpgace_item = defel; + } else elog(ERROR, "option \"%s\" not recognized", defel->defname); *************** AlterDatabase(AlterDatabaseStmt *stmt, b *** 1358,1363 **** --- 1377,1383 ---- newtuple = heap_modify_tuple(tuple, RelationGetDescr(rel), new_record, new_record_nulls, new_record_repl); + pgaceGramAlterDatabase(rel, newtuple, dpgace_item); simple_heap_update(rel, &tuple->t_self, newtuple); /* Update indexes */ diff -Nrpc base/src/backend/commands/functioncmds.c sepgsql/src/backend/commands/functioncmds.c *** base/src/backend/commands/functioncmds.c Tue Jan 6 14:45:31 2009 --- sepgsql/src/backend/commands/functioncmds.c Tue Jan 6 14:52:44 2009 *************** *** 53,58 **** --- 53,59 ---- #include "parser/parse_expr.h" #include "parser/parse_func.h" #include "parser/parse_type.h" + #include "security/pgace.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" *************** compute_attributes_sql_style(List *optio *** 517,523 **** bool *security_definer, ArrayType **proconfig, float4 *procost, ! float4 *prorows) { ListCell *option; DefElem *as_item = NULL; --- 518,525 ---- bool *security_definer, ArrayType **proconfig, float4 *procost, ! float4 *prorows, ! DefElem **pgaceItem) { ListCell *option; DefElem *as_item = NULL; *************** compute_attributes_sql_style(List *optio *** 558,563 **** --- 560,573 ---- errmsg("conflicting or redundant options"))); windowfunc_item = defel; } + else if (pgaceIsGramSecurityItem(defel)) + { + if (*pgaceItem) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + *pgaceItem = defel; + } else if (compute_common_attribute(defel, &volatility_item, &strict_item, *************** CreateFunction(CreateFunctionStmt *stmt, *** 765,770 **** --- 775,781 ---- HeapTuple languageTuple; Form_pg_language languageStruct; List *as_clause; + DefElem *pgaceItem = NULL; /* Convert list of names to a name and namespace */ namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname, *************** CreateFunction(CreateFunctionStmt *stmt, *** 790,796 **** &as_clause, &language, &isWindowFunc, &volatility, &isStrict, &security, ! &proconfig, &procost, &prorows); /* Convert language name to canonical case */ languageName = case_translate_language_name(language); --- 801,807 ---- &as_clause, &language, &isWindowFunc, &volatility, &isStrict, &security, ! &proconfig, &procost, &prorows, &pgaceItem); /* Convert language name to canonical case */ languageName = case_translate_language_name(language); *************** CreateFunction(CreateFunctionStmt *stmt, *** 926,932 **** parameterDefaults, PointerGetDatum(proconfig), procost, ! prorows); } --- 937,944 ---- parameterDefaults, PointerGetDatum(proconfig), procost, ! prorows, ! pgaceItem); } *************** AlterFunction(AlterFunctionStmt *stmt) *** 1276,1281 **** --- 1288,1294 ---- List *set_items = NIL; DefElem *cost_item = NULL; DefElem *rows_item = NULL; + DefElem *pgaceItem = NULL; rel = heap_open(ProcedureRelationId, RowExclusiveLock); *************** AlterFunction(AlterFunctionStmt *stmt) *** 1307,1312 **** --- 1320,1334 ---- { DefElem *defel = (DefElem *) lfirst(l); + if (pgaceIsGramSecurityItem(defel)) { + if (pgaceItem) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + pgaceItem = defel; + continue; + } + if (compute_common_attribute(defel, &volatility_item, &strict_item, *************** AlterFunction(AlterFunctionStmt *stmt) *** 1377,1382 **** --- 1399,1405 ---- tup = heap_modify_tuple(tup, RelationGetDescr(rel), repl_val, repl_null, repl_repl); } + pgaceGramAlterFunction(rel, tup, pgaceItem); /* Do the update */ simple_heap_update(rel, &tup->t_self, tup); diff -Nrpc base/src/backend/commands/lockcmds.c sepgsql/src/backend/commands/lockcmds.c *** base/src/backend/commands/lockcmds.c Tue Jan 13 09:22:28 2009 --- sepgsql/src/backend/commands/lockcmds.c Tue Jan 13 09:39:35 2009 *************** *** 20,25 **** --- 20,26 ---- #include "miscadmin.h" #include "optimizer/prep.h" #include "parser/parse_clause.h" + #include "security/pgace.h" #include "utils/acl.h" #include "utils/lsyscache.h" #include "utils/rel.h" *************** LockTableCommand(LockStmt *lockstmt) *** 71,76 **** --- 72,79 ---- aclcheck_error(aclresult, ACL_KIND_CLASS, get_rel_name(childreloid)); + pgaceLockTable(childreloid); + if (lockstmt->nowait) rel = relation_open_nowait(childreloid, lockstmt->mode); else diff -Nrpc base/src/backend/commands/proclang.c sepgsql/src/backend/commands/proclang.c *** base/src/backend/commands/proclang.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/commands/proclang.c Sat Jan 3 15:58:18 2009 *************** CreateProceduralLanguage(CreatePLangStmt *** 151,157 **** NIL, PointerGetDatum(NULL), 1, ! 0); } /* --- 151,158 ---- NIL, PointerGetDatum(NULL), 1, ! 0, ! NULL); } /* *************** CreateProceduralLanguage(CreatePLangStmt *** 186,192 **** NIL, PointerGetDatum(NULL), 1, ! 0); } } else --- 187,194 ---- NIL, PointerGetDatum(NULL), 1, ! 0, ! NULL); } } else diff -Nrpc base/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/tablecmds.c *** base/src/backend/commands/tablecmds.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/commands/tablecmds.c Fri Jan 23 10:55:35 2009 *************** *** 63,68 **** --- 63,69 ---- #include "parser/parser.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" + #include "security/pgace.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/smgr.h" *************** DefineRelation(CreateStmt *stmt, char re *** 509,515 **** parentOidCount, stmt->oncommit, reloptions, ! allowSystemTableMods); StoreCatalogInheritance(relationId, inheritOids); --- 510,517 ---- parentOidCount, stmt->oncommit, reloptions, ! allowSystemTableMods, ! pgaceRelationAttrList(stmt)); StoreCatalogInheritance(relationId, inheritOids); *************** ExecuteTruncate(TruncateStmt *stmt) *** 856,861 **** --- 858,865 ---- heap_truncate_check_FKs(rels, false); #endif + pgaceExecTruncate(rels); + /* * If we are asked to restart sequences, find all the sequences, * lock them (we only need AccessShareLock because that's all that *************** ATPrepCmd(List **wqueue, Relation rel, A *** 2491,2496 **** --- 2495,2501 ---- case AT_DisableRule: case AT_AddInherit: /* INHERIT / NO INHERIT */ case AT_DropInherit: + case AT_SetSecurityLabel: ATSimplePermissions(rel, false); /* These commands never recurse */ /* No command-specific prep needed */ *************** ATExecCmd(List **wqueue, AlteredTableInf *** 2718,2723 **** --- 2723,2731 ---- case AT_DropInherit: ATExecDropInherit(rel, (RangeVar *) cmd->def); break; + case AT_SetSecurityLabel: + pgaceAlterRelationCommon(rel, cmd); + break; default: /* oops */ elog(ERROR, "unrecognized alter table type: %d", (int) cmd->subtype); *************** ATRewriteTable(AlteredTableInfo *tab, Oi *** 3056,3066 **** --- 3064,3080 ---- if (newrel) { Oid tupOid = InvalidOid; + Oid tupRowAcl = InvalidOid; + Oid tupSecLabel = InvalidOid; /* Extract data from old tuple */ heap_deform_tuple(tuple, oldTupDesc, values, isnull); if (oldTupDesc->tdhasoid) tupOid = HeapTupleGetOid(tuple); + if (HeapTupleHasRowAcl(tuple)) + tupRowAcl = HeapTupleGetRowAcl(tuple); + if (HeapTupleHasSecLabel(tuple)) + tupSecLabel = HeapTupleGetSecLabel(tuple); /* Set dropped attributes to null in new tuple */ foreach(lc, dropped_attrs) *************** ATRewriteTable(AlteredTableInfo *tab, Oi *** 3092,3097 **** --- 3106,3117 ---- /* Preserve OID, if any */ if (newTupDesc->tdhasoid) HeapTupleSetOid(tuple, tupOid); + /* Preserve RowAcl, if any */ + if (HeapTupleHasRowAcl(tuple)) + HeapTupleSetRowAcl(tuple, tupRowAcl); + /* Preserve SecLabel, if any */ + if (HeapTupleHasSecLabel(tuple)) + HeapTupleSetSecLabel(tuple, tupSecLabel); } /* Now check any constraints on the possibly-changed tuple */ *************** ATExecAddColumn(AlteredTableInfo *tab, R *** 3585,3590 **** --- 3605,3611 ---- attribute.attndims = list_length(colDef->typename->arrayBounds); attribute.attstorage = tform->typstorage; attribute.attalign = tform->typalign; + attribute.attkind = relkind; attribute.attnotnull = colDef->is_not_null; attribute.atthasdef = false; attribute.attisdropped = false; *************** ATExecAddColumn(AlteredTableInfo *tab, R *** 3594,3600 **** ReleaseSysCache(typeTuple); ! InsertPgAttributeTuple(attrdesc, &attribute, NULL); heap_close(attrdesc, RowExclusiveLock); --- 3615,3621 ---- ReleaseSysCache(typeTuple); ! InsertPgAttributeTuple(attrdesc, &attribute, NULL, NIL); heap_close(attrdesc, RowExclusiveLock); diff -Nrpc base/src/backend/commands/trigger.c sepgsql/src/backend/commands/trigger.c *** base/src/backend/commands/trigger.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/commands/trigger.c Fri Jan 23 10:55:35 2009 *************** *** 33,38 **** --- 33,39 ---- #include "nodes/makefuncs.h" #include "parser/parse_func.h" #include "pgstat.h" + #include "security/pgace.h" #include "storage/bufmgr.h" #include "tcop/utility.h" #include "utils/acl.h" *************** ExecCallTriggerFunc(TriggerData *trigdat *** 1560,1569 **** --- 1561,1576 ---- * call. */ if (finfo->fn_oid == InvalidOid) + { fmgr_info(trigdata->tg_trigger->tgfoid, finfo); + pgaceCallFunction(finfo); + } Assert(finfo->fn_oid == trigdata->tg_trigger->tgfoid); + if (!pgaceCallTriggerFunction(trigdata)) + return (HeapTuple) NULL; + /* * If doing EXPLAIN ANALYZE, start charging time to this trigger. */ *************** ExecBRUpdateTriggers(EState *estate, Res *** 1984,1989 **** --- 1991,2014 ---- if (newSlot != NULL) intuple = newtuple = ExecRemoveJunk(estate->es_junkFilter, newSlot); + /* + * The before-row-triggers are fired prior to transcribing system + * attributes from the old tuple to the new one. When no explicit + * new values are given, we have to preserve them, so the following + * code do it to avoid to make triggers get confusion. + */ + if (HeapTupleHasOid(newtuple) && + !OidIsValid(HeapTupleGetOid(newtuple))) + HeapTupleSetOid(newtuple, HeapTupleGetOid(trigtuple)); + + if (HeapTupleHasRowAcl(newtuple) && + !OidIsValid(HeapTupleGetRowAcl(newtuple))) + HeapTupleSetRowAcl(newtuple, HeapTupleGetRowAcl(trigtuple)); + + if (HeapTupleHasSecLabel(newtuple) && + !OidIsValid(HeapTupleGetSecLabel(newtuple))) + HeapTupleSetSecLabel(newtuple, HeapTupleGetSecLabel(trigtuple)); + LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_ROW | diff -Nrpc base/src/backend/executor/execJunk.c sepgsql/src/backend/executor/execJunk.c *** base/src/backend/executor/execJunk.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/executor/execJunk.c Sat Jan 3 15:58:18 2009 *************** *** 60,66 **** * An optional resultSlot can be passed as well. */ JunkFilter * ! ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) { JunkFilter *junkfilter; TupleDesc cleanTupType; --- 60,68 ---- * An optional resultSlot can be passed as well. */ JunkFilter * ! ExecInitJunkFilter(List *targetList, ! bool hasoid, bool hassecacl, bool hasseclabel, ! TupleTableSlot *slot) { JunkFilter *junkfilter; TupleDesc cleanTupType; *************** ExecInitJunkFilter(List *targetList, boo *** 72,78 **** /* * Compute the tuple descriptor for the cleaned tuple. */ ! cleanTupType = ExecCleanTypeFromTL(targetList, hasoid); /* * Use the given slot, or make a new slot if we weren't given one. --- 74,80 ---- /* * Compute the tuple descriptor for the cleaned tuple. */ ! cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, hassecacl, hasseclabel); /* * Use the given slot, or make a new slot if we weren't given one. diff -Nrpc base/src/backend/executor/execMain.c sepgsql/src/backend/executor/execMain.c *** base/src/backend/executor/execMain.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/executor/execMain.c Fri Jan 23 10:55:35 2009 *************** *** 50,55 **** --- 50,56 ---- #include "optimizer/clauses.h" #include "parser/parse_clause.h" #include "parser/parsetree.h" + #include "security/pgace.h" #include "storage/bufmgr.h" #include "storage/lmgr.h" #include "storage/smgr.h" *************** standard_ExecutorStart(QueryDesc *queryD *** 158,163 **** --- 159,166 ---- Assert(queryDesc != NULL); Assert(queryDesc->estate == NULL); + pgaceExecutorStart(queryDesc, eflags); + /* * If the transaction is read-only, we need to check if any writes are * planned to non-temporary tables. EXPLAIN is considered read-only. *************** InitPlan(QueryDesc *queryDesc, int eflag *** 901,916 **** for (i = 0; i < as_nplans; i++) { PlanState *subplan = appendplans[i]; JunkFilter *j; if (operation == CMD_UPDATE) ! ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, ! subplan->plan->targetlist); j = ExecInitJunkFilter(subplan->plan->targetlist, ! resultRelInfo->ri_RelationDesc->rd_att->tdhasoid, ! ExecAllocTableSlot(estate->es_tupleTable)); ! /* * Since it must be UPDATE/DELETE, there had better be a * "ctid" junk attribute in the tlist ... but ctid could --- 904,920 ---- for (i = 0; i < as_nplans; i++) { PlanState *subplan = appendplans[i]; + Relation resultRel = resultRelInfo->ri_RelationDesc; JunkFilter *j; if (operation == CMD_UPDATE) ! ExecCheckPlanOutput(resultRel, subplan->plan->targetlist); j = ExecInitJunkFilter(subplan->plan->targetlist, ! RelationGetDescr(resultRel)->tdhasoid, ! RelationGetDescr(resultRel)->tdhasrowacl, ! RelationGetDescr(resultRel)->tdhasseclabel, ! ExecAllocTableSlot(estate->es_tupleTable)); /* * Since it must be UPDATE/DELETE, there had better be a * "ctid" junk attribute in the tlist ... but ctid could *************** InitPlan(QueryDesc *queryDesc, int eflag *** 952,959 **** planstate->plan->targetlist); j = ExecInitJunkFilter(planstate->plan->targetlist, ! tupType->tdhasoid, ! ExecAllocTableSlot(estate->es_tupleTable)); estate->es_junkFilter = j; if (estate->es_result_relation_info) estate->es_result_relation_info->ri_junkFilter = j; --- 956,963 ---- planstate->plan->targetlist); j = ExecInitJunkFilter(planstate->plan->targetlist, ! tupType->tdhasoid, tupType->tdhasrowacl, tupType->tdhasseclabel, ! ExecAllocTableSlot(estate->es_tupleTable)); estate->es_junkFilter = j; if (estate->es_result_relation_info) estate->es_result_relation_info->ri_junkFilter = j; *************** InitPlan(QueryDesc *queryDesc, int eflag *** 1023,1029 **** * We assume all the sublists will generate the same output tupdesc. */ tupType = ExecTypeFromTL((List *) linitial(plannedstmt->returningLists), ! false); /* Set up a slot for the output of the RETURNING projection(s) */ slot = ExecAllocTableSlot(estate->es_tupleTable); --- 1027,1033 ---- * We assume all the sublists will generate the same output tupdesc. */ tupType = ExecTypeFromTL((List *) linitial(plannedstmt->returningLists), ! false, false, false); /* Set up a slot for the output of the RETURNING projection(s) */ slot = ExecAllocTableSlot(estate->es_tupleTable); *************** ExecContextForcesOids(PlanState *plansta *** 1346,1351 **** --- 1350,1417 ---- return false; } + /* + * ExecContextForcesRowAcl + * + * We need to ensure that result tuples have space for row level ACLs. + * If row_level_acl = true on the given relation, it should be allocated. + */ + bool ExecContextForcesRowAcl(PlanState *planstate, bool *hasrowacl) + { + if (planstate->state->es_select_into) + { + IntoClause *into = planstate->state->es_plannedstmt->intoClause; + + Assert(into != NULL); + + *hasrowacl = pgaceTupleDescHasRowAcl(NULL, into->options); + return true; + } + else + { + ResultRelInfo *ri = planstate->state->es_result_relation_info; + + if (ri && ri->ri_RelationDesc) + { + *hasrowacl = pgaceTupleDescHasRowAcl(ri->ri_RelationDesc, NIL); + return true; + } + } + + return false; + } + + /* + * ExecContextForcesSecLabel + * + * We need to ensure that result tuples have space for security label, + * if the security feature need to store it within the given relation. + */ + bool ExecContextForcesSecLabel(PlanState *planstate, bool *hassecurity) + { + if (planstate->state->es_select_into) + { + IntoClause *into = planstate->state->es_plannedstmt->intoClause; + + Assert(into != NULL); + + *hassecurity = pgaceTupleDescHasSecLabel(NULL, into->options); + return true; + } + else + { + ResultRelInfo *ri = planstate->state->es_result_relation_info; + + if (ri && ri->ri_RelationDesc) + { + *hassecurity = pgaceTupleDescHasSecLabel(ri->ri_RelationDesc, NIL); + return true; + } + } + + return false; + } + /* ---------------------------------------------------------------- * ExecEndPlan * *************** ExecEndPlan(PlanState *planstate, EState *** 1426,1431 **** --- 1492,1581 ---- } } + /* + * fetchWritableSystemAttribute() fetches writable system column data + * using Junkfilter, and saves them at TupleTableSlot temporary. + * + * storeWritableSystemAttribute() copies these fetched data into + * header structure of HeapTuple. + */ + static void + fetchWritableSystemAttribute(JunkFilter *junkfilter, TupleTableSlot *slot, + Datum *tts_rowacl, Datum *tts_seclabel) + { + AttrNumber attno; + Datum datum; + bool isnull; + + /* for Row-level ACLs */ + attno = ExecFindJunkAttribute(junkfilter, SecurityAclAttributeName); + if (attno != InvalidAttrNumber) + { + datum = ExecGetJunkAttribute(slot, attno, &isnull); + if (isnull) + ereport(ERROR, + (errcode(ERRCODE_ROWACL_ERROR), + errmsg("setting NULL on \"%s\" system column is not supported", + SecurityAclAttributeName))); + *tts_rowacl = datum; + } + + /* for Security Label */ + attno = ExecFindJunkAttribute(junkfilter, SecurityLabelAttributeName); + if (attno != InvalidAttrNumber) + { + datum = ExecGetJunkAttribute(slot, attno, &isnull); + if (isnull) + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("setting NULL on \"%s\" system column is not supported", + SecurityLabelAttributeName))); + *tts_seclabel = datum; + } + } + + static void + storeWritableSystemAttribute(Relation rel, TupleTableSlot *slot, HeapTuple tuple) + { + /* for Row-level ACLs */ + if (HeapTupleHasRowAcl(tuple)) + { + if (!DatumGetPointer(slot->tts_rowacl)) + HeapTupleSetRowAcl(tuple, InvalidOid); + else + { + Acl *acl = DatumGetAclP(slot->tts_rowacl); + HeapTupleSetRowAcl(tuple, rowaclSecurityAclToSid(acl)); + } + } + else if (DatumGetPointer(slot->tts_rowacl)) + { + ereport(ERROR, + (errcode(ERRCODE_ROWACL_ERROR), + errmsg("Row-level ACLs are unavailable for relation: %s", + RelationGetRelationName(rel)))); + } + + /* for Security Label */ + if (HeapTupleHasSecLabel(tuple)) + { + if (!DatumGetPointer(slot->tts_seclabel)) + HeapTupleSetSecLabel(tuple, InvalidOid); + else + { + char *label = TextDatumGetCString(slot->tts_seclabel); + HeapTupleSetSecLabel(tuple, pgaceSecurityLabelToSid(label)); + } + } + else if (DatumGetPointer(slot->tts_seclabel)) + { + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("Security Label is unavailable for relation: %s", + RelationGetRelationName(rel)))); + } + } + /* ---------------------------------------------------------------- * ExecutePlan * *************** ExecutePlan(EState *estate, *** 1487,1492 **** --- 1637,1645 ---- */ for (;;) { + Datum tts_rowacl = PointerGetDatum(NULL); + Datum tts_seclabel = PointerGetDatum(NULL); + /* Reset the per-output-tuple exprcontext */ ResetPerTupleExprContext(estate); *************** lnext: ; *** 1631,1636 **** --- 1784,1795 ---- } /* + * extract writable system attribute + */ + fetchWritableSystemAttribute(junkfilter, slot, + &tts_rowacl, &tts_seclabel); + + /* * extract the 'ctid' junk attribute. */ if (operation == CMD_UPDATE || operation == CMD_DELETE) *************** lnext: ; *** 1657,1662 **** --- 1816,1823 ---- if (operation != CMD_DELETE) slot = ExecFilterJunk(junkfilter, slot); } + slot->tts_rowacl = tts_rowacl; + slot->tts_seclabel = tts_seclabel; /* * now that we have a tuple, do the appropriate thing with it.. either *************** ExecInsert(TupleTableSlot *slot, *** 1766,1771 **** --- 1927,1934 ---- resultRelInfo = estate->es_result_relation_info; resultRelationDesc = resultRelInfo->ri_RelationDesc; + storeWritableSystemAttribute(resultRelationDesc, slot, tuple); + /* BEFORE ROW INSERT Triggers */ if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) *************** ExecInsert(TupleTableSlot *slot, *** 1802,1807 **** --- 1965,1977 ---- ExecConstraints(resultRelInfo, slot, estate); /* + * Mandatory access controls of the tuple + */ + if (!pgaceHeapTupleInsert(resultRelationDesc, tuple, + false, !!resultRelInfo->ri_projectReturning)) + return; + + /* * insert the tuple * * Note: heap_insert returns the tid (location) of the new tuple in the *************** ExecDelete(ItemPointer tupleid, *** 1867,1872 **** --- 2037,2046 ---- return; } + if (!pgaceHeapTupleDelete(resultRelationDesc, tupleid, + false, !!resultRelInfo->ri_projectReturning)) + return; + /* * delete the tuple * *************** ExecUpdate(TupleTableSlot *slot, *** 2003,2008 **** --- 2177,2184 ---- resultRelInfo = estate->es_result_relation_info; resultRelationDesc = resultRelInfo->ri_RelationDesc; + storeWritableSystemAttribute(resultRelationDesc, slot, tuple); + /* BEFORE ROW UPDATE Triggers */ if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) *************** lreplace:; *** 2047,2052 **** --- 2223,2235 ---- ExecConstraints(resultRelInfo, slot, estate); /* + * Mandatory access controls of the tuple + */ + if (!pgaceHeapTupleUpdate(resultRelationDesc, tupleid, tuple, + false, !!resultRelInfo->ri_projectReturning)) + return; + + /* * replace the heap tuple * * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check that *************** OpenIntoRel(QueryDesc *queryDesc) *** 2911,2917 **** 0, into->onCommit, reloptions, ! allowSystemTableMods); FreeTupleDesc(tupdesc); --- 3094,3101 ---- 0, into->onCommit, reloptions, ! allowSystemTableMods, ! NIL); FreeTupleDesc(tupdesc); *************** intorel_receive(TupleTableSlot *slot, De *** 3021,3026 **** --- 3205,3217 ---- */ tuple = ExecMaterializeSlot(slot); + storeWritableSystemAttribute(myState->rel, slot, tuple); + if (!pgaceHeapTupleInsert(myState->rel, tuple, false, false)) + { + heap_freetuple(tuple); + return; + } + heap_insert(myState->rel, tuple, myState->estate->es_output_cid, diff -Nrpc base/src/backend/executor/execQual.c sepgsql/src/backend/executor/execQual.c *** base/src/backend/executor/execQual.c Tue Jan 13 09:22:28 2009 --- sepgsql/src/backend/executor/execQual.c Fri Jan 16 17:05:27 2009 *************** *** 47,52 **** --- 47,53 ---- #include "nodes/nodeFuncs.h" #include "optimizer/planner.h" #include "pgstat.h" + #include "security/pgace.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" *************** init_fcache(Oid foid, FuncExprState *fca *** 1047,1052 **** --- 1048,1054 ---- /* Set up the primary fmgr lookup information */ fmgr_info_cxt(foid, &(fcache->func), fcacheCxt); fcache->func.fn_expr = (Node *) fcache->xprstate.expr; + pgaceCallFunction(&fcache->func); /* If function returns set, prepare expected tuple descriptor */ if (fcache->func.fn_retset && needDescForSets) *************** ExecEvalArrayCoerceExpr(ArrayCoerceExprS *** 4014,4019 **** --- 4016,4023 ---- /* Initialize additional info */ astate->elemfunc.fn_expr = (Node *) acoerce; + + pgaceCallFunction(&astate->elemfunc); } /* diff -Nrpc base/src/backend/executor/execScan.c sepgsql/src/backend/executor/execScan.c *** base/src/backend/executor/execScan.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/executor/execScan.c Sat Jan 3 15:58:18 2009 *************** *** 20,25 **** --- 20,26 ---- #include "executor/executor.h" #include "miscadmin.h" + #include "security/pgace.h" #include "utils/memutils.h" *************** TupleTableSlot * *** 48,54 **** ExecScan(ScanState *node, ExecScanAccessMtd accessMtd) /* function returning a tuple */ { ! ExprContext *econtext; List *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; --- 49,55 ---- ExecScan(ScanState *node, ExecScanAccessMtd accessMtd) /* function returning a tuple */ { ! ExprContext *econtext = node->ps.ps_ExprContext; List *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; *************** ExecScan(ScanState *node, *** 65,71 **** * all the overhead and return the raw scan tuple. */ if (!qual && !projInfo) ! return (*accessMtd) (node); /* * Check to see if we're still projecting out tuples from a previous scan --- 66,87 ---- * all the overhead and return the raw scan tuple. */ if (!qual && !projInfo) ! { ! while (true) ! { ! resultSlot = (*accessMtd) (node); ! ! if (TupIsNull(resultSlot)) ! break; ! ! if (pgaceExecScan((Scan *)node->ps.plan, ! node->ss_currentRelation, resultSlot)) ! break; ! ! ResetExprContext(econtext); ! } ! return resultSlot; ! } /* * Check to see if we're still projecting out tuples from a previous scan *************** ExecScan(ScanState *node, *** 87,93 **** * storage allocated in the previous tuple cycle. Note this can't happen * until we're done projecting out tuples from a scan tuple. */ - econtext = node->ps.ps_ExprContext; ResetExprContext(econtext); /* --- 103,108 ---- *************** ExecScan(ScanState *node, *** 127,134 **** * check for non-nil qual here to avoid a function call to ExecQual() * when the qual is nil ... saves only a few cycles, but they add up * ... */ ! if (!qual || ExecQual(qual, econtext, false)) { /* * Found a satisfactory scan tuple. --- 142,152 ---- * check for non-nil qual here to avoid a function call to ExecQual() * when the qual is nil ... saves only a few cycles, but they add up * ... + * And security check for tuple level access controls at the last. */ ! if ((!qual || ExecQual(qual, econtext, false)) ! && pgaceExecScan((Scan *)node->ps.plan, ! node->ss_currentRelation, slot)) { /* * Found a satisfactory scan tuple. *************** tlist_matches_tupdesc(PlanState *ps, Lis *** 197,202 **** --- 215,222 ---- int numattrs = tupdesc->natts; int attrno; bool hasoid; + bool hasrowacl; + bool hasseclabel; ListCell *tlist_item = list_head(tlist); /* Check the tlist attributes */ *************** tlist_matches_tupdesc(PlanState *ps, Lis *** 240,251 **** return false; /* tlist too long */ /* ! * If the plan context requires a particular hasoid setting, then that has ! * to match, too. */ if (ExecContextForcesOids(ps, &hasoid) && hasoid != tupdesc->tdhasoid) return false; return true; } --- 260,279 ---- return false; /* tlist too long */ /* ! * If the plan context requires a particular hasoid/hassecurity setting, ! * then they have to match, too. */ if (ExecContextForcesOids(ps, &hasoid) && hasoid != tupdesc->tdhasoid) return false; + if (ExecContextForcesRowAcl(ps, &hasrowacl) && + hasrowacl != tupdesc->tdhasrowacl) + return false; + + if (ExecContextForcesSecLabel(ps, &hasseclabel) && + hasseclabel != tupdesc->tdhasseclabel) + return false; + return true; } diff -Nrpc base/src/backend/executor/execTuples.c sepgsql/src/backend/executor/execTuples.c *** base/src/backend/executor/execTuples.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/executor/execTuples.c Sat Jan 3 15:58:18 2009 *************** *** 99,106 **** #include "utils/typcache.h" ! static TupleDesc ExecTypeFromTLInternal(List *targetList, ! bool hasoid, bool skipjunk); /* ---------------------------------------------------------------- --- 99,106 ---- #include "utils/typcache.h" ! static TupleDesc ExecTypeFromTLInternal(List *targetList, bool hasoid, ! bool hasrowacl, bool hasseclabel, bool skipjunk); /* ---------------------------------------------------------------- *************** ExecInitNullTupleSlot(EState *estate, Tu *** 948,956 **** * ---------------------------------------------------------------- */ TupleDesc ! ExecTypeFromTL(List *targetList, bool hasoid) { ! return ExecTypeFromTLInternal(targetList, hasoid, false); } /* ---------------------------------------------------------------- --- 948,957 ---- * ---------------------------------------------------------------- */ TupleDesc ! ExecTypeFromTL(List *targetList, bool hasoid, bool hasrowacl, bool hasseclabel) { ! return ExecTypeFromTLInternal(targetList, ! hasoid, hasrowacl, hasseclabel, false); } /* ---------------------------------------------------------------- *************** ExecTypeFromTL(List *targetList, bool ha *** 960,972 **** * ---------------------------------------------------------------- */ TupleDesc ! ExecCleanTypeFromTL(List *targetList, bool hasoid) { ! return ExecTypeFromTLInternal(targetList, hasoid, true); } static TupleDesc ! ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) { TupleDesc typeInfo; ListCell *l; --- 961,975 ---- * ---------------------------------------------------------------- */ TupleDesc ! ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hasrowacl, bool hasseclabel) { ! return ExecTypeFromTLInternal(targetList, ! hasoid, hasrowacl, hasseclabel, true); } static TupleDesc ! ExecTypeFromTLInternal(List *targetList, ! bool hasoid, bool hasrowacl, bool hasseclabel, bool skipjunk) { TupleDesc typeInfo; ListCell *l; *************** ExecTypeFromTLInternal(List *targetList, *** 978,983 **** --- 981,988 ---- else len = ExecTargetListLength(targetList); typeInfo = CreateTemplateTupleDesc(len, hasoid); + typeInfo->tdhasrowacl = hasrowacl; + typeInfo->tdhasseclabel = hasseclabel; foreach(l, targetList) { diff -Nrpc base/src/backend/executor/execUtils.c sepgsql/src/backend/executor/execUtils.c *** base/src/backend/executor/execUtils.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/executor/execUtils.c Sat Jan 3 15:58:18 2009 *************** void *** 504,509 **** --- 504,511 ---- ExecAssignResultTypeFromTL(PlanState *planstate) { bool hasoid; + bool hassecacl; + bool hasseclabel; TupleDesc tupDesc; if (ExecContextForcesOids(planstate, &hasoid)) *************** ExecAssignResultTypeFromTL(PlanState *pl *** 516,527 **** hasoid = false; } /* * ExecTypeFromTL needs the parse-time representation of the tlist, not a * list of ExprStates. This is good because some plan nodes don't bother * to set up planstate->targetlist ... */ ! tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid); ExecAssignResultType(planstate, tupDesc); } --- 518,535 ---- hasoid = false; } + if (!ExecContextForcesRowAcl(planstate, &hassecacl)) + hassecacl = false; + if (!ExecContextForcesSecLabel(planstate, &hasseclabel)) + hasseclabel = false; + /* * ExecTypeFromTL needs the parse-time representation of the tlist, not a * list of ExprStates. This is good because some plan nodes don't bother * to set up planstate->targetlist ... */ ! tupDesc = ExecTypeFromTL(planstate->plan->targetlist, ! hasoid, hassecacl, hasseclabel); ExecAssignResultType(planstate, tupDesc); } diff -Nrpc base/src/backend/executor/functions.c sepgsql/src/backend/executor/functions.c *** base/src/backend/executor/functions.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/executor/functions.c Sat Jan 3 15:58:18 2009 *************** check_sql_fn_retval(Oid func_id, Oid ret *** 1134,1140 **** /* Set up junk filter if needed */ if (junkFilter) ! *junkFilter = ExecInitJunkFilter(tlist, false, NULL); } else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) { --- 1134,1140 ---- /* Set up junk filter if needed */ if (junkFilter) ! *junkFilter = ExecInitJunkFilter(tlist, false, false, false, NULL); } else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) { *************** check_sql_fn_retval(Oid func_id, Oid ret *** 1166,1172 **** COERCE_DONTCARE); /* Set up junk filter if needed */ if (junkFilter) ! *junkFilter = ExecInitJunkFilter(tlist, false, NULL); return false; /* NOT returning whole tuple */ } } --- 1166,1172 ---- COERCE_DONTCARE); /* Set up junk filter if needed */ if (junkFilter) ! *junkFilter = ExecInitJunkFilter(tlist, false, false, false, NULL); return false; /* NOT returning whole tuple */ } } *************** check_sql_fn_retval(Oid func_id, Oid ret *** 1179,1185 **** * what the caller expects will happen at runtime. */ if (junkFilter) ! *junkFilter = ExecInitJunkFilter(tlist, false, NULL); return true; } Assert(tupdesc); --- 1179,1185 ---- * what the caller expects will happen at runtime. */ if (junkFilter) ! *junkFilter = ExecInitJunkFilter(tlist, false, false, false, NULL); return true; } Assert(tupdesc); diff -Nrpc base/src/backend/executor/nodeAgg.c sepgsql/src/backend/executor/nodeAgg.c *** base/src/backend/executor/nodeAgg.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/executor/nodeAgg.c Fri Jan 16 17:05:27 2009 *************** *** 79,84 **** --- 79,85 ---- #include "parser/parse_agg.h" #include "parser/parse_coerce.h" #include "parser/parse_oper.h" + #include "security/pgace.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/lsyscache.h" *************** ExecInitAgg(Agg *node, EState *estate, i *** 1430,1435 **** --- 1431,1438 ---- aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(aggref->aggfnoid)); + pgaceCallAggFunction(aggTuple); + peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn; peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn; *************** ExecInitAgg(Agg *node, EState *estate, i *** 1493,1503 **** --- 1496,1508 ---- fmgr_info(transfn_oid, &peraggstate->transfn); peraggstate->transfn.fn_expr = (Node *) transfnexpr; + pgaceCallFunction(&peraggstate->transfn); if (OidIsValid(finalfn_oid)) { fmgr_info(finalfn_oid, &peraggstate->finalfn); peraggstate->finalfn.fn_expr = (Node *) finalfnexpr; + pgaceCallFunction(&peraggstate->finalfn); } get_typlenbyval(aggref->aggtype, diff -Nrpc base/src/backend/executor/nodeMergejoin.c sepgsql/src/backend/executor/nodeMergejoin.c *** base/src/backend/executor/nodeMergejoin.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/executor/nodeMergejoin.c Fri Jan 16 17:05:27 2009 *************** *** 98,103 **** --- 98,104 ---- #include "executor/execdefs.h" #include "executor/nodeMergejoin.h" #include "miscadmin.h" + #include "security/pgace.h" #include "utils/acl.h" #include "utils/lsyscache.h" #include "utils/memutils.h" *************** MJExamineQuals(List *mergeclauses, *** 218,223 **** --- 219,225 ---- /* Set up the fmgr lookup information */ fmgr_info(cmpproc, &(clause->cmpfinfo)); + pgaceCallFunction(&clause->cmpfinfo); /* Fill the additional comparison-strategy flags */ if (opstrategy == BTLessStrategyNumber) diff -Nrpc base/src/backend/executor/nodeSubplan.c sepgsql/src/backend/executor/nodeSubplan.c *** base/src/backend/executor/nodeSubplan.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/executor/nodeSubplan.c Sat Jan 3 15:58:18 2009 *************** ExecInitSubPlan(SubPlan *subplan, PlanSt *** 869,875 **** * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. */ ! tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecAllocTableSlot(tupTable); ExecSetSlotDescriptor(slot, tupDesc); sstate->projLeft = ExecBuildProjectionInfo(lefttlist, --- 869,875 ---- * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. */ ! tupDesc = ExecTypeFromTL(leftptlist, false, false, false); slot = ExecAllocTableSlot(tupTable); ExecSetSlotDescriptor(slot, tupDesc); sstate->projLeft = ExecBuildProjectionInfo(lefttlist, *************** ExecInitSubPlan(SubPlan *subplan, PlanSt *** 877,883 **** slot, NULL); ! tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecAllocTableSlot(tupTable); ExecSetSlotDescriptor(slot, tupDesc); sstate->projRight = ExecBuildProjectionInfo(righttlist, --- 877,883 ---- slot, NULL); ! tupDesc = ExecTypeFromTL(rightptlist, false, false, false); slot = ExecAllocTableSlot(tupTable); ExecSetSlotDescriptor(slot, tupDesc); sstate->projRight = ExecBuildProjectionInfo(righttlist, diff -Nrpc base/src/backend/executor/nodeWindowAgg.c sepgsql/src/backend/executor/nodeWindowAgg.c *** base/src/backend/executor/nodeWindowAgg.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/executor/nodeWindowAgg.c Fri Jan 16 17:05:27 2009 *************** *** 43,48 **** --- 43,49 ---- #include "optimizer/clauses.h" #include "parser/parse_agg.h" #include "parser/parse_coerce.h" + #include "security/pgace.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/datum.h" *************** ExecInitWindowAgg(WindowAgg *node, EStat *** 1231,1236 **** --- 1232,1238 ---- fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo, tmpcontext->ecxt_per_query_memory); perfuncstate->flinfo.fn_expr = (Node *) wfunc; + pgaceCallFunction(&perfuncstate->flinfo); get_typlenbyval(wfunc->wintype, &perfuncstate->resulttypeLen, &perfuncstate->resulttypeByVal); *************** initialize_peragg(WindowAggState *winsta *** 1457,1467 **** --- 1459,1471 ---- fmgr_info(transfn_oid, &peraggstate->transfn); peraggstate->transfn.fn_expr = (Node *) transfnexpr; + pgaceCallFunction(&peraggstate->transfn); if (OidIsValid(finalfn_oid)) { fmgr_info(finalfn_oid, &peraggstate->finalfn); peraggstate->finalfn.fn_expr = (Node *) finalfnexpr; + pgaceCallFunction(&peraggstate->finalfn); } get_typlenbyval(wfunc->wintype, diff -Nrpc base/src/backend/executor/spi.c sepgsql/src/backend/executor/spi.c *** base/src/backend/executor/spi.c Thu Jan 22 14:34:54 2009 --- sepgsql/src/backend/executor/spi.c Thu Jan 22 14:41:23 2009 *************** SPI_modifytuple(Relation rel, HeapTuple *** 705,710 **** --- 705,714 ---- mtuple->t_tableOid = tuple->t_tableOid; if (rel->rd_att->tdhasoid) HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple)); + if (HeapTupleHasRowAcl(mtuple)) + HeapTupleSetRowAcl(mtuple, HeapTupleGetRowAcl(tuple)); + if (HeapTupleHasSecLabel(mtuple)) + HeapTupleSetSecLabel(mtuple, HeapTupleGetSecLabel(tuple)); } else { diff -Nrpc base/src/backend/libpq/be-fsstubs.c sepgsql/src/backend/libpq/be-fsstubs.c *** base/src/backend/libpq/be-fsstubs.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/libpq/be-fsstubs.c Sat Jan 3 15:58:18 2009 *************** *** 45,50 **** --- 45,51 ---- #include "libpq/be-fsstubs.h" #include "libpq/libpq-fs.h" #include "miscadmin.h" + #include "security/pgace.h" #include "storage/fd.h" #include "storage/large_object.h" #include "utils/builtins.h" *************** lo_read(int fd, char *buf, int len) *** 156,161 **** --- 157,164 ---- (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("invalid large-object descriptor: %d", fd))); + pgaceLargeObjectRead(cookies[fd], len); + status = inv_read(cookies[fd], buf, len); return status; *************** lo_write(int fd, const char *buf, int le *** 177,182 **** --- 180,187 ---- errmsg("large object descriptor %d was not opened for writing", fd))); + pgaceLargeObjectWrite(cookies[fd], len); + status = inv_write(cookies[fd], buf, len); return status; *************** lo_import_internal(text *filename, Oid l *** 377,382 **** --- 382,392 ---- oid = inv_create(lobjOid); /* + * check permission to import a file into this object + */ + pgaceLargeObjectImport(oid, FileRawDescriptor(fd), fnamebuf); + + /* * read in from the filesystem and write to the inversion object */ lobj = inv_open(oid, INV_WRITE, fscxt); *************** lo_export(PG_FUNCTION_ARGS) *** 447,452 **** --- 457,466 ---- (errcode_for_file_access(), errmsg("could not create server file \"%s\": %m", fnamebuf))); + /* + * check permission to export this object into a file + */ + pgaceLargeObjectExport(lobjId, FileRawDescriptor(fd), fnamebuf); /* * read in from the inversion file and write to the filesystem *************** lo_truncate(PG_FUNCTION_ARGS) *** 482,487 **** --- 496,503 ---- (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("invalid large-object descriptor: %d", fd))); + pgaceLargeObjectTruncate(cookies[fd], len); + inv_truncate(cookies[fd], len); PG_RETURN_INT32(0); diff -Nrpc base/src/backend/nodes/copyfuncs.c sepgsql/src/backend/nodes/copyfuncs.c *** base/src/backend/nodes/copyfuncs.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/nodes/copyfuncs.c Fri Jan 23 10:55:35 2009 *************** *** 24,29 **** --- 24,30 ---- #include "nodes/plannodes.h" #include "nodes/relation.h" + #include "nodes/security.h" #include "utils/datum.h" *************** _copyPlannedStmt(PlannedStmt *from) *** 90,95 **** --- 91,97 ---- COPY_NODE_FIELD(relationOids); COPY_NODE_FIELD(invalItems); COPY_SCALAR_FIELD(nParamExec); + COPY_NODE_FIELD(pgaceItem); return newnode; } *************** CopyScanFields(Scan *from, Scan *newnode *** 259,264 **** --- 261,267 ---- CopyPlanFields((Plan *) from, (Plan *) newnode); COPY_SCALAR_FIELD(scanrelid); + COPY_SCALAR_FIELD(pgaceTuplePerms); } /* *************** _copyRangeTblEntry(RangeTblEntry *from) *** 1742,1747 **** --- 1745,1751 ---- COPY_SCALAR_FIELD(checkAsUser); COPY_BITMAPSET_FIELD(selectedCols); COPY_BITMAPSET_FIELD(modifiedCols); + COPY_SCALAR_FIELD(pgaceTuplePerms); return newnode; } *************** _copyColumnDef(ColumnDef *from) *** 2083,2088 **** --- 2087,2093 ---- COPY_NODE_FIELD(raw_default); COPY_STRING_FIELD(cooked_default); COPY_NODE_FIELD(constraints); + COPY_NODE_FIELD(pgaceItem); return newnode; } *************** _copyQuery(Query *from) *** 2180,2185 **** --- 2185,2191 ---- COPY_NODE_FIELD(limitCount); COPY_NODE_FIELD(rowMarks); COPY_NODE_FIELD(setOperations); + COPY_NODE_FIELD(pgaceItem); return newnode; } *************** _copyCreateStmt(CreateStmt *from) *** 2431,2436 **** --- 2437,2443 ---- COPY_NODE_FIELD(options); COPY_SCALAR_FIELD(oncommit); COPY_STRING_FIELD(tablespacename); + COPY_NODE_FIELD(pgaceItem); return newnode; } *************** _copyValue(Value *from) *** 3438,3443 **** --- 3445,3470 ---- return newnode; } + /* **************************************************************** + * nodes/security.h copy functions + * **************************************************************** + */ + static SelinuxEvalItem * + _copySelinuxEvalItem(SelinuxEvalItem *from) + { + SelinuxEvalItem *newnode = makeNode(SelinuxEvalItem); + int n; + + COPY_SCALAR_FIELD(relid); + COPY_SCALAR_FIELD(inh); + + COPY_SCALAR_FIELD(relperms); + COPY_SCALAR_FIELD(nattrs); + COPY_POINTER_FIELD(attperms, from->nattrs * sizeof(uint32)); + + return newnode; + } + /* * copyObject * *************** copyObject(void *from) *** 4115,4120 **** --- 4142,4150 ---- case T_XmlSerialize: retval = _copyXmlSerialize(from); break; + case T_SelinuxEvalItem: + retval = _copySelinuxEvalItem(from); + break; default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from)); diff -Nrpc base/src/backend/nodes/equalfuncs.c sepgsql/src/backend/nodes/equalfuncs.c *** base/src/backend/nodes/equalfuncs.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/nodes/equalfuncs.c Fri Jan 23 10:55:35 2009 *************** *** 30,35 **** --- 30,36 ---- #include "postgres.h" #include "nodes/relation.h" + #include "nodes/security.h" #include "utils/datum.h" *************** _equalQuery(Query *a, Query *b) *** 871,876 **** --- 872,878 ---- COMPARE_NODE_FIELD(limitCount); COMPARE_NODE_FIELD(rowMarks); COMPARE_NODE_FIELD(setOperations); + COMPARE_NODE_FIELD(pgaceItem); return true; } *************** _equalCreateStmt(CreateStmt *a, CreateSt *** 1086,1091 **** --- 1088,1094 ---- COMPARE_NODE_FIELD(options); COMPARE_SCALAR_FIELD(oncommit); COMPARE_STRING_FIELD(tablespacename); + COMPARE_NODE_FIELD(pgaceItem); return true; } *************** _equalColumnDef(ColumnDef *a, ColumnDef *** 2062,2067 **** --- 2065,2071 ---- COMPARE_NODE_FIELD(raw_default); COMPARE_STRING_FIELD(cooked_default); COMPARE_NODE_FIELD(constraints); + COMPARE_NODE_FIELD(pgaceItem); return true; } *************** _equalXmlSerialize(XmlSerialize *a, XmlS *** 2229,2234 **** --- 2233,2253 ---- } /* + * Stuff from nodes/security.h + */ + static bool + _equalSelinuxEvalItem(SelinuxEvalItem *a, SelinuxEvalItem *b) + { + COMPARE_SCALAR_FIELD(relid); + COMPARE_SCALAR_FIELD(inh); + COMPARE_SCALAR_FIELD(relperms); + COMPARE_SCALAR_FIELD(nattrs); + COMPARE_POINTER_FIELD(attperms, a->nattrs * sizeof(uint32)); + + return true; + } + + /* * Stuff from pg_list.h */ *************** equal(void *a, void *b) *** 2891,2896 **** --- 2910,2918 ---- case T_XmlSerialize: retval = _equalXmlSerialize(a, b); break; + case T_SelinuxEvalItem: + retval = _equalSelinuxEvalItem(a, b); + break; default: elog(ERROR, "unrecognized node type: %d", diff -Nrpc base/src/backend/nodes/outfuncs.c sepgsql/src/backend/nodes/outfuncs.c *** base/src/backend/nodes/outfuncs.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/nodes/outfuncs.c Fri Jan 23 10:55:35 2009 *************** *** 26,31 **** --- 26,32 ---- #include "lib/stringinfo.h" #include "nodes/plannodes.h" #include "nodes/relation.h" + #include "nodes/security.h" #include "utils/datum.h" *************** _outPlannedStmt(StringInfo str, PlannedS *** 255,260 **** --- 256,262 ---- WRITE_NODE_FIELD(relationOids); WRITE_NODE_FIELD(invalItems); WRITE_INT_FIELD(nParamExec); + WRITE_NODE_FIELD(pgaceItem); } /* *************** _outScanInfo(StringInfo str, Scan *node) *** 285,290 **** --- 287,293 ---- _outPlanInfo(str, (Plan *) node); WRITE_UINT_FIELD(scanrelid); + WRITE_UINT_FIELD(pgaceTuplePerms); } /* *************** _outRelOptInfo(StringInfo str, RelOptInf *** 1526,1531 **** --- 1529,1535 ---- WRITE_BOOL_FIELD(has_eclass_joins); WRITE_BITMAPSET_FIELD(index_outer_relids); WRITE_NODE_FIELD(index_inner_paths); + WRITE_UINT_FIELD(pgaceTuplePerms); } static void *************** _outCreateStmt(StringInfo str, CreateStm *** 1718,1723 **** --- 1722,1728 ---- WRITE_NODE_FIELD(options); WRITE_ENUM_FIELD(oncommit, OnCommitAction); WRITE_STRING_FIELD(tablespacename); + WRITE_NODE_FIELD(pgaceItem); } static void *************** _outColumnDef(StringInfo str, ColumnDef *** 1838,1843 **** --- 1843,1849 ---- WRITE_NODE_FIELD(raw_default); WRITE_STRING_FIELD(cooked_default); WRITE_NODE_FIELD(constraints); + WRITE_NODE_FIELD(pgaceItem); } static void *************** _outQuery(StringInfo str, Query *node) *** 1932,1937 **** --- 1938,1944 ---- WRITE_NODE_FIELD(limitCount); WRITE_NODE_FIELD(rowMarks); WRITE_NODE_FIELD(setOperations); + WRITE_NODE_FIELD(pgaceItem); } static void *************** _outRangeTblEntry(StringInfo str, RangeT *** 2060,2065 **** --- 2067,2073 ---- WRITE_OID_FIELD(checkAsUser); WRITE_BITMAPSET_FIELD(selectedCols); WRITE_BITMAPSET_FIELD(modifiedCols); + WRITE_UINT_FIELD(pgaceTuplePerms); } static void *************** _outFkConstraint(StringInfo str, FkConst *** 2332,2337 **** --- 2340,2368 ---- WRITE_BOOL_FIELD(skip_validation); } + /***************************************************************************** + * + * Stuff from nodes/security.h + * + *****************************************************************************/ + static void + _outSelinuxEvalItem(StringInfo str, SelinuxEvalItem *node) + { + int i; + + WRITE_NODE_TYPE("SELINUXEVALITEM"); + + WRITE_OID_FIELD(relid); + WRITE_BOOL_FIELD(inh); + + WRITE_UINT_FIELD(relperms); + WRITE_UINT_FIELD(nattrs); + + appendStringInfo(str, " :attperms ["); + for (i = 0; i < node->nattrs; i++) + appendStringInfo(str, " %u", node->attperms[i]); + appendStringInfo(str, " ]"); + } /* * _outNode - *************** _outNode(StringInfo str, void *obj) *** 2776,2781 **** --- 2807,2815 ---- case T_XmlSerialize: _outXmlSerialize(str, obj); break; + case T_SelinuxEvalItem: + _outSelinuxEvalItem(str, obj); + break; default: diff -Nrpc base/src/backend/nodes/readfuncs.c sepgsql/src/backend/nodes/readfuncs.c *** base/src/backend/nodes/readfuncs.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/nodes/readfuncs.c Fri Jan 23 10:55:35 2009 *************** *** 30,35 **** --- 30,36 ---- #include "nodes/parsenodes.h" #include "nodes/readfuncs.h" + #include "nodes/security.h" /* *************** _readQuery(void) *** 216,221 **** --- 217,223 ---- READ_NODE_FIELD(limitCount); READ_NODE_FIELD(rowMarks); READ_NODE_FIELD(setOperations); + READ_NODE_FIELD(pgaceItem); READ_DONE(); } *************** _readRangeTblEntry(void) *** 1149,1158 **** --- 1151,1199 ---- READ_OID_FIELD(checkAsUser); READ_BITMAPSET_FIELD(selectedCols); READ_BITMAPSET_FIELD(modifiedCols); + READ_UINT_FIELD(pgaceTuplePerms); READ_DONE(); } + /* + * Stuff from nodes/security.h + */ + static SelinuxEvalItem * + _readSelinuxEvalItem(void) + { + int i; + + READ_LOCALS(SelinuxEvalItem); + + READ_OID_FIELD(relid); + READ_BOOL_FIELD(inh); + + READ_UINT_FIELD(relperms); + READ_UINT_FIELD(nattrs); + + /* + * TODO: This part should be moved to readArray() ? + */ + local_node->attperms = palloc0(local_node->nattrs * sizeof(uint32)); + + token = pg_strtok(&length); /* skip :attperms */ + token = pg_strtok(&length); /* read '[' */ + if (token == NULL || strcmp(token, "[") != 0) + elog(ERROR, "expected \"[\" to start array, but got \"%s\"", + token ? (const char *) token : "[NULL]"); + for (i = 0; i < local_node->nattrs; i++) + { + token = pg_strtok(&length); + local_node->attperms[i] = atoui(token); + } + token = pg_strtok(&length); /* read ']' */ + if (token == NULL || strcmp(token, "[") != 0) + elog(ERROR, "expected \"[\" to end array, but got \"%s\"", + token ? (const char *) token : "[NULL]"); + + READ_DONE(); + } /* * parseNodeString *************** parseNodeString(void) *** 1274,1279 **** --- 1315,1322 ---- return_value = _readNotifyStmt(); else if (MATCH("DECLARECURSOR", 13)) return_value = _readDeclareCursorStmt(); + else if (MATCH("SELINUXEVALITEM", 15)) + return_value = _readSelinuxEvalItem(); else { elog(ERROR, "badly formatted node string \"%.32s\"...", token); diff -Nrpc base/src/backend/optimizer/plan/createplan.c sepgsql/src/backend/optimizer/plan/createplan.c *** base/src/backend/optimizer/plan/createplan.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/optimizer/plan/createplan.c Sat Jan 3 15:58:18 2009 *************** create_scan_plan(PlannerInfo *root, Path *** 302,307 **** --- 302,313 ---- } /* + * The guest of PGACE can refer plan->pgaceTuplePerms to apply + * tuple level access control in the pgaceExecScan() hook. + */ + ((Scan *)plan)->pgaceTuplePerms = rel->pgaceTuplePerms; + + /* * If there are any pseudoconstant clauses attached to this node, insert a * gating Result node that evaluates the pseudoconstants as one-time * quals. diff -Nrpc base/src/backend/optimizer/plan/planner.c sepgsql/src/backend/optimizer/plan/planner.c *** base/src/backend/optimizer/plan/planner.c Tue Jan 13 09:22:28 2009 --- sepgsql/src/backend/optimizer/plan/planner.c Tue Jan 13 09:39:35 2009 *************** standard_planner(Query *parse, int curso *** 230,235 **** --- 230,236 ---- result->relationOids = glob->relationOids; result->invalItems = glob->invalItems; result->nParamExec = list_length(glob->paramlist); + result->pgaceItem = parse->pgaceItem; return result; } diff -Nrpc base/src/backend/optimizer/util/clauses.c sepgsql/src/backend/optimizer/util/clauses.c *** base/src/backend/optimizer/util/clauses.c Tue Jan 13 09:22:28 2009 --- sepgsql/src/backend/optimizer/util/clauses.c Tue Jan 13 09:39:35 2009 *************** *** 38,43 **** --- 38,44 ---- #include "parser/parse_coerce.h" #include "parser/parse_func.h" #include "rewrite/rewriteManip.h" + #include "security/pgace.h" #include "tcop/tcopprot.h" #include "utils/acl.h" #include "utils/builtins.h" *************** inline_function(Oid funcid, Oid result_t *** 3548,3553 **** --- 3549,3557 ---- if (pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK) return NULL; + if (!pgaceAllowFunctionInlined(funcid, func_tuple)) + return NULL; + /* * Setup error traceback support for ereport(). This is so that we can * finger the function that bad information came from. *************** inline_set_returning_function(PlannerInf *** 4005,4010 **** --- 4009,4015 ---- funcform->prosecdef || !funcform->proretset || !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || + !pgaceAllowFunctionInlined(fexpr->funcid, func_tuple) || funcform->pronargs != list_length(fexpr->args)) { ReleaseSysCache(func_tuple); diff -Nrpc base/src/backend/optimizer/util/relnode.c sepgsql/src/backend/optimizer/util/relnode.c *** base/src/backend/optimizer/util/relnode.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/optimizer/util/relnode.c Sat Jan 3 15:58:18 2009 *************** build_simple_rel(PlannerInfo *root, int *** 91,96 **** --- 91,97 ---- rel->has_eclass_joins = false; rel->index_outer_relids = NULL; rel->index_inner_paths = NIL; + rel->pgaceTuplePerms = rte->pgaceTuplePerms; /* Check type of rtable entry */ switch (rte->rtekind) diff -Nrpc base/src/backend/parser/analyze.c sepgsql/src/backend/parser/analyze.c *** base/src/backend/parser/analyze.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/parser/analyze.c Fri Jan 23 10:55:35 2009 *************** *** 25,30 **** --- 25,31 ---- #include "postgres.h" #include "access/sysattr.h" + #include "catalog/heap.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" *************** *** 39,44 **** --- 40,46 ---- #include "parser/parse_target.h" #include "parser/parsetree.h" #include "rewrite/rewriteManip.h" + #include "security/pgace.h" #include "utils/rel.h" *************** transformInsertStmt(ParseState *pstate, *** 651,657 **** tle = makeTargetEntry(expr, attr_num, col->name, ! false); qry->targetList = lappend(qry->targetList, tle); rte->modifiedCols = bms_add_member(rte->modifiedCols, --- 653,659 ---- tle = makeTargetEntry(expr, attr_num, col->name, ! attr_num < 0 ? true : false); qry->targetList = lappend(qry->targetList, tle); rte->modifiedCols = bms_add_member(rte->modifiedCols, *************** transformInsertRow(ParseState *pstate, L *** 766,771 **** --- 768,814 ---- return result; } + static void + transformSelectIntoSystemColumn(ParseState *pstate, Query *qry) + { + ListCell *l; + uint32 system_attrs = 0; + bool relhasoids + = interpretOidsOption(qry->intoClause->options); + + foreach (l, qry->targetList) { + Form_pg_attribute attr; + TargetEntry *tle = lfirst(l); + + if (tle->resjunk) + continue; + + attr = SystemAttributeByName(tle->resname, relhasoids); + if (attr && SystemAttributeIsWritable(attr->attnum)) + { + uint32 mask = (1<<(-attr->attnum)); + + /* duplication checks */ + if (system_attrs & mask) + continue; + system_attrs |= mask; + + if (exprType((Node *) tle->expr) != attr->atttypid) + { + tle->expr = + (Expr *) coerce_to_target_type(pstate, + (Node *) tle->expr, + exprType((Node *) tle->expr), + attr->atttypid, + attr->atttypmod, + COERCION_IMPLICIT, + COERCE_IMPLICIT_CAST, + -1); + } + tle->resjunk = true; + } + } + } /* * transformSelectStmt - *************** transformSelectStmt(ParseState *pstate, *** 869,874 **** --- 912,918 ---- if (stmt->intoClause) { qry->intoClause = stmt->intoClause; + transformSelectIntoSystemColumn(pstate, qry); if (stmt->intoClause->colNames) applyColumnNames(qry->targetList, stmt->intoClause->colNames); } diff -Nrpc base/src/backend/parser/gram.y sepgsql/src/backend/parser/gram.y *** base/src/backend/parser/gram.y Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/parser/gram.y Fri Jan 23 10:55:35 2009 *************** *** 57,62 **** --- 57,63 ---- #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "parser/gramparse.h" + #include "security/pgace.h" #include "storage/lmgr.h" #include "utils/date.h" #include "utils/datetime.h" *************** static TypeName *TableFuncTypeName(List *** 402,407 **** --- 403,410 ---- %type OptTableSpace OptConsTableSpace OptTableSpaceOwner %type opt_check_option + %type OptSecurityItem SecurityItem + %type xml_attribute_el %type xml_attribute_list xml_attributes %type xml_root_version opt_xml_root_standalone *************** alter_table_cmd: *** 1796,1801 **** --- 1799,1822 ---- n->def = (Node *)$2; $$ = (Node *)n; } + /* ALTER TABLE CONTEXT = '...' */ + | SecurityItem + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetSecurityLabel; + n->name = NULL; + n->def = (Node *) $1; + $$ = (Node *) n; + } + /* ALTER TABLE ALTER [COLUMN] CONTEXT = '...' */ + | ALTER opt_column ColId SecurityItem + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetSecurityLabel; + n->name = $3; + n->def = (Node *) $4; + $$ = (Node *) n; + } ; alter_column_default: *************** opt_using: *** 1997,2003 **** *****************************************************************************/ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' ! OptInherit OptWith OnCommitOption OptTableSpace { CreateStmt *n = makeNode(CreateStmt); $4->istemp = $2; --- 2018,2024 ---- *****************************************************************************/ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' ! OptInherit OptWith OnCommitOption OptTableSpace OptSecurityItem { CreateStmt *n = makeNode(CreateStmt); $4->istemp = $2; *************** CreateStmt: CREATE OptTemp TABLE qualifi *** 2008,2017 **** n->options = $9; n->oncommit = $10; n->tablespacename = $11; $$ = (Node *)n; } | CREATE OptTemp TABLE qualified_name OF qualified_name ! '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace { /* SQL99 CREATE TABLE OF (cols) seems to be satisfied * by our inheritance capabilities. Let's try it... --- 2029,2039 ---- n->options = $9; n->oncommit = $10; n->tablespacename = $11; + n->pgaceItem = (Node *) $12; $$ = (Node *)n; } | CREATE OptTemp TABLE qualified_name OF qualified_name ! '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace OptSecurityItem { /* SQL99 CREATE TABLE OF (cols) seems to be satisfied * by our inheritance capabilities. Let's try it... *************** CreateStmt: CREATE OptTemp TABLE qualifi *** 2025,2030 **** --- 2047,2053 ---- n->options = $10; n->oncommit = $11; n->tablespacename = $12; + n->pgaceItem = (Node *) $13; $$ = (Node *)n; } ; *************** TableElement: *** 2067,2079 **** | TableConstraint { $$ = $1; } ; ! columnDef: ColId Typename ColQualList { ColumnDef *n = makeNode(ColumnDef); n->colname = $1; n->typename = $2; n->constraints = $3; n->is_local = true; $$ = (Node *)n; } ; --- 2090,2103 ---- | TableConstraint { $$ = $1; } ; ! columnDef: ColId Typename ColQualList OptSecurityItem { ColumnDef *n = makeNode(ColumnDef); n->colname = $1; n->typename = $2; n->constraints = $3; n->is_local = true; + n->pgaceItem = (Node *) $4; $$ = (Node *)n; } ; *************** common_func_opt_item: *** 4834,4839 **** --- 4858,4867 ---- /* we abuse the normal content of a DefElem here */ $$ = makeDefElem("set", (Node *)$1); } + | SecurityItem + { + $$ = $1; + } ; createfunc_opt_item: *************** createdb_opt_item: *** 5997,6002 **** --- 6025,6034 ---- { $$ = makeDefElem("owner", NULL); } + | SecurityItem + { + $$ = $1; + } ; /* *************** alterdb_opt_item: *** 6053,6058 **** --- 6085,6094 ---- { $$ = makeDefElem("connectionlimit", (Node *)makeInteger($4)); } + | SecurityItem + { + $$ = $1; + } ; *************** target_el: a_expr AS ColLabel *** 9840,9845 **** --- 9876,9903 ---- } ; + /***************************************************************************** + * + * PGACE Security Items + * + *****************************************************************************/ + + OptSecurityItem: + SecurityItem { $$ = $1; } + | /* EMPTY */ { $$ = NULL; } + ; + + SecurityItem: + IDENT '=' Sconst + { + DefElem *node = makeDefElem($1, (Node *) makeString($3)); + + if (!pgaceIsGramSecurityItem(node)) + yyerror("syntax error"); + + $$ = node; + } + ; /***************************************************************************** * diff -Nrpc base/src/backend/parser/parse_target.c sepgsql/src/backend/parser/parse_target.c *** base/src/backend/parser/parse_target.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/parser/parse_target.c Fri Jan 23 10:55:35 2009 *************** *** 14,19 **** --- 14,20 ---- */ #include "postgres.h" + #include "catalog/heap.h" #include "catalog/pg_type.h" #include "commands/dbcommands.h" #include "funcapi.h" *************** *** 27,32 **** --- 28,34 ---- #include "parser/parse_relation.h" #include "parser/parse_target.h" #include "parser/parse_type.h" + #include "security/pgace.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/typcache.h" *************** transformAssignedExpr(ParseState *pstate *** 361,376 **** Oid attrtype; /* type of target column */ int32 attrtypmod; Relation rd = pstate->p_target_relation; Assert(rd != NULL); ! if (attrno <= 0) ! ereport(ERROR, ! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("cannot assign to system column \"%s\"", ! colname), ! parser_errposition(pstate, location))); ! attrtype = attnumTypeId(rd, attrno); ! attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; /* * If the expression is a DEFAULT placeholder, insert the attribute's --- 363,395 ---- Oid attrtype; /* type of target column */ int32 attrtypmod; Relation rd = pstate->p_target_relation; + bool relhasoids = RelationGetForm(rd)->relhasoids; Assert(rd != NULL); ! if (attrno > 0) ! { ! attrtype = attnumTypeId(rd, attrno); ! attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; ! } ! else ! { ! Form_pg_attribute attr ! = SystemAttributeDefinition(attrno, relhasoids); ! if (attr && SystemAttributeIsWritable(attrno)) ! { ! attrtype = attr->atttypid; ! attrtypmod = attr->atttypmod; ! } ! else ! { ! ereport(ERROR, ! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ! errmsg("cannot assign to system column \"%s\"", ! colname), ! parser_errposition(pstate, location))); ! return NULL; /* compiler kindness */ ! } ! } /* * If the expression is a DEFAULT placeholder, insert the attribute's *************** updateTargetListEntry(ParseState *pstate *** 515,520 **** --- 534,542 ---- */ tle->resno = (AttrNumber) attrno; tle->resname = colname; + + if (SystemAttributeIsWritable(attrno)) + tle->resjunk = true; } *************** checkInsertTargets(ParseState *pstate, L *** 789,794 **** --- 811,817 ---- Bitmapset *wholecols = NULL; Bitmapset *partialcols = NULL; ListCell *tl; + uint32 system_attrs = 0; foreach(tl, cols) { *************** checkInsertTargets(ParseState *pstate, L *** 797,810 **** int attrno; /* Lookup column name, ereport on failure */ ! attrno = attnameAttNum(pstate->p_target_relation, name, false); if (attrno == InvalidAttrNumber) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", name, RelationGetRelationName(pstate->p_target_relation)), parser_errposition(pstate, col->location))); /* * Check for duplicates, but only of whole columns --- we allow --- 820,856 ---- int attrno; /* Lookup column name, ereport on failure */ ! attrno = attnameAttNum(pstate->p_target_relation, name, true); if (attrno == InvalidAttrNumber) + { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" of relation \"%s\" does not exist", name, RelationGetRelationName(pstate->p_target_relation)), parser_errposition(pstate, col->location))); + } + else if (attrno < 0) + { + if (SystemAttributeIsWritable(attrno)) + { + uint32 mask = (1<<(-attrno)); + + if ((system_attrs & mask) != 0) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_COLUMN), + errmsg("column \"%s\" specified more than once", name), + parser_errposition(pstate, col->location))); + system_attrs |= mask; + *attrnos = lappend_int(*attrnos, attrno); + continue; + } + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("column \"%s\" of relation \"%s\" is system column", + name, RelationGetRelationName(pstate->p_target_relation)), + parser_errposition(pstate, col->location))); + } /* * Check for duplicates, but only of whole columns --- we allow diff -Nrpc base/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmaster/postmaster.c *** base/src/backend/postmaster/postmaster.c Mon Jan 5 17:36:07 2009 --- sepgsql/src/backend/postmaster/postmaster.c Mon Jan 5 17:41:04 2009 *************** *** 108,113 **** --- 108,114 ---- #include "postmaster/pgarch.h" #include "postmaster/postmaster.h" #include "postmaster/syslogger.h" + #include "security/pgace.h" #include "storage/fd.h" #include "storage/ipc.h" #include "storage/pg_shmem.h" *************** static pid_t StartupPID = 0, *** 215,221 **** AutoVacPID = 0, PgArchPID = 0, PgStatPID = 0, ! SysLoggerPID = 0; /* Startup/shutdown state */ #define NoShutdown 0 --- 216,223 ---- AutoVacPID = 0, PgArchPID = 0, PgStatPID = 0, ! SysLoggerPID = 0, ! pgaceWorkerPID = 0; /* Startup/shutdown state */ #define NoShutdown 0 *************** ServerLoop(void) *** 1330,1335 **** --- 1332,1341 ---- if (PgStatPID == 0 && pmState == PM_RUN) PgStatPID = pgstat_start(); + /* If we have lost the pgace worker (if needed), try to start a new one */ + if (pgaceWorkerPID == 0 && pmState == PM_RUN) + pgaceWorkerPID = pgaceStartupWorkerProcess(); + /* * Touch the socket and lock file every 58 minutes, to ensure that * they are not removed by overzealous /tmp-cleaning tasks. We assume *************** SIGHUP_handler(SIGNAL_ARGS) *** 1933,1938 **** --- 1939,1946 ---- signal_child(SysLoggerPID, SIGHUP); if (PgStatPID != 0) signal_child(PgStatPID, SIGHUP); + if (pgaceWorkerPID != 0) + signal_child(pgaceWorkerPID, SIGHUP); /* Reload authentication config files too */ if (!load_hba()) *************** pmdie(SIGNAL_ARGS) *** 1992,1997 **** --- 2000,2008 ---- /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); + /* and the pgace worker too */ + if (pgaceWorkerPID != 0) + signal_child(pgaceWorkerPID, SIGTERM); pmState = PM_WAIT_BACKUP; } *************** pmdie(SIGNAL_ARGS) *** 2031,2036 **** --- 2042,2050 ---- /* and the walwriter too */ if (WalWriterPID != 0) signal_child(WalWriterPID, SIGTERM); + /* and the pgaceWorker too */ + if (pgaceWorkerPID != 0) + signal_child(pgaceWorkerPID, SIGTERM); pmState = PM_WAIT_BACKENDS; } *************** pmdie(SIGNAL_ARGS) *** 2064,2069 **** --- 2078,2085 ---- signal_child(PgArchPID, SIGQUIT); if (PgStatPID != 0) signal_child(PgStatPID, SIGQUIT); + if (pgaceWorkerPID != 0) + signal_child(pgaceWorkerPID, SIGQUIT); ExitPostmaster(0); break; } *************** reaper(SIGNAL_ARGS) *** 2312,2317 **** --- 2328,2343 ---- continue; } + /* Was it the PGACE worker process? */ + if (pid == pgaceWorkerPID) + { + pgaceWorkerPID = 0; + if (!EXIT_STATUS_0(exitstatus)) + LogChildExit(LOG, _("PGACE worker process"), + pid, exitstatus); + continue; + } + /* * Else do standard backend child cleanup. */ *************** HandleChildCrash(int pid, int exitstatus *** 2479,2484 **** --- 2505,2522 ---- signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT)); } + /* Take care of the pgace worker too */ + if (pid == pgaceWorkerPID) + pgaceWorkerPID = 0; + else if (pgaceWorkerPID != 0 && !FatalError) + { + ereport(DEBUG2, + (errmsg_internal("sending %s to process %d", + (SendStop ? "SIGSTOP" : "SIGQUIT"), + (int) pgaceWorkerPID))); + signal_child(pgaceWorkerPID, (SendStop ? SIGSTOP : SIGQUIT)); + } + /* * Force a power-cycle of the pgarch process too. (This isn't absolutely * necessary, but it seems like a good idea for robustness, and it *************** PostmasterStateMachine(void) *** 2609,2615 **** StartupPID == 0 && (BgWriterPID == 0 || !FatalError) && WalWriterPID == 0 && ! AutoVacPID == 0) { if (FatalError) { --- 2647,2654 ---- StartupPID == 0 && (BgWriterPID == 0 || !FatalError) && WalWriterPID == 0 && ! AutoVacPID == 0 && ! pgaceWorkerPID == 0) { if (FatalError) { diff -Nrpc base/src/backend/rewrite/rewriteHandler.c sepgsql/src/backend/rewrite/rewriteHandler.c *** base/src/backend/rewrite/rewriteHandler.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/rewrite/rewriteHandler.c Fri Jan 23 10:55:35 2009 *************** *** 23,28 **** --- 23,29 ---- #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteHandler.h" #include "rewrite/rewriteManip.h" + #include "security/pgace.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "commands/trigger.h" *************** QueryRewrite(Query *parsetree) *** 1972,1976 **** if (!foundOriginalQuery && lastInstead != NULL) lastInstead->canSetTag = true; ! return results; } --- 1973,1977 ---- if (!foundOriginalQuery && lastInstead != NULL) lastInstead->canSetTag = true; ! return pgacePostQueryRewrite(results); } diff -Nrpc base/src/backend/security/Makefile sepgsql/src/backend/security/Makefile *** base/src/backend/security/Makefile Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/Makefile Wed Jan 14 15:02:53 2009 *************** *** 0 **** --- 1,23 ---- + # + # src/backend/security/Makefile + # Makefile for Security Purpose Extensions + # + # Copyright (c) 2006 - 2007 KaiGai Kohei + # + subdir = src/backend/security + top_builddir = ../../.. + include $(top_builddir)/src/Makefile.global + + # common facilities for enhanced security features + OBJS := pgaceCommon.o pgaceHooks.o + + # DAC feature : Row-level Database ACLs + OBJS += rowacl/rowacl.o + + # MAC feature : Security-Enhanced PostgreSQL + ifeq ($(enable_selinux), yes) + OBJS += sepgsql/avc.o sepgsql/core.o sepgsql/hooks.o \ + sepgsql/permissions.o sepgsql/proxy.o + endif + + include $(top_builddir)/src/backend/common.mk diff -Nrpc base/src/backend/security/pgaceCommon.c sepgsql/src/backend/security/pgaceCommon.c *** base/src/backend/security/pgaceCommon.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/pgaceCommon.c Mon Dec 29 18:34:29 2008 *************** *** 0 **** --- 1,729 ---- + + /* + * src/backend/security/pgaceCommon.c + * common framework of security modules + * + * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + */ + #include "postgres.h" + + #include "access/genam.h" + #include "access/hash.h" + #include "access/heapam.h" + #include "access/xact.h" + #include "catalog/catalog.h" + #include "catalog/indexing.h" + #include "catalog/pg_attribute.h" + #include "catalog/pg_largeobject.h" + #include "catalog/pg_security.h" + #include "catalog/pg_type.h" + #include "executor/executor.h" + #include "libpq/be-fsstubs.h" + #include "miscadmin.h" + #include "nodes/makefuncs.h" + #include "nodes/parsenodes.h" + #include "parser/parse_expr.h" + #include "security/pgace.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" + #include "utils/syscache.h" + #include "utils/tqual.h" + #include + #include + + /***************************************************************************** + * GUC Parameter Support + *****************************************************************************/ + + int pgace_feature; + + /***************************************************************************** + * Extended SQL statements support + *****************************************************************************/ + + /* + * PGACE enables to create a new table labed as explicitly specified security + * attribute. It is implemented as an extension of SQL statement like: + * CREATE TABLE memo ( + * id integer primary key, + * msg TEXT + * ) CONTEXT = 'system_u:object_r:sepgsql_secret_table_t'; + * + * The specified security attribute is chained as a list of DefElem object, + * at CreateStmt->pgaceItem for a table, ColumnDef->pgaceItem for a column. + * + * These items are generated at pgaceGramSecurityItem() hook invoked from + * parser/gram.y. Then, pgaceRelationAttrList() pick them up and re-organize + * as a list, to pass it as an argument of heap_create_with_catalog(). + * + * When the list is not NIL, it means user specifies a security attribute + * explicitly for a newly created table or column. + * pgaceGramCreateRelation() and pgaceGramCreateAttribute() are invoked + * just before inserting a new tuple into system catalog, and PGACE + * framework invokes pgaceGramCreateRelation() and/or pgaceGramCreateAttribute() + * hooks to give a chance the gurst to attach proper security attributes. + */ + + List * + pgaceRelationAttrList(CreateStmt *stmt) + { + List *result = NIL; + ListCell *l; + DefElem *defel, *newel; + + if (stmt->pgaceItem) + { + defel = (DefElem *) stmt->pgaceItem; + + Assert(IsA(defel, DefElem)); + + if (!pgaceIsGramSecurityItem(defel)) + elog(ERROR, "node is not a pgace security item"); + newel = makeDefElem(NULL, (Node *) copyObject(defel)); + result = lappend(result, newel); + } + + foreach(l, stmt->tableElts) + { + ColumnDef *cdef = (ColumnDef *) lfirst(l); + + defel = (DefElem *) cdef->pgaceItem; + + if (defel) + { + Assert(IsA(defel, DefElem)); + + if (!pgaceIsGramSecurityItem(defel)) + elog(ERROR, "node is not a pgace security item"); + newel = makeDefElem(pstrdup(cdef->colname), + (Node *) copyObject(defel)); + result = lappend(result, newel); + } + } + return result; + } + + void + pgaceCreateRelationCommon(Relation rel, HeapTuple tuple, List *pgaceAttrList) + { + ListCell *l; + + foreach(l, pgaceAttrList) + { + DefElem *defel = (DefElem *) lfirst(l); + + if (!defel->defname) + { + Assert(pgaceIsGramSecurityItem((DefElem *) defel->arg)); + pgaceGramCreateRelation(rel, tuple, (DefElem *) defel->arg); + break; + } + } + } + + void + pgaceCreateAttributeCommon(Relation rel, HeapTuple tuple, + List *pgaceAttrList) + { + Form_pg_attribute attr = (Form_pg_attribute) GETSTRUCT(tuple); + ListCell *l; + + foreach(l, pgaceAttrList) + { + DefElem *defel = lfirst(l); + + if (!defel->defname) + continue; /* for table */ + if (strcmp(defel->defname, NameStr(attr->attname)) == 0) + { + Assert(pgaceIsGramSecurityItem((DefElem *) defel->arg)); + pgaceGramCreateAttribute(rel, tuple, (DefElem *) defel->arg); + break; + } + } + } + + /* + * pgaceAlterRelationCommon() + * + * This function is invoked when a user requires to change security attribute + * of table/column with "ALTER TABLE" statement. + * + * When a user attempt to relabel a table, PGACE invokes alterRelationCommon() + * and it gives the guest module a chance to set a new security attribute of + * specified table. + * When a user attempt to relabel a column, PGACE invokes alterAttributeCommon() + * and it gives the guest module a chance to set a new security attribute of + * specified column. + */ + + static void + alterRelationCommon(Relation rel, DefElem *defel) + { + Relation pg_class; + HeapTuple tuple; + + pg_class = heap_open(RelationRelationId, RowExclusiveLock); + + tuple = SearchSysCacheCopy(RELOID, + ObjectIdGetDatum(RelationGetRelid(rel)), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation '%s'", + RelationGetRelationName(rel)); + pgaceGramAlterRelation(rel, tuple, defel); + + simple_heap_update(pg_class, &tuple->t_self, tuple); + CatalogUpdateIndexes(pg_class, tuple); + + heap_freetuple(tuple); + heap_close(pg_class, RowExclusiveLock); + } + + static void + alterAttributeCommon(Relation rel, char *colName, DefElem *defel) + { + Relation pg_attr; + HeapTuple tuple; + + pg_attr = heap_open(AttributeRelationId, RowExclusiveLock); + + tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for attribute '%s' of relation '%s'", + colName, RelationGetRelationName(rel)); + pgaceGramAlterAttribute(rel, tuple, defel); + + simple_heap_update(pg_attr, &tuple->t_self, tuple); + CatalogUpdateIndexes(pg_attr, tuple); + + heap_freetuple(tuple); + heap_close(pg_attr, RowExclusiveLock); + } + + void + pgaceAlterRelationCommon(Relation rel, AlterTableCmd *cmd) + { + DefElem *defel = (DefElem *) cmd->def; + + Assert(IsA(defel, DefElem)); + + if (!pgaceIsGramSecurityItem(defel)) + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("PGACE: unsupported security item"))); + + if (!cmd->name) + { + alterRelationCommon(rel, defel); + } + else + { + alterAttributeCommon(rel, cmd->name, defel); + } + } + + /***************************************************************************** + * security attribute management + *****************************************************************************/ + + /* + * The following functions enables to manage security attribute of each tuple + * (including ones within system catalog). + * + * Security attribute has these features: + * 1. It is imported/exported with text representation, like + * 'system_u:object_r:sepgsql_table_t:s0' + * 2. In generally, many tuples share a same security attribute. + * (They are grouped by security attribute in other word.) + * 3. A object can have one security attribute at most. + * (It can have a state of unlabeled.) + * + * PGACE utilizes a newly added system catalog of pg_security to store text + * representation of security attribute efficiently. Any tuple has a object id + * of a tuple within pg_security system catalog, we call it as a security id. + * + * Users can show security attribute as if it stored text data, but any tuple + * has a security id which has a length of sizeof(Oid), without text data. + * It is translated each other when it is exported/imported. + * + * pgaceSidToSecurityLabel() returns a text representation for a given security, + * id, and pgaceSecurityLabelToSid() returns a security id for a give text + * representation. (If a given text representation was not found on pg_security + * system catalog, PGACE inserts a new entry automatically.) + * + * In the very early phase (invoked by initdb), pg_security system catalos is + * not available yet. The earlySecurityLabelToSid() and earlySidToSecurityLabel() + * is used to hold relationships between security id and text representation. + * These relationships are stored at the end of bootstraping mode by + * pgacePostBootstrapingMode(). It write any cached relationships into pg_security + * system catalog. + */ + + typedef struct earlySeclabel + { + struct earlySeclabel *next; + Oid sid; + char label[1]; + } earlySeclabel; + + static earlySeclabel *earlySeclabelList = NULL; + + static Oid + earlySecurityLabelToSid(char *label) + { + earlySeclabel *es; + Oid minsid = SecurityRelationId; + + for (es = earlySeclabelList; es != NULL; es = es->next) + { + if (!strcmp(label, es->label)) + return es->sid; + if (es->sid < minsid) + minsid = es->sid; + } + /* + * not found + */ + es = malloc(sizeof(earlySeclabel) + strlen(label)); + es->next = earlySeclabelList; + es->sid = minsid - 1; + strcpy(es->label, label); + earlySeclabelList = es; + + return es->sid; + } + + static char * + earlySidToSecurityLabel(Oid sid) + { + earlySeclabel *es; + + for (es = earlySeclabelList; es != NULL; es = es->next) + { + if (es->sid == sid) + return pstrdup(es->label); + } + + return NULL; /* not found */ + } + + void + pgacePostBootstrapingMode(void) + { + Relation rel; + CatalogIndexState ind; + HeapTuple tuple; + earlySeclabel *es, *_es; + Oid meta_sid; + Datum value; + bool isnull; + + if (!earlySeclabelList) + return; + + StartTransactionCommand(); + + meta_sid = earlySecurityLabelToSid(pgaceSecurityLabelOfLabel()); + + rel = heap_open(SecurityRelationId, RowExclusiveLock); + ind = CatalogOpenIndexes(rel); + + for (es = earlySeclabelList; es != NULL; es = _es) + { + _es = es->next; + + value = DirectFunctionCall1(textin, CStringGetDatum(es->label)); + isnull = false; + tuple = heap_form_tuple(RelationGetDescr(rel), &value, &isnull); + + HeapTupleSetOid(tuple, es->sid); + if (HeapTupleHasSecLabel(tuple)) + HeapTupleSetSecLabel(tuple, meta_sid); + + simple_heap_insert(rel, tuple); + CatalogIndexInsert(ind, tuple); + + heap_freetuple(tuple); + + free(es); + } + CatalogCloseIndexes(ind); + heap_close(rel, RowExclusiveLock); + + CommitTransactionCommand(); + } + + /* + * pgaceLookupSecurityId() + * + * The PGACE guest subsystem can use this interface to get a security id + * for a given text representation. + */ + Oid + pgaceLookupSecurityId(char *raw_label) + { + Oid labelOid, labelSid; + HeapTuple tuple; + + if (IsBootstrapProcessingMode()) + return earlySecurityLabelToSid(raw_label); + + /* + * lookup syscache at first + */ + tuple = SearchSysCache(SECURITYLABEL, + CStringGetTextDatum(raw_label), + 0, 0, 0); + if (HeapTupleIsValid(tuple)) + { + labelOid = HeapTupleGetOid(tuple); + ReleaseSysCache(tuple); + } + else + { + /* + * not found, insert a new one into pg_security + */ + Relation rel; + CatalogIndexState ind; + char *slabel; + Datum labelTxt; + bool isnull; + + rel = heap_open(SecurityRelationId, RowExclusiveLock); + + slabel = pgaceSecurityLabelOfLabel(); + + if (!slabel) + { + labelSid = InvalidOid; + labelOid = GetNewOid(rel); + } + else if (!strcmp(raw_label, slabel)) + { + labelOid = labelSid = GetNewOid(rel); + } + else + { + labelSid = pgaceLookupSecurityId(slabel); + labelOid = GetNewOid(rel); + } + + ind = CatalogOpenIndexes(rel); + + labelTxt = CStringGetTextDatum(raw_label); + isnull = false; + tuple = heap_form_tuple(RelationGetDescr(rel), + &labelTxt, &isnull); + if (HeapTupleHasSecLabel(tuple)) + HeapTupleSetSecLabel(tuple, labelSid); + HeapTupleSetOid(tuple, labelOid); + + simple_heap_insert(rel, tuple); + CatalogIndexInsert(ind, tuple); + + /* + * NOTE: + * We also have to insert a cache entry of new tuple of + * pg_security for temporary usage. + * If user tries to apply same security attribute twice + * or more within same command id, PGACE cannot decide + * whether it should be inserted, or not, because it + * cannot scan the prior one with SnapshotNow. + * + * A cache entry inserted will be invalidated on the + * next CommandIdIncrement(). + * The purpose of InsertSysCache() here is to prevent + * duplicate insertion + */ + InsertSysCache(RelationGetRelid(rel), tuple); + + CatalogCloseIndexes(ind); + heap_close(rel, RowExclusiveLock); + } + + return labelOid; + } + + Oid + pgaceSecurityLabelToSid(char *label) + { + char *raw_label = pgaceTranslateSecurityLabelIn(label); + + if (!pgaceCheckValidSecurityLabel(raw_label)) + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("PGACE: invalid security label: %s", raw_label))); + + return pgaceLookupSecurityId(raw_label); + } + + /* + * pgaceLookupSecurityLabel() + * + * The PGACE guest module can use this interface to get a text representation + * in raw-format, without cosmetic translation. + */ + char * + pgaceLookupSecurityLabel(Oid sid) + { + HeapTuple tuple; + Datum labelTxt; + char *label; + bool isnull; + + if (!OidIsValid(sid)) + return NULL; + + if (IsBootstrapProcessingMode()) + return earlySidToSecurityLabel(sid); + + tuple = SearchSysCache(SECURITYOID, + ObjectIdGetDatum(sid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + return NULL; + + labelTxt = SysCacheGetAttr(SECURITYOID, + tuple, Anum_pg_security_seclabel, &isnull); + Assert(!isnull); + label = TextDatumGetCString(labelTxt); + ReleaseSysCache(tuple); + + return label; + } + + char * + pgaceSidToSecurityLabel(Oid sid) + { + char *label; + + label = pgaceLookupSecurityLabel(sid); + if (!label || !pgaceCheckValidSecurityLabel(label)) + label = pgaceUnlabeledSecurityLabel(); + + if (!label) + return pstrdup(""); + + return pgaceTranslateSecurityLabelOut(label); + } + + Datum + pgaceHeapGetSecurityLabelSysattr(HeapTuple tuple) + { + Oid sid = HeapTupleGetSecLabel(tuple); + + return CStringGetTextDatum(pgaceSidToSecurityLabel(sid)); + } + + /***************************************************************************** + * Set/Get security attribute of Large Object + *****************************************************************************/ + + /* + * lo_get_security() + * + * This function returns a security attribute of large object + * in TEXT representation. + * + * It assumes the first page means the whole of large object. + * The guest of PGACE should pay effort to keep its consistency. + */ + Datum + lo_get_security(PG_FUNCTION_ARGS) + { + Oid loid = PG_GETARG_OID(0); + Relation rel; + ScanKeyData skey; + SysScanDesc scan; + HeapTuple tuple; + Oid sid; + + rel = heap_open(LargeObjectRelationId, AccessShareLock); + + ScanKeyInit(&skey, + Anum_pg_largeobject_loid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(loid)); + + scan = systable_beginscan(rel, LargeObjectLOidPNIndexId, true, + SnapshotNow, 1, &skey); + tuple = systable_getnext(scan); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("large object %u does not exist", loid))); + pgaceLargeObjectGetSecurity(rel, tuple); + sid = HeapTupleGetSecLabel(tuple); + + systable_endscan(scan); + heap_close(rel, AccessShareLock); + + return CStringGetTextDatum(pgaceSidToSecurityLabel(sid)); + } + + /* + * lo_set_security() + * + * This function set a new security attribute of a large object. + * It scans pg_largeobject system catalog with a given loid, + * and invokes pgaceLargeObjectSetSecurity() for each page frame. + */ + Datum + lo_set_security(PG_FUNCTION_ARGS) + { + Oid loid = PG_GETARG_OID(0); + Datum labelTxt = PG_GETARG_DATUM(1); + Relation rel; + ScanKeyData skey; + SysScanDesc sd; + HeapTuple oldtup, newtup; + CatalogIndexState indstate; + Oid sid; + List *okList = NIL; + bool found = false; + + sid = pgaceSecurityLabelToSid(TextDatumGetCString(labelTxt)); + + ScanKeyInit(&skey, + Anum_pg_largeobject_loid, + BTEqualStrategyNumber, + F_OIDEQ, ObjectIdGetDatum(loid)); + + rel = heap_open(LargeObjectRelationId, RowExclusiveLock); + + indstate = CatalogOpenIndexes(rel); + + sd = systable_beginscan(rel, + LargeObjectLOidPNIndexId, true, + SnapshotNow, 1, &skey); + + while ((oldtup = systable_getnext(sd)) != NULL) + { + ListCell *l; + + newtup = heap_copytuple(oldtup); + HeapTupleSetSecLabel(newtup, sid); + + foreach (l, okList) + { + if (HeapTupleGetSecLabel(oldtup) == lfirst_oid(l)) + goto skip; /* already checked */ + } + okList = lappend_oid(okList, HeapTupleGetSecLabel(oldtup)); + + pgaceLargeObjectSetSecurity(rel, newtup, oldtup); + skip: + simple_heap_update(rel, &newtup->t_self, newtup); + CatalogUpdateIndexes(rel, newtup); + found = true; + } + systable_endscan(sd); + CatalogCloseIndexes(indstate); + heap_close(rel, RowExclusiveLock); + + CommandCounterIncrement(); + + if (!found) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("large object %u does not exist", loid))); + + PG_RETURN_BOOL(true); + } + + /****************************************************************** + * Function stubs related to security modules + ******************************************************************/ + + /* + * If the guest of PGACE added its specific functions, it has to put + * function stubs on the following section, because the guest modules + * are not compiled and linked when it is disabled. + * It can cause a build problem in other environments. + */ + #ifndef HAVE_SELINUX + + static Datum + unavailable_function(const char *fn_name, int error_code) + { + ereport(ERROR, + (errcode(error_code), + errmsg("%s is not available", fn_name))); + PG_RETURN_VOID(); + } + + Datum + sepgsql_getcon(PG_FUNCTION_ARGS) + { + return unavailable_function(__FUNCTION__, + ERRCODE_SELINUX_ERROR); + } + + Datum + sepgsql_getservcon(PG_FUNCTION_ARGS) + { + return unavailable_function(__FUNCTION__, + ERRCODE_SELINUX_ERROR); + } + + Datum + sepgsql_get_user(PG_FUNCTION_ARGS) + { + return unavailable_function(__FUNCTION__, + ERRCODE_SELINUX_ERROR); + } + + Datum + sepgsql_get_role(PG_FUNCTION_ARGS) + { + return unavailable_function(__FUNCTION__, + ERRCODE_SELINUX_ERROR); + } + + Datum + sepgsql_get_type(PG_FUNCTION_ARGS) + { + return unavailable_function(__FUNCTION__, + ERRCODE_SELINUX_ERROR); + } + + Datum + sepgsql_get_range(PG_FUNCTION_ARGS) + { + return unavailable_function(__FUNCTION__, + ERRCODE_SELINUX_ERROR); + } + + Datum + sepgsql_set_user(PG_FUNCTION_ARGS) + { + return unavailable_function(__FUNCTION__, + ERRCODE_SELINUX_ERROR); + } + + Datum + sepgsql_set_role(PG_FUNCTION_ARGS) + { + return unavailable_function(__FUNCTION__, + ERRCODE_SELINUX_ERROR); + } + + Datum + sepgsql_set_type(PG_FUNCTION_ARGS) + { + return unavailable_function(__FUNCTION__, + ERRCODE_SELINUX_ERROR); + } + + Datum + sepgsql_set_range(PG_FUNCTION_ARGS) + { + return unavailable_function(__FUNCTION__, + ERRCODE_SELINUX_ERROR); + } + + #endif /* HAVE_SELINUX */ diff -Nrpc base/src/backend/security/pgaceHooks.c sepgsql/src/backend/security/pgaceHooks.c *** base/src/backend/security/pgaceHooks.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/pgaceHooks.c Wed Jan 21 17:22:37 2009 *************** *** 0 **** --- 1,1547 ---- + /* + * src/backend/security/pgaceHooks.c + * Security hooks in PostgreSQL Access Control Extension (PGACE) + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + */ + #include "postgres.h" + + #include "security/pgace.h" + + /* + * GUC parameter: pgace_feature + * It allows users to choose an enhanced security feature. + * It has a state of 'none' in the default, so you should + * specify it explicitly with initdb --pgace-feature=FEATURE. + */ + int pgace_feature; + + /* + * PGACE (PostgreSQL Access Control Extension) + * + * It provides a set of security hooks at strategic points and + * common facilities to manage security attribute of database + * objects. Its purpose is to allow various kind of enhanced + * security features with minimum impact to the core PostgreSQL + * codes. + * In generally, individual security feature has its own access + * control model, policy and granuality, however, they also have + * facilities to be shared commonly. + * + * The one is a set of security hooks. All the enhanced security + * codes have to be invoked via the hooks, and return a proper + * value or raise an error, if necessary. + * When you add a new security feature, you need the following steps. + * 1. add a option to 'pgace_feature' parameter. + * 2. modify hooks to invoke your security feature. + * Please note that you don't need to modify all the hooks. + * If you don't provide any feature, please keep it as is. + * + * Example: pgaceHeapTupleInsert() hook + * ------------------------------------ + * bool + * pgaceHeapTupleInsert(Relation rel, HeapTuple tuple, + * bool is_internal, bool with_returning) + * { + * if (!rowaclHeapTupleInsert(rel, tuple, + * is_internal, + * with_returning)) + * return false; + * + * switch (pgace_feature) + * { + * #ifdef HAVE_SELINUX + * case PGACE_FEATURE_SELINUX: + * if (sepgsqlIsEnabled()) + * return sepgsqlHeapTupleInsert(rel, tuple, + * is_internal, + * with_returning); + * break; + * #endif + * #ifdef HAVE_FOO_SECURITY + * case PGACE_FEATURE_FOO_SECURITY: + * return fooSecurityHeapTupleInsert(rel, tuple, + * is_internal, + * with_returning); + * break; + * #endif + * default: + * break; + * } + * return true; + * } + * ------------------------------------ + * If your security feature has platform dependency, related code + * should be enclosed by #ifdef ... #endif block. + * (In this case, it is named as FOO_SECURITY.) + * The pgace_feature shows what enhanced security feature is activated + * in this system. If your security feature is chosen, it can be invoked + * via pgaceHeapTupleInsert() just before a new tuple is inserted on + * the target relation. Your fooSecurityHeapTupleInsert() can make its + * decision based on its policy and given informations. + * This hook requires to return 'true' or 'false'. If it returns 'false', + * it will be skipped to insert the given tuple. + * You can notice the Row-level database ACLs feature is hardwired. + * It is an application of traditional DAC policy in Row-level. + * + * The other is facilities to manage security attribtue of database + * objects. They have text representation as most of secure operating + * system doing, but it is not stored in each tuples directly, to reduce + * storage comsumption. + * We can fetch them via HeapTupleGetSecLabel(tuple) macro. It is stored + * as a Oid value (called as security identifier) which indicates pg_security + * system catalog. It holds mapping between security identifier and security + * attribute in text representation. + * User can see/set security attribute of database objects via security_label + * system column. + */ + + /****************************************************************** + * Initialization hooks + ******************************************************************/ + + /* + * pgaceShmemSize + * + * This hook has to return the size of shared memory required + * by the guest. If it needs no shared memory region, it should + * return 0. + */ + Size + pgaceShmemSize(void) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + return sepgsqlShmemSize(); + break; + #endif + default: + break; + } + + return (Size) 0; + } + + /* + * pgaceInitialize + * + * This hook is invoked when a new PostgreSQL instance is created. + * The guest can use this hook to initialize itself. + * + * is_bootstrap is true, if bootstraping mode. + */ + void + pgaceInitialize(bool is_bootstrap) + { + /* Hardwired DAC initialization */ + rowaclInitialize(is_bootstrap); + + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlInitialize(is_bootstrap); + break; + #endif + default: + break; + } + } + + /* + * pgaceStartupWorkerProcess + * + * The guest can create a worker process in this hook, if necessary. + * (currently, PGACE does not support multiple worker processes.) + * + * This hooks has to return the PID of child process. It is managed + * by postmaster in the same way to manage the other children. + * So, the worker process has to be available to handle signals. + * + * If unnecessary, it has to return (pid_t) 0. + */ + pid_t + pgaceStartupWorkerProcess(void) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + return sepgsqlStartupWorkerProcess(); + break; + #endif + default: + break; + } + + return (pid_t) 0; + } + + /****************************************************************** + * SQL proxy hooks + ******************************************************************/ + + /* + * pgacePostQueryRewrite + * + * This hook is invoked just after query is rewritten. + * + * The guest can check/modify/replace given query trees in this + * hook, if necessary. + * queryList is a list of Query object processes by rewriter. + */ + List * + pgacePostQueryRewrite(List *queryList) + { + /* Hardwired DAC checks */ + queryList = rowaclPostQueryRewrite(queryList); + + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + return sepgsqlPostQueryRewrite(queryList); + break; + #endif + default: + break; + } + + return queryList; + } + + /* + * pgaceExecutorStart + * + * This hook is invoked on the head of ExecutorStart(). + * + * The arguments of this hook are come from the ones of ExecutorStart + * as is. + */ + void + pgaceExecutorStart(QueryDesc *queryDesc, int eflags) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlExecutorStart(queryDesc, eflags); + break; + #endif + default: + break; + } + } + + /* + * pgaceExecScan + * + * This hook is invoked on ExecScan for each tuple fetched. + * The guest can check its visibility, and can skip to scan the given + * tuple. If this hook returns false, the tuple is filtered from the + * result set or the target of updates/deletion. + * + * Otherwise, it has to return true. + * + * The guest can refer Scan::pgaceTuplePerms (declared as uint32). + * It is a copy come from RangeTblEntry::pgaceTuplePerms set in + * the previous phase. It can be used to mark what permissions are + * required to scanned tuples. + */ + bool + pgaceExecScan(Scan *scan, Relation rel, TupleTableSlot *slot) + { + /* Hardwired DAC checks */ + if (!rowaclExecScan(scan, rel, slot)) + return false; + + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + return sepgsqlExecScan(scan, rel, slot); + break; + #endif + default: + break; + } + return true; + } + + /* + * pgaceProcessUtility + * + * This hooks is invoked on the head of ProcessUtility(). + */ + void + pgaceProcessUtility(Node *parsetree, ParamListInfo params, bool isTopLevel) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlProcessUtility(parsetree, params, isTopLevel); + break; + #endif + default: + break; + } + } + + /****************************************************************** + * HeapTuple modification hooks + ******************************************************************/ + + /* + * pgaceHeapTupleInsert + * + * This hooks is invoked just before a new tuple is inserted. + * If it returns false, inserting the given tuple is skipped. + * (or generates an error, if we cannot skip it simply.) + * + * The guest has to set a security attribute of a newly inserted + * tuple, if necessary and when user does not specify it explicitly. + * + * arguments: + * - rel is the target relation to be inserted. + * - tuple is the new tuple to be inserted. + * - is_internal is a bool to show whether it directly come from + * user's query, or not. + * - with_returning is a bool to show whether this INSERT statement + * has RETURNING clause, or not. + */ + bool + pgaceHeapTupleInsert(Relation rel, HeapTuple tuple, + bool is_internal, bool with_returning) + { + /* Hardwired DAC check */ + if (!rowaclHeapTupleInsert(rel, tuple, + is_internal, + with_returning)) + return false; + + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + return sepgsqlHeapTupleInsert(rel, tuple, + is_internal, + with_returning); + break; + #endif + default: + break; + } + return true; + } + + /* + * pgaceHeapTupleUpdate + * + * This hook is invoked just before a tuple is updated. + * If it returns false, updating the given tuple is skipped. + * (or generates an error, if we cannot skip it simply.) + * + * The guest has to preserve a security attribute of the updated + * tuple, if necessary and when user specify its new security + * attribute explicitly. + * + * arguments: + * - rel is the target relation to be updated. + * - otid is the ItemPointer of the tuple with older version. + * - newtup is the tuple to be updated. + * - is_internal is a bool to show whether it directly come from + * user's query, or not. + * - with_returning is a bool to show whether this INSERT statement + * has RETURNING clause, or not. + */ + bool + pgaceHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup, + bool is_internal, bool with_returning) + { + /* Hardwired DAC check */ + if (!rowaclHeapTupleUpdate(rel, otid, newtup, + is_internal, + with_returning)) + return false; + + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + return sepgsqlHeapTupleUpdate(rel, otid, newtup, + is_internal, + with_returning); + break; + #endif + default: + break; + } + return true; + } + + /* + * pgaceHeapTupleDelete + * + * This hook is invoked just before a tuple is deleted. + * If it returns false, deleting the given tuple is skipped. + * (or generates an error, if we cannot skip it simply.) + * + * arguments: + * - rel is the target relation to be deleted. + * - otid is the ItemPointer of the tuple to be deleted. + * - is_internal is a bool to show whether it directly come from + * user's query, or not. + * - with_returning is a bool to show whether this INSERT statement + * has RETURNING clause, or not. + */ + bool + pgaceHeapTupleDelete(Relation rel, ItemPointer otid, + bool is_internal, bool with_returning) + { + /* Hardwired DAC check */ + if (!rowaclHeapTupleDelete(rel, otid, + is_internal, + with_returning)) + return false; + + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + return sepgsqlHeapTupleDelete(rel, otid, + is_internal, + with_returning); + break; + #endif + default: + break; + } + return true; + } + + /****************************************************************** + * Extended SQL statement hooks + ******************************************************************/ + + /* + * pgaceIsGramSecurityItem + * + * PGACE framework provides its guest to manage security attribute + * for some kind of database obejcts, using an enhanced SQL statement. + * + * For example: + * CREATE TABLE tbl ( + * x integer, + * y text + * ) security_label = 'system_u:object_r:sepgsql_table_t:Classified'; + * + * This hook is invoked during parsing given queries at parser/gram.y. + * It generates a DefElem object which holds explicitly specified + * security attribute. If working guest support the feature and the + * given DefElem has correct pair of defname and argument string, + * this hook should return true. + * In ths above example, the given DefElem has "security_label" as + * defname, and "system_u:object_r:sepgsql_table_t:Classified" as + * its argument string. + */ + bool + pgaceIsGramSecurityItem(DefElem *defel) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + return sepgsqlIsGramSecurityItem(defel); + break; + #endif + default: + break; + } + return false; + } + + /* + * The series of following hooks has three arguments. + * - rel is an opened relation of the target system catalog. + * - tuple is a new tuple to be inserted/updated. + * - defel is a DefElem object checked in pgaceIsGramSecurityItem(). + */ + + /* + * pgaceGramCreateRelation + * + * This hook invoked to apply an explicitly specified security attribute + * just before inserting a new tuple into pg_class system catalog on + * the processing of CREATE TABLE. + * The guest can attach the required security attribute for the given + * tuple which means a new relation. + */ + void + pgaceGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + { + sepgsqlGramCreateRelation(rel, tuple, defel); + return; + } + break; + #endif + default: + break; + } + + if (defel) + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("unable to set security attribute of table " + "via CREATE TABLE"))); + } + + /* + * pgaceGramCreateAttribute + * + * This hook invoked to apply an explicitly specified security attribute + * just before inserting a new tuple into pg_attribute system catalog on + * the processing of CREATE TABLE. + * The guest can attach the required security attribute for the given + * tuple which means a new column. + */ + void + pgaceGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + { + sepgsqlGramCreateAttribute(rel, tuple, defel); + return; + } + break; + #endif + default: + break; + } + + if (defel) + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("unable to set security attribute of column " + "via CREATE TABLE"))); + } + + /* + * pgaceGramAlterRelation + * + * This hook invoked to apply an explicitly specified security attribute + * just before updating an older tuple of pg_class system catalog on + * the processing of ALTER TABLE. + * The guest can attach the required security attribute for the given + * tuple which means a table. + */ + void + pgaceGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + { + sepgsqlGramAlterRelation(rel, tuple, defel); + return; + } + break; + #endif + default: + break; + } + + if (defel) + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("unable to set security attribute of table " + "via ALTER TABLE"))); + } + + /* + * pgaceGramAlterAttribute + * + * This hook invoked to apply an explicitly specified security attribute + * just before updating an older tuple of pg_attribute system catalog on + * the processing of ALTER TABLE. + * The guest can attach the required security attribute for the given + * tuple which means a column. + */ + void + pgaceGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + { + sepgsqlGramAlterAttribute(rel, tuple, defel); + return; + } + break; + #endif + default: + break; + } + + if (defel) + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("unable to set security attribute of column " + "via ALTER TABLE"))); + } + + /* + * pgaceGramCreateDatabase + * + * This hook invoked to apply an explicitly specified security attribute + * just before inserting a new tuple into pg_database system catalog on + * the processing of CREATE DATABASE. + * The guest can attach the required security attribute for the given + * tuple which means a database. + */ + void + pgaceGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + { + sepgsqlGramCreateDatabase(rel, tuple, defel); + return; + } + break; + #endif + default: + break; + } + + if (defel) + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("unable to set security attribute of database " + "via CREATE DATABASE"))); + } + + /* + * pgaceGramAlterDatabase + * + * This hook invoked to apply an explicitly specified security attribute + * just before updating an older tuple of pg_database system catalog on + * the processing of ALTER DATABASE. + * The guest can attach the required security attribute for the given + * tuple which means a database. + */ + void + pgaceGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + { + sepgsqlGramAlterDatabase(rel, tuple, defel); + return; + } + break; + #endif + default: + break; + } + + if (defel) + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("unable to set security attribute of database " + "via ALTER DATABASE"))); + } + + /* + * pgaceGramCreateFunction + * + * This hook invoked to apply an explicitly specified security attribute + * just before inserting a new tuple into pg_proc system catalog on + * the processing of CREATE FUNCTION. + * The guest can attach the required security attribute for the given + * tuple which means a function. + */ + void + pgaceGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + { + sepgsqlGramCreateFunction(rel, tuple, defel); + return; + } + break; + #endif + default: + break; + } + + if (defel) + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("unable to set security attribute of function " + "via CREATE FUNCTION"))); + } + + /* + * pgaceGramAlterFunction + * + * This hook invoked to apply an explicitly specified security attribute + * just before updating an older tuple of pg_proc system catalog on + * the processing of ALTER FUNCTION. + * The guest can attach the required security attribute for the given + * tuple which means a function. + */ + void + pgaceGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + { + sepgsqlGramAlterFunction(rel, tuple, defel); + return; + } + break; + #endif + default: + break; + } + + if (defel) + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("unable to set security attribute of function " + "via ALTER FUNCTION"))); + } + + /****************************************************************** + * DATABASE related hooks + ******************************************************************/ + + /* + * pgaceSetDatabaseParam + * + * This hook is invoked just before putting a new value on a GUC + * variable. + * + * arguments: + * - name is a name of GUC variable. + * - argstring is its new value. NULL means user tries to reset + * the given GUC variable. + */ + void + pgaceSetDatabaseParam(const char *name, char *argstring) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlSetDatabaseParam(name, argstring); + break; + #endif + default: + break; + } + } + + /* + * pgaceGetDatabaseParam + * + * This hook is invoked just before reffering a GUC variable. + * + * arguments: + * - name is a name of GUC variable. + */ + void + pgaceGetDatabaseParam(const char *name) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlGetDatabaseParam(name); + break; + #endif + default: + break; + } + } + + /****************************************************************** + * FUNCTION related hooks + ******************************************************************/ + + /* + * pgaceCallFunction + * + * This hook is invoked when a function is invoked as a part + * of the given query. It provides a FmgrInfo object of the + * function, so the guest can store its opaque data within + * FmgrInfo::fn_pgaceItem. + */ + void + pgaceCallFunction(FmgrInfo *finfo) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlCallFunction(finfo); + break; + #endif + default: + break; + } + } + + /* + * pgaceCallAggFunction + * + * This hook is invoked when an aggregate function is invoked + * in the given query. pgaceCallFunction() is also invoked for + * its transate function and finalize function. + * + * arguments: + * - aggTuple is the tuple of target aggregate function stored + * in pg_aggregate system catalog. + */ + void + pgaceCallAggFunction(HeapTuple aggTuple) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlCallAggFunction(aggTuple); + break; + #endif + default: + break; + } + } + + /* + * pgaceCallFunctionTrigger + * + * This hook is invoked just before executing trigger function. + * If it returns false, the trigger function is not invoked and + * caller receives a NULL tuple as a result. + * (It also means skip to update/delete the tuple in BR-triggers.) + */ + bool + pgaceCallTriggerFunction(TriggerData *tgdata) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + return sepgsqlCallTriggerFunction(tgdata); + break; + #endif + default: + break; + } + return true; + } + + /* + * pgaceBeginPerformCheckFK + * + * This hook is invoked just before performing FK constraint checks. + * The guest can change its internal state during the checks. + * The major purpose of this function is to prevent violation of + * integrity consistentency violation due to row-level access control. + * If the guest requires an opaque data, it should be returned then + * it will be delivered via pgaceEndPerformCheckFK(). + */ + void + pgaceBeginPerformCheckFK(Relation rel, bool is_primary, Oid save_userid, + Datum *rowacl_private, Datum *pgace_private) + { + /* A wired DAC state change */ + *rowacl_private = rowaclBeginPerformCheckFK(rel, is_primary, save_userid); + + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + *pgace_private = sepgsqlBeginPerformCheckFK(rel, is_primary, save_userid); + break; + #endif + default: + break; + } + } + + /* + * pgaceEndPerformCheckFK + * + * This hook is invoked just after performing FK constraint checks. + * The guest can restore its internal state using this hook. + */ + void + pgaceEndPerformCheckFK(Relation rel, Datum rowacl_private, Datum pgace_private) + { + /* A wired DAC state restore */ + rowaclEndPerformCheckFK(rel, rowacl_private); + + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlEndPerformCheckFK(rel, pgace_private); + break; + #endif + default: + break; + } + } + + /* + * pgaceAllowInlineFunction + * + * This hook gives guest a chance to make decision just before + * a set-returning function is inlined. + * + * arguments: + * - fnoid is oid of the function to be inlined. + * - func_tuple is tuple of the function stored in pg_proc. + */ + bool + pgaceAllowFunctionInlined(Oid fnoid, HeapTuple func_tuple) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + return sepgsqlAllowFunctionInlined(fnoid, func_tuple); + break; + #endif + default: + break; + } + return true; + } + + /****************************************************************** + * TABLE related hooks + ******************************************************************/ + + /* + * pgaceLockTable + * + * This hook is invoked when user tries to LOCK a table explicitly. + * The argument of relid shows the target relation id. + */ + void + pgaceLockTable(Oid relid) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlLockTable(relid); + break; + #endif + default: + break; + } + } + + /* + * pgaceExecTruncate + * + * This hook is invoked just before it truncate tables. + * The argument is a list of already opened relations with + * AccessExclusiveLock. + */ + void + pgaceExecTruncate(List *trunk_rels) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlExecTruncate(trunk_rels); + break; + #endif + default: + break; + } + } + + /****************************************************************** + * COPY TO/COPY FROM statement hooks + ******************************************************************/ + + /* + * pgaceCopyTable + * + * This hook is invoked before executing COPY TO/COPY FROM statement, + * to give the guest a chance to check tables/columns appeared in. + * + * arguments: + * - rel is the target relation of this COPY TO/FROM statement. + * It can be NULL, when COPY (SELECT ...) TO ... is given. + * - attNumList is a list of attribute number + * - isFrom is a bool to show the direction of the COPY + */ + void + pgaceCopyTable(Relation rel, List *attNumList, bool isFrom) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlCopyTable(rel, attNumList, isFrom); + break; + #endif + default: + break; + } + } + + /* + * pgaceCopyFile + * + * This hook is invoked just after a target file is opened + * at COPY TO/COPY FROM statement to give the guest a chance to + * check whether it allows to read/write the file. + * + * arguments: + * - rel is the target relation of this COPY TO/FROM statement. + * It can be NULL, when COPY (SELECT ...) TO ... is given. + * - isFrom is a bool to show the direction of the COPY + * - fdesc is the file descriptor of the target file opened. + * - filename is the filename of fdesc + */ + void + pgaceCopyFile(Relation rel, int fdesc, const char *filename, bool isFrom) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlCopyFile(rel, fdesc, filename, isFrom); + break; + #endif + default: + break; + } + } + + /* + * pgaceCopyToTuple + * + * This hook is invoked just before output of a fetched tuple on + * processing COPY TO statement, to give the guest a chance to make + * a decision whether the given tuple is visible, or not. + * If it returns false, the given tuple is not exported, as if it + * does not exist on the target relation. + * Elsewhere, + * + * arguments: + * - rel is the target relation of this + * - attNumList is a list of attribute number + * - tuple is a tuple to be checked + */ + bool + pgaceCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple) + { + /* A wired DAC check */ + if (!rowaclCopyToTuple(rel, attNumList, tuple)) + return false; + + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + return sepgsqlCopyToTuple(rel, attNumList, tuple); + break; + #endif + default: + break; + } + return true; + } + + /****************************************************************** + * Loadable shared library module hooks + ******************************************************************/ + + /* + * pgaceLoadSharedModule + * + * This hook is invoked before loading a shared library module, + * to give the guest a change to confirm whether the required + * module is safe, or not. + * + * This hook can be also invoked implicitly when a user tries + * to call a function implemented within external modules. + */ + void + pgaceLoadSharedModule(const char *filename) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlLoadSharedModule(filename); + break; + #endif + default: + break; + } + } + + /****************************************************************** + * Binary Large Object (BLOB) hooks + ******************************************************************/ + + /* + * pgaceLargeObjectCreate + * + * This hooks is invoked just before the first tuple of a new large + * object is inserted, to give the guest a change to make its + * decision and attach proper security context for the tuple. + * + * The argument of rel is the opened pg_largeobject system catalog. + */ + void + pgaceLargeObjectCreate(Relation rel, HeapTuple tuple) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlLargeObjectCreate(rel, tuple); + break; + #endif + default: + break; + } + } + + /* + * pgaceLargeObjectDrop + * + * This hook is invoked just before each tuple of a large object + * are deleted, to give the guest a change to make its decision. + * + * The argument of pgaceItem is an opaque data, the guest can + * use it discreationally. + */ + void + pgaceLargeObjectDrop(Relation rel, HeapTuple tuple, void **pgaceItem) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlLargeObjectDrop(rel, tuple, pgaceItem); + break; + #endif + default: + break; + } + } + + /* + * pgaceLargeObjectRead + * + * This hook is invoked at the head of lo_read(). + * If the guest allows a large object to have non-uniform security + * attributes (not a unique one for each page frame), using HeapTuple + * related hooks are more recommendable. + */ + void + pgaceLargeObjectRead(LargeObjectDesc *lodesc, int length) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlLargeObjectRead(lodesc, length); + break; + #endif + default: + break; + } + } + + /* + * pgaceLargeObjectWrite + * + * This hook is invoked at the head of lo_write(). + */ + void + pgaceLargeObjectWrite(LargeObjectDesc *lodesc, int length) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlLargeObjectWrite(lodesc, length); + break; + #endif + default: + break; + } + } + + /* + * pgaceLargeObjectTruncate + * + * This hook is invoked at the head of lo_truncate(). + */ + void + pgaceLargeObjectTruncate(LargeObjectDesc *lodesc, int offset) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlLargeObjectTruncate(lodesc, offset); + break; + #endif + default: + break; + } + } + + /* + * pgaceLargeObjectImport + * + * This hook is invoked just before importing the given file. + */ + void + pgaceLargeObjectImport(Oid loid, int fdesc, const char *filename) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlLargeObjectImport(loid, fdesc, filename); + break; + #endif + default: + break; + } + } + + /* + * pgaceLargeObjectExport + * + * This hook is invoked just before exporting the given large object. + */ + void + pgaceLargeObjectExport(Oid loid, int fdesc, const char *filename) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + sepgsqlLargeObjectExport(loid, fdesc, filename); + break; + #endif + default: + break; + } + } + + /* + * pgaceLargeObjectGetSecurity + * + * This hook is invoked when user requires to run lo_get_security() + * Note that PGACE assumes the security attribute of first page frame + * of large object represents its security attribute. + */ + void + pgaceLargeObjectGetSecurity(Relation rel, HeapTuple tuple) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + { + sepgsqlLargeObjectGetSecurity(rel, tuple); + return; + } + break; + #endif + default: + break; + } + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("no enhanced security feature is available."))); + } + + /* + * pgaceLargeObjectSetSecurity + * + * This hook is invoked when user requires to run lo_set_security(), + * for each tuple within a given large object, which have unchecked + * security attribute. In other word, PGACE does not require the guest + * to check permission toward same security attribute twice, or more. + */ + void + pgaceLargeObjectSetSecurity(Relation rel, HeapTuple newtup, HeapTuple oldtup) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + { + sepgsqlLargeObjectSetSecurity(rel, newtup, oldtup); + return; + } + break; + #endif + default: + break; + } + ereport(ERROR, + (errcode(ERRCODE_PGACE_ERROR), + errmsg("no enhanced security feature is available."))); + } + + /****************************************************************** + * Security Label hooks + ******************************************************************/ + + /* + * pgaceTupleDescHasRowAcl + * + * This hook enables to control the value of TupleDesc->tdhasrowacl. + * + */ + bool + pgaceTupleDescHasRowAcl(Relation rel, List *relopts) + { + return rowaclTupleDescHasRowAcl(rel, relopts); + } + + /* + * pgaceTupleDescHasSecurity + * + * This hook enables to control the value in TupleDesc->tdhasseclabel. + * If it returns true, sizeof(Oid) bytes are allocated at the header + * of HeapTupleHeader structure. + * + * The 'rel' argument can be NULL, when we make a decision for newly + * created relation via SELECT INTO/CREATE TABLE AS. In this case, + * unparsed relation options are delivered. + */ + bool + pgaceTupleDescHasSecLabel(Relation rel, List *relopts) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + return sepgsqlTupleDescHasSecLabel(rel, relopts); + break; + #endif + default: + break; + } + return false; + } + + /* + * pgaceTranslateSecurityLabelIn + * + * This hook enables the guest to translate a text representation + * of a given security attribute in external format into internal + * raw-format. It is invoked when user specifies security attribute + * explicitly in INSERT/UPDATE statement, to translate it into + * raw-internal format. + * + * It has to return a palloc()'ed Cstring, as a raw-internal format. + * + * In SE-PostgreSQL it supports translation in MLS/MCS labels like: + * "system_u:object_r:sepgsql_table_t:SystemHigh" + * <--> "system_u:object_r:sepgsql_table_t:s0:c0.c1023" + */ + char * + pgaceTranslateSecurityLabelIn(char *seclabel) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + return sepgsqlTranslateSecurityLabelIn(seclabel); + break; + #endif + default: + break; + } + return seclabel; + } + + /* + * pgaceTranslateSecurityLabelOut + * + * This hook enables the guest to translate a text representation + * of a given security attribute in internal format into cosmetic + * external format. + */ + char * + pgaceTranslateSecurityLabelOut(char *seclabel) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + return sepgsqlTranslateSecurityLabelOut(seclabel); + break; + #endif + default: + break; + } + return seclabel; + } + + /* + * pgaceValidateSecurityLabel + * + * This hook enables the guest to validate the given security attribute + * in raw-internal format. + */ + bool + pgaceCheckValidSecurityLabel(char *seclabel) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + return sepgsqlCheckValidSecurityLabel(seclabel); + break; + #endif + default: + break; + } + return false; + } + + /* + * pgaceUnlabeledSecurityLabel + * + * This hooks allows the guest to provide an alternative security + * attribute, when no valid text representation found on pg_security. + * The hooks has to return an alternative attribute palloc()'ed. + */ + char * + pgaceUnlabeledSecurityLabel(void) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + return sepgsqlUnlabeledSecurityLabel(); + break; + #endif + default: + break; + } + return NULL; + } + + /* + * pgaceSecurityLabelOfLabel + * + * This hook has to return the security attribute of a newly inserted + * tuple withing pg_security system catalog. Note that we need a special + * handling in the case of pg_security. If a new tuple requires a quite + * new security attribute which is not on pg_security, its insertion + * invokes one more insertion into pg_security. In the result, it makes + * infinite function invocation. + * This hook is used to avoid such a situation. The guest has to return + * a text represented security attribute. + */ + char * + pgaceSecurityLabelOfLabel(void) + { + switch (pgace_feature) + { + #ifdef HAVE_SELINUX + case PGACE_FEATURE_SELINUX: + if (sepgsqlIsEnabled()) + return sepgsqlSecurityLabelOfLabel(); + break; + #endif + default: + break; + } + return NULL; + } diff -Nrpc base/src/backend/security/rowacl/rowacl.c sepgsql/src/backend/security/rowacl/rowacl.c *** base/src/backend/security/rowacl/rowacl.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/rowacl/rowacl.c Fri Jan 16 10:31:05 2009 *************** *** 0 **** --- 1,721 ---- + /* + * src/backend/rowacl/rowacl.c + * Row-level Database ACLs support + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ + #include "postgres.h" + + #include "access/reloptions.h" + #include "catalog/namespace.h" + #include "catalog/pg_type.h" + #include "commands/defrem.h" + #include "miscadmin.h" + #include "nodes/nodeFuncs.h" + #include "parser/parsetree.h" + #include "security/pgace.h" + #include "storage/bufmgr.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" + #include "utils/syscache.h" + + #define ROWACL_ALL_PRIVS (ACL_SELECT | ACL_UPDATE | ACL_DELETE | ACL_REFERENCES) + + static void walkOnQueryTree(Query *query); + + /****************************************************************** + * Mark appeared Query/Sub-Query + ******************************************************************/ + + static bool walkOnNodeTree(Node *node, Query *query) + { + if (!node) + return false; + + if (IsA(node, RangeTblRef)) + { + RangeTblRef *rtr = (RangeTblRef *) node; + RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable); + + if (rte->rtekind == RTE_RELATION && + rtr->rtindex != query->resultRelation) + { + rte->pgaceTuplePerms |= ACL_SELECT; + } + else if (rte->rtekind == RTE_SUBQUERY) + { + walkOnQueryTree(rte->subquery); + } + } + else if (IsA(node, Query)) + { + walkOnQueryTree((Query *) node); + } + else if (IsA(node, SortGroupClause)) + { + /* + * expression_tree_walker() does not understand + * T_SortGroupClause node, so we have to avoid it + * walking on the node type. + */ + return false; + } + + return expression_tree_walker(node, walkOnNodeTree, (void *) query); + } + + static void walkOnQueryTree(Query *query) + { + RangeTblEntry *rte; + + if (query->commandType == CMD_UPDATE) + { + rte = rt_fetch(query->resultRelation, query->rtable); + rte->pgaceTuplePerms |= ACL_UPDATE; + if (query->returningList) + rte->pgaceTuplePerms |= ACL_SELECT; + } + else if (query->commandType == CMD_DELETE) + { + rte = rt_fetch(query->resultRelation, query->rtable); + rte->pgaceTuplePerms |= ACL_DELETE; + if (query->returningList) + rte->pgaceTuplePerms |= ACL_SELECT; + } + query_tree_walker(query, + walkOnNodeTree, + (void *) query, 0); + } + + List *rowaclPostQueryRewrite(List *queryList) + { + ListCell *l; + + foreach (l, queryList) + { + Query *query = (Query *) lfirst(l); + + Assert(IsA(query, Query)); + + if (query->commandType == CMD_SELECT || + query->commandType == CMD_UPDATE || + query->commandType == CMD_DELETE) + walkOnQueryTree(query); + } + + return queryList; + } + + /****************************************************************** + * Cache boost row-level ACLs checks + ******************************************************************/ + + static MemoryContext RowAclMemCtx; + + #define ROWACL_CACHE_SLOT_NUM 128 + static List *rowaclCacheSlot[ROWACL_CACHE_SLOT_NUM]; + + static void rowaclCacheReset(void) + { + int i; + + MemoryContextReset(RowAclMemCtx); + + for (i=0; i < ROWACL_CACHE_SLOT_NUM; i++) + rowaclCacheSlot[i] = NIL; + } + + typedef struct { + Oid relid; + Oid userid; + Oid aclid; + AclMode privs; + } rowaclCacheItem; + + static int rowaclCacheHash(Oid relid, Oid userid, Oid aclid) + { + Oid keys[3] = { relid, userid, aclid }; + + return tag_hash(keys, sizeof(keys)) % ROWACL_CACHE_SLOT_NUM; + } + + static void rowaclCacheInsert(Oid relid, Oid userid, Oid aclid, AclMode privs) + { + MemoryContext oldctx; + rowaclCacheItem *aci; + int index = rowaclCacheHash(relid, userid, aclid); + + oldctx = MemoryContextSwitchTo(RowAclMemCtx); + + aci = palloc0(sizeof(rowaclCacheItem)); + aci->relid = relid; + aci->userid = userid; + aci->aclid = aclid; + aci->privs = privs; + + rowaclCacheSlot[index] = lappend(rowaclCacheSlot[index], aci); + + MemoryContextSwitchTo(oldctx); + } + + static bool rowaclCacheLookup(Oid relid, Oid userid, Oid aclid, AclMode *privs) + { + ListCell *l; + int index = rowaclCacheHash(relid, userid, aclid); + + foreach (l, rowaclCacheSlot[index]) + { + rowaclCacheItem *aci = lfirst(l); + + if (aci->relid == relid && + aci->userid == userid && + aci->aclid == aclid) + { + *privs = aci->privs; + return true; + } + } + + return false; + } + + static void + rowaclSyscacheCallback(Datum arg, int cacheid, ItemPointer tuplePtr) + { + rowaclCacheReset(); + } + + void rowaclInitialize(bool is_bootstrap) + { + RowAclMemCtx = AllocSetContextCreate(TopMemoryContext, + "Row-level ACL result cache", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + CacheRegisterSyscacheCallback(AUTHOID, + rowaclSyscacheCallback, 0); + CacheRegisterSyscacheCallback(RELOID, + rowaclSyscacheCallback, 0); + rowaclCacheReset(); + } + + /****************************************************************** + * Row-level access controls + ******************************************************************/ + + struct rowaclUserInfoType { + Oid userid; + bool abort_on_error; + }; + struct rowaclUserInfoType *rowaclUserInfo = NULL; + + Datum rowaclBeginPerformCheckFK(Relation rel, bool is_primary, Oid save_userid) + { + Datum save_pgace = PointerGetDatum(rowaclUserInfo); + struct rowaclUserInfoType *uinfo + = palloc0(sizeof(struct rowaclUserInfoType)); + uinfo->userid = save_userid; + uinfo->abort_on_error = is_primary; + + rowaclUserInfo = uinfo; + + return save_pgace; + } + + void rowaclEndPerformCheckFK(Relation rel, Datum save_pgace) + { + rowaclUserInfo = (struct rowaclUserInfoType *) DatumGetPointer(save_pgace); + } + + static bool + rowaclCheckPermission(Relation rel, HeapTuple tuple, AclMode required) + { + Oid relid = RelationGetRelid(rel); + Oid ownerid = RelationGetForm(rel)->relowner; + Oid userid = GetUserId(); + Oid aclid = HeapTupleGetRowAcl(tuple); + AclMode privs; + + if (rowaclUserInfo) + { + userid = rowaclUserInfo->userid; + + /* + * If ACL_SELECT is given within FK constraint checks, + * its privilege is replaced to ACL_REFERENCES. + */ + if (required & ACL_SELECT) + { + required &= ~ACL_SELECT; + required |= ACL_REFERENCES; + } + } + + if (!rowaclCacheLookup(relid, userid, aclid, &privs)) + { + /* Superusers/Owner bypass all permission checking */ + if (superuser_arg(userid) || userid == ownerid) + { + privs = ROWACL_ALL_PRIVS; + } + else + { + Acl *acl = rowaclSidToSecurityAcl(aclid, ownerid); + + privs = aclmask(acl, userid, ownerid, ROWACL_ALL_PRIVS, ACLMASK_ALL); + } + rowaclCacheInsert(relid, userid, aclid, privs); + } + + if ((privs & required) == required) + return true; + + if (rowaclUserInfo && rowaclUserInfo->abort_on_error) + ereport(ERROR, + (errcode(ERRCODE_ROWACL_ERROR), + errmsg("access violation in row-level acl"))); + + return false; + } + + bool rowaclExecScan(Scan *scan, Relation rel, TupleTableSlot *slot) + { + AclMode required = scan->pgaceTuplePerms & ROWACL_ALL_PRIVS; + HeapTuple tuple; + + if (required==0 || !RelationGetRowLevelAcl(rel)) + return true; + + tuple = ExecMaterializeSlot(slot); + + return rowaclCheckPermission(rel, tuple, required); + } + + bool rowaclCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple) + { + if (!RelationGetRowLevelAcl(rel)) + return true; + + return rowaclCheckPermission(rel, tuple, ACL_SELECT); + } + + /****************************************************************** + * Check ownership of tuples + ******************************************************************/ + bool rowaclHeapTupleInsert(Relation rel, HeapTuple tuple, + bool is_internal, bool with_returning) + { + if (!HeapTupleHasRowAcl(tuple)) + return true; + + if (OidIsValid(HeapTupleGetRowAcl(tuple))) + { + if (RelationGetForm(rel)->relkind != RELKIND_RELATION) + ereport(ERROR, + (errcode(ERRCODE_ROWACL_ERROR), + errmsg("only general relation can have row-level ACL"))); + if (!is_internal && + !pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) + ereport(ERROR, + (errcode(ERRCODE_ROWACL_ERROR), + errmsg("Only owner or superuser can set ACL"))); + } + else + { + char *default_row_acl = RelationGetDefaultRowAcl(rel); + + if (default_row_acl) + { + FmgrInfo finfo; + Datum aclDat; + Oid sid; + + fmgr_info(F_ARRAY_IN, &finfo); + aclDat = FunctionCall3(&finfo, + CStringGetDatum(default_row_acl), + ObjectIdGetDatum(ACLITEMOID), + Int32GetDatum(-1)); + sid = rowaclSecurityAclToSid(DatumGetAclP(aclDat)); + HeapTupleSetRowAcl(tuple, sid); + } + } + + return true; + } + + static HeapTuple + getHeapTupleFromItemPointer(Relation rel, ItemPointer tid) + { + /* + * obtain an old tuple + */ + Buffer buffer; + PageHeader dp; + ItemId lp; + HeapTupleData tuple; + HeapTuple oldtup; + + buffer = ReadBuffer(rel, ItemPointerGetBlockNumber(tid)); + LockBuffer(buffer, BUFFER_LOCK_SHARE); + + dp = (PageHeader) BufferGetPage(buffer); + lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid)); + + Assert(ItemIdIsNormal(lp)); + + tuple.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp); + tuple.t_len = ItemIdGetLength(lp); + tuple.t_self = *tid; + tuple.t_tableOid = RelationGetRelid(rel); + oldtup = heap_copytuple(&tuple); + + LockBuffer(buffer, BUFFER_LOCK_UNLOCK); + ReleaseBuffer(buffer); + + return oldtup; + } + + bool rowaclHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup, + bool is_internal, bool with_returning) + { + HeapTuple oldtup = getHeapTupleFromItemPointer(rel, otid); + + if (!HeapTupleHasRowAcl(newtup)) + return true; + + if (!OidIsValid(HeapTupleGetRowAcl(newtup))) + { + /* preserve old ACL */ + HeapTupleSetRowAcl(newtup, HeapTupleGetRowAcl(oldtup)); + } + else if (HeapTupleGetRowAcl(newtup) != HeapTupleGetRowAcl(oldtup)) + { + if (!is_internal && + !pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) + ereport(ERROR, + (errcode(ERRCODE_ROWACL_ERROR), + errmsg("Only owner or superuser can set ACL"))); + } + return true; + } + + bool rowaclHeapTupleDelete(Relation rel, ItemPointer otid, + bool is_internal, bool with_returning) + { + /* + * Do nothing here + */ + return true; + } + + /****************************************************************** + * Relation Options + ******************************************************************/ + void + rawaclValidateDefaultRowAclRelopt(const char *value) + { + FmgrInfo finfo; + Datum acldat; + + /* + * If given default row-acl in reloptions is not valid, + * aclitemin can raise an error. + */ + fmgr_info(F_ARRAY_IN, &finfo); + acldat = FunctionCall3(&finfo, + CStringGetDatum(value), + ObjectIdGetDatum(ACLITEMOID), + Int32GetDatum(-1)); + pfree(DatumGetAclP(acldat)); + } + + /****************************************************************** + * Row-level ACLs management + ******************************************************************/ + + bool rowaclTupleDescHasRowAcl(Relation rel, List *relopts) + { + ListCell *l; + + if (rel) + return RelationGetRowLevelAcl(rel); + + /* SELECT INTO cases */ + foreach (l, relopts) + { + DefElem *def = (DefElem *) lfirst(l); + + if (pg_strcasecmp(def->defname, "row_level_acl") == 0) + return defGetBoolean(def); + } + + return false; + } + + static Acl * + rowaclDefaultAclArray(Oid ownerId) + { + Acl *acl; + AclItem *aip; + + /* + * All permissions to public in default + */ + acl = allocacl(1); + aip = ACL_DAT(acl); + aip->ai_grantee = ACL_ID_PUBLIC; + aip->ai_grantor = ownerId; + aip->ai_privs = ROWACL_ALL_PRIVS; + + return acl; + } + + static bool + rowaclCheckValidSecurityAcl(const char *raw_acl) + { + char *copy, *tok, *sv = NULL; + AclItem ai; + + if (strncmp(raw_acl, "acl:", 4) != 0) + return false; + + copy = pstrdup(raw_acl + 4); + for (tok = strtok_r(copy, ",", &sv); + tok; + tok = strtok_r(NULL, ",", &sv)) + { + if (sscanf(tok, "%x=%x/%x", + &ai.ai_grantee, + &ai.ai_privs, + &ai.ai_grantor) != 3) + return false; + } + + return true; + } + + static Acl * + rawAclTextToAclArray(const char *raw_acl) + { + Acl *acl; + AclItem *aip; + char *copy, *tok, *sv = NULL; + int num = 0; + + Assert(strncmp(raw_acl, "acl:", 4) == 0); + + copy = pstrdup(raw_acl + 4); + aip = palloc(strlen(copy) * sizeof(AclItem) / 4); + for (tok = strtok_r(copy, ",", &sv); + tok; + tok = strtok_r(NULL, ",", &sv)) + { + if (sscanf(tok, "%x=%x/%x", + &aip[num].ai_grantee, + &aip[num].ai_privs, + &aip[num].ai_grantor) != 3) + continue; + num++; + } + + acl = allocacl(num); + memcpy(ACL_DAT(acl), aip, num * sizeof(AclItem)); + + pfree(aip); + pfree(copy); + + check_acl(acl); + + return acl; + } + + static char * + rawAclTextFromAclArray(Acl *acl) + { + AclItem *aip = ACL_DAT(acl); + char *raw_acl = palloc0(ACL_NUM(acl) * 30 + 10); /* enough length */ + int i, ofs; + + ofs = sprintf(raw_acl, "acl:"); + + for (i = 0; i < ACL_NUM(acl); i++) + { + if ((aip[i].ai_privs & ROWACL_ALL_PRIVS) != aip[i].ai_privs) + ereport(ERROR, + (errcode(ERRCODE_ROWACL_ERROR), + errmsg("unsupported privileges in row_acl: %04x", + aip[i].ai_privs & ~ROWACL_ALL_PRIVS))); + + ofs += sprintf(raw_acl + ofs, + "%s%x=%x/%x", + (i == 0 ? "" : ","), + aip[i].ai_grantee, + aip[i].ai_privs, + aip[i].ai_grantor); + } + + return raw_acl; + } + + Acl *rowaclSidToSecurityAcl(Oid sid, Oid ownerId) + { + char *raw_acl + = pgaceLookupSecurityLabel(sid); + + if (!raw_acl || !rowaclCheckValidSecurityAcl(raw_acl)) + return rowaclDefaultAclArray(ownerId); + + return rawAclTextToAclArray(raw_acl); + } + + Oid rowaclSecurityAclToSid(Acl *acl) + { + char *raw_acl + = rawAclTextFromAclArray(acl); + + return pgaceLookupSecurityId(raw_acl); + } + + Datum rowaclHeapGetSecurityAclSysattr(HeapTuple tuple) + { + HeapTuple rtup; + Oid rowaclSid = InvalidOid; + Oid relowner; + char relkind; + Datum reloptions; + bool isnull; + + rtup = SearchSysCache(RELOID, + ObjectIdGetDatum(tuple->t_tableOid), + 0, 0, 0); + if (!HeapTupleIsValid(rtup)) + elog(ERROR, "cache lookup failed for relation %u", tuple->t_tableOid); + + relowner = ((Form_pg_class) GETSTRUCT(rtup))->relowner; + relkind = ((Form_pg_class) GETSTRUCT(rtup))->relkind; + reloptions = SysCacheGetAttr(RELOID, rtup, + Anum_pg_class_reloptions, + &isnull); + if (!isnull) + { + StdRdOptions *RdOpts + = (StdRdOptions *) heap_reloptions(relkind, reloptions, false); + + if (RdOpts && RdOpts->row_level_acl) + rowaclSid = HeapTupleGetRowAcl(tuple); + } + + ReleaseSysCache(rtup); + + return PointerGetDatum(rowaclSidToSecurityAcl(rowaclSid, relowner)); + } + + /****************************************************************** + * SQL functions + ******************************************************************/ + + static Datum rowacl_grant_revoke(PG_FUNCTION_ARGS, bool grant, bool cascade) + { + char *input, *tok, *sv = NULL; + HeapTuple tuple; + Acl *acl; + Oid ownerid; + List *grantees = NIL; + AclMode privileges = 0; + + /* + * Get owner Id; + */ + tuple = SearchSysCache(RELOID, + PG_GETARG_DATUM(0), 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", + PG_GETARG_OID(0)); + + ownerid = ((Form_pg_class) GETSTRUCT(tuple))->relowner; + ReleaseSysCache(tuple); + + /* + * Extract Acl array + */ + acl = PG_GETARG_ACL_P(1); + + /* + * Extract usernames + */ + input = TextDatumGetCString(PG_GETARG_TEXT_P(2)); + for (tok = strtok_r(input, ",", &sv); + tok; + tok = strtok_r(NULL, ",", &sv)) + { + if (strcasecmp(tok, "public") == 0) + grantees = lappend_oid(grantees, ACL_ID_PUBLIC); + else + { + Oid roleid = get_roleid(tok); + + if (roleid == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_ROWACL_ERROR), + errmsg("%s is not a valid identifier", tok))); + + grantees = lappend_oid(grantees, roleid); + } + } + /* + * Extract permission names + */ + input = TextDatumGetCString(PG_GETARG_TEXT_P(3)); + for (tok = strtok_r(input, ",", &sv); + tok; + tok = strtok_r(NULL, ",", &sv)) + { + if (strcasecmp(tok, "all") == 0) + privileges |= ROWACL_ALL_PRIVS; + else if (strcasecmp(tok, "select") == 0) + privileges |= ACL_SELECT; + else if (strcasecmp(tok, "update") == 0) + privileges |= ACL_UPDATE; + else if (strcasecmp(tok, "delete") == 0) + privileges |= ACL_DELETE; + else if (strcasecmp(tok, "references") == 0) + privileges |= ACL_REFERENCES; + else + ereport(ERROR, + (errcode(ERRCODE_ROWACL_ERROR), + errmsg("%s is not a valid permission", tok))); + } + + /* + * Merge ACL + */ + acl = merge_acl_with_grant(acl, grant, false, cascade, + grantees, privileges, + GetUserId(), ownerid); + + PG_RETURN_ACL_P(acl); + } + + Datum + rowacl_grant(PG_FUNCTION_ARGS) + { + return rowacl_grant_revoke(fcinfo, true, false); + } + + Datum + rowacl_revoke(PG_FUNCTION_ARGS) + { + return rowacl_grant_revoke(fcinfo, false, false); + } + + Datum + rowacl_revoke_cascade(PG_FUNCTION_ARGS) + { + return rowacl_grant_revoke(fcinfo, false, true); + } diff -Nrpc base/src/backend/security/sepgsql/avc.c sepgsql/src/backend/security/sepgsql/avc.c *** base/src/backend/security/sepgsql/avc.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/avc.c Thu Jan 22 14:09:37 2009 *************** *** 0 **** --- 1,1200 ---- + /* + * src/backend/security/sepgsql/avc.c + * SE-PostgreSQL userspace access vector cache + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ + #include "postgres.h" + + #include "access/hash.h" + #include "libpq/pqsignal.h" + #include "postmaster/postmaster.h" + #include "security/pgace.h" + #include "storage/ipc.h" + #include "storage/lwlock.h" + #include "utils/memutils.h" + #include "utils/syscache.h" + #include + #include + #include + #include + + /* + * uAVC: userspace Access Vector Cache + * + * SE-PostgreSQL makes inqueries for SELinux to check whether the security + * policy allows the required action, or not. However, it need to invoke + * system call because SELinux is a kernel feature and it hold its security + * policy in the kernel memory. + * + * uAVC enables to reduce the number of kernel invocation, with caching + * the result of inquiries. When we have to make a decision based on the + * security policy of SELinux, it tries to find up an appropriate cache + * entry on the uAVC. If exist, we don't need to invoke a system call + * and can reduce unnecessary overhead. + * + * If not exist, SE-PostgreSQL makes a new cache entry based on the + * result of inquiries, and chains it on uAVC to prepare the following + * decision makings. + * + * uAVC has a version number to check whether it is now valid, or not. + * Not need to say, uAVC cache entry has to be invalid just after + * policy reloaded or state change. + * If it is not match the latest one, updated by the policy state + * monitoring process, uAVC has to be reseted. + */ + + /* + * Dynamic object class/access vector mapping + * + * SELinux exports the list of object classes (it means kind of object, like + * file or table) and access vectors (it means permission set, like read, + * select, ...) under /selinux/class. + * It enables to provide userspace object managers a interface to get what + * codes should be used to ask SELinux. + * + * libselinux provides an API to translate a string expression and a code + * used by the loaded security policy. These correspondences are not assured + * over the bound of policy loading, so we have to reload the mapping after + * in-kernel policy is reloaded, or its state is changed. + */ + static struct + { + struct + { + const char *name; + security_class_t internal; + } tclass; + struct + { + char *name; + access_vector_t internal; + } av_perms[sizeof(access_vector_t) * 8]; + } selinux_catalog[] = { + { + { "db_database", SECCLASS_DB_DATABASE}, + { + { "create", DB_DATABASE__CREATE }, + { "drop", DB_DATABASE__DROP }, + { "getattr", DB_DATABASE__GETATTR }, + { "setattr", DB_DATABASE__SETATTR }, + { "relabelfrom", DB_DATABASE__RELABELFROM }, + { "relabelto", DB_DATABASE__RELABELTO }, + { "access", DB_DATABASE__ACCESS }, + { "install_module", DB_DATABASE__INSTALL_MODULE }, + { "load_module", DB_DATABASE__LOAD_MODULE }, + { "get_param", DB_DATABASE__GET_PARAM }, + { "set_param", DB_DATABASE__SET_PARAM }, + { NULL, 0UL }, + } + }, + { + { "db_table", SECCLASS_DB_TABLE}, + { + { "create", DB_TABLE__CREATE }, + { "drop", DB_TABLE__DROP }, + { "getattr", DB_TABLE__GETATTR }, + { "setattr", DB_TABLE__SETATTR }, + { "relabelfrom", DB_TABLE__RELABELFROM }, + { "relabelto", DB_TABLE__RELABELTO }, + { "use", DB_TABLE__USE }, + { "select", DB_TABLE__SELECT }, + { "update", DB_TABLE__UPDATE }, + { "insert", DB_TABLE__INSERT }, + { "delete", DB_TABLE__DELETE }, + { "lock", DB_TABLE__LOCK }, + { NULL, 0UL }, + } + }, + { + { "db_procedure", SECCLASS_DB_PROCEDURE}, + { + { "create", DB_PROCEDURE__CREATE }, + { "drop", DB_PROCEDURE__DROP }, + { "getattr", DB_PROCEDURE__GETATTR }, + { "setattr", DB_PROCEDURE__SETATTR }, + { "relabelfrom", DB_PROCEDURE__RELABELFROM }, + { "relabelto", DB_PROCEDURE__RELABELTO }, + { "execute", DB_PROCEDURE__EXECUTE }, + { "entrypoint", DB_PROCEDURE__ENTRYPOINT }, + { "install", DB_PROCEDURE__INSTALL }, + { NULL, 0UL }, + } + }, + { + { "db_column", SECCLASS_DB_COLUMN}, + { + { "create", DB_COLUMN__CREATE }, + { "drop", DB_COLUMN__DROP }, + { "getattr", DB_COLUMN__GETATTR }, + { "setattr", DB_COLUMN__SETATTR }, + { "relabelfrom", DB_COLUMN__RELABELFROM }, + { "relabelto", DB_COLUMN__RELABELTO }, + { "use", DB_COLUMN__USE }, + { "select", DB_COLUMN__SELECT }, + { "update", DB_COLUMN__UPDATE }, + { "insert", DB_COLUMN__INSERT }, + { NULL, 0UL }, + } + }, + { + { "db_tuple", SECCLASS_DB_TUPLE }, + { + { "relabelfrom", DB_TUPLE__RELABELFROM}, + { "relabelto", DB_TUPLE__RELABELTO}, + { "use", DB_TUPLE__USE}, + { "select", DB_TUPLE__SELECT}, + { "update", DB_TUPLE__UPDATE}, + { "insert", DB_TUPLE__INSERT}, + { "delete", DB_TUPLE__DELETE}, + { NULL, 0UL}, + } + }, + { + { "db_blob", SECCLASS_DB_BLOB }, + { + { "create", DB_BLOB__CREATE}, + { "drop", DB_BLOB__DROP}, + { "getattr", DB_BLOB__GETATTR}, + { "setattr", DB_BLOB__SETATTR}, + { "relabelfrom", DB_BLOB__RELABELFROM}, + { "relabelto", DB_BLOB__RELABELTO}, + { "read", DB_BLOB__READ}, + { "write", DB_BLOB__WRITE}, + { "import", DB_BLOB__IMPORT}, + { "export", DB_BLOB__EXPORT}, + { NULL, 0UL}, + } + }, + }; + + static MemoryContext AvcMemCtx; + + #define AVC_HASH_NUM_SLOTS 256 + #define AVC_HASH_NUM_NODES 600 + + static sig_atomic_t avc_version; + static bool avc_enforcing; + + typedef struct + { + uint32 hash_key; + + security_class_t tclass; + Oid tsid; /* target security context */ + + security_context_t ncontext; /* newcontext */ + Oid nsid; /* newly created security context */ + + access_vector_t allowed; + access_vector_t decided; + access_vector_t auditallow; + access_vector_t auditdeny; + + bool hot_cache; + } avc_datum; + + typedef struct avc_page + { + struct avc_page *prev; + struct avc_page *next; + + security_context_t scontext; + + List *slot[AVC_HASH_NUM_SLOTS]; + + uint32 lru_hint; + } avc_page; + + static avc_page *current_avc_page = NULL; + static uint32 avc_datum_count = 0; + + /* + * selinux_state + * + * This structure shows the global state of SELinux and its security + * policy, and it is assigned on shared memory region. + * + * The most significant variable is selinux_state->version. + * Any instance can refer this variable to confirm current sequence + * number of policy state, without locking. + * + * The only process able to update this variable is policy state + * monitoring process forked by postmaster. It can receive notifications + * from the kernel via netlink socket, and it update selinux_state->version + * to encourage any instance to reflush its uAVC. + * + * When we read rest of variable, we have to hold SepgsqlAvcLock LWlock + * as a reader. enforceing shows the current SELinux working mode. + * catalog shows the mapping set of security classes and access vectors. + */ + struct + { + /* + * only state monitoring process can update version. + * any other process can read it without locks. + */ + volatile sig_atomic_t version; + + bool enforcing; + + struct + { + struct + { + security_class_t internal; + security_class_t external; + } tclass; + struct + { + access_vector_t internal; + access_vector_t external; + } av_perms[sizeof(access_vector_t) * 8]; + } catalog[lengthof(selinux_catalog)]; + } *selinux_state = NULL; + + Size + sepgsqlShmemSize(void) + { + return sizeof(*selinux_state); + } + + /* + * load_class_av_mapping + * + * This function rebuild the mapping set of security classes and access + * vectors on selinux_state. It has to be invoked by the policy state + * monitoring process with SepgsqlAvcLock in LW_EXCLUSIVE. + */ + static void + load_class_av_mapping(void) + { + int i, j; + + memset(selinux_state->catalog, 0, sizeof(selinux_state->catalog)); + + for (i = 0; i < lengthof(selinux_catalog); i++) + { + selinux_state->catalog[i].tclass.internal + = selinux_catalog[i].tclass.internal; + selinux_state->catalog[i].tclass.external + = string_to_security_class(selinux_catalog[i].tclass.name); + + for (j = 0; selinux_catalog[i].av_perms[j].name; j++) + { + selinux_state->catalog[i].av_perms[j].internal + = selinux_catalog[i].av_perms[j].internal; + selinux_state->catalog[i].av_perms[j].external + = string_to_av_perm(selinux_state->catalog[i].tclass.external, + selinux_catalog[i].av_perms[j].name); + } + } + } + + /* + * trans_to_external_tclass + * translates internal object class number into external one + * needed to communicate with in-kernel SELinux. + */ + static security_class_t + trans_to_external_tclass(security_class_t i_tclass) + { + /* have to hold SepgsqlAvcLock with LW_SHARED */ + int i; + + for (i = 0; i < lengthof(selinux_catalog); i++) + { + if (selinux_state->catalog[i].tclass.internal == i_tclass) + return selinux_state->catalog[i].tclass.external; + } + return i_tclass; /* use it as is for kernel classes */ + } + + /* + * trans_to_internal_perms + * translates external permission bits into internal ones + * needed to understand the answer from in-kernel SELinux. + * If in-kernel SELinux doesn't define required permissions, + * it sets/clears undefined bits based on caller's preference. + * It enables SE-PostgreSQL to work on legacy security policy. + */ + static access_vector_t + trans_to_internal_perms(security_class_t e_tclass, access_vector_t e_perms, + bool set_if_undefined) + { + /* have to hold SepgsqlAvcLock with LW_SHARED */ + access_vector_t i_perms = 0UL; + access_vector_t undef_mask = 0UL; + int i, j; + + for (i = 0; i < lengthof(selinux_catalog); i++) + { + if (selinux_state->catalog[i].tclass.external != e_tclass) + continue; + + for (j = 0; j < sizeof(access_vector_t) * 8; j++) + { + if (selinux_state->catalog[i].av_perms[j].external == 0) + undef_mask |= (1UL << j); + else if (selinux_state->catalog[i].av_perms[j].external & e_perms) + i_perms |= selinux_state->catalog[i].av_perms[j].internal; + } + + if (set_if_undefined) + i_perms |= undef_mask; + else + i_perms &= ~undef_mask; + + return i_perms; + } + return e_perms; /* use it as is for kernel classes */ + } + + /* + * sepgsql_class_to_string + * sepgsql_av_perm_to_string + * returns string representation of given object class and permission. + * Please note that given code have internal ones, so we cannot use + * libselinux's facility, because it assumes 'external code'. + * (Kernel object classes are ABI, so these are stable.) + */ + static const char * + sepgsql_class_to_string(security_class_t tclass) + { + int i; + + for (i = 0; i < lengthof(selinux_catalog); i++) + { + if (selinux_catalog[i].tclass.internal == tclass) + return selinux_catalog[i].tclass.name; + } + /* + * tclass is stable for kernel object classes. + */ + return security_class_to_string(tclass); + } + + static const char * + sepgsql_av_perm_to_string(security_class_t tclass, access_vector_t perm) + { + int i, j; + + for (i = 0; i < lengthof(selinux_catalog); i++) + { + if (selinux_catalog[i].tclass.internal == tclass) + { + char *perm_name; + + for (j = 0; (perm_name = selinux_catalog[i].av_perms[j].name); j++) + { + if (selinux_catalog[i].av_perms[j].internal == perm) + return perm_name; + } + return "unknown"; + } + } + /* + * tclass/perms are stable for kernel object classes. + */ + return security_av_perm_to_string(tclass, perm); + } + + /* + * sepgsql_avc_reset + * clears all uAVC entries and update its version. + */ + static void + sepgsql_avc_reset(void) + { + MemoryContextReset(AvcMemCtx); + + LWLockAcquire(SepgsqlAvcLock, LW_SHARED); + + avc_version = selinux_state->version; + switch (sepostgresql_mode) + { + case SEPGSQL_MODE_DEFAULT: + avc_enforcing = selinux_state->enforcing; + break; + case SEPGSQL_MODE_PERMISSIVE: + avc_enforcing = false; + break; + case SEPGSQL_MODE_ENFORCING: + avc_enforcing = false; + break; + default: + elog(FATAL, "SELinux: undefined state in SE-PostgreSQL"); + break; + } + + current_avc_page = NULL; + + avc_datum_count = 0; + + LWLockRelease(SepgsqlAvcLock); + + sepgsqlAvcSwitchClientContext(sepgsqlGetClientContext()); + } + + /* + * sepgsql_avc_reclaim + * reclaims recently unused uAVC entries, when the number of + * caches overs AVC_HASH_NUM_NODES. + */ + static void + sepgsql_avc_reclaim(void) + { + ListCell *l; + avc_page *avp; + avc_datum *cache; + int loop; + + Assert(current_avc_page != NULL); + + for (avp = current_avc_page->next; true; avp = avp->next) + { + for (loop = 0; loop < AVC_HASH_NUM_SLOTS; loop++) + { + if (avc_datum_count < AVC_HASH_NUM_NODES) + return; + + avp->lru_hint = (avp->lru_hint + 1) % AVC_HASH_NUM_SLOTS; + foreach (l, avp->slot[avp->lru_hint]) + { + cache = lfirst(l); + + if (cache->hot_cache) + { + cache->hot_cache = false; + continue; + } + + list_delete_ptr(avp->slot[avp->lru_hint], cache); + pfree(cache); + avc_datum_count--; + } + } + } + } + + /* + * avc_audit_common + * generates an audit message on the give string buffer based on + * the given av_decision which means the resutl of permission checks. + */ + static bool + avc_audit_common(char *buffer, uint32 buflen, + avc_datum *cache, access_vector_t perms, + const char *scontext, const char *tcontext, const char *objname) + { + access_vector_t denied, audited, mask; + security_context_t svcon, tvcon; + uint32 ofs = 0; + + denied = perms & ~cache->allowed; + audited = denied ? (denied & cache->auditdeny) : (perms & cache->auditallow); + + if (audited == 0) + return false; + + ofs += snprintf(buffer + ofs, buflen - ofs, "%s {", + denied ? "denied" : "granted"); + for (mask = 1; mask != 0; mask <<= 1) + { + if ((audited & mask) != 0) + ofs += snprintf(buffer + ofs, buflen - ofs, " %s", + sepgsql_av_perm_to_string(cache->tclass, mask)); + } + ofs += snprintf(buffer + ofs, buflen - ofs, " } "); + + if (!scontext) + svcon = sepgsqlTranslateSecurityLabelOut(current_avc_page->scontext); + else + svcon = sepgsqlTranslateSecurityLabelOut(scontext); + + if (!tcontext) + tvcon = pgaceSidToSecurityLabel(cache->tsid); + else + tvcon = sepgsqlTranslateSecurityLabelOut(tcontext); + + ofs += snprintf(buffer + ofs, buflen - ofs, + "scontext=%s tcontext=%s tclass=%s", + svcon, tvcon, sepgsql_class_to_string(cache->tclass)); + + pfree(svcon); + pfree(tvcon); + if (objname) + ofs += snprintf(buffer + ofs, buflen - ofs, " name=%s", objname); + + return true; + } + + /* + * avc_permission_common + * makes decision and output audit messages based on given avc_datum. + * If required permissions are not completely allowed, it raises an + * error or returns 'false' when permissive mode. + */ + static bool + avc_permission_common(avc_datum *cache, access_vector_t perms, bool abort, + const char *scontext, const char *tcontext, const char *objname) + { + char audit_buffer[2048]; + access_vector_t denied; + bool audit; + bool rc = true; + + audit = avc_audit_common(audit_buffer, sizeof(audit_buffer), + cache, perms, scontext, tcontext, objname); + + denied = perms & ~cache->allowed; + if (!perms || denied) + { + if (avc_enforcing) + rc = false; + else + { + /* + * In permissive mode, once denied permissions are + * allowed to avoid a flood of denied logs. + */ + cache->allowed |= perms; + } + } + + if (audit) + { + ereport((!rc && abort) ? ERROR : NOTICE, + (errcode(ERRCODE_SELINUX_AUDIT), + errmsg("SELinux: %s", audit_buffer))); + } + else if (!rc && abort) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_AUDIT), + errmsg("SELinux: security policy violation"))); + + return rc; + } + + /* + * avc_make_entry + * makes a query to in-kernel SELinux and an avc_datum object to + * cache the result of SELinux's decision for access rights and + * default security context. + */ + #define avc_hash_key(tsid,tclass) ((tsid) ^ ((tclass) << 2)) + + static avc_datum * + avc_make_entry(Oid tsid, security_class_t tclass) + { + security_context_t scontext, tcontext, ncontext; + security_class_t e_tclass; + MemoryContext oldctx = MemoryContextSwitchTo(AvcMemCtx); + struct av_decision avd; + avc_datum *cache; + uint32 hash_key, index; + + hash_key = avc_hash_key(tsid, tclass); + index = hash_key % AVC_HASH_NUM_SLOTS; + + cache = palloc0(sizeof(avc_datum)); + cache->hash_key = hash_key; + cache->tsid = tsid; + cache->tclass = tclass; + + scontext = current_avc_page->scontext; + tcontext = pgaceLookupSecurityLabel(tsid); + if (!tcontext || !pgaceCheckValidSecurityLabel(tcontext)) + tcontext = pgaceUnlabeledSecurityLabel(); + + LWLockAcquire(SepgsqlAvcLock, LW_SHARED); + + e_tclass = trans_to_external_tclass(tclass); + + if (security_compute_av_raw(scontext, tcontext, e_tclass, 0, &avd) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not compute av_decision: " + "scontext=%s tcontext=%s tclass=%s", + scontext, tcontext, sepgsql_class_to_string(tclass)))); + + cache->allowed = trans_to_internal_perms(e_tclass, avd.allowed, true); + cache->decided = trans_to_internal_perms(e_tclass, avd.decided, false); + cache->auditallow = trans_to_internal_perms(e_tclass, avd.auditallow, false); + cache->auditdeny = trans_to_internal_perms(e_tclass, avd.auditdeny, false); + cache->hot_cache = true; + + if (security_compute_create_raw(scontext, tcontext, e_tclass, &ncontext) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not compute new context: " + "scontext=%s tcontext=%s tclass=%s", + scontext, tcontext, sepgsql_class_to_string(tclass)))); + pfree(tcontext); + + LWLockRelease(SepgsqlAvcLock); + + PG_TRY(); + { + cache->ncontext = pstrdup(ncontext); + } + PG_CATCH(); + { + freecon(ncontext); + PG_RE_THROW(); + } + PG_END_TRY(); + + freecon(ncontext); + + sepgsql_avc_reclaim(); + + current_avc_page->slot[index] + = lcons(cache, current_avc_page->slot[index]); + + avc_datum_count++; + + MemoryContextSwitchTo(oldctx); + + return cache; + } + + static avc_datum *avc_lookup(Oid tsid, security_class_t tclass) + { + avc_datum *cache = NULL; + uint32 hash_key, index; + ListCell *l; + + /* + * check avc invalidation + */ + if (avc_version != selinux_state->version) + sepgsql_avc_reset(); + + /* + * lookup avc entry + */ + hash_key = avc_hash_key(tsid, tclass); + index = hash_key % AVC_HASH_NUM_SLOTS; + + foreach (l, current_avc_page->slot[index]) + { + cache = lfirst(l); + if (cache->hash_key == hash_key + && cache->tclass == tclass + && cache->tsid == tsid) + { + cache->hot_cache = true; + return cache; + } + } + return NULL; + } + + /* + * sepgsqlAvcSwitchClientContext() + * switches current avc_page. + * + * NOTE: In most cases, SE-PostgreSQL checks whether client is allowed + * to do required actions (like SELECT, UPDATE, ...) on the targets. + * Both of client and targets have its security context, and all rules + * are described as relationship between security context of a client, + * a target and kind of actions. + * However, the security context of client is unchanged in SE-PostgreSQL + * (an exception is invocation of trusted procedure), so we can omit + * to compare security context of client with entries of uAVC. + * The avc_page is a set of avc_datum sorted out by the security context + * of client, so we can lookup correct avc_datum on currently focued + * avc_page without comparing the security context of client. + * The reason why we don't not use a unique uAVC is the security context + * of client does not have its security identifier on pg_security, so + * it requires strcmp() for each entries, but it is heavier than integer + * comparisons. + * Thus we have to switch current avc_page, whenever the security context + * of client changes (via trusted procedure). It makes performance well + * in most cases. + */ + void sepgsqlAvcSwitchClientContext(security_context_t newcontext) + { + MemoryContext oldctx; + avc_page *avp; + int i; + + if (current_avc_page) + { + avp = current_avc_page; + do { + if (!strcmp(avp->scontext, newcontext)) + { + current_avc_page = avp; + return; + } + avp = avp->next; + } while (avp != current_avc_page); + } + + /* create a new avc_page */ + oldctx = MemoryContextSwitchTo(AvcMemCtx); + avp = palloc0(sizeof(avc_page)); + avp->scontext = pstrdup(newcontext); + MemoryContextSwitchTo(oldctx); + + for (i=0; i < AVC_HASH_NUM_SLOTS; i++) + avp->slot[i] = NIL; + + if (!current_avc_page) + { + avp->next = avp->prev = avp; + } + else + { + avp->next = current_avc_page; + avp->prev = current_avc_page->prev; + avp->prev->next = avp; + avp->next->prev = avp; + } + current_avc_page = avp; + } + + /* + * sepgsqlClientHasPermission + * checks client's privileges on given objects via uAVC. + * It raised an error, if required actions are violated. + */ + void + sepgsqlClientHasPermission(Oid tsid, security_class_t tclass, + access_vector_t perms, + const char *objname) + { + avc_datum *cache = avc_lookup(tsid, tclass); + + if (!cache) + cache = avc_make_entry(tsid, tclass); + + avc_permission_common(cache, perms, true, NULL, NULL, objname); + } + + /* + * sepgsqlClientHasPermissionNoAbort + * checks client's privileges on given objects via uAVC. + * It returns false, if required actions are violated. + */ + bool + sepgsqlClientHasPermissionNoAbort(Oid tsid, security_class_t tclass, + access_vector_t perms, + const char *objname) + { + avc_datum *cache = avc_lookup(tsid, tclass); + + if (!cache) + cache = avc_make_entry(tsid, tclass); + + return avc_permission_common(cache, perms, false, NULL, NULL, objname); + } + + /* + * sepgsqlClientCreateSid + * returns security identifier of newly created database object. + * Please note that you don't have to invoke this function for + * object classes except for database objects. It have a possibility + * to make an entry on pg_security via pgaceSecurityLabelToSid(), + * but it should be restricted to database object. + */ + Oid + sepgsqlClientCreateSid(Oid tsid, security_class_t tclass) + { + avc_datum *cache = avc_lookup(tsid, tclass); + + if (!cache || cache->nsid == InvalidOid) + { + if (!cache) + cache = avc_make_entry(tsid, tclass); + cache->nsid = pgaceSecurityLabelToSid(cache->ncontext); + } + return cache->nsid; + } + + /* + * sepgsqlClientCreateContext + * returns security context (string representation) of newly + * created object. It is available for any kind of object + * classes. + */ + security_context_t + sepgsqlClientCreateContext(Oid tsid, security_class_t tclass) + { + avc_datum *cache = avc_lookup(tsid, tclass); + + if (!cache) + cache = avc_make_entry(tsid, tclass); + + return pstrdup(cache->ncontext); + } + + /* + * sepgsql_shmem_init + * attaches shared memory segment. + */ + static void + sepgsql_shmem_init(void) + { + bool found; + + selinux_state = ShmemInitStruct("SELinux policy state", + sepgsqlShmemSize(), &found); + if (!found) + { + int enforcing = security_getenforce(); + + Assert(enforcing == 0 || enforcing == 1); + + LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); + selinux_state->version = 0; + selinux_state->enforcing = enforcing; + load_class_av_mapping(); + + LWLockRelease(SepgsqlAvcLock); + } + } + + /* + * sepgsqlAvcInit + * initialize local uAVC facility. + */ + void + sepgsqlAvcInit(void) + { + /* + * local memory context + */ + AvcMemCtx = AllocSetContextCreate(TopMemoryContext, + "SE-PostgreSQL userspace avc", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + sepgsql_shmem_init(); + + /* + * reset local avc + */ + sepgsql_avc_reset(); + } + + /* + * sepgsqlComputePermission + * sepgsqlComputeCreateContext + * + * The following two functions make a query to in-kernel SELinux + * without userspace caches, due to some reasons. + * The uAVC can cover most of cases, but some of corner cases are + * not suitable for uAVC structure, so we need uncached interfaces. + * For example, uAVC is unavailable when we tries to load a shared + * library module, because security context of the library does not + * have its security identifier, so we cannot put it on uAVC. + */ + bool + sepgsqlComputePermission(const security_context_t scontext, + const security_context_t tcontext, + security_class_t tclass, + access_vector_t perms, + const char *objname) + { + security_context_t svcon, tvcon; + security_class_t e_tclass; + struct av_decision avd; + avc_datum cache; + bool rc; + + svcon = (!security_check_context_raw(scontext) + ? scontext : sepgsqlGetUnlabeledContext()); + tvcon = (!security_check_context_raw(tcontext) + ? tcontext : sepgsqlGetUnlabeledContext()); + + LWLockAcquire(SepgsqlAvcLock, LW_SHARED); + e_tclass = trans_to_external_tclass(tclass); + + if (security_compute_av_raw(svcon, tvcon, e_tclass, 0, &avd) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not compute an av_decision" + " scontext=%s tcontext=%s tclass=%s", + svcon, tvcon, security_class_to_string(e_tclass)))); + + cache.tclass = tclass; + cache.allowed = trans_to_internal_perms(e_tclass, avd.allowed, true); + cache.decided = trans_to_internal_perms(e_tclass, avd.decided, false); + cache.auditallow = trans_to_internal_perms(e_tclass, avd.auditallow, false); + cache.auditdeny = trans_to_internal_perms(e_tclass, avd.auditdeny, false); + LWLockRelease(SepgsqlAvcLock); + + rc = avc_permission_common(&cache, perms, true, svcon, tvcon, objname); + + if (svcon != scontext) + pfree(svcon); + if (tvcon != tcontext) + pfree(tvcon); + + return rc; + } + + security_context_t + sepgsqlComputeCreateContext(const security_context_t scontext, + const security_context_t tcontext, + security_class_t tclass) + { + security_context_t svcon, tvcon, nwcon, copy; + security_class_t e_tclass; + + svcon = (!security_check_context_raw(scontext) + ? scontext : sepgsqlGetUnlabeledContext()); + tvcon = (!security_check_context_raw(tcontext) + ? tcontext : sepgsqlGetUnlabeledContext()); + + LWLockAcquire(SepgsqlAvcLock, LW_SHARED); + e_tclass = trans_to_external_tclass(tclass); + + if (security_compute_create_raw(svcon, tvcon, e_tclass, &nwcon) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not compute a default context" + " scontext=%s tcontext=%s tclass=%s", + scontext, tcontext, security_class_to_string(e_tclass)))); + + LWLockRelease(SepgsqlAvcLock); + + if (svcon != scontext) + pfree(svcon); + if (tvcon != tcontext) + pfree(tvcon); + + PG_TRY(); + { + copy = pstrdup(nwcon); + } + PG_CATCH(); + { + freecon(nwcon); + PG_RE_THROW(); + } + PG_END_TRY(); + + freecon(nwcon); + + return copy; + } + + /* + * SELinux state monitoring process + * + * This process is forked from postmaster to monitor the state of SELinux. + * SELinux can make a notifier message to userspace object manager via + * netlink socket. When it receives the message, it updates selinux_state + * structure assigned on shared memory region to make any instance reset + * its AVC soon. + */ + + static bool sepgsqlStateMonitorAlive = true; + + static void + sepgsqlStateMonitorSIGHUP(SIGNAL_ARGS) + { + ereport(NOTICE, + (errcode(ERRCODE_SELINUX_INFO), + errmsg("SELinux: invalidate userspace avc"))); + selinux_state->version = selinux_state->version + 1; + } + + static int + sepgsqlStateMonitorMain() + { + char buffer[2048]; + struct sockaddr_nl addr; + socklen_t addrlen; + struct nlmsghdr *nlh; + int rc, nl_sockfd; + + /* + * map shared memory segment + */ + sepgsql_shmem_init(); + + /* + * setup the signal handler + */ + pqinitmask(); + pqsignal(SIGHUP, sepgsqlStateMonitorSIGHUP); + pqsignal(SIGINT, SIG_IGN); + pqsignal(SIGTERM, exit); + pqsignal(SIGQUIT, exit); + pqsignal(SIGUSR1, SIG_IGN); + pqsignal(SIGUSR2, SIG_IGN); + pqsignal(SIGCHLD, SIG_DFL); + PG_SETMASK(&UnBlockSig); + + ereport(NOTICE, + (errcode(ERRCODE_SELINUX_INFO), + errmsg("SELinux: policy state monitor process (pid: %u)", + getpid()))); + /* + * open netlink socket + */ + nl_sockfd = socket(PF_NETLINK, SOCK_RAW, NETLINK_SELINUX); + if (nl_sockfd < 0) + { + ereport(NOTICE, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not open netlink socket"))); + return 1; + } + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_groups = SELNL_GRP_AVC; + if (bind(nl_sockfd, (struct sockaddr *) &addr, sizeof(addr))) + { + ereport(NOTICE, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not bind netlink socket"))); + return 1; + } + + /* + * waiting loop + */ + while (sepgsqlStateMonitorAlive) + { + addrlen = sizeof(addr); + rc = recvfrom(nl_sockfd, buffer, sizeof(buffer), 0, + (struct sockaddr *) &addr, &addrlen); + if (rc < 0) + { + if (errno == EINTR) + continue; + + ereport(NOTICE, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: error on netlink recvfrom(): %s", + strerror(errno)))); + return 1; + } + + if (addrlen != sizeof(addr)) + { + ereport(NOTICE, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: netlink address truncated (len=%d)", + addrlen))); + return 1; + } + + if (addr.nl_pid) + { + ereport(NOTICE, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: netlink received spoofed packet from: %u", + addr.nl_pid))); + continue; + } + + if (rc == 0) + { + ereport(NOTICE, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: netlink received EOF"))); + return 1; + } + + nlh = (struct nlmsghdr *) buffer; + if (nlh->nlmsg_flags & MSG_TRUNC || nlh->nlmsg_len > (unsigned int) rc) + { + ereport(NOTICE, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: netlink incomplete message"))); + return 1; + } + + switch (nlh->nlmsg_type) + { + case SELNL_MSG_SETENFORCE: + { + struct selnl_msg_setenforce *msg = NLMSG_DATA(nlh); + + ereport(NOTICE, + (errcode(ERRCODE_SELINUX_INFO), + errmsg("SELinux: setenforce notifier" + " (enforcing=%d)", msg->val))); + + LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); + load_class_av_mapping(); + + /* + * userspace avc invalidation + */ + selinux_state->version = selinux_state->version + 1; + selinux_state->enforcing = msg->val ? true : false; + + LWLockRelease(SepgsqlAvcLock); + break; + } + case SELNL_MSG_POLICYLOAD: + { + struct selnl_msg_policyload *msg = NLMSG_DATA(nlh); + + ereport(NOTICE, + (errcode(ERRCODE_SELINUX_INFO), + errmsg("policyload notifier (seqno=%d)", + msg->seqno))); + + LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); + load_class_av_mapping(); + /* + * userspace avc invalidation + */ + selinux_state->version = selinux_state->version + 1; + + LWLockRelease(SepgsqlAvcLock); + break; + } + case NLMSG_ERROR: + { + struct nlmsgerr *err = NLMSG_DATA(nlh); + + if (err->error == 0) + break; + + ereport(NOTICE, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: netlink error: %s", + strerror(-err->error)))); + return 1; + } + default: + ereport(NOTICE, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("netlink unknown message type (%d)", + nlh->nlmsg_type))); + return 1; + } + } + return 0; + } + + pid_t + sepgsqlStartupWorkerProcess(void) + { + pid_t chld; + + chld = fork(); + if (chld == 0) + { + ClosePostmasterPorts(false); + + on_exit_reset(); + + exit(sepgsqlStateMonitorMain()); + } + else if (chld > 0) + return chld; + + return (pid_t) 0; + } diff -Nrpc base/src/backend/security/sepgsql/core.c sepgsql/src/backend/security/sepgsql/core.c *** base/src/backend/security/sepgsql/core.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/core.c Thu Jan 15 17:07:21 2009 *************** *** 0 **** --- 1,623 ---- + /* + * src/backend/security/sepgsql/core.c + * SE-PostgreSQL core facilities + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ + #include "postgres.h" + + #include "catalog/pg_database.h" + #include "catalog/pg_security.h" + #include "libpq/libpq.h" + #include "miscadmin.h" + #include "security/pgace.h" + #include "utils/builtins.h" + #include "utils/syscache.h" + #include + + int sepostgresql_mode; + + static security_context_t serverContext = NULL; + static security_context_t clientContext = NULL; + static security_context_t unlabeledContext = NULL; + + const security_context_t + sepgsqlGetServerContext(void) + { + Assert(serverContext != NULL); + return serverContext; + } + + const security_context_t + sepgsqlGetClientContext(void) + { + Assert(clientContext != NULL); + return clientContext; + } + + const security_context_t + sepgsqlGetDatabaseContext(void) + { + security_context_t result; + + if (IsBootstrapProcessingMode()) + { + static security_context_t dbcontext = NULL; + + if (!dbcontext) + { + if (security_compute_create_raw(sepgsqlGetClientContext(), + sepgsqlGetClientContext(), + SECCLASS_DB_DATABASE, + &dbcontext) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not get database context"))); + } + result = pstrdup(dbcontext); + } + else + { + HeapTuple tuple; + + tuple = SearchSysCache(DATABASEOID, + ObjectIdGetDatum(MyDatabaseId), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SELinux: cache lookup failed for database: %u", MyDatabaseId); + + result = pgaceLookupSecurityLabel(HeapTupleGetSecLabel(tuple)); + if (!result || !pgaceCheckValidSecurityLabel(result)) + result = pgaceUnlabeledSecurityLabel(); + + ReleaseSysCache(tuple); + } + + return result; + } + + Oid + sepgsqlGetDatabaseSecurityId(void) + { + Oid sid; + + if (IsBootstrapProcessingMode()) + { + security_context_t dcontext + = sepgsqlGetDatabaseContext(); + + sid = pgaceSecurityLabelToSid(dcontext); + } + else + { + HeapTuple tuple; + + tuple = SearchSysCache(DATABASEOID, + ObjectIdGetDatum(MyDatabaseId), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SELinux: cache lookup failed for database: %u", MyDatabaseId); + + sid = HeapTupleGetSecLabel(tuple); + + ReleaseSysCache(tuple); + } + + return sid; + } + + const security_context_t + sepgsqlGetUnlabeledContext(void) + { + if (unlabeledContext) + return unlabeledContext; + + if (security_get_initial_context_raw("unlabeled", &unlabeledContext) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not get unlabeled context"))); + + return unlabeledContext; + } + + const security_context_t + sepgsqlSwitchClientContext(security_context_t new_context) + { + security_context_t original_context = clientContext; + + clientContext = new_context; + + sepgsqlAvcSwitchClientContext(new_context); + + return original_context; + } + + static void + initContexts(void) + { + /* + * server context + */ + if (getcon_raw(&serverContext) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not get server process context"))); + + /* + * client context + */ + if (!MyProcPort) + { + /* + * When the proces is not invoked as a backend of clietnt, + * it works as a server process and as a client process + * in same time. + */ + clientContext = serverContext; + } + else + { + if (getpeercon_raw(MyProcPort->sock, &clientContext) < 0) + { + /* + * fallbacked security context + * + * When getpeercon() API does not obtain the context of + * peer process, SEPGSQL_FALLBACK_CONTEXT environment + * variable is used as an alternative security context + * of the peer. + * + * getpeercon() needs the following condition to fail: + * - Connection come from remote host, + * - and, there is no labeled ipsec configuration between + * localhost and remote host. + * - and, there is no static fallbacked context configuration + * for the remote host. + */ + char *fallback = getenv("SEPGSQL_FALLBACK_CONTEXT"); + + if (!fallback) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg + ("SELinux: could not get client process context"))); + + if (security_check_context(fallback) < 0 + || selinux_trans_to_raw_context(fallback, &clientContext) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: %s is not a valid context", + fallback))); + } + } + } + + /* + * sepgsqlInitialize + * + * It initializes SE-PostgreSQL itself including assignment of shared + * memory segment, reset of AVC, obtaining the client/server security + * context and checks whether the client can access the required database, + * or not. + */ + void + sepgsqlInitialize(bool bootstrap) + { + char *dbname; + + initContexts(); + + sepgsqlAvcInit(); + + /* + * check db_database:{ access } + */ + if (IsBootstrapProcessingMode()) + dbname = "template1"; + else + { + Form_pg_database dbForm; + HeapTuple tuple; + + tuple = SearchSysCache(DATABASEOID, + ObjectIdGetDatum(MyDatabaseId), 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SELinux: cache lookup failed for database %u", + MyDatabaseId); + dbForm = (Form_pg_database) GETSTRUCT(tuple); + + dbname = pstrdup(NameStr(dbForm->datname)); + + ReleaseSysCache(tuple); + } + + sepgsqlComputePermission(sepgsqlGetClientContext(), + sepgsqlGetDatabaseContext(), + SECCLASS_DB_DATABASE, + DB_DATABASE__ACCESS, + dbname); + } + + /* + * sepgsqlIsEnabled + * + * This function returns the state of SE-PostgreSQL when PGACE hooks + * are invoked, to prevent to call sepgsqlXXXX() functions when + * SE-PostgreSQL is disabled. + * + * We can config the state of SE-PostgreSQL in $PGDATA/postgresql.conf. + * The GUC option "sepostgresql" can have the following four parameter. + * + * - default : It always follows the in-kernel SELinux state. When it + * works in Enforcing mode, SE-PostgreSQL also works in + * Enforcing mode. Changes of in-kernel state are delivered + * to userspace SE-PostgreSQL soon, and SELinux state + * monitoring process updates it rapidly. + * - enforcing : It always works in Enforcing mode. In-kernel SELinux + * has to be enabled. + * - permissive : It always works in Permissive mode. In-kernel SELinux + * has to be enabled. + * - disabled : It disables SE-PostgreSQL feature. It works as if + * original PostgreSQL + */ + bool + sepgsqlIsEnabled(void) + { + static int enabled = -1; + + if (enabled < 0) + { + if (sepostgresql_mode == SEPGSQL_MODE_DISABLED) + enabled = 0; + else + { + enabled = is_selinux_enabled(); + if (enabled == 0 /* in-kernel SELinux is disabled */ + && sepostgresql_mode != SEPGSQL_MODE_DEFAULT) + { + ereport(FATAL, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: disabled in kernel, but sepostgresql = %s", + SEPGSQL_MODE_ENFORCING ? "enforcing" : "permissive"))); + } + } + } + + return enabled > 0 ? true : false; + } + + /* + * sepgsql_getcon(void) + * + * It returns security context of client + */ + Datum + sepgsql_getcon(PG_FUNCTION_ARGS) + { + security_context_t context; + Datum labelTxt; + + if (!sepgsqlIsEnabled()) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: disabled now"))); + + if (selinux_raw_to_trans_context(clientContext, &context) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not translate mls label"))); + PG_TRY(); + { + labelTxt = CStringGetTextDatum(context); + } + PG_CATCH(); + { + freecon(context); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(context); + + PG_RETURN_DATUM(labelTxt); + } + + /* + * sepgsql_getcon(void) + * + * It returns security context of server process + */ + Datum + sepgsql_getservcon(PG_FUNCTION_ARGS) + { + security_context_t context; + Datum labelTxt; + + if (!sepgsqlIsEnabled()) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: disabled now"))); + + if (selinux_raw_to_trans_context(serverContext, &context) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not translate mls label"))); + PG_TRY(); + { + labelTxt = CStringGetTextDatum(context); + } + PG_CATCH(); + { + freecon(context); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(context); + + PG_RETURN_DATUM(labelTxt); + } + + static void + parse_to_context(security_context_t context, + char **user, char **role, char **type, char **range) + { + security_context_t raw_context; + + if (!sepgsqlIsEnabled()) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: disabled now"))); + + if (selinux_trans_to_raw_context(context, &raw_context) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not translate mls label"))); + PG_TRY(); + { + char *tmp; + + tmp = pstrdup(strtok(raw_context, ":")); + if (user) + *user = tmp; + tmp = pstrdup(strtok(NULL, ":")); + if (role) + *role = tmp; + tmp = pstrdup(strtok(NULL, ":")); + if (type) + *type = tmp; + if (is_selinux_mls_enabled()) + { + tmp = pstrdup(strtok(NULL, "\0")); + if (range) + *range = tmp; + } + else if (range) + *range = NULL; + } + PG_CATCH(); + { + freecon(raw_context); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(raw_context); + } + + /* + * text sepgsql_get_user(text) + * + * It picks up the USER field of given security context. + */ + Datum + sepgsql_get_user(PG_FUNCTION_ARGS) + { + char *user; + + parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)), + &user, NULL, NULL, NULL); + PG_RETURN_TEXT_P(CStringGetTextDatum(user)); + } + + /* + * text sepgsql_set_user(text, text) + * + * It replaces the USER field of given security context by the second argument. + */ + Datum + sepgsql_set_user(PG_FUNCTION_ARGS) + { + char *user, *role, *type, *range; + char buffer[1024]; + security_context_t newcon; + Datum result; + + parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)), + &user, &role, &type, &range); + if (range) + snprintf(buffer, sizeof(buffer), "%s:%s:%s:%s", + TextDatumGetCString(PG_GETARG_TEXT_P(1)), role, type, range); + else + snprintf(buffer, sizeof(buffer), "%s:%s:%s", + TextDatumGetCString(PG_GETARG_TEXT_P(1)), role, type); + if (selinux_raw_to_trans_context((security_context_t) buffer, &newcon) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not set a new user"))); + PG_TRY(); + { + result = CStringGetTextDatum(newcon); + } + PG_CATCH(); + { + freecon(newcon); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(newcon); + + PG_RETURN_DATUM(result); + } + + /* + * text sepgsql_get_role(text) + * + * It picks up the ROLE field of given security context. + */ + Datum + sepgsql_get_role(PG_FUNCTION_ARGS) + { + char *role; + + parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)), + NULL, &role, NULL, NULL); + PG_RETURN_TEXT_P(CStringGetTextDatum(role)); + } + + /* + * text sepgsql_set_user(text, text) + * + * It replaces the ROLE field of given security context by the second argument. + */ + Datum + sepgsql_set_role(PG_FUNCTION_ARGS) + { + char *user, *role, *type, *range; + char buffer[1024]; + security_context_t newcon; + Datum result; + + parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)), + &user, &role, &type, &range); + if (range) + snprintf(buffer, sizeof(buffer), "%s:%s:%s:%s", + user, TextDatumGetCString(PG_GETARG_TEXT_P(1)), type, range); + else + snprintf(buffer, sizeof(buffer), "%s:%s:%s", + user, TextDatumGetCString(PG_GETARG_TEXT_P(1)), type); + if (selinux_raw_to_trans_context((security_context_t) buffer, &newcon) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not set a new role"))); + PG_TRY(); + { + result = CStringGetTextDatum(newcon); + } + PG_CATCH(); + { + freecon(newcon); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(newcon); + + PG_RETURN_DATUM(result); + } + + /* + * text sepgsql_get_type(text) + * + * It picks up the TYPE field of given security context. + */ + Datum + sepgsql_get_type(PG_FUNCTION_ARGS) + { + char *type; + + parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)), + NULL, NULL, &type, NULL); + PG_RETURN_TEXT_P(CStringGetTextDatum(type)); + } + + /* + * text sepgsql_set_user(text, text) + * + * It replaces the TYPE field of given security context by the second argument. + */ + Datum + sepgsql_set_type(PG_FUNCTION_ARGS) + { + char *user, *role, *type, *range; + char buffer[1024]; + security_context_t newcon; + Datum result; + + parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)), + &user, &role, &type, &range); + if (range) + snprintf(buffer, sizeof(buffer), "%s:%s:%s:%s", + user, role, TextDatumGetCString(PG_GETARG_TEXT_P(1)), range); + else + snprintf(buffer, sizeof(buffer), "%s:%s:%s", + user, role, TextDatumGetCString(PG_GETARG_TEXT_P(1))); + if (selinux_raw_to_trans_context((security_context_t) buffer, &newcon) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not set a new type"))); + PG_TRY(); + { + result = CStringGetTextDatum(newcon); + } + PG_CATCH(); + { + freecon(newcon); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(newcon); + + PG_RETURN_DATUM(result); + } + + /* + * text sepgsql_get_range(text) + * + * It picks up the RANGE field of given security context. + */ + Datum + sepgsql_get_range(PG_FUNCTION_ARGS) + { + char *range; + + parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)), + NULL, NULL, NULL, &range); + PG_RETURN_TEXT_P(CStringGetTextDatum(range)); + } + + /* + * text sepgsql_set_user(text, text) + * + * It replaces the RANGE field of given security context by the second argument. + */ + Datum + sepgsql_set_range(PG_FUNCTION_ARGS) + { + char *user, *role, *type, *range; + char buffer[1024]; + security_context_t newcon; + Datum result; + + parse_to_context(TextDatumGetCString(PG_GETARG_TEXT_P(0)), + &user, &role, &type, &range); + if (range) + snprintf(buffer, sizeof(buffer), "%s:%s:%s:%s", + user, role, type, TextDatumGetCString(PG_GETARG_TEXT_P(1))); + else + snprintf(buffer, sizeof(buffer), "%s:%s:%s", user, role, type); + if (selinux_raw_to_trans_context((security_context_t) buffer, &newcon) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not set a new range"))); + PG_TRY(); + { + result = CStringGetTextDatum(newcon); + } + PG_CATCH(); + { + freecon(newcon); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(newcon); + + PG_RETURN_DATUM(result); + } diff -Nrpc base/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/security/sepgsql/hooks.c *** base/src/backend/security/sepgsql/hooks.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/hooks.c Thu Jan 22 10:39:45 2009 *************** *** 0 **** --- 1,1019 ---- + /* + * src/backend/security/sepgsql/hooks.c + * Implementations of PGACE framework in SE-PostgreSQL + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ + #include "postgres.h" + + #include "access/heapam.h" + #include "access/genam.h" + #include "access/skey.h" + #include "access/sysattr.h" + #include "catalog/indexing.h" + #include "catalog/pg_aggregate.h" + #include "catalog/pg_database.h" + #include "catalog/pg_largeobject.h" + #include "catalog/pg_proc.h" + #include "catalog/pg_security.h" + #include "miscadmin.h" + #include "nodes/makefuncs.h" + #include "security/pgace.h" + #include "storage/bufmgr.h" + #include "utils/fmgroids.h" + #include "utils/syscache.h" + #include "utils/tqual.h" + #include + #include + #include + #include + + /******************************************************************************* + * Extended SQL statement hooks + *******************************************************************************/ + bool + sepgsqlIsGramSecurityItem(DefElem *defel) + { + Assert(IsA(defel, DefElem)); + + if (defel->defname && + strcmp(defel->defname, SecurityLabelAttributeName) == 0) + return true; + + return false; + } + + static void + putExplicitContext(HeapTuple tuple, DefElem *defel) + { + if (defel) + { + Oid sid = pgaceSecurityLabelToSid(strVal(defel->arg)); + + HeapTupleSetSecLabel(tuple, sid); + } + } + + void + sepgsqlGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel) + { + putExplicitContext(tuple, defel); + } + + void + sepgsqlGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel) + { + putExplicitContext(tuple, defel); + } + + void + sepgsqlGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel) + { + putExplicitContext(tuple, defel); + } + + void + sepgsqlGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel) + { + putExplicitContext(tuple, defel); + } + + void + sepgsqlGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel) + { + putExplicitContext(tuple, defel); + } + + void + sepgsqlGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel) + { + putExplicitContext(tuple, defel); + } + + void + sepgsqlGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel) + { + putExplicitContext(tuple, defel); + } + + void + sepgsqlGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel) + { + putExplicitContext(tuple, defel); + } + + /******************************************************************************* + * DATABASE object related hooks + *******************************************************************************/ + + void + sepgsqlGetDatabaseParam(const char *name) + { + HeapTuple tuple; + const char *audit_name; + + tuple = SearchSysCache(DATABASEOID, + ObjectIdGetDatum(MyDatabaseId), 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SELinux: cache lookup failed for database %u", + MyDatabaseId); + + audit_name = sepgsqlTupleName(DatabaseRelationId, tuple); + sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), + SECCLASS_DB_DATABASE, + DB_DATABASE__GET_PARAM, + audit_name); + ReleaseSysCache(tuple); + } + + void + sepgsqlSetDatabaseParam(const char *name, char *argstring) + { + HeapTuple tuple; + const char *audit_name; + + tuple = SearchSysCache(DATABASEOID, + ObjectIdGetDatum(MyDatabaseId), 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SELinux: cache lookup failed for database %u", + MyDatabaseId); + + audit_name = sepgsqlTupleName(DatabaseRelationId, tuple); + sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), + SECCLASS_DB_DATABASE, + DB_DATABASE__SET_PARAM, + audit_name); + ReleaseSysCache(tuple); + } + + /******************************************************************************* + * RELATION(Table)/ATTRIBTUE(column) object related hooks + *******************************************************************************/ + void + sepgsqlLockTable(Oid relid) + { + HeapTuple tuple; + + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SELinux: cache lookup failed for relation %u", relid); + + if (((Form_pg_class) GETSTRUCT(tuple))->relkind == RELKIND_RELATION) + { + const char *audit_name + = sepgsqlTupleName(RelationRelationId, tuple); + sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), + SECCLASS_DB_TABLE, + DB_TABLE__LOCK, + audit_name); + } + ReleaseSysCache(tuple); + } + + void + sepgsqlExecTruncate(List *trunc_rels) + { + ListCell *l; + + foreach (l, trunc_rels) + { + const char *audit_name; + HeapTuple tuple; + HeapScanDesc scan; + Relation rel = (Relation) lfirst(l); + + if (RelationGetForm(rel)->relkind != RELKIND_RELATION) + continue; + + /* + * check db_table:{delete} + */ + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(RelationGetRelid(rel)), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SELinux: cache lookup failed for relation %u", + RelationGetRelid(rel)); + + audit_name = sepgsqlTupleName(RelationRelationId, tuple); + sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), + SECCLASS_DB_TABLE, + DB_TABLE__DELETE, + audit_name); + ReleaseSysCache(tuple); + + /* + * check db_tuple:{delete} + */ + scan = heap_beginscan(rel, SnapshotNow, 0, NULL); + + while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) + { + sepgsqlCheckTuplePerms(rel, tuple, NULL, + SEPGSQL_PERMS_DELETE, true); + } + heap_endscan(scan); + } + } + + /******************************************************************************* + * PROCEDURE related hooks + *******************************************************************************/ + + typedef struct + { + PGFunction fn_addr; + security_context_t fn_con; + } sepgsql_fn_info; + + static Datum + invokeTrustedProcedure(PG_FUNCTION_ARGS) + { + sepgsql_fn_info *sefinfo = fcinfo->flinfo->fn_pgaceItem; + security_context_t orig_context; + Datum retval; + + /* + * set new domain + */ + orig_context = sepgsqlSwitchClientContext(sefinfo->fn_con); + + PG_TRY(); + { + retval = sefinfo->fn_addr(fcinfo); + } + PG_CATCH(); + { + sepgsqlSwitchClientContext(orig_context); + PG_RE_THROW(); + } + PG_END_TRY(); + sepgsqlSwitchClientContext(orig_context); + + return retval; + } + + void + sepgsqlCallFunction(FmgrInfo *finfo) + { + MemoryContext oldctx; + HeapTuple tuple; + security_context_t newcon; + access_vector_t perms = DB_PROCEDURE__EXECUTE; + const char *audit_name; + + if (IsBootstrapProcessingMode()) + return; /* under initialization of pg_proc */ + + tuple = SearchSysCache(PROCOID, + ObjectIdGetDatum(finfo->fn_oid), + 0, 0, 0); + Assert(HeapTupleIsValid(tuple)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SELinux: cache lookup failed for procedure %u", finfo->fn_oid); + + oldctx = MemoryContextSwitchTo(finfo->fn_mcxt); + /* + * check trusted procedure + */ + newcon = sepgsqlClientCreateContext(HeapTupleGetSecLabel(tuple), + SECCLASS_PROCESS); + if (strcmp(newcon, sepgsqlGetClientContext()) != 0) + { + sepgsql_fn_info *sefinfo + = palloc0(sizeof(sepgsql_fn_info)); + + sefinfo->fn_addr = finfo->fn_addr; + sefinfo->fn_con = newcon; + finfo->fn_addr = invokeTrustedProcedure; + finfo->fn_pgaceItem = sefinfo; + + perms |= DB_PROCEDURE__ENTRYPOINT; + } + else + pfree(newcon); + + MemoryContextSwitchTo(oldctx); + + audit_name = sepgsqlTupleName(ProcedureRelationId, tuple); + sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), + SECCLASS_DB_PROCEDURE, + perms, + audit_name); + ReleaseSysCache(tuple); + } + + void + sepgsqlCallAggFunction(HeapTuple aggTuple) + { + Form_pg_aggregate aggForm + = (Form_pg_aggregate) GETSTRUCT(aggTuple); + HeapTuple tuple; + const char *audit_name; + + /* check pg_proc.oid = pg_aggregate.aggfnoid */ + tuple = SearchSysCache(PROCOID, + ObjectIdGetDatum(aggForm->aggfnoid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SELinux: cache lookup failed for procedure %u", + aggForm->aggfnoid); + + audit_name = sepgsqlTupleName(ProcedureRelationId, tuple); + sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), + SECCLASS_DB_PROCEDURE, + DB_PROCEDURE__EXECUTE, + audit_name); + ReleaseSysCache(tuple); + } + + bool + sepgsqlCallTriggerFunction(TriggerData *tgdata) + { + Relation rel = tgdata->tg_relation; + HeapTuple newtup = NULL; + HeapTuple oldtup = NULL; + + /* + * We don't need to check tuple permissions for + * statement triggers + */ + if (TRIGGER_FIRED_FOR_STATEMENT(tgdata->tg_event)) + return true; + + if (TRIGGER_FIRED_BY_INSERT(tgdata->tg_event)) + { + if (TRIGGER_FIRED_AFTER(tgdata->tg_event)) + newtup = tgdata->tg_trigtuple; + } + else if (TRIGGER_FIRED_BY_UPDATE(tgdata->tg_event)) + { + oldtup = tgdata->tg_trigtuple; + if (TRIGGER_FIRED_AFTER(tgdata->tg_event)) + { + Oid securityId = HeapTupleGetSecLabel(tgdata->tg_newtuple); + + if (HeapTupleGetSecLabel(oldtup) != securityId) + newtup = tgdata->tg_newtuple; + } + } + else if (TRIGGER_FIRED_BY_DELETE(tgdata->tg_event)) + { + if (TRIGGER_FIRED_AFTER(tgdata->tg_event)) + oldtup = tgdata->tg_trigtuple; + } + else + { + elog(ERROR, "SELinux: unexpected trigger event type (%u)", + tgdata->tg_event); + } + if (oldtup && !sepgsqlCheckTuplePerms(rel, oldtup, NULL, + SEPGSQL_PERMS_SELECT, false)) + return false; + if (newtup && !sepgsqlCheckTuplePerms(rel, newtup, NULL, + SEPGSQL_PERMS_SELECT, false)) + return false; + + return true; + } + + bool sepgsqlAllowFunctionInlined(Oid fnoid, HeapTuple func_tuple) + { + security_context_t newcon; + const char *audit_name; + + /* + * If function is defined as trusted procedure, we always should + * not allow it to be inlined, and actual permission checks are + * done later phase. + */ + newcon = sepgsqlClientCreateContext(HeapTupleGetSecLabel(func_tuple), + SECCLASS_PROCESS); + if (strcmp(newcon, sepgsqlGetClientContext()) != 0) + return false; + + audit_name = sepgsqlTupleName(ProcedureRelationId, func_tuple); + sepgsqlClientHasPermission(HeapTupleGetSecLabel(func_tuple), + SECCLASS_DB_PROCEDURE, + DB_PROCEDURE__EXECUTE, + audit_name); + return true; + } + + /******************************************************************************* + * LOAD shared library module hook + *******************************************************************************/ + void + sepgsqlLoadSharedModule(const char *filename) + { + security_context_t filecon; + + if (getfilecon_raw(filename, &filecon) < 0) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not access file \"%s\": %m", filename))); + PG_TRY(); + { + sepgsqlComputePermission(sepgsqlGetDatabaseContext(), + filecon, + SECCLASS_DB_DATABASE, + DB_DATABASE__LOAD_MODULE, + filename); + } + PG_CATCH(); + { + freecon(filecon); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(filecon); + } + + /******************************************************************************* + * Binary Large Object hooks + *******************************************************************************/ + + void + sepgsqlLargeObjectCreate(Relation rel, HeapTuple tuple) + { + const char *audit_name; + + sepgsqlSetDefaultContext(rel, tuple); + + audit_name = sepgsqlTupleName(LargeObjectRelationId, tuple); + sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), + SECCLASS_DB_BLOB, + DB_BLOB__CREATE, + audit_name); + } + + void + sepgsqlLargeObjectDrop(Relation rel, HeapTuple tuple, void **pgaceItem) + { + Oid security_id = HeapTupleGetSecLabel(tuple); + List *okList = (List *) (*pgaceItem); + ListCell *l; + const char *audit_name; + + foreach (l, okList) + { + if (security_id == lfirst_oid(l)) + return; /* already allowed */ + } + + audit_name = sepgsqlTupleName(LargeObjectRelationId, tuple); + sepgsqlClientHasPermission(security_id, + SECCLASS_DB_BLOB, + DB_BLOB__DROP, + audit_name); + + *pgaceItem = lappend_oid(okList, security_id); + } + + static void + checkLargeObjectPages(Oid loid, Snapshot snapshot, + int32 start_pageno, int32 end_pageno, + access_vector_t perms) + { + Relation rel; + HeapTuple tuple; + SysScanDesc sd; + ScanKeyData skey[2]; + List *okList = NIL; + + rel = heap_open(LargeObjectRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], + Anum_pg_largeobject_loid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(loid)); + + if (start_pageno <= 0) + sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, + true, snapshot, 1, skey); + else + { + ScanKeyInit(&skey[1], + Anum_pg_largeobject_pageno, + BTGreaterEqualStrategyNumber, F_INT4GE, + Int32GetDatum(start_pageno)); + + sd = systable_beginscan(rel, LargeObjectLOidPNIndexId, + true, snapshot, 2, skey); + } + + while ((tuple = systable_getnext(sd)) != NULL) + { + Form_pg_largeobject loForm + = (Form_pg_largeobject) GETSTRUCT(tuple); + Oid security_id; + ListCell *l; + const char *audit_name; + + if (end_pageno >= 0 && loForm->pageno > end_pageno) + break; + + security_id = HeapTupleGetSecLabel(tuple); + + foreach (l, okList) + { + if (security_id == lfirst_oid(l)) + goto skip; + } + okList = lappend_oid(okList, security_id); + + audit_name = sepgsqlTupleName(LargeObjectRelationId, tuple); + sepgsqlClientHasPermission(security_id, + SECCLASS_DB_BLOB, + perms, + audit_name); + skip: + ; + } + systable_endscan(sd); + + list_free(okList); + + heap_close(rel, NoLock); + } + + void + sepgsqlLargeObjectRead(LargeObjectDesc *lodesc, int32 length) + { + int32 start_pageno = lodesc->offset / LOBLKSIZE; + int32 end_pageno = (lodesc->offset + length + LOBLKSIZE - 1) / LOBLKSIZE; + + checkLargeObjectPages(lodesc->id, lodesc->snapshot, + start_pageno, end_pageno, DB_BLOB__READ); + } + + void + sepgsqlLargeObjectWrite(LargeObjectDesc *lodesc, int32 length) + { + int32 start_pageno = lodesc->offset / LOBLKSIZE; + int32 end_pageno = (lodesc->offset + length + LOBLKSIZE - 1) / LOBLKSIZE; + + checkLargeObjectPages(lodesc->id, lodesc->snapshot, + start_pageno, end_pageno, DB_BLOB__WRITE); + } + + void + sepgsqlLargeObjectTruncate(LargeObjectDesc *lodesc, int32 offset) + { + int32 start_pageno = lodesc->offset / LOBLKSIZE; + + checkLargeObjectPages(lodesc->id, lodesc->snapshot, + start_pageno, -1, DB_BLOB__WRITE); + } + + void + sepgsqlLargeObjectImport(Oid loid, int fdesc, const char *filename) + { + security_context_t tcontext; + security_class_t tclass + = sepgsqlFileObjectClass(fdesc, filename); + + if (fgetfilecon_raw(fdesc, &tcontext) < 0) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not get security context \"%s\": %m", filename))); + PG_TRY(); + { + sepgsqlComputePermission(sepgsqlGetClientContext(), + tcontext, + tclass, + FILE__READ, + filename); + } + PG_CATCH(); + { + freecon(tcontext); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(tcontext); + + checkLargeObjectPages(loid, SnapshotNow, -1, -1, + DB_BLOB__WRITE | DB_BLOB__IMPORT); + } + + void + sepgsqlLargeObjectExport(Oid loid, int fdesc, const char *filename) + { + security_context_t tcontext; + security_class_t tclass + = sepgsqlFileObjectClass(fdesc, filename); + + if (fgetfilecon_raw(fdesc, &tcontext) < 0) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not security context \"%s\": %m", filename))); + PG_TRY(); + { + sepgsqlComputePermission(sepgsqlGetClientContext(), + tcontext, + tclass, + FILE__WRITE, + filename); + } + PG_CATCH(); + { + freecon(tcontext); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(tcontext); + + checkLargeObjectPages(loid, SnapshotNow, -1, -1, + DB_BLOB__READ | DB_BLOB__EXPORT); + } + + void + sepgsqlLargeObjectGetSecurity(Relation rel, HeapTuple tuple) + { + const char *audit_name + = sepgsqlTupleName(LargeObjectRelationId, tuple); + + sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), + SECCLASS_DB_BLOB, + DB_BLOB__GETATTR, + audit_name); + } + + void + sepgsqlLargeObjectSetSecurity(Relation rel, HeapTuple newtup, HeapTuple oldtup) + { + const char *audit_name; + + if (HeapTupleGetSecLabel(newtup) == HeapTupleGetSecLabel(oldtup)) + return; + + audit_name = sepgsqlTupleName(LargeObjectRelationId, oldtup); + sepgsqlClientHasPermission(HeapTupleGetSecLabel(oldtup), + SECCLASS_DB_BLOB, + DB_BLOB__SETATTR | DB_BLOB__RELABELFROM, + audit_name); + /* + * check db_blob:{setattr relabelto} + */ + audit_name = sepgsqlTupleName(LargeObjectRelationId, newtup); + sepgsqlClientHasPermission(HeapTupleGetSecLabel(newtup), + SECCLASS_DB_BLOB, + DB_BLOB__RELABELTO, + audit_name); + } + + /******************************************************************************* + * ExecScan hooks + *******************************************************************************/ + static bool abort_on_violated_tuple = false; + + bool + sepgsqlExecScan(Scan *scan, Relation rel, TupleTableSlot *slot) + { + HeapTuple tuple; + uint32 perms = (scan->pgaceTuplePerms & SEPGSQL_PERMS_MASK); + + if (perms == 0) + return true; + + tuple = ExecMaterializeSlot(slot); + + return sepgsqlCheckTuplePerms(rel, tuple, NULL, perms, + abort_on_violated_tuple); + } + + /* ---------------------------------------------------------- + * special cases for Foreign Key constraint + * ---------------------------------------------------------- */ + Datum + sepgsqlBeginPerformCheckFK(Relation rel, bool is_primary, Oid save_userid) + { + Datum save_pgace = BoolGetDatum(abort_on_violated_tuple); + + /* + * NOTE: when a tuple is inserted/updated on FK relation, all we should + * do is simply filtering violated tuples on PK relation, as normal + * row-level access controls doing. + * At the result, INSERT/UPDATE with invisible tuple will be failed. + */ + if (is_primary) + abort_on_violated_tuple = true; + + return save_pgace; + } + + void + sepgsqlEndPerformCheckFK(Relation rel, Datum save_pgace) + { + abort_on_violated_tuple = DatumGetBool(save_pgace); + } + + /******************************************************************************* + * security_label hooks + *******************************************************************************/ + bool + sepgsqlTupleDescHasSecLabel(Relation rel, List *relopts) + { + /* + * Newly created table via SELECT INTO/CREATE TABLE AS + */ + if (rel == NULL) + return sepostgresql_row_level; + + if (RelationGetForm(rel)->relkind != RELKIND_RELATION && + RelationGetForm(rel)->relkind != RELKIND_SEQUENCE) + return false; + + if (RelationGetRelid(rel) == DatabaseRelationId || + RelationGetRelid(rel) == RelationRelationId || + RelationGetRelid(rel) == AttributeRelationId || + RelationGetRelid(rel) == ProcedureRelationId || + RelationGetRelid(rel) == LargeObjectRelationId) + return true; + + return sepostgresql_row_level; + } + + char * + sepgsqlTranslateSecurityLabelIn(const char *context) + { + security_context_t i_context; + char *result; + + if (selinux_trans_to_raw_context((security_context_t) context, &i_context) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not translate mls label"))); + PG_TRY(); + { + result = pstrdup(i_context); + } + PG_CATCH(); + { + freecon(i_context); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(i_context); + + return result; + } + + char * + sepgsqlTranslateSecurityLabelOut(const char *context) + { + security_context_t o_context; + char *result; + + if (selinux_raw_to_trans_context((security_context_t) context, &o_context) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not translate mls label"))); + PG_TRY(); + { + result = pstrdup(o_context); + } + PG_CATCH(); + { + freecon(o_context); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(o_context); + + return result; + } + + /* + * sepgsqlCheckValidSecurityLabel() checks whether the given + * security context is valid on the current working security + * policy, or not. + * If it's invalid, sepgsqlUnlabeledSecurityLabel() is invoked + * at the next to get an alternative security label. + */ + bool + sepgsqlCheckValidSecurityLabel(char *context) + { + if (security_check_context_raw((security_context_t) context) < 0) + return false; + + return true; + } + + char * + sepgsqlUnlabeledSecurityLabel(void) + { + security_context_t unlabeled; + char *result; + + if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not get unlabeled initial context"))); + PG_TRY(); + { + result = pstrdup(unlabeled); + } + PG_CATCH(); + { + freecon(unlabeled); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(unlabeled); + + return result; + } + + char * + sepgsqlSecurityLabelOfLabel(void) + { + security_context_t table_context, tuple_context; + HeapTuple tuple; + + /* + * obtain security context of pg_security + */ + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(SecurityRelationId), 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SELinux: cache lookup failed for relation %u", + SecurityRelationId); + + table_context = pgaceLookupSecurityLabel(HeapTupleGetSecLabel(tuple)); + if (!table_context || !pgaceCheckValidSecurityLabel(table_context)) + table_context = pgaceUnlabeledSecurityLabel(); + + tuple_context = sepgsqlComputeCreateContext(sepgsqlGetServerContext(), + table_context, SECCLASS_DB_TUPLE); + pfree(table_context); + + ReleaseSysCache(tuple); + + return tuple_context; + } + + /****************************************************************** + * HeapTuple modification hooks + ******************************************************************/ + static HeapTuple + getHeapTupleFromItemPointer(Relation rel, ItemPointer tid) + { + /* + * obtain an old tuple + */ + Buffer buffer; + PageHeader dp; + ItemId lp; + HeapTupleData tuple; + HeapTuple oldtup; + + buffer = ReadBuffer(rel, ItemPointerGetBlockNumber(tid)); + LockBuffer(buffer, BUFFER_LOCK_SHARE); + + dp = (PageHeader) BufferGetPage(buffer); + lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid)); + + Assert(ItemIdIsNormal(lp)); + + tuple.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp); + tuple.t_len = ItemIdGetLength(lp); + tuple.t_self = *tid; + tuple.t_tableOid = RelationGetRelid(rel); + oldtup = heap_copytuple(&tuple); + + LockBuffer(buffer, BUFFER_LOCK_UNLOCK); + ReleaseBuffer(buffer); + + return oldtup; + } + + static bool + isTrustedRelation(Relation rel, bool is_internal) + { + if (!is_internal) + return false; + + if (RelationGetForm(rel)->relkind != RELKIND_RELATION) + return true; + + switch (RelationGetRelid(rel)) + { + case LargeObjectRelationId: + case SecurityRelationId: + return true; + } + return false; + } + + bool + sepgsqlHeapTupleInsert(Relation rel, HeapTuple tuple, + bool is_internal, bool with_returning) + { + uint32 perms; + + /* + * default context for no explicit labeled tuple + */ + if (!OidIsValid(HeapTupleGetSecLabel(tuple))) + { + /* + * If user gives no valid security context, + * it assigns a default one on the new tuple. + */ + if (HeapTupleHasSecLabel(tuple)) + sepgsqlSetDefaultContext(rel, tuple); + } + else if (!is_internal && RelationGetRelid(rel) == LargeObjectRelationId) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: unable to insert " + "pg_largeobject.security_label"))); + + if (isTrustedRelation(rel, is_internal)) + return true; + + perms = SEPGSQL_PERMS_INSERT; + if (with_returning) + perms |= SEPGSQL_PERMS_SELECT; + + return sepgsqlCheckTuplePerms(rel, tuple, NULL, perms, is_internal); + } + + bool + sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup, + bool is_internal, bool with_returning) + { + Oid relid = RelationGetRelid(rel); + HeapTuple oldtup; + uint32 perms; + bool rc = true; + bool relabel = false; + + oldtup = getHeapTupleFromItemPointer(rel, otid); + + if (!OidIsValid(HeapTupleGetSecLabel(newtup))) + { + /* + * If user does not specify new security context + * explicitly, it preserves a security context of + * older tuple. + */ + Oid sid = HeapTupleGetSecLabel(oldtup); + + if (HeapTupleHasSecLabel(newtup)) + HeapTupleSetSecLabel(newtup, sid); + } + else if (!is_internal && RelationGetRelid(rel) == LargeObjectRelationId) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: unable to update " + "pg_largeobject.security_label"))); + + if (isTrustedRelation(rel, is_internal)) + return true; + + if (HeapTupleGetSecLabel(newtup) != HeapTupleGetSecLabel(oldtup) || + sepgsqlTupleObjectClass(relid, newtup) != sepgsqlTupleObjectClass(relid, oldtup)) + relabel = true; + + perms = SEPGSQL_PERMS_UPDATE; + if (relabel) + perms |= SEPGSQL_PERMS_RELABELFROM; + rc = sepgsqlCheckTuplePerms(rel, oldtup, newtup, perms, is_internal); + if (!rc) + goto out; + + if (relabel) + { + perms = SEPGSQL_PERMS_RELABELTO; + if (with_returning) + perms |= SEPGSQL_PERMS_SELECT; + rc = sepgsqlCheckTuplePerms(rel, newtup, NULL, perms, is_internal); + } + out: + heap_freetuple(oldtup); + return rc; + } + + bool + sepgsqlHeapTupleDelete(Relation rel, ItemPointer otid, + bool is_internal, bool with_returning) + { + HeapTuple oldtup; + uint32 perms = SEPGSQL_PERMS_DELETE; + bool rc; + + if (isTrustedRelation(rel, is_internal)) + return true; + + oldtup = getHeapTupleFromItemPointer(rel, otid); + rc = sepgsqlCheckTuplePerms(rel, oldtup, NULL, perms, is_internal); + heap_freetuple(oldtup); + + return rc; + } diff -Nrpc base/src/backend/security/sepgsql/permissions.c sepgsql/src/backend/security/sepgsql/permissions.c *** base/src/backend/security/sepgsql/permissions.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/permissions.c Thu Jan 22 14:26:56 2009 *************** *** 0 **** --- 1,785 ---- + /* + * src/backend/security/sepgsql/permissions.c + * applies SE-PostgreSQL permission checks + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ + #include "postgres.h" + + #include "access/heapam.h" + #include "access/genam.h" + #include "catalog/indexing.h" + #include "catalog/pg_aggregate.h" + #include "catalog/pg_am.h" + #include "catalog/pg_amproc.h" + #include "catalog/pg_attribute.h" + #include "catalog/pg_authid.h" + #include "catalog/pg_cast.h" + #include "catalog/pg_class.h" + #include "catalog/pg_conversion.h" + #include "catalog/pg_database.h" + #include "catalog/pg_language.h" + #include "catalog/pg_largeobject.h" + #include "catalog/pg_operator.h" + #include "catalog/pg_proc.h" + #include "catalog/pg_security.h" + #include "catalog/pg_trigger.h" + #include "catalog/pg_ts_parser.h" + #include "catalog/pg_ts_template.h" + #include "catalog/pg_type.h" + #include "miscadmin.h" + #include "security/pgace.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" + #include "utils/lsyscache.h" + #include "utils/syscache.h" + #include "utils/tqual.h" + + /* + * It can be configured via a GUC variable to toggle + * row-level access controls. + */ + bool sepostgresql_row_level; + + /* + * sepgsqlTupleName + * returns an identifier string to generate audit record for + * the given tuple. Please note that its results can indicate + * an address within the given tuple, so we should not refer + * the returned pointer after HeapTuple is released. + */ + const char * + sepgsqlTupleName(Oid relid, HeapTuple tuple) + { + static char buffer[NAMEDATALEN * 2 + 10]; + + switch (relid) + { + case DatabaseRelationId: + return NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); + + case RelationRelationId: + return NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname); + + case AttributeRelationId: + if (!IsBootstrapProcessingMode()) + { + Form_pg_attribute attForm + = (Form_pg_attribute) GETSTRUCT(tuple); + char *relname + = get_rel_name(attForm->attrelid); + + if (relname) + { + snprintf(buffer, sizeof(buffer), "%s.%s", + relname, NameStr(attForm->attname)); + pfree(relname); + return buffer; + } + } + return NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname); + + case ProcedureRelationId: + return NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname); + + case LargeObjectRelationId: + snprintf(buffer, sizeof(buffer), "loid:%u", + ((Form_pg_largeobject) GETSTRUCT(tuple))->loid); + return buffer; + } + return NULL; /* No tuple name for audit record */ + } + + /* + * sepgsqlFileObjectClass + * + * It returns proper object class of filesystem object already opened. + * It is necessary to check privileges voluntarily. + */ + security_class_t + sepgsqlFileObjectClass(int fdesc, const char *filename) + { + struct stat stbuf; + + if (fstat(fdesc, &stbuf) != 0) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not stat file \"%s\": %m", filename))); + + if (S_ISDIR(stbuf.st_mode)) + return SECCLASS_DIR; + else if (S_ISCHR(stbuf.st_mode)) + return SECCLASS_CHR_FILE; + else if (S_ISBLK(stbuf.st_mode)) + return SECCLASS_BLK_FILE; + else if (S_ISFIFO(stbuf.st_mode)) + return SECCLASS_FIFO_FILE; + else if (S_ISLNK(stbuf.st_mode)) + return SECCLASS_LNK_FILE; + else if (S_ISSOCK(stbuf.st_mode)) + return SECCLASS_SOCK_FILE; + + return SECCLASS_FILE; + } + + /* + * sepgsqlTupleObjectClass + * + * It returns proper object class of given tuple + */ + security_class_t + sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple) + { + Form_pg_class clsForm; + Form_pg_attribute attForm; + + switch (relid) + { + case DatabaseRelationId: + return SECCLASS_DB_DATABASE; + + case RelationRelationId: + clsForm = (Form_pg_class) GETSTRUCT(tuple); + if (clsForm->relkind == RELKIND_RELATION) + return SECCLASS_DB_TABLE; + break; + + case AttributeRelationId: + attForm = (Form_pg_attribute) GETSTRUCT(tuple); + if (attForm->attkind == RELKIND_RELATION) + return SECCLASS_DB_COLUMN; + break; + + case ProcedureRelationId: + return SECCLASS_DB_PROCEDURE; + + case LargeObjectRelationId: + return SECCLASS_DB_BLOB; + } + + return SECCLASS_DB_TUPLE; + } + + /* + * sepgsqlCheckTuplePerms + * + * This function evaluates given permission set (SEPGSQL_PERMS_*) onto the + * given tuple, with translating them into proper SELinux permission. + * + * Accesses to some of system catalog has special meanings. DELETE a tuple + * within pg_class also means DROP TABLE for instance. In this case, + * SE-PostgreSQL translate given SEPGSQL_PERMS_DELETE into DB_TABLE__DROP + * to keep consistency of user operation. To delete a tuple within pg_class + * always means dropping a table independent from what SQL statement is + * used. + * + * Thus, checks for some of system catalog need to modify given permission + * set at checkTuplePermsXXXX() functions. + */ + static access_vector_t + sepgsqlPermsToCommonAv(uint32 perms) + { + access_vector_t result = 0; + + result |= (perms & SEPGSQL_PERMS_USE ? COMMON_DATABASE__GETATTR : 0); + result |= (perms & SEPGSQL_PERMS_SELECT ? COMMON_DATABASE__GETATTR : 0); + result |= (perms & SEPGSQL_PERMS_UPDATE ? COMMON_DATABASE__SETATTR : 0); + result |= (perms & SEPGSQL_PERMS_INSERT ? COMMON_DATABASE__CREATE : 0); + result |= (perms & SEPGSQL_PERMS_DELETE ? COMMON_DATABASE__DROP : 0); + result |= (perms & SEPGSQL_PERMS_RELABELFROM ? COMMON_DATABASE__RELABELFROM : 0); + result |= (perms & SEPGSQL_PERMS_RELABELTO ? COMMON_DATABASE__RELABELTO : 0); + + return result; + } + + static access_vector_t + sepgsqlPermsToDatabaseAv(uint32 perms, HeapTuple tuple, HeapTuple newtup) + { + return sepgsqlPermsToCommonAv(perms); + } + + static access_vector_t + sepgsqlPermsToTableAv(uint32 perms, HeapTuple tuple, HeapTuple newtup) + { + return sepgsqlPermsToCommonAv(perms); + } + + static access_vector_t + sepgsqlPermsToProcedureAv(uint32 perms, HeapTuple tuple, HeapTuple newtup) + { + access_vector_t result = sepgsqlPermsToCommonAv(perms); + Form_pg_proc proForm; + HeapTuple protup; + Datum probin; + bool isnull; + + /* + * Check permission for loadable module installation + */ + protup = HeapTupleIsValid(newtup) ? newtup : tuple; + proForm = (Form_pg_proc) GETSTRUCT(protup); + + if (proForm->prolang == ClanguageId) + { + bool need_check = false; + + probin = SysCacheGetAttr(PROCOID, protup, + Anum_pg_proc_probin, + &isnull); + if (!isnull) + { + if (result & DB_PROCEDURE__CREATE) + need_check = true; + else if (HeapTupleIsValid(newtup)) + { + Form_pg_proc oldForm = (Form_pg_proc) GETSTRUCT(tuple); + + if (oldForm->prolang != proForm->prolang) + need_check = true; + else + { + Datum oldbin = SysCacheGetAttr(PROCOID, tuple, + Anum_pg_proc_probin, + &isnull); + if (isnull) + need_check = true; + else + { + Datum comp = DirectFunctionCall2(byteane, oldbin, probin); + need_check = DatumGetBool(comp); + } + } + } + + if (need_check) + { + char *filename = TextDatumGetCString(probin); + + sepgsqlCheckModuleInstallPerms(filename); + } + } + } + + return result; + } + + static access_vector_t + sepgsqlPermsToColumnAv(uint32 perms, HeapTuple tuple, HeapTuple newtup) + { + access_vector_t result = sepgsqlPermsToCommonAv(perms); + + if (HeapTupleIsValid(newtup)) + { + Form_pg_attribute oldatt = (Form_pg_attribute) GETSTRUCT(tuple); + Form_pg_attribute newatt = (Form_pg_attribute) GETSTRUCT(newtup); + + if (!oldatt->attisdropped && newatt->attisdropped) + result |= DB_COLUMN__DROP; + if (oldatt->attisdropped && !newatt->attisdropped) + result |= DB_COLUMN__CREATE; + } + return result; + } + + static access_vector_t + sepgsqlPermsToTupleAv(uint32 perms, HeapTuple tuple, HeapTuple newtup) + { + access_vector_t result = 0; + + result |= (perms & SEPGSQL_PERMS_USE ? DB_TUPLE__USE : 0); + result |= (perms & SEPGSQL_PERMS_SELECT ? DB_TUPLE__SELECT : 0); + result |= (perms & SEPGSQL_PERMS_UPDATE ? DB_TUPLE__UPDATE : 0); + result |= (perms & SEPGSQL_PERMS_INSERT ? DB_TUPLE__INSERT : 0); + result |= (perms & SEPGSQL_PERMS_DELETE ? DB_TUPLE__DELETE : 0); + result |= (perms & SEPGSQL_PERMS_RELABELFROM ? DB_TUPLE__RELABELFROM : 0); + result |= (perms & SEPGSQL_PERMS_RELABELTO ? DB_TUPLE__RELABELTO : 0); + + return result; + } + + static access_vector_t + sepgsqlPermsToBlobAv(uint32 perms, HeapTuple tuple, HeapTuple newtup) + { + access_vector_t result = sepgsqlPermsToCommonAv(perms); + + /* + * NOTE: INSERT tuples into pg_largeobject has a possibility to create + * a new largeobject, if the given loid is not exist on the current + * pg_largeobject. Ditto for DELETE statement, it also has a possibility + * to drop a largeobject, if it removes all tuples within a large object. + * + * UPDATE pg_largeobject.loid has a possibility to create and drop + * a largeobject in same time, so we need to check it when loid is + * changed. + * + * db_blob:{create} and db_blob:{drop} should be evaluated for + * creation/deletion of largeobject, but we have to check pg_largeobject + * with SnapshotSelf whether there is one or more tuple having same loid, + * or not, on each tuple insertion or deletion. + * + * So, we assume any INSERT means db_blob:{create}, any DELETE means + * db_blob:{drop}. + */ + result |= (perms & SEPGSQL_PERMS_INSERT ? DB_BLOB__WRITE : 0); + if (perms & SEPGSQL_PERMS_UPDATE) + { + result |= DB_BLOB__WRITE; + + if (((Form_pg_largeobject) GETSTRUCT(tuple))->loid != + ((Form_pg_largeobject) GETSTRUCT(newtup))->loid) + result |= (DB_BLOB__CREATE | DB_BLOB__DROP); + } + result |= (perms & SEPGSQL_PERMS_DELETE ? DB_BLOB__WRITE : 0); + result |= (perms & SEPGSQL_PERMS_READ ? DB_BLOB__READ : 0); + + return result; + } + + /* + * sepgsqlCheckProcedureInstall + * checks permission: db_procedure:{install}, when client tries to modify + * a system catalog which contains procedure id to invoke it later. + * Because these functions are invoked internally, to search a table with + * a special index algorithm for example, the security policy has to prevent + * malicious user-defined functions to be installed. + */ + static void + checkProcedureInstall(Oid proc_oid) + { + if (!OidIsValid(proc_oid)) + return; + + if (IsBootstrapProcessingMode()) + { + /* + * We assume all procedures have same security context + * in bootstrap processing mode, because no one can + * relabel it. + */ + Oid proc_sid + = sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(), + SECCLASS_DB_PROCEDURE); + sepgsqlClientHasPermission(proc_sid, + SECCLASS_DB_PROCEDURE, + DB_PROCEDURE__INSTALL, + NULL); + } + else + { + HeapTuple protup; + const char *audit_name; + + protup = SearchSysCache(PROCOID, + ObjectIdGetDatum(proc_oid), + 0, 0, 0); + if (!HeapTupleIsValid(protup)) + return; + + audit_name = sepgsqlTupleName(ProcedureRelationId, protup); + sepgsqlClientHasPermission(HeapTupleGetSecLabel(protup), + SECCLASS_DB_PROCEDURE, + DB_PROCEDURE__INSTALL, + audit_name); + ReleaseSysCache(protup); + } + } + + #define CHECK_PROC_INSTALL_HANDLER(catalog,member,tuple,newtup) \ + do { \ + if (!HeapTupleIsValid(newtup)) \ + checkProcedureInstall(((CppConcat(Form_,catalog)) GETSTRUCT(tuple))->member); \ + else if (((CppConcat(Form_,catalog)) GETSTRUCT(tuple))->member \ + != ((CppConcat(Form_,catalog)) GETSTRUCT(newtup))->member) \ + checkProcedureInstall(((CppConcat(Form_,catalog)) GETSTRUCT(newtup))->member); \ + } while(0) + + static void + sepgsqlCheckProcedureInstall(Relation rel, HeapTuple tuple, HeapTuple newtup) + { + /* + * Some of system catalog can be configured to invoke functions + * implicitly. It checks permission to prevent implicit invocation + * of malicious functions. + */ + switch (RelationGetRelid(rel)) + { + case AggregateRelationId: + CHECK_PROC_INSTALL_HANDLER(pg_aggregate, aggfnoid, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_aggregate, aggtransfn, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_aggregate, aggfinalfn, tuple, newtup); + break; + + case AccessMethodRelationId: + CHECK_PROC_INSTALL_HANDLER(pg_am, aminsert, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_am, ambeginscan, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_am, amgettuple, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_am, amgetbitmap, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_am, amrescan, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_am, amendscan, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_am, ammarkpos, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_am, amrestrpos, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_am, ambuild, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_am, ambulkdelete, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_am, amvacuumcleanup, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_am, amcostestimate, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_am, amoptions, tuple, newtup); + break; + + case AccessMethodProcedureRelationId: + CHECK_PROC_INSTALL_HANDLER(pg_amproc, amproc, tuple, newtup); + break; + + case CastRelationId: + CHECK_PROC_INSTALL_HANDLER(pg_cast, castfunc, tuple, newtup); + break; + + case ConversionRelationId: + CHECK_PROC_INSTALL_HANDLER(pg_conversion, conproc, tuple, newtup); + break; + + case LanguageRelationId: + CHECK_PROC_INSTALL_HANDLER(pg_language, lanplcallfoid, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_language, lanvalidator, tuple, newtup); + break; + + case OperatorRelationId: + CHECK_PROC_INSTALL_HANDLER(pg_operator, oprcode, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_operator, oprrest, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_operator, oprjoin, tuple, newtup); + break; + + case TriggerRelationId: + CHECK_PROC_INSTALL_HANDLER(pg_trigger, tgfoid, tuple, newtup); + break; + + case TSParserRelationId: + CHECK_PROC_INSTALL_HANDLER(pg_ts_parser, prsstart, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_ts_parser, prstoken, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_ts_parser, prsend, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_ts_parser, prsheadline, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_ts_parser, prslextype, tuple, newtup); + break; + + case TSTemplateRelationId: + CHECK_PROC_INSTALL_HANDLER(pg_ts_template, tmplinit, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_ts_template, tmpllexize, tuple, newtup); + break; + + case TypeRelationId: + CHECK_PROC_INSTALL_HANDLER(pg_type, typinput, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_type, typoutput, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_type, typreceive, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_type, typsend, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_type, typmodin, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_type, typmodout, tuple, newtup); + CHECK_PROC_INSTALL_HANDLER(pg_type, typanalyze, tuple, newtup); + break; + } + } + + bool + sepgsqlCheckTuplePerms(Relation rel, HeapTuple tuple, HeapTuple newtup, + uint32 perms, bool abort) + { + security_class_t tclass; + access_vector_t av = 0; + bool rc = true; + + Assert(HeapTupleIsValid(tuple)); + + if ((perms & (SEPGSQL_PERMS_INSERT | SEPGSQL_PERMS_UPDATE)) != 0) + sepgsqlCheckProcedureInstall(rel, tuple, newtup); + + tclass = sepgsqlTupleObjectClass(RelationGetRelid(rel), tuple); + + switch (tclass) + { + case SECCLASS_DB_DATABASE: + av = sepgsqlPermsToDatabaseAv(perms, tuple, newtup); + break; + + case SECCLASS_DB_TABLE: + av = sepgsqlPermsToTableAv(perms, tuple, newtup); + break; + + case SECCLASS_DB_PROCEDURE: + av = sepgsqlPermsToProcedureAv(perms, tuple, newtup); + break; + + case SECCLASS_DB_COLUMN: + av = sepgsqlPermsToColumnAv(perms, tuple, newtup); + break; + + case SECCLASS_DB_BLOB: + av = sepgsqlPermsToBlobAv(perms, tuple, newtup); + break; + + default: /* SECCLASS_DB_TUPLE */ + if (sepostgresql_row_level) + av = sepgsqlPermsToTupleAv(perms, tuple, newtup); + break; + } + + if (av) + { + const char *audit_name + = sepgsqlTupleName(RelationGetRelid(rel), tuple); + + if (abort) + { + sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), + tclass, av, audit_name); + } + else + { + rc = sepgsqlClientHasPermissionNoAbort(HeapTupleGetSecLabel(tuple), + tclass, av, audit_name); + } + } + + return rc; + } + + /* + * sepgsqlCheckModuleInstallPerms + * + * It checks client's privilege to install a new shared loadable file. + */ + void + sepgsqlCheckModuleInstallPerms(const char *filename) + { + security_context_t file_context; + Form_pg_database dbform; + HeapTuple dbtup; + char *fullpath; + + /* (client) <-- db_database:module_install --> (database) */ + dbtup = SearchSysCache(DATABASEOID, + ObjectIdGetDatum(MyDatabaseId), + 0, 0, 0); + if (!HeapTupleIsValid(dbtup)) + elog(ERROR, "SELinux: cache lookup failed for database: %u", MyDatabaseId); + + dbform = (Form_pg_database) GETSTRUCT(dbtup); + sepgsqlClientHasPermission(HeapTupleGetSecLabel(dbtup), + SECCLASS_DB_DATABASE, + DB_DATABASE__INSTALL_MODULE, + NameStr(dbform->datname)); + ReleaseSysCache(dbtup); + + /* (client) <-- db_databse:module_install --> (*.so file) */ + fullpath = expand_dynamic_library_name(filename); + if (getfilecon_raw(fullpath, &file_context) < 0) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not access file \"%s\": %m", fullpath))); + PG_TRY(); + { + sepgsqlComputePermission(sepgsqlGetClientContext(), + file_context, + SECCLASS_DB_DATABASE, + DB_DATABASE__INSTALL_MODULE, + fullpath); + } + PG_CATCH(); + { + freecon(file_context); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(file_context); + } + + /* + * sepgsqlSetDefaultContext + * + * This function attach a proper security context for a newly inserted tuple, + * refering the security policy. + * In the default, any tuple inherits the security context of its table. + * However, we have several exception for some of system catalog. It come from + * TYPE_TRANSITION rules in the security policy. + */ + static Oid + sepgsqlDefaultDatabaseContext(Relation rel, HeapTuple tuple) + { + security_context_t newcon; + + newcon = sepgsqlComputeCreateContext(sepgsqlGetClientContext(), + sepgsqlGetClientContext(), + SECCLASS_DB_DATABASE); + return pgaceSecurityLabelToSid(newcon); + } + + static Oid + sepgsqlDefaultTableContext(Relation rel, HeapTuple tuple) + { + return sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(), + SECCLASS_DB_TABLE); + } + + static Oid + sepgsqlDefaultProcedureContext(Relation rel, HeapTuple tuple) + { + return sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(), + SECCLASS_DB_PROCEDURE); + } + + static Oid + sepgsqlDefaultColumnContext(Relation rel, HeapTuple tuple) + { + Form_pg_attribute attForm; + Oid tblsid; + + attForm = (Form_pg_attribute) GETSTRUCT(tuple); + + if (IsBootstrapProcessingMode() && + (attForm->attrelid == TypeRelationId || + attForm->attrelid == ProcedureRelationId || + attForm->attrelid == AttributeRelationId || + attForm->attrelid == RelationRelationId)) + { + /* + * We cannot access relation caches on very early phase + * in bootstrap, so it assumes tables has default security + * context and unlabeled by initdb. + */ + tblsid = sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(), + SECCLASS_DB_TABLE); + } + else + { + HeapTuple reltup + = SearchSysCache(RELOID, + ObjectIdGetDatum(attForm->attrelid), + 0, 0, 0); + if (!HeapTupleIsValid(reltup)) + elog(ERROR, "SELinux: cache lookup failed for relation: %u", + attForm->attrelid); + + tblsid = HeapTupleGetSecLabel(reltup); + + ReleaseSysCache(reltup); + } + + return sepgsqlClientCreateSid(tblsid, SECCLASS_DB_COLUMN); + } + + static Oid + sepgsqlDefaultTupleContext(Relation rel, HeapTuple tuple) + { + Oid tblsid; + + if (IsBootstrapProcessingMode() && + (RelationGetRelid(rel) == TypeRelationId || + RelationGetRelid(rel) == ProcedureRelationId || + RelationGetRelid(rel) == AttributeRelationId || + RelationGetRelid(rel) == RelationRelationId)) + { + /* + * We cannot access relation caches on very early phase + * in bootstrap, so it assumes tables has default security + * context and unlabeled by initdb. + */ + tblsid = sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(), + SECCLASS_DB_TABLE); + } + else + { + HeapTuple reltup + = SearchSysCache(RELOID, + ObjectIdGetDatum(RelationGetRelid(rel)), + 0, 0, 0); + if (!HeapTupleIsValid(reltup)) + elog(ERROR, "SELinux: cache lookup failed for relation: %u", + RelationGetRelid(rel)); + + tblsid = HeapTupleGetSecLabel(reltup); + + ReleaseSysCache(reltup); + } + + return sepgsqlClientCreateSid(tblsid, SECCLASS_DB_TUPLE); + } + + static Oid + sepgsqlDefaultBlobContext(Relation rel, HeapTuple tuple) + { + /* + * NOTE: + * A new tuple to be inserted into pg_largeobject inherits + * a security context of prior tuples of same large object. + * The "SnapshotNow" is available for this purpose because + * lo_create() invokes CommandCounterIncrement() just after + * creation of a new large object. + * + * If we can find no prior tuples, it means this action to + * insert the first page, or client invokes INSERT INTO ... + * with multiple tuples with same loid. However, these + * tuples are labeled by same TYPE_TRANSITION rules in both + * cases. So, there are no differences. + */ + Form_pg_largeobject loForm + = (Form_pg_largeobject) GETSTRUCT(tuple); + ScanKeyData skey; + SysScanDesc scan; + HeapTuple lotup; + Oid newsid = InvalidOid; + + ScanKeyInit(&skey, + Anum_pg_largeobject_loid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(loForm->loid)); + scan = systable_beginscan(rel, + LargeObjectLOidPNIndexId, true, + SnapshotNow, 1, &skey); + while ((lotup = systable_getnext(scan)) != NULL) + { + newsid = HeapTupleGetSecLabel(lotup); + if (OidIsValid(newsid)) + break; + } + systable_endscan(scan); + + if (!OidIsValid(newsid)) + { + newsid = sepgsqlClientCreateSid(sepgsqlGetDatabaseSecurityId(), + SECCLASS_DB_BLOB); + } + + return newsid; + } + + void + sepgsqlSetDefaultContext(Relation rel, HeapTuple tuple) + { + security_class_t tclass; + Oid newsid; + + Assert(HeapTupleHasSecLabel(tuple)); + tclass = sepgsqlTupleObjectClass(RelationGetRelid(rel), tuple); + + switch (tclass) + { + case SECCLASS_DB_DATABASE: + newsid = sepgsqlDefaultDatabaseContext(rel, tuple); + break; + case SECCLASS_DB_TABLE: + newsid = sepgsqlDefaultTableContext(rel, tuple); + break; + case SECCLASS_DB_PROCEDURE: + newsid = sepgsqlDefaultProcedureContext(rel, tuple); + break; + case SECCLASS_DB_COLUMN: + newsid = sepgsqlDefaultColumnContext(rel, tuple); + break; + case SECCLASS_DB_BLOB: + newsid = sepgsqlDefaultBlobContext(rel, tuple); + break; + default: /* SECCLASS_DB_TUPLE */ + newsid = sepgsqlDefaultTupleContext(rel, tuple); + break; + } + + HeapTupleSetSecLabel(tuple, newsid); + } diff -Nrpc base/src/backend/security/sepgsql/proxy.c sepgsql/src/backend/security/sepgsql/proxy.c *** base/src/backend/security/sepgsql/proxy.c Thu Jan 1 09:00:00 1970 --- sepgsql/src/backend/security/sepgsql/proxy.c Fri Jan 23 13:05:55 2009 *************** *** 0 **** --- 1,1097 ---- + /* + * src/backend/security/sepgsql/proxy.c + * Proxying the given Query trees via SE-PostgreSQL + * + * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + */ + #include "postgres.h" + + #include "access/genam.h" + #include "access/heapam.h" + #include "access/sysattr.h" + #include "catalog/heap.h" + #include "catalog/indexing.h" + #include "catalog/namespace.h" + #include "catalog/pg_attribute.h" + #include "catalog/pg_class.h" + #include "catalog/pg_constraint.h" + #include "catalog/pg_database.h" + #include "catalog/pg_largeobject.h" + #include "catalog/pg_operator.h" + #include "catalog/pg_proc.h" + #include "catalog/pg_security.h" + #include "catalog/pg_trigger.h" + #include "catalog/pg_type.h" + #include "executor/executor.h" + #include "nodes/nodeFuncs.h" + #include "nodes/security.h" + #include "optimizer/clauses.h" + #include "optimizer/plancat.h" + #include "optimizer/prep.h" + #include "optimizer/tlist.h" + #include "parser/parsetree.h" + #include "security/pgace.h" + #include "storage/lock.h" + #include "utils/array.h" + #include "utils/fmgroids.h" + #include "utils/fmgrtab.h" + #include "utils/lsyscache.h" + #include "utils/syscache.h" + #include "utils/tqual.h" + + /* + * sepgsqlWalkerContext + * + * This structure holds a context during analyzing a given query. + * selist is a list of SEvalItemXXX objects to enumerate appared + * tables and columns. These are evaluated later, just before + * executing query. + * is_internal_use shows the current state whether the current + * Node is chained with target list, or conditional clause. + */ + typedef struct sepgsqlWalkerContext + { + struct sepgsqlWalkerContext *parent; + Query *query; /* Query structure of current layer */ + List *selist; /* list of SEvalItemXXX */ + bool is_internal_use; + } sepgsqlWalkerContext; + + #define seitem_index_to_attno(index) \ + ((index) + FirstLowInvalidHeapAttributeNumber + 1) + #define seitem_attno_to_index(attno) \ + ((attno) - FirstLowInvalidHeapAttributeNumber - 1) + + + /* + * addEvalRelation + * addEvalRelationRTE + * + * These functions add a given relation into selist, if it is not + * contained yet. In addition, addEvalRelationRTE also marks required + * permissions on rte->pgaceTuplePerms. It is delivered to Scan object + * and we can use it on ExecScan hook to apply tuple-level access + * controls. + */ + static List * + addEvalRelation(List *selist, Oid relid, bool inh, uint32 perms) + { + SelinuxEvalItem *seitem; + Form_pg_class relForm; + HeapTuple tuple; + ListCell *l; + + foreach (l, selist) + { + seitem = (SelinuxEvalItem *) lfirst(l); + Assert(IsA(seitem, SelinuxEvalItem)); + + if (seitem->relid == relid && seitem->inh == inh) + { + seitem->relperms |= perms; + return selist; + } + } + + /* not found, so create a new one */ + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", relid); + relForm = (Form_pg_class) GETSTRUCT(tuple); + + seitem = makeNode(SelinuxEvalItem); + seitem->relid = relid; + seitem->inh = inh; + seitem->relperms = perms; + seitem->nattrs = seitem_attno_to_index(relForm->relnatts) + 1; + seitem->attperms = palloc0(seitem->nattrs * sizeof(uint32)); + + ReleaseSysCache(tuple); + + return lappend(selist, seitem); + } + + static List * + addEvalRelationRTE(List *selist, RangeTblEntry *rte, uint32 perms) + { + rte->pgaceTuplePerms |= (perms & DB_TABLE__USE ? SEPGSQL_PERMS_USE : 0); + rte->pgaceTuplePerms |= (perms & DB_TABLE__SELECT ? SEPGSQL_PERMS_SELECT : 0); + + return addEvalRelation(selist, rte->relid, rte->inh, perms); + } + + /* + * addEvalAttribute + * addEvalAttributeRTE + * + * These functions add a given attribute into selist, if it is not + * contained yet. In addition, addEvalAttributeRTE also marks required + * permissions on rte->pgaceTuplePerms. It is delivered to Scan object + * and we can use it on ExecScan hook to apply tuple-level access + * controls. + */ + static List * + addEvalAttribute(List *selist, Oid relid, bool inh, AttrNumber attno, uint32 perms) + { + SelinuxEvalItem *seitem; + Form_pg_class relForm; + HeapTuple tuple; + ListCell *l; + int index = seitem_attno_to_index(attno); + + foreach (l, selist) + { + seitem = (SelinuxEvalItem *) lfirst(l); + Assert(IsA(seitem, SelinuxEvalItem)); + + if (seitem->relid == relid && seitem->inh == inh) + { + if (index >= seitem->nattrs) + { + uint32 *attperms, nattrs; + + /* + * NOTE: the following step has a possibility that + * index number overs seitem->nattrs + * + * 1. PREPARE p AS SELECT t FROM t; + * 2. ALTER TABLE t ADD COLUMN x int; + * 3. EXECUTE p; + * + * Because whole-row-reference is extracted to + * references to all the user columns, so table + * may have different number of columns between + * state.1 and state.3. + * In this case, we need to rebuild seitem->attperms + */ + + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", relid); + relForm = (Form_pg_class) GETSTRUCT(tuple); + + nattrs = seitem_attno_to_index(relForm->relnatts) + 1; + attperms = palloc0(nattrs * sizeof(uint32)); + memcpy(attperms, seitem->attperms, + seitem->nattrs * sizeof(uint32)); + seitem->nattrs = nattrs; + seitem->attperms = attperms; + + ReleaseSysCache(tuple); + } + + if (index < 0 || index >= seitem->nattrs) + elog(ERROR, "SELinux: invalid attribute number: %d at relation: %u", + attno, relid); + + seitem->attperms[index] |= perms; + + return selist; + } + } + + /* not found, so create a new one */ + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for relation %u", relid); + relForm = (Form_pg_class) GETSTRUCT(tuple); + + seitem = makeNode(SelinuxEvalItem); + seitem->relid = relid; + seitem->inh = inh; + seitem->relperms = 0; + seitem->nattrs = seitem_attno_to_index(relForm->relnatts) + 1; + seitem->attperms = palloc0(seitem->nattrs * sizeof(uint32)); + if (index < 0 || index >= seitem->nattrs) + elog(ERROR, "SELinux: invalid attribute number: %d at relation: %u", + attno, relid); + seitem->attperms[index] |= perms; + + ReleaseSysCache(tuple); + + return lappend(selist, seitem); + } + + static List * + addEvalAttributeRTE(List *selist, RangeTblEntry *rte, AttrNumber attno, uint32 perms) + { + uint32 tbl_perms = 0; + + tbl_perms |= (perms & DB_COLUMN__USE ? DB_TABLE__USE : 0); + tbl_perms |= (perms & DB_COLUMN__SELECT ? DB_TABLE__SELECT : 0); + tbl_perms |= (perms & DB_COLUMN__INSERT ? DB_TABLE__INSERT : 0); + tbl_perms |= (perms & DB_COLUMN__UPDATE ? DB_TABLE__UPDATE : 0); + selist = addEvalRelationRTE(selist, rte, tbl_perms); + + /* + * Special care for pg_largeobject.data + */ + if ((perms & DB_COLUMN__SELECT) != 0 && + rte->relid == LargeObjectRelationId && + attno == Anum_pg_largeobject_data) + rte->pgaceTuplePerms |= SEPGSQL_PERMS_READ; + + return addEvalAttribute(selist, rte->relid, rte->inh, attno, perms); + } + + /* + * addEvalForeignKeyConstraint + * + * This function add special case handling for PK/FK constraints. + * invoke trigger function requires to access rights for all attribute + * + */ + static List * + addEvalForeignKeyConstraint(List *selist, Form_pg_trigger trigger) + { + HeapTuple contup; + Datum attdat; + ArrayType *attrs; + int index; + int16 *attnum; + bool isnull; + + contup = SearchSysCache(CONSTROID, + ObjectIdGetDatum(trigger->tgconstraint), + 0, 0, 0); + if (!HeapTupleIsValid(contup)) + elog(ERROR, "SELinux: cache lookup failed for constraint %u", + trigger->tgconstrrelid); + + if (trigger->tgfoid == F_RI_FKEY_CHECK_INS || + trigger->tgfoid == F_RI_FKEY_CHECK_UPD) + attdat = SysCacheGetAttr(CONSTROID, contup, + Anum_pg_constraint_conkey, &isnull); + else + attdat = SysCacheGetAttr(CONSTROID, contup, + Anum_pg_constraint_confkey, &isnull); + if (isnull) + elog(ERROR, "null PK/FK for constraint %u", + trigger->tgconstrrelid); + attrs = DatumGetArrayTypeP(attdat); + + if (ARR_NDIM(attrs) != 1 || + ARR_HASNULL(attrs) || + ARR_ELEMTYPE(attrs) != INT2OID) + elog(ERROR, "SELinux: unexpected constraint %u", trigger->tgconstrrelid); + + attnum = (int16 *) ARR_DATA_PTR(attrs); + for (index = 0; index < ARR_DIMS(attrs)[0]; index++) + selist = addEvalAttribute(selist, trigger->tgrelid, false, + attnum[index], DB_COLUMN__SELECT); + + ReleaseSysCache(contup); + + return selist; + } + + /* + * addEvalTriggerFunction + * + * This function adds needed items into selist, to execute a trigger + * function. At least, it requires permission set to execute a function + * configured as a trigger, to select a table and whole of columns + * because whole of a tuple is delivered to trigger functions. + */ + static List * + addEvalTriggerFunction(List *selist, Oid relid, int cmdType) + { + Relation rel; + SysScanDesc scan; + ScanKeyData skey; + HeapTuple tuple; + + rel = heap_open(TriggerRelationId, AccessShareLock); + ScanKeyInit(&skey, + Anum_pg_trigger_tgrelid, + BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); + scan = systable_beginscan(rel, TriggerRelidNameIndexId, + true, SnapshotNow, 1, &skey); + while (HeapTupleIsValid((tuple = systable_getnext(scan)))) + { + Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple); + Form_pg_class relForm; + HeapTuple reltup; + + /* + * Skip not-invoked triggers + */ + if (!trigForm->tgenabled) + continue; + if (cmdType == CMD_INSERT && !TRIGGER_FOR_INSERT(trigForm->tgtype)) + continue; + if (cmdType == CMD_UPDATE && !TRIGGER_FOR_UPDATE(trigForm->tgtype)) + continue; + if (cmdType == CMD_DELETE && !TRIGGER_FOR_DELETE(trigForm->tgtype)) + continue; + + /* + * per STATEMENT trigger cannot refer whole of a tuple + */ + if (!TRIGGER_FOR_ROW(trigForm->tgtype)) + continue; + + /* + * BEFORE-ROW-INSERT trigger cannot refer whole of a tuple + */ + if (TRIGGER_FOR_BEFORE(trigForm->tgtype) && + TRIGGER_FOR_INSERT(trigForm->tgtype)) + continue; + + reltup = SearchSysCache(RELOID, + ObjectIdGetDatum(relid), + 0, 0, 0); + relForm = (Form_pg_class) GETSTRUCT(reltup); + + selist = addEvalRelation(selist, relid, false, DB_TABLE__SELECT); + + if (RI_FKey_trigger_type(trigForm->tgfoid) != RI_TRIGGER_NONE) + selist = addEvalForeignKeyConstraint(selist, trigForm); + else + selist = addEvalAttribute(selist, relid, false, + 0, DB_COLUMN__SELECT); + ReleaseSysCache(reltup); + } + systable_endscan(scan); + heap_close(rel, AccessShareLock); + + return selist; + } + + /* + * sepgsqlExprWalker + * + * This function walks on the given expression tree to pick up + * all the appeared tables and columns. Their identifiers are + * chains on swc->selist to evaluate permissions on them later. + * + * walkVarHelper picks up an accessed column and its contained + * table, and chains them on swc->selist. + * When swc->is_internal_use is true, it means this reference + * is checked as "use" permission because its contents are + * consumed internally, and not to be returned to client directly. + * Otherwise, "select" permission is applied. + * + * walkQueryHelper walks on Query structure. + * The reason why we don't use query_tree_walker() is that + * SE-PostgreSQL need to apply different permission between + * targetList and havingQual, for example. + */ + + static bool + sepgsqlExprWalker(Node *node, sepgsqlWalkerContext *swc); + + static void + sepgsqlExprWalkerFlags(Node *node, sepgsqlWalkerContext *swc, + bool is_internal_use); + + /* + * wholeRefJoinWalker + * + * A corner case need to invoke this walker function. + * When we use whole-row-reference on RTE_JOIN relation, + * it should be extracted to whole-row-references on + * sources relations. + * + * EXAMPLE: + * SELECT t4 FROM (t1 JOIN (t2 JOIN t3 USING (a)) USING (b)) AS t4; + * + * Because RangeTblEntry with RTE_JOIN does not have any identifiers + * of its source relations, we have to scan Query->jointree again to + * look up sources again. :( + */ + typedef struct + { + Query *query; + int rtindex; + /* + * rtindex == 0 means we are now walking on the required JoinExpr + * or its leafs, so we need to pick up all the appeared relations + * under the JoinExpr in this case. + */ + List *selist; + uint32 perms; + } wholeRefJoinWalkerContext; + + static bool + wholeRefJoinWalker(Node *node, wholeRefJoinWalkerContext *jwc) + { + if (!node) + return false; + + if (IsA(node, JoinExpr)) + { + JoinExpr *j = (JoinExpr *) node; + + if (j->rtindex == jwc->rtindex) + { + int rtindex_backup = jwc->rtindex; + bool rc; + + jwc->rtindex = 0; + rc = expression_tree_walker(node, wholeRefJoinWalker, jwc); + jwc->rtindex = rtindex_backup; + + return rc; + } + } + else if (IsA(node, RangeTblRef) && jwc->rtindex == 0) + { + RangeTblRef *rtr = (RangeTblRef *) node; + RangeTblEntry *rte = rt_fetch(rtr->rtindex, + jwc->query->rtable); + if (rte->rtekind == RTE_RELATION) + { + jwc->selist = addEvalAttributeRTE(jwc->selist, rte, 0, jwc->perms); + } + } + return expression_tree_walker(node, wholeRefJoinWalker, jwc); + } + + static void + walkVarHelper(Var *var, sepgsqlWalkerContext *swc) + { + sepgsqlWalkerContext *cur = swc; + Query *query; + RangeTblEntry *rte; + int lv; + + Assert(IsA(var, Var)); + + for (lv = var->varlevelsup; lv > 0; lv--) + { + Assert(cur->parent != NULL); + cur = cur->parent; + } + query = cur->query; + + rte = rt_fetch(var->varno, query->rtable); + Assert(IsA(rte, RangeTblEntry)); + + if (rte->rtekind == RTE_RELATION) + { + uint32 perms = swc->is_internal_use + ? DB_COLUMN__USE : DB_COLUMN__SELECT; + + swc->selist = addEvalAttributeRTE(swc->selist, rte, + var->varattno, perms); + } + else if (rte->rtekind == RTE_JOIN) + { + if (var->varattno == 0) + { + wholeRefJoinWalkerContext jwcData; + + jwcData.query = query; + jwcData.rtindex = var->varno; + jwcData.selist = swc->selist; + jwcData.perms = swc->is_internal_use + ? DB_COLUMN__USE : DB_COLUMN__SELECT; + + wholeRefJoinWalker((Node *)query->jointree, &jwcData); + swc->selist = jwcData.selist; + } + else + { + Node *node = list_nth(rte->joinaliasvars, + var->varattno - 1); + sepgsqlExprWalker(node, swc); + } + } + } + + static List * + walkQueryHelper(Query *query, sepgsqlWalkerContext *swc) + { + sepgsqlWalkerContext swcData; + RangeTblEntry *rte; + + memset(&swcData, 0, sizeof(swcData)); + swcData.parent = swc; + swcData.selist = (!swc ? NIL : swc->selist); + swcData.query = query; + + if (query->commandType != CMD_DELETE) + { + ListCell *l; + + foreach (l, query->targetList) + { + TargetEntry *tle = lfirst(l); + bool is_security = false; + + Assert(IsA(tle, TargetEntry)); + + if (tle->resjunk && + tle->resname && + strcmp(tle->resname, SecurityLabelAttributeName) == 0) + is_security = true; + + if (tle->resjunk && !is_security) + { + sepgsqlExprWalkerFlags((Node *) tle->expr, &swcData, true); + continue; + } + + sepgsqlExprWalkerFlags((Node *) tle->expr, &swcData, false); + + if (query->commandType != CMD_SELECT) + { + AttrNumber attno = tle->resno; + uint32 perms; + + if (is_security) + attno = SecurityLabelAttributeNumber; + + if (query->commandType == CMD_UPDATE) + perms = DB_COLUMN__UPDATE; + else + perms = DB_COLUMN__INSERT; + + rte = rt_fetch(query->resultRelation, query->rtable); + Assert(IsA(rte, RangeTblEntry)); + + swcData.selist + = addEvalAttributeRTE(swcData.selist, rte, attno, perms); + } + } + } + else + { + /* no need to check column-level permission for DELETE */ + rte = rt_fetch(query->resultRelation, query->rtable); + Assert(IsA(rte, RangeTblEntry)); + + swcData.selist + = addEvalRelationRTE(swcData.selist, rte, DB_TABLE__DELETE); + } + + sepgsqlExprWalkerFlags((Node *) query->returningList, &swcData, false); + sepgsqlExprWalkerFlags((Node *) query->jointree, &swcData, true); + sepgsqlExprWalkerFlags((Node *) query->setOperations, &swcData, true); + sepgsqlExprWalkerFlags((Node *) query->havingQual, &swcData, true); + sepgsqlExprWalkerFlags((Node *) query->sortClause, &swcData, true); + sepgsqlExprWalkerFlags((Node *) query->groupClause, &swcData, true); + sepgsqlExprWalkerFlags((Node *) query->limitOffset, &swcData, true); + sepgsqlExprWalkerFlags((Node *) query->limitCount, &swcData, true); + sepgsqlExprWalkerFlags((Node *) query->cteList, &swcData, true); + sepgsqlExprWalkerFlags((Node *) query->windowClause, &swcData, true); + + return swcData.selist; + } + + static void + walkRangeTblRefHelper(RangeTblRef *rtr, sepgsqlWalkerContext *swc) + { + Query *query = swc->query; + RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable); + + Assert(IsA(rte, RangeTblEntry)); + + switch (rte->rtekind) + { + case RTE_RELATION: + if (rtr->rtindex != query->resultRelation) + swc->selist = addEvalRelationRTE(swc->selist, rte, + DB_TABLE__SELECT); + break; + + case RTE_SUBQUERY: + swc->selist = walkQueryHelper(rte->subquery, swc); + break; + + case RTE_FUNCTION: + sepgsqlExprWalker(rte->funcexpr, swc); + break; + + case RTE_VALUES: + sepgsqlExprWalker((Node *) rte->values_lists, swc); + break; + + default: + /* do nothing */ + break; + } + } + + static void + walkSortGroupClauseHelper(SortGroupClause *sgc, sepgsqlWalkerContext *swc) + { + Query *query = swc->query; + TargetEntry *tle + = get_sortgroupref_tle(sgc->tleSortGroupRef, + query->targetList); + + Assert(IsA(tle, TargetEntry)); + + sepgsqlExprWalker((Node *) tle->expr, swc); + } + + static bool + sepgsqlExprWalker(Node *node, sepgsqlWalkerContext *swc) + { + if (node == NULL) + return false; + else if (IsA(node, Var)) + walkVarHelper((Var *) node, swc); + else if (IsA(node, RangeTblRef)) + walkRangeTblRefHelper((RangeTblRef *) node, swc); + else if (IsA(node, Query)) + { + swc->selist + = walkQueryHelper((Query *) node, swc); + } + else if (IsA(node, SortGroupClause)) + { + walkSortGroupClauseHelper((SortGroupClause *) node, swc); + + return false; + } + return expression_tree_walker(node, sepgsqlExprWalker, (void *) swc); + } + + static void + sepgsqlExprWalkerFlags(Node *node, sepgsqlWalkerContext *swc, + bool is_internal_use) + { + bool saved_is_internal_use = swc->is_internal_use; + + swc->is_internal_use = is_internal_use; + sepgsqlExprWalker(node, swc); + swc->is_internal_use = saved_is_internal_use; + } + + /* + * sepgsqlPostQueryRewrite + * + * This function is invoked just after given queries are rewritten + * via query-rewritter phase. It walks on given query trees to + * picks up all appeared tables and columns, and to chains the list + * of them on query->pgaceItem. + * This list is used to evaluate permissions later, just before + * the query execution. + * + * It do nothing for DDL queries, because these are processed in + * sepgsqlProcessUtility() hook. + */ + List * + sepgsqlPostQueryRewrite(List *queryList) + { + ListCell *l; + + foreach (l, queryList) + { + Query *query = (Query *) lfirst(l); + + Assert(IsA(query, Query)); + + if (query->commandType == CMD_SELECT || + query->commandType == CMD_UPDATE || + query->commandType == CMD_INSERT || + query->commandType == CMD_DELETE) + { + query->pgaceItem + = (Node *) walkQueryHelper(query, NULL); + } + } + + return queryList; + } + + /* + * checkSelinuxEvalItem + * checks give SelinuxEvalItem object based on the security + * policy of SELinux. + */ + static void + checkSelinuxEvalItem(SelinuxEvalItem *seitem) + { + Form_pg_class relForm; + Form_pg_attribute attForm; + HeapTuple tuple; + AttrNumber attno; + const char *audit_name; + int index; + + Assert(IsA(seitem, SelinuxEvalItem)); + + /* + * Prevent to write pg_security by hand + */ + if (seitem->relid == SecurityRelationId && + (seitem->relperms & (DB_TABLE__UPDATE | DB_TABLE__INSERT | DB_TABLE__DELETE))) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not modify pg_security by hand"))); + + /* + * Permission checks on table + */ + tuple = SearchSysCache(RELOID, + ObjectIdGetDatum(seitem->relid), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SELinux: cache lookup failed for relation: %u", + seitem->relid); + relForm = (Form_pg_class) GETSTRUCT(tuple); + if (relForm->relkind != RELKIND_RELATION) + { + ReleaseSysCache(tuple); + return; + } + + audit_name = sepgsqlTupleName(RelationRelationId, tuple); + sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), + SECCLASS_DB_TABLE, + seitem->relperms, + audit_name); + ReleaseSysCache(tuple); + + /* + * Expand whole-row-reference + */ + index = seitem_attno_to_index(InvalidAttrNumber); + if (seitem->attperms[index] != 0) + { + uint32 perms = seitem->attperms[index]; + + seitem->attperms[index] = 0; + for (index++; index < seitem->nattrs; index++) + seitem->attperms[index] |= perms; + } + + /* + * Permission checks on columns + */ + for (index = 0; index < seitem->nattrs; index++) + { + if (seitem->attperms[index] == 0) + continue; + + attno = seitem_index_to_attno(index); + tuple = SearchSysCache(ATTNUM, + ObjectIdGetDatum(seitem->relid), + Int16GetDatum(attno), + 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "SELinux: cache lookup failed for attribute %d of relation %u", + attno, seitem->relid); + attForm = (Form_pg_attribute) GETSTRUCT(tuple); + /* + * NOTE: When user uses whole-row-reference on a table + * which has already dropped column, the column can have + * non-zero required permissions, but being ignorable. + */ + if (attForm->attisdropped) + { + ReleaseSysCache(tuple); + continue; + } + + audit_name = sepgsqlTupleName(AttributeRelationId, tuple); + sepgsqlClientHasPermission(HeapTupleGetSecLabel(tuple), + SECCLASS_DB_COLUMN, + seitem->attperms[index], + audit_name); + ReleaseSysCache(tuple); + } + } + + static List * + expandEvalItemInheritance(List *selist) + { + List *result = NIL; + List *inherits; + ListCell *l, *i; + int index; + + foreach (l, selist) + { + SelinuxEvalItem *seitem = lfirst(l); + + Assert(IsA(seitem, SelinuxEvalItem)); + + if (!seitem->inh) + { + result = lappend(result, seitem); + continue; + } + + inherits = find_all_inheritors(seitem->relid); + foreach (i, inherits) + { + result = addEvalRelation(result, lfirst_oid(i), false, + seitem->relperms); + for (index = 0; index < seitem->nattrs; index++) + { + Oid relid_inh = lfirst_oid(i); + AttrNumber attno; + + if (seitem->attperms[index] == 0) + continue; + + attno = seitem_index_to_attno(index); + if (attno < 1 || seitem->relid == relid_inh) + { + /* + * If attribute is system-column or whole-row-reference, + * or inherit relation is itself, we don't need to fix up + * attribute number. + */ + result = addEvalAttribute(result, relid_inh, false, + attno, seitem->attperms[index]); + continue; + } + else + { + char *attname = get_attname(seitem->relid, attno); + + if (!attname) + elog(ERROR, "cache lookup failed for attribute %d of relation %u", + attno, seitem->relid); + + attno = get_attnum(relid_inh, attname); + if (attno == InvalidAttrNumber) + elog(ERROR, "cache lookup failed for attribute %s of relation %u", + attname, relid_inh); + + result = addEvalAttribute(result, relid_inh, false, + attno, seitem->attperms[index]); + pfree(attname); + } + } + } + } + return result; + } + + /* + * sepgsqlExecutorStart + * + * This function is invoked at the head of ExecutorStart, to evaluate + * permissions to access appeared object within the given query. + * Query->pgaceItem is a list of SelinuxEvalItem objects generated in + * previous phase, and it is copied to PlannedStmt->pgaceItem in the + * optimizer. + * This functions expand given selist based on table inheritance, + * adds additional permissions related to trigger functions, and + * expands whole-row-references. Then, these items are evaluated + * based on the security policy of SELinux. + */ + void + sepgsqlExecutorStart(QueryDesc *queryDesc, int eflags) + { + PlannedStmt *pstmt = queryDesc->plannedstmt; + RangeTblEntry *rte; + List *selist; + ListCell *l; + + /* + * EXPLAIN statement does not access any object. + */ + if (eflags & EXEC_FLAG_EXPLAIN_ONLY) + return; + + if (!pstmt->pgaceItem) + return; + + Assert(IsA(pstmt->pgaceItem, List)); + selist = copyObject(pstmt->pgaceItem); + + /* + * expand table inheritances + */ + selist = expandEvalItemInheritance(selist); + + /* + * add checks for access via trigger function + */ + foreach(l, pstmt->resultRelations) + { + Index rindex = lfirst_int(l); + + rte = rt_fetch(rindex, pstmt->rtable); + Assert(IsA(rte, RangeTblEntry)); + + selist = addEvalTriggerFunction(selist, rte->relid, + pstmt->commandType); + } + + /* + * Check SelinuxEvalItem + */ + foreach (l, selist) + checkSelinuxEvalItem((SelinuxEvalItem *) lfirst(l)); + } + + /* + * -------------------------------------------------------------- + * Process Utility hooks + * -------------------------------------------------------------- + */ + + /* + * sepgsqlProcessUtility + * + * This function is invoked from the head of ProcessUtility(), and + * checks given DDL queries. + * SE-PostgreSQL catch most of DDL actions on HeapTuple hooks, but + * an exception is TRUNCATE statement. + */ + void + sepgsqlProcessUtility(Node *parsetree, ParamListInfo params, bool isTopLevel) + { + switch (nodeTag(parsetree)) + { + case T_LoadStmt: + { + LoadStmt *load = (LoadStmt *)parsetree; + sepgsqlCheckModuleInstallPerms(load->filename); + } + break; + + case T_CreateFdwStmt: + { + CreateFdwStmt *createFdw = (CreateFdwStmt *)parsetree; + sepgsqlCheckModuleInstallPerms(createFdw->library); + } + break; + + case T_AlterFdwStmt: + { + AlterFdwStmt *alterFdw = (AlterFdwStmt *)parsetree; + if (alterFdw->library) + sepgsqlCheckModuleInstallPerms(alterFdw->library); + } + break; + + default: + /* + * do nothing here + */ + break; + } + } + + /* ---------------------------------------------------------- + * COPY TO/COPY FROM statement hooks + * ---------------------------------------------------------- */ + + /* + * sepgsqlCopyTable + * + * This function checks permission on the target table and columns + * of COPY statement. We don't place it at sepgsql/hooks.c because + * it internally uses addEvalXXXX() interface statically declared. + */ + void + sepgsqlCopyTable(Relation rel, List *attNumList, bool isFrom) + { + List *selist = NIL; + ListCell *l; + + /* + * on 'COPY FROM SELECT ...' cases, any checkings are done in select.c + */ + if (rel == NULL) + return; + + /* + * no need to check non-table relation + */ + if (RelationGetForm(rel)->relkind != RELKIND_RELATION) + return; + + selist = addEvalRelation(selist, RelationGetRelid(rel), false, + isFrom ? DB_TABLE__INSERT : DB_TABLE__SELECT); + foreach(l, attNumList) + { + AttrNumber attnum = lfirst_int(l); + + selist = addEvalAttribute(selist, RelationGetRelid(rel), false, attnum, + isFrom ? DB_COLUMN__INSERT : DB_COLUMN__SELECT); + } + + /* + * check call trigger function + */ + if (isFrom) + selist = addEvalTriggerFunction(selist, RelationGetRelid(rel), CMD_INSERT); + + foreach (l, selist) + checkSelinuxEvalItem((SelinuxEvalItem *) lfirst(l)); + } + + /* + * sepgsqlCopyFile + * + * This function check permission whether the client can + * read from/write to the given file. + */ + void sepgsqlCopyFile(Relation rel, int fdesc, const char *filename, bool isFrom) + { + security_context_t context; + security_class_t tclass + = sepgsqlFileObjectClass(fdesc, filename); + + if (fgetfilecon_raw(fdesc, &context) < 0) + ereport(ERROR, + (errcode(ERRCODE_SELINUX_ERROR), + errmsg("SELinux: could not get context of %s", filename))); + PG_TRY(); + { + sepgsqlComputePermission(sepgsqlGetClientContext(), + context, + tclass, + isFrom ? FILE__READ : FILE__WRITE, + filename); + } + PG_CATCH(); + { + freecon(context); + PG_RE_THROW(); + } + PG_END_TRY(); + freecon(context); + } + + /* + * sepgsqlCopyToTuple + * + * This function check permission to read the given tuple. + * If not allowed to read, it returns false to skip COPY TO + * this tuple. In the result, any violated tuples are filtered + * from the result of COPY TO, as if these are not exist. + */ + bool + sepgsqlCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple) + { + uint32 perms = SEPGSQL_PERMS_SELECT; + + /* + * for 'pg_largeobject' + */ + if (RelationGetRelid(rel) == LargeObjectRelationId) + { + ListCell *l; + + foreach(l, attNumList) + { + AttrNumber attnum = lfirst_int(l); + + if (attnum == Anum_pg_largeobject_data) + { + perms |= SEPGSQL_PERMS_READ; + break; + } + } + } + return sepgsqlCheckTuplePerms(rel, tuple, NULL, perms, false); + } diff -Nrpc base/src/backend/storage/file/fd.c sepgsql/src/backend/storage/file/fd.c *** base/src/backend/storage/file/fd.c Tue Jan 13 09:22:28 2009 --- sepgsql/src/backend/storage/file/fd.c Tue Jan 13 09:39:35 2009 *************** FileTruncate(File file, off_t offset) *** 1291,1296 **** --- 1291,1303 ---- return returnCode; } + int + FileRawDescriptor(File file) + { + Assert(FileIsValid(file)); + + return VfdCache[file].fd; + } /* * Routines that want to use stdio (ie, FILE*) should use AllocateFile diff -Nrpc base/src/backend/storage/ipc/ipci.c sepgsql/src/backend/storage/ipc/ipci.c *** base/src/backend/storage/ipc/ipci.c Mon Jan 5 17:36:07 2009 --- sepgsql/src/backend/storage/ipc/ipci.c Mon Jan 5 17:41:04 2009 *************** *** 25,30 **** --- 25,31 ---- #include "postmaster/autovacuum.h" #include "postmaster/bgwriter.h" #include "postmaster/postmaster.h" + #include "security/pgace.h" #include "storage/bufmgr.h" #include "storage/ipc.h" #include "storage/pg_shmem.h" *************** CreateSharedMemoryAndSemaphores(bool mak *** 118,123 **** --- 119,125 ---- #ifdef EXEC_BACKEND size = add_size(size, ShmemBackendArraySize()); #endif + size = add_size(size, pgaceShmemSize()); /* freeze the addin request size and include it */ addin_request_allowed = false; diff -Nrpc base/src/backend/tcop/fastpath.c sepgsql/src/backend/tcop/fastpath.c *** base/src/backend/tcop/fastpath.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/tcop/fastpath.c Fri Jan 16 17:05:27 2009 *************** *** 28,33 **** --- 28,34 ---- #include "miscadmin.h" #include "tcop/fastpath.h" #include "tcop/tcopprot.h" + #include "security/pgace.h" #include "utils/acl.h" #include "utils/lsyscache.h" #include "utils/snapmgr.h" *************** HandleFunctionRequest(StringInfo msgBuf) *** 348,353 **** --- 349,355 ---- if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(fid)); + pgaceCallFunction(&fip->flinfo); /* * Prepare function call info block and insert arguments. diff -Nrpc base/src/backend/tcop/pquery.c sepgsql/src/backend/tcop/pquery.c *** base/src/backend/tcop/pquery.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/tcop/pquery.c Sat Jan 3 15:58:18 2009 *************** PortalStart(Portal portal, ParamListInfo *** 573,579 **** Assert(pstmt->returningLists); portal->tupDesc = ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), ! false); } /* --- 573,579 ---- Assert(pstmt->returningLists); portal->tupDesc = ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), ! false, false, false); } /* diff -Nrpc base/src/backend/tcop/utility.c sepgsql/src/backend/tcop/utility.c *** base/src/backend/tcop/utility.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/tcop/utility.c Fri Jan 23 10:55:35 2009 *************** *** 49,54 **** --- 49,55 ---- #include "postmaster/bgwriter.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteRemove.h" + #include "security/pgace.h" #include "storage/fd.h" #include "tcop/pquery.h" #include "tcop/utility.h" *************** ProcessUtility(Node *parsetree, *** 258,263 **** --- 259,266 ---- if (completionTag) completionTag[0] = '\0'; + pgaceProcessUtility(parsetree, params, isTopLevel); + switch (nodeTag(parsetree)) { /* diff -Nrpc base/src/backend/utils/adt/acl.c sepgsql/src/backend/utils/adt/acl.c *** base/src/backend/utils/adt/acl.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/utils/adt/acl.c Fri Jan 23 10:55:35 2009 *************** static List *cached_membership_roles = N *** 66,73 **** static const char *getid(const char *s, char *n); static void putid(char *p, const char *s); - static Acl *allocacl(int n); - static void check_acl(const Acl *acl); static const char *aclparse(const char *s, AclItem *aip); static bool aclitem_match(const AclItem *a1, const AclItem *a2); static void check_circularity(const Acl *old_acl, const AclItem *mod_aip, --- 66,71 ---- *************** aclparse(const char *s, AclItem *aip) *** 347,353 **** * RETURNS: * the new Acl */ ! static Acl * allocacl(int n) { Acl *new_acl; --- 345,351 ---- * RETURNS: * the new Acl */ ! Acl * allocacl(int n) { Acl *new_acl; *************** aclconcat(const Acl *left_acl, const Acl *** 410,416 **** /* * Verify that an ACL array is acceptable (one-dimensional and has no nulls) */ ! static void check_acl(const Acl *acl) { if (ARR_ELEMTYPE(acl) != ACLITEMOID) --- 408,414 ---- /* * Verify that an ACL array is acceptable (one-dimensional and has no nulls) */ ! void check_acl(const Acl *acl) { if (ARR_ELEMTYPE(acl) != ACLITEMOID) diff -Nrpc base/src/backend/utils/adt/ri_triggers.c sepgsql/src/backend/utils/adt/ri_triggers.c *** base/src/backend/utils/adt/ri_triggers.c Fri Jan 9 10:09:59 2009 --- sepgsql/src/backend/utils/adt/ri_triggers.c Fri Jan 9 10:15:15 2009 *************** *** 39,44 **** --- 39,45 ---- #include "parser/parse_coerce.h" #include "parser/parse_relation.h" #include "miscadmin.h" + #include "security/pgace.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" *************** ri_PerformCheck(RI_QueryKey *qkey, SPIPl *** 3264,3269 **** --- 3265,3272 ---- int spi_result; Oid save_userid; bool save_secdefcxt; + Datum rowacl_private = 0; + Datum pgace_private = 0; Datum vals[RI_MAX_NUMKEYS * 2]; char nulls[RI_MAX_NUMKEYS * 2]; *************** ri_PerformCheck(RI_QueryKey *qkey, SPIPl *** 3346,3356 **** GetUserIdAndContext(&save_userid, &save_secdefcxt); SetUserIdAndContext(RelationGetForm(query_rel)->relowner, true); ! /* Finally we can run the query. */ ! spi_result = SPI_execute_snapshot(qplan, ! vals, nulls, ! test_snapshot, crosscheck_snapshot, ! false, false, limit); /* Restore UID */ SetUserIdAndContext(save_userid, save_secdefcxt); --- 3349,3371 ---- GetUserIdAndContext(&save_userid, &save_secdefcxt); SetUserIdAndContext(RelationGetForm(query_rel)->relowner, true); ! pgaceBeginPerformCheckFK(query_rel, detectNewRows, save_userid, ! &rowacl_private, &pgace_private); ! PG_TRY(); ! { ! /* Finally we can run the query. */ ! spi_result = SPI_execute_snapshot(qplan, ! vals, nulls, ! test_snapshot, crosscheck_snapshot, ! false, false, limit); ! } ! PG_CATCH(); ! { ! pgaceEndPerformCheckFK(query_rel, rowacl_private, pgace_private); ! PG_RE_THROW(); ! } ! PG_END_TRY(); ! pgaceEndPerformCheckFK(query_rel, rowacl_private, pgace_private); /* Restore UID */ SetUserIdAndContext(save_userid, save_secdefcxt); diff -Nrpc base/src/backend/utils/adt/trigfuncs.c sepgsql/src/backend/utils/adt/trigfuncs.c *** base/src/backend/utils/adt/trigfuncs.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/utils/adt/trigfuncs.c Sat Jan 3 15:58:18 2009 *************** suppress_redundant_updates_trigger(PG_FU *** 62,78 **** newheader = newtuple->t_data; oldheader = oldtuple->t_data; - /* - * We are called before the OID, if any, has been transcribed from the - * old tuple to the new (in heap_update). To avoid a bogus compare - * failure, copy the OID now. But check that someone didn't already put - * another OID value into newtuple. (That's not actually possible at - * present, but maybe someday.) - */ - if (trigdata->tg_relation->rd_rel->relhasoids && - !OidIsValid(HeapTupleHeaderGetOid(newheader))) - HeapTupleHeaderSetOid(newheader, HeapTupleHeaderGetOid(oldheader)); - /* if the tuple payload is the same ... */ if (newtuple->t_len == oldtuple->t_len && newheader->t_hoff == oldheader->t_hoff && --- 62,67 ---- diff -Nrpc base/src/backend/utils/cache/catcache.c sepgsql/src/backend/utils/cache/catcache.c *** base/src/backend/utils/cache/catcache.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/utils/cache/catcache.c Fri Jan 16 17:05:27 2009 *************** ReleaseCatCache(HeapTuple tuple) *** 1316,1321 **** --- 1316,1353 ---- CatCacheRemoveCTup(ct->my_cache, ct); } + /* + * InsertCatCache + * + * This function enables to refer a tuple recently inserted, using catcache + * until next CommandCounterIncrement. + */ + void InsertCatCache(CatCache *cache, HeapTuple tuple) + { + ScanKeyData skey[4]; + uint32 hashValue; + Index hashIndex; + bool isnull; + int i; + + /* initialize the search key information */ + memcpy(skey, cache->cc_skey, sizeof(skey)); + for (i=0; i < cache->cc_nkeys; i++) + { + skey[i].sk_argument = heap_getattr(tuple, cache->cc_key[i], + cache->cc_tupdesc, &isnull); + Assert(!isnull); + } + + /* find the hash bucket in which to look for the tuple */ + if (cache->cc_tupdesc == NULL) + CatalogCacheInitializeCache(cache); + hashValue = CatalogCacheComputeHashValue(cache, cache->cc_nkeys, skey); + hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets); + + /* Insert a new tuple */ + CatalogCacheCreateEntry(cache, tuple, hashValue, hashIndex, false); + } /* * SearchCatCacheList diff -Nrpc base/src/backend/utils/cache/plancache.c sepgsql/src/backend/utils/cache/plancache.c *** base/src/backend/utils/cache/plancache.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/utils/cache/plancache.c Sat Jan 3 15:58:18 2009 *************** PlanCacheComputeResultDesc(List *stmt_li *** 847,858 **** if (IsA(node, Query)) { query = (Query *) node; ! return ExecCleanTypeFromTL(query->targetList, false); } if (IsA(node, PlannedStmt)) { pstmt = (PlannedStmt *) node; ! return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false); } /* other cases shouldn't happen, but return NULL */ break; --- 847,860 ---- if (IsA(node, Query)) { query = (Query *) node; ! return ExecCleanTypeFromTL(query->targetList, ! false, false, false); } if (IsA(node, PlannedStmt)) { pstmt = (PlannedStmt *) node; ! return ExecCleanTypeFromTL(pstmt->planTree->targetlist, ! false, false, false); } /* other cases shouldn't happen, but return NULL */ break; *************** PlanCacheComputeResultDesc(List *stmt_li *** 863,875 **** { query = (Query *) node; Assert(query->returningList); ! return ExecCleanTypeFromTL(query->returningList, false); } if (IsA(node, PlannedStmt)) { pstmt = (PlannedStmt *) node; Assert(pstmt->returningLists); ! return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false); } /* other cases shouldn't happen, but return NULL */ break; --- 865,879 ---- { query = (Query *) node; Assert(query->returningList); ! return ExecCleanTypeFromTL(query->returningList, ! false, false, false); } if (IsA(node, PlannedStmt)) { pstmt = (PlannedStmt *) node; Assert(pstmt->returningLists); ! return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), ! false, false, false); } /* other cases shouldn't happen, but return NULL */ break; diff -Nrpc base/src/backend/utils/cache/relcache.c sepgsql/src/backend/utils/cache/relcache.c *** base/src/backend/utils/cache/relcache.c Fri Jan 23 10:23:37 2009 --- sepgsql/src/backend/utils/cache/relcache.c Fri Jan 23 10:55:35 2009 *************** *** 55,60 **** --- 55,61 ---- #include "optimizer/prep.h" #include "optimizer/var.h" #include "rewrite/rewriteDefine.h" + #include "security/pgace.h" #include "storage/fd.h" #include "storage/lmgr.h" #include "storage/smgr.h" *************** AllocateRelationDesc(Relation relation, *** 329,335 **** /* initialize relation tuple form */ relation->rd_rel = relationForm; ! /* and allocate attribute tuple form storage */ relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts, relationForm->relhasoids); /* which we mark as a reference-counted tupdesc */ --- 330,342 ---- /* initialize relation tuple form */ relation->rd_rel = relationForm; ! /* ! * and allocate attribute tuple form storage ! * ! * Please note that relation->rd_att->tdhasrowacl and tdhasseclabel ! * have to be fixed up correctly at RelationBuildTupleDesc(), because ! * security module may need reloptions info to make its decision. ! */ relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts, relationForm->relhasoids); /* which we mark as a reference-counted tupdesc */ *************** RelationBuildDesc(Oid targetRelId, Relat *** 887,892 **** --- 894,905 ---- /* extract reloptions if any */ RelationParseRelOptions(relation, pg_class_tuple); + /* fixup relation->rd_att->tdhassecacl and tdhasseclabel */ + relation->rd_att->tdhasrowacl + = pgaceTupleDescHasRowAcl(relation, NIL); + relation->rd_att->tdhasseclabel + = pgaceTupleDescHasSecLabel(relation, NIL); + /* * initialize the relation lock manager information */ *************** formrdesc(const char *relationName, Oid *** 1474,1479 **** --- 1487,1500 ---- relation->rd_rel->relfilenode = RelationGetRelid(relation); /* + * Fixup relation->rd_att->tdhasrowacl and tdhasseclabel + */ + RelationGetDescr(relation)->tdhasrowacl + = pgaceTupleDescHasRowAcl(relation, NIL); + RelationGetDescr(relation)->tdhasseclabel + = pgaceTupleDescHasSecLabel(relation, NIL); + + /* * initialize the relation lock manager information */ RelationInitLockInfo(relation); /* see lmgr.c */ *************** BuildHardcodedDescriptor(int natts, Form *** 2708,2713 **** --- 2729,2741 ---- oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + /* + * NOTE: we assume the returned TupleDesc is only used for + * references to toast'ed data, and it is not delivered to + * heap_form_tuple(), so TupleDesc->tdhasrowacl and tdhasseclabel + * don't give us any effect. + * We omit to invoke pgaceTupleDescHasSecurity() here. + */ result = CreateTemplateTupleDesc(natts, hasoids); result->tdtypeid = RECORDOID; /* not right, but we don't care */ result->tdtypmod = -1; *************** load_relcache_init_file(void) *** 3465,3470 **** --- 3493,3506 ---- rel->rd_options = NULL; } + /* + * fixup rel->rd_att->tdhassecurity + */ + rel->rd_att->tdhasrowacl + = pgaceTupleDescHasRowAcl(rel, NIL); + rel->rd_att->tdhasseclabel + = pgaceTupleDescHasSecLabel(rel, NIL); + /* mark not-null status */ if (has_not_null) { diff -Nrpc base/src/backend/utils/cache/syscache.c sepgsql/src/backend/utils/cache/syscache.c *** base/src/backend/utils/cache/syscache.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/utils/cache/syscache.c Sat Jan 3 15:58:18 2009 *************** *** 41,46 **** --- 41,47 ---- #include "catalog/pg_opfamily.h" #include "catalog/pg_proc.h" #include "catalog/pg_rewrite.h" + #include "catalog/pg_security.h" #include "catalog/pg_statistic.h" #include "catalog/pg_ts_config.h" #include "catalog/pg_ts_config_map.h" *************** static const struct cachedesc cacheinfo[ *** 584,589 **** --- 585,614 ---- }, 1024 }, + {SecurityRelationId, /*SECURITYOID */ + SecurityOidIndexId, + 0, + 1, + { + ObjectIdAttributeNumber, + 0, + 0, + 0 + }, + 128 + }, + {SecurityRelationId, /* SECURITYLABEL */ + SecuritySeclabelIndexId, + 0, + 1, + { + Anum_pg_security_seclabel, + 0, + 0, + 0 + }, + 128 + }, {StatisticRelationId, /* STATRELATT */ StatisticRelidAttnumIndexId, Anum_pg_statistic_starelid, *************** ReleaseSysCache(HeapTuple tuple) *** 859,864 **** --- 884,904 ---- } /* + * InsertSysCache + * interts a tuple temporary until next CommandCounterIncrement + */ + void InsertSysCache(Oid relid, HeapTuple tuple) + { + int cacheId; + + for (cacheId = 0; cacheId < SysCacheSize; cacheId++) + { + if (SysCache[cacheId]->cc_reloid == relid) + InsertCatCache(SysCache[cacheId], tuple); + } + } + + /* * SearchSysCacheCopy * * A convenience routine that does SearchSysCache and (if successful) diff -Nrpc base/src/backend/utils/fmgr/dfmgr.c sepgsql/src/backend/utils/fmgr/dfmgr.c *** base/src/backend/utils/fmgr/dfmgr.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/utils/fmgr/dfmgr.c Sat Jan 3 15:58:18 2009 *************** *** 23,28 **** --- 23,29 ---- #endif #include "lib/stringinfo.h" #include "miscadmin.h" + #include "security/pgace.h" #include "utils/dynamic_loader.h" #include "utils/hsearch.h" *************** static void incompatible_module_error(co *** 76,82 **** const Pg_magic_struct *module_magic_data); static void internal_unload_library(const char *libname); static bool file_exists(const char *name); - static char *expand_dynamic_library_name(const char *name); static void check_restricted_library_name(const char *name); static char *substitute_libpath_macro(const char *name); static char *find_in_dynamic_libpath(const char *basename); --- 77,82 ---- *************** load_external_function(char *filename, c *** 109,114 **** --- 109,117 ---- /* Expand the possibly-abbreviated filename to an exact path name */ fullname = expand_dynamic_library_name(filename); + /* Check whether the shared library should be loaded, or not */ + pgaceLoadSharedModule(fullname); + /* Load the shared library, unless we already did */ lib_handle = internal_load_library(fullname); *************** load_file(const char *filename, bool res *** 149,154 **** --- 152,160 ---- /* Expand the possibly-abbreviated filename to an exact path name */ fullname = expand_dynamic_library_name(filename); + /* Check whether the library should be loaded, or not */ + pgaceLoadSharedModule(fullname); + /* Unload the library if currently loaded */ internal_unload_library(fullname); *************** file_exists(const char *name) *** 470,476 **** * * The result will always be freshly palloc'd. */ ! static char * expand_dynamic_library_name(const char *name) { bool have_slash; --- 476,482 ---- * * The result will always be freshly palloc'd. */ ! char * expand_dynamic_library_name(const char *name) { bool have_slash; diff -Nrpc base/src/backend/utils/init/postinit.c sepgsql/src/backend/utils/init/postinit.c *** base/src/backend/utils/init/postinit.c Sat Jan 3 13:01:35 2009 --- sepgsql/src/backend/utils/init/postinit.c Sat Jan 3 15:58:18 2009 *************** *** 32,37 **** --- 32,38 ---- #include "pgstat.h" #include "postmaster/autovacuum.h" #include "postmaster/postmaster.h" + #include "security/pgace.h" #include "storage/backendid.h" #include "storage/bufmgr.h" #include "storage/fd.h" *************** InitPostgres(const char *in_dbname, Oid *** 645,650 **** --- 646,654 ---- if (!bootstrap) pgstat_bestart(); + /* initialize mandatory access control facilities */ + pgaceInitialize(bootstrap); + /* close the transaction we started above */ if (!bootstrap) CommitTransactionCommand(); diff -Nrpc base/src/backend/utils/misc/guc.c sepgsql/src/backend/utils/misc/guc.c *** base/src/backend/utils/misc/guc.c Thu Jan 22 14:34:54 2009 --- sepgsql/src/backend/utils/misc/guc.c Thu Jan 22 14:41:23 2009 *************** *** 56,61 **** --- 56,62 ---- #include "postmaster/syslogger.h" #include "postmaster/walwriter.h" #include "regex/regex.h" + #include "security/pgace.h" #include "storage/bufmgr.h" #include "storage/fd.h" #include "tcop/tcopprot.h" *************** static const struct config_enum_entry xm *** 296,301 **** --- 297,320 ---- {NULL, 0, false} }; + static const struct config_enum_entry pgace_feature_options[] = { + {"none", PGACE_FEATURE_NONE, false}, + #ifdef HAVE_SELINUX + {"selinux", PGACE_FEATURE_SELINUX, false}, + #endif + {NULL, 0, false} + }; + + #ifdef HAVE_SELINUX + static const struct config_enum_entry sepgsqloption_options[] = { + {"default", SEPGSQL_MODE_DEFAULT, false}, + {"enforcing", SEPGSQL_MODE_ENFORCING, false}, + {"permissive", SEPGSQL_MODE_PERMISSIVE, false}, + {"disabled", SEPGSQL_MODE_DISABLED, false}, + {NULL, 0, false} + }; + #endif + /* * Although only "on", "off", and "safe_encoding" are documented, we * accept all the likely variants of "on" and "off". *************** static struct config_bool ConfigureNames *** 1220,1225 **** --- 1239,1255 ---- &IgnoreSystemIndexes, false, NULL, NULL }, + #ifdef HAVE_SELINUX + { + {"sepostgresql_row_level", PGC_POSTMASTER, UNGROUPED, + gettext_noop("Availability of row-level security in SE-PostgreSQL."), + NULL, + GUC_NOT_IN_SAMPLE, + }, + &sepostgresql_row_level, + true, NULL, NULL + }, + #endif /* End-of-list marker */ { *************** static struct config_enum ConfigureNames *** 2671,2678 **** &xmloption, XMLOPTION_CONTENT, xmloption_options, NULL, NULL }, ! ! /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL --- 2701,2725 ---- &xmloption, XMLOPTION_CONTENT, xmloption_options, NULL, NULL }, ! { ! {"pgace_feature", PGC_POSTMASTER, UNGROUPED, ! gettext_noop("A option to choose an enhanced security feature which is " ! "a guest of PGACE security framework"), ! NULL ! }, ! &pgace_feature, ! PGACE_FEATURE_NONE, pgace_feature_options, NULL, NULL ! }, ! #ifdef HAVE_SELINUX ! { ! {"sepostgresql", PGC_POSTMASTER, UNGROUPED, ! gettext_noop("SE-PostgreSQL mode (default|permissive|enforcing|disabled)"), ! NULL ! }, ! &sepostgresql_mode, ! SEPGSQL_MODE_DEFAULT, sepgsqloption_options, NULL, NULL ! }, ! #endif /* End-of-list marker */ { {NULL, 0, 0, NULL, NULL}, NULL, 0, NULL, NULL, NULL *************** ResetAllOptions(void) *** 3551,3556 **** --- 3598,3605 ---- { int i; + pgaceSetDatabaseParam("all", NULL); + for (i = 0; i < num_guc_variables; i++) { struct config_generic *gconf = guc_variables[i]; *************** ExecSetVariableStmt(VariableSetStmt *stm *** 5476,5481 **** --- 5525,5531 ---- { case VAR_SET_VALUE: case VAR_SET_CURRENT: + pgaceSetDatabaseParam(stmt->name, ExtractSetVariableArgs(stmt)); set_config_option(stmt->name, ExtractSetVariableArgs(stmt), (superuser() ? PGC_SUSET : PGC_USERSET), *************** ExecSetVariableStmt(VariableSetStmt *stm *** 5533,5538 **** --- 5583,5589 ---- break; case VAR_SET_DEFAULT: case VAR_RESET: + pgaceSetDatabaseParam(stmt->name, NULL); set_config_option(stmt->name, NULL, (superuser() ? PGC_SUSET : PGC_USERSET), *************** EmitWarningsOnPlaceholders(const char *c *** 5956,5961 **** --- 6007,6015 ---- void GetPGVariable(const char *name, DestReceiver *dest) { + /* Check get param permissions */ + pgaceGetDatabaseParam(name); + if (guc_name_compare(name, "all") == 0) ShowAllGUCConfig(dest); else diff -Nrpc base/src/backend/utils/misc/postgresql.conf.sample sepgsql/src/backend/utils/misc/postgresql.conf.sample *** base/src/backend/utils/misc/postgresql.conf.sample Thu Jan 22 14:34:54 2009 --- sepgsql/src/backend/utils/misc/postgresql.conf.sample Thu Jan 22 14:41:23 2009 *************** *** 485,490 **** --- 485,496 ---- #------------------------------------------------------------------------------ + # ENHANCED SECURITY OPTIONS + #------------------------------------------------------------------------------ + + #pgace_feature = 'none' + + #------------------------------------------------------------------------------ # CUSTOMIZED OPTIONS #------------------------------------------------------------------------------ diff -Nrpc base/src/include/access/htup.h sepgsql/src/include/access/htup.h *** base/src/include/access/htup.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/access/htup.h Sat Jan 3 15:58:18 2009 *************** typedef HeapTupleHeaderData *HeapTupleHe *** 187,193 **** * information stored in t_infomask2: */ #define HEAP_NATTS_MASK 0x07FF /* 11 bits for number of attributes */ ! /* bits 0x3800 are available */ #define HEAP_HOT_UPDATED 0x4000 /* tuple was HOT-updated */ #define HEAP_ONLY_TUPLE 0x8000 /* this is heap-only tuple */ --- 187,195 ---- * information stored in t_infomask2: */ #define HEAP_NATTS_MASK 0x07FF /* 11 bits for number of attributes */ ! /* bits 0x0800 are available */ ! #define HEAP_HAS_ROWACL 0x1000 /* tuple has Row-level ACLs */ ! #define HEAP_HAS_SECLABEL 0x2000 /* tuple has Security Label */ #define HEAP_HOT_UPDATED 0x4000 /* tuple was HOT-updated */ #define HEAP_ONLY_TUPLE 0x8000 /* this is heap-only tuple */ *************** do { \ *** 290,295 **** --- 292,300 ---- (tup)->t_choice.t_datum.datum_typmod = (typmod) \ ) + #define HeapTupleHeaderHasOid(tup) \ + ((tup)->t_infomask & HEAP_HASOID) + #define HeapTupleHeaderGetOid(tup) \ ( \ ((tup)->t_infomask & HEAP_HASOID) ? \ *************** do { \ *** 349,354 **** --- 354,400 ---- (tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \ ) + #define HeapTupleHeaderHasRowAcl(tup) \ + ((tup)->t_infomask2 & HEAP_HAS_ROWACL) + + #define HeapTupleHeaderHasSecLabel(tup) \ + ((tup)->t_infomask2 & HEAP_HAS_SECLABEL) + + #define HeapTupleHeaderGetRowAcl(tup) \ + ( \ + HeapTupleHeaderHasRowAcl(tup) \ + ? (*((Oid *)((char *)(tup) + (tup)->t_hoff \ + - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ + - (HeapTupleHeaderHasSecLabel(tup) ? sizeof(Oid) : 0) \ + - sizeof(Oid)))) \ + : InvalidOid \ + ) + + #define HeapTupleHeaderGetSecLabel(tup) \ + ( \ + HeapTupleHeaderHasSecLabel(tup) \ + ? (*(Oid *)((char *)(tup) + (tup)->t_hoff \ + - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ + - sizeof(Oid))) \ + : InvalidOid \ + ) + + #define HeapTupleHeaderSetRowAcl(tup, rowacl) \ + do { \ + Assert(HeapTupleHeaderHasRowAcl(tup)); \ + *((Oid *)((char *)(tup) + (tup)->t_hoff \ + - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ + - (HeapTupleHeaderHasSecLabel(tup) ? sizeof(Oid) : 0) \ + - sizeof(Oid))) = (rowacl); \ + } while (0) + + #define HeapTupleHeaderSetSecLabel(tup, seclabel) \ + do { \ + Assert(HeapTupleHeaderHasSecLabel(tup)); \ + *((Oid *)((char *)(tup) + (tup)->t_hoff \ + - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ + - sizeof(Oid))) = (seclabel); \ + } while(0) /* * BITMAPLEN(NATTS) - *************** typedef HeapTupleData *HeapTuple; *** 543,554 **** --- 589,620 ---- #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 HeapTupleHasRowAcl(tuple) \ + HeapTupleHeaderHasRowAcl((tuple)->t_data) + + #define HeapTupleHasSecLabel(tuple) \ + HeapTupleHeaderHasSecLabel((tuple)->t_data) + + #define HeapTupleGetRowAcl(tuple) \ + HeapTupleHeaderGetRowAcl((tuple)->t_data) + + #define HeapTupleGetSecLabel(tuple) \ + HeapTupleHeaderGetSecLabel((tuple)->t_data) + + #define HeapTupleSetRowAcl(tuple, rowacl) \ + HeapTupleHeaderSetRowAcl((tuple)->t_data, (rowacl)) + + #define HeapTupleSetSecLabel(tuple, seclabel) \ + HeapTupleHeaderSetSecLabel((tuple)->t_data, (seclabel)) /* * WAL record definitions for heapam.c's WAL operations diff -Nrpc base/src/include/access/sysattr.h sepgsql/src/include/access/sysattr.h *** base/src/include/access/sysattr.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/access/sysattr.h Sat Jan 3 15:58:18 2009 *************** *** 25,31 **** #define MaxTransactionIdAttributeNumber (-5) #define MaxCommandIdAttributeNumber (-6) #define TableOidAttributeNumber (-7) ! #define FirstLowInvalidHeapAttributeNumber (-8) #endif /* SYSATTR_H */ --- 25,38 ---- #define MaxTransactionIdAttributeNumber (-5) #define MaxCommandIdAttributeNumber (-6) #define TableOidAttributeNumber (-7) ! #define SecurityAclAttributeNumber (-8) ! #define SecurityLabelAttributeNumber (-9) ! #define FirstLowInvalidHeapAttributeNumber (-10) + /* + * Attribute names for the system-defined attributes + */ + #define SecurityAclAttributeName "security_acl" + #define SecurityLabelAttributeName "security_label" #endif /* SYSATTR_H */ diff -Nrpc base/src/include/access/tupdesc.h sepgsql/src/include/access/tupdesc.h *** base/src/include/access/tupdesc.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/access/tupdesc.h Sat Jan 3 15:58:18 2009 *************** typedef struct tupleDesc *** 75,80 **** --- 75,82 ---- Oid tdtypeid; /* composite type ID for tuple type */ int32 tdtypmod; /* typmod for tuple type */ bool tdhasoid; /* tuple has oid attribute in its header */ + bool tdhasrowacl; /* tuple has Row-level ACLs in its header */ + bool tdhasseclabel; /* tuple has security label in its header */ int tdrefcount; /* reference count, or -1 if not counting */ } *TupleDesc; diff -Nrpc base/src/include/catalog/heap.h sepgsql/src/include/catalog/heap.h *** base/src/include/catalog/heap.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/catalog/heap.h Sat Jan 3 15:58:18 2009 *************** extern Oid heap_create_with_catalog(cons *** 56,62 **** int oidinhcount, OnCommitAction oncommit, Datum reloptions, ! bool allow_system_table_mods); extern void heap_drop_with_catalog(Oid relid); --- 56,63 ---- int oidinhcount, OnCommitAction oncommit, Datum reloptions, ! bool allow_system_table_mods, ! List *pgaceAttrList); extern void heap_drop_with_catalog(Oid relid); *************** extern List *heap_truncate_find_FKs(List *** 68,79 **** extern void InsertPgAttributeTuple(Relation pg_attribute_rel, Form_pg_attribute new_attribute, ! CatalogIndexState indstate); extern void InsertPgClassTuple(Relation pg_class_desc, Relation new_rel_desc, Oid new_rel_oid, ! Datum reloptions); extern List *AddRelationNewConstraints(Relation rel, List *newColDefaults, --- 69,82 ---- extern void InsertPgAttributeTuple(Relation pg_attribute_rel, Form_pg_attribute new_attribute, ! CatalogIndexState indstate, ! List *pgaceAttrList); extern void InsertPgClassTuple(Relation pg_class_desc, Relation new_rel_desc, Oid new_rel_oid, ! Datum reloptions, ! List *pgaceAttrList); extern List *AddRelationNewConstraints(Relation rel, List *newColDefaults, *************** extern Form_pg_attribute SystemAttribute *** 103,108 **** --- 106,113 ---- extern Form_pg_attribute SystemAttributeByName(const char *attname, bool relhasoids); + extern bool SystemAttributeIsWritable(AttrNumber attnum); + extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind); extern void CheckAttributeType(const char *attname, Oid atttypid); diff -Nrpc base/src/include/catalog/indexing.h sepgsql/src/include/catalog/indexing.h *** base/src/include/catalog/indexing.h Fri Jan 23 10:23:37 2009 --- sepgsql/src/include/catalog/indexing.h Fri Jan 23 10:55:35 2009 *************** DECLARE_UNIQUE_INDEX(pg_type_oid_index, *** 252,257 **** --- 252,262 ---- DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typname name_ops, typnamespace oid_ops)); #define TypeNameNspIndexId 2704 + DECLARE_UNIQUE_INDEX(pg_security_oid_index, 3401, on pg_security using btree(oid oid_ops)); + #define SecurityOidIndexId 3401 + DECLARE_UNIQUE_INDEX(pg_security_seclabel_index, 3402, on pg_security using btree(seclabel text_ops)); + #define SecuritySeclabelIndexId 3402 + DECLARE_UNIQUE_INDEX(pg_foreign_data_wrapper_oid_index, 112, on pg_foreign_data_wrapper using btree(oid oid_ops)); #define ForeignDataWrapperOidIndexId 112 diff -Nrpc base/src/include/catalog/pg_attribute.h sepgsql/src/include/catalog/pg_attribute.h *** base/src/include/catalog/pg_attribute.h Fri Jan 23 10:23:37 2009 --- sepgsql/src/include/catalog/pg_attribute.h Fri Jan 23 11:58:46 2009 *************** CATALOG(pg_attribute,1249) BKI_BOOTSTRAP *** 130,135 **** --- 130,141 ---- */ char attalign; + /* + * attkind is a copy of the relkind field from pg_class for this + * attribute. See pg_class.h for more details. + */ + char attkind; + /* This flag represents the "NOT NULL" constraint */ bool attnotnull; *************** typedef FormData_pg_attribute *Form_pg_a *** 176,182 **** * ---------------- */ ! #define Natts_pg_attribute 18 #define Anum_pg_attribute_attrelid 1 #define Anum_pg_attribute_attname 2 #define Anum_pg_attribute_atttypid 3 --- 182,188 ---- * ---------------- */ ! #define Natts_pg_attribute 19 #define Anum_pg_attribute_attrelid 1 #define Anum_pg_attribute_attname 2 #define Anum_pg_attribute_atttypid 3 *************** typedef FormData_pg_attribute *Form_pg_a *** 189,200 **** #define Anum_pg_attribute_attbyval 10 #define Anum_pg_attribute_attstorage 11 #define Anum_pg_attribute_attalign 12 ! #define Anum_pg_attribute_attnotnull 13 ! #define Anum_pg_attribute_atthasdef 14 ! #define Anum_pg_attribute_attisdropped 15 ! #define Anum_pg_attribute_attislocal 16 ! #define Anum_pg_attribute_attinhcount 17 ! #define Anum_pg_attribute_attacl 18 /* ---------------- --- 195,207 ---- #define Anum_pg_attribute_attbyval 10 #define Anum_pg_attribute_attstorage 11 #define Anum_pg_attribute_attalign 12 ! #define Anum_pg_attribute_attkind 13 ! #define Anum_pg_attribute_attnotnull 14 ! #define Anum_pg_attribute_atthasdef 15 ! #define Anum_pg_attribute_attisdropped 16 ! #define Anum_pg_attribute_attislocal 17 ! #define Anum_pg_attribute_attinhcount 18 ! #define Anum_pg_attribute_attacl 19 /* ---------------- *************** typedef FormData_pg_attribute *Form_pg_a *** 212,457 **** * ---------------- */ #define Schema_pg_type \ ! { 1247, {"typname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typnamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typowner"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typlen"}, 21, -1, 2, 4, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typbyval"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typtype"}, 18, -1, 1, 6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typcategory"}, 18, -1, 1, 7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typispreferred"},16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typisdefined"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typdelim"}, 18, -1, 1, 10, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typrelid"}, 26, -1, 4, 11, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typelem"}, 26, -1, 4, 12, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typarray"}, 26, -1, 4, 13, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typinput"}, 24, -1, 4, 14, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typoutput"}, 24, -1, 4, 15, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typreceive"}, 24, -1, 4, 16, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typsend"}, 24, -1, 4, 17, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typmodin"}, 24, -1, 4, 18, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typmodout"}, 24, -1, 4, 19, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typanalyze"}, 24, -1, 4, 20, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typalign"}, 18, -1, 1, 21, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typstorage"}, 18, -1, 1, 22, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typnotnull"}, 16, -1, 1, 23, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typbasetype"}, 26, -1, 4, 24, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typtypmod"}, 23, -1, 4, 25, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typndims"}, 23, -1, 4, 26, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typdefaultbin"}, 25, -1, -1, 27, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \ ! { 1247, {"typdefault"}, 25, -1, -1, 28, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } } ! ! DATA(insert ( 1247 typname 19 -1 NAMEDATALEN 1 0 -1 -1 f p c t f f t 0 _null_)); ! DATA(insert ( 1247 typnamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 typowner 26 -1 4 3 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 typlen 21 -1 2 4 0 -1 -1 t p s t f f t 0 _null_)); ! DATA(insert ( 1247 typbyval 16 -1 1 5 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1247 typtype 18 -1 1 6 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1247 typcategory 18 -1 1 7 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1247 typispreferred 16 -1 1 8 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1247 typisdefined 16 -1 1 9 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1247 typdelim 18 -1 1 10 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1247 typrelid 26 -1 4 11 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 typelem 26 -1 4 12 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 typarray 26 -1 4 13 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 typinput 24 -1 4 14 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 typoutput 24 -1 4 15 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 typreceive 24 -1 4 16 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 typsend 24 -1 4 17 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 typmodin 24 -1 4 18 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 typmodout 24 -1 4 19 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 typanalyze 24 -1 4 20 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 typalign 18 -1 1 21 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1247 typstorage 18 -1 1 22 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1247 typnotnull 16 -1 1 23 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1247 typbasetype 26 -1 4 24 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 typtypmod 23 -1 4 25 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 typndims 23 -1 4 26 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 typdefaultbin 25 -1 -1 27 0 -1 -1 f x i f f f t 0 _null_)); ! DATA(insert ( 1247 typdefault 25 -1 -1 28 0 -1 -1 f x i f f f t 0 _null_)); ! DATA(insert ( 1247 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0 _null_)); ! DATA(insert ( 1247 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1247 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); /* ---------------- * pg_proc * ---------------- */ #define Schema_pg_proc \ ! { 1255, {"proname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"pronamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"proowner"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"prolang"}, 26, -1, 4, 4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"procost"}, 700, -1, 4, 5, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"prorows"}, 700, -1, 4, 6, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"provariadic"}, 26, -1, 4, 7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"proisagg"}, 16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"proiswindow"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"prosecdef"}, 16, -1, 1, 10, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"proisstrict"}, 16, -1, 1, 11, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"proretset"}, 16, -1, 1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"provolatile"}, 18, -1, 1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"pronargs"}, 21, -1, 2, 14, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"pronargdefaults"}, 21, -1, 2, 15, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"prorettype"}, 26, -1, 4, 16, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"proargtypes"}, 30, -1, -1, 17, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"proallargtypes"}, 1028, -1, -1, 18, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \ ! { 1255, {"proargmodes"}, 1002, -1, -1, 19, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \ ! { 1255, {"proargnames"}, 1009, -1, -1, 20, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \ ! { 1255, {"proargdefaults"}, 25, -1, -1, 21, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \ ! { 1255, {"prosrc"}, 25, -1, -1, 22, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \ ! { 1255, {"probin"}, 17, -1, -1, 23, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \ ! { 1255, {"proconfig"}, 1009, -1, -1, 24, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \ ! { 1255, {"proacl"}, 1034, -1, -1, 25, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } } ! ! DATA(insert ( 1255 proname 19 -1 NAMEDATALEN 1 0 -1 -1 f p c t f f t 0 _null_)); ! DATA(insert ( 1255 pronamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1255 proowner 26 -1 4 3 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1255 prolang 26 -1 4 4 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1255 procost 700 -1 4 5 0 -1 -1 FLOAT4PASSBYVAL p i t f f t 0 _null_)); ! DATA(insert ( 1255 prorows 700 -1 4 6 0 -1 -1 FLOAT4PASSBYVAL p i t f f t 0 _null_)); ! DATA(insert ( 1255 provariadic 26 -1 4 7 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1255 proisagg 16 -1 1 8 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1255 proiswindow 16 -1 1 9 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1255 prosecdef 16 -1 1 10 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1255 proisstrict 16 -1 1 11 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1255 proretset 16 -1 1 12 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1255 provolatile 18 -1 1 13 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1255 pronargs 21 -1 2 14 0 -1 -1 t p s t f f t 0 _null_)); ! DATA(insert ( 1255 pronargdefaults 21 -1 2 15 0 -1 -1 t p s t f f t 0 _null_)); ! DATA(insert ( 1255 prorettype 26 -1 4 16 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1255 proargtypes 30 -1 -1 17 1 -1 -1 f p i t f f t 0 _null_)); ! DATA(insert ( 1255 proallargtypes 1028 -1 -1 18 1 -1 -1 f x i f f f t 0 _null_)); ! DATA(insert ( 1255 proargmodes 1002 -1 -1 19 1 -1 -1 f x i f f f t 0 _null_)); ! DATA(insert ( 1255 proargnames 1009 -1 -1 20 1 -1 -1 f x i f f f t 0 _null_)); ! DATA(insert ( 1255 proargdefaults 25 -1 -1 21 0 -1 -1 f x i f f f t 0 _null_)); ! DATA(insert ( 1255 prosrc 25 -1 -1 22 0 -1 -1 f x i f f f t 0 _null_)); ! DATA(insert ( 1255 probin 17 -1 -1 23 0 -1 -1 f x i f f f t 0 _null_)); ! DATA(insert ( 1255 proconfig 1009 -1 -1 24 1 -1 -1 f x i f f f t 0 _null_)); ! DATA(insert ( 1255 proacl 1034 -1 -1 25 1 -1 -1 f x i f f f t 0 _null_)); ! DATA(insert ( 1255 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0 _null_)); ! DATA(insert ( 1255 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1255 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1255 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1255 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1255 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1255 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); /* ---------------- * pg_attribute * ---------------- */ #define Schema_pg_attribute \ ! { 1249, {"attrelid"}, 26, -1, 4, 1, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attname"}, 19, -1, NAMEDATALEN, 2, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"atttypid"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attstattarget"}, 23, -1, 4, 4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attlen"}, 21, -1, 2, 5, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attnum"}, 21, -1, 2, 6, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attndims"}, 23, -1, 4, 7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attcacheoff"}, 23, -1, 4, 8, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"atttypmod"}, 23, -1, 4, 9, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attbyval"}, 16, -1, 1, 10, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attstorage"}, 18, -1, 1, 11, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attalign"}, 18, -1, 1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attnotnull"}, 16, -1, 1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"atthasdef"}, 16, -1, 1, 14, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attisdropped"}, 16, -1, 1, 15, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attislocal"}, 16, -1, 1, 16, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attinhcount"}, 23, -1, 4, 17, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attacl"}, 1034, -1, -1, 18, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } } ! ! DATA(insert ( 1249 attrelid 26 -1 4 1 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1249 attname 19 -1 NAMEDATALEN 2 0 -1 -1 f p c t f f t 0 _null_)); ! DATA(insert ( 1249 atttypid 26 -1 4 3 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1249 attstattarget 23 -1 4 4 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1249 attlen 21 -1 2 5 0 -1 -1 t p s t f f t 0 _null_)); ! DATA(insert ( 1249 attnum 21 -1 2 6 0 -1 -1 t p s t f f t 0 _null_)); ! DATA(insert ( 1249 attndims 23 -1 4 7 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1249 attcacheoff 23 -1 4 8 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1249 atttypmod 23 -1 4 9 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1249 attbyval 16 -1 1 10 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1249 attstorage 18 -1 1 11 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1249 attalign 18 -1 1 12 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1249 attnotnull 16 -1 1 13 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1249 atthasdef 16 -1 1 14 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1249 attisdropped 16 -1 1 15 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1249 attislocal 16 -1 1 16 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1249 attinhcount 23 -1 4 17 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1249 attacl 1034 -1 -1 18 1 -1 -1 f x i f f f t 0 _null_)); ! DATA(insert ( 1249 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0 _null_)); /* no OIDs in pg_attribute */ ! DATA(insert ( 1249 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1249 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1249 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1249 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); /* ---------------- * pg_class * ---------------- */ #define Schema_pg_class \ ! { 1259, {"relname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relnamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"reltype"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relowner"}, 26, -1, 4, 4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relam"}, 26, -1, 4, 5, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relfilenode"}, 26, -1, 4, 6, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"reltablespace"}, 26, -1, 4, 7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relpages"}, 23, -1, 4, 8, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"reltuples"}, 700, -1, 4, 9, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"reltoastrelid"}, 26, -1, 4, 10, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"reltoastidxid"}, 26, -1, 4, 11, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relhasindex"}, 16, -1, 1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relisshared"}, 16, -1, 1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relkind"}, 18, -1, 1, 14, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relnatts"}, 21, -1, 2, 15, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relchecks"}, 21, -1, 2, 16, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relhasoids"}, 16, -1, 1, 17, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relhaspkey"}, 16, -1, 1, 18, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relhasrules"}, 16, -1, 1, 19, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relhastriggers"},16, -1, 1, 20, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relhassubclass"},16, -1, 1, 21, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relfrozenxid"}, 28, -1, 4, 22, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relacl"}, 1034, -1, -1, 23, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \ ! { 1259, {"reloptions"}, 1009, -1, -1, 24, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } } ! ! DATA(insert ( 1259 relname 19 -1 NAMEDATALEN 1 0 -1 -1 f p c t f f t 0 _null_)); ! DATA(insert ( 1259 relnamespace 26 -1 4 2 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1259 reltype 26 -1 4 3 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1259 relowner 26 -1 4 4 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1259 relam 26 -1 4 5 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1259 relfilenode 26 -1 4 6 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1259 reltablespace 26 -1 4 7 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1259 relpages 23 -1 4 8 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1259 reltuples 700 -1 4 9 0 -1 -1 FLOAT4PASSBYVAL p i t f f t 0 _null_)); ! DATA(insert ( 1259 reltoastrelid 26 -1 4 10 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1259 reltoastidxid 26 -1 4 11 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1259 relhasindex 16 -1 1 12 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1259 relisshared 16 -1 1 13 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1259 relkind 18 -1 1 14 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1259 relnatts 21 -1 2 15 0 -1 -1 t p s t f f t 0 _null_)); ! DATA(insert ( 1259 relchecks 21 -1 2 16 0 -1 -1 t p s t f f t 0 _null_)); ! DATA(insert ( 1259 relhasoids 16 -1 1 17 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1259 relhaspkey 16 -1 1 18 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1259 relhasrules 16 -1 1 19 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1259 relhastriggers 16 -1 1 20 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1259 relhassubclass 16 -1 1 21 0 -1 -1 t p c t f f t 0 _null_)); ! DATA(insert ( 1259 relfrozenxid 28 -1 4 22 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1259 relacl 1034 -1 -1 23 1 -1 -1 f x i f f f t 0 _null_)); ! DATA(insert ( 1259 reloptions 1009 -1 -1 24 1 -1 -1 f x i f f f t 0 _null_)); ! DATA(insert ( 1259 ctid 27 0 6 -1 0 -1 -1 f p s t f f t 0 _null_)); ! DATA(insert ( 1259 oid 26 0 4 -2 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1259 xmin 28 0 4 -3 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1259 cmin 29 0 4 -4 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1259 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1259 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); ! DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); /* ---------------- * pg_index --- 219,477 ---- * ---------------- */ #define Schema_pg_type \ ! { 1247, {"typname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typnamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typowner"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typlen"}, 21, -1, 2, 4, 0, -1, -1, true, 'p', 's', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typbyval"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typtype"}, 18, -1, 1, 6, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typcategory"}, 18, -1, 1, 7, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typispreferred"},16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typisdefined"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typdelim"}, 18, -1, 1, 10, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typrelid"}, 26, -1, 4, 11, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typelem"}, 26, -1, 4, 12, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typarray"}, 26, -1, 4, 13, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typinput"}, 24, -1, 4, 14, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typoutput"}, 24, -1, 4, 15, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typreceive"}, 24, -1, 4, 16, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typsend"}, 24, -1, 4, 17, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typmodin"}, 24, -1, 4, 18, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typmodout"}, 24, -1, 4, 19, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typanalyze"}, 24, -1, 4, 20, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typalign"}, 18, -1, 1, 21, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typstorage"}, 18, -1, 1, 22, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typnotnull"}, 16, -1, 1, 23, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typbasetype"}, 26, -1, 4, 24, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typtypmod"}, 23, -1, 4, 25, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typndims"}, 23, -1, 4, 26, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1247, {"typdefaultbin"}, 25, -1, -1, 27, 0, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } }, \ ! { 1247, {"typdefault"}, 25, -1, -1, 28, 0, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } } ! ! DATA(insert ( 1247 typname 19 -1 NAMEDATALEN 1 0 -1 -1 f p c r t f f t 0 _null_)); ! DATA(insert ( 1247 typnamespace 26 -1 4 2 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 typowner 26 -1 4 3 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 typlen 21 -1 2 4 0 -1 -1 t p s r t f f t 0 _null_)); ! DATA(insert ( 1247 typbyval 16 -1 1 5 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1247 typtype 18 -1 1 6 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1247 typcategory 18 -1 1 7 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1247 typispreferred 16 -1 1 8 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1247 typisdefined 16 -1 1 9 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1247 typdelim 18 -1 1 10 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1247 typrelid 26 -1 4 11 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 typelem 26 -1 4 12 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 typarray 26 -1 4 13 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 typinput 24 -1 4 14 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 typoutput 24 -1 4 15 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 typreceive 24 -1 4 16 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 typsend 24 -1 4 17 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 typmodin 24 -1 4 18 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 typmodout 24 -1 4 19 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 typanalyze 24 -1 4 20 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 typalign 18 -1 1 21 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1247 typstorage 18 -1 1 22 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1247 typnotnull 16 -1 1 23 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1247 typbasetype 26 -1 4 24 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 typtypmod 23 -1 4 25 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 typndims 23 -1 4 26 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 typdefaultbin 25 -1 -1 27 0 -1 -1 f x i r f f f t 0 _null_)); ! DATA(insert ( 1247 typdefault 25 -1 -1 28 0 -1 -1 f x i r f f f t 0 _null_)); ! DATA(insert ( 1247 ctid 27 0 6 -1 0 -1 -1 f p s r t f f t 0 _null_)); ! DATA(insert ( 1247 oid 26 0 4 -2 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 xmin 28 0 4 -3 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 cmin 29 0 4 -4 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 xmax 28 0 4 -5 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 cmax 29 0 4 -6 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 tableoid 26 0 4 -7 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1247 security_acl 1034 0 -1 -8 1 -1 -1 f x i r t f f t 0 _null_)); ! DATA(insert ( 1247 security_label 25 0 -1 -9 0 -1 -1 f x i r t f f t 0 _null_)); /* ---------------- * pg_proc * ---------------- */ #define Schema_pg_proc \ ! { 1255, {"proname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"pronamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"proowner"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"prolang"}, 26, -1, 4, 4, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"procost"}, 700, -1, 4, 5, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"prorows"}, 700, -1, 4, 6, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"provariadic"}, 26, -1, 4, 7, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"proisagg"}, 16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"proiswindow"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"prosecdef"}, 16, -1, 1, 10, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"proisstrict"}, 16, -1, 1, 11, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"proretset"}, 16, -1, 1, 12, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"provolatile"}, 18, -1, 1, 13, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"pronargs"}, 21, -1, 2, 14, 0, -1, -1, true, 'p', 's', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"pronargdefaults"}, 21, -1, 2, 15, 0, -1, -1, true, 'p', 's', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"prorettype"}, 26, -1, 4, 16, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"proargtypes"}, 30, -1, -1, 17, 1, -1, -1, false, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1255, {"proallargtypes"}, 1028, -1, -1, 18, 1, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } }, \ ! { 1255, {"proargmodes"}, 1002, -1, -1, 19, 1, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } }, \ ! { 1255, {"proargnames"}, 1009, -1, -1, 20, 1, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } }, \ ! { 1255, {"proargdefaults"}, 25, -1, -1, 21, 0, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } }, \ ! { 1255, {"prosrc"}, 25, -1, -1, 22, 0, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } }, \ ! { 1255, {"probin"}, 17, -1, -1, 23, 0, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } }, \ ! { 1255, {"proconfig"}, 1009, -1, -1, 24, 1, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } }, \ ! { 1255, {"proacl"}, 1034, -1, -1, 25, 1, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } } ! ! DATA(insert ( 1255 proname 19 -1 NAMEDATALEN 1 0 -1 -1 f p c r t f f t 0 _null_)); ! DATA(insert ( 1255 pronamespace 26 -1 4 2 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1255 proowner 26 -1 4 3 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1255 prolang 26 -1 4 4 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1255 procost 700 -1 4 5 0 -1 -1 FLOAT4PASSBYVAL p i r t f f t 0 _null_)); ! DATA(insert ( 1255 prorows 700 -1 4 6 0 -1 -1 FLOAT4PASSBYVAL p i r t f f t 0 _null_)); ! DATA(insert ( 1255 provariadic 26 -1 4 7 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1255 proisagg 16 -1 1 8 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1255 proiswindow 16 -1 1 9 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1255 prosecdef 16 -1 1 10 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1255 proisstrict 16 -1 1 11 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1255 proretset 16 -1 1 12 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1255 provolatile 18 -1 1 13 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1255 pronargs 21 -1 2 14 0 -1 -1 t p s r t f f t 0 _null_)); ! DATA(insert ( 1255 pronargdefaults 21 -1 2 15 0 -1 -1 t p s r t f f t 0 _null_)); ! DATA(insert ( 1255 prorettype 26 -1 4 16 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1255 proargtypes 30 -1 -1 17 1 -1 -1 f p i r t f f t 0 _null_)); ! DATA(insert ( 1255 proallargtypes 1028 -1 -1 18 1 -1 -1 f x i r f f f t 0 _null_)); ! DATA(insert ( 1255 proargmodes 1002 -1 -1 19 1 -1 -1 f x i r f f f t 0 _null_)); ! DATA(insert ( 1255 proargnames 1009 -1 -1 20 1 -1 -1 f x i r f f f t 0 _null_)); ! DATA(insert ( 1255 proargdefaults 25 -1 -1 21 0 -1 -1 f x i r f f f t 0 _null_)); ! DATA(insert ( 1255 prosrc 25 -1 -1 22 0 -1 -1 f x i r f f f t 0 _null_)); ! DATA(insert ( 1255 probin 17 -1 -1 23 0 -1 -1 f x i r f f f t 0 _null_)); ! DATA(insert ( 1255 proconfig 1009 -1 -1 24 1 -1 -1 f x i r f f f t 0 _null_)); ! DATA(insert ( 1255 proacl 1034 -1 -1 25 1 -1 -1 f x i r f f f t 0 _null_)); ! DATA(insert ( 1255 ctid 27 0 6 -1 0 -1 -1 f p s r t f f t 0 _null_)); ! DATA(insert ( 1255 oid 26 0 4 -2 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1255 xmin 28 0 4 -3 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1255 cmin 29 0 4 -4 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1255 xmax 28 0 4 -5 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1255 cmax 29 0 4 -6 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1255 tableoid 26 0 4 -7 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1255 security_acl 1034 0 -1 -8 1 -1 -1 f x i r t f f t 0 _null_)); ! DATA(insert ( 1255 security_label 25 0 -1 -9 0 -1 -1 f x i r t f f t 0 _null_)); ! /* ---------------- * pg_attribute * ---------------- */ #define Schema_pg_attribute \ ! { 1249, {"attrelid"}, 26, -1, 4, 1, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attname"}, 19, -1, NAMEDATALEN, 2, 0, -1, -1, false, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"atttypid"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attstattarget"}, 23, -1, 4, 4, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attlen"}, 21, -1, 2, 5, 0, -1, -1, true, 'p', 's', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attnum"}, 21, -1, 2, 6, 0, -1, -1, true, 'p', 's', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attndims"}, 23, -1, 4, 7, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attcacheoff"}, 23, -1, 4, 8, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"atttypmod"}, 23, -1, 4, 9, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attbyval"}, 16, -1, 1, 10, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attstorage"}, 18, -1, 1, 11, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attalign"}, 18, -1, 1, 12, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attkind"}, 18, -1, 1, 13, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attnotnull"}, 16, -1, 1, 14, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"atthasdef"}, 16, -1, 1, 15, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attisdropped"}, 16, -1, 1, 16, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attislocal"}, 16, -1, 1, 17, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attinhcount"}, 23, -1, 4, 18, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1249, {"attacl"}, 1034, -1, -1, 19, 1, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } } ! ! DATA(insert ( 1249 attrelid 26 -1 4 1 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1249 attname 19 -1 NAMEDATALEN 2 0 -1 -1 f p c r t f f t 0 _null_)); ! DATA(insert ( 1249 atttypid 26 -1 4 3 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1249 attstattarget 23 -1 4 4 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1249 attlen 21 -1 2 5 0 -1 -1 t p s r t f f t 0 _null_)); ! DATA(insert ( 1249 attnum 21 -1 2 6 0 -1 -1 t p s r t f f t 0 _null_)); ! DATA(insert ( 1249 attndims 23 -1 4 7 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1249 attcacheoff 23 -1 4 8 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1249 atttypmod 23 -1 4 9 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1249 attbyval 16 -1 1 10 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1249 attstorage 18 -1 1 11 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1249 attalign 18 -1 1 12 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1249 attkind 18 -1 1 13 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1249 attnotnull 16 -1 1 14 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1249 atthasdef 16 -1 1 15 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1249 attisdropped 16 -1 1 16 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1249 attislocal 16 -1 1 17 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1249 attinhcount 23 -1 4 18 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1249 attacl 1034 -1 -1 19 1 -1 -1 f x i r f f f t 0 _null_)); ! DATA(insert ( 1249 ctid 27 0 6 -1 0 -1 -1 f p s r t f f t 0 _null_)); /* no OIDs in pg_attribute */ ! DATA(insert ( 1249 xmin 28 0 4 -3 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1249 cmin 29 0 4 -4 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1249 xmax 28 0 4 -5 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1249 cmax 29 0 4 -6 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1249 security_acl 1034 0 -1 -8 1 -1 -1 f x i r t f f t 0 _null_)); ! DATA(insert ( 1249 security_label 25 0 -1 -9 0 -1 -1 f x i r t f f t 0 _null_)); ! /* ---------------- * pg_class * ---------------- */ #define Schema_pg_class \ ! { 1259, {"relname"}, 19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relnamespace"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"reltype"}, 26, -1, 4, 3, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relowner"}, 26, -1, 4, 4, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relam"}, 26, -1, 4, 5, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relfilenode"}, 26, -1, 4, 6, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"reltablespace"}, 26, -1, 4, 7, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relpages"}, 23, -1, 4, 8, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"reltuples"}, 700, -1, 4, 9, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"reltoastrelid"}, 26, -1, 4, 10, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"reltoastidxid"}, 26, -1, 4, 11, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relhasindex"}, 16, -1, 1, 12, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relisshared"}, 16, -1, 1, 13, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relkind"}, 18, -1, 1, 14, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relnatts"}, 21, -1, 2, 15, 0, -1, -1, true, 'p', 's', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relchecks"}, 21, -1, 2, 16, 0, -1, -1, true, 'p', 's', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relhasoids"}, 16, -1, 1, 17, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relhaspkey"}, 16, -1, 1, 18, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relhasrules"}, 16, -1, 1, 19, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relhastriggers"},16, -1, 1, 20, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relhassubclass"},16, -1, 1, 21, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relfrozenxid"}, 28, -1, 4, 22, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 1259, {"relacl"}, 1034, -1, -1, 23, 1, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } }, \ ! { 1259, {"reloptions"}, 1009, -1, -1, 24, 1, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } } ! ! DATA(insert ( 1259 relname 19 -1 NAMEDATALEN 1 0 -1 -1 f p c r t f f t 0 _null_)); ! DATA(insert ( 1259 relnamespace 26 -1 4 2 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1259 reltype 26 -1 4 3 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1259 relowner 26 -1 4 4 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1259 relam 26 -1 4 5 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1259 relfilenode 26 -1 4 6 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1259 reltablespace 26 -1 4 7 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1259 relpages 23 -1 4 8 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1259 reltuples 700 -1 4 9 0 -1 -1 FLOAT4PASSBYVAL p i r t f f t 0 _null_)); ! DATA(insert ( 1259 reltoastrelid 26 -1 4 10 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1259 reltoastidxid 26 -1 4 11 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1259 relhasindex 16 -1 1 12 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1259 relisshared 16 -1 1 13 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1259 relkind 18 -1 1 14 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1259 relnatts 21 -1 2 15 0 -1 -1 t p s r t f f t 0 _null_)); ! DATA(insert ( 1259 relchecks 21 -1 2 16 0 -1 -1 t p s r t f f t 0 _null_)); ! DATA(insert ( 1259 relhasoids 16 -1 1 17 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1259 relhaspkey 16 -1 1 18 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1259 relhasrules 16 -1 1 19 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1259 relhastriggers 16 -1 1 20 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1259 relhassubclass 16 -1 1 21 0 -1 -1 t p c r t f f t 0 _null_)); ! DATA(insert ( 1259 relfrozenxid 28 -1 4 22 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1259 relacl 1034 -1 -1 23 1 -1 -1 f x i r f f f t 0 _null_)); ! DATA(insert ( 1259 reloptions 1009 -1 -1 24 1 -1 -1 f x i r f f f t 0 _null_)); ! DATA(insert ( 1259 ctid 27 0 6 -1 0 -1 -1 f p s r t f f t 0 _null_)); ! DATA(insert ( 1259 oid 26 0 4 -2 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1259 xmin 28 0 4 -3 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1259 cmin 29 0 4 -4 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1259 xmax 28 0 4 -5 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1259 cmax 29 0 4 -6 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i r t f f t 0 _null_)); ! DATA(insert ( 1259 security_acl 1034 0 -1 -8 1 -1 -1 f x i r t f f t 0 _null_)); ! DATA(insert ( 1259 security_label 25 0 -1 -9 0 -1 -1 f x i r t f f t 0 _null_)); ! /* ---------------- * pg_index *************** DATA(insert ( 1259 tableoid 26 0 4 - *** 462,480 **** * ---------------- */ #define Schema_pg_index \ ! { 0, {"indexrelid"}, 26, -1, 4, 1, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indrelid"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indnatts"}, 21, -1, 2, 3, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indisunique"}, 16, -1, 1, 4, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indisprimary"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indisclustered"}, 16, -1, 1, 6, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indisvalid"}, 16, -1, 1, 7, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indcheckxmin"}, 16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indisready"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indkey"}, 22, -1, -1, 10, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indclass"}, 30, -1, -1, 11, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indoption"}, 22, -1, -1, 12, 1, -1, -1, false, 'p', 'i', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indexprs"}, 25, -1, -1, 13, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, \ ! { 0, {"indpred"}, 25, -1, -1, 14, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } } #endif /* PG_ATTRIBUTE_H */ --- 482,500 ---- * ---------------- */ #define Schema_pg_index \ ! { 0, {"indexrelid"}, 26, -1, 4, 1, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indrelid"}, 26, -1, 4, 2, 0, -1, -1, true, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indnatts"}, 21, -1, 2, 3, 0, -1, -1, true, 'p', 's', 'r', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indisunique"}, 16, -1, 1, 4, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indisprimary"}, 16, -1, 1, 5, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indisclustered"}, 16, -1, 1, 6, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indisvalid"}, 16, -1, 1, 7, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indcheckxmin"}, 16, -1, 1, 8, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indisready"}, 16, -1, 1, 9, 0, -1, -1, true, 'p', 'c', 'r', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indkey"}, 22, -1, -1, 10, 1, -1, -1, false, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indclass"}, 30, -1, -1, 11, 1, -1, -1, false, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indoption"}, 22, -1, -1, 12, 1, -1, -1, false, 'p', 'i', 'r', true, false, false, true, 0, { 0 } }, \ ! { 0, {"indexprs"}, 25, -1, -1, 13, 0, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } }, \ ! { 0, {"indpred"}, 25, -1, -1, 14, 0, -1, -1, false, 'x', 'i', 'r', false, false, false, true, 0, { 0 } } #endif /* PG_ATTRIBUTE_H */ diff -Nrpc base/src/include/catalog/pg_class.h sepgsql/src/include/catalog/pg_class.h *** base/src/include/catalog/pg_class.h Fri Jan 23 10:23:37 2009 --- sepgsql/src/include/catalog/pg_class.h Fri Jan 23 11:58:46 2009 *************** typedef FormData_pg_class *Form_pg_class *** 123,129 **** /* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */ DATA(insert OID = 1247 ( pg_type PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f r 28 0 t f f f f 3 _null_ _null_ )); DESCR(""); ! DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f r 18 0 f f f f f 3 _null_ _null_ )); DESCR(""); DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f r 25 0 t f f f f 3 _null_ _null_ )); DESCR(""); --- 123,129 ---- /* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */ DATA(insert OID = 1247 ( pg_type PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f r 28 0 t f f f f 3 _null_ _null_ )); DESCR(""); ! DATA(insert OID = 1249 ( pg_attribute PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f r 19 0 f f f f f 3 _null_ _null_ )); DESCR(""); DATA(insert OID = 1255 ( pg_proc PGNSP 81 PGUID 0 1255 0 0 0 0 0 f f r 25 0 t f f f f 3 _null_ _null_ )); DESCR(""); diff -Nrpc base/src/include/catalog/pg_proc.h sepgsql/src/include/catalog/pg_proc.h *** base/src/include/catalog/pg_proc.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/catalog/pg_proc.h Mon Jan 5 17:23:27 2009 *************** DESCR("I/O"); *** 4300,4305 **** --- 4300,4326 ---- DATA(insert OID = 2963 ( uuid_hash PGNSP PGUID 12 1 0 0 f f f t f i 1 0 23 "2950" _null_ _null_ _null_ _null_ uuid_hash _null_ _null_ _null_ )); DESCR("hash"); + /* PostgreSQL Access Control Extension related functions */ + DATA(insert OID = 3410 ( lo_get_security PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "26" _null_ _null_ _null_ _null_ lo_get_security _null_ _null_ _null_ )); + DATA(insert OID = 3411 ( lo_set_security PGNSP PGUID 12 1 0 0 f f f t f v 2 0 16 "26 25" _null_ _null_ _null_ _null_ lo_set_security _null_ _null_ _null_ )); + + /* Row-level Database ACLs related function */ + DATA(insert OID = 3430 ( rowacl_grant PGNSP PGUID 12 1 0 0 f f f t f v 4 0 1034 "26 1034 25 25" _null_ _null_ _null_ _null_ rowacl_grant _null_ _null_ _null_)); + DATA(insert OID = 3431 ( rowacl_revoke PGNSP PGUID 12 1 0 0 f f f t f v 4 0 1034 "26 1034 25 25" _null_ _null_ _null_ _null_ rowacl_revoke _null_ _null_ _null_)); + DATA(insert OID = 3432 ( rowacl_revoke_cascade PGNSP PGUID 12 1 0 0 f f f t f v 4 0 1034 "26 1034 25 25" _null_ _null_ _null_ _null_ rowacl_revoke_cascade _null_ _null_ _null_)); + + /* SE-PostgreSQL related function */ + DATA(insert OID = 3450 ( sepgsql_getcon PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ sepgsql_getcon _null_ _null_ _null_ )); + DATA(insert OID = 3451 ( sepgsql_getservcon PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ sepgsql_getservcon _null_ _null_ _null_ )); + DATA(insert OID = 3452 ( sepgsql_get_user PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_user _null_ _null_ _null_ )); + DATA(insert OID = 3453 ( sepgsql_set_user PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_user _null_ _null_ _null_ )); + DATA(insert OID = 3454 ( sepgsql_get_role PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_role _null_ _null_ _null_ )); + DATA(insert OID = 3455 ( sepgsql_set_role PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_role _null_ _null_ _null_ )); + DATA(insert OID = 3456 ( sepgsql_get_type PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_type _null_ _null_ _null_ )); + DATA(insert OID = 3457 ( sepgsql_set_type PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_type _null_ _null_ _null_ )); + DATA(insert OID = 3458 ( sepgsql_get_range PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_range _null_ _null_ _null_ )); + DATA(insert OID = 3459 ( sepgsql_set_range PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_range _null_ _null_ _null_ )); + /* enum related procs */ DATA(insert OID = 3504 ( anyenum_in PGNSP PGUID 12 1 0 0 f f f t f i 1 0 3500 "2275" _null_ _null_ _null_ _null_ anyenum_in _null_ _null_ _null_ )); DESCR("I/O"); diff -Nrpc base/src/include/catalog/pg_proc_fn.h sepgsql/src/include/catalog/pg_proc_fn.h *** base/src/include/catalog/pg_proc_fn.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/catalog/pg_proc_fn.h Sat Jan 3 15:58:18 2009 *************** extern Oid ProcedureCreate(const char *p *** 37,43 **** List *parameterDefaults, Datum proconfig, float4 procost, ! float4 prorows); extern bool function_parse_error_transpose(const char *prosrc); --- 37,44 ---- List *parameterDefaults, Datum proconfig, float4 procost, ! float4 prorows, ! void *pgaceItem); extern bool function_parse_error_transpose(const char *prosrc); diff -Nrpc base/src/include/catalog/pg_security.h sepgsql/src/include/catalog/pg_security.h *** base/src/include/catalog/pg_security.h Thu Jan 1 09:00:00 1970 --- sepgsql/src/include/catalog/pg_security.h Fri Sep 21 01:08:03 2007 *************** *** 0 **** --- 1,31 ---- + /* + * src/include/catalog/pg_security.h + * Definition of the security label relation (pg_security) + * + * Copyright (c) 2006 - 2007 KaiGai Kohei + */ + #ifndef PG_SECURITY_H + #define PG_SECURITY_H + + #define SecurityRelationId 3400 + + CATALOG(pg_security,3400) BKI_SHARED_RELATION + { + text seclabel; /* text representation of security label */ + } FormData_pg_security; + + /* ---------------- + * Form_pg_security corresponds to a pointer to a tuple with + * the format of pg_security relation. + * ---------------- + */ + typedef FormData_pg_security *Form_pg_security; + + /* ---------------- + * compiler constants for pg_selinux + * ---------------- + */ + #define Natts_pg_security 1 + #define Anum_pg_security_seclabel 1 + + #endif /* PG_SELINUX_H */ diff -Nrpc base/src/include/catalog/pg_type.h sepgsql/src/include/catalog/pg_type.h *** base/src/include/catalog/pg_type.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/catalog/pg_type.h Sat Jan 3 15:58:18 2009 *************** DATA(insert OID = 1033 ( aclitem PGNSP *** 459,464 **** --- 459,465 ---- DESCR("access control list"); #define ACLITEMOID 1033 DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); + #define ACLITEMARRAYOID 1034 DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); diff -Nrpc base/src/include/executor/executor.h sepgsql/src/include/executor/executor.h *** base/src/include/executor/executor.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/executor/executor.h Sat Jan 3 15:58:18 2009 *************** extern TupleHashEntry FindTupleHashEntry *** 130,137 **** /* * prototypes from functions in execJunk.c */ ! extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, ! TupleTableSlot *slot); extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, TupleDesc cleanTupType, TupleTableSlot *slot); --- 130,138 ---- /* * prototypes from functions in execJunk.c */ ! extern JunkFilter *ExecInitJunkFilter(List *targetList, ! bool hasoid, bool hassecacl, bool hasseclabel, ! TupleTableSlot *slot); extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, TupleDesc cleanTupType, TupleTableSlot *slot); *************** extern void InitResultRelInfo(ResultRelI *** 163,168 **** --- 164,171 ---- bool doInstrument); extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); + extern bool ExecContextForcesRowAcl(PlanState *planstate, bool *hasrowacl); + extern bool ExecContextForcesSecLabel(PlanState *planstate, bool *hasseclabel); extern void ExecConstraints(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate); extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, *************** extern void ExecInitScanTupleSlot(EState *** 216,223 **** extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate); extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, TupleDesc tupType); ! extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid); ! extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid); extern TupleDesc ExecTypeFromExprList(List *exprList); extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); --- 219,226 ---- extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate); extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, TupleDesc tupType); ! extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid, bool hassecacl, bool hasseclabel); ! extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hassecacl, bool hasseclabel); extern TupleDesc ExecTypeFromExprList(List *exprList); extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); diff -Nrpc base/src/include/executor/tuptable.h sepgsql/src/include/executor/tuptable.h *** base/src/include/executor/tuptable.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/executor/tuptable.h Sat Jan 3 15:58:18 2009 *************** typedef struct TupleTableSlot *** 118,123 **** --- 118,127 ---- MinimalTuple tts_mintuple; /* set if it's a minimal tuple, else NULL */ HeapTupleData tts_minhdr; /* workspace if it's a minimal tuple */ long tts_off; /* saved state for slot_deform_tuple */ + + /* temporary storage variables for writable system column */ + Datum tts_rowacl; /* for row level acls */ + Datum tts_seclabel; /* for security label */ } TupleTableSlot; /* diff -Nrpc base/src/include/fmgr.h sepgsql/src/include/fmgr.h *** base/src/include/fmgr.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/fmgr.h Fri Jan 16 17:05:27 2009 *************** typedef struct FmgrInfo *** 53,58 **** --- 53,60 ---- void *fn_extra; /* extra space for use by handler */ MemoryContext fn_mcxt; /* memory context to store fn_extra in */ fmNodePtr fn_expr; /* expression parse tree for call, or NULL */ + + void *fn_pgaceItem; /* PGACE opaque field */ } FmgrInfo; /* *************** extern bool get_call_expr_arg_stable(fmN *** 524,529 **** --- 526,532 ---- */ extern char *Dynamic_library_path; + extern char *expand_dynamic_library_name(const char *name); extern PGFunction load_external_function(char *filename, char *funcname, bool signalNotFound, void **filehandle); extern PGFunction lookup_external_function(void *filehandle, char *funcname); diff -Nrpc base/src/include/libpq/be-fsstubs.h sepgsql/src/include/libpq/be-fsstubs.h *** base/src/include/libpq/be-fsstubs.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/libpq/be-fsstubs.h Sat Jan 3 15:58:18 2009 *************** extern Datum lo_tell(PG_FUNCTION_ARGS); *** 37,42 **** --- 37,45 ---- extern Datum lo_unlink(PG_FUNCTION_ARGS); extern Datum lo_truncate(PG_FUNCTION_ARGS); + extern Datum lo_get_security(PG_FUNCTION_ARGS); + extern Datum lo_set_security(PG_FUNCTION_ARGS); + /* * These are not fmgr-callable, but are available to C code. * Probably these should have had the underscore-free names, diff -Nrpc base/src/include/nodes/nodes.h sepgsql/src/include/nodes/nodes.h *** base/src/include/nodes/nodes.h Fri Jan 23 10:23:37 2009 --- sepgsql/src/include/nodes/nodes.h Fri Jan 23 10:55:35 2009 *************** typedef enum NodeTag *** 390,396 **** T_TriggerData = 950, /* in commands/trigger.h */ T_ReturnSetInfo, /* in nodes/execnodes.h */ T_WindowObjectData, /* private in nodeWindowAgg.c */ ! T_TIDBitmap /* in nodes/tidbitmap.h */ } NodeTag; /* --- 390,397 ---- T_TriggerData = 950, /* in commands/trigger.h */ T_ReturnSetInfo, /* in nodes/execnodes.h */ T_WindowObjectData, /* private in nodeWindowAgg.c */ ! T_TIDBitmap, /* in nodes/tidbitmap.h */ ! T_SelinuxEvalItem, /* in nodes/security.h */ } NodeTag; /* diff -Nrpc base/src/include/nodes/parsenodes.h sepgsql/src/include/nodes/parsenodes.h *** base/src/include/nodes/parsenodes.h Fri Jan 23 10:23:37 2009 --- sepgsql/src/include/nodes/parsenodes.h Fri Jan 23 10:55:35 2009 *************** typedef struct Query *** 152,157 **** --- 152,158 ---- Node *setOperations; /* set-operation tree if this is top level of * a UNION/INTERSECT/EXCEPT query */ + Node *pgaceItem; /* PGACE: an opaque item for security purpose */ } Query; *************** typedef struct ColumnDef *** 471,476 **** --- 472,478 ---- Node *raw_default; /* default value (untransformed parse tree) */ char *cooked_default; /* nodeToString representation */ List *constraints; /* other constraints on column */ + Node *pgaceItem; /* PGACE: security attribute */ } ColumnDef; /* *************** typedef struct RangeTblEntry *** 717,722 **** --- 719,735 ---- Oid checkAsUser; /* if valid, check access as this role */ Bitmapset *selectedCols; /* columns needing SELECT permission */ Bitmapset *modifiedCols; /* columns needing INSERT/UPDATE permission */ + + /* + * PGACE allows its guest to use pgaceTuplePerms to mark required + * permission set for tuple-level access controls. This field is + * copied to Scan node (like SeqScan), and it can be refered within + * pgaceExecScan() hook. + * Please note that the wired rowacl reserves the least 8-bits + * (0x000000ff), so any other optional feature should not use + * these bits. + */ + uint32 pgaceTuplePerms; } RangeTblEntry; /* *************** typedef enum AlterTableType *** 1133,1139 **** AT_EnableReplicaRule, /* ENABLE REPLICA RULE name */ AT_DisableRule, /* DISABLE RULE name */ AT_AddInherit, /* INHERIT parent */ ! AT_DropInherit /* NO INHERIT parent */ } AlterTableType; typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */ --- 1146,1153 ---- AT_EnableReplicaRule, /* ENABLE REPLICA RULE name */ AT_DisableRule, /* DISABLE RULE name */ AT_AddInherit, /* INHERIT parent */ ! AT_DropInherit, /* NO INHERIT parent */ ! AT_SetSecurityLabel, /* PGACE: set security label */ } AlterTableType; typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */ *************** typedef struct CreateStmt *** 1336,1341 **** --- 1350,1356 ---- List *options; /* options from WITH clause */ OnCommitAction oncommit; /* what do we do at COMMIT? */ char *tablespacename; /* table space to use, or NULL */ + Node *pgaceItem; /* PGACE: security attribute */ } CreateStmt; /* ---------- diff -Nrpc base/src/include/nodes/plannodes.h sepgsql/src/include/nodes/plannodes.h *** base/src/include/nodes/plannodes.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/nodes/plannodes.h Sat Jan 3 15:58:18 2009 *************** typedef struct PlannedStmt *** 76,81 **** --- 76,83 ---- List *invalItems; /* other dependencies, as PlanInvalItems */ int nParamExec; /* number of PARAM_EXEC Params used */ + + Node *pgaceItem; /* PGACE: an opaque item for security purpose */ } PlannedStmt; /* macro for fetching the Plan associated with a SubPlan node */ *************** typedef struct Scan *** 239,244 **** --- 241,254 ---- { Plan plan; Index scanrelid; /* relid is index into the range table */ + + /* + * pgaceTuplePerms is used to show permission set to be applied to + * tuple-leve access controls by security module. + * It is copied from related RangeTblEntry's one when Scan structure + * is created. + */ + uint32 pgaceTuplePerms; } Scan; /* ---------------- diff -Nrpc base/src/include/nodes/relation.h sepgsql/src/include/nodes/relation.h *** base/src/include/nodes/relation.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/nodes/relation.h Sat Jan 3 15:58:18 2009 *************** typedef struct RelOptInfo *** 383,388 **** --- 383,390 ---- * list just to avoid recomputing the best inner indexscan repeatedly for * similar outer relations. See comments for InnerIndexscanInfo. */ + + uint32 pgaceTuplePerms; /* copied from RangeTblEntry */ } RelOptInfo; /* diff -Nrpc base/src/include/nodes/security.h sepgsql/src/include/nodes/security.h *** base/src/include/nodes/security.h Thu Jan 1 09:00:00 1970 --- sepgsql/src/include/nodes/security.h Wed Jan 21 16:59:48 2009 *************** *** 0 **** --- 1,40 ---- + /*------------------------------------------------------------------------- + * + * src/include/nodes/security.h + * definitions for security extention related nodes + * + * Portions Copyright (c) 2007-2008, PostgreSQL Global Development Group + * + *------------------------------------------------------------------------- + */ + #ifndef NODES_SECURITY_H + #define NODES_SECURITY_H + + #include "access/attnum.h" + #include "nodes/nodes.h" + + /* + * SelinuxEvalItem + * + * Required permissions on tables/columns used by SE-PostgreSQL. + * It is constracted just after query rewriter phase, then its + * list is checked based on the security policy of operating + * system. + * + * NOTE: attperms array can contains system attributes and + * whole-row-reference, so it is indexed as + * attperms[(attnum) + FirstLowInvalidHeapAttributeNumber - 1] + */ + typedef struct SelinuxEvalItem + { + NodeTag type; + + Oid relid; /* relation id */ + bool inh; /* flags to inheritable/only */ + + uint32 relperms; /* required permissions on table */ + uint32 nattrs; /* length of attperms */ + uint32 *attperms; /* required permissions on columns */ + } SelinuxEvalItem; + + #endif /* NODES_SECURITY_H */ diff -Nrpc base/src/include/pg_config.h.in sepgsql/src/include/pg_config.h.in *** base/src/include/pg_config.h.in Tue Jan 13 09:22:28 2009 --- sepgsql/src/include/pg_config.h.in Tue Jan 13 09:39:35 2009 *************** *** 388,393 **** --- 388,396 ---- /* Define to 1 if you have the header file. */ #undef HAVE_SECURITY_PAM_APPL_H + /* Define to 1 if you enable SELinux support */ + #undef HAVE_SELINUX + /* Define to 1 if you have the `setproctitle' function. */ #undef HAVE_SETPROCTITLE diff -Nrpc base/src/include/security/pgace.h sepgsql/src/include/security/pgace.h *** base/src/include/security/pgace.h Thu Jan 1 09:00:00 1970 --- sepgsql/src/include/security/pgace.h Wed Jan 21 17:22:37 2009 *************** *** 0 **** --- 1,181 ---- + /* + * include/security/pgace.h + * headers for PostgreSQL Access Control Extension (PGACE) + * + * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + */ + #ifndef PGACE_H + #define PGACE_H + + #include "access/htup.h" + #include "commands/trigger.h" + #include "executor/execdesc.h" + #include "fmgr.h" + #include "nodes/params.h" + #include "nodes/parsenodes.h" + #include "nodes/plannodes.h" + #include "storage/large_object.h" + #include "utils/rel.h" + + #include "security/rowacl.h" + #ifdef HAVE_SELINUX + #include "security/sepgsql.h" + #endif + + /* + * pgace_feature : GUC parameter to choose an enhanced security feature + */ + typedef enum + { + PGACE_FEATURE_NONE, + #ifdef HAVE_SELINUX + PGACE_FEATURE_SELINUX, + #endif + } PgaceFeatureOpts; + + extern int pgace_feature; + + /* + * Initialization hooks + */ + extern Size pgaceShmemSize(void); + extern void pgaceInitialize(bool is_bootstrap); + extern pid_t pgaceStartupWorkerProcess(void); + + /* + * SQL proxy hooks + */ + extern List *pgacePostQueryRewrite(List *queryList); + extern void pgaceExecutorStart(QueryDesc *queryDesc, int eflags); + extern void pgaceProcessUtility(Node *parsetree, ParamListInfo params, + bool isTopLevel); + /* + * HeapTuple input/output hooks + */ + extern bool pgaceExecScan(Scan *scan, Relation rel, TupleTableSlot *slot); + extern bool pgaceHeapTupleInsert(Relation rel, HeapTuple tuple, + bool is_internal, bool with_returning); + extern bool pgaceHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup, + bool is_internal, bool with_returning); + extern bool pgaceHeapTupleDelete(Relation rel, ItemPointer otid, + bool is_internal, bool with_returning); + /* + * Enhanced SQL statements + */ + extern bool pgaceIsGramSecurityItem(DefElem *defel); + extern void pgaceGramCreateRelation(Relation rel, HeapTuple tuple, DefElem *defel); + extern void pgaceGramCreateAttribute(Relation rel, HeapTuple tuple, DefElem *defel); + extern void pgaceGramAlterRelation(Relation rel, HeapTuple tuple, DefElem *defel); + extern void pgaceGramAlterAttribute(Relation rel, HeapTuple tuple, DefElem *defel); + extern void pgaceGramCreateDatabase(Relation rel, HeapTuple tuple, DefElem *defel); + extern void pgaceGramAlterDatabase(Relation rel, HeapTuple tuple, DefElem *defel); + extern void pgaceGramCreateFunction(Relation rel, HeapTuple tuple, DefElem *defel); + extern void pgaceGramAlterFunction(Relation rel, HeapTuple tuple, DefElem *defel); + + /* + * Function related hooks + */ + extern void pgaceCallFunction(FmgrInfo *finfo); + extern void pgaceCallAggFunction(HeapTuple aggTuple); + extern bool pgaceCallTriggerFunction(TriggerData *tgdata); + extern void pgaceBeginPerformCheckFK(Relation rel, bool is_primary, Oid save_userid, + Datum *rowacl_private, Datum *pgace_private); + extern void pgaceEndPerformCheckFK(Relation rel, + Datum rowacl_private, Datum pgace_private); + extern bool pgaceAllowFunctionInlined(Oid fnoid, HeapTuple func_tuple); + + /* + * Misc hooks + */ + extern void pgaceSetDatabaseParam(const char *name, char *argstring); + extern void pgaceGetDatabaseParam(const char *name); + extern void pgaceExecTruncate(List *trunc_rels); + extern void pgaceLockTable(Oid relid); + + /* + * COPY TO/FROM statement hooks + */ + extern void pgaceCopyTable(Relation rel, List *attNumList, bool isFrom); + extern void pgaceCopyFile(Relation rel, int fdesc, const char *filename, bool isFrom); + extern bool pgaceCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple); + + /* + * Loadable shared library module hooks + */ + extern void pgaceLoadSharedModule(const char *filename); + + /* + * Binary Large Object hooks + */ + extern void pgaceLargeObjectCreate(Relation rel, HeapTuple tuple); + extern void pgaceLargeObjectDrop(Relation rel, HeapTuple tuple, void **pgaceItem); + extern void pgaceLargeObjectRead(LargeObjectDesc *lodesc, int length); + extern void pgaceLargeObjectWrite(LargeObjectDesc *lodesc, int length); + extern void pgaceLargeObjectTruncate(LargeObjectDesc *lodesc, int offset); + extern void pgaceLargeObjectImport(Oid loid, int fdesc, const char *filename); + extern void pgaceLargeObjectExport(Oid loid, int fdesc, const char *filename); + extern void pgaceLargeObjectGetSecurity(Relation rel, HeapTuple tuple); + extern void pgaceLargeObjectSetSecurity(Relation rel, + HeapTuple newtup, HeapTuple oldtup); + /* + * Security Label hooks + */ + extern bool pgaceTupleDescHasRowAcl(Relation rel, List *relopts); + extern bool pgaceTupleDescHasSecLabel(Relation rel, List *relopts); + extern char *pgaceTranslateSecurityLabelIn(char *seclabel); + extern char *pgaceTranslateSecurityLabelOut(char *seclabel); + extern bool pgaceCheckValidSecurityLabel(char *seclabel); + extern char *pgaceUnlabeledSecurityLabel(void); + extern char *pgaceSecurityLabelOfLabel(void); + + /* + * PGACE common facilities (not hooks) + */ + + /* security label management */ + extern void pgacePostBootstrapingMode(void); + + extern Oid pgaceLookupSecurityId(char *label); + + extern char *pgaceLookupSecurityLabel(Oid sid); + + extern Oid pgaceSecurityLabelToSid(char *label); + + extern char *pgaceSidToSecurityLabel(Oid sid); + + /* Enhanced SQL statements related */ + extern List *pgaceRelationAttrList(CreateStmt *stmt); + + extern void pgaceCreateRelationCommon(Relation rel, HeapTuple tuple, + List *pgaceAttrList); + extern void pgaceCreateAttributeCommon(Relation rel, HeapTuple tuple, + List *pgaceAttrList); + extern void pgaceAlterRelationCommon(Relation rel, AlterTableCmd *cmd); + + /* Export security system columns */ + extern Datum pgaceHeapGetSecurityLabelSysattr(HeapTuple tuple); + + /* + * SQL functions + */ + + /* SE-PostgreSQL */ + extern Datum sepgsql_getcon(PG_FUNCTION_ARGS); + extern Datum sepgsql_getservcon(PG_FUNCTION_ARGS); + extern Datum sepgsql_get_user(PG_FUNCTION_ARGS); + extern Datum sepgsql_get_role(PG_FUNCTION_ARGS); + extern Datum sepgsql_get_type(PG_FUNCTION_ARGS); + extern Datum sepgsql_get_range(PG_FUNCTION_ARGS); + extern Datum sepgsql_set_user(PG_FUNCTION_ARGS); + extern Datum sepgsql_set_role(PG_FUNCTION_ARGS); + extern Datum sepgsql_set_type(PG_FUNCTION_ARGS); + extern Datum sepgsql_set_range(PG_FUNCTION_ARGS); + + /* Row-level ACLs */ + extern Datum rowacl_grant(PG_FUNCTION_ARGS); + extern Datum rowacl_revoke(PG_FUNCTION_ARGS); + extern Datum rowacl_revoke_cascade(PG_FUNCTION_ARGS); + + #endif // PGACE_H diff -Nrpc base/src/include/security/rowacl.h sepgsql/src/include/security/rowacl.h *** base/src/include/security/rowacl.h Thu Jan 1 09:00:00 1970 --- sepgsql/src/include/security/rowacl.h Fri Jan 16 10:31:05 2009 *************** *** 0 **** --- 1,41 ---- + /* + * src/include/security/rowacl.h + * headers for Row-level database ACL support + */ + #ifndef ROWACL_H + #define ROWACL_H + + #include "utils/acl.h" + + extern void rowaclInitialize(bool is_bootstrap); + + extern List *rowaclPostQueryRewrite(List *queryList); + + extern Datum rowaclBeginPerformCheckFK(Relation rel, bool is_primary, Oid userid_saved); + + extern void rowaclEndPerformCheckFK(Relation rel, Datum rowacl_private); + + extern bool rowaclExecScan(Scan *scan, Relation rel, TupleTableSlot *slot); + + extern bool rowaclCopyToTuple(Relation rel, List *attNumList, HeapTuple tuple); + + extern bool rowaclHeapTupleInsert(Relation rel, HeapTuple tuple, + bool is_internal, bool with_returning); + + extern bool rowaclHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup, + bool is_internal, bool with_returning); + + extern bool rowaclHeapTupleDelete(Relation rel, ItemPointer otid, + bool is_internal, bool with_returning); + + extern bool rowaclTupleDescHasRowAcl(Relation rel, List *relopts); + + extern Acl *rowaclSidToSecurityAcl(Oid sid, Oid ownerId); + + extern Oid rowaclSecurityAclToSid(Acl *acl); + + extern Datum rowaclHeapGetSecurityAclSysattr(HeapTuple tuple); + + extern void rawaclValidateDefaultRowAclRelopt(const char *value); + + #endif /* ROWACL_H */ diff -Nrpc base/src/include/security/sepgsql.h sepgsql/src/include/security/sepgsql.h *** base/src/include/security/sepgsql.h Thu Jan 1 09:00:00 1970 --- sepgsql/src/include/security/sepgsql.h Wed Jan 21 17:22:37 2009 *************** *** 0 **** --- 1,241 ---- + /* + * src/include/security/sepgsql.h + * headers for Security-Enhanced PostgreSQL (SE-PostgreSQL) + * + * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + */ + #ifndef SEPGSQL_H + #define SEPGSQL_H + + #include + #include + #include + + /* + * SE-PostgreSQL modes + */ + typedef enum + { + SEPGSQL_MODE_DEFAULT, + SEPGSQL_MODE_ENFORCING, + SEPGSQL_MODE_PERMISSIVE, + SEPGSQL_MODE_DISABLED, + } SepgsqlModeType; + + extern int sepostgresql_mode; + extern bool sepostgresql_row_level; + + /* + * Permission bits delivered to sepgsqlCheckTuplePerms(). + * Please note that 0x000000ff of RangeTblEntry->pgaceTuplePerms + * are reserved by rowacl. These bits are also stored within + * pgaceTuplePerms, we have to avoid to use the lower bits. + */ + #define SEPGSQL_PERMS_USE (1UL << 8) + #define SEPGSQL_PERMS_SELECT (1UL << 9) + #define SEPGSQL_PERMS_UPDATE (1UL << 10) + #define SEPGSQL_PERMS_INSERT (1UL << 11) + #define SEPGSQL_PERMS_DELETE (1UL << 12) + #define SEPGSQL_PERMS_RELABELFROM (1UL << 13) + #define SEPGSQL_PERMS_RELABELTO (1UL << 14) + #define SEPGSQL_PERMS_READ (1UL << 15) + #define SEPGSQL_PERMS_MASK (0xffffff00) + + /* + * The implementation of PGACE/SE-PostgreSQL hooks + */ + + /* Initialize / Finalize related hooks */ + extern Size sepgsqlShmemSize(void); + + extern void sepgsqlInitialize(bool is_bootstrap); + + extern pid_t sepgsqlStartupWorkerProcess(void); + + /* SQL proxy hooks */ + extern List *sepgsqlPostQueryRewrite(List *queryList); + + extern void sepgsqlExecutorStart(QueryDesc *queryDesc, int eflags); + + extern void sepgsqlProcessUtility(Node *parsetree, ParamListInfo params, bool isTopLevel); + + /* ExecScan hooks */ + extern bool sepgsqlExecScan(Scan *scan, Relation rel, TupleTableSlot *slot); + + /* HeapTuple modification hooks */ + extern bool sepgsqlHeapTupleInsert(Relation rel, HeapTuple tuple, + bool is_internal, bool with_returning); + extern bool sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, + HeapTuple newtup, bool is_internal, + bool with_returning); + extern bool sepgsqlHeapTupleDelete(Relation rel, ItemPointer otid, + bool is_internal, bool with_returning); + + /* Extended SQL statement hooks */ + extern bool sepgsqlIsGramSecurityItem(DefElem *defel); + + extern void sepgsqlGramCreateRelation(Relation rel, HeapTuple tuple, + DefElem *defel); + extern void sepgsqlGramCreateAttribute(Relation rel, HeapTuple tuple, + DefElem *defel); + extern void sepgsqlGramAlterRelation(Relation rel, HeapTuple tuple, + DefElem *defel); + extern void sepgsqlGramAlterAttribute(Relation rel, HeapTuple tuple, + DefElem *defel); + extern void sepgsqlGramCreateDatabase(Relation rel, HeapTuple tuple, + DefElem *defel); + extern void sepgsqlGramAlterDatabase(Relation rel, HeapTuple tuple, + DefElem *defel); + extern void sepgsqlGramCreateFunction(Relation rel, HeapTuple tuple, + DefElem *defel); + extern void sepgsqlGramAlterFunction(Relation rel, HeapTuple tuple, + DefElem *defel); + + /* DATABASE related hooks */ + extern void sepgsqlSetDatabaseParam(const char *name, char *argstring); + + extern void sepgsqlGetDatabaseParam(const char *name); + + /* FUNCTION related hooks */ + extern void sepgsqlCallFunction(FmgrInfo *finfo); + + extern void sepgsqlCallAggFunction(HeapTuple aggTuple); + + extern bool sepgsqlCallTriggerFunction(TriggerData *tgdata); + + extern Datum sepgsqlBeginPerformCheckFK(Relation rel, bool is_primary, Oid save_userid); + + extern void sepgsqlEndPerformCheckFK(Relation rel, Datum save_pgace); + + extern bool sepgsqlAllowFunctionInlined(Oid fnoid, HeapTuple func_tuple); + + /* TABLE related hooks */ + extern void sepgsqlLockTable(Oid relid); + + extern void sepgsqlExecTruncate(List *trunc_rels); + + extern bool sepgsqlAlterTable(Relation rel, AlterTableCmd *cmd); + + /* COPY TO/COPY FROM statement hooks */ + extern void sepgsqlCopyTable(Relation rel, List *attnumlist, bool is_from); + + extern void sepgsqlCopyFile(Relation rel, int fdesc, const char *filename, bool isFrom); + + extern bool sepgsqlCopyToTuple(Relation rel, List *attnumlist, HeapTuple tuple); + + /* Loadable shared library module hooks */ + extern void sepgsqlLoadSharedModule(const char *filename); + + /* Binary Large Object (BLOB) hooks */ + extern void sepgsqlLargeObjectCreate(Relation rel, HeapTuple tuple); + + extern void sepgsqlLargeObjectDrop(Relation rel, HeapTuple tuple, void **pgaceItem); + + extern void sepgsqlLargeObjectRead(LargeObjectDesc *lodesc, int length); + + extern void sepgsqlLargeObjectWrite(LargeObjectDesc *lodesc, int length); + + extern void sepgsqlLargeObjectTruncate(LargeObjectDesc *lodesc, int offset); + + extern void sepgsqlLargeObjectImport(Oid loid, int fdesc, const char *filename); + + extern void sepgsqlLargeObjectExport(Oid loid, int fdesc, const char *filename); + + extern void sepgsqlLargeObjectGetSecurity(Relation rel, HeapTuple tuple); + + extern void sepgsqlLargeObjectSetSecurity(Relation rel, HeapTuple newtup, HeapTuple oldtup); + + /* Security Label hooks */ + extern bool sepgsqlTupleDescHasSecLabel(Relation rel, List *relopts); + + extern char *sepgsqlTranslateSecurityLabelIn(const char *context); + + extern char *sepgsqlTranslateSecurityLabelOut(const char *context); + + extern bool sepgsqlCheckValidSecurityLabel(char *context); + + extern char *sepgsqlUnlabeledSecurityLabel(void); + + extern char *sepgsqlSecurityLabelOfLabel(void); + + /* + * SE-PostgreSQL core functions + * src/backend/security/sepgsql/core.c + */ + extern bool sepgsqlIsEnabled(void); + + extern const security_context_t sepgsqlGetServerContext(void); + + extern const security_context_t sepgsqlGetClientContext(void); + + extern const security_context_t sepgsqlGetDatabaseContext(void); + + extern const security_context_t sepgsqlGetUnlabeledContext(void); + + extern const security_context_t sepgsqlSwitchClientContext(security_context_t newcon); + + extern Oid sepgsqlGetDatabaseSecurityId(void); + + /* + * SE-PostgreSQL userspace avc functions + * src/backend/security/sepgsql/avc.c + */ + extern void sepgsqlAvcInit(void); + + extern void sepgsqlAvcSwitchClientContext(security_context_t context); + + extern void sepgsqlClientHasPermission(Oid target_security_id, + security_class_t tclass, + access_vector_t perms, + const char *objname); + + extern bool sepgsqlClientHasPermissionNoAbort(Oid target_security_id, + security_class_t tclass, + access_vector_t perms, + const char *objname); + + extern Oid sepgsqlClientCreateSid(Oid target_security_id, + security_class_t tclass); + + extern security_context_t + sepgsqlClientCreateContext(Oid target_security_id, + security_class_t tclass); + + extern bool sepgsqlComputePermission(const security_context_t scontext, + const security_context_t tcontext, + security_class_t tclass, + access_vector_t perms, + const char *objname); + + extern security_context_t + sepgsqlComputeCreateContext(const security_context_t scontext, + const security_context_t tcontext, + security_class_t tclass); + + /* + * SE-PostgreSQL permission evaluation related + * src/backend/security/sepgsql/permission.c + */ + extern const char *sepgsqlTupleName(Oid relid, HeapTuple tuple); + + extern security_class_t sepgsqlFileObjectClass(int fdesc, const char *filename); + + extern security_class_t sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple); + + extern void sepgsqlSetDefaultContext(Relation rel, HeapTuple tuple); + + extern bool sepgsqlCheckTuplePerms(Relation rel, HeapTuple tuple, HeapTuple newtup, + uint32 perms, bool abort); + + extern void sepgsqlCheckModuleInstallPerms(const char *filename); + + /* + * workaround for older libselinux + */ + #ifndef DB_PROCEDURE__INSTALL + #define DB_PROCEDURE__INSTALL 0x00000100UL + #endif + + #endif /* SEPGSQL_H */ diff -Nrpc base/src/include/storage/fd.h sepgsql/src/include/storage/fd.h *** base/src/include/storage/fd.h Tue Jan 13 09:22:28 2009 --- sepgsql/src/include/storage/fd.h Tue Jan 13 09:39:35 2009 *************** extern int FileWrite(File file, char *bu *** 68,73 **** --- 68,74 ---- extern int FileSync(File file); extern off_t FileSeek(File file, off_t offset, int whence); extern int FileTruncate(File file, off_t offset); + extern int FileRawDescriptor(File file); /* Operations that allow use of regular stdio --- USE WITH CAUTION */ extern FILE *AllocateFile(const char *name, const char *mode); diff -Nrpc base/src/include/storage/lwlock.h sepgsql/src/include/storage/lwlock.h *** base/src/include/storage/lwlock.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/storage/lwlock.h Sat Jan 3 15:58:18 2009 *************** typedef enum LWLockId *** 68,73 **** --- 68,74 ---- AutovacuumLock, AutovacuumScheduleLock, SyncScanLock, + SepgsqlAvcLock, /* Individual lock IDs end here */ FirstBufMappingLock, FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS, diff -Nrpc base/src/include/utils/acl.h sepgsql/src/include/utils/acl.h *** base/src/include/utils/acl.h Fri Jan 23 10:23:37 2009 --- sepgsql/src/include/utils/acl.h Fri Jan 23 10:55:35 2009 *************** typedef struct *** 222,227 **** --- 222,228 ---- /* * routines used internally */ + extern Acl *allocacl(int n); extern Acl *acldefault(GrantObjectType objtype, Oid ownerId); extern Acl *aclupdate(const Acl *old_acl, const AclItem *mod_aip, int modechg, Oid ownerId, DropBehavior behavior); *************** extern bool has_privs_of_role(Oid member *** 237,242 **** --- 238,244 ---- extern bool is_member_of_role(Oid member, Oid role); extern bool is_member_of_role_nosuper(Oid member, Oid role); extern bool is_admin_of_role(Oid member, Oid role); + extern void check_acl(const Acl *acl); extern void check_is_member_of_role(Oid member, Oid role); extern void select_best_grantor(Oid roleId, AclMode privileges, *************** extern AclResult pg_tablespace_aclcheck( *** 295,300 **** --- 297,307 ---- extern AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode); extern AclResult pg_foreign_server_aclcheck(Oid srv_oid, Oid roleid, AclMode mode); + extern Acl *merge_acl_with_grant(Acl *old_acl, bool is_grant, + bool grant_option, DropBehavior behavior, + List *grantees, AclMode privileges, + Oid grantorId, Oid ownerId); + extern void aclcheck_error(AclResult aclerr, AclObjectKind objectkind, const char *objectname); diff -Nrpc base/src/include/utils/catcache.h sepgsql/src/include/utils/catcache.h *** base/src/include/utils/catcache.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/utils/catcache.h Sat Jan 3 15:58:18 2009 *************** extern HeapTuple SearchCatCache(CatCache *** 172,177 **** --- 172,178 ---- Datum v1, Datum v2, Datum v3, Datum v4); extern void ReleaseCatCache(HeapTuple tuple); + extern void InsertCatCache(CatCache *cache, HeapTuple tuple); extern CatCList *SearchCatCacheList(CatCache *cache, int nkeys, Datum v1, Datum v2, diff -Nrpc base/src/include/utils/errcodes.h sepgsql/src/include/utils/errcodes.h *** base/src/include/utils/errcodes.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/utils/errcodes.h Sat Jan 3 15:58:18 2009 *************** *** 346,351 **** --- 346,358 ---- #define ERRCODE_NO_DATA_FOUND MAKE_SQLSTATE('P','0', '0','0','2') #define ERRCODE_TOO_MANY_ROWS MAKE_SQLSTATE('P','0', '0','0','3') + /* Class SE - Security Error (PGACE/SE-PostgreSQL error class) */ + #define ERRCODE_PGACE_ERROR MAKE_SQLSTATE('S','E', '0','0','0') + #define ERRCODE_ROWACL_ERROR MAKE_SQLSTATE('S','E', '0','1','1') + #define ERRCODE_SELINUX_ERROR MAKE_SQLSTATE('S','E', '0','2','1') + #define ERRCODE_SELINUX_AUDIT MAKE_SQLSTATE('S','E', '0','2','2') + #define ERRCODE_SELINUX_INFO MAKE_SQLSTATE('S','E', '0','2','3') + /* Class XX - Internal Error (PostgreSQL-specific error class) */ /* (this is for "can't-happen" conditions and software bugs) */ #define ERRCODE_INTERNAL_ERROR MAKE_SQLSTATE('X','X', '0','0','0') diff -Nrpc base/src/include/utils/rel.h sepgsql/src/include/utils/rel.h *** base/src/include/utils/rel.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/utils/rel.h Fri Jan 9 10:54:34 2009 *************** typedef struct StdRdOptions *** 218,223 **** --- 218,225 ---- { int32 vl_len_; /* varlena header (do not touch directly!) */ int fillfactor; /* page fill factor in percent (0..100) */ + bool row_level_acl; /* validator of Row-level ACLs */ + int default_row_acl;/* dafault Row-level ACLs */ } StdRdOptions; #define HEAP_MIN_FILLFACTOR 10 *************** typedef struct StdRdOptions *** 232,237 **** --- 234,255 ---- ((StdRdOptions *) (relation)->rd_options)->fillfactor : (defaultff)) /* + * RelationGetRowLevelAcl + * Returns the relations's avairability of Row-level ACLs. + */ + #define RelationGetRowLevelAcl(relation) \ + ((relation)->rd_options ? \ + ((StdRdOptions *) (relation)->rd_options)->row_level_acl : false) + + /* + * RelationGetDefaultAcl + * Returns the relations's default Row-level ACLs in text + */ + #define RelationGetDefaultRowAcl(relation) \ + ((relation)->rd_options ? \ + GET_STRING_RELOPTION(((StdRdOptions *) (relation)->rd_options), default_row_acl) : NULL) + + /* * RelationGetTargetPageUsage * Returns the relation's desired space usage per page in bytes. */ diff -Nrpc base/src/include/utils/syscache.h sepgsql/src/include/utils/syscache.h *** base/src/include/utils/syscache.h Sat Jan 3 12:25:21 2009 --- sepgsql/src/include/utils/syscache.h Sat Jan 3 15:58:18 2009 *************** enum SysCacheIdentifier *** 69,74 **** --- 69,76 ---- RELNAMENSP, RELOID, RULERELNAME, + SECURITYOID, + SECURITYLABEL, STATRELATT, TSCONFIGMAP, TSCONFIGNAMENSP, *************** extern HeapTuple SearchSysCache(int cach *** 92,97 **** --- 94,101 ---- Datum key1, Datum key2, Datum key3, Datum key4); extern void ReleaseSysCache(HeapTuple tuple); + extern void InsertSysCache(Oid relid, HeapTuple tuple); + /* convenience routines */ extern HeapTuple SearchSysCacheCopy(int cacheId, Datum key1, Datum key2, Datum key3, Datum key4);