diff --git a/.cvsignore b/.cvsignore deleted file mode 100644 index 227e30f..0000000 --- a/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -postgresql-8.4.2.tar.bz2 diff --git a/Makefile b/Makefile deleted file mode 100644 index 59ecad3..0000000 --- a/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -# Makefile for source rpm: sepostgresql -# $Id$ -NAME := sepostgresql -SPECFILE = $(firstword $(wildcard *.spec)) - -define find-makefile-common -for d in common ../common ../../common ; do if [ -f $$d/Makefile.common ] ; then if [ -f $$d/CVS/Root -a -w $$d/Makefile.common ] ; then cd $$d ; cvs -Q update ; fi ; echo "$$d/Makefile.common" ; break ; fi ; done -endef - -MAKEFILE_COMMON := $(shell $(find-makefile-common)) - -ifeq ($(MAKEFILE_COMMON),) -# attept a checkout -define checkout-makefile-common -test -f CVS/Root && { cvs -Q -d $$(cat CVS/Root) checkout common && echo "common/Makefile.common" ; } || { echo "ERROR: I can't figure out how to checkout the 'common' module." ; exit -1 ; } >&2 -endef - -MAKEFILE_COMMON := $(shell $(checkout-makefile-common)) -endif - -include $(MAKEFILE_COMMON) diff --git a/dead.package b/dead.package new file mode 100644 index 0000000..e1c0fac --- /dev/null +++ b/dead.package @@ -0,0 +1 @@ +SE-PostgreSQL was merged into mainstream as contrib/sepgsql extension diff --git a/pgsql-01-8.4-blobs.patch b/pgsql-01-8.4-blobs.patch deleted file mode 100644 index fefc54a..0000000 --- a/pgsql-01-8.4-blobs.patch +++ /dev/null @@ -1,2210 +0,0 @@ -diff --git a/contrib/lo/lo_test.sql b/contrib/lo/lo_test.sql -index aac0e99..b9ae89c 100644 ---- a/contrib/lo/lo_test.sql -+++ b/contrib/lo/lo_test.sql -@@ -12,7 +12,7 @@ SET search_path = public; - -- - - -- Check what is in pg_largeobject --SELECT count(DISTINCT loid) FROM pg_largeobject; -+SELECT count(oid) FROM pg_largeobject_metadata; - - -- ignore any errors here - simply drop the table if it already exists - DROP TABLE a; -@@ -74,6 +74,6 @@ DELETE FROM a; - DROP TABLE a; - - -- Check what is in pg_largeobject ... if different from original, trouble --SELECT count(DISTINCT loid) FROM pg_largeobject; -+SELECT count(oid) FROM pg_largeobject_metadata; - - -- end of tests -diff --git a/contrib/vacuumlo/vacuumlo.c b/contrib/vacuumlo/vacuumlo.c -index 3793cde..acc94cc 100644 ---- a/contrib/vacuumlo/vacuumlo.c -+++ b/contrib/vacuumlo/vacuumlo.c -@@ -142,7 +142,10 @@ vacuumlo(char *database, struct _param * param) - */ - buf[0] = '\0'; - strcat(buf, "CREATE TEMP TABLE vacuum_l AS "); -- strcat(buf, "SELECT DISTINCT loid AS lo FROM pg_largeobject "); -+ if (PQserverVersion(conn) >= 80500) -+ strcat(buf, "SELECT oid AS lo FROM pg_largeobject_metadata"); -+ else -+ strcat(buf, "SELECT DISTINCT loid AS lo FROM pg_largeobject"); - res = PQexec(conn, buf); - if (PQresultStatus(res) != PGRES_COMMAND_OK) - { -diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile -index 400ae80..3644ca6 100644 ---- a/src/backend/catalog/Makefile -+++ b/src/backend/catalog/Makefile -@@ -29,9 +29,9 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ - pg_proc.h pg_type.h pg_attribute.h pg_class.h \ - pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \ - pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \ -- pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \ -- pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \ -- pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ -+ pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \ -+ pg_statistic.h pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h \ -+ pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ - pg_database.h pg_tablespace.h pg_pltemplate.h \ - pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ - pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ -diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c -index ec4aaf0..1be417c 100644 ---- a/src/backend/catalog/aclchk.c -+++ b/src/backend/catalog/aclchk.c -@@ -30,6 +30,8 @@ - #include "catalog/pg_foreign_data_wrapper.h" - #include "catalog/pg_foreign_server.h" - #include "catalog/pg_language.h" -+#include "catalog/pg_largeobject.h" -+#include "catalog/pg_largeobject_metadata.h" - #include "catalog/pg_namespace.h" - #include "catalog/pg_opclass.h" - #include "catalog/pg_operator.h" -@@ -57,6 +59,7 @@ static void ExecGrant_Fdw(InternalGrant *grantStmt); - static void ExecGrant_ForeignServer(InternalGrant *grantStmt); - static void ExecGrant_Function(InternalGrant *grantStmt); - static void ExecGrant_Language(InternalGrant *grantStmt); -+static void ExecGrant_Largeobject(InternalGrant *grantStmt); - static void ExecGrant_Namespace(InternalGrant *grantStmt); - static void ExecGrant_Tablespace(InternalGrant *grantStmt); - -@@ -200,6 +203,9 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs, - case ACL_KIND_LANGUAGE: - whole_mask = ACL_ALL_RIGHTS_LANGUAGE; - break; -+ case ACL_KIND_LARGEOBJECT: -+ whole_mask = ACL_ALL_RIGHTS_LARGEOBJECT; -+ break; - case ACL_KIND_NAMESPACE: - whole_mask = ACL_ALL_RIGHTS_NAMESPACE; - break; -@@ -344,6 +350,10 @@ ExecuteGrantStmt(GrantStmt *stmt) - all_privileges = ACL_ALL_RIGHTS_LANGUAGE; - errormsg = gettext_noop("invalid privilege type %s for language"); - break; -+ case ACL_OBJECT_LARGEOBJECT: -+ all_privileges = ACL_ALL_RIGHTS_LARGEOBJECT; -+ errormsg = gettext_noop("invalid privilege type %s for large object"); -+ break; - case ACL_OBJECT_NAMESPACE: - all_privileges = ACL_ALL_RIGHTS_NAMESPACE; - errormsg = gettext_noop("invalid privilege type %s for schema"); -@@ -449,6 +459,9 @@ ExecGrantStmt_oids(InternalGrant *istmt) - case ACL_OBJECT_LANGUAGE: - ExecGrant_Language(istmt); - break; -+ case ACL_OBJECT_LARGEOBJECT: -+ ExecGrant_Largeobject(istmt); -+ break; - case ACL_OBJECT_NAMESPACE: - ExecGrant_Namespace(istmt); - break; -@@ -533,6 +546,20 @@ objectNamesToOids(GrantObjectType objtype, List *objnames) - ReleaseSysCache(tuple); - } - break; -+ case ACL_OBJECT_LARGEOBJECT: -+ foreach(cell, objnames) -+ { -+ Oid lobjOid = intVal(lfirst(cell)); -+ -+ if (!LargeObjectExists(lobjOid)) -+ ereport(ERROR, -+ (errcode(ERRCODE_UNDEFINED_OBJECT), -+ errmsg("large object %u does not exist", -+ lobjOid))); -+ -+ objects = lappend_oid(objects, lobjOid); -+ } -+ break; - case ACL_OBJECT_NAMESPACE: - foreach(cell, objnames) - { -@@ -1746,6 +1773,138 @@ ExecGrant_Language(InternalGrant *istmt) - } - - static void -+ExecGrant_Largeobject(InternalGrant *istmt) -+{ -+ Relation relation; -+ ListCell *cell; -+ -+ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS) -+ istmt->privileges = ACL_ALL_RIGHTS_LARGEOBJECT; -+ -+ relation = heap_open(LargeObjectMetadataRelationId, -+ RowExclusiveLock); -+ -+ foreach(cell, istmt->objects) -+ { -+ Oid loid = lfirst_oid(cell); -+ Form_pg_largeobject_metadata form_lo_meta; -+ char loname[NAMEDATALEN]; -+ Datum aclDatum; -+ bool isNull; -+ AclMode avail_goptions; -+ AclMode this_privileges; -+ Acl *old_acl; -+ Acl *new_acl; -+ Oid grantorId; -+ Oid ownerId; -+ HeapTuple newtuple; -+ Datum values[Natts_pg_largeobject_metadata]; -+ bool nulls[Natts_pg_largeobject_metadata]; -+ bool replaces[Natts_pg_largeobject_metadata]; -+ int noldmembers; -+ int nnewmembers; -+ Oid *oldmembers; -+ Oid *newmembers; -+ ScanKeyData entry[1]; -+ SysScanDesc scan; -+ HeapTuple tuple; -+ -+ /* There's no syscache for pg_largeobject_metadata */ -+ ScanKeyInit(&entry[0], -+ ObjectIdAttributeNumber, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(loid)); -+ -+ scan = systable_beginscan(relation, -+ LargeObjectMetadataOidIndexId, true, -+ SnapshotNow, 1, entry); -+ -+ tuple = systable_getnext(scan); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for large object %u", loid); -+ -+ form_lo_meta = (Form_pg_largeobject_metadata) GETSTRUCT(tuple); -+ -+ /* -+ * Get owner ID and working copy of existing ACL. If there's no ACL, -+ * substitute the proper default. -+ */ -+ ownerId = form_lo_meta->lomowner; -+ aclDatum = heap_getattr(tuple, -+ Anum_pg_largeobject_metadata_lomacl, -+ RelationGetDescr(relation), &isNull); -+ if (isNull) -+ old_acl = acldefault(ACL_OBJECT_LARGEOBJECT, ownerId); -+ else -+ old_acl = DatumGetAclPCopy(aclDatum); -+ -+ /* Determine ID to do the grant as, and available grant options */ -+ select_best_grantor(GetUserId(), istmt->privileges, -+ old_acl, ownerId, -+ &grantorId, &avail_goptions); -+ -+ /* -+ * Restrict the privileges to what we can actually grant, and emit the -+ * standards-mandated warning and error messages. -+ */ -+ snprintf(loname, sizeof(loname), "large object %u", loid); -+ this_privileges = -+ restrict_and_check_grant(istmt->is_grant, avail_goptions, -+ istmt->all_privs, istmt->privileges, -+ loid, grantorId, ACL_KIND_LARGEOBJECT, -+ loname, 0, NULL); -+ -+ /* -+ * Generate new ACL. -+ * -+ * We need the members of both old and new ACLs so we can correct the -+ * shared dependency information. -+ */ -+ noldmembers = aclmembers(old_acl, &oldmembers); -+ -+ new_acl = merge_acl_with_grant(old_acl, istmt->is_grant, -+ istmt->grant_option, istmt->behavior, -+ istmt->grantees, this_privileges, -+ grantorId, ownerId); -+ -+ nnewmembers = aclmembers(new_acl, &newmembers); -+ -+ /* finished building new ACL value, now insert it */ -+ MemSet(values, 0, sizeof(values)); -+ MemSet(nulls, false, sizeof(nulls)); -+ MemSet(replaces, false, sizeof(replaces)); -+ -+ replaces[Anum_pg_largeobject_metadata_lomacl - 1] = true; -+ values[Anum_pg_largeobject_metadata_lomacl - 1] -+ = PointerGetDatum(new_acl); -+ -+ newtuple = heap_modify_tuple(tuple, RelationGetDescr(relation), -+ values, nulls, replaces); -+ -+ simple_heap_update(relation, &newtuple->t_self, newtuple); -+ -+ /* keep the catalog indexes up to date */ -+ CatalogUpdateIndexes(relation, newtuple); -+ -+ /* Update the shared dependency ACL info */ -+ updateAclDependencies(LargeObjectRelationId, -+ HeapTupleGetOid(tuple), 0, -+ ownerId, istmt->is_grant, -+ noldmembers, oldmembers, -+ nnewmembers, newmembers); -+ -+ systable_endscan(scan); -+ -+ pfree(new_acl); -+ -+ /* prevent error when processing duplicate objects */ -+ CommandCounterIncrement(); -+ } -+ -+ heap_close(relation, RowExclusiveLock); -+} -+ -+static void - ExecGrant_Namespace(InternalGrant *istmt) - { - Relation relation; -@@ -2085,6 +2244,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] = - gettext_noop("permission denied for type %s"), - /* ACL_KIND_LANGUAGE */ - gettext_noop("permission denied for language %s"), -+ /* ACL_KIND_LARGEOBJECT */ -+ gettext_noop("permission denied for large object %s"), - /* ACL_KIND_NAMESPACE */ - gettext_noop("permission denied for schema %s"), - /* ACL_KIND_OPCLASS */ -@@ -2123,6 +2284,8 @@ static const char *const not_owner_msg[MAX_ACL_KIND] = - gettext_noop("must be owner of type %s"), - /* ACL_KIND_LANGUAGE */ - gettext_noop("must be owner of language %s"), -+ /* ACL_KIND_LARGEOBJECT */ -+ gettext_noop("must be owner of large object %s"), - /* ACL_KIND_NAMESPACE */ - gettext_noop("must be owner of schema %s"), - /* ACL_KIND_OPCLASS */ -@@ -2242,6 +2405,9 @@ pg_aclmask(AclObjectKind objkind, Oid table_oid, AttrNumber attnum, Oid roleid, - return pg_proc_aclmask(table_oid, roleid, mask, how); - case ACL_KIND_LANGUAGE: - return pg_language_aclmask(table_oid, roleid, mask, how); -+ case ACL_KIND_LARGEOBJECT: -+ return pg_largeobject_aclmask_snapshot(table_oid, roleid, -+ mask, how, SnapshotNow); - case ACL_KIND_NAMESPACE: - return pg_namespace_aclmask(table_oid, roleid, mask, how); - case ACL_KIND_TABLESPACE: -@@ -2625,6 +2791,90 @@ pg_language_aclmask(Oid lang_oid, Oid roleid, - } - - /* -+ * Exported routine for examining a user's privileges for a largeobject -+ * -+ * The reason why this interface has an argument of snapshot is that -+ * we apply a snapshot available on lo_open(), not SnapshotNow, when -+ * it is opened as read-only mode. -+ * If we could see the metadata and data from inconsistent viewpoint, -+ * it will give us much confusion. So, we need to provide an interface -+ * which takes an argument of snapshot. -+ * -+ * If the caller refers a large object with a certain snapshot except -+ * for SnapshotNow, its permission checks should be also applied in -+ * the same snapshot. -+ */ -+AclMode -+pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid, -+ AclMode mask, AclMaskHow how, -+ Snapshot snapshot) -+{ -+ AclMode result; -+ Relation pg_lo_meta; -+ ScanKeyData entry[1]; -+ SysScanDesc scan; -+ HeapTuple tuple; -+ Datum aclDatum; -+ bool isNull; -+ Acl *acl; -+ Oid ownerId; -+ -+ /* Superusers bypass all permission checking. */ -+ if (superuser_arg(roleid)) -+ return mask; -+ -+ /* -+ * Get the largeobject's ACL from pg_language_metadata -+ */ -+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, -+ AccessShareLock); -+ -+ ScanKeyInit(&entry[0], -+ ObjectIdAttributeNumber, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(lobj_oid)); -+ -+ scan = systable_beginscan(pg_lo_meta, -+ LargeObjectMetadataOidIndexId, true, -+ snapshot, 1, entry); -+ -+ tuple = systable_getnext(scan); -+ if (!HeapTupleIsValid(tuple)) -+ ereport(ERROR, -+ (errcode(ERRCODE_UNDEFINED_OBJECT), -+ errmsg("large object %u does not exist", lobj_oid))); -+ -+ ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(tuple))->lomowner; -+ -+ aclDatum = heap_getattr(tuple, Anum_pg_largeobject_metadata_lomacl, -+ RelationGetDescr(pg_lo_meta), &isNull); -+ -+ if (isNull) -+ { -+ /* No ACL, so build default ACL */ -+ acl = acldefault(ACL_OBJECT_LARGEOBJECT, ownerId); -+ aclDatum = (Datum) 0; -+ } -+ else -+ { -+ /* detoast ACL if necessary */ -+ acl = DatumGetAclP(aclDatum); -+ } -+ -+ result = aclmask(acl, roleid, ownerId, mask, how); -+ -+ /* if we have a detoasted copy, free it */ -+ if (acl && (Pointer) acl != DatumGetPointer(aclDatum)) -+ pfree(acl); -+ -+ systable_endscan(scan); -+ -+ heap_close(pg_lo_meta, AccessShareLock); -+ -+ return result; -+} -+ -+/* - * Exported routine for examining a user's privileges for a namespace - */ - AclMode -@@ -3075,6 +3325,20 @@ pg_language_aclcheck(Oid lang_oid, Oid roleid, AclMode mode) - } - - /* -+ * Exported routine for checking a user's access privileges to a largeobject -+ */ -+AclResult -+pg_largeobject_aclcheck_snapshot(Oid lobj_oid, Oid roleid, AclMode mode, -+ Snapshot snapshot) -+{ -+ if (pg_largeobject_aclmask_snapshot(lobj_oid, roleid, mode, -+ ACLMASK_ANY, snapshot) != 0) -+ return ACLCHECK_OK; -+ else -+ return ACLCHECK_NO_PRIV; -+} -+ -+/* - * Exported routine for checking a user's access privileges to a namespace - */ - AclResult -@@ -3265,6 +3529,53 @@ pg_language_ownercheck(Oid lan_oid, Oid roleid) - } - - /* -+ * Ownership check for a largeobject (specified by OID) -+ * -+ * Note that we have no candidate to call this routine with a certain -+ * snapshot except for SnapshotNow, so we don't provide an interface -+ * with _snapshot() version now. -+ */ -+bool -+pg_largeobject_ownercheck(Oid lobj_oid, Oid roleid) -+{ -+ Relation pg_lo_meta; -+ ScanKeyData entry[1]; -+ SysScanDesc scan; -+ HeapTuple tuple; -+ Oid ownerId; -+ -+ /* Superusers bypass all permission checking. */ -+ if (superuser_arg(roleid)) -+ return true; -+ -+ /* There's no syscache for pg_largeobject_metadata */ -+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, -+ AccessShareLock); -+ -+ ScanKeyInit(&entry[0], -+ ObjectIdAttributeNumber, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(lobj_oid)); -+ -+ scan = systable_beginscan(pg_lo_meta, -+ LargeObjectMetadataOidIndexId, true, -+ SnapshotNow, 1, entry); -+ -+ tuple = systable_getnext(scan); -+ if (!HeapTupleIsValid(tuple)) -+ ereport(ERROR, -+ (errcode(ERRCODE_UNDEFINED_OBJECT), -+ errmsg("large object %u does not exist", lobj_oid))); -+ -+ ownerId = ((Form_pg_largeobject_metadata) GETSTRUCT(tuple))->lomowner; -+ -+ systable_endscan(scan); -+ heap_close(pg_lo_meta, AccessShareLock); -+ -+ return has_privs_of_role(roleid, ownerId); -+} -+ -+/* - * Ownership check for a namespace (specified by OID). - */ - bool -diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c -index cb9a9c2..416c149 100644 ---- a/src/backend/catalog/dependency.c -+++ b/src/backend/catalog/dependency.c -@@ -36,6 +36,7 @@ - #include "catalog/pg_foreign_data_wrapper.h" - #include "catalog/pg_foreign_server.h" - #include "catalog/pg_language.h" -+#include "catalog/pg_largeobject.h" - #include "catalog/pg_namespace.h" - #include "catalog/pg_opclass.h" - #include "catalog/pg_operator.h" -@@ -129,6 +130,7 @@ static const Oid object_classes[MAX_OCLASS] = { - ConversionRelationId, /* OCLASS_CONVERSION */ - AttrDefaultRelationId, /* OCLASS_DEFAULT */ - LanguageRelationId, /* OCLASS_LANGUAGE */ -+ LargeObjectRelationId, /* OCLASS_LARGEOBJECT */ - OperatorRelationId, /* OCLASS_OPERATOR */ - OperatorClassRelationId, /* OCLASS_OPCLASS */ - OperatorFamilyRelationId, /* OCLASS_OPFAMILY */ -@@ -1071,6 +1073,10 @@ doDeletion(const ObjectAddress *object) - DropProceduralLanguageById(object->objectId); - break; - -+ case OCLASS_LARGEOBJECT: -+ LargeObjectDrop(object->objectId); -+ break; -+ - case OCLASS_OPERATOR: - RemoveOperatorById(object->objectId); - break; -@@ -1984,6 +1990,10 @@ getObjectClass(const ObjectAddress *object) - Assert(object->objectSubId == 0); - return OCLASS_LANGUAGE; - -+ case LargeObjectRelationId: -+ Assert(object->objectSubId == 0); -+ return OCLASS_LARGEOBJECT; -+ - case OperatorRelationId: - Assert(object->objectSubId == 0); - return OCLASS_OPERATOR; -@@ -2232,6 +2242,10 @@ getObjectDescription(const ObjectAddress *object) - ReleaseSysCache(langTup); - break; - } -+ case OCLASS_LARGEOBJECT: -+ appendStringInfo(&buffer, _("large object %u"), -+ object->objectId); -+ break; - - case OCLASS_OPERATOR: - appendStringInfo(&buffer, _("operator %s"), -diff --git a/src/backend/catalog/pg_largeobject.c b/src/backend/catalog/pg_largeobject.c -index c92ab02..54d992f 100644 ---- a/src/backend/catalog/pg_largeobject.c -+++ b/src/backend/catalog/pg_largeobject.c -@@ -16,8 +16,16 @@ - - #include "access/genam.h" - #include "access/heapam.h" -+#include "access/sysattr.h" -+#include "catalog/catalog.h" -+#include "catalog/dependency.h" - #include "catalog/indexing.h" -+#include "catalog/pg_authid.h" - #include "catalog/pg_largeobject.h" -+#include "catalog/pg_largeobject_metadata.h" -+#include "catalog/toasting.h" -+#include "miscadmin.h" -+#include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" - #include "utils/rel.h" -@@ -27,113 +35,258 @@ - /* - * Create a large object having the given LO identifier. - * -- * We do this by inserting an empty first page, so that the object will -- * appear to exist with size 0. Note that the unique index will reject -- * an attempt to create a duplicate page. -+ * We create a new large object by inserting an entry into -+ * pg_largeobject_metadata without any data pages, so that the object -+ * will appear to exist with size 0. - */ --void -+Oid - LargeObjectCreate(Oid loid) - { -- Relation pg_largeobject; -+ Relation pg_lo_meta; - HeapTuple ntup; -- Datum values[Natts_pg_largeobject]; -- bool nulls[Natts_pg_largeobject]; -- int i; -+ Oid loid_new; -+ Datum values[Natts_pg_largeobject_metadata]; -+ bool nulls[Natts_pg_largeobject_metadata]; - -- pg_largeobject = heap_open(LargeObjectRelationId, RowExclusiveLock); -+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, -+ RowExclusiveLock); - - /* -- * Form new tuple -+ * Insert metadata of the largeobject - */ -- for (i = 0; i < Natts_pg_largeobject; i++) -- { -- values[i] = (Datum) NULL; -- nulls[i] = false; -- } -+ memset(values, 0, sizeof(values)); -+ memset(nulls, false, sizeof(nulls)); - -- i = 0; -- values[i++] = ObjectIdGetDatum(loid); -- values[i++] = Int32GetDatum(0); -- values[i++] = DirectFunctionCall1(byteain, -- CStringGetDatum("")); -+ values[Anum_pg_largeobject_metadata_lomowner - 1] -+ = ObjectIdGetDatum(GetUserId()); -+ nulls[Anum_pg_largeobject_metadata_lomacl - 1] = true; - -- ntup = heap_form_tuple(pg_largeobject->rd_att, values, nulls); -+ ntup = heap_form_tuple(RelationGetDescr(pg_lo_meta), -+ values, nulls); -+ if (OidIsValid(loid)) -+ HeapTupleSetOid(ntup, loid); - -- /* -- * Insert it -- */ -- simple_heap_insert(pg_largeobject, ntup); -- -- /* Update indexes */ -- CatalogUpdateIndexes(pg_largeobject, ntup); -+ loid_new = simple_heap_insert(pg_lo_meta, ntup); -+ Assert(!OidIsValid(loid) || loid == loid_new); - -- heap_close(pg_largeobject, RowExclusiveLock); -+ CatalogUpdateIndexes(pg_lo_meta, ntup); - - heap_freetuple(ntup); -+ -+ heap_close(pg_lo_meta, RowExclusiveLock); -+ -+ return loid_new; - } - -+/* -+ * Drop a large object having the given LO identifier. -+ * -+ * When we drop a large object, it is necessary to drop both of metadata -+ * and data pages in same time. -+ */ - void - LargeObjectDrop(Oid loid) - { -- bool found = false; -+ Relation pg_lo_meta; - Relation pg_largeobject; - ScanKeyData skey[1]; -- SysScanDesc sd; -+ SysScanDesc scan; - HeapTuple tuple; - -+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, -+ RowExclusiveLock); -+ -+ pg_largeobject = heap_open(LargeObjectRelationId, -+ RowExclusiveLock); -+ -+ /* -+ * Delete an entry from pg_largeobject_metadata -+ */ - ScanKeyInit(&skey[0], -- Anum_pg_largeobject_loid, -+ ObjectIdAttributeNumber, - BTEqualStrategyNumber, F_OIDEQ, -- ObjectIdGetDatum(loid)); -+ ObjectIdGetDatum(loid)); - -- pg_largeobject = heap_open(LargeObjectRelationId, RowExclusiveLock); -+ scan = systable_beginscan(pg_lo_meta, -+ LargeObjectMetadataOidIndexId, true, -+ SnapshotNow, 1, skey); - -- sd = systable_beginscan(pg_largeobject, 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))); -+ -+ simple_heap_delete(pg_lo_meta, &tuple->t_self); -+ -+ systable_endscan(scan); -+ -+ /* -+ * Delete all the associated entries from pg_largeobject -+ */ -+ ScanKeyInit(&skey[0], -+ Anum_pg_largeobject_loid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(loid)); - -- while ((tuple = systable_getnext(sd)) != NULL) -+ scan = systable_beginscan(pg_largeobject, -+ LargeObjectLOidPNIndexId, true, -+ SnapshotNow, 1, skey); -+ while (HeapTupleIsValid(tuple = systable_getnext(scan))) - { - simple_heap_delete(pg_largeobject, &tuple->t_self); -- found = true; - } - -- systable_endscan(sd); -+ systable_endscan(scan); - - heap_close(pg_largeobject, RowExclusiveLock); - -- if (!found) -+ heap_close(pg_lo_meta, RowExclusiveLock); -+} -+ -+/* -+ * LargeObjectAlterOwner -+ * -+ * Implementation of ALTER LARGE OBJECT statement -+ */ -+void -+LargeObjectAlterOwner(Oid loid, Oid newOwnerId) -+{ -+ Form_pg_largeobject_metadata form_lo_meta; -+ Relation pg_lo_meta; -+ ScanKeyData skey[1]; -+ SysScanDesc scan; -+ HeapTuple oldtup; -+ HeapTuple newtup; -+ -+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, -+ RowExclusiveLock); -+ -+ ScanKeyInit(&skey[0], -+ ObjectIdAttributeNumber, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(loid)); -+ -+ scan = systable_beginscan(pg_lo_meta, -+ LargeObjectMetadataOidIndexId, true, -+ SnapshotNow, 1, skey); -+ -+ oldtup = systable_getnext(scan); -+ if (!HeapTupleIsValid(oldtup)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("large object %u does not exist", loid))); -+ -+ form_lo_meta = (Form_pg_largeobject_metadata) GETSTRUCT(oldtup); -+ if (form_lo_meta->lomowner != newOwnerId) -+ { -+ Datum values[Natts_pg_largeobject_metadata]; -+ bool nulls[Natts_pg_largeobject_metadata]; -+ bool replaces[Natts_pg_largeobject_metadata]; -+ Acl *newAcl; -+ Datum aclDatum; -+ bool isnull; -+ -+ /* Superusers can always do it */ -+ if (!superuser()) -+ { -+ /* -+ * The 'lo_compat_privileges' is not checked here, because we -+ * don't have any access control features in the 8.4.x series -+ * or earlier release. -+ * So, it is not a place we can define a compatible behavior. -+ */ -+ -+ /* Otherwise, must be owner of the existing object */ -+ if (!pg_largeobject_ownercheck(loid, GetUserId())) -+ ereport(ERROR, -+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -+ errmsg("must be owner of large object %u", loid))); -+ -+ /* Must be able to become new owner */ -+ check_is_member_of_role(GetUserId(), newOwnerId); -+ } -+ -+ memset(values, 0, sizeof(values)); -+ memset(nulls, false, sizeof(nulls)); -+ memset(replaces, false, sizeof(nulls)); -+ -+ values[Anum_pg_largeobject_metadata_lomowner - 1] -+ = ObjectIdGetDatum(newOwnerId); -+ replaces[Anum_pg_largeobject_metadata_lomowner - 1] = true; -+ -+ /* -+ * Determine the modified ACL for the new owner. -+ * This is only necessary when the ACL is non-null. -+ */ -+ aclDatum = heap_getattr(oldtup, -+ Anum_pg_largeobject_metadata_lomacl, -+ RelationGetDescr(pg_lo_meta), &isnull); -+ if (!isnull) -+ { -+ newAcl = aclnewowner(DatumGetAclP(aclDatum), -+ form_lo_meta->lomowner, newOwnerId); -+ values[Anum_pg_largeobject_metadata_lomacl - 1] -+ = PointerGetDatum(newAcl); -+ replaces[Anum_pg_largeobject_metadata_lomacl - 1] = true; -+ } -+ -+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(pg_lo_meta), -+ values, nulls, replaces); -+ -+ simple_heap_update(pg_lo_meta, &newtup->t_self, newtup); -+ CatalogUpdateIndexes(pg_lo_meta, newtup); -+ -+ heap_freetuple(newtup); -+ -+ /* Update owner dependency reference */ -+ changeDependencyOnOwner(LargeObjectRelationId, -+ loid, newOwnerId); -+ } -+ systable_endscan(scan); -+ -+ heap_close(pg_lo_meta, RowExclusiveLock); - } - -+/* -+ * LargeObjectExists -+ * -+ * Currently, we don't use system cache to contain metadata of -+ * large objects, because massive number of large objects can -+ * consume not a small amount of process local memory. -+ * -+ * Note that LargeObjectExists always scans the system catalog -+ * with SnapshotNow, so it is unavailable to use to check -+ * existence in read-only accesses. -+ */ - bool - LargeObjectExists(Oid loid) - { -+ Relation pg_lo_meta; -+ ScanKeyData skey[1]; -+ SysScanDesc sd; -+ HeapTuple tuple; - bool retval = false; -- Relation pg_largeobject; -- ScanKeyData skey[1]; -- SysScanDesc sd; - -- /* -- * See if we can find any tuples belonging to the specified LO -- */ - ScanKeyInit(&skey[0], -- Anum_pg_largeobject_loid, -+ ObjectIdAttributeNumber, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(loid)); - -- pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock); -+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, -+ AccessShareLock); - -- sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true, -+ sd = systable_beginscan(pg_lo_meta, -+ LargeObjectMetadataOidIndexId, true, - SnapshotNow, 1, skey); - -- if (systable_getnext(sd) != NULL) -+ tuple = systable_getnext(sd); -+ if (HeapTupleIsValid(tuple)) - retval = true; - - systable_endscan(sd); - -- heap_close(pg_largeobject, AccessShareLock); -+ heap_close(pg_lo_meta, AccessShareLock); - - return retval; - } -diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c -index cd04053..451724f 100644 ---- a/src/backend/catalog/pg_shdepend.c -+++ b/src/backend/catalog/pg_shdepend.c -@@ -24,6 +24,7 @@ - #include "catalog/pg_conversion.h" - #include "catalog/pg_database.h" - #include "catalog/pg_language.h" -+#include "catalog/pg_largeobject.h" - #include "catalog/pg_namespace.h" - #include "catalog/pg_operator.h" - #include "catalog/pg_proc.h" -@@ -1210,6 +1211,9 @@ shdepDropOwned(List *roleids, DropBehavior behavior) - case LanguageRelationId: - istmt.objtype = ACL_OBJECT_LANGUAGE; - break; -+ case LargeObjectRelationId: -+ istmt.objtype = ACL_OBJECT_LARGEOBJECT; -+ break; - case NamespaceRelationId: - istmt.objtype = ACL_OBJECT_NAMESPACE; - break; -@@ -1365,6 +1369,10 @@ shdepReassignOwned(List *roleids, Oid newrole) - AlterLanguageOwner_oid(sdepForm->objid, newrole); - break; - -+ case LargeObjectRelationId: -+ LargeObjectAlterOwner(sdepForm->objid, newrole); -+ break; -+ - default: - elog(ERROR, "unexpected classid %d", sdepForm->classid); - break; -diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c -index 835b738..46bc4df 100644 ---- a/src/backend/commands/alter.c -+++ b/src/backend/commands/alter.c -@@ -15,6 +15,7 @@ - #include "postgres.h" - - #include "catalog/namespace.h" -+#include "catalog/pg_largeobject.h" - #include "commands/alter.h" - #include "commands/conversioncmds.h" - #include "commands/dbcommands.h" -@@ -233,6 +234,10 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) - AlterLanguageOwner(strVal(linitial(stmt->object)), newowner); - break; - -+ case OBJECT_LARGEOBJECT: -+ LargeObjectAlterOwner(intVal(linitial(stmt->object)), newowner); -+ break; -+ - case OBJECT_OPERATOR: - Assert(list_length(stmt->objarg) == 2); - AlterOperatorOwner(stmt->object, -diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c -index ccf33ea..aaaf09a 100644 ---- a/src/backend/commands/comment.c -+++ b/src/backend/commands/comment.c -@@ -25,6 +25,7 @@ - #include "catalog/pg_description.h" - #include "catalog/pg_language.h" - #include "catalog/pg_largeobject.h" -+#include "catalog/pg_largeobject_metadata.h" - #include "catalog/pg_namespace.h" - #include "catalog/pg_opclass.h" - #include "catalog/pg_operator.h" -@@ -42,6 +43,7 @@ - #include "commands/comment.h" - #include "commands/dbcommands.h" - #include "commands/tablespace.h" -+#include "libpq/be-fsstubs.h" - #include "miscadmin.h" - #include "nodes/makefuncs.h" - #include "parser/parse_func.h" -@@ -1422,7 +1424,20 @@ CommentLargeObject(List *qualname, char *comment) - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("large object %u does not exist", loid))); - -- /* Call CreateComments() to create/drop the comments */ -+ /* Permission checks */ -+ if (!lo_compat_privileges && -+ !pg_largeobject_ownercheck(loid, GetUserId())) -+ ereport(ERROR, -+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -+ errmsg("must be owner of large object %u", loid))); -+ -+ /* -+ * Call CreateComments() to create/drop the comments -+ * -+ * See the comment in the inv_create() which describes -+ * the reason why LargeObjectRelationId is used instead -+ * of the LargeObjectMetadataRelationId. -+ */ - CreateComments(loid, LargeObjectRelationId, 0, comment); - } - -diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c -index 24b1223..96dda00 100644 ---- a/src/backend/commands/tablecmds.c -+++ b/src/backend/commands/tablecmds.c -@@ -5902,6 +5902,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, - case OCLASS_CAST: - case OCLASS_CONVERSION: - case OCLASS_LANGUAGE: -+ case OCLASS_LARGEOBJECT: - case OCLASS_OPERATOR: - case OCLASS_OPCLASS: - case OCLASS_OPFAMILY: -diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c -index 0831071..b6c46c1 100644 ---- a/src/backend/libpq/be-fsstubs.c -+++ b/src/backend/libpq/be-fsstubs.c -@@ -42,14 +42,20 @@ - #include - #include - -+#include "catalog/pg_largeobject_metadata.h" - #include "libpq/be-fsstubs.h" - #include "libpq/libpq-fs.h" - #include "miscadmin.h" - #include "storage/fd.h" - #include "storage/large_object.h" -+#include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/memutils.h" - -+/* -+ * compatibility flag for permission checks -+ */ -+bool lo_compat_privileges; - - /*#define FSDB 1*/ - #define BUFSIZE 8192 -@@ -156,6 +162,17 @@ lo_read(int fd, char *buf, int len) - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("invalid large-object descriptor: %d", fd))); - -+ /* Permission checks */ -+ if (!lo_compat_privileges && -+ pg_largeobject_aclcheck_snapshot(cookies[fd]->id, -+ GetUserId(), -+ ACL_SELECT, -+ cookies[fd]->snapshot) != ACLCHECK_OK) -+ ereport(ERROR, -+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -+ errmsg("permission denied for large object %u", -+ cookies[fd]->id))); -+ - status = inv_read(cookies[fd], buf, len); - - return status; -@@ -177,6 +194,17 @@ lo_write(int fd, const char *buf, int len) - errmsg("large object descriptor %d was not opened for writing", - fd))); - -+ /* Permission checks */ -+ if (!lo_compat_privileges && -+ pg_largeobject_aclcheck_snapshot(cookies[fd]->id, -+ GetUserId(), -+ ACL_UPDATE, -+ cookies[fd]->snapshot) != ACLCHECK_OK) -+ ereport(ERROR, -+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -+ errmsg("permission denied for large object %u", -+ cookies[fd]->id))); -+ - status = inv_write(cookies[fd], buf, len); - - return status; -@@ -251,6 +279,13 @@ lo_unlink(PG_FUNCTION_ARGS) - { - Oid lobjId = PG_GETARG_OID(0); - -+ /* Must be owner of the largeobject */ -+ if (!lo_compat_privileges && -+ !pg_largeobject_ownercheck(lobjId, GetUserId())) -+ ereport(ERROR, -+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -+ errmsg("must be owner of large object %u", lobjId))); -+ - /* - * If there are any open LO FDs referencing that ID, close 'em. - */ -@@ -482,6 +517,17 @@ lo_truncate(PG_FUNCTION_ARGS) - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("invalid large-object descriptor: %d", fd))); - -+ /* Permission checks */ -+ if (!lo_compat_privileges && -+ pg_largeobject_aclcheck_snapshot(cookies[fd]->id, -+ GetUserId(), -+ ACL_UPDATE, -+ cookies[fd]->snapshot) != ACLCHECK_OK) -+ ereport(ERROR, -+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -+ errmsg("permission denied for large object %u", -+ cookies[fd]->id))); -+ - inv_truncate(cookies[fd], len); - - PG_RETURN_INT32(0); -diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y -index 20ab0ba..d13b0f0 100644 ---- a/src/backend/parser/gram.y -+++ b/src/backend/parser/gram.y -@@ -378,6 +378,7 @@ static TypeName *TableFuncTypeName(List *columns); - %type opt_varying opt_timezone - - %type Iconst SignedIconst -+%type Iconst_list - %type Sconst comment_text - %type RoleId opt_granted_by opt_boolean ColId_or_Sconst - %type var_list -@@ -4379,6 +4380,13 @@ privilege_target: - n->objs = $2; - $$ = n; - } -+ | LARGE_P OBJECT_P Iconst_list -+ { -+ PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); -+ n->objtype = ACL_OBJECT_LARGEOBJECT; -+ n->objs = $3; -+ $$ = n; -+ } - | SCHEMA name_list - { - PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget)); -@@ -5506,6 +5514,14 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId - n->newowner = $7; - $$ = (Node *)n; - } -+ | ALTER LARGE_P OBJECT_P Iconst OWNER TO RoleId -+ { -+ AlterOwnerStmt *n = makeNode(AlterOwnerStmt); -+ n->objectType = OBJECT_LARGEOBJECT; -+ n->object = list_make1(makeInteger($4)); -+ n->newowner = $7; -+ $$ = (Node *)n; -+ } - | ALTER OPERATOR any_operator oper_argtypes OWNER TO RoleId - { - AlterOwnerStmt *n = makeNode(AlterOwnerStmt); -@@ -10066,6 +10082,10 @@ SignedIconst: Iconst { $$ = $1; } - | '-' Iconst { $$ = - $2; } - ; - -+Iconst_list: Iconst { $$ = list_make1(makeInteger($1)); } -+ | Iconst_list ',' Iconst { $$ = lappend($1, makeInteger($3)); } -+ ; -+ - /* - * Name classification hierarchy. - * -diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c -index 51b49dd..a946972 100644 ---- a/src/backend/storage/large_object/inv_api.c -+++ b/src/backend/storage/large_object/inv_api.c -@@ -32,18 +32,23 @@ - - #include "access/genam.h" - #include "access/heapam.h" -+#include "access/sysattr.h" - #include "access/tuptoaster.h" - #include "access/xact.h" - #include "catalog/catalog.h" -+#include "catalog/dependency.h" - #include "catalog/indexing.h" - #include "catalog/pg_largeobject.h" -+#include "catalog/pg_largeobject_metadata.h" - #include "commands/comment.h" - #include "libpq/libpq-fs.h" -+#include "miscadmin.h" - #include "storage/large_object.h" - #include "utils/fmgroids.h" - #include "utils/rel.h" - #include "utils/resowner.h" - #include "utils/snapmgr.h" -+#include "utils/syscache.h" - #include "utils/tqual.h" - - -@@ -139,30 +144,31 @@ close_lo_relation(bool isCommit) - static bool - myLargeObjectExists(Oid loid, Snapshot snapshot) - { -+ Relation pg_lo_meta; -+ ScanKeyData skey[1]; -+ SysScanDesc sd; -+ HeapTuple tuple; - bool retval = false; -- Relation pg_largeobject; -- ScanKeyData skey[1]; -- SysScanDesc sd; - -- /* -- * See if we can find any tuples belonging to the specified LO -- */ - ScanKeyInit(&skey[0], -- Anum_pg_largeobject_loid, -+ ObjectIdAttributeNumber, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(loid)); - -- pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock); -+ pg_lo_meta = heap_open(LargeObjectMetadataRelationId, -+ AccessShareLock); - -- sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true, -+ sd = systable_beginscan(pg_lo_meta, -+ LargeObjectMetadataOidIndexId, true, - snapshot, 1, skey); - -- if (systable_getnext(sd) != NULL) -+ tuple = systable_getnext(sd); -+ if (HeapTupleIsValid(tuple)) - retval = true; - - systable_endscan(sd); - -- heap_close(pg_largeobject, AccessShareLock); -+ heap_close(pg_lo_meta, AccessShareLock); - - return retval; - } -@@ -193,31 +199,31 @@ getbytealen(bytea *data) - Oid - inv_create(Oid lobjId) - { -+ Oid lobjId_new; -+ - /* -- * Allocate an OID to be the LO's identifier, unless we were told what to -- * use. We can use the index on pg_largeobject for checking OID -- * uniqueness, even though it has additional columns besides OID. -+ * Create a new largeobject with empty data pages - */ -- if (!OidIsValid(lobjId)) -- { -- open_lo_relation(); -- -- lobjId = GetNewOidWithIndex(lo_heap_r, LargeObjectLOidPNIndexId, -- Anum_pg_largeobject_loid); -- } -+ lobjId_new = LargeObjectCreate(lobjId); - - /* -- * Create the LO by writing an empty first page for it in pg_largeobject -- * (will fail if duplicate) -+ * dependency on the owner of largeobject -+ * -+ * The reason why we use LargeObjectRelationId instead of -+ * LargeObjectMetadataRelationId here is to provide backward -+ * compatibility to the applications which utilize a knowledge -+ * about internal layout of system catalogs. -+ * OID of pg_largeobject_metadata and loid of pg_largeobject -+ * are same value, so there are no actual differences here. - */ -- LargeObjectCreate(lobjId); -- -+ recordDependencyOnOwner(LargeObjectRelationId, -+ lobjId_new, GetUserId()); - /* - * Advance command counter to make new tuple visible to later operations. - */ - CommandCounterIncrement(); - -- return lobjId; -+ return lobjId_new; - } - - /* -@@ -292,10 +298,15 @@ inv_close(LargeObjectDesc *obj_desc) - int - inv_drop(Oid lobjId) - { -- LargeObjectDrop(lobjId); -+ ObjectAddress object; - -- /* Delete any comments on the large object */ -- DeleteComments(lobjId, LargeObjectRelationId, 0); -+ /* -+ * Delete any comments and dependencies on the large object -+ */ -+ object.classId = LargeObjectRelationId; -+ object.objectId = lobjId; -+ object.objectSubId = 0; -+ performDeletion(&object, DROP_CASCADE); - - /* - * Advance command counter so that tuple removal will be seen by later -@@ -315,7 +326,6 @@ inv_drop(Oid lobjId) - static uint32 - inv_getsize(LargeObjectDesc *obj_desc) - { -- bool found = false; - uint32 lastbyte = 0; - ScanKeyData skey[1]; - SysScanDesc sd; -@@ -339,13 +349,13 @@ inv_getsize(LargeObjectDesc *obj_desc) - * large object in reverse pageno order. So, it's sufficient to examine - * the first valid tuple (== last valid page). - */ -- while ((tuple = systable_getnext_ordered(sd, BackwardScanDirection)) != NULL) -+ tuple = systable_getnext_ordered(sd, BackwardScanDirection); -+ if (HeapTupleIsValid(tuple)) - { - Form_pg_largeobject data; - bytea *datafield; - bool pfreeit; - -- found = true; - if (HeapTupleHasNulls(tuple)) /* paranoia */ - elog(ERROR, "null field found in pg_largeobject"); - data = (Form_pg_largeobject) GETSTRUCT(tuple); -@@ -360,15 +370,10 @@ inv_getsize(LargeObjectDesc *obj_desc) - lastbyte = data->pageno * LOBLKSIZE + getbytealen(datafield); - if (pfreeit) - pfree(datafield); -- break; - } - - systable_endscan_ordered(sd); - -- if (!found) -- ereport(ERROR, -- (errcode(ERRCODE_UNDEFINED_OBJECT), -- errmsg("large object %u does not exist", obj_desc->id))); - return lastbyte; - } - -@@ -545,6 +550,12 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes) - errmsg("large object %u was not opened for writing", - obj_desc->id))); - -+ /* check existence of the target largeobject */ -+ if (!LargeObjectExists(obj_desc->id)) -+ ereport(ERROR, -+ (errcode(ERRCODE_UNDEFINED_OBJECT), -+ errmsg("large object %u was already dropped", obj_desc->id))); -+ - if (nbytes <= 0) - return 0; - -@@ -736,6 +747,12 @@ inv_truncate(LargeObjectDesc *obj_desc, int len) - errmsg("large object %u was not opened for writing", - obj_desc->id))); - -+ /* check existence of the target largeobject */ -+ if (!LargeObjectExists(obj_desc->id)) -+ ereport(ERROR, -+ (errcode(ERRCODE_UNDEFINED_OBJECT), -+ errmsg("large object %u was already dropped", obj_desc->id))); -+ - open_lo_relation(); - - indstate = CatalogOpenIndexes(lo_heap_r); -diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c -index 8adb79f..9e82a48 100644 ---- a/src/backend/tcop/utility.c -+++ b/src/backend/tcop/utility.c -@@ -1625,6 +1625,9 @@ CreateCommandTag(Node *parsetree) - case OBJECT_LANGUAGE: - tag = "ALTER LANGUAGE"; - break; -+ case OBJECT_LARGEOBJECT: -+ tag = "ALTER LARGE OBJECT"; -+ break; - case OBJECT_OPERATOR: - tag = "ALTER OPERATOR"; - break; -diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c -index 334823b..1de704a 100644 ---- a/src/backend/utils/adt/acl.c -+++ b/src/backend/utils/adt/acl.c -@@ -631,6 +631,11 @@ acldefault(GrantObjectType objtype, Oid ownerId) - world_default = ACL_USAGE; - owner_default = ACL_ALL_RIGHTS_LANGUAGE; - break; -+ case ACL_OBJECT_LARGEOBJECT: -+ /* Grant SELECT,UPDATE by default, for now */ -+ world_default = ACL_NO_RIGHTS; -+ owner_default = ACL_ALL_RIGHTS_LARGEOBJECT; -+ break; - case ACL_OBJECT_NAMESPACE: - world_default = ACL_NO_RIGHTS; - owner_default = ACL_ALL_RIGHTS_NAMESPACE; -diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c -index cb59d35..db1d933 100644 ---- a/src/backend/utils/misc/guc.c -+++ b/src/backend/utils/misc/guc.c -@@ -38,6 +38,7 @@ - #include "commands/trigger.h" - #include "funcapi.h" - #include "libpq/auth.h" -+#include "libpq/be-fsstubs.h" - #include "libpq/pqformat.h" - #include "miscadmin.h" - #include "optimizer/cost.h" -@@ -1221,6 +1222,16 @@ static struct config_bool ConfigureNamesBool[] = - false, NULL, NULL - }, - -+ { -+ {"lo_compat_privileges", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS, -+ gettext_noop("Enables backward compatibility in privilege checks on large objects"), -+ gettext_noop("When turned on, privilege checks on large objects perform " -+ "with backward compatibility as 8.4.x or earlier releases.") -+ }, -+ &lo_compat_privileges, -+ false, NULL, NULL -+ }, -+ - /* End-of-list marker */ - { - {NULL, 0, 0, NULL, NULL}, NULL, false, NULL, NULL -diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample -index 645f355..85acc4e 100644 ---- a/src/backend/utils/misc/postgresql.conf.sample -+++ b/src/backend/utils/misc/postgresql.conf.sample -@@ -483,6 +483,7 @@ - #backslash_quote = safe_encoding # on, off, or safe_encoding - #default_with_oids = off - #escape_string_warning = on -+#lo_compat_privileges = off - #regex_flavor = advanced # advanced, extended, or basic - #sql_inheritance = on - #standard_conforming_strings = off -diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c -index b14f3a0..1fcf590 100644 ---- a/src/bin/initdb/initdb.c -+++ b/src/bin/initdb/initdb.c -@@ -1815,6 +1815,7 @@ setup_privileges(void) - " WHERE relkind IN ('r', 'v', 'S') AND relacl IS NULL;\n", - "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n", - "GRANT CREATE, USAGE ON SCHEMA public TO PUBLIC;\n", -+ "REVOKE ALL ON pg_largeobject FROM PUBLIC;\n", - NULL - }; - -diff --git a/src/bin/pg_dump/dumputils.c b/src/bin/pg_dump/dumputils.c -index 93bd4d4..a14f4db 100644 ---- a/src/bin/pg_dump/dumputils.c -+++ b/src/bin/pg_dump/dumputils.c -@@ -758,6 +758,11 @@ do { \ - CONVERT_PRIV('U', "USAGE"); - else if (strcmp(type, "SERVER") == 0) - CONVERT_PRIV('U', "USAGE"); -+ else if (strcmp(type, "LARGE OBJECT") == 0) -+ { -+ CONVERT_PRIV('r', "SELECT"); -+ CONVERT_PRIV('w', "UPDATE"); -+ } - else - abort(); - -diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c -index c6a178f..57b5f7d 100644 ---- a/src/bin/pg_dump/pg_dump.c -+++ b/src/bin/pg_dump/pg_dump.c -@@ -1923,7 +1923,9 @@ hasBlobs(Archive *AH) - selectSourceSchema("pg_catalog"); - - /* Check for BLOB OIDs */ -- if (AH->remoteVersion >= 70100) -+ if (AH->remoteVersion >= 80402) -+ blobQry = "SELECT oid FROM pg_largeobject_metadata LIMIT 1"; -+ else if (AH->remoteVersion >= 70100) - blobQry = "SELECT loid FROM pg_largeobject LIMIT 1"; - else - blobQry = "SELECT oid FROM pg_class WHERE relkind = 'l' LIMIT 1"; -@@ -1959,7 +1961,9 @@ dumpBlobs(Archive *AH, void *arg) - selectSourceSchema("pg_catalog"); - - /* Cursor to get all BLOB OIDs */ -- if (AH->remoteVersion >= 70100) -+ if (AH->remoteVersion >= 80402) -+ blobQry = "DECLARE bloboid CURSOR FOR SELECT oid FROM pg_largeobject_metadata"; -+ else if (AH->remoteVersion >= 70100) - blobQry = "DECLARE bloboid CURSOR FOR SELECT DISTINCT loid FROM pg_largeobject"; - else - blobQry = "DECLARE bloboid CURSOR FOR SELECT oid FROM pg_class WHERE relkind = 'l'"; -@@ -2023,7 +2027,9 @@ dumpBlobs(Archive *AH, void *arg) - - /* - * dumpBlobComments -- * dump all blob comments -+ * dump all blob properties. -+ * It has "BLOB COMMENTS" tag due to the historical reason, but note -+ * that it is the routine to dump all the properties of blobs. - * - * Since we don't provide any way to be selective about dumping blobs, - * there's no need to be selective about their comments either. We put -@@ -2034,30 +2040,35 @@ dumpBlobComments(Archive *AH, void *arg) - { - const char *blobQry; - const char *blobFetchQry; -- PQExpBuffer commentcmd = createPQExpBuffer(); -+ PQExpBuffer cmdQry = createPQExpBuffer(); - PGresult *res; - int i; - - if (g_verbose) -- write_msg(NULL, "saving large object comments\n"); -+ write_msg(NULL, "saving large object properties\n"); - - /* Make sure we are in proper schema */ - selectSourceSchema("pg_catalog"); - - /* Cursor to get all BLOB comments */ -- if (AH->remoteVersion >= 70300) -+ if (AH->remoteVersion >= 80402) -+ blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, " -+ "obj_description(oid, 'pg_largeobject'), " -+ "pg_get_userbyid(lomowner), lomacl " -+ "FROM pg_largeobject_metadata"; -+ else if (AH->remoteVersion >= 70300) - blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, " -- "obj_description(loid, 'pg_largeobject') " -+ "obj_description(loid, 'pg_largeobject'), NULL, NULL " - "FROM (SELECT DISTINCT loid FROM " - "pg_description d JOIN pg_largeobject l ON (objoid = loid) " - "WHERE classoid = 'pg_largeobject'::regclass) ss"; - else if (AH->remoteVersion >= 70200) - blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, " -- "obj_description(loid, 'pg_largeobject') " -+ "obj_description(loid, 'pg_largeobject'), NULL, NULL " - "FROM (SELECT DISTINCT loid FROM pg_largeobject) ss"; - else if (AH->remoteVersion >= 70100) - blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, " -- "obj_description(loid) " -+ "obj_description(loid), NULL, NULL " - "FROM (SELECT DISTINCT loid FROM pg_largeobject) ss"; - else - blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, " -@@ -2065,7 +2076,7 @@ dumpBlobComments(Archive *AH, void *arg) - " SELECT description " - " FROM pg_description pd " - " WHERE pd.objoid=pc.oid " -- " ) " -+ " ), NULL, NULL " - "FROM pg_class pc WHERE relkind = 'l'"; - - res = PQexec(g_conn, blobQry); -@@ -2085,22 +2096,51 @@ dumpBlobComments(Archive *AH, void *arg) - /* Process the tuples, if any */ - for (i = 0; i < PQntuples(res); i++) - { -- Oid blobOid; -- char *comment; -+ Oid blobOid = atooid(PQgetvalue(res, i, 0)); -+ char *lo_comment = PQgetvalue(res, i, 1); -+ char *lo_owner = PQgetvalue(res, i, 2); -+ char *lo_acl = PQgetvalue(res, i, 3); -+ char lo_name[32]; - -- /* ignore blobs without comments */ -- if (PQgetisnull(res, i, 1)) -- continue; -+ resetPQExpBuffer(cmdQry); - -- blobOid = atooid(PQgetvalue(res, i, 0)); -- comment = PQgetvalue(res, i, 1); -+ /* comment on the blob */ -+ if (!PQgetisnull(res, i, 1)) -+ { -+ appendPQExpBuffer(cmdQry, -+ "COMMENT ON LARGE OBJECT %u IS ", blobOid); -+ appendStringLiteralAH(cmdQry, lo_comment, AH); -+ appendPQExpBuffer(cmdQry, ";\n"); -+ } -+ -+ /* dump blob ownership, if necessary */ -+ if (!PQgetisnull(res, i, 2)) -+ { -+ appendPQExpBuffer(cmdQry, -+ "ALTER LARGE OBJECT %u OWNER TO %s;\n", -+ blobOid, lo_owner); -+ } - -- printfPQExpBuffer(commentcmd, "COMMENT ON LARGE OBJECT %u IS ", -- blobOid); -- appendStringLiteralAH(commentcmd, comment, AH); -- appendPQExpBuffer(commentcmd, ";\n"); -+ /* dump blob privileges, if necessary */ -+ if (!PQgetisnull(res, i, 3) && -+ !dataOnly && !aclsSkip) -+ { -+ snprintf(lo_name, sizeof(lo_name), "%u", blobOid); -+ if (!buildACLCommands(lo_name, NULL, "LARGE OBJECT", -+ lo_acl, lo_owner, -+ AH->remoteVersion, cmdQry)) -+ { -+ write_msg(NULL, "could not parse ACL (%s) for " -+ "large object %u", lo_acl, blobOid); -+ exit_nicely(); -+ } -+ } - -- archputs(commentcmd->data, AH); -+ if (cmdQry->len > 0) -+ { -+ appendPQExpBuffer(cmdQry, "\n"); -+ archputs(cmdQry->data, AH); -+ } - } - } while (PQntuples(res) > 0); - -@@ -2108,7 +2148,7 @@ dumpBlobComments(Archive *AH, void *arg) - - archputs("\n", AH); - -- destroyPQExpBuffer(commentcmd); -+ destroyPQExpBuffer(cmdQry); - - return 1; - } -diff --git a/src/bin/psql/large_obj.c b/src/bin/psql/large_obj.c -index a97cc73..f829368 100644 ---- a/src/bin/psql/large_obj.c -+++ b/src/bin/psql/large_obj.c -@@ -278,13 +278,28 @@ do_lo_list(void) - char buf[1024]; - printQueryOpt myopt = pset.popt; - -- snprintf(buf, sizeof(buf), -- "SELECT loid as \"%s\",\n" -- " pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n" -- "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n" -- "ORDER BY 1", -- gettext_noop("ID"), -- gettext_noop("Description")); -+ if (pset.sversion >= 80500) -+ { -+ snprintf(buf, sizeof(buf), -+ "SELECT oid as \"%s\",\n" -+ " pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n" -+ " pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n" -+ " FROM pg_catalog.pg_largeobject_metadata " -+ " ORDER BY oid", -+ gettext_noop("ID"), -+ gettext_noop("Owner"), -+ gettext_noop("Description")); -+ } -+ else -+ { -+ snprintf(buf, sizeof(buf), -+ "SELECT loid as \"%s\",\n" -+ " pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n" -+ "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n" -+ "ORDER BY 1", -+ gettext_noop("ID"), -+ gettext_noop("Description")); -+ } - - res = PSQLexec(buf, false); - if (!res) -diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c -index 6fef61b..6457c9c 100644 ---- a/src/bin/psql/tab-complete.c -+++ b/src/bin/psql/tab-complete.c -@@ -693,7 +693,7 @@ psql_completion(char *text, int start, int end) - { - static const char *const list_ALTER[] = - {"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FOREIGN DATA WRAPPER", "FUNCTION", -- "GROUP", "INDEX", "LANGUAGE", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE", -+ "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE", - "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL}; - - COMPLETE_WITH_LIST(list_ALTER); -@@ -762,6 +762,17 @@ psql_completion(char *text, int start, int end) - COMPLETE_WITH_LIST(list_ALTERLANGUAGE); - } - -+ /* ALTER LARGE OBJECT */ -+ else if (pg_strcasecmp(prev4_wd, "ALTER") == 0 && -+ pg_strcasecmp(prev3_wd, "LARGE") == 0 && -+ pg_strcasecmp(prev2_wd, "OBJECT") == 0) -+ { -+ static const char *const list_ALTERLARGEOBJECT[] = -+ {"OWNER TO", NULL}; -+ -+ COMPLETE_WITH_LIST(list_ALTERLARGEOBJECT); -+ } -+ - /* ALTER USER,ROLE */ - else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && - !(pg_strcasecmp(prev2_wd, "USER") == 0 && pg_strcasecmp(prev_wd, "MAPPING") == 0) && -@@ -1703,6 +1714,7 @@ psql_completion(char *text, int start, int end) - " UNION SELECT 'FOREIGN SERVER'" - " UNION SELECT 'FUNCTION'" - " UNION SELECT 'LANGUAGE'" -+ " UNION SELECT 'LARGE OBJECT'" - " UNION SELECT 'SCHEMA'" - " UNION SELECT 'TABLESPACE'"); - -diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h -index 1e74251..5459f12 100644 ---- a/src/include/catalog/catversion.h -+++ b/src/include/catalog/catversion.h -@@ -53,6 +53,6 @@ - */ - - /* yyyymmddN */ --#define CATALOG_VERSION_NO 200904091 -+#define CATALOG_VERSION_NO 200912151 - - #endif -diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h -index a2f6761..62b9a52 100644 ---- a/src/include/catalog/dependency.h -+++ b/src/include/catalog/dependency.h -@@ -128,6 +128,7 @@ typedef enum ObjectClass - OCLASS_CONVERSION, /* pg_conversion */ - OCLASS_DEFAULT, /* pg_attrdef */ - OCLASS_LANGUAGE, /* pg_language */ -+ OCLASS_LARGEOBJECT, /* pg_largeobject */ - OCLASS_OPERATOR, /* pg_operator */ - OCLASS_OPCLASS, /* pg_opclass */ - OCLASS_OPFAMILY, /* pg_opfamily */ -diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h -index 81e18a1..0a46611 100644 ---- a/src/include/catalog/indexing.h -+++ b/src/include/catalog/indexing.h -@@ -165,6 +165,9 @@ DECLARE_UNIQUE_INDEX(pg_language_oid_index, 2682, on pg_language using btree(oid - DECLARE_UNIQUE_INDEX(pg_largeobject_loid_pn_index, 2683, on pg_largeobject using btree(loid oid_ops, pageno int4_ops)); - #define LargeObjectLOidPNIndexId 2683 - -+DECLARE_UNIQUE_INDEX(pg_largeobject_metadata_oid_index, 2996, on pg_largeobject_metadata using btree(oid oid_ops)); -+#define LargeObjectMetadataOidIndexId 2996 -+ - DECLARE_UNIQUE_INDEX(pg_namespace_nspname_index, 2684, on pg_namespace using btree(nspname name_ops)); - #define NamespaceNameIndexId 2684 - DECLARE_UNIQUE_INDEX(pg_namespace_oid_index, 2685, on pg_namespace using btree(oid oid_ops)); -diff --git a/src/include/catalog/pg_largeobject.h b/src/include/catalog/pg_largeobject.h -index 5ccfa94..6dd2fb0 100644 ---- a/src/include/catalog/pg_largeobject.h -+++ b/src/include/catalog/pg_largeobject.h -@@ -51,8 +51,9 @@ typedef FormData_pg_largeobject *Form_pg_largeobject; - #define Anum_pg_largeobject_pageno 2 - #define Anum_pg_largeobject_data 3 - --extern void LargeObjectCreate(Oid loid); -+extern Oid LargeObjectCreate(Oid loid); - extern void LargeObjectDrop(Oid loid); -+extern void LargeObjectAlterOwner(Oid loid, Oid newOwnerId); - extern bool LargeObjectExists(Oid loid); - - #endif /* PG_LARGEOBJECT_H */ -diff --git a/src/include/catalog/pg_largeobject_metadata.h b/src/include/catalog/pg_largeobject_metadata.h -new file mode 100644 -index 0000000..e0b6c9a ---- /dev/null -+++ b/src/include/catalog/pg_largeobject_metadata.h -@@ -0,0 +1,52 @@ -+/*------------------------------------------------------------------------- -+ * -+ * pg_largeobject_metadata.h -+ * definition of the system "largeobject_metadata" relation (pg_largeobject_metadata) -+ * along with the relation's initial contents. -+ * -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ * -+ * $PostgreSQL$ -+ * -+ * NOTES -+ * the genbki.sh script reads this file and generates .bki -+ * information from the DATA() statements. -+ * -+ *------------------------------------------------------------------------- -+ */ -+#ifndef PG_LARGEOBJECT_METADATA_H -+#define PG_LARGEOBJECT_METADATA_H -+ -+#include "catalog/genbki.h" -+ -+/* ---------------- -+ * pg_largeobject_metadata definition. cpp turns this into -+ * typedef struct FormData_pg_largeobject_metadata -+ * ---------------- -+ */ -+#define LargeObjectMetadataRelationId 2995 -+ -+CATALOG(pg_largeobject_metadata,2995) -+{ -+ Oid lomowner; /* OID of the largeobject owner */ -+ aclitem lomacl[1]; /* access permissions */ -+} FormData_pg_largeobject_metadata; -+ -+/* ---------------- -+ * Form_pg_largeobject_metadata corresponds to a pointer to a tuple -+ * with the format of pg_largeobject_metadata relation. -+ * ---------------- -+ */ -+typedef FormData_pg_largeobject_metadata *Form_pg_largeobject_metadata; -+ -+/* ---------------- -+ * compiler constants for pg_largeobject_metadata -+ * ---------------- -+ */ -+#define Natts_pg_largeobject_metadata 2 -+#define Anum_pg_largeobject_metadata_lomowner 1 -+#define Anum_pg_largeobject_metadata_lomacl 2 -+ -+#endif /* PG_LARGEOBJECT_METADATA_H */ -diff --git a/src/include/libpq/be-fsstubs.h b/src/include/libpq/be-fsstubs.h -index 5579618..862b014 100644 ---- a/src/include/libpq/be-fsstubs.h -+++ b/src/include/libpq/be-fsstubs.h -@@ -38,6 +38,11 @@ extern Datum lo_unlink(PG_FUNCTION_ARGS); - extern Datum lo_truncate(PG_FUNCTION_ARGS); - - /* -+ * compatibility option for access control -+ */ -+extern bool lo_compat_privileges; -+ -+/* - * These are not fmgr-callable, but are available to C code. - * Probably these should have had the underscore-free names, - * but too late now... -diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h -index 3ff93c4..487a226 100644 ---- a/src/include/nodes/parsenodes.h -+++ b/src/include/nodes/parsenodes.h -@@ -1186,6 +1186,7 @@ typedef enum GrantObjectType - ACL_OBJECT_FOREIGN_SERVER, /* foreign server */ - ACL_OBJECT_FUNCTION, /* function */ - ACL_OBJECT_LANGUAGE, /* procedural language */ -+ ACL_OBJECT_LARGEOBJECT, /* largeobject */ - ACL_OBJECT_NAMESPACE, /* namespace */ - ACL_OBJECT_TABLESPACE /* tablespace */ - } GrantObjectType; -diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h -index bde8727..8799dc0 100644 ---- a/src/include/utils/acl.h -+++ b/src/include/utils/acl.h -@@ -26,6 +26,7 @@ - - #include "nodes/parsenodes.h" - #include "utils/array.h" -+#include "utils/snapshot.h" - - - /* -@@ -151,6 +152,7 @@ typedef ArrayType Acl; - #define ACL_ALL_RIGHTS_FOREIGN_SERVER (ACL_USAGE) - #define ACL_ALL_RIGHTS_FUNCTION (ACL_EXECUTE) - #define ACL_ALL_RIGHTS_LANGUAGE (ACL_USAGE) -+#define ACL_ALL_RIGHTS_LARGEOBJECT (ACL_SELECT|ACL_UPDATE) - #define ACL_ALL_RIGHTS_NAMESPACE (ACL_USAGE|ACL_CREATE) - #define ACL_ALL_RIGHTS_TABLESPACE (ACL_CREATE) - -@@ -181,6 +183,7 @@ typedef enum AclObjectKind - ACL_KIND_OPER, /* pg_operator */ - ACL_KIND_TYPE, /* pg_type */ - ACL_KIND_LANGUAGE, /* pg_language */ -+ ACL_KIND_LARGEOBJECT, /* pg_largeobject */ - ACL_KIND_NAMESPACE, /* pg_namespace */ - ACL_KIND_OPCLASS, /* pg_opclass */ - ACL_KIND_OPFAMILY, /* pg_opfamily */ -@@ -273,6 +276,8 @@ extern AclMode pg_proc_aclmask(Oid proc_oid, Oid roleid, - AclMode mask, AclMaskHow how); - extern AclMode pg_language_aclmask(Oid lang_oid, Oid roleid, - AclMode mask, AclMaskHow how); -+extern AclMode pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid, -+ AclMode mask, AclMaskHow how, Snapshot snapshot); - extern AclMode pg_namespace_aclmask(Oid nsp_oid, Oid roleid, - AclMode mask, AclMaskHow how); - extern AclMode pg_tablespace_aclmask(Oid spc_oid, Oid roleid, -@@ -290,6 +295,8 @@ extern AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode); - extern AclResult pg_database_aclcheck(Oid db_oid, Oid roleid, AclMode mode); - extern AclResult pg_proc_aclcheck(Oid proc_oid, Oid roleid, AclMode mode); - extern AclResult pg_language_aclcheck(Oid lang_oid, Oid roleid, AclMode mode); -+extern AclResult pg_largeobject_aclcheck_snapshot(Oid lang_oid, Oid roleid, -+ AclMode mode, Snapshot snapshot); - extern AclResult pg_namespace_aclcheck(Oid nsp_oid, Oid roleid, AclMode mode); - extern AclResult pg_tablespace_aclcheck(Oid spc_oid, Oid roleid, AclMode mode); - extern AclResult pg_foreign_data_wrapper_aclcheck(Oid fdw_oid, Oid roleid, AclMode mode); -@@ -307,6 +314,7 @@ extern bool pg_type_ownercheck(Oid type_oid, Oid roleid); - extern bool pg_oper_ownercheck(Oid oper_oid, Oid roleid); - extern bool pg_proc_ownercheck(Oid proc_oid, Oid roleid); - extern bool pg_language_ownercheck(Oid lan_oid, Oid roleid); -+extern bool pg_largeobject_ownercheck(Oid lobj_oid, Oid roleid); - extern bool pg_namespace_ownercheck(Oid nsp_oid, Oid roleid); - extern bool pg_tablespace_ownercheck(Oid spc_oid, Oid roleid); - extern bool pg_opclass_ownercheck(Oid opc_oid, Oid roleid); -diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out -index a17ff59..a4debf9 100644 ---- a/src/test/regress/expected/privileges.out -+++ b/src/test/regress/expected/privileges.out -@@ -11,6 +11,12 @@ DROP ROLE IF EXISTS regressuser2; - DROP ROLE IF EXISTS regressuser3; - DROP ROLE IF EXISTS regressuser4; - DROP ROLE IF EXISTS regressuser5; -+DROP ROLE IF EXISTS regressuser6; -+SELECT lo_unlink(oid) FROM pg_largeobject_metadata; -+ lo_unlink -+----------- -+(0 rows) -+ - RESET client_min_messages; - -- test proper begins here - CREATE USER regressuser1; -@@ -815,6 +821,194 @@ SELECT has_table_privilege('regressuser1', 'atest4', 'SELECT WITH GRANT OPTION') - t - (1 row) - -+-- largeobject privilege tests -+\c - -+SET SESSION AUTHORIZATION regressuser1; -+SELECT lo_create(1001); -+ lo_create -+----------- -+ 1001 -+(1 row) -+ -+SELECT lo_create(1002); -+ lo_create -+----------- -+ 1002 -+(1 row) -+ -+SELECT lo_create(1003); -+ lo_create -+----------- -+ 1003 -+(1 row) -+ -+SELECT lo_create(1004); -+ lo_create -+----------- -+ 1004 -+(1 row) -+ -+SELECT lo_create(1005); -+ lo_create -+----------- -+ 1005 -+(1 row) -+ -+GRANT ALL ON LARGE OBJECT 1001 TO PUBLIC; -+GRANT SELECT ON LARGE OBJECT 1003 TO regressuser2; -+GRANT SELECT,UPDATE ON LARGE OBJECT 1004 TO regressuser2; -+GRANT ALL ON LARGE OBJECT 1005 TO regressuser2; -+GRANT SELECT ON LARGE OBJECT 1005 TO regressuser2 WITH GRANT OPTION; -+GRANT SELECT, INSERT ON LARGE OBJECT 1001 TO PUBLIC; -- to be failed -+ERROR: invalid privilege type INSERT for large object -+GRANT SELECT, UPDATE ON LARGE OBJECT 1001 TO nosuchuser; -- to be failed -+ERROR: role "nosuchuser" does not exist -+GRANT SELECT, UPDATE ON LARGE OBJECT 999 TO PUBLIC; -- to be failed -+ERROR: large object 999 does not exist -+\c - -+SET SESSION AUTHORIZATION regressuser2; -+SELECT lo_create(2001); -+ lo_create -+----------- -+ 2001 -+(1 row) -+ -+SELECT lo_create(2002); -+ lo_create -+----------- -+ 2002 -+(1 row) -+ -+SELECT loread(lo_open(1001, x'40000'::int), 32); -+ loread -+-------- -+ -+(1 row) -+ -+SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied -+ERROR: permission denied for large object 1002 -+SELECT loread(lo_open(1003, x'40000'::int), 32); -+ loread -+-------- -+ -+(1 row) -+ -+SELECT loread(lo_open(1004, x'40000'::int), 32); -+ loread -+-------- -+ -+(1 row) -+ -+SELECT lowrite(lo_open(1001, x'20000'::int), 'abcd'); -+ lowrite -+--------- -+ 4 -+(1 row) -+ -+SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied -+ERROR: permission denied for large object 1002 -+SELECT lowrite(lo_open(1003, x'20000'::int), 'abcd'); -- to be denied -+ERROR: permission denied for large object 1003 -+SELECT lowrite(lo_open(1004, x'20000'::int), 'abcd'); -+ lowrite -+--------- -+ 4 -+(1 row) -+ -+GRANT SELECT ON LARGE OBJECT 1005 TO regressuser3; -+GRANT UPDATE ON LARGE OBJECT 1006 TO regressuser3; -- to be denied -+ERROR: large object 1006 does not exist -+REVOKE ALL ON LARGE OBJECT 2001, 2002 FROM PUBLIC; -+GRANT ALL ON LARGE OBJECT 2001 TO regressuser3; -+SELECT lo_unlink(1001); -- to be denied -+ERROR: must be owner of large object 1001 -+SELECT lo_unlink(2002); -+ lo_unlink -+----------- -+ 1 -+(1 row) -+ -+\c - -+-- confirm ACL setting -+SELECT oid, pg_get_userbyid(lomowner) ownername, lomacl FROM pg_largeobject_metadata; -+ oid | ownername | lomacl -+------+--------------+------------------------------------------------------------------------------------------ -+ 1002 | regressuser1 | -+ 1001 | regressuser1 | {regressuser1=rw/regressuser1,=rw/regressuser1} -+ 1003 | regressuser1 | {regressuser1=rw/regressuser1,regressuser2=r/regressuser1} -+ 1004 | regressuser1 | {regressuser1=rw/regressuser1,regressuser2=rw/regressuser1} -+ 1005 | regressuser1 | {regressuser1=rw/regressuser1,regressuser2=r*w/regressuser1,regressuser3=r/regressuser2} -+ 2001 | regressuser2 | {regressuser2=rw/regressuser2,regressuser3=rw/regressuser2} -+(6 rows) -+ -+SET SESSION AUTHORIZATION regressuser3; -+SELECT loread(lo_open(1001, x'40000'::int), 32); -+ loread -+-------- -+ abcd -+(1 row) -+ -+SELECT loread(lo_open(1003, x'40000'::int), 32); -- to be denied -+ERROR: permission denied for large object 1003 -+SELECT loread(lo_open(1005, x'40000'::int), 32); -+ loread -+-------- -+ -+(1 row) -+ -+SELECT lo_truncate(lo_open(1005, x'20000'::int), 10); -- to be denied -+ERROR: permission denied for large object 1005 -+SELECT lo_truncate(lo_open(2001, x'20000'::int), 10); -+ lo_truncate -+------------- -+ 0 -+(1 row) -+ -+-- compatibility mode in largeobject permission -+\c - -+SET lo_compat_privileges = false; -- default setting -+SET SESSION AUTHORIZATION regressuser4; -+SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied -+ERROR: permission denied for large object 1002 -+SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied -+ERROR: permission denied for large object 1002 -+SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); -- to be denied -+ERROR: permission denied for large object 1002 -+SELECT lo_unlink(1002); -- to be denied -+ERROR: must be owner of large object 1002 -+SELECT lo_export(1001, '/dev/null'); -- to be denied -+ERROR: must be superuser to use server-side lo_export() -+HINT: Anyone can use the client-side lo_export() provided by libpq. -+\c - -+SET lo_compat_privileges = true; -- compatibility mode -+SET SESSION AUTHORIZATION regressuser4; -+SELECT loread(lo_open(1002, x'40000'::int), 32); -+ loread -+-------- -+ -+(1 row) -+ -+SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -+ lowrite -+--------- -+ 4 -+(1 row) -+ -+SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); -+ lo_truncate -+------------- -+ 0 -+(1 row) -+ -+SELECT lo_unlink(1002); -+ lo_unlink -+----------- -+ 1 -+(1 row) -+ -+SELECT lo_export(1001, '/dev/null'); -- to be denied -+ERROR: must be superuser to use server-side lo_export() -+HINT: Anyone can use the client-side lo_export() provided by libpq. - -- clean up - \c - DROP FUNCTION testfunc2(int); -@@ -836,6 +1030,16 @@ DROP TABLE atest6; - DROP TABLE atestc; - DROP TABLE atestp1; - DROP TABLE atestp2; -+SELECT lo_unlink(oid) FROM pg_largeobject_metadata; -+ lo_unlink -+----------- -+ 1 -+ 1 -+ 1 -+ 1 -+ 1 -+(5 rows) -+ - DROP GROUP regressgroup1; - DROP GROUP regressgroup2; - REVOKE USAGE ON LANGUAGE sql FROM regressuser1; -@@ -844,3 +1048,5 @@ DROP USER regressuser2; - DROP USER regressuser3; - DROP USER regressuser4; - DROP USER regressuser5; -+DROP USER regressuser6; -+ERROR: role "regressuser6" does not exist -diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out -index c6f1f15..9a66ba0 100644 ---- a/src/test/regress/expected/sanity_check.out -+++ b/src/test/regress/expected/sanity_check.out -@@ -104,6 +104,7 @@ SELECT relname, relhasindex - pg_inherits | t - pg_language | t - pg_largeobject | t -+ pg_largeobject_metadata | t - pg_listener | f - pg_namespace | t - pg_opclass | t -@@ -151,7 +152,7 @@ SELECT relname, relhasindex - timetz_tbl | f - tinterval_tbl | f - varchar_tbl | f --(140 rows) -+(141 rows) - - -- - -- another sanity check: every system catalog that has OIDs should have -diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql -index 5aa1012..2119aa1 100644 ---- a/src/test/regress/sql/privileges.sql -+++ b/src/test/regress/sql/privileges.sql -@@ -15,6 +15,9 @@ DROP ROLE IF EXISTS regressuser2; - DROP ROLE IF EXISTS regressuser3; - DROP ROLE IF EXISTS regressuser4; - DROP ROLE IF EXISTS regressuser5; -+DROP ROLE IF EXISTS regressuser6; -+ -+SELECT lo_unlink(oid) FROM pg_largeobject_metadata; - - RESET client_min_messages; - -@@ -36,7 +39,6 @@ ALTER GROUP regressgroup2 ADD USER regressuser2; -- duplicate - ALTER GROUP regressgroup2 DROP USER regressuser2; - ALTER GROUP regressgroup2 ADD USER regressuser4; - -- - -- test owner privileges - - SET SESSION AUTHORIZATION regressuser1; -@@ -468,6 +470,83 @@ SELECT has_table_privilege('regressuser3', 'atest4', 'SELECT'); -- false - - SELECT has_table_privilege('regressuser1', 'atest4', 'SELECT WITH GRANT OPTION'); -- true - -+-- largeobject privilege tests -+\c - -+SET SESSION AUTHORIZATION regressuser1; -+ -+SELECT lo_create(1001); -+SELECT lo_create(1002); -+SELECT lo_create(1003); -+SELECT lo_create(1004); -+SELECT lo_create(1005); -+ -+GRANT ALL ON LARGE OBJECT 1001 TO PUBLIC; -+GRANT SELECT ON LARGE OBJECT 1003 TO regressuser2; -+GRANT SELECT,UPDATE ON LARGE OBJECT 1004 TO regressuser2; -+GRANT ALL ON LARGE OBJECT 1005 TO regressuser2; -+GRANT SELECT ON LARGE OBJECT 1005 TO regressuser2 WITH GRANT OPTION; -+ -+GRANT SELECT, INSERT ON LARGE OBJECT 1001 TO PUBLIC; -- to be failed -+GRANT SELECT, UPDATE ON LARGE OBJECT 1001 TO nosuchuser; -- to be failed -+GRANT SELECT, UPDATE ON LARGE OBJECT 999 TO PUBLIC; -- to be failed -+ -+\c - -+SET SESSION AUTHORIZATION regressuser2; -+ -+SELECT lo_create(2001); -+SELECT lo_create(2002); -+ -+SELECT loread(lo_open(1001, x'40000'::int), 32); -+SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied -+SELECT loread(lo_open(1003, x'40000'::int), 32); -+SELECT loread(lo_open(1004, x'40000'::int), 32); -+ -+SELECT lowrite(lo_open(1001, x'20000'::int), 'abcd'); -+SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied -+SELECT lowrite(lo_open(1003, x'20000'::int), 'abcd'); -- to be denied -+SELECT lowrite(lo_open(1004, x'20000'::int), 'abcd'); -+ -+GRANT SELECT ON LARGE OBJECT 1005 TO regressuser3; -+GRANT UPDATE ON LARGE OBJECT 1006 TO regressuser3; -- to be denied -+REVOKE ALL ON LARGE OBJECT 2001, 2002 FROM PUBLIC; -+GRANT ALL ON LARGE OBJECT 2001 TO regressuser3; -+ -+SELECT lo_unlink(1001); -- to be denied -+SELECT lo_unlink(2002); -+ -+\c - -+-- confirm ACL setting -+SELECT oid, pg_get_userbyid(lomowner) ownername, lomacl FROM pg_largeobject_metadata; -+ -+SET SESSION AUTHORIZATION regressuser3; -+ -+SELECT loread(lo_open(1001, x'40000'::int), 32); -+SELECT loread(lo_open(1003, x'40000'::int), 32); -- to be denied -+SELECT loread(lo_open(1005, x'40000'::int), 32); -+ -+SELECT lo_truncate(lo_open(1005, x'20000'::int), 10); -- to be denied -+SELECT lo_truncate(lo_open(2001, x'20000'::int), 10); -+ -+-- compatibility mode in largeobject permission -+\c - -+SET lo_compat_privileges = false; -- default setting -+SET SESSION AUTHORIZATION regressuser4; -+ -+SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied -+SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied -+SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); -- to be denied -+SELECT lo_unlink(1002); -- to be denied -+SELECT lo_export(1001, '/dev/null'); -- to be denied -+ -+\c - -+SET lo_compat_privileges = true; -- compatibility mode -+SET SESSION AUTHORIZATION regressuser4; -+ -+SELECT loread(lo_open(1002, x'40000'::int), 32); -+SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -+SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); -+SELECT lo_unlink(1002); -+SELECT lo_export(1001, '/dev/null'); -- to be denied - - -- clean up - -@@ -493,6 +572,8 @@ DROP TABLE atestc; - DROP TABLE atestp1; - DROP TABLE atestp2; - -+SELECT lo_unlink(oid) FROM pg_largeobject_metadata; -+ - DROP GROUP regressgroup1; - DROP GROUP regressgroup2; - -@@ -502,3 +583,4 @@ DROP USER regressuser2; - DROP USER regressuser3; - DROP USER regressuser4; - DROP USER regressuser5; -+DROP USER regressuser6; diff --git a/pgsql-02-8.4-sepgsql.patch b/pgsql-02-8.4-sepgsql.patch deleted file mode 100644 index 9bd8c12..0000000 --- a/pgsql-02-8.4-sepgsql.patch +++ /dev/null @@ -1,17591 +0,0 @@ -diff --git a/configure b/configure -index 98a55b5..ddbdb07 100755 ---- a/configure -+++ b/configure -@@ -710,6 +710,8 @@ with_libxml - with_libxslt - with_system_tzdata - with_zlib -+enable_selinux -+SELINUX_LIBS - GREP - EGREP - ELF_SYS -@@ -1378,6 +1380,7 @@ Optional Features: - --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 -@@ -5532,6 +5535,183 @@ fi - - - # -+# 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 -+ SELINUX_LIBS="-lselinux" -+ { echo "$as_me:$LINENO: checking for avc_netlink_loop in -lselinux" >&5 -+echo $ECHO_N "checking for avc_netlink_loop in -lselinux... $ECHO_C" >&6; } -+if test "${ac_cv_lib_selinux_avc_netlink_loop+set}" = set; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+else -+ ac_check_lib_save_LIBS=$LIBS -+LIBS="-lselinux $LIBS" -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+ -+/* Override any GCC internal prototype to avoid an error. -+ Use char because int might match the return type of a GCC -+ builtin and then its argument prototype would still apply. */ -+#ifdef __cplusplus -+extern "C" -+#endif -+char avc_netlink_loop (); -+int -+main () -+{ -+return avc_netlink_loop (); -+ ; -+ return 0; -+} -+_ACEOF -+rm -f conftest.$ac_objext conftest$ac_exeext -+if { (ac_try="$ac_link" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 -+ (eval "$ac_link") 2>conftest.er1 -+ ac_status=$? -+ grep -v '^ *+' conftest.er1 >conftest.err -+ rm -f conftest.er1 -+ cat conftest.err >&5 -+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 -+ (exit $ac_status); } && { -+ test -z "$ac_c_werror_flag" || -+ test ! -s conftest.err -+ } && test -s conftest$ac_exeext && -+ $as_test_x conftest$ac_exeext; then -+ ac_cv_lib_selinux_avc_netlink_loop=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_cv_lib_selinux_avc_netlink_loop=no -+fi -+ -+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ -+ conftest$ac_exeext conftest.$ac_ext -+LIBS=$ac_check_lib_save_LIBS -+fi -+{ echo "$as_me:$LINENO: result: $ac_cv_lib_selinux_avc_netlink_loop" >&5 -+echo "${ECHO_T}$ac_cv_lib_selinux_avc_netlink_loop" >&6; } -+if test $ac_cv_lib_selinux_avc_netlink_loop = yes; then -+ -+cat >>confdefs.h <<_ACEOF -+#define HAVE_SELINUX 1 -+_ACEOF -+ -+else -+ { { echo "$as_me:$LINENO: error: \"--enable-selinux requires libselinux.\"" >&5 -+echo "$as_me: error: \"--enable-selinux requires libselinux.\"" >&2;} -+ { (exit 1); exit 1; }; } -+fi -+ -+ { echo "$as_me:$LINENO: checking for audit_open in -laudit" >&5 -+echo $ECHO_N "checking for audit_open in -laudit... $ECHO_C" >&6; } -+if test "${ac_cv_lib_audit_audit_open+set}" = set; then -+ echo $ECHO_N "(cached) $ECHO_C" >&6 -+else -+ ac_check_lib_save_LIBS=$LIBS -+LIBS="-laudit $LIBS" -+cat >conftest.$ac_ext <<_ACEOF -+/* confdefs.h. */ -+_ACEOF -+cat confdefs.h >>conftest.$ac_ext -+cat >>conftest.$ac_ext <<_ACEOF -+/* end confdefs.h. */ -+ -+/* Override any GCC internal prototype to avoid an error. -+ Use char because int might match the return type of a GCC -+ builtin and then its argument prototype would still apply. */ -+#ifdef __cplusplus -+extern "C" -+#endif -+char audit_open (); -+int -+main () -+{ -+return audit_open (); -+ ; -+ return 0; -+} -+_ACEOF -+rm -f conftest.$ac_objext conftest$ac_exeext -+if { (ac_try="$ac_link" -+case "(($ac_try" in -+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; -+ *) ac_try_echo=$ac_try;; -+esac -+eval "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_audit_audit_open=yes -+else -+ echo "$as_me: failed program was:" >&5 -+sed 's/^/| /' conftest.$ac_ext >&5 -+ -+ ac_cv_lib_audit_audit_open=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_audit_audit_open" >&5 -+echo "${ECHO_T}$ac_cv_lib_audit_audit_open" >&6; } -+if test $ac_cv_lib_audit_audit_open = yes; then -+ cat >>confdefs.h <<_ACEOF -+#define HAVE_LIBAUDIT 1 -+_ACEOF -+ -+ SELINUX_LIBS="$SELINUX_LIBS -laudit" -+fi -+ -+ -+ -+fi -+ -+# - # Elf - # - -@@ -28137,11 +28317,11 @@ with_libxml!$with_libxml$ac_delim - 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 -+SELINUX_LIBS!$SELINUX_LIBS$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 -@@ -28183,6 +28363,8 @@ _ACEOF - ac_delim='%!_!# ' - for ac_last_try in false false false false false :; do - cat >conf$$subs.sed <<_ACEOF -+LDFLAGS_SL!$LDFLAGS_SL$ac_delim -+LD!$LD$ac_delim - with_gnu_ld!$with_gnu_ld$ac_delim - ld_R_works!$ld_R_works$ac_delim - RANLIB!$RANLIB$ac_delim -@@ -28245,7 +28427,7 @@ vpath_build!$vpath_build$ac_delim - LTLIBOBJS!$LTLIBOBJS$ac_delim - _ACEOF - -- if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 60; then -+ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 62; then - break - elif $ac_last_try; then - { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 -diff --git a/configure.in b/configure.in -index 1a68732..16f6eab 100644 ---- a/configure.in -+++ b/configure.in -@@ -764,6 +764,24 @@ PGAC_ARG_BOOL(with, zlib, yes, - AC_SUBST(with_zlib) - - # -+# SELinux support -+# -+PGAC_ARG_BOOL(enable, selinux, no, -+ [enable to build with SELinux support]) -+if test "$enable_selinux" = yes; then -+ SELINUX_LIBS="-lselinux" -+ AC_CHECK_LIB(selinux, avc_netlink_loop, -+ AC_DEFINE_UNQUOTED(HAVE_SELINUX, 1, -+ [SE-PostgreSQL feature is enabled]), -+ AC_MSG_ERROR("--enable-selinux requires libselinux.")) -+ AC_CHECK_LIB(audit, audit_open, -+ AC_DEFINE_UNQUOTED(HAVE_LIBAUDIT, 1) -+ SELINUX_LIBS="$SELINUX_LIBS -laudit") -+ AC_SUBST(enable_selinux) -+ AC_SUBST(SELINUX_LIBS) -+fi -+ -+# - # Elf - # - -diff --git a/src/Makefile.global.in b/src/Makefile.global.in -index 6244e8a..39e2493 100644 ---- a/src/Makefile.global.in -+++ b/src/Makefile.global.in -@@ -165,6 +165,7 @@ enable_nls = @enable_nls@ - enable_debug = @enable_debug@ - enable_dtrace = @enable_dtrace@ - enable_coverage = @enable_coverage@ -+enable_selinux = @enable_selinux@ - enable_thread_safety = @enable_thread_safety@ - - python_includespec = @python_includespec@ -@@ -184,6 +185,8 @@ TCL_INCLUDE_SPEC = @TCL_INCLUDE_SPEC@ - TCL_SHARED_BUILD = @TCL_SHARED_BUILD@ - TCL_SHLIB_LD_LIBS = @TCL_SHLIB_LD_LIBS@ - -+SELINUX_LIBS = @SELINUX_LIBS@ -+ - PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ - PTHREAD_LIBS = @PTHREAD_LIBS@ - -diff --git a/src/backend/Makefile b/src/backend/Makefile -index baa45e1..bc13bb9 100644 ---- a/src/backend/Makefile -+++ b/src/backend/Makefile -@@ -16,7 +16,7 @@ include $(top_builddir)/src/Makefile.global - - SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \ - main nodes optimizer port postmaster regex rewrite \ -- storage tcop tsearch utils $(top_builddir)/src/timezone -+ security storage tcop tsearch utils $(top_builddir)/src/timezone - - include $(srcdir)/common.mk - -@@ -40,6 +40,9 @@ LIBS := $(filter-out -lpgport, $(LIBS)) $(LDAP_LIBS_BE) - # The backend doesn't need everything that's in LIBS, however - LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS)) - -+# SELinux Libraries -+LIBS += $(SELINUX_LIBS) -+ - ########################################################################## - - all: submake-libpgport postgres $(POSTGRES_IMP) -diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c -index a86716e..7c4d1f6 100644 ---- a/src/backend/access/common/heaptuple.c -+++ b/src/backend/access/common/heaptuple.c -@@ -60,6 +60,7 @@ - #include "access/heapam.h" - #include "access/sysattr.h" - #include "access/tuptoaster.h" -+#include "catalog/pg_security.h" - #include "executor/tuptable.h" - - -@@ -287,6 +288,7 @@ heap_attisnull(HeapTuple tup, int attnum) - case MinCommandIdAttributeNumber: - case MaxTransactionIdAttributeNumber: - case MaxCommandIdAttributeNumber: -+ case SecurityAttributeNumber: - /* these are never null */ - break; - -@@ -599,6 +601,9 @@ heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull) - case TableOidAttributeNumber: - result = ObjectIdGetDatum(tup->t_tableOid); - break; -+ case SecurityAttributeNumber: -+ result = securitySysattSecLabelOut(tup->t_tableOid, tup); -+ break; - default: - elog(ERROR, "invalid attnum: %d", attnum); - result = 0; /* keep compiler quiet */ -@@ -722,6 +727,8 @@ heap_form_tuple(TupleDesc tupleDescriptor, - - if (tupleDescriptor->tdhasoid) - len += sizeof(Oid); -+ if (tupleDescriptor->tdhassecid) -+ len += sizeof(Oid); - - hoff = len = MAXALIGN(len); /* align user data safely */ - -@@ -753,6 +760,8 @@ heap_form_tuple(TupleDesc tupleDescriptor, - - if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ - td->t_infomask = HEAP_HASOID; -+ if (tupleDescriptor->tdhassecid) -+ td->t_infomask |= HEAP_HASSECID; - - heap_fill_tuple(tupleDescriptor, - values, -@@ -864,6 +873,8 @@ heap_modify_tuple(HeapTuple tuple, - newTuple->t_tableOid = tuple->t_tableOid; - if (tupleDesc->tdhasoid) - HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); -+ if (HeapTupleHasSecid(newTuple)) -+ HeapTupleSetSecid(newTuple, HeapTupleGetSecid(tuple)); - - return newTuple; - } -@@ -1474,6 +1485,8 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor, - - if (tupleDescriptor->tdhasoid) - len += sizeof(Oid); -+ if (tupleDescriptor->tdhassecid) -+ len += sizeof(Oid); - - hoff = len = MAXALIGN(len); /* align user data safely */ - -@@ -1495,6 +1508,8 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor, - - if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ - tuple->t_infomask = HEAP_HASOID; -+ if (tupleDescriptor->tdhassecid) -+ tuple->t_infomask |= HEAP_HASSECID; - - heap_fill_tuple(tupleDescriptor, - values, -diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c -index 8582a7d..7df1631 100644 ---- a/src/backend/access/common/tupdesc.c -+++ b/src/backend/access/common/tupdesc.c -@@ -88,6 +88,7 @@ CreateTemplateTupleDesc(int natts, bool hasoid) - desc->tdtypeid = RECORDOID; - desc->tdtypmod = -1; - desc->tdhasoid = hasoid; -+ desc->tdhassecid = false; - desc->tdrefcount = -1; /* assume not reference-counted */ - - return desc; -@@ -121,6 +122,7 @@ CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs) - desc->tdtypeid = RECORDOID; - desc->tdtypmod = -1; - desc->tdhasoid = hasoid; -+ desc->tdhassecid = false; - desc->tdrefcount = -1; /* assume not reference-counted */ - - return desc; -@@ -150,6 +152,7 @@ CreateTupleDescCopy(TupleDesc tupdesc) - - desc->tdtypeid = tupdesc->tdtypeid; - desc->tdtypmod = tupdesc->tdtypmod; -+ desc->tdhassecid = tupdesc->tdhassecid; - - return desc; - } -@@ -208,6 +211,7 @@ CreateTupleDescCopyConstr(TupleDesc tupdesc) - - desc->tdtypeid = tupdesc->tdtypeid; - desc->tdtypmod = tupdesc->tdtypmod; -+ desc->tdhassecid = tupdesc->tdhassecid; - - return desc; - } -@@ -314,6 +318,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) - return false; - if (tupdesc1->tdhasoid != tupdesc2->tdhasoid) - return false; -+ if (tupdesc1->tdhassecid != tupdesc2->tdhassecid) -+ return false; - - for (i = 0; i < tupdesc1->natts; i++) - { -diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c -index b0a911e..697946b 100644 ---- a/src/backend/access/heap/heapam.c -+++ b/src/backend/access/heap/heapam.c -@@ -54,6 +54,7 @@ - #include "catalog/namespace.h" - #include "miscadmin.h" - #include "pgstat.h" -+#include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/freespace.h" - #include "storage/lmgr.h" -@@ -2016,6 +2017,12 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid, - Oid - simple_heap_insert(Relation relation, HeapTuple tup) - { -+ /* -+ * SELinux assigns default security label for the tuple, -+ * but does not check permissions to the internal operations. -+ */ -+ sepgsqlHeapTupleInsert(relation, tup, true); -+ - return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL); - } - -@@ -2558,6 +2565,11 @@ l2: - Assert(!(newtup->t_data->t_infomask & HEAP_HASOID)); - } - -+ /* Preserve SecurityId, if not changed */ -+ if (HeapTupleHasSecid(newtup) && -+ !OidIsValid(HeapTupleGetSecid(newtup))) -+ HeapTupleSetSecid(newtup, HeapTupleGetSecid(&oldtup)); -+ - newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK); - newtup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK); - newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED); -@@ -3499,6 +3511,8 @@ heap_inplace_update(Relation relation, HeapTuple tuple) - memcpy((char *) htup + htup->t_hoff, - (char *) tuple->t_data + tuple->t_data->t_hoff, - newlen); -+ if (HeapTupleHeaderGetSecid(htup) != HeapTupleGetSecid(tuple)) -+ HeapTupleHeaderSetSecid(htup, HeapTupleGetSecid(tuple)); - - MarkBufferDirty(buffer); - -diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c -index 7b2ebe5..6179e52 100644 ---- a/src/backend/access/heap/tuptoaster.c -+++ b/src/backend/access/heap/tuptoaster.c -@@ -591,6 +591,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, - hoff += BITMAPLEN(numAttrs); - if (newtup->t_data->t_infomask & HEAP_HASOID) - hoff += sizeof(Oid); -+ if (HeapTupleHasSecid(newtup)) -+ hoff += sizeof(Oid); - hoff = MAXALIGN(hoff); - Assert(hoff == newtup->t_data->t_hoff); - /* now convert to a limit on the tuple data size */ -@@ -864,6 +866,8 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup, - new_len += BITMAPLEN(numAttrs); - if (olddata->t_infomask & HEAP_HASOID) - new_len += sizeof(Oid); -+ if (HeapTupleHeaderHasSecid(olddata)) -+ new_len += sizeof(Oid); - new_len = MAXALIGN(new_len); - Assert(new_len == olddata->t_hoff); - new_data_len = heap_compute_data_size(tupleDesc, -@@ -1015,6 +1019,8 @@ toast_flatten_tuple_attribute(Datum value, - new_len += BITMAPLEN(numAttrs); - if (olddata->t_infomask & HEAP_HASOID) - new_len += sizeof(Oid); -+ if (HeapTupleHeaderHasSecid(olddata)) -+ new_len += sizeof(Oid); - new_len = MAXALIGN(new_len); - Assert(new_len == olddata->t_hoff); - new_data_len = heap_compute_data_size(tupleDesc, -@@ -1213,6 +1219,12 @@ toast_save_datum(Relation rel, Datum value, int options) - memcpy(VARDATA(&chunk_data), data_p, chunk_size); - toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull); - -+ /* -+ * NOTE: SE-PostgreSQL does not assign any security label -+ * for tuples within the TOASTVALUE relation, so we omit -+ * to put sepgsqlHeapTupleInsert() hook here. -+ */ -+ - heap_insert(toastrel, toasttup, mycid, options, NULL); - - /* -diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c -index abf1ca1..e6db40a 100644 ---- a/src/backend/access/transam/xact.c -+++ b/src/backend/access/transam/xact.c -@@ -36,6 +36,8 @@ - #include "libpq/be-fsstubs.h" - #include "miscadmin.h" - #include "pgstat.h" -+#include "security/rowlevel.h" -+#include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/fd.h" - #include "storage/lmgr.h" -@@ -140,6 +142,8 @@ typedef struct TransactionStateData - Oid prevUser; /* previous CurrentUserId setting */ - int prevSecContext; /* previous SecurityRestrictionContext */ - bool prevXactReadOnly; /* entry-time xact r/o state */ -+ char *prevSecLabel; /* previous security label of client */ -+ int prevRowlv; /* previous Row-level control behavior */ - struct TransactionStateData *parent; /* back link to parent */ - } TransactionStateData; - -@@ -168,6 +172,8 @@ static TransactionStateData TopTransactionStateData = { - InvalidOid, /* previous CurrentUserId setting */ - 0, /* previous SecurityRestrictionContext */ - false, /* entry-time xact r/o state */ -+ NULL, /* previous security label of client */ -+ ROWLV_FILTER_MODE, /* previous Row-level control behavior */ - NULL /* link to parent state block */ - }; - -@@ -1527,6 +1533,9 @@ StartTransaction(void) - /* SecurityRestrictionContext should never be set outside a transaction */ - Assert(s->prevSecContext == 0); - -+ s->prevSecLabel = sepgsqlGetClientLabel(); -+ s->prevRowlv = rowlvGetPerformingMode(); -+ - /* - * initialize other subsystems for new transaction - */ -@@ -2031,6 +2040,12 @@ AbortTransaction(void) - SetUserIdAndSecContext(s->prevUser, s->prevSecContext); - - /* -+ * Reset SELinux features -+ */ -+ sepgsqlSetClientLabel(s->prevSecLabel); -+ rowlvSetPerformingMode(s->prevRowlv); -+ -+ /* - * do abort processing - */ - AfterTriggerEndXact(false); -@@ -3874,6 +3889,12 @@ AbortSubTransaction(void) - SetUserIdAndSecContext(s->prevUser, s->prevSecContext); - - /* -+ * Reset SELinux features -+ */ -+ sepgsqlSetClientLabel(s->prevSecLabel); -+ rowlvSetPerformingMode(s->prevRowlv); -+ -+ /* - * We can skip all this stuff if the subxact failed before creating a - * ResourceOwner... - */ -@@ -4015,6 +4036,8 @@ PushTransaction(void) - s->blockState = TBLOCK_SUBBEGIN; - GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext); - s->prevXactReadOnly = XactReadOnly; -+ s->prevSecLabel = sepgsqlGetClientLabel(); -+ s->prevRowlv = rowlvGetPerformingMode(); - - CurrentTransactionState = s; - -diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y -index 8d3f6c2..8700483 100644 ---- a/src/backend/bootstrap/bootparse.y -+++ b/src/backend/bootstrap/bootparse.y -@@ -42,6 +42,7 @@ - #include "nodes/pg_list.h" - #include "nodes/primnodes.h" - #include "rewrite/prs2lock.h" -+#include "security/sepgsql.h" - #include "storage/block.h" - #include "storage/fd.h" - #include "storage/ipc.h" -@@ -211,6 +212,13 @@ Boot_CreateStmt: - else - { - Oid id; -+ Oid *secLabels = -+ sepgsql_relation_create(LexIDStr($5), -+ RELKIND_RELATION, -+ tupdesc, -+ PG_CATALOG_NAMESPACE, -+ NULL, NIL, -+ false, false); - - id = heap_create_with_catalog(LexIDStr($5), - PG_CATALOG_NAMESPACE, -@@ -225,7 +233,8 @@ Boot_CreateStmt: - 0, - ONCOMMIT_NOOP, - (Datum) 0, -- true); -+ true, -+ secLabels); - elog(DEBUG4, "relation created with oid %u", id); - } - do_end(); -diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c -index 13d5bcb..0de0c85 100644 ---- a/src/backend/bootstrap/bootstrap.c -+++ b/src/backend/bootstrap/bootstrap.c -@@ -26,12 +26,14 @@ - #include "access/xact.h" - #include "bootstrap/bootstrap.h" - #include "catalog/index.h" -+#include "catalog/pg_security.h" - #include "catalog/pg_type.h" - #include "libpq/pqsignal.h" - #include "miscadmin.h" - #include "nodes/makefuncs.h" - #include "postmaster/bgwriter.h" - #include "postmaster/walwriter.h" -+#include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/ipc.h" - #include "storage/proc.h" -@@ -338,6 +340,11 @@ AuxiliaryProcessMain(int argc, char *argv[]) - case WalWriterProcess: - statmsg = "wal writer process"; - break; -+#ifdef HAVE_SELINUX -+ case SelinuxReceiverProcess: -+ statmsg = "selinux netlink receiver"; -+ break; -+#endif - default: - statmsg = "??? process"; - break; -@@ -430,6 +437,12 @@ AuxiliaryProcessMain(int argc, char *argv[]) - WalWriterMain(); - proc_exit(1); /* should never return */ - -+#ifdef HAVE_SELINUX -+ case SelinuxReceiverProcess: -+ sepgsqlReceiverMain(); -+ proc_exit(1); /* should nener return */ -+#endif -+ - default: - elog(PANIC, "unrecognized process type: %d", auxType); - proc_exit(1); -@@ -497,6 +510,11 @@ BootstrapModeMain(void) - */ - boot_yyparse(); - -+ /* -+ * SELinux initial labeling -+ */ -+ sepgsqlPostBootstrapingMode(); -+ - /* Perform a checkpoint to ensure everything's down to disk */ - SetProcessingMode(NormalProcessing); - CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE); -@@ -794,6 +812,8 @@ InsertOneTuple(Oid objectid) - tupDesc = CreateTupleDesc(numattr, - RelationGetForm(boot_reldesc)->relhasoids, - attrtypes); -+ tupDesc->tdhassecid = RelationGetDescr(boot_reldesc)->tdhassecid; -+ - tuple = heap_form_tuple(tupDesc, values, Nulls); - if (objectid != (Oid) 0) - HeapTupleSetOid(tuple, objectid); -diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile -index 3644ca6..632e3ed 100644 ---- a/src/backend/catalog/Makefile -+++ b/src/backend/catalog/Makefile -@@ -13,7 +13,7 @@ include $(top_builddir)/src/Makefile.global - OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ - pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o pg_enum.o \ - pg_inherits.o pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o \ -- pg_shdepend.o pg_type.o storage.o toasting.o -+ pg_security.o pg_shdepend.o pg_type.o storage.o toasting.o - - BKIFILES = postgres.bki postgres.description postgres.shdescription - -@@ -34,7 +34,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ - pg_cast.h pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \ - pg_database.h pg_tablespace.h pg_pltemplate.h \ - pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ -- pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ -+ pg_security.h pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ - pg_ts_parser.h pg_ts_template.h \ - pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ - toasting.h indexing.h \ -diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c -index 1be417c..6139e4b 100644 ---- a/src/backend/catalog/aclchk.c -+++ b/src/backend/catalog/aclchk.c -@@ -37,6 +37,7 @@ - #include "catalog/pg_operator.h" - #include "catalog/pg_opfamily.h" - #include "catalog/pg_proc.h" -+#include "catalog/pg_security.h" - #include "catalog/pg_tablespace.h" - #include "catalog/pg_type.h" - #include "catalog/pg_ts_config.h" -@@ -45,6 +46,7 @@ - #include "foreign/foreign.h" - #include "miscadmin.h" - #include "parser/parse_func.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/fmgroids.h" - #include "utils/lsyscache.h" -@@ -699,6 +701,12 @@ expand_all_col_privileges(Oid table_oid, Form_pg_class classForm, - if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids) - continue; - -+ /* Skip OID column, if it doesn't exist */ -+ if (curr_att == SecurityAttributeNumber && -+ (classForm->relkind != RELKIND_RELATION || -+ table_oid == SecurityRelationId)) -+ continue; -+ - /* Views don't have any system columns at all */ - if (classForm->relkind == RELKIND_VIEW && curr_att < 0) - continue; -@@ -801,6 +809,8 @@ ExecGrant_Attribute(InternalGrant *istmt, Oid relOid, const char *relname, - relOid, grantorId, ACL_KIND_COLUMN, - relname, attnum, - NameStr(pg_attribute_tuple->attname)); -+ /* SELinux checks */ -+ sepgsql_attribute_grant(relOid, attnum); - - /* - * Generate new ACL. -@@ -1056,6 +1066,8 @@ ExecGrant_Relation(InternalGrant *istmt) - ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS, - NameStr(pg_class_tuple->relname), - 0, NULL); -+ /* SELinux checks */ -+ sepgsql_relation_grant(relOid); - - /* - * Generate new ACL. -@@ -1244,6 +1256,8 @@ ExecGrant_Database(InternalGrant *istmt) - datId, grantorId, ACL_KIND_DATABASE, - NameStr(pg_database_tuple->datname), - 0, NULL); -+ /* SELinux permission checks */ -+ sepgsql_database_grant(datId); - - /* - * Generate new ACL. -@@ -1362,6 +1376,8 @@ ExecGrant_Fdw(InternalGrant *istmt) - fdwid, grantorId, ACL_KIND_FDW, - NameStr(pg_fdw_tuple->fdwname), - 0, NULL); -+ /* SELinux permission checks */ -+ sepgsql_fdw_grant(fdwid); - - /* - * Generate new ACL. -@@ -1481,6 +1497,8 @@ ExecGrant_ForeignServer(InternalGrant *istmt) - srvid, grantorId, ACL_KIND_FOREIGN_SERVER, - NameStr(pg_server_tuple->srvname), - 0, NULL); -+ /* SELinux checks */ -+ sepgsql_foreign_server_grant(srvid); - - /* - * Generate new ACL. -@@ -1599,6 +1617,8 @@ ExecGrant_Function(InternalGrant *istmt) - funcId, grantorId, ACL_KIND_PROC, - NameStr(pg_proc_tuple->proname), - 0, NULL); -+ /* SELinux: db_procedure:{setattr} */ -+ sepgsql_proc_grant(funcId); - - /* - * Generate new ACL. -@@ -1723,6 +1743,8 @@ ExecGrant_Language(InternalGrant *istmt) - langId, grantorId, ACL_KIND_LANGUAGE, - NameStr(pg_language_tuple->lanname), - 0, NULL); -+ /* SELinux checks */ -+ sepgsql_language_grant(langId); - - /* - * Generate new ACL. -@@ -1974,6 +1996,9 @@ ExecGrant_Namespace(InternalGrant *istmt) - NameStr(pg_namespace_tuple->nspname), - 0, NULL); - -+ /* SELinux: db_schema:{setattr} */ -+ sepgsql_schema_grant(nspid); -+ - /* - * Generate new ACL. - * -diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c -index d168694..efb4333 100644 ---- a/src/backend/catalog/catalog.c -+++ b/src/backend/catalog/catalog.c -@@ -31,6 +31,7 @@ - #include "catalog/pg_database.h" - #include "catalog/pg_namespace.h" - #include "catalog/pg_pltemplate.h" -+#include "catalog/pg_security.h" - #include "catalog/pg_shdepend.h" - #include "catalog/pg_shdescription.h" - #include "catalog/pg_tablespace.h" -@@ -304,6 +305,7 @@ IsSharedRelation(Oid relationId) - relationId == AuthMemRelationId || - relationId == DatabaseRelationId || - relationId == PLTemplateRelationId || -+ relationId == SecurityRelationId || - relationId == SharedDescriptionRelationId || - relationId == SharedDependRelationId || - relationId == TableSpaceRelationId) -@@ -316,6 +318,8 @@ IsSharedRelation(Oid relationId) - relationId == DatabaseNameIndexId || - relationId == DatabaseOidIndexId || - relationId == PLTemplateNameIndexId || -+ relationId == SecuritySecidIndexId || -+ relationId == SecuritySecattrIndexId || - relationId == SharedDescriptionObjIndexId || - relationId == SharedDependDependerIndexId || - relationId == SharedDependReferenceIndexId || -@@ -327,6 +331,8 @@ IsSharedRelation(Oid relationId) - relationId == PgAuthidToastIndex || - relationId == PgDatabaseToastTable || - relationId == PgDatabaseToastIndex || -+ relationId == PgSecurityToastTable || -+ relationId == PgSecurityToastIndex || - relationId == PgShdescriptionToastTable || - relationId == PgShdescriptionToastIndex) - return true; -diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c -index 416c149..4e447f0 100644 ---- a/src/backend/catalog/dependency.c -+++ b/src/backend/catalog/dependency.c -@@ -64,6 +64,7 @@ - #include "nodes/nodeFuncs.h" - #include "parser/parsetree.h" - #include "rewrite/rewriteRemove.h" -+#include "security/sepgsql.h" - #include "storage/lmgr.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -162,7 +163,8 @@ static void reportDependentObjects(const ObjectAddresses *targetObjects, - DropBehavior behavior, - int msglevel, - const ObjectAddress *origObject); --static void deleteOneObject(const ObjectAddress *object, Relation depRel); -+static void deleteOneObject(const ObjectAddress *object, -+ Relation depRel, bool permission); - static void doDeletion(const ObjectAddress *object); - static void AcquireDeletionLock(const ObjectAddress *object); - static void ReleaseDeletionLock(const ObjectAddress *object); -@@ -194,9 +196,9 @@ static void getOpFamilyDescription(StringInfo buffer, Oid opfid); - * are variants on the same theme; if you change anything here you'll likely - * need to fix them too. - */ --void --performDeletion(const ObjectAddress *object, -- DropBehavior behavior) -+static void -+performDeletionInternal(const ObjectAddress *object, -+ DropBehavior behavior, bool permission) - { - Relation depRel; - ObjectAddresses *targetObjects; -@@ -242,7 +244,7 @@ performDeletion(const ObjectAddress *object, - { - ObjectAddress *thisobj = targetObjects->refs + i; - -- deleteOneObject(thisobj, depRel); -+ deleteOneObject(thisobj, depRel, permission); - } - - /* And clean up */ -@@ -251,6 +253,18 @@ performDeletion(const ObjectAddress *object, - heap_close(depRel, RowExclusiveLock); - } - -+void -+performDeletion(const ObjectAddress *object, DropBehavior behavior) -+{ -+ performDeletionInternal(object, behavior, true); -+} -+ -+void -+performDeletionNoPerms(const ObjectAddress *object, DropBehavior behavior) -+{ -+ performDeletionInternal(object, behavior, false); -+} -+ - /* - * performMultipleDeletions: Similar to performDeletion, but act on multiple - * objects at once. -@@ -324,7 +338,8 @@ performMultipleDeletions(const ObjectAddresses *objects, - { - ObjectAddress *thisobj = targetObjects->refs + i; - -- deleteOneObject(thisobj, depRel); -+ /* currently, all the caller path need permission checks */ -+ deleteOneObject(thisobj, depRel, true); - } - - /* And clean up */ -@@ -395,7 +410,7 @@ deleteWhatDependsOn(const ObjectAddress *object, - if (thisextra->flags & DEPFLAG_ORIGINAL) - continue; - -- deleteOneObject(thisobj, depRel); -+ deleteOneObject(thisobj, depRel, false); - } - - /* And clean up */ -@@ -945,13 +960,17 @@ reportDependentObjects(const ObjectAddresses *targetObjects, - * depRel is the already-open pg_depend relation. - */ - static void --deleteOneObject(const ObjectAddress *object, Relation depRel) -+deleteOneObject(const ObjectAddress *object, Relation depRel, bool permission) - { - ScanKeyData key[3]; - int nkeys; - SysScanDesc scan; - HeapTuple tup; - -+ /* SELinux checks db_xxx:{drop}, if necessary */ -+ if (permission) -+ sepgsql_sysobj_drop(object); -+ - /* - * First remove any pg_depend records that link from this object to - * others. (Any records linking to this object should be gone already.) -diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c -index 7557400..b5161c7 100644 ---- a/src/backend/catalog/heap.c -+++ b/src/backend/catalog/heap.c -@@ -43,6 +43,7 @@ - #include "catalog/pg_constraint.h" - #include "catalog/pg_inherits.h" - #include "catalog/pg_namespace.h" -+#include "catalog/pg_security.h" - #include "catalog/pg_statistic.h" - #include "catalog/pg_tablespace.h" - #include "catalog/pg_type.h" -@@ -56,6 +57,7 @@ - #include "parser/parse_coerce.h" - #include "parser/parse_expr.h" - #include "parser/parse_relation.h" -+#include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/freespace.h" - #include "storage/smgr.h" -@@ -74,7 +76,8 @@ static void AddNewRelationTuple(Relation pg_class_desc, - Oid new_rel_oid, Oid new_type_oid, - Oid relowner, - char relkind, -- Datum reloptions); -+ Datum reloptions, -+ Oid *secLabels); - static Oid AddNewRelationType(const char *typeName, - Oid typeNamespace, - Oid new_rel_oid, -@@ -158,7 +161,16 @@ static FormData_pg_attribute a7 = { - true, 'p', 'i', true, false, false, true, 0, {0} - }; - --static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; -+/* -+ * System columns for enhanced security features -+ */ -+static FormData_pg_attribute a8 = { -+ 0, {SecurityAttributeName}, TEXTOID, 0, -1, -+ SecurityAttributeNumber, 0, -1, -1, -+ false, 'x', 'i', true, false, false, true, 0, {0} -+}; -+ -+static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8}; - - /* - * This function returns a Form_pg_attribute pointer for a system attribute. -@@ -198,6 +210,17 @@ SystemAttributeByName(const char *attname, bool relhasoids) - return NULL; - } - -+/* -+ * If the given attribute number is writable, returns true. -+ */ -+bool -+SystemAttributeIsWritable(AttrNumber attnum) -+{ -+ if (attnum == SecurityAttributeNumber) -+ return true; -+ -+ return false; -+} - - /* ---------------------------------------------------------------- - * XXX END OF UGLY HARD CODED BADNESS XXX -@@ -293,6 +316,11 @@ heap_create(const char *relname, - relid, - reltablespace, - shared_relation); -+ /* -+ * Does the relation have security attribute? -+ */ -+ RelationGetDescr(rel)->tdhassecid -+ = securityTupleDescHasSecid(relid, relkind); - - /* - * Have the storage manager create the relation's disk file, if needed. -@@ -487,7 +515,8 @@ CheckAttributeType(const char *attname, Oid atttypid) - void - InsertPgAttributeTuple(Relation pg_attribute_rel, - Form_pg_attribute new_attribute, -- CatalogIndexState indstate) -+ CatalogIndexState indstate, -+ Oid new_att_secid) - { - Datum values[Natts_pg_attribute]; - bool nulls[Natts_pg_attribute]; -@@ -520,6 +549,9 @@ InsertPgAttributeTuple(Relation pg_attribute_rel, - - tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls); - -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, new_att_secid); -+ - /* finally insert the new tuple, update the indexes, and clean up */ - simple_heap_insert(pg_attribute_rel, tup); - -@@ -543,13 +575,15 @@ AddNewAttributeTuples(Oid new_rel_oid, - TupleDesc tupdesc, - char relkind, - bool oidislocal, -- int oidinhcount) -+ int oidinhcount, -+ Oid *secLabels) - { - Form_pg_attribute attr; - int i; - Relation rel; - CatalogIndexState indstate; - int natts = tupdesc->natts; -+ Oid new_att_secid; - ObjectAddress myself, - referenced; - -@@ -573,7 +607,11 @@ AddNewAttributeTuples(Oid new_rel_oid, - attr->attstattarget = -1; - attr->attcacheoff = -1; - -- InsertPgAttributeTuple(rel, attr, indstate); -+ /* Security label of the column */ -+ new_att_secid = (!secLabels ? InvalidOid -+ : secLabels[i - FirstLowInvalidHeapAttributeNumber]); -+ -+ InsertPgAttributeTuple(rel, attr, indstate, new_att_secid); - - /* Add dependency info */ - myself.classId = RelationRelationId; -@@ -601,6 +639,12 @@ AddNewAttributeTuples(Oid new_rel_oid, - SysAtt[i]->attnum == ObjectIdAttributeNumber) - continue; - -+ /* skip Secid where appropriate */ -+ if (SysAtt[i]->attnum == SecurityAttributeNumber && -+ (relkind != RELKIND_RELATION || -+ new_rel_oid == SecurityRelationId)) -+ continue; -+ - memcpy(&attStruct, (char *) SysAtt[i], sizeof(FormData_pg_attribute)); - - /* Fill in the correct relation OID in the copied tuple */ -@@ -613,7 +657,11 @@ AddNewAttributeTuples(Oid new_rel_oid, - attStruct.attinhcount = oidinhcount; - } - -- InsertPgAttributeTuple(rel, &attStruct, indstate); -+ /* Security label of the system column */ -+ new_att_secid = (!secLabels ? InvalidOid -+ : secLabels[SysAtt[i]->attnum - FirstLowInvalidHeapAttributeNumber]); -+ -+ InsertPgAttributeTuple(rel, &attStruct, indstate, new_att_secid); - } - } - -@@ -641,7 +689,8 @@ void - InsertPgClassTuple(Relation pg_class_desc, - Relation new_rel_desc, - Oid new_rel_oid, -- Datum reloptions) -+ Datum reloptions, -+ Oid new_rel_secid) - { - Form_pg_class rd_rel = new_rel_desc->rd_rel; - Datum values[Natts_pg_class]; -@@ -690,6 +739,9 @@ InsertPgClassTuple(Relation pg_class_desc, - */ - HeapTupleSetOid(tup, new_rel_oid); - -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, new_rel_secid); -+ - /* finally insert the new tuple, update the indexes, and clean up */ - simple_heap_insert(pg_class_desc, tup); - -@@ -712,9 +764,11 @@ AddNewRelationTuple(Relation pg_class_desc, - Oid new_type_oid, - Oid relowner, - char relkind, -- Datum reloptions) -+ Datum reloptions, -+ Oid *secLabels) - { - Form_pg_class new_rel_reltup; -+ Oid new_rel_secid = InvalidOid; - - /* - * first we update some of the information in our uncataloged relation's -@@ -771,8 +825,12 @@ AddNewRelationTuple(Relation pg_class_desc, - - new_rel_desc->rd_att->tdtypeid = new_type_oid; - -+ if (secLabels) -+ new_rel_secid = secLabels[0]; -+ - /* Now build and insert the tuple */ -- InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions); -+ InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, -+ reloptions, new_rel_secid); - } - - -@@ -843,7 +901,8 @@ heap_create_with_catalog(const char *relname, - int oidinhcount, - OnCommitAction oncommit, - Datum reloptions, -- bool allow_system_table_mods) -+ bool allow_system_table_mods, -+ Oid *secLabels) - { - Relation pg_class_desc; - Relation new_rel_desc; -@@ -1019,13 +1078,14 @@ heap_create_with_catalog(const char *relname, - new_type_oid, - ownerid, - relkind, -- reloptions); -+ reloptions, -+ secLabels); - - /* - * now add tuples to pg_attribute for the attributes in our new relation. - */ - AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind, -- oidislocal, oidinhcount); -+ oidislocal, oidinhcount, secLabels); - - /* - * Make a dependency link to force the relation to be deleted if its -@@ -1484,6 +1544,11 @@ heap_drop_with_catalog(Oid relid) - * delete relation tuple - */ - DeleteRelationTuple(relid); -+ -+ /* -+ * delete orphan pg_security entries -+ */ -+ securityReclaimOnDropTable(relid); - } - - -diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c -index ad679ea..828d51a 100644 ---- a/src/backend/catalog/index.c -+++ b/src/backend/catalog/index.c -@@ -48,6 +48,7 @@ - #include "nodes/nodeFuncs.h" - #include "optimizer/clauses.h" - #include "optimizer/var.h" -+#include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/lmgr.h" - #include "storage/procarray.h" -@@ -352,7 +353,8 @@ AppendAttributeTuples(Relation indexRelation, int numatts) - Assert(indexTupDesc->attrs[i]->attnum == i + 1); - Assert(indexTupDesc->attrs[i]->attcacheoff == -1); - -- InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], indstate); -+ InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], -+ indstate, InvalidOid); - } - - CatalogCloseIndexes(indstate); -@@ -653,7 +655,7 @@ index_create(Oid heapRelationId, - */ - InsertPgClassTuple(pg_class, indexRelation, - RelationGetRelid(indexRelation), -- reloptions); -+ reloptions, InvalidOid); - - /* done with pg_class */ - heap_close(pg_class, RowExclusiveLock); -diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c -index 2b0cb35..64de050 100644 ---- a/src/backend/catalog/namespace.c -+++ b/src/backend/catalog/namespace.c -@@ -39,6 +39,7 @@ - #include "miscadmin.h" - #include "nodes/makefuncs.h" - #include "parser/parse_func.h" -+#include "security/sepgsql.h" - #include "storage/backendid.h" - #include "storage/ipc.h" - #include "utils/acl.h" -@@ -2105,7 +2106,10 @@ LookupExplicitNamespace(const char *nspname) - if (strcmp(nspname, "pg_temp") == 0) - { - if (OidIsValid(myTempNamespace)) -+ { -+ sepgsql_schema_search(myTempNamespace, true); - return myTempNamespace; -+ } - - /* - * Since this is used only for looking up existing objects, there is -@@ -2127,6 +2131,7 @@ LookupExplicitNamespace(const char *nspname) - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - nspname); -+ sepgsql_schema_search(namespaceId, true); - - return namespaceId; - } -@@ -2722,7 +2727,8 @@ recomputeNamespacePath(void) - if (OidIsValid(namespaceId) && - !list_member_oid(oidlist, namespaceId) && - pg_namespace_aclcheck(namespaceId, roleid, -- ACL_USAGE) == ACLCHECK_OK) -+ ACL_USAGE) == ACLCHECK_OK && -+ sepgsql_schema_search(namespaceId, false)) - oidlist = lappend_oid(oidlist, namespaceId); - } - } -@@ -2731,7 +2737,8 @@ recomputeNamespacePath(void) - /* pg_temp --- substitute temp namespace, if any */ - if (OidIsValid(myTempNamespace)) - { -- if (!list_member_oid(oidlist, myTempNamespace)) -+ if (!list_member_oid(oidlist, myTempNamespace) && -+ sepgsql_schema_search(myTempNamespace, false)) - oidlist = lappend_oid(oidlist, myTempNamespace); - } - else -@@ -2750,7 +2757,8 @@ recomputeNamespacePath(void) - if (OidIsValid(namespaceId) && - !list_member_oid(oidlist, namespaceId) && - pg_namespace_aclcheck(namespaceId, roleid, -- ACL_USAGE) == ACLCHECK_OK) -+ ACL_USAGE) == ACLCHECK_OK && -+ sepgsql_schema_search(namespaceId, false)) - oidlist = lappend_oid(oidlist, namespaceId); - } - } -@@ -2816,6 +2824,7 @@ InitTempTableNamespace(void) - char namespaceName[NAMEDATALEN]; - Oid namespaceId; - Oid toastspaceId; -+ Oid nspsecid; - - Assert(!OidIsValid(myTempNamespace)); - -@@ -2836,6 +2845,9 @@ InitTempTableNamespace(void) - errmsg("permission denied to create temporary tables in database \"%s\"", - get_database_name(MyDatabaseId)))); - -+ /* SELinux checks permission to create temp schema */ -+ nspsecid = sepgsql_schema_create(namespaceName, true, NULL); -+ - snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId); - - namespaceId = GetSysCacheOid(NAMESPACENAME, -@@ -2851,7 +2863,9 @@ InitTempTableNamespace(void) - * temp tables. This works because the places that access the temp - * namespace for my own backend skip permissions checks on it. - */ -- namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); -+ namespaceId = NamespaceCreate(namespaceName, -+ BOOTSTRAP_SUPERUSERID, -+ nspsecid); - /* Advance command counter to make namespace visible */ - CommandCounterIncrement(); - } -@@ -2877,7 +2891,9 @@ InitTempTableNamespace(void) - 0, 0, 0); - if (!OidIsValid(toastspaceId)) - { -- toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); -+ toastspaceId = NamespaceCreate(namespaceName, -+ BOOTSTRAP_SUPERUSERID, -+ nspsecid); - /* Advance command counter to make namespace visible */ - CommandCounterIncrement(); - } -@@ -3030,6 +3046,13 @@ RemoveTempRelations(Oid tempNamespaceId) - object.objectId = tempNamespaceId; - object.objectSubId = 0; - -+ /* -+ * TODO: -+ * SELinux should not check db_xxx:{drop} permission during cleaning -+ * up all the temporary objects. It may be necessary a bool argument -+ * to control MAC permission check on deleteOneObject() called from -+ * deleteWhatDependsOn() and so on. -+ */ - deleteWhatDependsOn(&object, false); - } - -diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c -index 845322e..cc1c59a 100644 ---- a/src/backend/catalog/pg_aggregate.c -+++ b/src/backend/catalog/pg_aggregate.c -@@ -231,7 +231,8 @@ AggregateCreate(const char *aggName, - NIL, /* parameterDefaults */ - PointerGetDatum(NULL), /* proconfig */ - 1, /* procost */ -- 0); /* prorows */ -+ 0, /* prorows */ -+ NULL); /* proseclabel*/ - - /* - * Okay to create the pg_aggregate entry. -diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c -index d4a8183..30f2604 100644 ---- a/src/backend/catalog/pg_conversion.c -+++ b/src/backend/catalog/pg_conversion.c -@@ -40,7 +40,7 @@ Oid - ConversionCreate(const char *conname, Oid connamespace, - Oid conowner, - int32 conforencoding, int32 contoencoding, -- Oid conproc, bool def) -+ Oid conproc, Oid consecid, bool def) - { - int i; - Relation rel; -@@ -104,6 +104,8 @@ ConversionCreate(const char *conname, Oid connamespace, - values[Anum_pg_conversion_condefault - 1] = BoolGetDatum(def); - - tup = heap_form_tuple(tupDesc, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, consecid); - - /* insert a new tuple */ - oid = simple_heap_insert(rel, tup); -diff --git a/src/backend/catalog/pg_largeobject.c b/src/backend/catalog/pg_largeobject.c -index 54d992f..67faa30 100644 ---- a/src/backend/catalog/pg_largeobject.c -+++ b/src/backend/catalog/pg_largeobject.c -@@ -25,6 +25,7 @@ - #include "catalog/pg_largeobject_metadata.h" - #include "catalog/toasting.h" - #include "miscadmin.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -40,7 +41,7 @@ - * will appear to exist with size 0. - */ - Oid --LargeObjectCreate(Oid loid) -+LargeObjectCreate(Oid loid, Oid secid) - { - Relation pg_lo_meta; - HeapTuple ntup; -@@ -65,6 +66,8 @@ LargeObjectCreate(Oid loid) - values, nulls); - if (OidIsValid(loid)) - HeapTupleSetOid(ntup, loid); -+ if (HeapTupleHasSecid(ntup)) -+ HeapTupleSetSecid(ntup, secid); - - loid_new = simple_heap_insert(pg_lo_meta, ntup); - Assert(!OidIsValid(loid) || loid == loid_new); -@@ -205,6 +208,9 @@ LargeObjectAlterOwner(Oid loid, Oid newOwnerId) - - /* Must be able to become new owner */ - check_is_member_of_role(GetUserId(), newOwnerId); -+ -+ /* SELinux: db_blob:{setattr} */ -+ sepgsql_largeobject_alter(loid); - } - - memset(values, 0, sizeof(values)); -diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c -index 8fac0b6..62802a9 100644 ---- a/src/backend/catalog/pg_namespace.c -+++ b/src/backend/catalog/pg_namespace.c -@@ -28,7 +28,7 @@ - * --------------- - */ - Oid --NamespaceCreate(const char *nspName, Oid ownerId) -+NamespaceCreate(const char *nspName, Oid ownerId, Oid nspsecid) - { - Relation nspdesc; - HeapTuple tup; -@@ -66,6 +66,8 @@ NamespaceCreate(const char *nspName, Oid ownerId) - tupDesc = nspdesc->rd_att; - - tup = heap_form_tuple(tupDesc, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, nspsecid); - - nspoid = simple_heap_insert(nspdesc, tup); - Assert(OidIsValid(nspoid)); -diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c -index af307b7..6fe025c 100644 ---- a/src/backend/catalog/pg_operator.c -+++ b/src/backend/catalog/pg_operator.c -@@ -28,6 +28,7 @@ - #include "catalog/pg_type.h" - #include "miscadmin.h" - #include "parser/parse_oper.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -@@ -204,6 +205,7 @@ OperatorShellMake(const char *operatorName, - { - Relation pg_operator_desc; - Oid operatorObjectId; -+ Oid secid; - int i; - HeapTuple tup; - Datum values[Natts_pg_operator]; -@@ -220,6 +222,10 @@ OperatorShellMake(const char *operatorName, - errmsg("\"%s\" is not a valid operator name", - operatorName))); - -+ /* SELinux permission check */ -+ secid = sepgsql_operator_create(operatorName, InvalidOid, -+ operatorNamespace, -+ InvalidOid, InvalidOid, InvalidOid); - /* - * initialize our *nulls and *values arrays - */ -@@ -260,6 +266,8 @@ OperatorShellMake(const char *operatorName, - * create a new operator tuple - */ - tup = heap_form_tuple(tupDesc, values, nulls); -+ if (HeapTupleHasSecid(tup) && OidIsValid(secid)) -+ HeapTupleSetSecid(tup, secid); - - /* - * insert our "shell" operator tuple -@@ -347,6 +355,7 @@ OperatorCreate(const char *operatorName, - bool selfCommutator = false; - NameData oname; - TupleDesc tupDesc; -+ Oid secid; - int i; - - /* -@@ -476,6 +485,10 @@ OperatorCreate(const char *operatorName, - else - negatorId = InvalidOid; - -+ /* SELinux permission checks */ -+ secid = sepgsql_operator_create(operatorName, operatorObjectId, -+ operatorNamespace, -+ procedureId, restrictionId, joinId); - /* - * set up values in the operator tuple - */ -@@ -523,6 +536,8 @@ OperatorCreate(const char *operatorName, - values, - nulls, - replaces); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, secid); - - simple_heap_update(pg_operator_desc, &tup->t_self, tup); - } -@@ -530,6 +545,8 @@ OperatorCreate(const char *operatorName, - { - tupDesc = pg_operator_desc->rd_att; - tup = heap_form_tuple(tupDesc, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, secid); - - operatorObjectId = simple_heap_insert(pg_operator_desc, tup); - } -diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c -index 2980d2b..55e66e5 100644 ---- a/src/backend/catalog/pg_proc.c -+++ b/src/backend/catalog/pg_proc.c -@@ -29,6 +29,7 @@ - #include "miscadmin.h" - #include "nodes/nodeFuncs.h" - #include "parser/parse_type.h" -+#include "security/sepgsql.h" - #include "tcop/pquery.h" - #include "tcop/tcopprot.h" - #include "utils/acl.h" -@@ -78,7 +79,8 @@ ProcedureCreate(const char *procedureName, - List *parameterDefaults, - Datum proconfig, - float4 procost, -- float4 prorows) -+ float4 prorows, -+ Node *proseclabel) - { - Oid retval; - int parameterCount; -@@ -97,6 +99,7 @@ ProcedureCreate(const char *procedureName, - Datum values[Natts_pg_proc]; - bool replaces[Natts_pg_proc]; - Oid relid; -+ Oid prosecid = InvalidOid; - NameData procname; - TupleDesc tupDesc; - bool is_update; -@@ -344,6 +347,11 @@ ProcedureCreate(const char *procedureName, - ObjectIdGetDatum(procNamespace), - 0); - -+ /* Check permission to create/replace a function */ -+ prosecid = sepgsql_proc_create(procedureName, oldtup, -+ procNamespace, languageObjectId, -+ (DefElem *)proseclabel); -+ - if (HeapTupleIsValid(oldtup)) - { - /* There is one; okay to replace it? */ -@@ -481,6 +489,8 @@ ProcedureCreate(const char *procedureName, - - /* Okay, do it... */ - tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, prosecid); - simple_heap_update(rel, &tup->t_self, tup); - - ReleaseSysCache(oldtup); -@@ -490,6 +500,8 @@ ProcedureCreate(const char *procedureName, - { - /* Creating a new procedure */ - tup = heap_form_tuple(tupDesc, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, prosecid); - simple_heap_insert(rel, tup); - is_update = false; - } -diff --git a/src/backend/catalog/pg_security.c b/src/backend/catalog/pg_security.c -new file mode 100644 -index 0000000..0db05e2 ---- /dev/null -+++ b/src/backend/catalog/pg_security.c -@@ -0,0 +1,483 @@ -+/* -+ * src/backend/catalog/pg_security.c -+ * routines to support security label management -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+#include "postgres.h" -+ -+#include "access/genam.h" -+#include "access/heapam.h" -+#include "access/sysattr.h" -+#include "access/xact.h" -+#include "catalog/catalog.h" -+#include "catalog/indexing.h" -+#include "catalog/pg_proc.h" -+#include "catalog/pg_security.h" -+#include "catalog/pg_type.h" -+#include "executor/spi.h" -+#include "miscadmin.h" -+#include "security/rowlevel.h" -+#include "security/sepgsql.h" -+#include "utils/builtins.h" -+#include "utils/fmgroids.h" -+#include "utils/memutils.h" -+#include "utils/rel.h" -+#include "utils/lsyscache.h" -+#include "utils/syscache.h" -+#include "utils/tqual.h" -+ -+bool -+securityTupleDescHasSecid(Oid relid, char relkind) -+{ -+ return sepgsqlTupleDescHasSecid(relid, relkind); -+} -+ -+/* -+ * securityOnCreateDatabase -+ * copies all the entries refered by source database -+ */ -+void -+securityOnCreateDatabase(Oid src_datid, Oid dst_datid) -+{ -+ Relation rel; -+ ScanKeyData keys[1]; -+ SysScanDesc scan; -+ HeapTuple oldtup, newtup; -+ Datum values[Natts_pg_security]; -+ bool nulls[Natts_pg_security]; -+ bool replaces[Natts_pg_security]; -+ -+ /* Scan all entries with pg_security.datid = src_datid */ -+ ScanKeyInit(&keys[0], -+ Anum_pg_security_datid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(src_datid)); -+ -+ rel = heap_open(SecurityRelationId, RowExclusiveLock); -+ -+ scan = systable_beginscan(rel, SecuritySecidIndexId, true, -+ SnapshotNow, 1, keys); -+ -+ /* pg_security.datid shall be replaced */ -+ memset(values, 0, sizeof(values)); -+ memset(nulls, false, sizeof(nulls)); -+ memset(replaces, false, sizeof(replaces)); -+ -+ values[Anum_pg_security_datid - 1] = ObjectIdGetDatum(dst_datid); -+ replaces[Anum_pg_security_datid - 1] = true; -+ -+ while (HeapTupleIsValid(oldtup = systable_getnext(scan))) -+ { -+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), -+ values, nulls, replaces); -+ simple_heap_insert(rel, newtup); -+ -+ CatalogUpdateIndexes(rel, newtup); -+ -+ heap_freetuple(newtup); -+ } -+ systable_endscan(scan); -+ -+ heap_close(rel, RowExclusiveLock); -+} -+ -+/* -+ * securityOnDropDatabase -+ * drops all the entries refered by dropped database -+ */ -+void -+securityOnDropDatabase(Oid datid) -+{ -+ Relation rel; -+ ScanKeyData keys[1]; -+ SysScanDesc scan; -+ HeapTuple tuple; -+ -+ /* Scan all entries with pg_security.datid = datid */ -+ ScanKeyInit(&keys[0], -+ Anum_pg_security_datid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(datid)); -+ -+ rel = heap_open(SecurityRelationId, RowExclusiveLock); -+ -+ scan = systable_beginscan(rel, SecuritySecidIndexId, true, -+ SnapshotNow, 1, keys); -+ -+ while (HeapTupleIsValid(tuple = systable_getnext(scan))) -+ { -+ simple_heap_delete(rel, &tuple->t_self); -+ } -+ -+ systable_endscan(scan); -+ -+ heap_close(rel, RowExclusiveLock); -+} -+ -+/* -+ * InputSecurityAttr -+ */ -+static Oid -+InputSecurityAttr(Oid relid, const char *secattr) -+{ -+ LOCKMODE lockmode = AccessShareLock; -+ Relation rel; -+ ScanKeyData skey[3]; -+ SysScanDesc scan; -+ HeapTuple tuple; -+ Oid datid; -+ Oid secid; -+ Datum values[Natts_pg_security]; -+ bool nulls[Natts_pg_security]; -+ -+ datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); -+ -+retry: -+ /* -+ * Lookup pg_security catalog first -+ */ -+ rel = heap_open(SecurityRelationId, lockmode); -+ -+ ScanKeyInit(&skey[0], -+ Anum_pg_security_datid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(datid)); -+ ScanKeyInit(&skey[1], -+ Anum_pg_security_relid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(relid)); -+ ScanKeyInit(&skey[2], -+ Anum_pg_security_secattr, -+ BTEqualStrategyNumber, F_TEXTEQ, -+ CStringGetTextDatum(secattr)); -+ -+ scan = systable_beginscan(rel, SecuritySecattrIndexId, true, -+ SnapshotToast, 3, skey); -+ -+ tuple = systable_getnext(scan); -+ if (HeapTupleIsValid(tuple)) -+ { -+ secid = ((Form_pg_security) GETSTRUCT(tuple))->secid; -+ -+ systable_endscan(scan); -+ -+ heap_close(rel, lockmode); -+ -+ return secid; -+ } -+ -+ systable_endscan(scan); -+ -+ /* -+ * If not exist, try to insert a new entry. -+ */ -+ if (lockmode == AccessShareLock) -+ { -+ heap_close(rel, lockmode); -+ -+ lockmode = RowExclusiveLock; -+ -+ goto retry; -+ } -+ -+ memset(nulls, false, sizeof(nulls)); -+ secid = GetNewOidWithIndex(rel, SecuritySecidIndexId, -+ Anum_pg_security_secid); -+ values[Anum_pg_security_secid - 1] = ObjectIdGetDatum(secid); -+ values[Anum_pg_security_datid - 1] = ObjectIdGetDatum(datid); -+ values[Anum_pg_security_relid - 1] = ObjectIdGetDatum(relid); -+ values[Anum_pg_security_secattr - 1] = CStringGetTextDatum(secattr); -+ -+ tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls); -+ -+ simple_heap_insert(rel, tuple); -+ -+ CatalogUpdateIndexes(rel, tuple); -+ -+ heap_close(rel, lockmode); -+ -+ return secid; -+} -+ -+static char * -+OutputSecurityAttr(Oid relid, Oid secid) -+{ -+ Relation rel; -+ ScanKeyData skey[3]; -+ SysScanDesc scan; -+ HeapTuple tuple; -+ Oid datid; -+ char *result = NULL; -+ -+ datid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); -+ -+ /* -+ * Lookup pg_security catalog first -+ */ -+ rel = heap_open(SecurityRelationId, AccessShareLock); -+ -+ ScanKeyInit(&skey[0], -+ Anum_pg_security_secid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(secid)); -+ ScanKeyInit(&skey[1], -+ Anum_pg_security_datid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(datid)); -+ ScanKeyInit(&skey[2], -+ Anum_pg_security_relid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(relid)); -+ -+ scan = systable_beginscan(rel, SecuritySecidIndexId, true, -+ SnapshotToast, 3, skey); -+ -+ tuple = systable_getnext(scan); -+ if (HeapTupleIsValid(tuple)) -+ { -+ Datum datum; -+ bool isnull; -+ -+ datum = heap_getattr(tuple, -+ Anum_pg_security_secattr, -+ RelationGetDescr(rel), &isnull); -+ if (!isnull) -+ result = TextDatumGetCString(datum); -+ } -+ -+ systable_endscan(scan); -+ -+ heap_close(rel, AccessShareLock); -+ -+ return result; -+} -+ -+/* -+ * input/output handler -+ */ -+Oid -+securityRawSecLabelIn(Oid relid, char *seclabel) -+{ -+ seclabel = sepgsqlRawSecLabelIn(seclabel); -+ -+ return InputSecurityAttr(relid, seclabel); -+} -+ -+char * -+securityRawSecLabelOut(Oid relid, Oid secid) -+{ -+ char *seclabel = OutputSecurityAttr(relid, secid); -+ -+ return sepgsqlRawSecLabelOut(seclabel); -+} -+ -+Oid -+securityTransSecLabelIn(Oid relid, char *seclabel) -+{ -+ seclabel = sepgsqlTransSecLabelIn(seclabel); -+ -+ return securityRawSecLabelIn(relid, seclabel); -+} -+ -+char * -+securityTransSecLabelOut(Oid relid, Oid secid) -+{ -+ char *seclabel = securityRawSecLabelOut(relid, secid); -+ -+ return sepgsqlTransSecLabelOut(seclabel); -+} -+ -+/* -+ * Output handler for system columns -+ */ -+Datum -+securitySysattSecLabelOut(Oid relid, HeapTuple tuple) -+{ -+ char *seclabel; -+ -+ seclabel = sepgsqlSysattSecLabelOut(relid, tuple); -+ if (!seclabel) -+ seclabel = "unlabled"; -+ -+ return CStringGetTextDatum(seclabel); -+} -+ -+/* -+ * securityReclaimOnDropTable -+ * drop orphan entries within pg_security on drop table -+ */ -+void -+securityReclaimOnDropTable(Oid relid) -+{ -+ Relation rel; -+ SysScanDesc scan; -+ ScanKeyData key[2]; -+ HeapTuple tuple; -+ Oid database_oid; -+ -+ database_oid = (IsSharedRelation(relid) ? InvalidOid : MyDatabaseId); -+ ScanKeyInit(&key[0], -+ Anum_pg_security_datid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(database_oid)); -+ ScanKeyInit(&key[1], -+ Anum_pg_security_relid, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(relid)); -+ -+ rel = heap_open(SecurityRelationId, RowExclusiveLock); -+ scan = systable_beginscan(rel, SecuritySecattrIndexId, true, -+ SnapshotNow, 2, key); -+ while (HeapTupleIsValid(tuple = systable_getnext(scan))) -+ simple_heap_delete(rel, &tuple->t_self); -+ -+ systable_endscan(scan); -+ -+ heap_close(rel, RowExclusiveLock); -+} -+ -+/* -+ * security_quote_relation -+ * returns palloc'de identifier with explicit namespace -+ */ -+static char * -+security_quote_relation(Oid relid) -+{ -+ Oid nspoid = get_rel_namespace(relid); -+ char *nspname; -+ char *relname; -+ -+ nspname = get_namespace_name(nspoid); -+ relname = get_rel_name(relid); -+ -+ return quote_qualified_identifier(nspname, relname); -+} -+ -+/* -+ * security_reclaim_table -+ * reclaims orphan entries associated to a certain table -+ */ -+static int -+seclabelRelationReclaimExec(Oid relOid) -+{ -+ StringInfoData query; -+ SPIPlanPtr plan; -+ Oid types[2]; -+ Datum values[2]; -+ Oid proc_oid; -+ Oid database_oid; -+ char *relname_full; -+ char *attname_datid; -+ char *attname_relid; -+ char *attname_secid; -+ char *attname_seckind; -+ char *attname_secattr; -+ char *sec_proname; -+ char *sec_nspname; -+ Form_pg_proc proForm; -+ HeapTuple protup; -+ -+ /* -+ * LOCK the target table -+ */ -+ initStringInfo(&query); -+ relname_full = security_quote_relation(relOid); -+ appendStringInfo(&query, "LOCK %s IN SHARE MODE", relname_full); -+ if (SPI_execute(query.data, false, 0) != SPI_OK_UTILITY) -+ elog(ERROR, "SPI_execute failed on %s", query.data); -+ -+ /* -+ * DELETE orphan entries -+ */ -+ initStringInfo(&query); -+ attname_secid = get_attname(SecurityRelationId, Anum_pg_security_secid); -+ attname_datid = get_attname(SecurityRelationId, Anum_pg_security_datid); -+ attname_relid = get_attname(SecurityRelationId, Anum_pg_security_relid); -+ attname_secattr = get_attname(SecurityRelationId, Anum_pg_security_secattr); -+ -+ appendStringInfo(&query, -+ "DELETE FROM %s " -+ "WHERE %s = $1 AND %s = $2 AND %s NOT IN ", -+ security_quote_relation(SecurityRelationId), -+ quote_identifier(attname_datid), -+ quote_identifier(attname_relid), -+ quote_identifier(attname_secid)); -+ -+ protup = SearchSysCache(PROCOID, -+ ObjectIdGetDatum(F_SECLABEL_TO_SECID), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(protup)) -+ elog(ERROR, "cache lookup failed for procedure: %u", F_SECLABEL_TO_SECID); -+ -+ proForm = (Form_pg_proc) GETSTRUCT(protup); -+ sec_proname = NameStr(proForm->proname); -+ sec_nspname = get_namespace_name(proForm->pronamespace); -+ -+ appendStringInfo(&query, -+ "(SELECT %s.%s(%s) FROM ONLY %s)", -+ quote_identifier(sec_nspname), -+ quote_identifier(sec_proname), -+ quote_identifier(get_rel_name(relOid)), -+ relname_full); -+ ReleaseSysCache(protup); -+ -+ /* -+ * Setup and execute query -+ */ -+ types[0] = OIDOID; -+ types[1] = OIDOID; -+ plan = SPI_prepare(query.data, 2, types); -+ if (!plan) -+ elog(ERROR, "SPI_prepare failed on %s", query.data); -+ -+ database_oid = (IsSharedRelation(relOid) ? InvalidOid : MyDatabaseId); -+ -+ values[0] = ObjectIdGetDatum(database_oid); -+ values[1] = ObjectIdGetDatum(relOid); -+ if (SPI_execute_plan(plan, values, NULL, false, 0) != SPI_OK_DELETE) -+ elog(ERROR, "SPI_execute_plan failed on %s", query.data); -+ -+ SPI_freetuptable(SPI_tuptable); -+ -+ return SPI_processed; -+} -+ -+void -+seclabelRelationReclaim(Oid relOid) -+{ -+ int save_mode; -+ -+ if (!superuser() || -+ get_rel_relkind(relOid) != RELKIND_RELATION) -+ return; -+ -+ save_mode = sepostgresql_mode; -+ sepostgresql_mode = SEPGSQL_MODE_INTERNAL; -+ PG_TRY(); -+ { -+ if (SPI_connect() != SPI_OK_CONNECT) -+ elog(ERROR, "SPI_connect failed"); -+ -+ seclabelRelationReclaimExec(relOid); -+ -+ if (SPI_finish() != SPI_OK_FINISH) -+ elog(ERROR, "SPI_finish failed"); -+ } -+ PG_CATCH(); -+ { -+ sepostgresql_mode = save_mode; -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ sepostgresql_mode = save_mode; -+} -+ -+Datum -+seclabel_to_secid(PG_FUNCTION_ARGS) -+{ -+ HeapTupleHeader tuphdr = PG_GETARG_HEAPTUPLEHEADER(0); -+ -+ PG_RETURN_OID(HeapTupleHeaderGetSecid(tuphdr)); -+} -diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c -index 451724f..b95b414 100644 ---- a/src/backend/catalog/pg_shdepend.c -+++ b/src/backend/catalog/pg_shdepend.c -@@ -37,6 +37,7 @@ - #include "commands/schemacmds.h" - #include "commands/tablecmds.h" - #include "commands/typecmds.h" -+#include "security/sepgsql.h" - #include "storage/lmgr.h" - #include "miscadmin.h" - #include "utils/acl.h" -@@ -1340,6 +1341,8 @@ shdepReassignOwned(List *roleids, Oid newrole) - break; - - case TypeRelationId: -+ /* SELinux checks */ -+ sepgsql_type_alter(sdepForm->objid, NULL, InvalidOid); - AlterTypeOwnerInternal(sdepForm->objid, newrole, true); - break; - -@@ -1352,7 +1355,8 @@ shdepReassignOwned(List *roleids, Oid newrole) - break; - - case RelationRelationId: -- -+ /* SELinux checks */ -+ sepgsql_relation_alter(sdepForm->objid, NULL, InvalidOid); - /* - * Pass recursing = true so that we don't fail on indexes, - * owned sequences, etc when we happen to visit them -diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c -index 7696480..4586056 100644 ---- a/src/backend/catalog/pg_type.c -+++ b/src/backend/catalog/pg_type.c -@@ -25,6 +25,7 @@ - #include "commands/typecmds.h" - #include "miscadmin.h" - #include "parser/scansup.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -56,10 +57,17 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) - Datum values[Natts_pg_type]; - bool nulls[Natts_pg_type]; - Oid typoid; -+ Oid typsid; - NameData name; - - Assert(PointerIsValid(typeName)); - -+ /* SELinux check permission to create a shell type */ -+ typsid = sepgsql_type_create(typeName, InvalidOid, typeNamespace, -+ F_SHELL_IN, F_SHELL_OUT, -+ InvalidOid, InvalidOid, -+ InvalidOid, InvalidOid, InvalidOid); -+ - /* - * open pg_type - */ -@@ -201,6 +209,7 @@ TypeCreate(Oid newTypeOid, - { - Relation pg_type_desc; - Oid typeObjectId; -+ Oid typeSecid = InvalidOid; - bool rebuildDeps = false; - HeapTuple tup; - bool nulls[Natts_pg_type]; -@@ -367,6 +376,15 @@ TypeCreate(Oid newTypeOid, - CStringGetDatum(typeName), - ObjectIdGetDatum(typeNamespace), - 0, 0); -+ -+ /* SELinux checks to create/replace type */ -+ if (!isImplicitArray && typeType != TYPTYPE_COMPOSITE) -+ typeSecid = sepgsql_type_create(typeName, tup, typeNamespace, -+ inputProcedure, outputProcedure, -+ receiveProcedure, sendProcedure, -+ typmodinProcedure, typmodoutProcedure, -+ analyzeProcedure); -+ - if (HeapTupleIsValid(tup)) - { - /* -@@ -412,6 +430,8 @@ TypeCreate(Oid newTypeOid, - /* Force the OID if requested by caller, else heap_insert does it */ - if (OidIsValid(newTypeOid)) - HeapTupleSetOid(tup, newTypeOid); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, typeSecid); - - typeObjectId = simple_heap_insert(pg_type_desc, tup); - } -diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c -index 6e7b5cf..10ea3a2 100644 ---- a/src/backend/catalog/toasting.c -+++ b/src/backend/catalog/toasting.c -@@ -28,6 +28,7 @@ - #include "catalog/toasting.h" - #include "miscadmin.h" - #include "nodes/makefuncs.h" -+#include "security/sepgsql.h" - #include "utils/builtins.h" - #include "utils/syscache.h" - -@@ -125,6 +126,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, - char toast_relname[NAMEDATALEN]; - char toast_idxname[NAMEDATALEN]; - IndexInfo *indexInfo; -+ Oid *secLabels; - Oid classObjectId[2]; - int16 coloptions[2]; - ObjectAddress baseobject, -@@ -199,6 +201,11 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, - else - namespaceid = PG_TOAST_NAMESPACE; - -+ secLabels = sepgsql_relation_create(toast_relname, -+ RELKIND_TOASTVALUE, -+ tupdesc, namespaceid, -+ NULL, NIL, false, false); -+ - toast_relid = heap_create_with_catalog(toast_relname, - namespaceid, - rel->rd_rel->reltablespace, -@@ -212,7 +219,8 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, - 0, - ONCOMMIT_NOOP, - reloptions, -- true); -+ true, -+ secLabels); - - /* make the toast relation visible, else index creation will fail */ - CommandCounterIncrement(); -diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c -index fd3f336..dfca678 100644 ---- a/src/backend/commands/aggregatecmds.c -+++ b/src/backend/commands/aggregatecmds.c -@@ -32,6 +32,7 @@ - #include "miscadmin.h" - #include "parser/parse_func.h" - #include "parser/parse_type.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -@@ -311,6 +312,9 @@ RenameAggregate(List *name, List *args, const char *newname) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux permission checks */ -+ sepgsql_proc_alter(procOid, newname, InvalidOid); -+ - /* rename */ - namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname); - simple_heap_update(rel, &tup->t_self, tup); -diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c -index 46bc4df..79131d5 100644 ---- a/src/backend/commands/alter.c -+++ b/src/backend/commands/alter.c -@@ -289,3 +289,32 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) - (int) stmt->objectType); - } - } -+ -+void -+ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt) -+{ -+ DefElem *seclabel = (DefElem *)stmt->secLabel; -+ -+ switch (stmt->objectType) -+ { -+ case OBJECT_DATABASE: -+ AlterDatabaseSecLabel(strVal(linitial(stmt->object)), seclabel); -+ break; -+ case OBJECT_SCHEMA: -+ AlterSchemaSecLabel(strVal(linitial(stmt->object)), seclabel); -+ break; -+ case OBJECT_TABLE: -+ case OBJECT_SEQUENCE: -+ case OBJECT_COLUMN: -+ CheckRelationOwnership(stmt->relation, true); -+ AlterRelationSecLabel(stmt->relation, stmt->subname, -+ stmt->objectType, seclabel); -+ break; -+ case OBJECT_FUNCTION: -+ AlterFunctionSecLabel(stmt->object, stmt->objarg, seclabel); -+ break; -+ default: -+ elog(ERROR, "unrecognized AlterSecLabelStmt type: %d", -+ (int) stmt->objectType); -+ } -+} -diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c -index a6ba2ec..b990a33 100644 ---- a/src/backend/commands/cluster.c -+++ b/src/backend/commands/cluster.c -@@ -36,6 +36,7 @@ - #include "commands/trigger.h" - #include "commands/vacuum.h" - #include "miscadmin.h" -+#include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/procarray.h" - #include "utils/acl.h" -@@ -617,8 +618,9 @@ rebuild_relation(Relation OldHeap, Oid indexOid) - /* - * The new relation is local to our transaction and we know nothing - * depends on it, so DROP_RESTRICT should be OK. -+ * SELinux does not check any permissions here. - */ -- performDeletion(&object, DROP_RESTRICT); -+ performDeletionNoPerms(&object, DROP_RESTRICT); - - /* performDeletion does CommandCounterIncrement at end */ - -@@ -712,7 +714,8 @@ make_new_heap(Oid OIDOldHeap, const char *NewName, Oid NewTableSpace) - 0, - ONCOMMIT_NOOP, - reloptions, -- allowSystemTableMods); -+ allowSystemTableMods, -+ sepgsql_relation_copy(OldHeap)); - - ReleaseSysCache(tuple); - -@@ -924,6 +927,10 @@ copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex) - if (NewHeap->rd_rel->relhasoids) - HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple)); - -+ /* Preserve SID, if any */ -+ if (HeapTupleHasSecid(copiedTuple)) -+ HeapTupleSetSecid(copiedTuple, HeapTupleGetSecid(tuple)); -+ - /* The heap rewrite module does the rest */ - rewrite_heap_tuple(rwstate, tuple, copiedTuple); - -diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c -index 045ffca..97914d5 100644 ---- a/src/backend/commands/conversioncmds.c -+++ b/src/backend/commands/conversioncmds.c -@@ -24,6 +24,7 @@ - #include "mb/pg_wchar.h" - #include "miscadmin.h" - #include "parser/parse_func.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -@@ -45,6 +46,7 @@ CreateConversionCommand(CreateConversionStmt *stmt) - int from_encoding; - int to_encoding; - Oid funcoid; -+ Oid secid; - const char *from_encoding_name = stmt->for_encoding_name; - const char *to_encoding_name = stmt->to_encoding_name; - List *func_name = stmt->func_name; -@@ -96,6 +98,9 @@ CreateConversionCommand(CreateConversionStmt *stmt) - aclcheck_error(aclresult, ACL_KIND_PROC, - NameListToString(func_name)); - -+ /* SELinux checks */ -+ secid = sepgsql_conversion_create(conversion_name, namespaceId, funcoid); -+ - /* - * Check that the conversion function is suitable for the requested source - * and target encodings. We do that by calling the function with an empty -@@ -114,7 +119,7 @@ CreateConversionCommand(CreateConversionStmt *stmt) - * name) - */ - ConversionCreate(conversion_name, namespaceId, GetUserId(), -- from_encoding, to_encoding, funcoid, stmt->def); -+ from_encoding, to_encoding, funcoid, secid, stmt->def); - } - - /* -@@ -240,6 +245,9 @@ RenameConversion(List *name, const char *newname) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux checks */ -+ sepgsql_conversion_alter(conversionOid, newname); -+ - /* rename */ - namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname); - simple_heap_update(rel, &tup->t_self, tup); -@@ -336,6 +344,8 @@ AlterConversionOwner_internal(Relation rel, Oid conversionOid, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(convForm->connamespace)); - } -+ /* SELinux checks */ -+ sepgsql_conversion_alter(HeapTupleGetOid(tup), NULL); - - /* - * Modify the owner --- okay to scribble on tup because it's a copy -diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c -index a151999..ee7344d 100644 ---- a/src/backend/commands/copy.c -+++ b/src/backend/commands/copy.c -@@ -21,8 +21,11 @@ - #include - - #include "access/heapam.h" -+#include "access/sysattr.h" - #include "access/xact.h" -+#include "catalog/heap.h" - #include "catalog/namespace.h" -+#include "catalog/pg_security.h" - #include "catalog/pg_type.h" - #include "commands/copy.h" - #include "commands/trigger.h" -@@ -34,6 +37,8 @@ - #include "optimizer/planner.h" - #include "parser/parse_relation.h" - #include "rewrite/rewriteHandler.h" -+#include "security/rowlevel.h" -+#include "security/sepgsql.h" - #include "storage/fd.h" - #include "tcop/tcopprot.h" - #include "utils/acl.h" -@@ -160,6 +165,10 @@ typedef struct CopyStateData - char *raw_buf; - int raw_buf_index; /* next byte to process */ - int raw_buf_len; /* total # of bytes stored */ -+ -+ /* dump/restore support for security_label */ -+ FmgrInfo seclabel_out_function; -+ bool seclabel_force_quot; - } CopyStateData; - - typedef CopyStateData *CopyState; -@@ -243,8 +252,8 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0"; - /* non-export function prototypes */ - static void DoCopyTo(CopyState cstate); - static void CopyTo(CopyState cstate); --static void CopyOneRowTo(CopyState cstate, Oid tupleOid, -- Datum *values, bool *nulls); -+static void CopyOneRowTo(CopyState cstate, HeapTuple tuple, -+ Datum *values, bool *nulls); - static void CopyFrom(CopyState cstate); - static bool CopyReadLine(CopyState cstate); - static bool CopyReadLineText(CopyState cstate); -@@ -958,12 +967,19 @@ DoCopy(const CopyStmt *stmt, const char *queryString) - errmsg("CSV quote character must not appear in the NULL specification"))); - - /* Disallow file COPY except to superusers. */ -- if (!pipe && !superuser()) -- ereport(ERROR, -- (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -- errmsg("must be superuser to COPY to or from a file"), -- errhint("Anyone can COPY to stdout or from stdin. " -- "psql's \\copy command also works for anyone."))); -+ if (!pipe) -+ { -+ if (!superuser()) -+ ereport(ERROR, -+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -+ errmsg("must be superuser to COPY to or from a file"), -+ errhint("Anyone can COPY to stdout or from stdin. " -+ "psql's \\copy command also works for anyone."))); -+ if (is_from) -+ sepgsql_file_read(stmt->filename); -+ else -+ sepgsql_file_write(stmt->filename); -+ } - - if (stmt->relation) - { -@@ -1090,6 +1106,9 @@ DoCopy(const CopyStmt *stmt, const char *queryString) - - num_phys_attrs = tupDesc->natts; - -+ /* SELinux: check table/column level permission */ -+ sepgsqlCheckCopyTable(cstate->rel, cstate->attnumlist, is_from); -+ - /* Convert FORCE QUOTE name list to per-column flags, check validity */ - cstate->force_quote_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool)); - if (force_quote) -@@ -1104,11 +1123,31 @@ DoCopy(const CopyStmt *stmt, const char *queryString) - int attnum = lfirst_int(cur); - - if (!list_member_int(cstate->attnumlist, attnum)) -+ { -+ Form_pg_attribute attForm; -+ -+ if (SystemAttributeIsWritable(attnum)) -+ attForm = SystemAttributeDefinition(attnum, true); -+ else -+ attForm = tupDesc->attrs[attnum - 1]; -+ -+ Assert(attForm != NULL); -+ - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", -- NameStr(tupDesc->attrs[attnum - 1]->attname)))); -- cstate->force_quote_flags[attnum - 1] = true; -+ NameStr(attForm->attname)))); -+ } -+ -+ switch (attnum) -+ { -+ case SecurityAttributeNumber: -+ cstate->seclabel_force_quot = true; -+ break; -+ default: -+ cstate->force_quote_flags[attnum - 1] = true; -+ break; -+ } - } - } - -@@ -1126,10 +1165,23 @@ DoCopy(const CopyStmt *stmt, const char *queryString) - int attnum = lfirst_int(cur); - - if (!list_member_int(cstate->attnumlist, attnum)) -+ { -+ Form_pg_attribute attForm; -+ -+ if (SystemAttributeIsWritable(attnum)) -+ attForm = SystemAttributeDefinition(attnum, true); -+ else -+ attForm = tupDesc->attrs[attnum - 1]; -+ -+ Assert(attForm != NULL); -+ - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), - errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", -- NameStr(tupDesc->attrs[attnum - 1]->attname)))); -+ NameStr(attForm->attname)))); -+ } -+ if (SystemAttributeIsWritable(attnum)) -+ continue; /* ignore, if specified */ - cstate->force_notnull_flags[attnum - 1] = true; - } - } -@@ -1321,16 +1373,31 @@ CopyTo(CopyState cstate) - int attnum = lfirst_int(cur); - Oid out_func_oid; - bool isvarlena; -+ FmgrInfo *out_fmgr; -+ Form_pg_attribute attForm; -+ -+ switch (attnum) -+ { -+ case SecurityAttributeNumber: -+ attForm = SystemAttributeDefinition(attnum, true); -+ out_fmgr = &cstate->seclabel_out_function; -+ break; -+ -+ default: -+ attForm = attr[attnum - 1]; -+ out_fmgr = &cstate->out_functions[attnum - 1]; -+ break; -+ } - - if (cstate->binary) -- getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid, -+ getTypeBinaryOutputInfo(attForm->atttypid, - &out_func_oid, - &isvarlena); - else -- getTypeOutputInfo(attr[attnum - 1]->atttypid, -+ getTypeOutputInfo(attForm->atttypid, - &out_func_oid, - &isvarlena); -- fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); -+ fmgr_info(out_func_oid, out_fmgr); - } - - /* -@@ -1385,7 +1452,14 @@ CopyTo(CopyState cstate) - CopySendChar(cstate, cstate->delim[0]); - hdr_delim = true; - -- colname = NameStr(attr[attnum - 1]->attname); -+ if (SystemAttributeIsWritable(attnum)) -+ { -+ Form_pg_attribute attForm -+ = SystemAttributeDefinition(attnum, true); -+ colname = NameStr(attForm->attname); -+ } -+ else -+ colname = NameStr(attr[attnum - 1]->attname); - - CopyAttributeOutCSV(cstate, colname, false, - list_length(cstate->attnumlist) == 1); -@@ -1411,11 +1485,15 @@ CopyTo(CopyState cstate) - { - CHECK_FOR_INTERRUPTS(); - -+ /* check Row-level permission on the tuple */ -+ if (!rowlvCopyToTuple(cstate->rel, tuple)) -+ continue; -+ - /* Deconstruct the tuple ... faster than repeated heap_getattr */ - heap_deform_tuple(tuple, tupDesc, values, nulls); - - /* Format and send the data */ -- CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls); -+ CopyOneRowTo(cstate, tuple, values, nulls); - } - - heap_endscan(scandesc); -@@ -1441,7 +1519,8 @@ CopyTo(CopyState cstate) - * Emit one row during CopyTo(). - */ - static void --CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) -+CopyOneRowTo(CopyState cstate, HeapTuple tuple, -+ Datum *values, bool *nulls) - { - bool need_delim = false; - FmgrInfo *out_functions = cstate->out_functions; -@@ -1461,7 +1540,7 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) - { - /* Hack --- assume Oid is same size as int32 */ - CopySendInt32(cstate, sizeof(int32)); -- CopySendInt32(cstate, tupleOid); -+ CopySendInt32(cstate, HeapTupleGetOid(tuple)); - } - } - else -@@ -1471,7 +1550,7 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) - if (cstate->oids) - { - string = DatumGetCString(DirectFunctionCall1(oidout, -- ObjectIdGetDatum(tupleOid))); -+ ObjectIdGetDatum(HeapTupleGetOid(tuple)))); - CopySendString(cstate, string); - need_delim = true; - } -@@ -1480,8 +1559,11 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) - foreach(cur, cstate->attnumlist) - { - int attnum = lfirst_int(cur); -- Datum value = values[attnum - 1]; -- bool isnull = nulls[attnum - 1]; -+ Oid relid; -+ Datum value; -+ bool isnull; -+ bool force_quot; -+ FmgrInfo *out_fmgr; - - if (!cstate->binary) - { -@@ -1490,6 +1572,24 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) - need_delim = true; - } - -+ switch (attnum) -+ { -+ case SecurityAttributeNumber: -+ relid = RelationGetRelid(cstate->rel); -+ value = securitySysattSecLabelOut(relid, tuple); -+ isnull = false; -+ force_quot = cstate->seclabel_force_quot; -+ out_fmgr = &cstate->seclabel_out_function; -+ break; -+ -+ default: -+ value = values[attnum - 1]; -+ isnull = nulls[attnum - 1]; -+ force_quot = cstate->force_quote_flags[attnum - 1]; -+ out_fmgr = &out_functions[attnum - 1]; -+ break; -+ } -+ - if (isnull) - { - if (!cstate->binary) -@@ -1501,11 +1601,9 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) - { - if (!cstate->binary) - { -- string = OutputFunctionCall(&out_functions[attnum - 1], -- value); -+ string = OutputFunctionCall(out_fmgr, value); - if (cstate->csv_mode) -- CopyAttributeOutCSV(cstate, string, -- cstate->force_quote_flags[attnum - 1], -+ CopyAttributeOutCSV(cstate, string, force_quot, - list_length(cstate->attnumlist) == 1); - else - CopyAttributeOutText(cstate, string); -@@ -1514,8 +1612,7 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) - { - bytea *outputbytes; - -- outputbytes = SendFunctionCall(&out_functions[attnum - 1], -- value); -+ outputbytes = SendFunctionCall(out_fmgr, value); - CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); - CopySendData(cstate, VARDATA(outputbytes), - VARSIZE(outputbytes) - VARHDRSZ); -@@ -1649,8 +1746,10 @@ CopyFrom(CopyState cstate) - num_defaults; - FmgrInfo *in_functions; - FmgrInfo oid_in_function; -+ FmgrInfo seclabel_in_function; - Oid *typioparams; - Oid oid_typioparam; -+ Oid seclabel_typioparam; - int attnum; - int i; - Oid in_func_oid; -@@ -1888,6 +1987,18 @@ CopyFrom(CopyState cstate) - fmgr_info(in_func_oid, &oid_in_function); - } - -+ if (list_member_int(cstate->attnumlist, -+ SecurityAttributeNumber)) -+ { -+ if (!cstate->binary) -+ getTypeInputInfo(TEXTOID, -+ &in_func_oid, &seclabel_typioparam); -+ else -+ getTypeBinaryInputInfo(TEXTOID, -+ &in_func_oid, &seclabel_typioparam); -+ fmgr_info(in_func_oid, &seclabel_in_function); -+ } -+ - values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); - nulls = (bool *) palloc(num_phys_attrs * sizeof(bool)); - -@@ -1922,6 +2033,7 @@ CopyFrom(CopyState cstate) - { - bool skip_tuple; - Oid loaded_oid = InvalidOid; -+ Oid loaded_seclabel = InvalidOid; - - CHECK_FOR_INTERRUPTS(); - -@@ -1993,14 +2105,21 @@ CopyFrom(CopyState cstate) - /* Loop to read the user attributes on the line. */ - foreach(cur, cstate->attnumlist) - { -+ Form_pg_attribute attForm; -+ Datum dat; - int attnum = lfirst_int(cur); - int m = attnum - 1; - -+ if (SystemAttributeIsWritable(attnum)) -+ attForm = SystemAttributeDefinition(attnum, true); -+ else -+ attForm = attr[m]; -+ - if (fieldno >= fldct) - ereport(ERROR, - (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - errmsg("missing data for column \"%s\"", -- NameStr(attr[m]->attname)))); -+ NameStr(attForm->attname)))); - string = field_strings[fieldno++]; - - if (cstate->csv_mode && string == NULL && -@@ -2010,14 +2129,40 @@ CopyFrom(CopyState cstate) - string = cstate->null_print; - } - -- cstate->cur_attname = NameStr(attr[m]->attname); -+ cstate->cur_attname = NameStr(attForm->attname); - cstate->cur_attval = string; -- values[m] = InputFunctionCall(&in_functions[m], -- string, -- typioparams[m], -- attr[m]->atttypmod); -- if (string != NULL) -- nulls[m] = false; -+ -+ switch (attnum) -+ { -+ case SecurityAttributeNumber: -+ if (!string) -+ break; -+ -+ dat = InputFunctionCall(&seclabel_in_function, -+ string, -+ seclabel_typioparam, -+ attForm->atttypmod); -+ loaded_seclabel -+ = securityTransSecLabelIn(RelationGetRelid(cstate->rel), -+ TextDatumGetCString(dat)); -+ break; -+ -+ default: -+ if (cstate->csv_mode && string == NULL && -+ cstate->force_notnull_flags[m]) -+ { -+ /* Go ahead and read the NULL string */ -+ string = cstate->null_print; -+ } -+ -+ values[m] = InputFunctionCall(&in_functions[m], -+ string, -+ typioparams[m], -+ attForm->atttypmod); -+ if (string != NULL) -+ nulls[m] = false; -+ break; -+ } - cstate->cur_attname = NULL; - cstate->cur_attval = NULL; - } -@@ -2063,17 +2208,41 @@ CopyFrom(CopyState cstate) - i = 0; - foreach(cur, cstate->attnumlist) - { -+ Form_pg_attribute attForm; -+ Datum dat; - int attnum = lfirst_int(cur); - int m = attnum - 1; - -- cstate->cur_attname = NameStr(attr[m]->attname); -+ if (SystemAttributeIsWritable(attnum)) -+ attForm = SystemAttributeDefinition(attnum, false); -+ else -+ attForm = attr[m]; -+ -+ cstate->cur_attname = NameStr(attForm->attname); - i++; -- values[m] = CopyReadBinaryAttribute(cstate, -- i, -- &in_functions[m], -- typioparams[m], -- attr[m]->atttypmod, -- &nulls[m]); -+ -+ switch (attnum) -+ { -+ case SecurityAttributeNumber: -+ dat = CopyReadBinaryAttribute(cstate, i, -+ &seclabel_in_function, -+ seclabel_typioparam, -+ attForm->atttypmod, -+ &isnull); -+ if (!isnull) -+ loaded_seclabel -+ = securityTransSecLabelIn(RelationGetRelid(cstate->rel), -+ TextDatumGetCString(dat)); -+ break; -+ -+ default: -+ values[m] = CopyReadBinaryAttribute(cstate, i, -+ &in_functions[m], -+ typioparams[m], -+ attr[m]->atttypmod, -+ &nulls[m]); -+ break; -+ } - cstate->cur_attname = NULL; - } - } -@@ -2094,6 +2263,8 @@ CopyFrom(CopyState cstate) - - if (cstate->oids && file_has_oids) - HeapTupleSetOid(tuple, loaded_oid); -+ if (HeapTupleHasSecid(tuple)) -+ HeapTupleSetSecid(tuple, loaded_seclabel); - - /* Triggers and stuff need to be invoked in query context. */ - MemoryContextSwitchTo(oldcontext); -@@ -2118,6 +2289,9 @@ CopyFrom(CopyState cstate) - } - - if (!skip_tuple) -+ sepgsqlHeapTupleInsert(cstate->rel, tuple, false); -+ -+ if (!skip_tuple) - { - /* Place tuple in tuple slot */ - ExecStoreTuple(tuple, slot, InvalidBuffer, false); -@@ -3398,6 +3572,13 @@ CopyGetAttnums(TupleDesc tupDesc, Relation rel, List *attnamelist) - } - if (attnum == InvalidAttrNumber) - { -+ Form_pg_attribute attForm -+ = SystemAttributeByName(name, tupDesc->tdhasoid); -+ if (attForm && SystemAttributeIsWritable(attForm->attnum)) -+ attnum = attForm->attnum; -+ } -+ if (attnum == InvalidAttrNumber) -+ { - if (rel != NULL) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), -@@ -3445,7 +3626,8 @@ copy_dest_receive(TupleTableSlot *slot, DestReceiver *self) - slot_getallattrs(slot); - - /* And send the data */ -- CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull); -+ CopyOneRowTo(cstate, slot->tts_tuple, -+ slot->tts_values, slot->tts_isnull); - } - - /* -diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c -index ec1db5a..ea35f05 100644 ---- a/src/backend/commands/dbcommands.c -+++ b/src/backend/commands/dbcommands.c -@@ -33,6 +33,7 @@ - #include "catalog/indexing.h" - #include "catalog/pg_authid.h" - #include "catalog/pg_database.h" -+#include "catalog/pg_security.h" - #include "catalog/pg_tablespace.h" - #include "commands/comment.h" - #include "commands/dbcommands.h" -@@ -41,6 +42,7 @@ - #include "miscadmin.h" - #include "pgstat.h" - #include "postmaster/bgwriter.h" -+#include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/fd.h" - #include "storage/lmgr.h" -@@ -111,6 +113,7 @@ createdb(const CreatedbStmt *stmt) - bool new_record_nulls[Natts_pg_database]; - Oid dboid; - Oid datdba; -+ Oid datsecid; - ListCell *option; - DefElem *dtablespacename = NULL; - DefElem *downer = NULL; -@@ -119,6 +122,7 @@ createdb(const CreatedbStmt *stmt) - DefElem *dcollate = NULL; - DefElem *dctype = NULL; - DefElem *dconnlimit = NULL; -+ DefElem *dseclabel = NULL; - char *dbname = stmt->dbname; - char *dbowner = NULL; - const char *dbtemplate = NULL; -@@ -200,6 +204,14 @@ createdb(const CreatedbStmt *stmt) - errmsg("LOCATION is not supported anymore"), - errhint("Consider using tablespaces instead."))); - } -+ else if (strcmp(defel->defname, "security_context") == 0) -+ { -+ if (dseclabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_SYNTAX_ERROR), -+ errmsg("conflicting or redundant options"))); -+ dseclabel = defel; -+ } - else - elog(ERROR, "option \"%s\" not recognized", - defel->defname); -@@ -294,6 +306,9 @@ createdb(const CreatedbStmt *stmt) - errmsg("template database \"%s\" does not exist", - dbtemplate))); - -+ /* SELinux checks db_database:{create} */ -+ datsecid = sepgsql_database_create(dbname, src_dboid, dseclabel); -+ - /* - * Permission check: to copy a DB that's not marked datistemplate, you - * must be superuser or the owner thereof. -@@ -557,6 +572,8 @@ createdb(const CreatedbStmt *stmt) - new_record, new_record_nulls); - - HeapTupleSetOid(tuple, dboid); -+ if (HeapTupleHasSecid(tuple)) -+ HeapTupleSetSecid(tuple, datsecid); - - simple_heap_insert(pg_database_rel, tuple); - -@@ -573,6 +590,9 @@ createdb(const CreatedbStmt *stmt) - /* Create pg_shdepend entries for objects within database */ - copyTemplateDependencies(src_dboid, dboid); - -+ /* Create pg_security entries for objects within database */ -+ securityOnCreateDatabase(src_dboid, dboid); -+ - /* - * Force a checkpoint before starting the copy. This will force dirty - * buffers out to disk, to ensure source database is up-to-date on disk -@@ -776,6 +796,9 @@ dropdb(const char *dbname, bool missing_ok) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - dbname); - -+ /* SELinux checks db_database:{drop} permission */ -+ sepgsql_database_drop(db_id); -+ - /* - * Disallow dropping a DB that is marked istemplate. This is just to - * prevent people from accidentally dropping template0 or template1; they -@@ -829,6 +852,11 @@ dropdb(const char *dbname, bool missing_ok) - dropDatabaseDependencies(db_id); - - /* -+ * Remove pg_security entries for the database. -+ */ -+ securityOnDropDatabase(db_id); -+ -+ /* - * Drop pages for this database that are in the shared buffer cache. This - * is important to ensure that no remaining backend tries to write out a - * dirty buffer to the dead database later... -@@ -913,6 +941,9 @@ RenameDatabase(const char *oldname, const char *newname) - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to rename database"))); - -+ /* SELinux: check db_database:{setattr} */ -+ sepgsql_database_alter(db_id); -+ - /* - * Make sure the new name doesn't exist. See notes for same error in - * CREATE DATABASE. -@@ -1025,6 +1056,9 @@ movedb(const char *dbname, const char *tblspcname) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - dbname); - -+ /* SELinux checks db_database:{setattr} */ -+ sepgsql_database_alter(db_id); -+ - /* - * Obviously can't move the tables of my own database - */ -@@ -1377,6 +1411,9 @@ AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - stmt->dbname); - -+ /* SELinux checks db_database:{setattr} */ -+ sepgsql_database_alter(HeapTupleGetOid(tuple)); -+ - /* - * Build an updated tuple, perusing the information just obtained - */ -@@ -1449,6 +1486,9 @@ AlterDatabaseSet(AlterDatabaseSetStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, - stmt->dbname); - -+ /* SELinux checks db_database:{setattr} */ -+ sepgsql_database_alter(HeapTupleGetOid(tuple)); -+ - memset(repl_repl, false, sizeof(repl_repl)); - repl_repl[Anum_pg_database_datconfig - 1] = true; - -@@ -1571,6 +1611,9 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to change owner of database"))); - -+ /* SELinux checks db_database:{setattr} */ -+ sepgsql_database_alter(HeapTupleGetOid(tuple)); -+ - memset(repl_null, false, sizeof(repl_null)); - memset(repl_repl, false, sizeof(repl_repl)); - -@@ -1615,6 +1658,58 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) - */ - } - -+/* -+ * ALTER DATABASE name SECURITY_LABEL [=] newlabel -+ */ -+void -+AlterDatabaseSecLabel(const char *dbname, DefElem *seclabel) -+{ -+ Relation rel; -+ HeapTuple oldtup; -+ HeapTuple newtup; -+ ScanKeyData scankey; -+ SysScanDesc scan; -+ Oid secid; -+ bool replaces[Natts_pg_database]; -+ -+ /* Fetch the old tuple */ -+ rel = heap_open(DatabaseRelationId, RowExclusiveLock); -+ ScanKeyInit(&scankey, -+ Anum_pg_database_datname, -+ BTEqualStrategyNumber, F_NAMEEQ, -+ NameGetDatum(dbname)); -+ scan = systable_beginscan(rel, DatabaseNameIndexId, true, -+ SnapshotNow, 1, &scankey); -+ oldtup = systable_getnext(scan); -+ if (!HeapTupleIsValid(oldtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_UNDEFINED_DATABASE), -+ errmsg("database \"%s\" does not exist", dbname))); -+ -+ memset(replaces, false, sizeof(replaces)); -+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), -+ NULL, NULL, replaces); -+ if (!HeapTupleHasSecid(newtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security label on \"%s\"", dbname))); -+ systable_endscan(scan); -+ -+ /* check DAC permission */ -+ if (!pg_database_ownercheck(HeapTupleGetOid(newtup), GetUserId())) -+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname); -+ -+ /* SELinux checks db_database:{setattr relabelfrom relabelto} */ -+ secid = sepgsql_database_relabel(HeapTupleGetOid(newtup), seclabel); -+ HeapTupleSetSecid(newtup, secid); -+ -+ simple_heap_update(rel, &newtup->t_self, newtup); -+ CatalogUpdateIndexes(rel, newtup); -+ -+ heap_freetuple(newtup); -+ -+ heap_close(rel, RowExclusiveLock); -+} - - /* - * Helper functions -diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c -index 46493b1..7236279 100644 ---- a/src/backend/commands/foreigncmds.c -+++ b/src/backend/commands/foreigncmds.c -@@ -27,6 +27,7 @@ - #include "foreign/foreign.h" - #include "miscadmin.h" - #include "parser/parse_func.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -@@ -233,6 +234,9 @@ AlterForeignDataWrapperOwner(const char *name, Oid newOwnerId) - - if (form->fdwowner != newOwnerId) - { -+ /* SELinux permission check */ -+ sepgsql_fdw_alter(fdwId, InvalidOid); -+ - form->fdwowner = newOwnerId; - - simple_heap_update(rel, &tup->t_self, tup); -@@ -297,6 +301,8 @@ AlterForeignServerOwner(const char *name, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); - } - } -+ /* SELinux permission checks */ -+ sepgsql_foreign_server_alter(srvId); - - form->srvowner = newOwnerId; - -@@ -342,6 +348,7 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) - Oid fdwvalidator; - Datum fdwoptions; - Oid ownerId; -+ Oid secid; - - /* Must be super user */ - if (!superuser()) -@@ -380,6 +387,9 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) - else - fdwvalidator = InvalidOid; - -+ /* SELinux permission checks */ -+ secid = sepgsql_fdw_create(stmt->fdwname, fdwvalidator); -+ - values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = fdwvalidator; - - nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true; -@@ -393,6 +403,8 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) - nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true; - - tuple = heap_form_tuple(rel->rd_att, values, nulls); -+ if (HeapTupleHasSecid(tuple)) -+ HeapTupleSetSecid(tuple, secid); - - fdwId = simple_heap_insert(rel, tuple); - CatalogUpdateIndexes(rel, tuple); -@@ -487,6 +499,9 @@ AlterForeignDataWrapper(AlterFdwStmt *stmt) - fdwvalidator = DatumGetObjectId(datum); - } - -+ /* SELinux permission checks */ -+ sepgsql_fdw_alter(fdwId, fdwvalidator); -+ - /* - * Options specified, validate and update. - */ -@@ -609,6 +624,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt) - HeapTuple tuple; - Oid srvId; - Oid ownerId; -+ Oid secid; - AclResult aclresult; - ObjectAddress myself; - ObjectAddress referenced; -@@ -636,6 +652,8 @@ CreateForeignServer(CreateForeignServerStmt *stmt) - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); - -+ secid = sepgsql_foreign_server_create(stmt->fdwname); -+ - /* - * Insert tuple into pg_foreign_server. - */ -@@ -676,6 +694,8 @@ CreateForeignServer(CreateForeignServerStmt *stmt) - nulls[Anum_pg_foreign_server_srvoptions - 1] = true; - - tuple = heap_form_tuple(rel->rd_att, values, nulls); -+ if (HeapTupleHasSecid(tuple)) -+ HeapTupleSetSecid(tuple, secid); - - srvId = simple_heap_insert(rel, tuple); - -@@ -732,6 +752,9 @@ AlterForeignServer(AlterForeignServerStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, - stmt->servername); - -+ /* SELinux permission checks */ -+ sepgsql_foreign_server_alter(srvId); -+ - memset(repl_val, 0, sizeof(repl_val)); - memset(repl_null, false, sizeof(repl_null)); - memset(repl_repl, false, sizeof(repl_repl)); -diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c -index f0989bf..0668bd7 100644 ---- a/src/backend/commands/functioncmds.c -+++ b/src/backend/commands/functioncmds.c -@@ -53,6 +53,7 @@ - #include "parser/parse_expr.h" - #include "parser/parse_func.h" - #include "parser/parse_type.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -517,7 +518,8 @@ compute_attributes_sql_style(List *options, - bool *security_definer, - ArrayType **proconfig, - float4 *procost, -- float4 *prorows) -+ float4 *prorows, -+ Node **proseclabel) - { - ListCell *option; - DefElem *as_item = NULL; -@@ -529,6 +531,7 @@ compute_attributes_sql_style(List *options, - List *set_items = NIL; - DefElem *cost_item = NULL; - DefElem *rows_item = NULL; -+ DefElem *seclabel_item = NULL; - - foreach(option, options) - { -@@ -558,6 +561,14 @@ compute_attributes_sql_style(List *options, - errmsg("conflicting or redundant options"))); - windowfunc_item = defel; - } -+ else if (strcmp(defel->defname, "security_context") == 0) -+ { -+ if (seclabel_item) -+ ereport(ERROR, -+ (errcode(ERRCODE_SYNTAX_ERROR), -+ errmsg("conflicting or redundant options"))); -+ seclabel_item = defel; -+ } - else if (compute_common_attribute(defel, - &volatility_item, - &strict_item, -@@ -622,6 +633,8 @@ compute_attributes_sql_style(List *options, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("ROWS must be positive"))); - } -+ if (seclabel_item) -+ *proseclabel = (Node *)seclabel_item; - } - - -@@ -762,6 +775,7 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) - ArrayType *proconfig; - float4 procost; - float4 prorows; -+ Node *proseclabel; - HeapTuple languageTuple; - Form_pg_language languageStruct; - List *as_clause; -@@ -784,13 +798,14 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) - proconfig = NULL; - procost = -1; /* indicates not set */ - prorows = -1; /* indicates not set */ -+ proseclabel = NULL; - - /* override attributes from explicit list */ - compute_attributes_sql_style(stmt->options, - &as_clause, &language, - &isWindowFunc, &volatility, - &isStrict, &security, -- &proconfig, &procost, &prorows); -+ &proconfig, &procost, &prorows, &proseclabel); - - /* Convert language name to canonical case */ - languageName = case_translate_language_name(language); -@@ -926,7 +941,8 @@ CreateFunction(CreateFunctionStmt *stmt, const char *queryString) - parameterDefaults, - PointerGetDatum(proconfig), - procost, -- prorows); -+ prorows, -+ proseclabel); - } - - -@@ -1112,6 +1128,9 @@ RenameFunction(List *name, List *argtypes, const char *newname) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux permission checks */ -+ sepgsql_proc_alter(procOid, newname, InvalidOid); -+ - /* rename */ - namestrcpy(&(procForm->proname), newname); - simple_heap_update(rel, &tup->t_self, tup); -@@ -1220,6 +1239,8 @@ AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(procForm->pronamespace)); - } -+ /* SELinux permission checks */ -+ sepgsql_proc_alter(procOid, NULL, InvalidOid); - - memset(repl_null, false, sizeof(repl_null)); - memset(repl_repl, false, sizeof(repl_repl)); -@@ -1258,6 +1279,59 @@ AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) - } - - /* -+ * ALTER FUNCTION name(args,...) SECURITY_LABEL [=] newlabel -+ */ -+void -+AlterFunctionSecLabel(List *name, List *argtypes, DefElem *seclabel) -+{ -+ Relation rel; -+ HeapTuple oldtup; -+ HeapTuple newtup; -+ Oid procOid; -+ Oid secid; -+ bool replaces[Natts_pg_proc]; -+ -+ /* open pg_proc system catalog */ -+ rel = heap_open(ProcedureRelationId, RowExclusiveLock); -+ -+ procOid = LookupFuncNameTypeNames(name, argtypes, false); -+ -+ oldtup = SearchSysCache(PROCOID, -+ ObjectIdGetDatum(procOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(oldtup)) -+ elog(ERROR, "cache lookup failed for function %u", procOid); -+ -+ memset(replaces, false, sizeof(replaces)); -+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), -+ NULL, NULL, replaces); -+ -+ if (!HeapTupleHasSecid(newtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security label on \"%s\"", -+ get_func_name(procOid)))); -+ -+ ReleaseSysCache(oldtup); -+ -+ /* DAC permission checks */ -+ if (!pg_proc_ownercheck(HeapTupleGetOid(newtup), GetUserId())) -+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, -+ get_func_name(HeapTupleGetOid(newtup))); -+ -+ /* SELinux permission checks */ -+ secid = sepgsql_proc_relabel(procOid, seclabel); -+ HeapTupleSetSecid(newtup, secid); -+ -+ simple_heap_update(rel, &newtup->t_self, newtup); -+ CatalogUpdateIndexes(rel, newtup); -+ -+ heap_freetuple(newtup); -+ -+ heap_close(rel, RowExclusiveLock); -+} -+ -+/* - * Implements the ALTER FUNCTION utility command (except for the - * RENAME and OWNER clauses, which are handled as part of the generic - * ALTER framework). -@@ -1296,6 +1370,9 @@ AlterFunction(AlterFunctionStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - NameListToString(stmt->func->funcname)); - -+ /* SELinux checks permissions */ -+ sepgsql_proc_alter(funcOid, NULL, InvalidOid); -+ - if (procForm->proisagg) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), -@@ -1473,6 +1550,7 @@ CreateCast(CreateCastStmt *stmt) - char sourcetyptype; - char targettyptype; - Oid funcid; -+ Oid secid; - int nargs; - char castcontext; - char castmethod; -@@ -1674,6 +1752,8 @@ CreateCast(CreateCastStmt *stmt) - castcontext = 0; /* keep compiler quiet */ - break; - } -+ /* SELinux permission check */ -+ secid = sepgsql_cast_create(sourcetypeid, targettypeid, funcid); - - relation = heap_open(CastRelationId, RowExclusiveLock); - -@@ -1704,6 +1784,9 @@ CreateCast(CreateCastStmt *stmt) - - tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls); - -+ if (HeapTupleHasSecid(tuple)) -+ HeapTupleSetSecid(tuple, secid); -+ - simple_heap_insert(relation, tuple); - - CatalogUpdateIndexes(relation, tuple); -@@ -1897,6 +1980,9 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg, - NameStr(proc->proname), - newschema))); - -+ /* SELinux checks permissions */ -+ sepgsql_proc_alter(procOid, NULL, nspOid); -+ - /* OK, modify the pg_proc row */ - - /* tup is a copy, so we can scribble directly on it */ -diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c -index 99ab0e5..78b4455 100644 ---- a/src/backend/commands/indexcmds.c -+++ b/src/backend/commands/indexcmds.c -@@ -37,6 +37,7 @@ - #include "parser/parse_coerce.h" - #include "parser/parse_func.h" - #include "parser/parsetree.h" -+#include "security/sepgsql.h" - #include "storage/lmgr.h" - #include "storage/proc.h" - #include "storage/procarray.h" -@@ -197,6 +198,9 @@ DefineIndex(RangeVar *heapRelation, - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceId)); -+ -+ /* SELinux checks */ -+ sepgsql_index_create(relationId, namespaceId); - } - - /* -diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c -index 1e5c92e..6949b54 100644 ---- a/src/backend/commands/lockcmds.c -+++ b/src/backend/commands/lockcmds.c -@@ -20,6 +20,7 @@ - #include "commands/lockcmds.h" - #include "miscadmin.h" - #include "parser/parse_clause.h" -+#include "security/sepgsql.h" - #include "storage/lmgr.h" - #include "utils/acl.h" - #include "utils/lsyscache.h" -@@ -140,6 +141,9 @@ LockTableRecurse(Oid reloid, RangeVar *rv, - errmsg("\"%s\" is not a table", - RelationGetRelationName(rel)))); - -+ /* SELinux: check db_table:{lock} permission */ -+ sepgsql_relation_lock(reloid); -+ - /* - * If requested, recurse to children. We use find_inheritance_children - * not find_all_inheritors to avoid taking locks far in advance of -diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c -index 84dc2ce..b23919d 100644 ---- a/src/backend/commands/opclasscmds.c -+++ b/src/backend/commands/opclasscmds.c -@@ -35,6 +35,7 @@ - #include "parser/parse_func.h" - #include "parser/parse_oper.h" - #include "parser/parse_type.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -177,6 +178,7 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid) - HeapTuple tup; - Datum values[Natts_pg_opfamily]; - bool nulls[Natts_pg_opfamily]; -+ Oid opfSecid; - NameData opfName; - ObjectAddress myself, - referenced; -@@ -197,6 +199,9 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid) - errmsg("operator family \"%s\" for access method \"%s\" already exists", - opfname, amname))); - -+ /* SELinux check permission */ -+ opfSecid = sepgsql_opfamily_create(opfname, namespaceoid); -+ - /* - * Okay, let's create the pg_opfamily entry. - */ -@@ -210,6 +215,8 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid) - values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId()); - - tup = heap_form_tuple(rel->rd_att, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, opfSecid); - - opfamilyoid = simple_heap_insert(rel, tup); - -@@ -265,6 +272,7 @@ DefineOpClass(CreateOpClassStmt *stmt) - Form_pg_am pg_am; - Datum values[Natts_pg_opclass]; - bool nulls[Natts_pg_opclass]; -+ Oid opcSecid; - AclResult aclresult; - NameData opcName; - ObjectAddress myself, -@@ -353,6 +361,9 @@ DefineOpClass(CreateOpClassStmt *stmt) - NameListToString(stmt->opfamilyname), stmt->amname))); - opfamilyoid = HeapTupleGetOid(tup); - -+ /* SELinux checks permission */ -+ sepgsql_opfamily_alter(opfamilyoid, NULL); -+ - /* - * XXX given the superuser check above, there's no need for an - * ownership check here -@@ -371,6 +382,9 @@ DefineOpClass(CreateOpClassStmt *stmt) - { - opfamilyoid = HeapTupleGetOid(tup); - -+ /* SELinux checks permission */ -+ sepgsql_opfamily_alter(opfamilyoid, NULL); -+ - /* - * XXX given the superuser check above, there's no need for an - * ownership check here -@@ -441,6 +455,8 @@ DefineOpClass(CreateOpClassStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - get_func_name(funcOid)); - #endif -+ /* SELinux check permission */ -+ sepgsql_opfamily_add_operator(opfamilyoid, operOid); - - /* Save the info */ - member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); -@@ -465,6 +481,8 @@ DefineOpClass(CreateOpClassStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - get_func_name(funcOid)); - #endif -+ /* SELinux check permission */ -+ sepgsql_opfamily_add_procedure(opfamilyoid, funcOid); - - /* Save the info */ - member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); -@@ -531,6 +549,9 @@ DefineOpClass(CreateOpClassStmt *stmt) - errmsg("operator class \"%s\" for access method \"%s\" already exists", - opcname, stmt->amname))); - -+ /* SELinux permission check */ -+ opcSecid = sepgsql_opclass_create(opcname, namespaceoid); -+ - /* - * If we are creating a default opclass, check there isn't one already. - * (Note we do not restrict this test to visible opclasses; this ensures -@@ -657,6 +678,7 @@ DefineOpFamily(CreateOpFamilyStmt *stmt) - HeapTuple tup; - Datum values[Natts_pg_opfamily]; - bool nulls[Natts_pg_opfamily]; -+ Oid opfSecid; - AclResult aclresult; - NameData opfName; - ObjectAddress myself, -@@ -699,6 +721,9 @@ DefineOpFamily(CreateOpFamilyStmt *stmt) - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to create an operator family"))); - -+ /* SELinux permission check */ -+ opfSecid = sepgsql_opfamily_create(opfname, namespaceoid); -+ - rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock); - - /* -@@ -773,6 +798,7 @@ AlterOpFamily(AlterOpFamilyStmt *stmt) - int maxOpNumber, /* amstrategies value */ - maxProcNumber; /* amsupport value */ - HeapTuple tup; -+ Oid opfSecid; - Form_pg_am pg_am; - - /* Get necessary info about access method */ -@@ -805,6 +831,7 @@ AlterOpFamily(AlterOpFamilyStmt *stmt) - errmsg("operator family \"%s\" does not exist for access method \"%s\"", - NameListToString(stmt->opfamilyname), stmt->amname))); - opfamilyoid = HeapTupleGetOid(tup); -+ opfSecid = HeapTupleGetSecid(tup); - ReleaseSysCache(tup); - - /* -@@ -817,6 +844,9 @@ AlterOpFamily(AlterOpFamilyStmt *stmt) - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to alter an operator family"))); - -+ /* SELinux permission checks */ -+ sepgsql_opfamily_alter(opfamilyoid, NULL); -+ - /* - * ADD and DROP cases need separate code from here on down. - */ -@@ -893,6 +923,8 @@ AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid, - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - get_func_name(funcOid)); - #endif -+ /* SELinux permission check */ -+ sepgsql_opfamily_add_operator(opfamilyoid, operOid); - - /* Save the info */ - member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); -@@ -917,6 +949,8 @@ AlterOpFamilyAdd(List *opfamilyname, Oid amoid, Oid opfamilyoid, - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - get_func_name(funcOid)); - #endif -+ /* SELinux permission check */ -+ sepgsql_opfamily_add_procedure(opfamilyoid, funcOid); - - /* Save the info */ - member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); -@@ -1815,6 +1849,9 @@ RenameOpClass(List *name, const char *access_method, const char *newname) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux permission checks */ -+ sepgsql_opclass_alter(opcOid, newname); -+ - /* rename */ - namestrcpy(&(((Form_pg_opclass) GETSTRUCT(tup))->opcname), newname); - simple_heap_update(rel, &tup->t_self, tup); -@@ -1915,6 +1952,9 @@ RenameOpFamily(List *name, const char *access_method, const char *newname) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux check permissions */ -+ sepgsql_opfamily_alter(opfOid, newname); -+ - /* rename */ - namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname); - simple_heap_update(rel, &tup->t_self, tup); -@@ -2035,6 +2075,8 @@ AlterOpClassOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - } -+ /* SELinux permission check */ -+ sepgsql_opclass_alter(HeapTupleGetOid(tup), NULL); - - /* - * Modify the owner --- okay to scribble on tup because it's a copy -@@ -2162,6 +2204,8 @@ AlterOpFamilyOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - } -+ /* SELinux permission checks */ -+ sepgsql_opfamily_alter(HeapTupleGetOid(tup), NULL); - - /* - * Modify the owner --- okay to scribble on tup because it's a copy -diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c -index 6c05611..c934424 100644 ---- a/src/backend/commands/operatorcmds.c -+++ b/src/backend/commands/operatorcmds.c -@@ -45,6 +45,7 @@ - #include "parser/parse_func.h" - #include "parser/parse_oper.h" - #include "parser/parse_type.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/lsyscache.h" - #include "utils/rel.h" -@@ -432,6 +433,8 @@ AlterOperatorOwner_internal(Relation rel, Oid operOid, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(oprForm->oprnamespace)); - } -+ /* SELinux permission check */ -+ sepgsql_operator_alter(operOid); - - /* - * Modify the owner --- okay to scribble on tup because it's a copy -diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c -index 3faf445..8f1e212 100644 ---- a/src/backend/commands/proclang.c -+++ b/src/backend/commands/proclang.c -@@ -30,6 +30,7 @@ - #include "miscadmin.h" - #include "parser/gramparse.h" - #include "parser/parse_func.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -151,7 +152,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) - NIL, - PointerGetDatum(NULL), - 1, -- 0); -+ 0, -+ NULL); - } - - /* -@@ -186,7 +188,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) - NIL, - PointerGetDatum(NULL), - 1, -- 0); -+ 0, -+ NULL); - } - } - else -@@ -275,10 +278,16 @@ create_proc_lang(const char *languageName, - bool nulls[Natts_pg_language]; - NameData langname; - HeapTuple tup; -+ Oid langSecid; - ObjectAddress myself, - referenced; - - /* -+ * SELinux permission checks -+ */ -+ langSecid = sepgsql_language_create(languageName, handlerOid, valOid); -+ -+ /* - * Insert the new language into pg_language - */ - rel = heap_open(LanguageRelationId, RowExclusiveLock); -@@ -297,6 +306,8 @@ create_proc_lang(const char *languageName, - nulls[Anum_pg_language_lanacl - 1] = true; - - tup = heap_form_tuple(tupDesc, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, langSecid); - - simple_heap_insert(rel, tup); - -@@ -518,6 +529,9 @@ RenameLanguage(const char *oldname, const char *newname) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, - oldname); - -+ /* SELinux permission checks */ -+ sepgsql_language_alter(HeapTupleGetOid(tup)); -+ - /* rename */ - namestrcpy(&(((Form_pg_language) GETSTRUCT(tup))->lanname), newname); - simple_heap_update(rel, &tup->t_self, tup); -@@ -613,6 +627,9 @@ AlterLanguageOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) - /* Must be able to become new owner */ - check_is_member_of_role(GetUserId(), newOwnerId); - -+ /* SELinux permission checks */ -+ sepgsql_language_alter(HeapTupleGetOid(tup)); -+ - memset(repl_null, false, sizeof(repl_null)); - memset(repl_repl, false, sizeof(repl_repl)); - -diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c -index 0d047cf..748bdd6 100644 ---- a/src/backend/commands/schemacmds.c -+++ b/src/backend/commands/schemacmds.c -@@ -25,6 +25,7 @@ - #include "commands/schemacmds.h" - #include "miscadmin.h" - #include "parser/parse_utilcmd.h" -+#include "security/sepgsql.h" - #include "tcop/utility.h" - #include "utils/acl.h" - #include "utils/builtins.h" -@@ -48,6 +49,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) - ListCell *parsetree_item; - Oid owner_uid; - Oid saved_uid; -+ Oid nspsecid; - int save_sec_context; - AclResult aclresult; - -@@ -75,6 +77,10 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) - - check_is_member_of_role(saved_uid, owner_uid); - -+ /* SELinux checks db_schema:{create} */ -+ nspsecid = sepgsql_schema_create(schemaName, false, -+ (DefElem *)stmt->secLabel); -+ - /* Additional check to protect reserved schema names */ - if (!allowSystemTableMods && IsReservedName(schemaName)) - ereport(ERROR, -@@ -95,7 +101,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) - save_sec_context | SECURITY_LOCAL_USERID_CHANGE); - - /* Create the schema's namespace */ -- namespaceId = NamespaceCreate(schemaName, owner_uid); -+ namespaceId = NamespaceCreate(schemaName, owner_uid, nspsecid); - - /* Advance cmd counter to make the namespace visible */ - CommandCounterIncrement(); -@@ -268,8 +274,7 @@ RenameSchema(const char *oldname, const char *newname) - errmsg("schema \"%s\" does not exist", oldname))); - - /* make sure the new name doesn't exist */ -- if (HeapTupleIsValid( -- SearchSysCache(NAMESPACENAME, -+ if (HeapTupleIsValid(SearchSysCache(NAMESPACENAME, - CStringGetDatum(newname), - 0, 0, 0))) - ereport(ERROR, -@@ -287,6 +292,9 @@ RenameSchema(const char *oldname, const char *newname) - aclcheck_error(aclresult, ACL_KIND_DATABASE, - get_database_name(MyDatabaseId)); - -+ /* SELinux checks db_schema:{setattr} */ -+ sepgsql_schema_alter(HeapTupleGetOid(tup)); -+ - if (!allowSystemTableMods && IsReservedName(newname)) - ereport(ERROR, - (errcode(ERRCODE_RESERVED_NAME), -@@ -398,6 +406,9 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_DATABASE, - get_database_name(MyDatabaseId)); - -+ /* SELinux checks db_schema:{setattr} */ -+ sepgsql_schema_alter(HeapTupleGetOid(tup)); -+ - memset(repl_null, false, sizeof(repl_null)); - memset(repl_repl, false, sizeof(repl_repl)); - -@@ -432,3 +443,51 @@ AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerId) - } - - } -+ -+/* -+ * ALTER SCHEMA name SECURITY_LABEL [=] newlabel -+ */ -+void -+AlterSchemaSecLabel(const char *name, DefElem *secLabel) -+{ -+ Relation rel; -+ HeapTuple oldtup; -+ HeapTuple newtup; -+ Oid secid; -+ bool replaces[Natts_pg_namespace]; -+ -+ /* open pg_namespace relation */ -+ rel = heap_open(NamespaceRelationId, RowExclusiveLock); -+ oldtup = SearchSysCache(NAMESPACENAME, -+ CStringGetDatum(name), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(oldtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_UNDEFINED_SCHEMA), -+ errmsg("schema \"%s\" does not exist", name))); -+ -+ memset(replaces, false, sizeof(replaces)); -+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), -+ NULL, NULL, replaces); -+ if (!HeapTupleHasSecid(newtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security label on \"%s\"", name))); -+ -+ ReleaseSysCache(oldtup); -+ -+ /* DAC permission check */ -+ if (!pg_namespace_ownercheck(HeapTupleGetOid(newtup), GetUserId())) -+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, name); -+ /* SELinux checks db_schema:{setattr relabelfrom relabelto} */ -+ secid = sepgsql_schema_relabel(HeapTupleGetOid(newtup), secLabel); -+ HeapTupleSetSecid(newtup, secid); -+ -+ simple_heap_update(rel, &newtup->t_self, newtup); -+ -+ CatalogUpdateIndexes(rel, newtup); -+ -+ heap_freetuple(newtup); -+ -+ heap_close(rel, RowExclusiveLock); -+} -diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c -index abc65aa..9507ef1 100644 ---- a/src/backend/commands/sequence.c -+++ b/src/backend/commands/sequence.c -@@ -26,6 +26,7 @@ - #include "commands/tablecmds.h" - #include "miscadmin.h" - #include "nodes/makefuncs.h" -+#include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/lmgr.h" - #include "storage/proc.h" -@@ -201,6 +202,7 @@ DefineSequence(CreateSeqStmt *seq) - stmt->options = list_make1(defWithOids(false)); - stmt->oncommit = ONCOMMIT_NOOP; - stmt->tablespacename = NULL; -+ stmt->secLabel = seq->secLabel; - - seqoid = DefineRelation(stmt, RELKIND_SEQUENCE); - -@@ -328,6 +330,8 @@ AlterSequence(AlterSeqStmt *stmt) - if (!pg_class_ownercheck(relid, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - stmt->sequence->relname); -+ /* SELinux checks db_sequence:{setattr} */ -+ sepgsql_relation_alter(relid, NULL, InvalidOid); - - /* do the work */ - AlterSequenceInternal(relid, stmt->options); -@@ -467,6 +471,9 @@ nextval_internal(Oid relid) - errmsg("permission denied for sequence %s", - RelationGetRelationName(seqrel)))); - -+ /* SELinux check db_sequence:{next_value} */ -+ sepgsql_sequence_next_value(elm->relid); -+ - if (elm->last != elm->cached) /* some numbers were cached */ - { - Assert(elm->last_valid); -@@ -662,6 +669,9 @@ currval_oid(PG_FUNCTION_ARGS) - errmsg("permission denied for sequence %s", - RelationGetRelationName(seqrel)))); - -+ /* SELinux check db_sequence:{get_value} */ -+ sepgsql_sequence_get_value(elm->relid); -+ - if (!elm->last_valid) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), -@@ -706,6 +716,9 @@ lastval(PG_FUNCTION_ARGS) - errmsg("permission denied for sequence %s", - RelationGetRelationName(seqrel)))); - -+ /* SELinux check db_sequence:{get_value} */ -+ sepgsql_sequence_get_value(last_used_seq->relid); -+ - result = last_used_seq->last; - relation_close(seqrel, NoLock); - -@@ -742,6 +755,9 @@ do_setval(Oid relid, int64 next, bool iscalled) - errmsg("permission denied for sequence %s", - RelationGetRelationName(seqrel)))); - -+ /* SELinux check db_sequence:{set_value} */ -+ sepgsql_sequence_set_value(elm->relid); -+ - /* lock page' buffer and read tuple */ - seq = read_info(elm, seqrel, &buf); - -diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c -index 96dda00..85b8800 100644 ---- a/src/backend/commands/tablecmds.c -+++ b/src/backend/commands/tablecmds.c -@@ -62,6 +62,7 @@ - #include "parser/parser.h" - #include "rewrite/rewriteDefine.h" - #include "rewrite/rewriteHandler.h" -+#include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/lmgr.h" - #include "storage/smgr.h" -@@ -260,8 +261,8 @@ static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, - static void ATRewriteTables(List **wqueue); - static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap); - static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel); --static void ATSimplePermissions(Relation rel, bool allowView); --static void ATSimplePermissionsRelationOrIndex(Relation rel); -+static void ATSimplePermissions(Relation rel, const char *colname, bool allowView); -+static void ATSimplePermissionsRelationOrIndex(Relation rel, const char *colname); - static void ATSimpleRecursion(List **wqueue, Relation rel, - AlterTableCmd *cmd, bool recurse); - static void ATOneLevelRecursion(List **wqueue, Relation rel, -@@ -351,6 +352,7 @@ DefineRelation(CreateStmt *stmt, char relkind) - List *rawDefaults; - List *cookedDefaults; - Datum reloptions; -+ Oid *secLabels; - ListCell *listptr; - AttrNumber attnum; - static char *validnsps[] = HEAP_RELOPT_NAMESPACES; -@@ -454,6 +456,16 @@ DefineRelation(CreateStmt *stmt, char relkind) - localHasOids = interpretOidsOption(stmt->options); - descriptor->tdhasoid = (localHasOids || parentOidCount > 0); - -+ /* SELinux checks db_table:{create} and db_column:{create} */ -+ secLabels = sepgsql_relation_create(relname, -+ relkind, -+ descriptor, -+ namespaceId, -+ (DefElem *)stmt->secLabel, -+ schema, -+ false, -+ true); -+ - /* - * Find columns with default values and prepare for insertion of the - * defaults. Pre-cooked (that is, inherited) defaults go into a list of -@@ -523,7 +535,8 @@ DefineRelation(CreateStmt *stmt, char relkind) - parentOidCount, - stmt->oncommit, - reloptions, -- allowSystemTableMods); -+ allowSystemTableMods, -+ secLabels); - - StoreCatalogInheritance(relationId, inheritOids); - -@@ -897,6 +910,8 @@ ExecuteTruncate(TruncateStmt *stmt) - if (!pg_class_ownercheck(seq_relid, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - RelationGetRelationName(seq_rel)); -+ /* SELinux checks */ -+ sepgsql_relation_alter(seq_relid, NULL, InvalidOid); - - seq_relids = lappend_oid(seq_relids, seq_relid); - -@@ -1052,6 +1067,9 @@ truncate_check_rel(Relation rel) - errmsg("permission denied: \"%s\" is a system catalog", - RelationGetRelationName(rel)))); - -+ /* SELinux: check db_table:{delete} permission */ -+ sepgsql_relation_truncate(rel); -+ - /* - * We can never allow truncation of shared or nailed-in-cache relations, - * because we can't support changing their relfilenode values. -@@ -1226,6 +1244,8 @@ MergeAttributes(List *schema, List *supers, bool istemp, - if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - RelationGetRelationName(relation)); -+ /* SELinux checks db_table:{setattr} */ -+ sepgsql_relation_alter(RelationGetRelid(relation), NULL, InvalidOid); - - /* - * Reject duplications in the list of parents. -@@ -1931,6 +1951,9 @@ renameatt(Oid myrelid, - errmsg("cannot rename system column \"%s\"", - oldattname))); - -+ /* SELinux checks db_column:{setattr} */ -+ sepgsql_attribute_alter(myrelid, oldattname); -+ - /* - * if the attribute is inherited, forbid the renaming, unless we are - * already inside a recursive rename. -@@ -2036,6 +2059,9 @@ RenameRelation(Oid myrelid, const char *newrelname, ObjectType reltype) - Oid namespaceId; - char relkind; - -+ /* SELinux checks */ -+ sepgsql_relation_alter(myrelid, newrelname, InvalidOid); -+ - /* - * Grab an exclusive lock on the target table, index, sequence or view, - * which we will NOT release until end of transaction. -@@ -2369,14 +2395,14 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, - switch (cmd->subtype) - { - case AT_AddColumn: /* ADD COLUMN */ -- ATSimplePermissions(rel, false); -+ ATSimplePermissions(rel, NULL, false); - /* Performs own recursion */ - ATPrepAddColumn(wqueue, rel, recurse, cmd); - pass = AT_PASS_ADD_COL; - break; - case AT_AddColumnToView: /* add column via CREATE OR REPLACE - * VIEW */ -- ATSimplePermissions(rel, true); -+ ATSimplePermissions(rel, NULL, true); - /* Performs own recursion */ - ATPrepAddColumn(wqueue, rel, recurse, cmd); - pass = AT_PASS_ADD_COL; -@@ -2389,19 +2415,19 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, - * substitutes default values into INSERTs before it expands - * rules. - */ -- ATSimplePermissions(rel, true); -+ ATSimplePermissions(rel, cmd->name, true); - ATSimpleRecursion(wqueue, rel, cmd, recurse); - /* No command-specific prep needed */ - pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP; - break; - case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ -- ATSimplePermissions(rel, false); -+ ATSimplePermissions(rel, cmd->name, false); - ATSimpleRecursion(wqueue, rel, cmd, recurse); - /* No command-specific prep needed */ - pass = AT_PASS_DROP; - break; - case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */ -- ATSimplePermissions(rel, false); -+ ATSimplePermissions(rel, cmd->name, false); - ATSimpleRecursion(wqueue, rel, cmd, recurse); - /* No command-specific prep needed */ - pass = AT_PASS_ADD_CONSTR; -@@ -2413,13 +2439,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, - pass = AT_PASS_COL_ATTRS; - break; - case AT_SetStorage: /* ALTER COLUMN STORAGE */ -- ATSimplePermissions(rel, false); -+ ATSimplePermissions(rel, cmd->name, false); - ATSimpleRecursion(wqueue, rel, cmd, recurse); - /* No command-specific prep needed */ - pass = AT_PASS_COL_ATTRS; - break; - case AT_DropColumn: /* DROP COLUMN */ -- ATSimplePermissions(rel, false); -+ ATSimplePermissions(rel, NULL, false); - /* Recursion occurs during execution phase */ - /* No command-specific prep needed except saving recurse flag */ - if (recurse) -@@ -2427,13 +2453,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, - pass = AT_PASS_DROP; - break; - case AT_AddIndex: /* ADD INDEX */ -- ATSimplePermissions(rel, false); -+ ATSimplePermissions(rel, NULL, false); - /* This command never recurses */ - /* No command-specific prep needed */ - pass = AT_PASS_ADD_INDEX; - break; - case AT_AddConstraint: /* ADD CONSTRAINT */ -- ATSimplePermissions(rel, false); -+ ATSimplePermissions(rel, NULL, false); - /* Recursion occurs during execution phase */ - /* No command-specific prep needed except saving recurse flag */ - if (recurse) -@@ -2441,7 +2467,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, - pass = AT_PASS_ADD_CONSTR; - break; - case AT_DropConstraint: /* DROP CONSTRAINT */ -- ATSimplePermissions(rel, false); -+ ATSimplePermissions(rel, NULL, false); - /* Recursion occurs during execution phase */ - /* No command-specific prep needed except saving recurse flag */ - if (recurse) -@@ -2449,7 +2475,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, - pass = AT_PASS_DROP; - break; - case AT_AlterColumnType: /* ALTER COLUMN TYPE */ -- ATSimplePermissions(rel, false); -+ ATSimplePermissions(rel, cmd->name, false); - /* Performs own recursion */ - ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd); - pass = AT_PASS_ALTER_TYPE; -@@ -2461,20 +2487,20 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, - break; - case AT_ClusterOn: /* CLUSTER ON */ - case AT_DropCluster: /* SET WITHOUT CLUSTER */ -- ATSimplePermissions(rel, false); -+ ATSimplePermissions(rel, NULL, false); - /* These commands never recurse */ - /* No command-specific prep needed */ - pass = AT_PASS_MISC; - break; - case AT_AddOids: /* SET WITH OIDS */ -- ATSimplePermissions(rel, false); -+ ATSimplePermissions(rel, NULL, false); - /* Performs own recursion */ - if (!rel->rd_rel->relhasoids || recursing) - ATPrepAddOids(wqueue, rel, recurse, cmd); - pass = AT_PASS_ADD_COL; - break; - case AT_DropOids: /* SET WITHOUT OIDS */ -- ATSimplePermissions(rel, false); -+ ATSimplePermissions(rel, NULL, false); - /* Performs own recursion */ - if (rel->rd_rel->relhasoids) - { -@@ -2488,14 +2514,14 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, - pass = AT_PASS_DROP; - break; - case AT_SetTableSpace: /* SET TABLESPACE */ -- ATSimplePermissionsRelationOrIndex(rel); -+ ATSimplePermissionsRelationOrIndex(rel, NULL); - /* This command never recurses */ - ATPrepSetTableSpace(tab, rel, cmd->name); - pass = AT_PASS_MISC; /* doesn't actually matter */ - break; - case AT_SetRelOptions: /* SET (...) */ - case AT_ResetRelOptions: /* RESET (...) */ -- ATSimplePermissionsRelationOrIndex(rel); -+ ATSimplePermissionsRelationOrIndex(rel, NULL); - /* This command never recurses */ - /* No command-specific prep needed */ - pass = AT_PASS_MISC; -@@ -2514,7 +2540,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, - case AT_DisableRule: - case AT_AddInherit: /* INHERIT / NO INHERIT */ - case AT_DropInherit: -- ATSimplePermissions(rel, false); -+ ATSimplePermissions(rel, NULL, false); - /* These commands never recurse */ - /* No command-specific prep needed */ - pass = AT_PASS_MISC; -@@ -2860,8 +2886,9 @@ ATRewriteTables(List **wqueue) - /* - * The new relation is local to our transaction and we know - * nothing depends on it, so DROP_RESTRICT should be OK. -+ * SELinux does not apply any permission checks here. - */ -- performDeletion(&object, DROP_RESTRICT); -+ performDeletionNoPerms(&object, DROP_RESTRICT); - /* performDeletion does CommandCounterIncrement at end */ - - /* -@@ -3086,11 +3113,14 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) - if (newrel) - { - Oid tupOid = InvalidOid; -+ Oid tupSecid = InvalidOid; - - /* Extract data from old tuple */ - heap_deform_tuple(tuple, oldTupDesc, values, isnull); - if (oldTupDesc->tdhasoid) - tupOid = HeapTupleGetOid(tuple); -+ if (HeapTupleHasSecid(tuple)) -+ tupSecid = HeapTupleGetSecid(tuple); - - /* Set dropped attributes to null in new tuple */ - foreach(lc, dropped_attrs) -@@ -3122,6 +3152,9 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) - /* Preserve OID, if any */ - if (newTupDesc->tdhasoid) - HeapTupleSetOid(tuple, tupOid); -+ /* Preserve SID, if any */ -+ if (HeapTupleHasSecid(tuple)) -+ HeapTupleSetSecid(tuple, tupSecid); - } - - /* Now check any constraints on the possibly-changed tuple */ -@@ -3223,7 +3256,7 @@ ATGetQueueEntry(List **wqueue, Relation rel) - * - Ensure that it is not a system table - */ - static void --ATSimplePermissions(Relation rel, bool allowView) -+ATSimplePermissions(Relation rel, const char *colName, bool allowView) - { - if (rel->rd_rel->relkind != RELKIND_RELATION) - { -@@ -3247,6 +3280,12 @@ ATSimplePermissions(Relation rel, bool allowView) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - RelationGetRelationName(rel)); - -+ /* SELinux checks */ -+ if (!colName) -+ sepgsql_relation_alter(RelationGetRelid(rel), NULL, InvalidOid); -+ else -+ sepgsql_attribute_alter(RelationGetRelid(rel), colName); -+ - if (!allowSystemTableMods && IsSystemRelation(rel)) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -@@ -3262,7 +3301,7 @@ ATSimplePermissions(Relation rel, bool allowView) - * - Ensure that it is not a system table - */ - static void --ATSimplePermissionsRelationOrIndex(Relation rel) -+ATSimplePermissionsRelationOrIndex(Relation rel, const char *colName) - { - if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_INDEX) -@@ -3276,6 +3315,12 @@ ATSimplePermissionsRelationOrIndex(Relation rel) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - RelationGetRelationName(rel)); - -+ /* SELinux checks */ -+ if (!colName) -+ sepgsql_relation_alter(RelationGetRelid(rel), NULL, InvalidOid); -+ else -+ sepgsql_attribute_alter(RelationGetRelid(rel), colName); -+ - if (!allowSystemTableMods && IsSystemRelation(rel)) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -@@ -3519,6 +3564,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, - HeapTuple typeTuple; - Oid typeOid; - int32 typmod; -+ Oid attsecid; - Form_pg_type tform; - Expr *defval; - -@@ -3556,6 +3602,9 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, - errmsg("child table \"%s\" has a conflicting \"%s\" column", - RelationGetRelationName(rel), colDef->colname))); - -+ /* SELinux checks db_column:{setattr} */ -+ sepgsql_attribute_alter(myrelid, colDef->colname); -+ - /* Bump the existing child att's inhcount */ - childatt->attinhcount++; - simple_heap_update(attrdesc, &tuple->t_self, tuple); -@@ -3595,6 +3644,9 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, - errmsg("column \"%s\" of relation \"%s\" already exists", - colDef->colname, RelationGetRelationName(rel)))); - -+ /* SELinux checks db_column:{create} */ -+ attsecid = sepgsql_attribute_create(myrelid, colDef); -+ - /* Determine the new attribute's number */ - if (isOid) - newattnum = ObjectIdAttributeNumber; -@@ -3637,7 +3689,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, - - ReleaseSysCache(typeTuple); - -- InsertPgAttributeTuple(attrdesc, &attribute, NULL); -+ InsertPgAttributeTuple(attrdesc, &attribute, NULL, attsecid); - - heap_close(attrdesc, RowExclusiveLock); - -@@ -4026,6 +4078,8 @@ ATPrepSetStatistics(Relation rel, const char *colName, Node *flagValue) - if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - RelationGetRelationName(rel)); -+ /* SELinux checks */ -+ sepgsql_attribute_alter(RelationGetRelid(rel), colName); - } - - static void -@@ -4181,7 +4235,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, - - /* At top level, permission check was done in ATPrepCmd, else do it */ - if (recursing) -- ATSimplePermissions(rel, false); -+ ATSimplePermissions(rel, NULL, false); - - /* - * get the number of the attribute -@@ -4483,7 +4537,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, - - /* At top level, permission check was done in ATPrepCmd, else do it */ - if (recursing) -- ATSimplePermissions(rel, false); -+ ATSimplePermissions(rel, NULL, false); - - /* - * Call AddRelationNewConstraints to do the work, making sure it works on -@@ -5385,7 +5439,7 @@ ATExecDropConstraint(Relation rel, const char *constrName, - - /* At top level, permission check was done in ATPrepCmd, else do it */ - if (recursing) -- ATSimplePermissions(rel, false); -+ ATSimplePermissions(rel, NULL, false); - - conrel = heap_open(ConstraintRelationId, RowExclusiveLock); - -@@ -6319,6 +6373,8 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - } -+ /* SELinux checks db_table:{setattr} */ -+ sepgsql_relation_alter(relationOid, NULL, InvalidOid); - } - - memset(repl_null, false, sizeof(repl_null)); -@@ -6923,7 +6979,7 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent) - * Must be owner of both parent and child -- child was checked by - * ATSimplePermissions call in ATPrepCmd - */ -- ATSimplePermissions(parent_rel, false); -+ ATSimplePermissions(parent_rel, NULL, false); - - /* Permanent rels cannot inherit from temporary ones */ - if (parent_rel->rd_istemp && !child_rel->rd_istemp) -@@ -7581,6 +7637,9 @@ AlterTableNamespace(RangeVar *relation, const char *newschema, - RelationGetRelationName(rel), - newschema))); - -+ /* SELinux checks */ -+ sepgsql_relation_alter(relid, NULL, nspOid); -+ - /* disallow renaming into or out of temp schemas */ - if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid)) - ereport(ERROR, -@@ -7773,6 +7832,134 @@ AlterSeqNamespaces(Relation classRel, Relation rel, - relation_close(depRel, AccessShareLock); - } - -+/* -+ * ALTER TABLE/SEQUENCE name SECURITY_LABEL [=] newlabel -+ * ALTER TABLE/SEQUENCE name ALTER column SECURITY_LABEL [=] newlabel -+ */ -+static void -+ExecRelationSetSecLabel(Oid relid, DefElem *seclabel) -+{ -+ Relation rel; -+ HeapTuple oldtup; -+ HeapTuple newtup; -+ Oid secid; -+ bool replaces[Natts_pg_class]; -+ -+ rel = heap_open(RelationRelationId, RowExclusiveLock); -+ oldtup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(oldtup)) -+ elog(ERROR, "cache lookup failed for relation: %u", relid); -+ -+ memset(replaces, false, sizeof(replaces)); -+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), -+ NULL, NULL, replaces); -+ if (!HeapTupleHasSecid(newtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security label on \"%s\"", -+ get_rel_name(relid)))); -+ -+ ReleaseSysCache(oldtup); -+ -+ /* SELinux checks db_table:{setattr relabelfrom relabelto} */ -+ secid = sepgsql_relation_relabel(relid, seclabel); -+ -+ HeapTupleSetSecid(newtup, secid); -+ -+ simple_heap_update(rel, &newtup->t_self, newtup); -+ -+ CatalogUpdateIndexes(rel, newtup); -+ -+ heap_freetuple(newtup); -+ -+ heap_close(rel, RowExclusiveLock); -+} -+ -+static void -+ExecAttributeSetSecLabel(Oid relid, const char *attname, DefElem *seclabel) -+{ -+ Relation rel; -+ HeapTuple oldtup; -+ HeapTuple newtup; -+ AttrNumber attnum; -+ Oid secid; -+ bool replaces[Natts_pg_attribute]; -+ -+ rel = heap_open(AttributeRelationId, RowExclusiveLock); -+ oldtup = SearchSysCacheAttName(relid, attname); -+ if (!HeapTupleIsValid(oldtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_UNDEFINED_COLUMN), -+ errmsg("column \"%s\" of relation \"%s\" does not exist", -+ attname, get_rel_name(relid)))); -+ attnum = ((Form_pg_attribute) GETSTRUCT(oldtup))->attnum; -+ -+ memset(replaces, false, sizeof(replaces)); -+ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), -+ NULL, NULL, replaces); -+ if (!HeapTupleHasSecid(newtup)) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security context on \"%s.%s\"", -+ get_rel_name(relid), attname))); -+ -+ ReleaseSysCache(oldtup); -+ -+ /* SELinux checks db_column:{setattr relabelfrom relabelto} */ -+ secid = sepgsql_attribute_relabel(relid, attnum, seclabel); -+ -+ HeapTupleSetSecid(newtup, secid); -+ -+ simple_heap_update(rel, &newtup->t_self, newtup); -+ -+ CatalogUpdateIndexes(rel, newtup); -+ -+ heap_freetuple(newtup); -+ -+ heap_close(rel, RowExclusiveLock); -+} -+ -+void -+AlterRelationSecLabel(RangeVar *relation, const char *attname, -+ ObjectType objtype, DefElem *seclabel) -+{ -+ Oid relid; -+ char relkind; -+ -+ /* Check relation type against type specified in the ALTER command */ -+ relid = RangeVarGetRelid(relation, false); -+ relkind = get_rel_relkind(relid); -+ -+ switch (objtype) -+ { -+ case OBJECT_TABLE: -+ case OBJECT_COLUMN: -+ if (relkind != RELKIND_RELATION) -+ ereport(ERROR, -+ (errcode(ERRCODE_WRONG_OBJECT_TYPE), -+ errmsg("\"%s\" is not a table", get_rel_name(relid)))); -+ break; -+ -+ case OBJECT_SEQUENCE: -+ if (relkind != RELKIND_SEQUENCE) -+ ereport(ERROR, -+ (errcode(ERRCODE_WRONG_OBJECT_TYPE), -+ errmsg("\"%s\" is not a sequence", get_rel_name(relid)))); -+ break; -+ -+ default: -+ elog(ERROR, "unrecognized object type: %d", (int)objtype); -+ break; -+ } -+ -+ /* Exec set security label */ -+ if (objtype != OBJECT_COLUMN) -+ ExecRelationSetSecLabel(relid, seclabel); -+ else -+ ExecAttributeSetSecLabel(relid, attname, seclabel); -+} - - /* - * This code supports -diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c -index f432e74..469b5cb 100644 ---- a/src/backend/commands/trigger.c -+++ b/src/backend/commands/trigger.c -@@ -33,6 +33,7 @@ - #include "nodes/makefuncs.h" - #include "parser/parse_func.h" - #include "pgstat.h" -+#include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "tcop/utility.h" - #include "utils/acl.h" -@@ -182,6 +183,10 @@ CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid, bool checkPermissions) - NameListToString(stmt->funcname)))); - } - -+ /* SELinux checks */ -+ if (checkPermissions) -+ sepgsql_trigger_create(RelationGetRelid(rel), stmt->trigname, funcoid); -+ - /* - * If the command is a user-entered CREATE CONSTRAINT TRIGGER command that - * references one of the built-in RI_FKey trigger functions, assume it is -@@ -746,6 +751,7 @@ DropTrigger(Oid relid, const char *trigname, DropBehavior behavior, - if (!pg_class_ownercheck(relid, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - get_rel_name(relid)); -+ sepgsql_trigger_drop(relid, trigname); - - object.classId = TriggerRelationId; - object.objectId = HeapTupleGetOid(tup); -@@ -862,6 +868,9 @@ renametrig(Oid relid, - */ - targetrel = heap_open(relid, AccessExclusiveLock); - -+ /* SELinux checks */ -+ sepgsql_trigger_alter(relid, oldname); -+ - /* - * Scan pg_trigger twice for existing triggers on relation. We do this in - * order to ensure a trigger does not exist with newname (The unique index -diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c -index a1f301b..16cfa5d 100644 ---- a/src/backend/commands/tsearchcmds.c -+++ b/src/backend/commands/tsearchcmds.c -@@ -35,6 +35,7 @@ - #include "miscadmin.h" - #include "nodes/makefuncs.h" - #include "parser/parse_func.h" -+#include "security/sepgsql.h" - #include "tsearch/ts_cache.h" - #include "tsearch/ts_public.h" - #include "tsearch/ts_utils.h" -@@ -171,6 +172,7 @@ DefineTSParser(List *names, List *parameters) - NameData pname; - Oid prsOid; - Oid namespaceoid; -+ Oid secid; - - if (!superuser()) - ereport(ERROR, -@@ -250,12 +252,22 @@ DefineTSParser(List *names, List *parameters) - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("text search parser lextypes method is required"))); - -+ /* Permission checks */ -+ secid = sepgsql_ts_parser_create(prsname, namespaceoid, -+ DatumGetObjectId(values[Anum_pg_ts_parser_prsstart - 1]), -+ DatumGetObjectId(values[Anum_pg_ts_parser_prstoken - 1]), -+ DatumGetObjectId(values[Anum_pg_ts_parser_prsend - 1]), -+ DatumGetObjectId(values[Anum_pg_ts_parser_prsheadline - 1]), -+ DatumGetObjectId(values[Anum_pg_ts_parser_prslextype - 1])); -+ - /* - * Looks good, insert - */ - prsRel = heap_open(TSParserRelationId, RowExclusiveLock); - - tup = heap_form_tuple(prsRel->rd_att, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, secid); - - prsOid = simple_heap_insert(prsRel, tup); - -@@ -372,6 +384,9 @@ RenameTSParser(List *oldname, const char *newname) - - prsId = TSParserGetPrsid(oldname, false); - -+ /* SELinux checks */ -+ sepgsql_ts_parser_alter(prsId, newname); -+ - tup = SearchSysCacheCopy(TSPARSEROID, - ObjectIdGetDatum(prsId), - 0, 0, 0); -@@ -503,6 +518,7 @@ DefineTSDictionary(List *names, List *parameters) - List *dictoptions = NIL; - Oid dictOid; - Oid namespaceoid; -+ Oid secid; - AclResult aclresult; - char *dictname; - -@@ -515,6 +531,9 @@ DefineTSDictionary(List *names, List *parameters) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceoid)); - -+ /* SELinux check */ -+ secid = sepgsql_ts_dict_create(dictname, namespaceoid); -+ - /* - * loop over the definition list and extract the information we need. - */ -@@ -563,6 +582,8 @@ DefineTSDictionary(List *names, List *parameters) - dictRel = heap_open(TSDictionaryRelationId, RowExclusiveLock); - - tup = heap_form_tuple(dictRel->rd_att, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, secid); - - dictOid = simple_heap_insert(dictRel, tup); - -@@ -621,6 +642,9 @@ RenameTSDictionary(List *oldname, const char *newname) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* SELinux checks */ -+ sepgsql_ts_dict_alter(dictId, newname); -+ - namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname); - simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); -@@ -762,6 +786,9 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, - NameListToString(stmt->dictname)); - -+ /* SELinux checks */ -+ sepgsql_ts_dict_alter(dictId, NULL); -+ - /* deserialize the existing set of options */ - opt = SysCacheGetAttr(TSDICTOID, tup, - Anum_pg_ts_dict_dictinitoption, -@@ -889,6 +916,8 @@ AlterTSDictionaryOwner(List *name, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - } -+ /* SELinux checks */ -+ sepgsql_ts_dict_alter(dictId, NULL); - - form->dictowner = newOwnerId; - -@@ -999,6 +1028,7 @@ DefineTSTemplate(List *names, List *parameters) - NameData dname; - int i; - Oid dictOid; -+ Oid dictSecid; - Oid namespaceoid; - char *tmplname; - -@@ -1054,6 +1084,11 @@ DefineTSTemplate(List *names, List *parameters) - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("text search template lexize method is required"))); - -+ /* SELinux checks */ -+ dictSecid = sepgsql_ts_template_create(tmplname, namespaceoid, -+ DatumGetObjectId(values[Anum_pg_ts_template_tmplinit - 1]), -+ DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1])); -+ - /* - * Looks good, insert - */ -@@ -1061,6 +1096,8 @@ DefineTSTemplate(List *names, List *parameters) - tmplRel = heap_open(TSTemplateRelationId, RowExclusiveLock); - - tup = heap_form_tuple(tmplRel->rd_att, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, dictSecid); - - dictOid = simple_heap_insert(tmplRel, tup); - -@@ -1093,6 +1130,9 @@ RenameTSTemplate(List *oldname, const char *newname) - - tmplId = TSTemplateGetTmplid(oldname, false); - -+ /* Permission checks */ -+ sepgsql_ts_template_alter(tmplId, newname); -+ - tup = SearchSysCacheCopy(TSTEMPLATEOID, - ObjectIdGetDatum(tmplId), - 0, 0, 0); -@@ -1335,6 +1375,7 @@ DefineTSConfiguration(List *names, List *parameters) - Oid sourceOid = InvalidOid; - Oid prsOid = InvalidOid; - Oid cfgOid; -+ Oid cfgSecid; - ListCell *pl; - - /* Convert list of names to a name and namespace */ -@@ -1399,6 +1440,9 @@ DefineTSConfiguration(List *names, List *parameters) - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), - errmsg("text search parser is required"))); - -+ /* SELinux checks */ -+ cfgSecid = sepgsql_ts_config_create(cfgname, namespaceoid); -+ - /* - * Looks good, build tuple and insert - */ -@@ -1414,6 +1458,8 @@ DefineTSConfiguration(List *names, List *parameters) - cfgRel = heap_open(TSConfigRelationId, RowExclusiveLock); - - tup = heap_form_tuple(cfgRel->rd_att, values, nulls); -+ if (HeapTupleHasSecid(tup)) -+ HeapTupleSetSecid(tup, cfgSecid); - - cfgOid = simple_heap_insert(cfgRel, tup); - -@@ -1519,6 +1565,9 @@ RenameTSConfiguration(List *oldname, const char *newname) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - -+ /* permission checks */ -+ sepgsql_ts_config_alter(cfgId, newname); -+ - namestrcpy(&(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname), newname); - simple_heap_update(rel, &tup->t_self, tup); - CatalogUpdateIndexes(rel, tup); -@@ -1690,6 +1739,8 @@ AlterTSConfigurationOwner(List *name, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceOid)); - } -+ /* SELinux checks */ -+ sepgsql_ts_config_alter(cfgId, NULL); - - form->cfgowner = newOwnerId; - -@@ -1727,6 +1778,9 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, - NameListToString(stmt->cfgname)); - -+ /* SELinux checks */ -+ sepgsql_ts_config_alter(HeapTupleGetOid(tup), NULL); -+ - relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock); - - /* Add or drop mappings */ -diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c -index 528a917..e10b914 100644 ---- a/src/backend/commands/typecmds.c -+++ b/src/backend/commands/typecmds.c -@@ -56,6 +56,7 @@ - #include "parser/parse_expr.h" - #include "parser/parse_func.h" - #include "parser/parse_type.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -1543,6 +1544,7 @@ AlterDomainDefault(List *names, Node *defaultRaw) - - /* Check it's a domain and check user has permission for ALTER DOMAIN */ - checkDomainOwner(tup, typename); -+ sepgsql_type_alter(domainoid, NULL, InvalidOid); - - /* Setup new tuple */ - MemSet(new_record, (Datum) 0, sizeof(new_record)); -@@ -1671,6 +1673,7 @@ AlterDomainNotNull(List *names, bool notNull) - - /* Check it's a domain and check user has permission for ALTER DOMAIN */ - checkDomainOwner(tup, typename); -+ sepgsql_type_alter(domainoid, NULL, InvalidOid); - - /* Is the domain already set to the desired constraint? */ - if (typTup->typnotnull == notNull) -@@ -1772,6 +1775,7 @@ AlterDomainDropConstraint(List *names, const char *constrName, - - /* Check it's a domain and check user has permission for ALTER DOMAIN */ - checkDomainOwner(tup, typename); -+ sepgsql_type_alter(domainoid, NULL, InvalidOid); - - /* Grab an appropriate lock on the pg_constraint relation */ - conrel = heap_open(ConstraintRelationId, RowExclusiveLock); -@@ -1848,6 +1852,7 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) - - /* Check it's a domain and check user has permission for ALTER DOMAIN */ - checkDomainOwner(tup, typename); -+ sepgsql_type_alter(domainoid, NULL, InvalidOid); - - /* Check for unsupported constraint types */ - if (IsA(newConstraint, FkConstraint)) -@@ -2470,6 +2475,9 @@ RenameType(List *names, const char *newTypeName) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, - format_type_be(typeOid)); - -+ /* SELinux check permission */ -+ sepgsql_type_alter(typeOid, newTypeName, InvalidOid); -+ - /* - * If it's a composite type, we need to check that it really is a - * free-standing composite type, and not a table's rowtype. We want people -@@ -2590,6 +2598,8 @@ AlterTypeOwner(List *names, Oid newOwnerId) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(typTup->typnamespace)); - } -+ /* SELinux checks permissions */ -+ sepgsql_type_alter(HeapTupleGetOid(tup), NULL, InvalidOid); - - /* - * If it's a composite type, invoke ATExecChangeOwner so that we fix -@@ -2706,6 +2716,9 @@ AlterTypeNamespace(List *names, const char *newschema) - errhint("You can alter type %s, which will alter the array type as well.", - format_type_be(elemOid)))); - -+ /* SELinux checks permissions */ -+ sepgsql_type_alter(typeOid, NULL, nspOid); -+ - /* and do the work */ - AlterTypeNamespaceInternal(typeOid, nspOid, false, true); - } -diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c -index dcb30f8..737b58a 100644 ---- a/src/backend/commands/vacuum.c -+++ b/src/backend/commands/vacuum.c -@@ -32,6 +32,7 @@ - #include "catalog/namespace.h" - #include "catalog/pg_database.h" - #include "catalog/pg_namespace.h" -+#include "catalog/pg_security.h" - #include "catalog/storage.h" - #include "commands/dbcommands.h" - #include "commands/vacuum.h" -@@ -1209,6 +1210,9 @@ vacuum_rel(Oid relid, VacuumStmt *vacstmt, bool do_toast, bool for_wraparound, - /* all done with this class, but hold lock until commit */ - relation_close(onerel, NoLock); - -+ /* Also reclaim orphan security label */ -+ seclabelRelationReclaim(relid); -+ - /* - * Complete the transaction and free all temporary memory used. - */ -diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c -index dfbce72..1bfab03 100644 ---- a/src/backend/commands/view.c -+++ b/src/backend/commands/view.c -@@ -28,6 +28,7 @@ - #include "rewrite/rewriteDefine.h" - #include "rewrite/rewriteManip.h" - #include "rewrite/rewriteSupport.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -@@ -166,6 +167,9 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - RelationGetRelationName(rel)); - -+ /* SELinux checks */ -+ sepgsql_view_replace(viewOid); -+ - /* Also check it's not in use already */ - CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW"); - -diff --git a/src/backend/executor/execJunk.c b/src/backend/executor/execJunk.c -index 564347f..154b3c4 100644 ---- a/src/backend/executor/execJunk.c -+++ b/src/backend/executor/execJunk.c -@@ -60,7 +60,8 @@ - * An optional resultSlot can be passed as well. - */ - JunkFilter * --ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) -+ExecInitJunkFilter(List *targetList, bool hasoid, bool hasseclabel, -+ TupleTableSlot *slot) - { - JunkFilter *junkfilter; - TupleDesc cleanTupType; -@@ -72,7 +73,7 @@ ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) - /* - * Compute the tuple descriptor for the cleaned tuple. - */ -- cleanTupType = ExecCleanTypeFromTL(targetList, hasoid); -+ cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, hasseclabel); - - /* - * Use the given slot, or make a new slot if we weren't given one. -diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c -index 131be22..5c4205b 100644 ---- a/src/backend/executor/execMain.c -+++ b/src/backend/executor/execMain.c -@@ -39,6 +39,7 @@ - #include "access/xact.h" - #include "catalog/heap.h" - #include "catalog/namespace.h" -+#include "catalog/pg_security.h" - #include "catalog/toasting.h" - #include "commands/tablespace.h" - #include "commands/trigger.h" -@@ -50,6 +51,7 @@ - #include "optimizer/clauses.h" - #include "parser/parse_clause.h" - #include "parser/parsetree.h" -+#include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/lmgr.h" - #include "storage/smgr.h" -@@ -442,7 +444,10 @@ ExecCheckRTPerms(List *rangeTable) - - foreach(l, rangeTable) - { -- ExecCheckRTEPerms((RangeTblEntry *) lfirst(l)); -+ RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); -+ -+ ExecCheckRTEPerms(rte); -+ sepgsqlCheckRTEPerms(rte); - } - } - -@@ -901,16 +906,16 @@ InitPlan(QueryDesc *queryDesc, int eflags) - for (i = 0; i < as_nplans; i++) - { - PlanState *subplan = appendplans[i]; -+ Relation resultRel = resultRelInfo->ri_RelationDesc; - JunkFilter *j; - - if (operation == CMD_UPDATE) -- ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, -- subplan->plan->targetlist); -+ ExecCheckPlanOutput(resultRel, subplan->plan->targetlist); - - j = ExecInitJunkFilter(subplan->plan->targetlist, -- resultRelInfo->ri_RelationDesc->rd_att->tdhasoid, -- ExecAllocTableSlot(estate->es_tupleTable)); -- -+ RelationGetDescr(resultRel)->tdhasoid, -+ RelationGetDescr(resultRel)->tdhassecid, -+ ExecAllocTableSlot(estate->es_tupleTable)); - /* - * Since it must be UPDATE/DELETE, there had better be a - * "ctid" junk attribute in the tlist ... but ctid could -@@ -953,6 +958,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) - - j = ExecInitJunkFilter(planstate->plan->targetlist, - tupType->tdhasoid, -+ tupType->tdhassecid, - ExecAllocTableSlot(estate->es_tupleTable)); - estate->es_junkFilter = j; - if (estate->es_result_relation_info) -@@ -1023,7 +1029,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) - * 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); -@@ -1346,6 +1352,37 @@ ExecContextForcesOids(PlanState *planstate, bool *hasoids) - return false; - } - -+/* -+ * ExecContextForcesSecids -+ * -+ * We need to ensure that result tuples have space for security identifier. -+ * if the security feature need to store it within the given relation. -+ */ -+bool ExecContextForcesSecids(PlanState *planstate, bool *hassecid) -+{ -+ if (planstate->state->es_select_into) -+ { -+ *hassecid = securityTupleDescHasSecid(InvalidOid, -+ RELKIND_RELATION); -+ return true; -+ } -+ else -+ { -+ ResultRelInfo *ri = planstate->state->es_result_relation_info; -+ -+ if (ri && ri->ri_RelationDesc) -+ { -+ Oid relid = RelationGetRelid(ri->ri_RelationDesc); -+ char relkind = RelationGetForm(ri->ri_RelationDesc)->relkind; -+ -+ *hassecid = securityTupleDescHasSecid(relid, relkind); -+ -+ return true; -+ } -+ } -+ return false; -+} -+ - /* ---------------------------------------------------------------- - * ExecEndPlan - * -@@ -1426,6 +1463,58 @@ ExecEndPlan(PlanState *planstate, EState *estate) - } - } - -+/* -+ * fetchWritableSystemAttribute() fetches writable system column data -+ * using Junkfilter, and saves them at TupleTableSlot temporary. -+ * -+ * storeWritableSystemAttribute() copies these fetched data into -+ * header structure of HeapTuple. -+ */ -+static void -+fetchWritableSystemAttribute(JunkFilter *junkfilter, TupleTableSlot *slot, -+ Datum *tts_seclabel) -+{ -+ AttrNumber attno; -+ Datum datum; -+ bool isnull; -+ -+ /* for Security Label */ -+ attno = ExecFindJunkAttribute(junkfilter, SecurityAttributeName); -+ if (attno != InvalidAttrNumber) -+ { -+ datum = ExecGetJunkAttribute(slot, attno, &isnull); -+ if (isnull) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set NULL on \"%s\"", -+ SecurityAttributeName))); -+ *tts_seclabel = datum; -+ } -+} -+ -+static void -+storeWritableSystemAttribute(Relation rel, TupleTableSlot *slot, HeapTuple tuple) -+{ -+ Oid relid = RelationGetRelid(rel); -+ Oid secid; -+ -+ /* "security_label" */ -+ if (DatumGetPointer(slot->tts_seclabel) != NULL) -+ { -+ char *seclabel = TextDatumGetCString(slot->tts_seclabel); -+ -+ if (!HeapTupleHasSecid(tuple)) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to assign security label on \"%s\"", -+ RelationGetRelationName(rel)))); -+ secid = securityTransSecLabelIn(relid, seclabel); -+ HeapTupleSetSecid(tuple, secid); -+ } -+ else if (HeapTupleHasSecid(tuple)) -+ HeapTupleSetSecid(tuple, InvalidOid); -+} -+ - /* ---------------------------------------------------------------- - * ExecutePlan - * -@@ -1487,6 +1576,8 @@ ExecutePlan(EState *estate, - */ - for (;;) - { -+ Datum tts_seclabel = PointerGetDatum(NULL); -+ - /* Reset the per-output-tuple exprcontext */ - ResetPerTupleExprContext(estate); - -@@ -1631,6 +1722,11 @@ lnext: ; - } - - /* -+ * extract writable system attribute -+ */ -+ fetchWritableSystemAttribute(junkfilter, slot, &tts_seclabel); -+ -+ /* - * extract the 'ctid' junk attribute. - */ - if (operation == CMD_UPDATE || operation == CMD_DELETE) -@@ -1657,6 +1753,7 @@ lnext: ; - if (operation != CMD_DELETE) - slot = ExecFilterJunk(junkfilter, slot); - } -+ slot->tts_seclabel = tts_seclabel; - - /* - * now that we have a tuple, do the appropriate thing with it.. either -@@ -1781,6 +1878,8 @@ ExecInsert(TupleTableSlot *slot, - if (resultRelationDesc->rd_rel->relhasoids) - HeapTupleSetOid(tuple, InvalidOid); - -+ storeWritableSystemAttribute(resultRelationDesc, slot, tuple); -+ - /* BEFORE ROW INSERT Triggers */ - if (resultRelInfo->ri_TrigDesc && - resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) -@@ -1811,6 +1910,12 @@ ExecInsert(TupleTableSlot *slot, - } - - /* -+ * SELinux assigns default security label, and -+ * it also checks db_tuple:{insert} permission -+ */ -+ sepgsqlHeapTupleInsert(resultRelationDesc, tuple, false); -+ -+ /* - * Check the constraints of the tuple - */ - if (resultRelationDesc->rd_att->constr) -@@ -2018,6 +2123,8 @@ ExecUpdate(TupleTableSlot *slot, - resultRelInfo = estate->es_result_relation_info; - resultRelationDesc = resultRelInfo->ri_RelationDesc; - -+ storeWritableSystemAttribute(resultRelationDesc, slot, tuple); -+ - /* BEFORE ROW UPDATE Triggers */ - if (resultRelInfo->ri_TrigDesc && - resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) -@@ -2048,6 +2155,9 @@ ExecUpdate(TupleTableSlot *slot, - } - } - -+ /* SELinux checks db_tuple:{relabelfrom relabelto}, if needed */ -+ sepgsqlHeapTupleUpdate(resultRelationDesc, tupleid, tuple); -+ - /* - * Check the constraints of the tuple - * -@@ -2843,6 +2953,7 @@ OpenIntoRel(QueryDesc *queryDesc) - Oid namespaceId; - Oid tablespaceId; - Datum reloptions; -+ Oid *secLabels; - AclResult aclresult; - Oid intoRelationId; - TupleDesc tupdesc; -@@ -2886,6 +2997,14 @@ OpenIntoRel(QueryDesc *queryDesc) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(namespaceId)); - -+ /* SELinux checks */ -+ secLabels = sepgsql_relation_create(intoName, -+ RELKIND_RELATION, -+ queryDesc->tupDesc, -+ namespaceId, -+ NULL, NIL, -+ true, true); -+ - /* - * Select tablespace to use. If not specified, use default tablespace - * (which may in turn default to database's default). -@@ -2944,7 +3063,8 @@ OpenIntoRel(QueryDesc *queryDesc) - 0, - into->onCommit, - reloptions, -- allowSystemTableMods); -+ allowSystemTableMods, -+ secLabels); - - FreeTupleDesc(tupdesc); - -@@ -3069,6 +3189,10 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self) - if (myState->rel->rd_rel->relhasoids) - HeapTupleSetOid(tuple, InvalidOid); - -+ storeWritableSystemAttribute(myState->rel, slot, tuple); -+ /* SELinux checks db_tuple:{insert} */ -+ sepgsqlHeapTupleInsert(myState->rel, tuple, false); -+ - heap_insert(myState->rel, - tuple, - myState->estate->es_output_cid, -diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c -index 119ddbc..66f918e 100644 ---- a/src/backend/executor/execQual.c -+++ b/src/backend/executor/execQual.c -@@ -47,6 +47,7 @@ - #include "nodes/nodeFuncs.h" - #include "optimizer/planner.h" - #include "pgstat.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -@@ -1034,6 +1035,7 @@ init_fcache(Oid foid, FuncExprState *fcache, - aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid)); -+ sepgsql_proc_execute(foid); - - /* - * Safety check on nargs. Under normal circumstances this should never -@@ -4032,6 +4034,7 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(acoerce->elemfuncid)); -+ sepgsql_proc_execute(acoerce->elemfuncid); - - /* Set up the primary fmgr lookup information */ - fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc), -diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c -index 19fa4e6..44021f2 100644 ---- a/src/backend/executor/execScan.c -+++ b/src/backend/executor/execScan.c -@@ -20,6 +20,7 @@ - - #include "executor/executor.h" - #include "miscadmin.h" -+#include "security/rowlevel.h" - #include "utils/memutils.h" - - -@@ -53,6 +54,7 @@ ExecScan(ScanState *node, - ProjectionInfo *projInfo; - ExprDoneCond isDone; - TupleTableSlot *resultSlot; -+ Scan *scan = (Scan *)node->ps.plan; - - /* - * Fetch data from node -@@ -64,7 +66,7 @@ ExecScan(ScanState *node, - * If we have neither a qual to check nor a projection to do, just skip - * all the overhead and return the raw scan tuple. - */ -- if (!qual && !projInfo) -+ if (!qual && !projInfo && !scan->rowlvPerms) - return (*accessMtd) (node); - - /* -@@ -128,9 +130,18 @@ ExecScan(ScanState *node, - * when the qual is nil ... saves only a few cycles, but they add up - * ... - */ -- if (!qual || ExecQual(qual, econtext, false)) -+ if (rowlvExecScanFilter(scan, node->ss_currentRelation, slot) -+ && (!qual || ExecQual(qual, econtext, false))) - { - /* -+ * NOTE: On FK checks, the Row-level feature needs to raise -+ * an error after evaluation of all the given quals to avoid -+ * incorrect error reporting. We assume FK implementation -+ * does not use malicious functions as the quals. -+ */ -+ rowlvExecScanAbort(scan, node->ss_currentRelation, slot); -+ -+ /* - * Found a satisfactory scan tuple. - */ - if (projInfo) -@@ -197,6 +208,7 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc - int numattrs = tupdesc->natts; - int attrno; - bool hasoid; -+ bool hassecid; - ListCell *tlist_item = list_head(tlist); - - /* Check the tlist attributes */ -@@ -240,12 +252,16 @@ tlist_matches_tupdesc(PlanState *ps, List *tlist, Index varno, TupleDesc tupdesc - return false; /* tlist too long */ - - /* -- * If the plan context requires a particular hasoid setting, then that has -- * to match, too. -+ * If the plan context requires a particular hasoid or hassecid setting, -+ * then that has to match, too. - */ - if (ExecContextForcesOids(ps, &hasoid) && - hasoid != tupdesc->tdhasoid) - return false; - -+ if (ExecContextForcesSecids(ps, &hassecid) && -+ hassecid != tupdesc->tdhassecid) -+ return false; -+ - return true; - } -diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c -index 06142c9..c5f614a 100644 ---- a/src/backend/executor/execTuples.c -+++ b/src/backend/executor/execTuples.c -@@ -100,7 +100,7 @@ - - - static TupleDesc ExecTypeFromTLInternal(List *targetList, -- bool hasoid, bool skipjunk); -+ bool hasoid, bool hasseclabel, bool skipjunk); - - - /* ---------------------------------------------------------------- -@@ -968,9 +968,9 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType) - * ---------------------------------------------------------------- - */ - TupleDesc --ExecTypeFromTL(List *targetList, bool hasoid) -+ExecTypeFromTL(List *targetList, bool hasoid, bool hassecid) - { -- return ExecTypeFromTLInternal(targetList, hasoid, false); -+ return ExecTypeFromTLInternal(targetList, hasoid, hassecid, false); - } - - /* ---------------------------------------------------------------- -@@ -980,13 +980,14 @@ ExecTypeFromTL(List *targetList, bool hasoid) - * ---------------------------------------------------------------- - */ - TupleDesc --ExecCleanTypeFromTL(List *targetList, bool hasoid) -+ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hassecid) - { -- return ExecTypeFromTLInternal(targetList, hasoid, true); -+ return ExecTypeFromTLInternal(targetList, hasoid, hassecid, true); - } - - static TupleDesc --ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) -+ExecTypeFromTLInternal(List *targetList, bool hasoid, -+ bool hassecid, bool skipjunk) - { - TupleDesc typeInfo; - ListCell *l; -@@ -998,6 +999,7 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) - else - len = ExecTargetListLength(targetList); - typeInfo = CreateTemplateTupleDesc(len, hasoid); -+ typeInfo->tdhassecid = hassecid; - - foreach(l, targetList) - { -diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c -index 7033189..34faaeb 100644 ---- a/src/backend/executor/execUtils.c -+++ b/src/backend/executor/execUtils.c -@@ -512,6 +512,7 @@ void - ExecAssignResultTypeFromTL(PlanState *planstate) - { - bool hasoid; -+ bool hassecid; - TupleDesc tupDesc; - - if (ExecContextForcesOids(planstate, &hasoid)) -@@ -524,12 +525,15 @@ ExecAssignResultTypeFromTL(PlanState *planstate) - hasoid = false; - } - -+ if (!ExecContextForcesSecids(planstate, &hassecid)) -+ hassecid = false; -+ - /* - * ExecTypeFromTL needs the parse-time representation of the tlist, not a - * list of ExprStates. This is good because some plan nodes don't bother - * to set up planstate->targetlist ... - */ -- tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid); -+ tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid, hassecid); - ExecAssignResultType(planstate, tupDesc); - } - -diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c -index 1d679a9..8b46999 100644 ---- a/src/backend/executor/functions.c -+++ b/src/backend/executor/functions.c -@@ -1135,7 +1135,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, - - /* Set up junk filter if needed */ - if (junkFilter) -- *junkFilter = ExecInitJunkFilter(tlist, false, NULL); -+ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); - } - else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) - { -@@ -1167,7 +1167,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, - COERCE_DONTCARE); - /* Set up junk filter if needed */ - if (junkFilter) -- *junkFilter = ExecInitJunkFilter(tlist, false, NULL); -+ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); - return false; /* NOT returning whole tuple */ - } - } -@@ -1180,7 +1180,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, - * what the caller expects will happen at runtime. - */ - if (junkFilter) -- *junkFilter = ExecInitJunkFilter(tlist, false, NULL); -+ *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); - return true; - } - Assert(tupdesc); -diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c -index d7cccc5..6ba72ca 100644 ---- a/src/backend/executor/nodeAgg.c -+++ b/src/backend/executor/nodeAgg.c -@@ -81,6 +81,7 @@ - #include "parser/parse_agg.h" - #include "parser/parse_coerce.h" - #include "parser/parse_oper.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -@@ -1431,6 +1432,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(aggref->aggfnoid)); -+ sepgsql_proc_execute(aggref->aggfnoid); - - peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn; - peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn; -@@ -1454,6 +1456,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(transfn_oid)); -+ sepgsql_proc_execute(transfn_oid); - if (OidIsValid(finalfn_oid)) - { - aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner, -@@ -1461,6 +1464,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(finalfn_oid)); -+ sepgsql_proc_execute(finalfn_oid); - } - } - -diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c -index b6143e6..4394855 100644 ---- a/src/backend/executor/nodeMergejoin.c -+++ b/src/backend/executor/nodeMergejoin.c -@@ -98,6 +98,7 @@ - #include "executor/execdefs.h" - #include "executor/nodeMergejoin.h" - #include "miscadmin.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/lsyscache.h" - #include "utils/memutils.h" -@@ -215,6 +216,7 @@ MJExamineQuals(List *mergeclauses, - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(cmpproc)); -+ sepgsql_proc_execute(cmpproc); - - /* Set up the fmgr lookup information */ - fmgr_info(cmpproc, &(clause->cmpfinfo)); -diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c -index aff7a63..41dede3 100644 ---- a/src/backend/executor/nodeSubplan.c -+++ b/src/backend/executor/nodeSubplan.c -@@ -869,7 +869,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) - * (hack alert!). The righthand expressions will be evaluated in our - * own innerecontext. - */ -- tupDesc = ExecTypeFromTL(leftptlist, false); -+ tupDesc = ExecTypeFromTL(leftptlist, false, false); - slot = ExecAllocTableSlot(tupTable); - ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, -@@ -877,7 +877,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) - slot, - NULL); - -- tupDesc = ExecTypeFromTL(rightptlist, false); -+ tupDesc = ExecTypeFromTL(rightptlist, false, false); - slot = ExecAllocTableSlot(tupTable); - ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, -diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c -index 6674f67..fec5c4a 100644 ---- a/src/backend/executor/nodeWindowAgg.c -+++ b/src/backend/executor/nodeWindowAgg.c -@@ -43,6 +43,7 @@ - #include "optimizer/clauses.h" - #include "parser/parse_agg.h" - #include "parser/parse_coerce.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/datum.h" -@@ -1224,6 +1225,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(wfunc->winfnoid)); -+ sepgsql_proc_execute(wfunc->winfnoid); - - /* Fill in the perfuncstate data */ - perfuncstate->wfuncstate = wfuncstate; -@@ -1418,6 +1420,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(transfn_oid)); -+ sepgsql_proc_execute(transfn_oid); - if (OidIsValid(finalfn_oid)) - { - aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner, -@@ -1425,6 +1428,7 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(finalfn_oid)); -+ sepgsql_proc_execute(finalfn_oid); - } - } - -diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c -index 4cd7b0b..f661849 100644 ---- a/src/backend/executor/spi.c -+++ b/src/backend/executor/spi.c -@@ -705,6 +705,8 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum, - mtuple->t_tableOid = tuple->t_tableOid; - if (rel->rd_att->tdhasoid) - HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple)); -+ if (HeapTupleHasSecid(mtuple)) -+ HeapTupleSetSecid(mtuple, HeapTupleGetSecid(tuple)); - } - else - { -diff --git a/src/backend/libpq/be-fsstubs.c b/src/backend/libpq/be-fsstubs.c -index b6c46c1..6445ea8 100644 ---- a/src/backend/libpq/be-fsstubs.c -+++ b/src/backend/libpq/be-fsstubs.c -@@ -46,6 +46,7 @@ - #include "libpq/be-fsstubs.h" - #include "libpq/libpq-fs.h" - #include "miscadmin.h" -+#include "security/sepgsql.h" - #include "storage/fd.h" - #include "storage/large_object.h" - #include "utils/acl.h" -@@ -173,6 +174,9 @@ lo_read(int fd, char *buf, int len) - errmsg("permission denied for large object %u", - cookies[fd]->id))); - -+ /* SELinux db_blob:{read} checks */ -+ sepgsql_largeobject_read(cookies[fd]->id, cookies[fd]->snapshot); -+ - status = inv_read(cookies[fd], buf, len); - - return status; -@@ -205,6 +209,9 @@ lo_write(int fd, const char *buf, int len) - errmsg("permission denied for large object %u", - cookies[fd]->id))); - -+ /* SELinux db_blob:{write} */ -+ sepgsql_largeobject_write(cookies[fd]->id, cookies[fd]->snapshot); -+ - status = inv_write(cookies[fd], buf, len); - - return status; -@@ -233,6 +240,10 @@ Datum - lo_creat(PG_FUNCTION_ARGS) - { - Oid lobjId; -+ Oid secid; -+ -+ /* SELinux: db_blob:{create} */ -+ secid = sepgsql_largeobject_create(InvalidOid, NULL); - - /* - * We don't actually need to store into fscxt, but create it anyway to -@@ -240,7 +251,7 @@ lo_creat(PG_FUNCTION_ARGS) - */ - CreateFSContext(); - -- lobjId = inv_create(InvalidOid); -+ lobjId = inv_create(InvalidOid, secid); - - PG_RETURN_OID(lobjId); - } -@@ -249,6 +260,10 @@ Datum - lo_create(PG_FUNCTION_ARGS) - { - Oid lobjId = PG_GETARG_OID(0); -+ Oid secid; -+ -+ /* SELinux: db_blob:{create} */ -+ secid = sepgsql_largeobject_create(lobjId, NULL); - - /* - * We don't actually need to store into fscxt, but create it anyway to -@@ -256,7 +271,7 @@ lo_create(PG_FUNCTION_ARGS) - */ - CreateFSContext(); - -- lobjId = inv_create(lobjId); -+ lobjId = inv_create(lobjId, secid); - - PG_RETURN_OID(lobjId); - } -@@ -286,6 +301,9 @@ lo_unlink(PG_FUNCTION_ARGS) - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be owner of large object %u", lobjId))); - -+ /* SELinux: db_blob:{drop} */ -+ sepgsql_largeobject_drop(lobjId); -+ - /* - * If there are any open LO FDs referencing that ID, close 'em. - */ -@@ -381,9 +399,10 @@ lo_import_internal(text *filename, Oid lobjOid) - int nbytes, - tmp; - char buf[BUFSIZE]; -- char fnamebuf[MAXPGPATH]; -+ char *fnamebuf = text_to_cstring(filename); - LargeObjectDesc *lobj; - Oid oid; -+ Oid secid; - - #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS - if (!superuser()) -@@ -392,13 +411,14 @@ lo_import_internal(text *filename, Oid lobjOid) - errmsg("must be superuser to use server-side lo_import()"), - errhint("Anyone can use the client-side lo_import() provided by libpq."))); - #endif -+ /* SELinux: db_blob:{create import} */ -+ secid = sepgsql_largeobject_import(lobjOid, fnamebuf); - - CreateFSContext(); - - /* - * open the file to be read in - */ -- text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); - fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, 0666); - if (fd < 0) - ereport(ERROR, -@@ -409,7 +429,7 @@ lo_import_internal(text *filename, Oid lobjOid) - /* - * create an inversion object - */ -- oid = inv_create(lobjOid); -+ oid = inv_create(lobjOid, secid); - - /* - * read in from the filesystem and write to the inversion object -@@ -447,7 +467,7 @@ lo_export(PG_FUNCTION_ARGS) - int nbytes, - tmp; - char buf[BUFSIZE]; -- char fnamebuf[MAXPGPATH]; -+ char *fnamebuf = text_to_cstring(filename); - LargeObjectDesc *lobj; - mode_t oumask; - -@@ -458,6 +478,8 @@ lo_export(PG_FUNCTION_ARGS) - errmsg("must be superuser to use server-side lo_export()"), - errhint("Anyone can use the client-side lo_export() provided by libpq."))); - #endif -+ /* SELinux: db_blob:{read export} */ -+ sepgsql_largeobject_export(lobjId, fnamebuf); - - CreateFSContext(); - -@@ -528,6 +550,9 @@ lo_truncate(PG_FUNCTION_ARGS) - errmsg("permission denied for large object %u", - cookies[fd]->id))); - -+ /* SELinux: db_blob:{write} */ -+ sepgsql_largeobject_write(cookies[fd]->id, cookies[fd]->snapshot); -+ - inv_truncate(cookies[fd], len); - - PG_RETURN_INT32(0); -diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c -index 13c82a6..ca21326 100644 ---- a/src/backend/nodes/copyfuncs.c -+++ b/src/backend/nodes/copyfuncs.c -@@ -259,6 +259,7 @@ CopyScanFields(Scan *from, Scan *newnode) - CopyPlanFields((Plan *) from, (Plan *) newnode); - - COPY_SCALAR_FIELD(scanrelid); -+ COPY_SCALAR_FIELD(rowlvPerms); - } - - /* -@@ -2075,6 +2076,7 @@ _copyColumnDef(ColumnDef *from) - COPY_NODE_FIELD(raw_default); - COPY_NODE_FIELD(cooked_default); - COPY_NODE_FIELD(constraints); -+ COPY_NODE_FIELD(secLabel); - - return newnode; - } -@@ -2414,6 +2416,7 @@ _copyCreateStmt(CreateStmt *from) - COPY_NODE_FIELD(options); - COPY_SCALAR_FIELD(oncommit); - COPY_STRING_FIELD(tablespacename); -+ COPY_NODE_FIELD(secLabel); - - return newnode; - } -@@ -2638,6 +2641,21 @@ _copyAlterOwnerStmt(AlterOwnerStmt *from) - return newnode; - } - -+static AlterSecLabelStmt * -+_copyAlterSecLabelStmt(AlterSecLabelStmt *from) -+{ -+ AlterSecLabelStmt *newnode = makeNode(AlterSecLabelStmt); -+ -+ COPY_SCALAR_FIELD(objectType); -+ COPY_NODE_FIELD(relation); -+ COPY_NODE_FIELD(object); -+ COPY_NODE_FIELD(objarg); -+ COPY_STRING_FIELD(subname); -+ COPY_NODE_FIELD(secLabel); -+ -+ return newnode; -+} -+ - static RuleStmt * - _copyRuleStmt(RuleStmt *from) - { -@@ -2887,6 +2905,7 @@ _copyCreateSeqStmt(CreateSeqStmt *from) - - COPY_NODE_FIELD(sequence); - COPY_NODE_FIELD(options); -+ COPY_NODE_FIELD(secLabel); - - return newnode; - } -@@ -3819,6 +3838,9 @@ copyObject(void *from) - case T_AlterOwnerStmt: - retval = _copyAlterOwnerStmt(from); - break; -+ case T_AlterSecLabelStmt: -+ retval = _copyAlterSecLabelStmt(from); -+ break; - case T_RuleStmt: - retval = _copyRuleStmt(from); - break; -diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c -index 06a06b3..0752771 100644 ---- a/src/backend/nodes/equalfuncs.c -+++ b/src/backend/nodes/equalfuncs.c -@@ -1078,6 +1078,7 @@ _equalCreateStmt(CreateStmt *a, CreateStmt *b) - COMPARE_NODE_FIELD(options); - COMPARE_SCALAR_FIELD(oncommit); - COMPARE_STRING_FIELD(tablespacename); -+ COMPARE_NODE_FIELD(secLabel); - - return true; - } -@@ -1271,6 +1272,19 @@ _equalAlterOwnerStmt(AlterOwnerStmt *a, AlterOwnerStmt *b) - } - - static bool -+_equalAlterSecLabelStmt(AlterSecLabelStmt *a, AlterSecLabelStmt *b) -+{ -+ COMPARE_SCALAR_FIELD(objectType); -+ COMPARE_NODE_FIELD(relation); -+ COMPARE_NODE_FIELD(object); -+ COMPARE_NODE_FIELD(objarg); -+ COMPARE_STRING_FIELD(subname); -+ COMPARE_NODE_FIELD(secLabel); -+ -+ return true; -+} -+ -+static bool - _equalRuleStmt(RuleStmt *a, RuleStmt *b) - { - COMPARE_NODE_FIELD(relation); -@@ -1477,6 +1491,7 @@ _equalCreateSeqStmt(CreateSeqStmt *a, CreateSeqStmt *b) - { - COMPARE_NODE_FIELD(sequence); - COMPARE_NODE_FIELD(options); -+ COMPARE_NODE_FIELD(secLabel); - - return true; - } -@@ -2054,6 +2069,7 @@ _equalColumnDef(ColumnDef *a, ColumnDef *b) - COMPARE_NODE_FIELD(raw_default); - COMPARE_NODE_FIELD(cooked_default); - COMPARE_NODE_FIELD(constraints); -+ COMPARE_NODE_FIELD(secLabel); - - return true; - } -@@ -2596,6 +2612,9 @@ equal(void *a, void *b) - case T_AlterOwnerStmt: - retval = _equalAlterOwnerStmt(a, b); - break; -+ case T_AlterSecLabelStmt: -+ retval = _equalAlterSecLabelStmt(a, b); -+ break; - case T_RuleStmt: - retval = _equalRuleStmt(a, b); - break; -diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c -index 1c8691a..ffc2f19 100644 ---- a/src/backend/nodes/outfuncs.c -+++ b/src/backend/nodes/outfuncs.c -@@ -285,6 +285,7 @@ _outScanInfo(StringInfo str, Scan *node) - _outPlanInfo(str, (Plan *) node); - - WRITE_UINT_FIELD(scanrelid); -+ WRITE_UINT_FIELD(rowlvPerms); - } - - /* -@@ -1534,6 +1535,7 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node) - WRITE_BOOL_FIELD(has_eclass_joins); - WRITE_BITMAPSET_FIELD(index_outer_relids); - WRITE_NODE_FIELD(index_inner_paths); -+ WRITE_UINT_FIELD(rowlvPerms); - } - - static void -@@ -1717,6 +1719,7 @@ _outCreateStmt(StringInfo str, CreateStmt *node) - WRITE_NODE_FIELD(options); - WRITE_ENUM_FIELD(oncommit, OnCommitAction); - WRITE_STRING_FIELD(tablespacename); -+ WRITE_NODE_FIELD(secLabel); - } - - static void -@@ -1839,6 +1842,7 @@ _outColumnDef(StringInfo str, ColumnDef *node) - WRITE_NODE_FIELD(raw_default); - WRITE_NODE_FIELD(cooked_default); - WRITE_NODE_FIELD(constraints); -+ WRITE_NODE_FIELD(secLabel); - } - - static void -diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c -index 6e5c251..8156719 100644 ---- a/src/backend/optimizer/plan/createplan.c -+++ b/src/backend/optimizer/plan/createplan.c -@@ -305,6 +305,9 @@ create_scan_plan(PlannerInfo *root, Path *best_path) - break; - } - -+ /* Copy of row-level permissions to Scan node */ -+ ((Scan *)plan)->rowlvPerms = rel->rowlvPerms; -+ - /* - * If there are any pseudoconstant clauses attached to this node, insert a - * gating Result node that evaluates the pseudoconstants as one-time -diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c -index be75590..694a40c 100644 ---- a/src/backend/optimizer/util/clauses.c -+++ b/src/backend/optimizer/util/clauses.c -@@ -38,6 +38,7 @@ - #include "parser/parse_coerce.h" - #include "parser/parse_func.h" - #include "rewrite/rewriteManip.h" -+#include "security/sepgsql.h" - #include "tcop/tcopprot.h" - #include "utils/acl.h" - #include "utils/builtins.h" -@@ -3502,6 +3503,7 @@ inline_function(Oid funcid, Oid result_type, List *args, - funcform->prosecdef || - funcform->proretset || - !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || -+ !sepgsql_proc_hint_inlined(func_tuple) || - funcform->pronargs != list_length(args)) - return NULL; - -@@ -3970,6 +3972,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte) - funcform->prosecdef || - !funcform->proretset || - !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || -+ !sepgsql_proc_hint_inlined(func_tuple) || - funcform->pronargs != list_length(fexpr->args)) - { - ReleaseSysCache(func_tuple); -diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c -index 1d93203..3d3c455 100644 ---- a/src/backend/optimizer/util/relnode.c -+++ b/src/backend/optimizer/util/relnode.c -@@ -21,6 +21,7 @@ - #include "optimizer/plancat.h" - #include "optimizer/restrictinfo.h" - #include "parser/parsetree.h" -+#include "security/rowlevel.h" - #include "utils/hsearch.h" - - -@@ -91,6 +92,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) - rel->has_eclass_joins = false; - rel->index_outer_relids = NULL; - rel->index_inner_paths = NIL; -+ rel->rowlvPerms = rowlvSetupPermissions(rte); - - /* Check type of rtable entry */ - switch (rte->rtekind) -diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c -index f110463..a236e3c 100644 ---- a/src/backend/parser/analyze.c -+++ b/src/backend/parser/analyze.c -@@ -25,6 +25,7 @@ - #include "postgres.h" - - #include "access/sysattr.h" -+#include "catalog/heap.h" - #include "catalog/pg_type.h" - #include "nodes/makefuncs.h" - #include "nodes/nodeFuncs.h" -@@ -660,7 +661,7 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) - tle = makeTargetEntry(expr, - attr_num, - col->name, -- false); -+ attr_num < 0 ? true : false); - qry->targetList = lappend(qry->targetList, tle); - - rte->modifiedCols = bms_add_member(rte->modifiedCols, -@@ -775,6 +776,48 @@ transformInsertRow(ParseState *pstate, List *exprlist, - 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 - -@@ -879,6 +922,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) - if (stmt->intoClause) - { - qry->intoClause = stmt->intoClause; -+ transformSelectIntoSystemColumn(pstate, qry); - if (stmt->intoClause->colNames) - applyColumnNames(qry->targetList, stmt->intoClause->colNames); - } -diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y -index d13b0f0..80a1f4a 100644 ---- a/src/backend/parser/gram.y -+++ b/src/backend/parser/gram.y -@@ -58,6 +58,7 @@ - #include "nodes/makefuncs.h" - #include "nodes/nodeFuncs.h" - #include "parser/gramparse.h" -+#include "security/sepgsql.h" - #include "storage/lmgr.h" - #include "utils/date.h" - #include "utils/datetime.h" -@@ -184,7 +185,7 @@ static TypeName *TableFuncTypeName(List *columns); - %type stmt schema_stmt - AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt - AlterForeignServerStmt AlterGroupStmt -- AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt -+ AlterObjectSchemaStmt AlterOwnerStmt AlterSecLabelStmt AlterSeqStmt AlterTableStmt - AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt - AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt - ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt -@@ -402,6 +403,10 @@ static TypeName *TableFuncTypeName(List *columns); - %type OptTableSpace OptConsTableSpace OptTableSpaceOwner - %type opt_check_option - -+%type OptSecLabel SecLabelItem SecLabelToItem -+%type OptTableSecLabel TableSecLabelList -+%type TableSecLabelItem -+ - %type xml_attribute_el - %type xml_attribute_list xml_attributes - %type xml_root_version opt_xml_root_standalone -@@ -437,7 +442,7 @@ static TypeName *TableFuncTypeName(List *columns); - CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE - CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT - COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS -- CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB -+ CONTENT_P CONTEXT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB - CREATEROLE CREATEUSER CROSS CSV CURRENT_P - CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA - CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE -@@ -608,6 +613,7 @@ stmt : - | AlterGroupStmt - | AlterObjectSchemaStmt - | AlterOwnerStmt -+ | AlterSecLabelStmt - | AlterSeqStmt - | AlterTableStmt - | AlterRoleSetStmt -@@ -1042,7 +1048,7 @@ DropGroupStmt: - *****************************************************************************/ - - CreateSchemaStmt: -- CREATE SCHEMA OptSchemaName AUTHORIZATION RoleId OptSchemaEltList -+ CREATE SCHEMA OptSchemaName AUTHORIZATION RoleId OptSecLabel OptSchemaEltList - { - CreateSchemaStmt *n = makeNode(CreateSchemaStmt); - /* One can omit the schema name or the authorization id. */ -@@ -1051,16 +1057,18 @@ CreateSchemaStmt: - else - n->schemaname = $5; - n->authid = $5; -- n->schemaElts = $6; -+ n->secLabel = $6; -+ n->schemaElts = $7; - $$ = (Node *)n; - } -- | CREATE SCHEMA ColId OptSchemaEltList -+ | CREATE SCHEMA ColId OptSecLabel OptSchemaEltList - { - CreateSchemaStmt *n = makeNode(CreateSchemaStmt); - /* ...but not both */ - n->schemaname = $3; - n->authid = NULL; -- n->schemaElts = $4; -+ n->secLabel = $4; -+ n->schemaElts = $5; - $$ = (Node *)n; - } - ; -@@ -2037,7 +2045,7 @@ opt_using: - *****************************************************************************/ - - CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' -- OptInherit OptWith OnCommitOption OptTableSpace -+ OptInherit OptWith OnCommitOption OptTableSpace OptTableSecLabel - { - CreateStmt *n = makeNode(CreateStmt); - $4->istemp = $2; -@@ -2048,10 +2056,11 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' - n->options = $9; - n->oncommit = $10; - n->tablespacename = $11; -+ n->secLabel = $12; - $$ = (Node *)n; - } - | CREATE OptTemp TABLE qualified_name OF qualified_name -- '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace -+ '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace OptTableSecLabel - { - /* SQL99 CREATE TABLE OF (cols) seems to be satisfied - * by our inheritance capabilities. Let's try it... -@@ -2065,6 +2074,7 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' - n->options = $10; - n->oncommit = $11; - n->tablespacename = $12; -+ n->secLabel = $13; - $$ = (Node *)n; - } - ; -@@ -2114,6 +2124,7 @@ columnDef: ColId Typename ColQualList - n->typename = $2; - n->constraints = $3; - n->is_local = true; -+ n->secLabel = NULL; - $$ = (Node *)n; - } - ; -@@ -2585,12 +2596,13 @@ opt_with_data: - *****************************************************************************/ - - CreateSeqStmt: -- CREATE OptTemp SEQUENCE qualified_name OptSeqOptList -+ CREATE OptTemp SEQUENCE qualified_name OptSeqOptList OptSecLabel - { - CreateSeqStmt *n = makeNode(CreateSeqStmt); - $4->istemp = $2; - n->sequence = $4; - n->options = $5; -+ n->secLabel = $6; - $$ = (Node *)n; - } - ; -@@ -4893,6 +4905,10 @@ createfunc_opt_item: - { - $$ = makeDefElem("window", (Node *)makeInteger(TRUE)); - } -+ | SecLabelItem -+ { -+ $$ = makeDefElem("security_context", $1); -+ } - | common_func_opt_item - { - $$ = $1; -@@ -5607,6 +5623,101 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId - } - ; - -+/***************************************************************************** -+ * -+ * ALTER THING name SECURITY CONTEXT TO -+ * -+ *****************************************************************************/ -+ -+AlterSecLabelStmt: ALTER DATABASE database_name SecLabelToItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_DATABASE; -+ n->object = list_make1(makeString($3)); -+ n->secLabel = $4; -+ $$ = (Node *) n; -+ } -+ | ALTER SCHEMA name SecLabelToItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_SCHEMA; -+ n->object = list_make1(makeString($3)); -+ n->secLabel = $4; -+ $$ = (Node *) n; -+ } -+ | ALTER TABLE relation_expr SecLabelToItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_TABLE; -+ n->relation = $3; -+ n->secLabel = $4; -+ $$ = (Node *) n; -+ } -+ | ALTER TABLE relation_expr ALTER opt_column ColId SecLabelToItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_COLUMN; -+ n->relation = $3; -+ n->subname = $6; -+ n->secLabel = $7; -+ $$ = (Node *) n; -+ } -+ | ALTER SEQUENCE relation_expr SecLabelToItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_SEQUENCE; -+ n->relation = $3; -+ n->secLabel = $4; -+ $$ = (Node *) n; -+ } -+ | ALTER FUNCTION function_with_argtypes SecLabelToItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_FUNCTION; -+ n->object = $3->funcname; -+ n->objarg = $3->funcargs; -+ n->secLabel = $4; -+ $$ = (Node *) n; -+ } -+ | ALTER LARGE_P OBJECT_P Iconst SecLabelToItem -+ { -+ AlterSecLabelStmt *n = makeNode(AlterSecLabelStmt); -+ n->objectType = OBJECT_LARGEOBJECT; -+ n->object = list_make1(makeInteger($4)); -+ n->secLabel = $5; -+ $$ = (Node *) n; -+ } -+ ; -+ -+OptTableSecLabel: SECURITY CONTEXT_P '(' TableSecLabelList ')' { $$ = $4; } -+ | /* EMPTY */ { $$ = NIL; } -+ ; -+ -+TableSecLabelList: TableSecLabelItem { $$ = list_make1($1); } -+ | TableSecLabelList ',' TableSecLabelItem { $$ = lappend($1, $3); } -+ ; -+ -+TableSecLabelItem: Sconst -+ { $$ = makeDefElem(NULL, (Node *)makeString($1)); } -+ | ColId '=' Sconst -+ { $$ = makeDefElem($1, (Node *)makeString($3)); } -+ ; -+ -+OptSecLabel: SecLabelItem { $$ = $1; } -+ | /* EMPTY */ { $$ = NULL; } -+ ; -+ -+SecLabelItem: SECURITY CONTEXT_P '(' Sconst ')' -+ { -+ $$ = (Node *) makeString($4); -+ } -+ ; -+ -+SecLabelToItem: SECURITY CONTEXT_P TO Sconst -+ { -+ $$ = (Node *) makeString($4); -+ } -+ ; - - /***************************************************************************** - * -@@ -6049,6 +6160,10 @@ createdb_opt_item: - { - $$ = makeDefElem("owner", NULL); - } -+ | SecLabelItem -+ { -+ $$ = makeDefElem("security_context", $1); -+ } - ; - - /* -@@ -10175,6 +10290,7 @@ unreserved_keyword: - | CONNECTION - | CONSTRAINTS - | CONTENT_P -+ | CONTEXT_P - | CONTINUE_P - | CONVERSION_P - | COPY -diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c -index 08b8edb..97aa7aa 100644 ---- a/src/backend/parser/parse_target.c -+++ b/src/backend/parser/parse_target.c -@@ -14,6 +14,7 @@ - */ - #include "postgres.h" - -+#include "catalog/heap.h" - #include "catalog/pg_type.h" - #include "commands/dbcommands.h" - #include "funcapi.h" -@@ -361,16 +362,33 @@ transformAssignedExpr(ParseState *pstate, - Oid attrtype; /* type of target column */ - int32 attrtypmod; - Relation rd = pstate->p_target_relation; -+ bool relhasoids = RelationGetForm(rd)->relhasoids; - - Assert(rd != NULL); -- if (attrno <= 0) -- ereport(ERROR, -- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -- errmsg("cannot assign to system column \"%s\"", -- colname), -- parser_errposition(pstate, location))); -- attrtype = attnumTypeId(rd, attrno); -- attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; -+ if (attrno > 0) -+ { -+ attrtype = attnumTypeId(rd, attrno); -+ attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; -+ } -+ else -+ { -+ Form_pg_attribute attForm -+ = SystemAttributeDefinition(attrno, relhasoids); -+ if (attForm && SystemAttributeIsWritable(attrno)) -+ { -+ attrtype = attForm->atttypid; -+ attrtypmod = attForm->atttypmod; -+ } -+ else -+ { -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("cannot assign to system column \"%s\"", -+ colname), -+ parser_errposition(pstate, location))); -+ return NULL; /* compiler kindness */ -+ } -+ } - - /* - * If the expression is a DEFAULT placeholder, insert the attribute's -@@ -515,6 +533,9 @@ updateTargetListEntry(ParseState *pstate, - */ - tle->resno = (AttrNumber) attrno; - tle->resname = colname; -+ -+ if (SystemAttributeIsWritable(attrno)) -+ tle->resjunk = true; - } - - -@@ -789,6 +810,7 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) - Bitmapset *wholecols = NULL; - Bitmapset *partialcols = NULL; - ListCell *tl; -+ uint32 system_attrs = 0UL; - - foreach(tl, cols) - { -@@ -797,14 +819,37 @@ checkInsertTargets(ParseState *pstate, List *cols, List **attrnos) - int attrno; - - /* Lookup column name, ereport on failure */ -- attrno = attnameAttNum(pstate->p_target_relation, name, false); -+ attrno = attnameAttNum(pstate->p_target_relation, name, true); - if (attrno == InvalidAttrNumber) -+ { - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("column \"%s\" of relation \"%s\" does not exist", - name, - RelationGetRelationName(pstate->p_target_relation)), - parser_errposition(pstate, col->location))); -+ } -+ else if (attrno < 0) -+ { -+ 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 --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c -index 1a9e387..c5ea995 100644 ---- a/src/backend/parser/parse_utilcmd.c -+++ b/src/backend/parser/parse_utilcmd.c -@@ -49,6 +49,7 @@ - #include "parser/parse_type.h" - #include "parser/parse_utilcmd.h" - #include "rewrite/rewriteManip.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/lsyscache.h" -@@ -565,6 +566,8 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_CLASS, - RelationGetRelationName(relation)); -+ /* SELinux checks */ -+ sepgsql_relation_copy_definition(RelationGetRelid(relation)); - - tupleDesc = RelationGetDescr(relation); - constr = tupleDesc->constr; -diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c -index 3e1a056..48d0642 100644 ---- a/src/backend/postmaster/autovacuum.c -+++ b/src/backend/postmaster/autovacuum.c -@@ -2004,7 +2004,7 @@ do_autovacuum(void) - object.classId = RelationRelationId; - object.objectId = relid; - object.objectSubId = 0; -- performDeletion(&object, DROP_CASCADE); -+ performDeletionNoPerms(&object, DROP_CASCADE); - } - else - { -diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c -index d63214b..e88b469 100644 ---- a/src/backend/postmaster/postmaster.c -+++ b/src/backend/postmaster/postmaster.c -@@ -108,6 +108,7 @@ - #include "postmaster/pgarch.h" - #include "postmaster/postmaster.h" - #include "postmaster/syslogger.h" -+#include "security/sepgsql.h" - #include "storage/fd.h" - #include "storage/ipc.h" - #include "storage/pg_shmem.h" -@@ -209,7 +210,8 @@ static pid_t StartupPID = 0, - AutoVacPID = 0, - PgArchPID = 0, - PgStatPID = 0, -- SysLoggerPID = 0; -+ SysLoggerPID = 0, -+ sepgsqlReceiverPID = 0; - - /* Startup/shutdown state */ - #define NoShutdown 0 -@@ -445,6 +447,7 @@ static void ShmemBackendArrayRemove(Backend *bn); - #define StartupDataBase() StartChildProcess(StartupProcess) - #define StartBackgroundWriter() StartChildProcess(BgWriterProcess) - #define StartWalWriter() StartChildProcess(WalWriterProcess) -+#define StartSELinuxReceiver() StartChildProcess(SelinuxReceiverProcess) - - /* Macros to check exit status of a child process */ - #define EXIT_STATUS_0(st) ((st) == 0) -@@ -1436,6 +1439,11 @@ ServerLoop(void) - if (PgStatPID == 0 && pmState == PM_RUN) - PgStatPID = pgstat_start(); - -+ /* if we have lost the selinux netlink receiver, try to start */ -+ if (sepgsqlReceiverPID == 0 && pmState == PM_RUN && -+ sepgsqlReceiverStart()) -+ sepgsqlReceiverPID = StartSELinuxReceiver(); -+ - /* If we need to signal the autovacuum launcher, do so now */ - if (avlauncher_needs_signal) - { -@@ -2055,6 +2063,8 @@ SIGHUP_handler(SIGNAL_ARGS) - signal_child(SysLoggerPID, SIGHUP); - if (PgStatPID != 0) - signal_child(PgStatPID, SIGHUP); -+ if (sepgsqlReceiverPID != 0) -+ signal_child(sepgsqlReceiverPID, SIGHUP); - - /* Reload authentication config files too */ - if (!load_hba()) -@@ -2115,6 +2125,9 @@ pmdie(SIGNAL_ARGS) - /* and the walwriter too */ - if (WalWriterPID != 0) - signal_child(WalWriterPID, SIGTERM); -+ /* and the selinux netlink receiver too */ -+ if (sepgsqlReceiverPID != 0) -+ signal_child(sepgsqlReceiverPID, SIGTERM); - pmState = PM_WAIT_BACKUP; - } - -@@ -2162,6 +2175,9 @@ pmdie(SIGNAL_ARGS) - /* and the walwriter too */ - if (WalWriterPID != 0) - signal_child(WalWriterPID, SIGTERM); -+ /* and the selinux netlink receiver too */ -+ if (sepgsqlReceiverPID != 0) -+ signal_child(sepgsqlReceiverPID, SIGTERM); - pmState = PM_WAIT_BACKENDS; - } - -@@ -2195,6 +2211,8 @@ pmdie(SIGNAL_ARGS) - signal_child(PgArchPID, SIGQUIT); - if (PgStatPID != 0) - signal_child(PgStatPID, SIGQUIT); -+ if (sepgsqlReceiverPID != 0) -+ signal_child(sepgsqlReceiverPID, SIGQUIT); - ExitPostmaster(0); - break; - } -@@ -2457,6 +2475,16 @@ reaper(SIGNAL_ARGS) - continue; - } - -+ /* Was it the selinux netlink receiver process? */ -+ if (pid == sepgsqlReceiverPID) -+ { -+ sepgsqlReceiverPID = 0; -+ if (!EXIT_STATUS_0(exitstatus)) -+ LogChildExit(LOG, _("SELinux netlink receiver process"), -+ pid, exitstatus); -+ continue; -+ } -+ - /* - * Else do standard backend child cleanup. - */ -@@ -2648,6 +2676,18 @@ HandleChildCrash(int pid, int exitstatus, const char *procname) - signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT)); - } - -+ /* Take care of the selinux netlink receiver too */ -+ if (pid == sepgsqlReceiverPID) -+ sepgsqlReceiverPID = 0; -+ else if (sepgsqlReceiverPID != 0 && !FatalError) -+ { -+ ereport(DEBUG2, -+ (errmsg_internal("sending %s to process %d", -+ (SendStop ? "SIGSTOP" : "SIGQUIT"), -+ (int) sepgsqlReceiverPID))); -+ signal_child(sepgsqlReceiverPID, (SendStop ? SIGSTOP : SIGQUIT)); -+ } -+ - /* - * Force a power-cycle of the pgarch process too. (This isn't absolutely - * necessary, but it seems like a good idea for robustness, and it -@@ -2780,7 +2820,8 @@ PostmasterStateMachine(void) - StartupPID == 0 && - (BgWriterPID == 0 || !FatalError) && - WalWriterPID == 0 && -- AutoVacPID == 0) -+ AutoVacPID == 0 && -+ sepgsqlReceiverPID == 0) - { - if (FatalError) - { -@@ -4323,6 +4364,12 @@ StartChildProcess(AuxProcType type) - ereport(LOG, - (errmsg("could not fork WAL writer process: %m"))); - break; -+#ifdef HAVE_SELINUX -+ case SelinuxReceiverProcess: -+ ereport(LOG, -+ (errmsg("could not fork selinux receiver process: %m"))); -+ break; -+#endif - default: - ereport(LOG, - (errmsg("could not fork process: %m"))); -diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c -index 0b4f279..5fe6fd0 100644 ---- a/src/backend/rewrite/rewriteDefine.c -+++ b/src/backend/rewrite/rewriteDefine.c -@@ -27,6 +27,7 @@ - #include "rewrite/rewriteDefine.h" - #include "rewrite/rewriteManip.h" - #include "rewrite/rewriteSupport.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/inval.h" -@@ -266,6 +267,9 @@ DefineQueryRewrite(char *rulename, - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - RelationGetRelationName(event_relation)); - -+ /* SELinux checks */ -+ sepgsql_rule_create(event_relid, rulename); -+ - /* - * No rule actions that modify OLD or NEW - */ -diff --git a/src/backend/rewrite/rewriteRemove.c b/src/backend/rewrite/rewriteRemove.c -index a65b020..b967838 100644 ---- a/src/backend/rewrite/rewriteRemove.c -+++ b/src/backend/rewrite/rewriteRemove.c -@@ -22,6 +22,7 @@ - #include "catalog/pg_rewrite.h" - #include "miscadmin.h" - #include "rewrite/rewriteRemove.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/fmgroids.h" - #include "utils/inval.h" -@@ -78,6 +79,9 @@ RemoveRewriteRule(Oid owningRel, const char *ruleName, DropBehavior behavior, - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, - get_rel_name(eventRelationOid)); - -+ /* SELinux checks */ -+ sepgsql_rule_drop(eventRelationOid, ruleName); -+ - /* - * Do the deletion - */ -diff --git a/src/backend/security/Makefile b/src/backend/security/Makefile -new file mode 100644 -index 0000000..23e51d6 ---- /dev/null -+++ b/src/backend/security/Makefile -@@ -0,0 +1,13 @@ -+# -+# Makefile for the enhanced security subsystem -+# -+ -+subdir = src/backend/security -+top_builddir = ../../.. -+include $(top_builddir)/src/Makefile.global -+ -+SUBDIRS = sepgsql -+ -+OBJS = rowlevel.o -+ -+include $(top_srcdir)/src/backend/common.mk -diff --git a/src/backend/security/rowlevel.c b/src/backend/security/rowlevel.c -new file mode 100644 -index 0000000..e08d4cc ---- /dev/null -+++ b/src/backend/security/rowlevel.c -@@ -0,0 +1,121 @@ -+/* -+ * src/backend/security/common.c -+ * common facilities for row-level access controls both of DAC and MAC -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+#include "postgres.h" -+ -+#include "catalog/pg_security.h" -+#include "security/rowlevel.h" -+#include "security/sepgsql.h" -+#include "storage/bufmgr.h" -+#include "storage/bufpage.h" -+#include "utils/rel.h" -+#include "utils/tqual.h" -+ -+/* -+ * rowlvGetPerformingMode -+ * rowlvSetPerformingMode -+ * enables to control the behavior of row-level features -+ * when violated tuples are detected. -+ * The default is ROWLV_FILTER_MODE which filters out -+ * violated tuples from result set, ROWLV_ABORT_MODE -+ * raises an error and ROWLV_BYPASS_MODE do nothing. -+ */ -+static int rowlv_mode = ROWLV_FILTER_MODE; -+ -+int rowlvGetPerformingMode(void) -+{ -+ return rowlv_mode; -+} -+ -+int rowlvSetPerformingMode(int new_mode) -+{ -+ int old_mode = new_mode; -+ -+ rowlv_mode = new_mode; -+ -+ return old_mode; -+} -+ -+/* -+ * rowlvSetupPermissions -+ * setups permissions for row-level access controls. -+ */ -+uint32 -+rowlvSetupPermissions(RangeTblEntry *rte) -+{ -+ return sepgsqlSetupTuplePerms(rte); -+} -+ -+/* -+ * rowlvExecScan -+ * a hook to filter out invisible/untouchable tuples. -+ */ -+static bool -+rowlvExecScan(Scan *scan, Relation rel, TupleTableSlot *slot, bool abort) -+{ -+ HeapTuple tuple; -+ uint32 perms = scan->rowlvPerms; -+ -+ if (!perms) -+ return true; -+ -+ tuple = ExecMaterializeSlot(slot); -+ -+ return sepgsqlExecScan(rel, tuple, perms, abort); -+} -+ -+bool -+rowlvExecScanFilter(Scan *scan, Relation rel, TupleTableSlot *slot) -+{ -+ if (!rel || !scan->rowlvPerms || rowlv_mode != ROWLV_FILTER_MODE) -+ return true; -+ -+ return rowlvExecScan(scan, rel, slot, false); -+} -+ -+void -+rowlvExecScanAbort(Scan *scan, Relation rel, TupleTableSlot *slot) -+{ -+ if (!rel || !scan->rowlvPerms || rowlv_mode != ROWLV_ABORT_MODE) -+ return; -+ -+ rowlvExecScan(scan, rel, slot, true); -+} -+ -+/* -+ * rowlvCopyToTuple -+ * checks permission on fetched tuple -+ */ -+bool -+rowlvCopyToTuple(Relation rel, HeapTuple tuple) -+{ -+ if (!sepgsqlExecScan(rel, tuple, SEPG_DB_TUPLE__SELECT, false)) -+ return false; -+ -+ return true; -+} -+ -+/* -+ * rowlvHeapTupleInsert -+ * assign default security attribute, and check permission -+ * if necessary. -+ */ -+void -+rowlvHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal) -+{ -+ sepgsqlHeapTupleInsert(rel, newtup, internal); -+} -+ -+/* -+ * rowlvHeapTupleUpdate -+ * check permission to change security attribute, if necesary -+ */ -+void -+rowlvHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup) -+{ -+ sepgsqlHeapTupleUpdate(rel, otid, newtup); -+} -diff --git a/src/backend/security/sepgsql/Makefile b/src/backend/security/sepgsql/Makefile -new file mode 100644 -index 0000000..2417aae ---- /dev/null -+++ b/src/backend/security/sepgsql/Makefile -@@ -0,0 +1,15 @@ -+# -+# Makefile -+# Makefile for utils/sepgsql : SE-PostgreSQL -+# -+ -+subdir = src/backend/security/sepgsql -+top_builddir = ../../../.. -+include $(top_builddir)/src/Makefile.global -+ -+OBJS = misc.o -+ifeq ($(enable_selinux), yes) -+OBJS += selinux.o checker.o bridge.o label.o -+endif -+ -+include $(top_srcdir)/src/backend/common.mk -diff --git a/src/backend/security/sepgsql/avc.c b/src/backend/security/sepgsql/avc.c -new file mode 100644 -index 0000000..8a88bcb ---- /dev/null -+++ b/src/backend/security/sepgsql/avc.c -@@ -0,0 +1,881 @@ -+/* -+ * src/backend/security/sepgsql/avc.c -+ * SE-PostgreSQL userspace access vector cache -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+#include "postgres.h" -+ -+#include "access/hash.h" -+#include "catalog/pg_security.h" -+#include "libpq/pqsignal.h" -+#include "miscadmin.h" -+#include "postmaster/postmaster.h" -+#include "security/sepgsql.h" -+#include "storage/ipc.h" -+#include "storage/lwlock.h" -+#include "utils/memutils.h" -+#include -+#include -+#include -+ -+/* -+ * AVC: userspace access vector cache -+ * -+ * SE-PostgreSQL asks in-kernel SELinux to make its decision whether -+ * the required accesses should be allowed, or not, based on the unified -+ * security policy. It needs a system call invocation to communicate -+ * a kernel feature, such as SELinux, but it is a heavy task in most cases -+ * due to the context switching. -+ * -+ * The userspace avc enables to minimize the number of system call -+ * invocations, using a chache mechanim for the certain pair of security -+ * contexts and object classes (it means the kind of actions). -+ * It enables to hold recently fetched results from the in-kernel SELinux, -+ * and make a decision without context switching, if the cache hit. -+ * -+ * When the state of security policy is changed, the cached results -+ * shall to be invalidated. The state monitoring process launched by -+ * postmaster can receives the notification messages from the kernel -+ * space, and invalidate the current version of avc. -+ */ -+static MemoryContext AvcMemCtx = NULL; -+ -+#define AVC_HASH_NUM_SLOTS 256 -+#define AVC_HASH_NUM_NODES 180 -+ -+#define AVC_DATUM_NSID_SLOTS 19 -+typedef struct -+{ -+ uint32 hash_key; -+ -+ security_class_t tclass; -+ sepgsql_sid_t tsid; -+ sepgsql_sid_t nsid[AVC_DATUM_NSID_SLOTS]; -+ -+ access_vector_t allowed; -+ access_vector_t decided; -+ access_vector_t auditallow; -+ access_vector_t auditdeny; -+ -+ bool hot_cache; -+ bool permissive; -+ -+ char ncontext[1]; -+} avc_datum; -+ -+typedef struct avc_page -+{ -+ struct avc_page *next; -+ -+ security_context_t scontext; -+ -+ List *slot[AVC_HASH_NUM_SLOTS]; -+ -+ uint32 avc_count; -+ uint32 lru_hint; -+} avc_page; -+ -+static avc_page *current_page = NULL; -+ -+static int avc_version; -+ -+/* -+ * selinux_state -+ * -+ * It is deployed on the shared memory region, to show the system -+ * state of SELinux and its security policy. -+ * -+ * The selinux_state->version should be checked prior to avc accesses. -+ * If it does not match with the local avc_version, it means that -+ * system security policy was reloaded or system state (enforcing -+ * or permissive) was changed. -+ * -+ * The state monitoring worker process receives messages from the -+ * kernel using libselinux, and it updates the selinux_state. -+ */ -+struct -+{ -+ int version; -+ -+ bool enforcing; -+ -+} *selinux_state = NULL; -+ -+Size -+sepgsqlShmemSize(void) -+{ -+ if (!sepgsqlIsEnabled()) -+ return 0; -+ -+ return sizeof(*selinux_state); -+} -+ -+/* -+ * sepgsql_shmem_init -+ * attaches shared memory segment. -+ */ -+static void -+sepgsqlShmemInit(void) -+{ -+ bool found; -+ -+ selinux_state = ShmemInitStruct("SELinux policy state", -+ sepgsqlShmemSize(), &found); -+ if (!found) -+ { -+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); -+ -+ selinux_state->version = 0; -+ selinux_state->enforcing = (security_getenforce() > 0); -+ -+ LWLockRelease(SepgsqlAvcLock); -+ } -+} -+ -+/* -+ * sepgsqlAvcReset -+ * -+ * It invalidate access vector cache. It has to be called on errors, -+ * because avc entries for newly created context is uncertain whether -+ * it is still valid, or not. -+ * If error happens before avc initialization, we simply skip it. -+ */ -+void -+sepgsqlAvcReset(void) -+{ -+ if (!sepgsqlIsEnabled() || !AvcMemCtx) -+ return; -+ -+ MemoryContextReset(AvcMemCtx); -+ -+ current_page = NULL; -+ -+ sepgsqlAvcSwitchClient(sepgsqlGetClientLabel()); -+} -+ -+/* -+ * sepgsqlAvcCheckValid -+ * -+ * It checks whether the current AVC pages are valid, or not. -+ * If state monitoring process already received an invalidation -+ * message from the kernel, it clears current AVC pages and -+ * returns false. -+ */ -+static bool -+sepgsqlAvcCheckValid(void) -+{ -+ bool result = true; -+ -+ LWLockAcquire(SepgsqlAvcLock, LW_SHARED); -+ if (avc_version != selinux_state->version) -+ { -+ /* reset invalid avc pages, and makes an empty one */ -+ MemoryContextReset(AvcMemCtx); -+ -+ current_page = NULL; -+ -+ sepgsqlAvcSwitchClient(sepgsqlGetClientLabel()); -+ -+ /* copy current version to local */ -+ avc_version = selinux_state->version; -+ -+ result = false; -+ } -+ LWLockRelease(SepgsqlAvcLock); -+ -+ return result; -+} -+ -+/* -+ * sepgsqlAvcInitialize -+ * -+ * It allocates a memory context for userspace AVC, -+ * map shared memory segment, and initialize avc_page -+ * for the current client's privilege. -+ * -+ * If the current backend is not associated with a certain -+ * client process, it switches to permissive mode to avoid -+ * to prevent any internal processes. -+ */ -+void -+sepgsqlAvcInitialize(void) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* -+ * local memory context -+ */ -+ AvcMemCtx = AllocSetContextCreate(TopMemoryContext, -+ "SE-PostgreSQL userspace avc", -+ ALLOCSET_DEFAULT_MINSIZE, -+ ALLOCSET_DEFAULT_INITSIZE, -+ ALLOCSET_DEFAULT_MAXSIZE); -+ sepgsqlShmemInit(); -+ -+ /* -+ * Switch to local permissive mode -+ */ -+ if (!MyProcPort) -+ sepgsqlSetEnforce(0); -+ -+ /* -+ * selinux_state->version is never negative value, -+ * so this call always reset local avc. -+ */ -+ avc_version = -1; -+ sepgsqlAvcCheckValid(); -+} -+ -+/* -+ * sepgsqlGetEnforce -+ * sepgsqlSetEnforce -+ * -+ * SELinux has two working mode called Enforcing/Permissive. -+ * In enforcing mode, it checks security policy and actually -+ * applies its access controls. In permissive mode, it also -+ * checks security policy, but does not apply any access -+ * controls. It is used to collect access denied logs to -+ * debug security policy. -+ * -+ * sepgsqlGetEnforce() returns the current working mode, and -+ * sepgsqlSetEnforce() switches the current working mode -+ * temporary. When we switches the mode, any errors have to -+ * be acquired, and it should be restored correctly. -+ */ -+static int local_enforce = -1; /* undefined */ -+ -+bool -+sepgsqlGetEnforce(void) -+{ -+ bool rc; -+ -+ if (local_enforce < 0) -+ { -+ LWLockAcquire(SepgsqlAvcLock, LW_SHARED); -+ rc = selinux_state->enforcing; -+ LWLockRelease(SepgsqlAvcLock); -+ -+ return rc; -+ } -+ -+ return (local_enforce > 0 ? true : false); -+} -+ -+int -+sepgsqlSetEnforce(int new_mode) -+{ -+ int old_mode = local_enforce; -+ -+ local_enforce = new_mode; -+ -+ return old_mode; -+} -+ -+/* -+ * sepgsqlAvcAudit -+ * -+ * It write out audit message, when auditdeny or auditallow -+ * matches the required permission bits. -+ * If external module support sepgsqlAvcAuditHook, it allows -+ * to write audit logs to external log manager, such as system -+ * auditd. -+ */ -+ -+PGDLLIMPORT sepgsqlAvcAuditHook_t sepgsqlAvcAuditHook = NULL; -+ -+static void -+sepgsqlAvcAudit(bool denied, char *scontext, char *tcontext, -+ uint16 tclass, uint32 audited, const char *audit_name) -+{ -+ StringInfoData buf; -+ uint32 mask; -+ const char *tclass_name; -+ -+ /* translate to human readable form */ -+ scontext = sepgsqlTransSecLabelOut(scontext); -+ tcontext = sepgsqlTransSecLabelOut(tcontext); -+ -+ /* permissions in text representation */ -+ initStringInfo(&buf); -+ appendStringInfo(&buf, "{"); -+ for (mask = 1; audited != 0; mask <<= 1) -+ { -+ if (audited & mask) -+ appendStringInfo(&buf, " %s", sepgsqlGetPermString(tclass, mask)); -+ -+ audited &= ~mask; -+ } -+ appendStringInfo(&buf, " }"); -+ -+ tclass_name = sepgsqlGetClassString(tclass); -+ -+ /* call external audit module, if loaded */ -+ if (sepgsqlAvcAuditHook) -+ (*sepgsqlAvcAuditHook) (denied, scontext, tcontext, -+ tclass_name, buf.data, audit_name); -+ else -+ { -+ appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s", -+ scontext, tcontext, tclass_name); -+ if (audit_name) -+ appendStringInfo(&buf, " name=%s", audit_name); -+ -+ ereport(LOG, -+ (errcode(ERRCODE_SELINUX_AUDIT), -+ errmsg("SELinux: %s %s", -+ denied ? "denied" : "granted", buf.data))); -+ } -+} -+ -+/* -+ * sepgsqlAvcReclaim -+ * -+ * It wipes recently unused AVC entries, when the number of entries -+ * reaches AVC_HASH_NUM_NODES.. -+ */ -+static void -+sepgsqlAvcReclaim(avc_page *page) -+{ -+ ListCell *l; -+ avc_datum *cache; -+ -+ while (page->avc_count > AVC_HASH_NUM_NODES) -+ { -+ foreach (l, page->slot[page->lru_hint]) -+ { -+ cache = lfirst(l); -+ -+ if (cache->hot_cache) -+ cache->hot_cache = false; -+ else -+ { -+ list_delete_ptr(page->slot[page->lru_hint], cache); -+ pfree(cache); -+ page->avc_count--; -+ } -+ } -+ page->lru_hint = (page->lru_hint + 1) % AVC_HASH_NUM_SLOTS; -+ } -+} -+ -+/* -+ * sepgsqlAvcMakeEntry -+ * -+ * It makes a new AVC entry and insert it on the avc_page. -+ * If is hold more than AVC_HASH_NUM_NODES entries, recently unused -+ * avc_datum shall be reclaimed. -+ */ -+#define avc_hash_key(trelid,tsecid,tclass) \ -+ (hash_uint32((trelid) ^ (tsecid) ^ ((tclass) << 3))) -+ -+static avc_datum * -+sepgsqlAvcMakeEntry(avc_page *page, sepgsql_sid_t tsid, uint16 tclass) -+{ -+ security_context_t scontext, tcontext, ncontext; -+ security_class_t tclass_ex; -+ MemoryContext oldctx; -+ struct av_decision avd; -+ avc_datum *cache; -+ uint32 hash_key, index; -+ -+ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass); -+ index = hash_key % AVC_HASH_NUM_SLOTS; -+ -+ scontext = page->scontext; -+ tcontext = securityRawSecLabelOut(tsid.relid, tsid.secid); -+ -+ /* -+ * Compute SELinux permission -+ */ -+ tclass_ex = sepgsqlTransToExternalClass(tclass); -+ if (tclass_ex > 0) -+ { -+ if (security_compute_av_flags_raw(scontext, tcontext, -+ tclass_ex, 0, &avd) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: unable to compute av_decision: " -+ "scontext=%s tcontext=%s tclass=%s", -+ scontext, tcontext, -+ sepgsqlGetClassString(tclass)))); -+ sepgsqlTransToInternalPerms(tclass, &avd); -+ } -+ else -+ { -+ /* fill it up as undefined class */ -+ avd.allowed = (security_deny_unknown() ? 0 : ~0UL); -+ avd.decided = ~0UL; -+ avd.auditallow = 0UL; -+ avd.auditdeny = ~0UL; -+ avd.flags = 0; -+ } -+ -+ /* -+ * Compute New security context -+ */ -+ if (security_compute_create_raw(scontext, tcontext, -+ tclass_ex, &ncontext) < 0) -+ { -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: unable to compute new context: " -+ "scontext=%s tcontext=%s tclass=%s", -+ scontext, tcontext, sepgsqlGetClassString(tclass)))); -+ } -+ -+ /* -+ * Copy them to avc_datum -+ */ -+ oldctx = MemoryContextSwitchTo(AvcMemCtx); -+ PG_TRY(); -+ { -+ cache = palloc0(sizeof(avc_datum) + strlen(ncontext)); -+ } -+ PG_CATCH(); -+ { -+ freecon(ncontext); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ -+ cache->hash_key = hash_key; -+ cache->tclass = tclass; -+ cache->tsid.relid = tsid.relid; -+ cache->tsid.secid = tsid.secid; -+ /* cache->nsid shall be set later */ -+ -+ cache->allowed = avd.allowed; -+ cache->decided = avd.decided; -+ cache->auditallow = avd.auditallow; -+ cache->auditdeny = avd.auditdeny; -+ -+ cache->hot_cache = true; -+ if (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) -+ cache->permissive = true; -+ strcpy(cache->ncontext, ncontext); -+ freecon(ncontext); -+ -+ sepgsqlAvcReclaim(page); -+ -+ page->slot[index] = lcons(cache, page->slot[index]); -+ page->avc_count++; -+ -+ MemoryContextSwitchTo(oldctx); -+ -+ return cache; -+} -+ -+/* -+ * sepgsqlAvcLookup -+ * -+ * It lookups required AVC entry. -+ */ -+static avc_datum * -+sepgsqlAvcLookup(avc_page *page, sepgsql_sid_t tsid, uint16 tclass) -+{ -+ avc_datum *cache = NULL; -+ uint32 hash_key, index; -+ ListCell *l; -+ -+ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass); -+ index = hash_key % AVC_HASH_NUM_SLOTS; -+ -+ foreach (l, page->slot[index]) -+ { -+ cache = lfirst(l); -+ if (cache->hash_key == hash_key -+ && cache->tclass == tclass -+ && cache->tsid.relid == tsid.relid -+ && cache->tsid.secid == tsid.secid) -+ { -+ cache->hot_cache = true; -+ return cache; -+ } -+ } -+ return NULL; -+} -+ -+/* -+ * sepgsqlAvcSwitchClientLabel() -+ * -+ * It switches the current avc_page. -+ * An avc_page is a set of cached access control decisions associated -+ * with a certain privilege of the client. This structure enables to -+ * lookup required avc_datum without any comparison to the subject -+ * label. -+ */ -+void -+sepgsqlAvcSwitchClient(const char *scontext) -+{ -+ MemoryContext oldctx; -+ avc_page *new_page; -+ int i; -+ -+ if (current_page) -+ { -+ new_page = current_page; -+ do { -+ if (strcmp(new_page->scontext, scontext) == 0) -+ { -+ current_page = new_page; -+ return; -+ } -+ new_page = new_page->next; -+ } while (new_page != current_page); -+ } -+ -+ /* Not found, create a new avc_page */ -+ oldctx = MemoryContextSwitchTo(AvcMemCtx); -+ new_page = palloc0(sizeof(avc_page)); -+ new_page->scontext = pstrdup(scontext); -+ MemoryContextSwitchTo(oldctx); -+ -+ for (i=0; i < AVC_HASH_NUM_SLOTS; i++) -+ new_page->slot[i] = NIL; -+ -+ if (!current_page) -+ new_page->next = new_page; -+ else -+ { -+ new_page->next = current_page->next; -+ current_page->next = new_page; -+ } -+ -+ current_page = new_page; -+} -+ -+/* -+ * sepgsqlClientHasPerms -+ * -+ * It checks client's privileges on the given object using avc. -+ */ -+bool -+sepgsqlClientHasPerms(sepgsql_sid_t tsid, -+ uint16 tclass, uint32 required, -+ const char *audit_name, bool abort) -+{ -+ avc_datum *cache; -+ uint32 denied, audited; -+ bool result = true; -+ -+ Assert(required != 0); -+ -+ do { -+ cache = sepgsqlAvcLookup(current_page, tsid, tclass); -+ if (!cache) -+ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass); -+ } while (!sepgsqlAvcCheckValid()); -+ -+ denied = required & ~cache->allowed; -+ audited = denied ? (denied & cache->auditdeny) -+ : (required & cache->auditallow); -+ if (audited) -+ { -+ sepgsqlAvcAudit(!!denied, -+ current_page->scontext, -+ securityRawSecLabelOut(tsid.relid, tsid.secid), -+ cache->tclass, audited, audit_name); -+ } -+ -+ if (denied) -+ { -+ if (!sepgsqlGetEnforce() || cache->permissive) -+ cache->allowed |= required; /* prevent flood of audit log */ -+ else -+ { -+ if (abort) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: security policy violation"))); -+ result = false; -+ } -+ } -+ -+ return result; -+} -+ -+/* -+ * sepgsqlClientCreateSecid -+ * sepgsqlClientCreateLabel -+ */ -+sepgsql_sid_t -+sepgsqlClientCreateSecid(sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) -+{ -+ sepgsql_sid_t nsid; -+ avc_datum *cache; -+ int index; -+ -+ do { -+ cache = sepgsqlAvcLookup(current_page, tsid, tclass); -+ if (!cache) -+ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass); -+ -+ index = (nrelid % AVC_DATUM_NSID_SLOTS); -+ if (cache->nsid[index].relid != nrelid) -+ { -+ cache->nsid[index].secid -+ = securityRawSecLabelIn(nrelid, cache->ncontext); -+ cache->nsid[index].relid = nrelid; -+ } -+ nsid = cache->nsid[index]; -+ } while (!sepgsqlAvcCheckValid()); -+ -+ return nsid; -+} -+ -+security_context_t -+sepgsqlClientCreateLabel(sepgsql_sid_t tsid, uint16 tclass) -+{ -+ avc_datum *cache; -+ -+ do { -+ cache = sepgsqlAvcLookup(current_page, tsid, tclass); -+ if (!cache) -+ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass); -+ } while (!sepgsqlAvcCheckValid()); -+ -+ return cache->ncontext; -+} -+ -+/* -+ * sepgsqlComputePerms -+ * sepgsqlComputeCreate -+ * -+ * The following two functions make a query to in-kernel SELinux -+ * without userspace caches, due to some reasons. -+ * The AVC can cover most of cases, but some of corner cases are -+ * not suitable for AVC structure, so we need uncached interfaces. -+ * For example, AVC is unavailable when we tries to load a shared -+ * library module, because security context of the library does not -+ * have its security identifier, so we cannot put it on AVC. -+ */ -+bool -+sepgsqlComputePerms(char *scontext, char *tcontext, -+ uint16 tclass_in, uint32 required, -+ const char *audit_name, bool abort) -+{ -+ access_vector_t denied, audited; -+ security_class_t tclass_ex; -+ struct av_decision avd; -+ -+ Assert(required != 0); -+ -+ tclass_ex = sepgsqlTransToExternalClass(tclass_in); -+ if (tclass_ex > 0) -+ { -+ /* -+ * security_compute_av_flags_raw() is a SELinux's API that -+ * returns its access control decision based on the security -+ * policy, to the given combination of user's privilege -+ * (scontext; security label of the client process), -+ * target's attribute (tcontext; security label of the -+ * object) and type of actions (tclass; object classes). -+ * -+ * The returned avd.allowed is a bitmap of allowed actions. -+ */ -+ if (security_compute_av_flags_raw(scontext, tcontext, -+ tclass_ex, 0, &avd) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: could not compute av_decision: " -+ "scontext=%s tcontext=%s tclass=%s", -+ scontext, tcontext, -+ sepgsqlGetClassString(tclass_in)))); -+ sepgsqlTransToInternalPerms(tclass_in, &avd); -+ } -+ else -+ { -+ /* -+ * If security policy does not support database related -+ * permissions, it fulls up permission bits by dummy -+ * data. -+ * If security_deny_unknown() returns positive value, -+ * undefined permissions should not be allowed. -+ * Otherwise, it shall be allowed. -+ */ -+ avd.allowed = (security_deny_unknown() > 0 ? 0 : ~0UL); -+ avd.decided = ~0UL; -+ avd.auditallow = 0UL; -+ avd.auditdeny = ~0UL; -+ avd.flags = 0; -+ } -+ -+ denied = required & ~avd.allowed; -+ audited = denied ? (denied & avd.auditdeny) -+ : (required & avd.auditallow); -+ if (audited) -+ { -+ /* -+ * If security policy requires to generate an audit log -+ * record for the given request, it should be logged. -+ */ -+ sepgsqlAvcAudit(!!denied, scontext, tcontext, -+ tclass_in, audited, audit_name); -+ } -+ -+ /* -+ * If any required permissions are not allowed, and -+ * SE-PgSQL performs in enforcing mode, and the given -+ * combination of subject, object and action does not -+ * have special flag to be handled as permission, -+ * SE-PgSQL returns false or raises an error. -+ * Otherwise, it returns true that means required -+ * actions are allowed. -+ */ -+ if (!denied || /* no policy violation */ -+ !sepgsqlGetEnforce() || /* permissive mode */ -+ (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) != 0) /* permissive domain */ -+ return true; -+ -+ if (abort) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: security policy violation"))); -+ -+ return false; -+} -+ -+char * -+sepgsqlComputeCreate(char *scontext, char *tcontext, uint16 tclass_in) -+{ -+ security_context_t ncontext, result; -+ security_class_t tclass_ex; -+ -+ tclass_ex = sepgsqlTransToExternalClass(tclass_in); -+ /* -+ * security_compute_create_raw() is a SELinux's API that -+ * returns a default security context to be assigned on -+ * a new object (categorized by object class) when a client -+ * labeled as scontext tries to create a new one under the -+ * parent object labeled as tcontext. -+ */ -+ if (security_compute_create_raw(scontext, tcontext, -+ tclass_ex, &ncontext) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("SELinux: could not compute a new context " -+ "scontext=%s tcontext=%s tclass=%s", -+ scontext, tcontext, sepgsqlGetClassString(tclass_in)))); -+ PG_TRY(); -+ { -+ result = pstrdup(ncontext); -+ } -+ PG_CATCH(); -+ { -+ freecon(ncontext); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(ncontext); -+ -+ return result; -+} -+ -+/* -+ * SELinux state monitoring process -+ * -+ * This process is forked from postmaster to monitor the state of SELinux. -+ * SELinux can make a notifier message to userspace object manager via -+ * netlink socket. When it receives the message, it updates selinux_state -+ * structure assigned on shared memory region to make any instance reset -+ * its AVC soon. -+ */ -+static int -+sepgsql_cb_log(int type, const char *fmt, ...) -+{ -+ char *c, buffer[1024]; -+ va_list ap; -+ -+ va_start(ap, fmt); -+ vsnprintf(buffer, sizeof(buffer), fmt, ap); -+ va_end(ap); -+ -+ c = strrchr(buffer, '\n'); -+ if (c) -+ *c = '\0'; -+ -+ ereport(LOG, -+ (errcode(ERRCODE_SELINUX_INFO), -+ errmsg("%s", buffer))); -+ -+ return 0; -+} -+ -+static int -+sepgsql_cb_setenforce(int enforce) -+{ -+ /* switch enforcing/permissive */ -+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); -+ selinux_state->enforcing = (enforce ? true : false); -+ selinux_state->version++; -+ LWLockRelease(SepgsqlAvcLock); -+ -+ return 0; -+} -+ -+static int -+sepgsql_cb_policyload(int seqno) -+{ -+ /* invalidate local avc */ -+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); -+ selinux_state->version++; -+ LWLockRelease(SepgsqlAvcLock); -+ -+ return 0; -+} -+ -+void -+sepgsqlReceiverMain(void) -+{ -+ union selinux_callback cb; -+ -+ Assert(sepgsqlIsEnabled()); -+ -+#ifdef HAVE_SETSID -+ if (setsid() < 0) -+ elog(FATAL, "setsid() failed: %m"); -+#endif -+ -+ /* -+ * setup the signal handler -+ */ -+ pqinitmask(); -+ pqsignal(SIGHUP, SIG_IGN); -+ pqsignal(SIGINT, SIG_IGN); -+ pqsignal(SIGTERM, exit); -+ pqsignal(SIGQUIT, exit); -+ pqsignal(SIGUSR1, SIG_IGN); -+ pqsignal(SIGUSR2, SIG_IGN); -+ pqsignal(SIGCHLD, SIG_DFL); -+ PG_SETMASK(&UnBlockSig); -+ -+ /* -+ * map shared memory segment -+ */ -+ sepgsqlShmemInit(); -+ -+ ereport(LOG, -+ (errcode(ERRCODE_SELINUX_INFO), -+ errmsg("SELinux: security policy monitor (pid=%u)", getpid()))); -+ /* -+ * setup callback functions from avc_netlink_loop() -+ */ -+ cb.func_log = sepgsql_cb_log; -+ selinux_set_callback(SELINUX_CB_LOG, cb); -+ cb.func_setenforce = sepgsql_cb_setenforce; -+ selinux_set_callback(SELINUX_CB_SETENFORCE, cb); -+ cb.func_policyload = sepgsql_cb_policyload; -+ selinux_set_callback(SELINUX_CB_POLICYLOAD, cb); -+ -+ /* -+ * open netlink socket and wait for messages -+ */ -+ avc_netlink_open(1); -+ -+ avc_netlink_loop(); -+ -+ exit(0); -+} -diff --git a/src/backend/security/sepgsql/bridge.c b/src/backend/security/sepgsql/bridge.c -new file mode 100644 -index 0000000..3a3630c ---- /dev/null -+++ b/src/backend/security/sepgsql/bridge.c -@@ -0,0 +1,2920 @@ -+/* -+ * src/backend/security/sepgsql/bridge.c -+ * -+ * New style security hooks for SE-PostgreSQL -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+#include "postgres.h" -+ -+#include "access/sysattr.h" -+#include "catalog/heap.h" -+#include "catalog/indexing.h" -+#include "catalog/pg_authid.h" -+#include "catalog/pg_cast.h" -+#include "catalog/pg_conversion.h" -+#include "catalog/pg_database.h" -+#include "catalog/pg_foreign_data_wrapper.h" -+#include "catalog/pg_foreign_server.h" -+#include "catalog/pg_language.h" -+#include "catalog/pg_largeobject_metadata.h" -+#include "catalog/pg_namespace.h" -+#include "catalog/pg_operator.h" -+#include "catalog/pg_opclass.h" -+#include "catalog/pg_opfamily.h" -+#include "catalog/pg_proc.h" -+#include "catalog/pg_rewrite.h" -+#include "catalog/pg_security.h" -+#include "catalog/pg_tablespace.h" -+#include "catalog/pg_ts_parser.h" -+#include "catalog/pg_ts_dict.h" -+#include "catalog/pg_ts_template.h" -+#include "catalog/pg_ts_config.h" -+#include "catalog/pg_type.h" -+#include "catalog/pg_user_mapping.h" -+#include "commands/dbcommands.h" -+#include "miscadmin.h" -+#include "security/sepgsql.h" -+#include "utils/builtins.h" -+#include "utils/fmgroids.h" -+#include "utils/lsyscache.h" -+#include "utils/syscache.h" -+#include "utils/tqual.h" -+ -+#include -+#include -+ -+/* ------------------------------------------------------------ * -+ * Common Helper Routines -+ * ------------------------------------------------------------ */ -+static bool sepgsql_database_common(Oid datOid, uint32 required, bool abort); -+static bool sepgsql_schema_common(Oid nspOid, uint32 required, bool abort); -+static bool sepgsql_attribute_common(Oid relOid, AttrNumber attnum, -+ uint32 required, bool abort); -+static bool sepgsql_relation_common(Oid relOid, uint32 required, bool abort); -+static bool sepgsql_proc_common(Oid procOid, uint32 required, bool abort); -+static bool sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort); -+static bool sepgsql_foreign_server_common(Oid fsrvOid, uint32 required, bool abort); -+static bool sepgsql_language_common(Oid langOid, uint32 required, bool abort); -+static bool sepgsql_operator_common(Oid oprOid, uint32 required, bool abort); -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_database related security hooks -+ * -+ * ------------------------------------------------------------ */ -+static bool -+sepgsql_database_common(Oid datOid, uint32 required, bool abort) -+{ -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ const char *auname; -+ bool rc; -+ -+ tuple = SearchSysCache(DATABASEOID, -+ ObjectIdGetDatum(datOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for database: %u", datOid); -+ -+ auname = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); -+ -+ sid = sepgsqlGetTupleSecid(DatabaseRelationId, tuple, &tclass); -+ -+ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); -+ -+ ReleaseSysCache(tuple); -+ -+ return rc; -+} -+ -+Oid -+sepgsql_database_create(const char *datName, Oid srcDatOid, DefElem *newLabel) -+{ -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ if (!newLabel) -+ sid = sepgsqlGetDefaultDatabaseSecid(srcDatOid); -+ else -+ { -+ sid.relid = DatabaseRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, -+ strVal(newLabel->arg)); -+ } -+ -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_DATABASE, -+ SEPG_DB_DATABASE__CREATE, -+ datName, true); -+ return sid.secid; -+} -+ -+void -+sepgsql_database_alter(Oid datOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_database_common(datOid, SEPG_DB_DATABASE__SETATTR, true); -+} -+ -+void -+sepgsql_database_drop(Oid datOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_database_common(datOid, SEPG_DB_DATABASE__DROP, true); -+} -+ -+Oid -+sepgsql_database_relabel(Oid datOid, DefElem *newLabel) -+{ -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (newLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SELinux is disabled now"))); -+ -+ return InvalidOid; -+ } -+ sid.relid = DatabaseRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); -+ -+ /* db_database:{setattr relabelfrom} to older seclabel */ -+ sepgsql_database_common(datOid, -+ SEPG_DB_DATABASE__SETATTR | -+ SEPG_DB_DATABASE__RELABELFROM, true); -+ -+ /* db_database:{relabelto} to newer seclabel */ -+ sepgsqlClientHasPerms(sid, -+ SEPG_CLASS_DB_DATABASE, -+ SEPG_DB_DATABASE__RELABELTO, -+ get_database_name(datOid), true); -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_database_grant(Oid datOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_database_common(datOid, SEPG_DB_DATABASE__SETATTR, true); -+} -+ -+void -+sepgsql_database_access(Oid datOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_database_common(datOid, SEPG_DB_DATABASE__ACCESS, true); -+} -+ -+void -+sepgsql_database_load_module(Oid datOid, const char *filename) -+{ -+ HeapTuple tuple; -+ security_context_t filecon; -+ security_context_t datcon; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ /* -+ * It assumes preloaded libraries are secure, -+ * because it can be set up using guc variable -+ * not any SQL statements. -+ */ -+ if (GetProcessingMode() == InitProcessing) -+ return; -+ -+ /* Get database context */ -+ tuple = SearchSysCache(DATABASEOID, -+ ObjectIdGetDatum(datOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for database: %u", datOid); -+ -+ datcon = securityRawSecLabelOut(DatabaseRelationId, -+ HeapTupleGetSecid(tuple)); -+ ReleaseSysCache(tuple); -+ -+ /* Get library context */ -+ if (getfilecon_raw(filename, &filecon) < 0) -+ ereport(ERROR, -+ (errcode_for_file_access(), -+ errmsg("could not access file \"%s\": %m", filename))); -+ PG_TRY(); -+ { -+ sepgsqlComputePerms(datcon, -+ filecon, -+ SEPG_CLASS_DB_DATABASE, -+ SEPG_DB_DATABASE__LOAD_MODULE, -+ filename, true); -+ } -+ PG_CATCH(); -+ { -+ freecon(filecon); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(filecon); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_namespace related security hooks -+ * -+ * ------------------------------------------------------------ */ -+static bool -+sepgsql_schema_common(Oid nspOid, uint32 required, bool abort) -+{ -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ const char *auname; -+ bool rc; -+ -+ tuple = SearchSysCache(NAMESPACEOID, -+ ObjectIdGetDatum(nspOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for namespace: %u", nspOid); -+ -+ sid = sepgsqlGetTupleSecid(NamespaceRelationId, tuple, &tclass); -+ -+ auname = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); -+ -+ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); -+ -+ ReleaseSysCache(tuple); -+ -+ return rc; -+} -+ -+Oid -+sepgsql_schema_create(const char *nspName, bool isTemp, DefElem *newLabel) -+{ -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ if (!newLabel) -+ sid = sepgsqlGetDefaultSchemaSecid(MyDatabaseId); -+ else -+ { -+ sid.relid = NamespaceRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); -+ } -+ -+ sepgsqlClientHasPerms(sid, -+ SEPG_CLASS_DB_SCHEMA, -+ SEPG_DB_SCHEMA__CREATE, -+ nspName, true); -+ return sid.secid; -+} -+ -+void -+sepgsql_schema_alter(Oid nspOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SETATTR, true); -+} -+ -+void -+sepgsql_schema_drop(Oid nspOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__DROP, true); -+} -+ -+Oid -+sepgsql_schema_relabel(Oid nspOid, DefElem *newLabel) -+{ -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (newLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SELinux is disabled now"))); -+ return InvalidOid; -+ } -+ sid.relid = NamespaceRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); -+ -+ /* db_schema:{setattr relabelfrom} for older seclabel */ -+ sepgsql_schema_common(nspOid, -+ SEPG_DB_SCHEMA__SETATTR | -+ SEPG_DB_SCHEMA__RELABELFROM, true); -+ -+ /* db_schema:{relabelto} for newer seclabel */ -+ sepgsqlClientHasPerms(sid, -+ SEPG_CLASS_DB_SCHEMA, -+ SEPG_DB_SCHEMA__RELABELTO, -+ get_namespace_name(nspOid), true); -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_schema_grant(Oid nspOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SETATTR, true); -+} -+ -+bool -+sepgsql_schema_search(Oid nspOid, bool abort) -+{ -+ if (!sepgsqlIsEnabled()) -+ return true; -+ -+ return sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__SEARCH, abort); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_attribute related security hooks -+ * -+ * ------------------------------------------------------------ */ -+static bool -+sepgsql_attribute_common(Oid relOid, AttrNumber attnum, -+ uint32 required, bool abort) -+{ -+ Form_pg_attribute attForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ char auname[NAMEDATALEN * 2 + 3]; -+ bool rc = true; -+ -+ /* Caller prevent case when relkind != RELKIND_RELATION */ -+ Assert(get_rel_relkind(relOid) == RELKIND_RELATION); -+ -+ tuple = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(relOid), -+ Int16GetDatum(attnum), -+ 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for attribute %d of relation %u", -+ attnum, relOid); -+ attForm = (Form_pg_attribute) GETSTRUCT(tuple); -+ -+ /* -+ * NOTE: when a table to be dropped, corresponding attributes -+ * are also removed. Some of them can be already logically -+ * dropped using ALTER TABLE ... DROP statement. -+ * In this case, SE-PostgreSQL does not check anything. -+ * If any other situation touches dropped column, it is a bug. -+ */ -+ if (attForm->attisdropped) -+ goto skip; -+ -+ sprintf(auname, "%s.%s", get_rel_name(relOid), NameStr(attForm->attname)); -+ -+ sid = sepgsqlGetTupleSecid(AttributeRelationId, tuple, &tclass); -+ -+ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); -+ -+skip: -+ ReleaseSysCache(tuple); -+ -+ return rc; -+} -+ -+Oid -+sepgsql_attribute_create(Oid relOid, ColumnDef *cdef) -+{ -+ sepgsql_sid_t sid; -+ char relkind; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (cdef->secLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SELinux is disabled now"))); -+ return InvalidOid; -+ } -+ -+ relkind = get_rel_relkind(relOid); -+ if (relkind == RELKIND_RELATION) -+ { -+ char auname[NAMEDATALEN * 2 + 3]; -+ -+ if (!cdef->secLabel) -+ sid = sepgsqlGetDefaultColumnSecid(relOid); -+ else -+ { -+ char *label = strVal(((DefElem *)cdef->secLabel)->arg); -+ -+ sid.relid = AttributeRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, label); -+ } -+ -+ sprintf(auname, "%s.%s", get_rel_name(relOid), cdef->colname); -+ sepgsqlClientHasPerms(sid, -+ SEPG_CLASS_DB_COLUMN, -+ SEPG_DB_COLUMN__CREATE, -+ auname, true); -+ } -+ else -+ { -+ /* no need to check for toast relation */ -+ if (relkind != RELKIND_TOASTVALUE) -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+ return InvalidOid; -+ } -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_attribute_alter(Oid relOid, const char *attname) -+{ -+ AttrNumber attno; -+ char relkind; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* -+ * If the target attribute does not exist, an error -+ * shall be raised later. -+ */ -+ attno = get_attnum(relOid, attname); -+ if (attno == InvalidAttrNumber) -+ return; -+ -+ relkind = get_rel_relkind(relOid); -+ if (relkind == RELKIND_RELATION) -+ { -+ sepgsql_attribute_common(relOid, attno, SEPG_DB_COLUMN__SETATTR, true); -+ } -+ else if (relkind != RELKIND_TOASTVALUE) -+ { -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+ } -+} -+ -+void -+sepgsql_attribute_drop(Oid relOid, AttrNumber attnum) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* -+ * We only need to check db_column:{drop} when relkind equals -+ * RELKIND_RELATION, because db_xxx:{drop} permission is already -+ * checked in other cases. (e.g DROP SEQUENCE, ...) -+ */ -+ if (get_rel_relkind(relOid) == RELKIND_RELATION) -+ sepgsql_attribute_common(relOid, attnum, -+ SEPG_DB_COLUMN__DROP, true); -+} -+ -+void -+sepgsql_attribute_grant(Oid relOid, AttrNumber attnum) -+{ -+ char relkind; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ relkind = get_rel_relkind(relOid); -+ if (relkind == RELKIND_RELATION) -+ { -+ sepgsql_attribute_common(relOid, attnum, SEPG_DB_COLUMN__SETATTR, true); -+ } -+ else if (relkind != RELKIND_TOASTVALUE) -+ { -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+ } -+} -+ -+Oid -+sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, DefElem *newLabel) -+{ -+ sepgsql_sid_t sid; -+ char auname[NAMEDATALEN * 2 + 3]; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (!newLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SELinux is disabled now"))); -+ return InvalidOid; -+ } -+ -+ Assert(get_rel_relkind(relOid) == RELKIND_RELATION); -+ -+ sid.relid = AttributeRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); -+ -+ /* db_column:{setattr relabelfrom} */ -+ sepgsql_attribute_common(relOid, attnum, -+ SEPG_DB_COLUMN__SETATTR | -+ SEPG_DB_COLUMN__RELABELFROM, true); -+ -+ /* db_column:{relabelto} */ -+ sprintf(auname, "%s.%s", -+ get_rel_name(relOid), -+ get_attname(relOid, attnum)); -+ sepgsqlClientHasPerms(sid, -+ SEPG_CLASS_DB_COLUMN, -+ SEPG_DB_COLUMN__RELABELTO, -+ auname, true); -+ -+ return sid.secid; -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_class related security hooks -+ * -+ * ------------------------------------------------------------ */ -+static bool -+sepgsql_relation_common(Oid relOid, uint32 required, bool abort) -+{ -+ Form_pg_class relForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ bool rc; -+ -+ tuple = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for relation %u", relOid); -+ relForm = (Form_pg_class) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(RelationRelationId, tuple, &tclass); -+ rc = sepgsqlClientHasPerms(sid, tclass, required, -+ NameStr(relForm->relname), abort); -+ -+ ReleaseSysCache(tuple); -+ -+ return rc; -+} -+ -+/* -+ * sepgsql_relation_create -+ * It returns an array of security identifier for the new table -+ * and columns to be assigned. The corresponding security labels -+ * are already checked for db_table/db_sequence/db_column:{create} -+ * permission. -+ * In the default labeling rule, a column inherits the security -+ * label of its table, but we cannot refer it using system caches, -+ * because the command counter is not incremented under the -+ * heap_create_with_catalog(). Thus, we need to compute and check -+ * them prior to the actual creation of table and columns. -+ */ -+Oid * -+sepgsql_relation_create(const char *relName, -+ char relkind, -+ TupleDesc tupDesc, -+ Oid nspOid, -+ DefElem *relLabel, -+ List *colList, -+ bool createAs, -+ bool permission) -+{ -+ Oid *secLabels; -+ sepgsql_sid_t relsid; -+ uint16 tclass; -+ uint32 required; -+ int index; -+ -+ if (!sepgsqlIsEnabled()) -+ return NULL; -+ -+ switch (relkind) -+ { -+ case RELKIND_RELATION: -+ if (!relLabel) -+ relsid = sepgsqlGetDefaultTableSecid(nspOid); -+ else -+ { -+ relsid.relid = RelationRelationId; -+ relsid.secid = securityTransSecLabelIn(relsid.relid, -+ strVal(relLabel->arg)); -+ } -+ tclass = SEPG_CLASS_DB_TABLE; -+ required = SEPG_DB_TABLE__CREATE; -+ if (createAs) -+ required |= SEPG_DB_TABLE__INSERT; -+ break; -+ -+ case RELKIND_SEQUENCE: -+ if (!relLabel) -+ relsid = sepgsqlGetDefaultSequenceSecid(nspOid); -+ else -+ { -+ relsid.relid = RelationRelationId; -+ relsid.secid = securityTransSecLabelIn(relsid.relid, -+ strVal(relLabel->arg)); -+ } -+ tclass = SEPG_CLASS_DB_SEQUENCE; -+ required = SEPG_DB_SEQUENCE__CREATE; -+ break; -+ -+ default: -+ if (!relLabel) -+ relsid = sepgsqlGetDefaultTupleSecid(RelationRelationId); -+ else -+ { -+ /* should not be happen */ -+ relsid.relid = RelationRelationId; -+ relsid.secid = securityTransSecLabelIn(relsid.relid, -+ strVal(relLabel->arg)); -+ } -+ tclass = SEPG_CLASS_DB_TUPLE; -+ required = SEPG_DB_TUPLE__INSERT; -+ break; -+ } -+ -+ /* -+ * The secLabeld array stores security identifiers to be assigned -+ * on the new table and columns. -+ * -+ * secLabels[0] is security identifier of the table. -+ * secLabels[attnum - FirstLowInvalidHeapAttributeNumber] -+ * is security identifier of columns (if necessary). -+ */ -+ secLabels = palloc0(sizeof(Oid) * (tupDesc->natts -+ - FirstLowInvalidHeapAttributeNumber)); -+ -+ /* relation's security identifier to be assigned on */ -+ secLabels[0] = relsid.secid; -+ -+ /* -+ * Note that this hook can be called during initdb processes. -+ * It is an exception of access controls, so we skip any checks. -+ * -+ * And, we don't need any checks for toast relations, because -+ * it is a quite internal stuff. -+ */ -+ if (permission) -+ { -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_table:{create}, db_sequence:{create} or db_tuple:{insert} */ -+ sepgsqlClientHasPerms(relsid, tclass, required, relName, true); -+ } -+ -+ /* no individual security context expect for RELKIND_RELATION */ -+ if (relkind != RELKIND_RELATION) -+ return secLabels; -+ -+ /* -+ * db_column:{create} permission -+ */ -+ for (index = FirstLowInvalidHeapAttributeNumber + 1; -+ index < tupDesc->natts; -+ index++) -+ { -+ Form_pg_attribute attr; -+ sepgsql_sid_t attsid = { InvalidOid, InvalidOid }; -+ char attname[NAMEDATALEN * 2 + 3]; -+ ListCell *l; -+ -+ /* skip unnecessary attributes */ -+ if (index == ObjectIdAttributeNumber && !tupDesc->tdhasoid) -+ continue; -+ -+ if (index < 0) -+ attr = SystemAttributeDefinition(index, tupDesc->tdhasoid); -+ else -+ attr = tupDesc->attrs[index]; -+ -+ /* Is there any given security context? */ -+ foreach (l, colList) -+ { -+ ColumnDef *cdef = lfirst(l); -+ -+ if (cdef->secLabel && -+ strcmp(cdef->colname, NameStr(attr->attname)) == 0) -+ { -+ attsid.relid = AttributeRelationId; -+ attsid.secid = securityTransSecLabelIn(attsid.relid, -+ strVal(((DefElem *)cdef->secLabel)->arg)); -+ break; -+ } -+ } -+ -+ /* default security context, if not given */ -+ if (!SidIsValid(attsid)) -+ attsid = sepgsqlClientCreateSecid(relsid, -+ SEPG_CLASS_DB_COLUMN, -+ AttributeRelationId); -+ if (permission) -+ { -+ required = SEPG_DB_COLUMN__CREATE; -+ -+ if (createAs) -+ required |= SEPG_DB_COLUMN__INSERT; -+ -+ /* db_column:{create (insert)} */ -+ sprintf(attname, "%s.%s", relName, NameStr(attr->attname)); -+ sepgsqlClientHasPerms(attsid, -+ SEPG_CLASS_DB_COLUMN, -+ required, attname, true); -+ } -+ /* column's security identifier to be assigend on */ -+ secLabels[index - FirstLowInvalidHeapAttributeNumber] = attsid.secid; -+ } -+ -+ return secLabels; -+} -+ -+/* -+ * sepgsql_relation_copy -+ * It returns an array of security identifier of table and columns -+ * to be copied on make_new_heap(). It actually create a new temporary -+ * relation and insert all the tuples within original one into the -+ * temporary one, but swap_relation_files() swaps their file nodes. -+ * Thus, there are no changes from the viewpoint of users. -+ * SE-PostgreSQL also does not check and change anything. It simply -+ * copies security identifier of the source relation to the destination -+ * relation. -+ */ -+Oid * -+sepgsql_relation_copy(Relation src) -+{ -+ Oid *secLabels; -+ HeapTuple tuple; -+ Oid relOid = RelationGetRelid(src); -+ int index; -+ -+ if (!sepgsqlIsEnabled()) -+ return NULL; -+ -+ /* see the comment at sepgsqlCreateTableColumn*/ -+ secLabels = palloc0(sizeof(Oid) * (RelationGetDescr(src)->natts -+ - FirstLowInvalidHeapAttributeNumber)); -+ -+ /* copy table's security identifier */ -+ tuple = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for relation \"%s\"", -+ RelationGetRelationName(src)); -+ -+ secLabels[0] = HeapTupleGetSecid(tuple); -+ -+ ReleaseSysCache(tuple); -+ -+ /* copy column's security identifier */ -+ for (index = FirstLowInvalidHeapAttributeNumber + 1; -+ index < RelationGetDescr(src)->natts; -+ index++) -+ { -+ Form_pg_attribute attr; -+ -+ if (index < 0) -+ attr = SystemAttributeDefinition(index, true); -+ else -+ attr = RelationGetDescr(src)->attrs[index]; -+ -+ tuple = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(relOid), -+ Int16GetDatum(attr->attnum), -+ 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ continue; -+ -+ secLabels[index - FirstLowInvalidHeapAttributeNumber] -+ = HeapTupleGetSecid(tuple); -+ -+ ReleaseSysCache(tuple); -+ } -+ -+ return secLabels; -+} -+ -+void -+sepgsql_relation_alter(Oid relOid, const char *newName, Oid newNsp) -+{ -+ Form_pg_class relForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for relation %u", relOid); -+ relForm = (Form_pg_class) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(RelationRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TABLE__SETATTR, -+ NameStr(relForm->relname), true); -+ -+ /* db_schema:{add_name remove_name}, if necessary */ -+ if (newName || OidIsValid(newNsp)) -+ { -+ if (!OidIsValid(newNsp)) -+ sepgsql_schema_common(relForm->relnamespace, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ else -+ { -+ sepgsql_schema_common(relForm->relnamespace, -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true); -+ } -+ } -+ ReleaseSysCache(tuple); -+} -+ -+void -+sepgsql_relation_drop(Oid relOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ if (get_rel_relkind(relOid) == RELKIND_TOASTVALUE) -+ return; -+ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__DROP, true); -+} -+ -+void -+sepgsql_relation_grant(Oid relOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+} -+ -+Oid -+sepgsql_relation_relabel(Oid relOid, DefElem *newLabel) -+{ -+ sepgsql_sid_t sid; -+ char relkind; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (newLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SELinux is disabled now"))); -+ return InvalidOid; -+ } -+ -+ relkind = get_rel_relkind(relOid); -+ if (relkind != RELKIND_RELATION && relkind != RELKIND_SEQUENCE) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("Unable to set security label on \"%s\"", -+ get_rel_name(relOid)))); -+ -+ /* input security context */ -+ sid.relid = RelationRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); -+ -+ /* db_table/db_sequence:{setattr relabelfrom} */ -+ sepgsql_relation_common(relOid, -+ SEPG_DB_TABLE__SETATTR | -+ SEPG_DB_TABLE__RELABELFROM, true); -+ -+ /* db_table/db_sequence:{relabelto} */ -+ sepgsqlClientHasPerms(sid, -+ (relkind == RELKIND_RELATION -+ ? SEPG_CLASS_DB_TABLE -+ : SEPG_CLASS_DB_SEQUENCE), -+ SEPG_DB_TABLE__RELABELTO, -+ get_rel_name(relOid), true); -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_relation_get_transaction_id(Oid relOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__GETATTR, true); -+} -+ -+void -+sepgsql_relation_copy_definition(Oid relOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__GETATTR, true); -+} -+ -+void -+sepgsql_relation_truncate(Relation rel) -+{ -+ HeapScanDesc scan; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ Assert(RelationGetForm(rel)->relkind == RELKIND_RELATION); -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* check db_table:{delete} permission */ -+ sepgsql_relation_common(RelationGetRelid(rel), -+ SEPG_DB_TABLE__DELETE, true); -+ -+ /* row-level access control is enabled? */ -+ if (!sepostgresql_row_level) -+ return; -+ -+ /* check db_tuple:{delete} permission */ -+ scan = heap_beginscan(rel, SnapshotNow, 0, NULL); -+ -+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) -+ { -+ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ NULL, true); -+ } -+ heap_endscan(scan); -+} -+ -+void -+sepgsql_relation_lock(Oid relOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ if (get_rel_relkind(relOid) != RELKIND_RELATION) -+ return; -+ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__LOCK, true); -+} -+ -+void -+sepgsql_view_replace(Oid viewOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ Assert(get_rel_relkind(viewOid) == RELKIND_VIEW); -+ -+ sepgsql_relation_common(viewOid, SEPG_DB_TABLE__SETATTR, true); -+} -+ -+void -+sepgsql_index_create(Oid relOid, Oid nspOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* db_table:{setattr} */ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+} -+ -+void -+sepgsql_sequence_get_value(Oid seqOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE); -+ -+ sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__GET_VALUE, true); -+} -+ -+void -+sepgsql_sequence_next_value(Oid seqOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE); -+ -+ sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__NEXT_VALUE, true); -+} -+ -+void -+sepgsql_sequence_set_value(Oid seqOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ Assert(get_rel_relkind(seqOid) == RELKIND_SEQUENCE); -+ -+ sepgsql_relation_common(seqOid, SEPG_DB_SEQUENCE__SET_VALUE, true); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_proc related security hooks -+ * -+ * ------------------------------------------------------------ */ -+static bool -+sepgsql_proc_common(Oid procOid, uint32 required, bool abort) -+{ -+ sepgsql_sid_t sid; -+ HeapTuple tuple; -+ uint16 tclass; -+ const char *auname; -+ bool rc; -+ -+ tuple = SearchSysCache(PROCOID, -+ ObjectIdGetDatum(procOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for procedure: %u", procOid); -+ -+ auname = NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname); -+ sid = sepgsqlGetTupleSecid(ProcedureRelationId, tuple, &tclass); -+ -+ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, abort); -+ -+ ReleaseSysCache(tuple); -+ -+ return rc; -+} -+ -+Oid -+sepgsql_proc_create(const char *procName, HeapTuple oldTup, -+ Oid nspOid, Oid langOid, DefElem *newLabel) -+{ -+ sepgsql_sid_t sid; -+ //HeapTuple tuple; -+ uint32 required; -+ //bool trusted; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ if (!HeapTupleIsValid(oldTup)) -+ { -+ /* create a new function */ -+ required = SEPG_DB_PROCEDURE__CREATE; -+ if (!newLabel) -+ sid = sepgsqlGetDefaultProcedureSecid(nspOid); -+ else -+ { -+ sid.relid = ProcedureRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); -+ } -+ } -+ else if (!newLabel) -+ { -+ /* replace an existing function, without any label */ -+ required = SEPG_DB_PROCEDURE__SETATTR; -+ sid = sepgsqlGetTupleSecid(ProcedureRelationId, oldTup, NULL); -+ } -+ else -+ { -+ /* replace an existing function, with relabeling */ -+ sepgsql_proc_common(HeapTupleGetOid(oldTup), -+ SEPG_DB_PROCEDURE__SETATTR | -+ SEPG_DB_PROCEDURE__RELABELFROM, true); -+ -+ required = SEPG_DB_PROCEDURE__RELABELTO; -+ sid = sepgsqlGetTupleSecid(ProcedureRelationId, oldTup, NULL); -+ } -+ -+#if 0 -+ /* Procedural language is trusted? */ -+ tuple = SearchSysCache(LANGOID, -+ ObjectIdGetDatum(langOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for procedural langugage: %u", langOid); -+ -+ trusted = ((Form_pg_language) GETSTRUCT(tuple))->lanpltrusted; -+ if (!trusted) -+ required |= SEPG_DB_PROCEDURE__UNTRUSTED; -+ -+ ReleaseSysCache(tuple); -+#endif -+ -+ /* check it */ -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_PROCEDURE, -+ required, procName, true); -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_proc_alter(Oid procOid, const char *newName, Oid newNsp) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); -+ if (newName || OidIsValid(newNsp)) -+ { -+ HeapTuple tuple; -+ Oid oldNsp; -+ -+ tuple = SearchSysCache(PROCOID, -+ ObjectIdGetDatum(procOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for function %u", procOid); -+ -+ oldNsp = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; -+ -+ ReleaseSysCache(tuple); -+ -+ if (!OidIsValid(newNsp)) -+ { -+ sepgsql_schema_common(oldNsp, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ else -+ { -+ sepgsql_schema_common(oldNsp, SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true); -+ } -+ } -+} -+ -+void -+sepgsql_proc_drop(Oid procOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__DROP, true); -+} -+ -+void -+sepgsql_proc_grant(Oid procOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__SETATTR, true); -+} -+ -+Oid -+sepgsql_proc_relabel(Oid procOid, DefElem *newLabel) -+{ -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ { -+ if (newLabel) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SELinux is disabled now"))); -+ return InvalidOid; -+ } -+ -+ sid.relid = ProcedureRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(newLabel->arg)); -+ -+ /* db_procedure:{setattr relabelfrom} for older seclabel */ -+ sepgsql_proc_common(procOid, -+ SEPG_DB_PROCEDURE__SETATTR | -+ SEPG_DB_PROCEDURE__RELABELFROM, true); -+ /* db_procedure:{relabelto} for newer seclabel */ -+ sepgsqlClientHasPerms(sid, -+ SEPG_CLASS_DB_PROCEDURE, -+ SEPG_DB_PROCEDURE__RELABELTO, -+ get_func_name(procOid), true); -+ return sid.secid; -+} -+ -+void -+sepgsql_proc_execute(Oid procOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__EXECUTE, true); -+} -+ -+bool -+sepgsql_proc_hint_inlined(HeapTuple protup) -+{ -+ security_context_t newcon; -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return true; -+ -+ if (!sepgsql_proc_common(HeapTupleGetOid(protup), -+ SEPG_DB_PROCEDURE__EXECUTE, false)) -+ return false; -+ /* -+ * If the security context of client is unchange -+ * before or after invocation of the functions, -+ * it is not a trusted procedure, so it can be -+ * inlined due to performance purpose. -+ */ -+ sid = sepgsqlGetTupleSecid(ProcedureRelationId, protup, NULL); -+ -+ newcon = sepgsqlClientCreateLabel(sid, SEPG_CLASS_PROCESS); -+ -+ if (strcmp(sepgsqlGetClientLabel(), newcon) == 0) -+ return true; -+ -+ return false; -+} -+ -+bool -+sepgsql_proc_entrypoint(HeapTuple protup) -+{ -+ security_context_t newcon; -+ sepgsql_sid_t proSid; -+ -+ if (!sepgsqlIsEnabled()) -+ return false; -+ -+ proSid = sepgsqlGetTupleSecid(ProcedureRelationId, -+ protup, NULL); -+ -+ newcon = sepgsqlClientCreateLabel(proSid, SEPG_CLASS_PROCESS); -+ -+ /* Do nothing, if it is not a trusted procedure */ -+ if (strcmp(newcon, sepgsqlGetClientLabel()) == 0) -+ return false; -+ -+ /* check db_procedure:{entrypoint} */ -+ sepgsqlClientHasPerms(proSid, -+ SEPG_CLASS_DB_PROCEDURE, -+ SEPG_DB_PROCEDURE__ENTRYPOINT, -+ NULL, true); -+ -+ /* check process:{transition} */ -+ sepgsqlComputePerms(sepgsqlGetClientLabel(), -+ newcon, -+ SEPG_CLASS_PROCESS, -+ SEPG_PROCESS__TRANSITION, -+ NULL, true); -+ -+ return true; -+} -+ -+char * -+sepgsql_proc_trusted(HeapTuple protup, MemoryContext mcxt) -+{ -+ MemoryContext oldcxt; -+ security_context_t newcon; -+ sepgsql_sid_t proSid; -+ -+ if (!sepgsqlIsEnabled()) -+ return NULL; -+ -+ proSid = sepgsqlGetTupleSecid(ProcedureRelationId, protup, NULL); -+ -+ oldcxt = MemoryContextSwitchTo(mcxt); -+ -+ newcon = sepgsqlClientCreateLabel(proSid, SEPG_CLASS_PROCESS); -+ -+ MemoryContextSwitchTo(oldcxt); -+ -+ return newcon; -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_cast related security hooks -+ * -+ * ------------------------------------------------------------ */ -+Oid -+sepgsql_cast_create(Oid sourceTypOid, Oid targetTypOid, Oid funcOid) -+{ -+ sepgsql_sid_t sid; -+ char audit_buffer[2*NAMEDATALEN+10]; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(CastRelationId); -+ -+ snprintf(audit_buffer, sizeof(audit_buffer), "%s::%s", -+ format_type_be(sourceTypOid), format_type_be(targetTypOid)); -+ -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ audit_buffer, true); -+ -+ if (OidIsValid(funcOid)) -+ sepgsql_proc_common(funcOid, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_cast_drop(Oid castOid) -+{ -+ Form_pg_cast castForm; -+ Relation rel; -+ HeapTuple tuple; -+ ScanKeyData skey; -+ SysScanDesc scan; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ char audit_buffer[2*NAMEDATALEN+10]; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ rel = heap_open(CastRelationId, AccessShareLock); -+ -+ ScanKeyInit(&skey, -+ ObjectIdAttributeNumber, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(castOid)); -+ -+ scan = systable_beginscan(rel, CastOidIndexId, true, -+ SnapshotNow, 1, &skey); -+ tuple = systable_getnext(scan); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "could not find tuple for cast: %u", castOid); -+ -+ castForm = (Form_pg_cast) GETSTRUCT(tuple); -+ -+ snprintf(audit_buffer, sizeof(audit_buffer), "%s::%s", -+ format_type_be(castForm->castsource), -+ format_type_be(castForm->casttarget)); -+ -+ sid = sepgsqlGetTupleSecid(CastRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ audit_buffer, true); -+ -+ systable_endscan(scan); -+ -+ heap_close(rel, AccessShareLock); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_conversion related security hooks -+ * -+ * ------------------------------------------------------------ */ -+Oid -+sepgsql_conversion_create(const char *convName, Oid nspOid, Oid procOid) -+{ -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(ConversionRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ convName, true); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_procedure:{install} */ -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_conversion_alter(Oid convOid, const char *newName) -+{ -+ Form_pg_conversion convForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(CONVOID, -+ ObjectIdGetDatum(convOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for conversion %u", convOid); -+ convForm = (Form_pg_conversion) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(ConversionRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(convForm->conname), true); -+ if (newName) -+ { -+ Oid nspOid = convForm->connamespace; -+ -+ sepgsql_schema_common(nspOid, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ ReleaseSysCache(tuple); -+} -+ -+void -+sepgsql_conversion_drop(Oid convOid) -+{ -+ Form_pg_conversion convForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(CONVOID, -+ ObjectIdGetDatum(convOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for conversion %u", convOid); -+ convForm = (Form_pg_conversion) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(ConversionRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(convForm->conname), true); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(convForm->connamespace, -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ ReleaseSysCache(tuple); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_foreign_data_wrapper related security hooks -+ * -+ * ------------------------------------------------------------ */ -+static bool -+sepgsql_fdw_common(Oid fdwOid, uint32 required, bool abort) -+{ -+ Form_pg_foreign_data_wrapper fdwForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ bool rc; -+ -+ tuple = SearchSysCache(FOREIGNDATAWRAPPEROID, -+ ObjectIdGetDatum(fdwOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for FDW: %u", fdwOid); -+ fdwForm = (Form_pg_foreign_data_wrapper) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(ForeignDataWrapperRelationId, tuple, &tclass); -+ rc = sepgsqlClientHasPerms(sid, tclass, required, -+ NameStr(fdwForm->fdwname), abort); -+ ReleaseSysCache(tuple); -+ -+ return rc; -+} -+ -+Oid -+sepgsql_fdw_create(const char *fdwName, Oid fdwValidator) -+{ -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(ForeignDataWrapperRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ fdwName, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(fdwValidator)) -+ sepgsql_proc_common(fdwValidator, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_fdw_alter(Oid fdwOid, Oid newValidator) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(newValidator)) -+ sepgsql_proc_common(newValidator, SEPG_DB_PROCEDURE__INSTALL, true); -+} -+ -+void -+sepgsql_fdw_drop(Oid fdwOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__DELETE, true); -+} -+ -+void -+sepgsql_fdw_grant(Oid fdwOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_fdw_common(fdwOid, SEPG_DB_TUPLE__UPDATE, true); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_foreign_server related security hooks -+ * -+ * ------------------------------------------------------------ */ -+static bool -+sepgsql_foreign_server_common(Oid fsrvOid, uint32 required, bool abort) -+{ -+ Form_pg_foreign_server fsrvForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ bool rc; -+ -+ tuple = SearchSysCache(FOREIGNSERVEROID, -+ ObjectIdGetDatum(fsrvOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for foreign server %u", fsrvOid); -+ fsrvForm = (Form_pg_foreign_server) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(ForeignServerRelationId, tuple, &tclass); -+ rc = sepgsqlClientHasPerms(sid, tclass, required, -+ NameStr(fsrvForm->srvname), abort); -+ ReleaseSysCache(tuple); -+ -+ return rc; -+} -+ -+Oid -+sepgsql_foreign_server_create(const char *fsrvName) -+{ -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(ForeignServerRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ fsrvName, true); -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_foreign_server_alter(Oid fsrvOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__UPDATE, true); -+} -+ -+void -+sepgsql_foreign_server_drop(Oid fsrvOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__DELETE, true); -+} -+ -+void -+sepgsql_foreign_server_grant(Oid fsrvOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_foreign_server_common(fsrvOid, SEPG_DB_TUPLE__UPDATE, true); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_language related security hooks -+ * -+ * ------------------------------------------------------------ */ -+static bool -+sepgsql_language_common(Oid langOid, uint32 required, bool abort) -+{ -+ Form_pg_language langForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ bool rc; -+ -+ tuple = SearchSysCache(LANGOID, -+ ObjectIdGetDatum(langOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for language %u", langOid); -+ langForm = (Form_pg_language) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(LanguageRelationId, tuple, &tclass); -+ rc = sepgsqlClientHasPerms(sid, tclass, required, -+ NameStr(langForm->lanname), abort); -+ -+ ReleaseSysCache(tuple); -+ -+ return rc; -+} -+ -+Oid -+sepgsql_language_create(const char *langName, Oid handlerOid, Oid validatorOid) -+{ -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(LanguageRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, langName, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(handlerOid)) -+ sepgsql_proc_common(handlerOid, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(validatorOid)) -+ sepgsql_proc_common(validatorOid, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_language_alter(Oid langOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_language_common(langOid, SEPG_DB_TUPLE__UPDATE, true); -+} -+ -+void -+sepgsql_language_drop(Oid langOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_language_common(langOid, SEPG_DB_TUPLE__DELETE, true); -+} -+ -+void -+sepgsql_language_grant(Oid langOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_language_common(langOid, SEPG_DB_TUPLE__UPDATE, true); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_largeobject related security hooks -+ * (need to backport v8.5 feature) -+ * ------------------------------------------------------------ */ -+static bool -+sepgsql_largeobject_common(Oid loid, uint32 required, Snapshot snapshot) -+{ -+ Relation rel; -+ ScanKeyData skey; -+ SysScanDesc scan; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ char auname[64]; -+ bool rc; -+ -+ rel = heap_open(LargeObjectMetadataRelationId, AccessShareLock); -+ -+ ScanKeyInit(&skey, -+ ObjectIdAttributeNumber, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(loid)); -+ -+ scan = systable_beginscan(rel, LargeObjectMetadataOidIndexId, -+ true, snapshot, 1, &skey); -+ -+ tuple = systable_getnext(scan); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "largeobject %u lookup failed", loid); -+ -+ snprintf(auname, sizeof(auname), "blob:%u", loid); -+ -+ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), tuple, &tclass); -+ -+ rc = sepgsqlClientHasPerms(sid, tclass, required, auname, true); -+ -+ systable_endscan(scan); -+ -+ heap_close(rel, AccessShareLock); -+ -+ return rc; -+} -+ -+Oid -+sepgsql_largeobject_create(Oid loid, Value *secLabel) -+{ -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ if (!secLabel) -+ sid = sepgsqlGetDefaultBlobSecid(MyDatabaseId); -+ else -+ { -+ sid.relid = LargeObjectMetadataRelationId; -+ sid.secid = securityTransSecLabelIn(sid.relid, strVal(secLabel)); -+ } -+ -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_BLOB, -+ SEPG_DB_BLOB__CREATE, -+ NULL, true); -+ return sid.secid; -+} -+ -+void -+sepgsql_largeobject_alter(Oid loid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_largeobject_common(loid, SEPG_DB_BLOB__SETATTR, SnapshotNow); -+} -+ -+void -+sepgsql_largeobject_drop(Oid loid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_largeobject_common(loid, SEPG_DB_BLOB__DROP, SnapshotNow); -+} -+ -+void -+sepgsql_largeobject_read(Oid loid, Snapshot snapshot) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_largeobject_common(loid, SEPG_DB_BLOB__READ, snapshot); -+} -+ -+void -+sepgsql_largeobject_write(Oid loid, Snapshot snapshot) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_largeobject_common(loid, SEPG_DB_BLOB__WRITE, snapshot); -+} -+ -+void -+sepgsql_largeobject_export(Oid loid, const char *filename) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_largeobject_common(loid, -+ SEPG_DB_BLOB__READ | -+ SEPG_DB_BLOB__EXPORT, SnapshotNow); -+ -+ sepgsql_file_write(filename); -+} -+ -+Oid -+sepgsql_largeobject_import(Oid loid, const char *filename) -+{ -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultBlobSecid(MyDatabaseId); -+ -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_BLOB, -+ SEPG_DB_BLOB__CREATE | -+ SEPG_DB_BLOB__WRITE | -+ SEPG_DB_BLOB__IMPORT, -+ NULL, true); -+ -+ sepgsql_file_read(filename); -+ -+ return sid.secid; -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_opclass related security hooks -+ * -+ * ------------------------------------------------------------ */ -+Oid -+sepgsql_opclass_create(const char *opcName, Oid nspOid) -+{ -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(OperatorClassRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ opcName, true); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_opclass_alter(Oid opcOid, const char *newName) -+{ -+ Form_pg_opclass opcForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(CLAOID, -+ ObjectIdGetDatum(opcOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for opclass %u", opcOid); -+ opcForm = (Form_pg_opclass) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(OperatorClassRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(opcForm->opcname), true); -+ -+ /* db_schema:{add_name remove_name} */ -+ if (newName) -+ { -+ sepgsql_schema_common(opcForm->opcnamespace, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ ReleaseSysCache(tuple); -+} -+ -+void -+sepgsql_opclass_drop(Oid opcOid) -+{ -+ Form_pg_opclass opcForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(CLAOID, -+ ObjectIdGetDatum(opcOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for opclass %u", opcOid); -+ opcForm = (Form_pg_opclass) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(OperatorClassRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(opcForm->opcname), true); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(opcForm->opcnamespace, -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ ReleaseSysCache(tuple); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_opfamily related security hooks -+ * -+ * ------------------------------------------------------------ */ -+Oid -+sepgsql_opfamily_create(const char *opfName, Oid nspOid) -+{ -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(OperatorFamilyRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ opfName, true); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_opfamily_alter(Oid opfOid, const char *newName) -+{ -+ Form_pg_opfamily opfForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(OPFAMILYOID, -+ ObjectIdGetDatum(opfOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for operator family: %u", opfOid); -+ opfForm = (Form_pg_opfamily) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(opfForm->opfname), true); -+ if (newName) -+ { -+ sepgsql_schema_common(opfForm->opfnamespace, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ ReleaseSysCache(tuple); -+} -+ -+void -+sepgsql_opfamily_drop(Oid opfOid) -+{ -+ Form_pg_opfamily opfForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(OPFAMILYOID, -+ ObjectIdGetDatum(opfOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for operator family: %u", opfOid); -+ opfForm = (Form_pg_opfamily) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ NameStr(opfForm->opfname), true); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(opfForm->opfnamespace, -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ ReleaseSysCache(tuple); -+} -+ -+void -+sepgsql_opfamily_add_operator(Oid opfOid, Oid operOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* currently, do nothing here */ -+} -+ -+void -+sepgsql_opfamily_add_procedure(Oid opfOid, Oid procOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* -+ * Note that db_tuple:{setattr} is already checked at the -+ * earlier phase, so db_procedure:{install} is only needed. -+ */ -+ if (OidIsValid(procOid)) -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_operator related security hooks -+ * -+ * ------------------------------------------------------------ */ -+static bool -+sepgsql_operator_common(Oid oprOid, uint32 required, bool abort) -+{ -+ Form_pg_operator oprForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ bool rc; -+ -+ tuple = SearchSysCache(OPEROID, -+ ObjectIdGetDatum(oprOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for operator: %u", oprOid); -+ oprForm = (Form_pg_operator) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(OperatorRelationId, tuple, &tclass); -+ rc = sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ NameStr(oprForm->oprname), abort); -+ -+ ReleaseSysCache(tuple); -+ -+ return rc; -+} -+ -+Oid -+sepgsql_operator_create(const char *oprName, Oid oprOid, Oid nspOid, -+ Oid codeFn, Oid restFn, Oid joinFn) -+{ -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint32 required; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ if (!OidIsValid(oprOid)) -+ { -+ sid = sepgsqlGetDefaultTupleSecid(OperatorRelationId); -+ required = SEPG_DB_TUPLE__INSERT; -+ } -+ else -+ { -+ tuple = SearchSysCache(OPEROID, -+ ObjectIdGetDatum(oprOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for operator %u", oprOid); -+ -+ sid = sepgsqlGetTupleSecid(OperatorRelationId, tuple, NULL); -+ -+ ReleaseSysCache(tuple); -+ -+ required = SEPG_DB_TUPLE__UPDATE; -+ } -+ -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ required, oprName, true); -+ -+ /* db_schema:{add_name} checks */ -+ if (!OidIsValid(oprOid)) -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_procedure:{install} checks */ -+ if (OidIsValid(codeFn)) -+ sepgsql_proc_common(codeFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(restFn)) -+ sepgsql_proc_common(restFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(joinFn)) -+ sepgsql_proc_common(joinFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_operator_alter(Oid oprOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_operator_common(oprOid, SEPG_DB_TUPLE__UPDATE, true); -+} -+ -+void -+sepgsql_operator_drop(Oid oprOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_operator_common(oprOid, SEPG_DB_TUPLE__DELETE, true); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_rewrite related security hooks -+ * -+ * ------------------------------------------------------------ */ -+void -+sepgsql_rule_create(Oid relOid, const char *ruleName) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+} -+ -+void -+sepgsql_rule_drop(Oid relOid, const char *ruleName) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_trigger related security hooks -+ * -+ * ------------------------------------------------------------ */ -+void -+sepgsql_trigger_create(Oid relOid, const char *trigName, Oid procOid) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* db_table:{setattr} */ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+ -+ /* db_procedure:{install} */ -+ sepgsql_proc_common(procOid, SEPG_DB_PROCEDURE__INSTALL, true); -+} -+ -+void -+sepgsql_trigger_alter(Oid relOid, const char *trigName) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* db_table:{setattr} */ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+} -+ -+void -+sepgsql_trigger_drop(Oid relOid, const char *trigName) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* db_table:{setattr} */ -+ sepgsql_relation_common(relOid, SEPG_DB_TABLE__SETATTR, true); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_type related security hooks -+ * -+ * ------------------------------------------------------------ */ -+Oid -+sepgsql_ts_config_create(const char *cfgName, Oid nspOid) -+{ -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(TSConfigRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ cfgName, true); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_ts_config_alter(Oid cfgOid, const char *newName) -+{ -+ Form_pg_ts_config cfgForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TSCONFIGOID, -+ ObjectIdGetDatum(cfgOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search dictionary %u", cfgOid); -+ cfgForm = (Form_pg_ts_config) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TSConfigRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(cfgForm->cfgname), true); -+ if (newName) -+ { -+ sepgsql_schema_common(cfgForm->cfgnamespace, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ ReleaseSysCache(tuple); -+} -+ -+void -+sepgsql_ts_config_drop(Oid cfgOid) -+{ -+ Form_pg_ts_config cfgForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TSCONFIGOID, -+ ObjectIdGetDatum(cfgOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search dictionary %u", cfgOid); -+ cfgForm = (Form_pg_ts_config) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TSConfigRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ NameStr(cfgForm->cfgname), true); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(cfgForm->cfgnamespace, -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ ReleaseSysCache(tuple); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_type related security hooks -+ * -+ * ------------------------------------------------------------ */ -+Oid -+sepgsql_ts_dict_create(const char *dictName, Oid nspOid) -+{ -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(TSDictionaryRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ dictName, true); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_ts_dict_alter(Oid dictOid, const char *newName) -+{ -+ Form_pg_ts_dict dictForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TSDICTOID, -+ ObjectIdGetDatum(dictOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search dictionary %u", dictOid); -+ dictForm = (Form_pg_ts_dict) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TSDictionaryRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(dictForm->dictname), true); -+ -+ /* db_schema:{add_name remove_name} */ -+ if (newName) -+ { -+ sepgsql_schema_common(dictForm->dictnamespace, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ ReleaseSysCache(tuple); -+} -+ -+void -+sepgsql_ts_dict_drop(Oid dictOid) -+{ -+ Form_pg_ts_dict dictForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TSDICTOID, -+ ObjectIdGetDatum(dictOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search dictionary %u", dictOid); -+ dictForm = (Form_pg_ts_dict) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TSDictionaryRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ NameStr(dictForm->dictname), true); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(dictForm->dictnamespace, -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ ReleaseSysCache(tuple); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_type related security hooks -+ * -+ * ------------------------------------------------------------ */ -+Oid -+sepgsql_ts_parser_create(const char *prsName, Oid nspOid, -+ Oid startFn, Oid tokenFn, Oid sendFn, -+ Oid headlineFn, Oid lextypeFn) -+{ -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(TSParserRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ prsName, true); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(startFn)) -+ sepgsql_proc_common(startFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(tokenFn)) -+ sepgsql_proc_common(tokenFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(sendFn)) -+ sepgsql_proc_common(sendFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(headlineFn)) -+ sepgsql_proc_common(headlineFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(lextypeFn)) -+ sepgsql_proc_common(lextypeFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_ts_parser_alter(Oid prsOid, const char *newName) -+{ -+ Form_pg_ts_parser prsForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TSPARSEROID, -+ ObjectIdGetDatum(prsOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search parser %u", prsOid); -+ -+ prsForm = (Form_pg_ts_parser) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TSParserRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(prsForm->prsname), true); -+ if (newName) -+ { -+ sepgsql_schema_common(prsForm->prsnamespace, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ ReleaseSysCache(tuple); -+} -+ -+void -+sepgsql_ts_parser_drop(Oid prsOid) -+{ -+ Form_pg_ts_parser prsForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TSPARSEROID, -+ ObjectIdGetDatum(prsOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search parser %u", prsOid); -+ -+ prsForm = (Form_pg_ts_parser) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TSParserRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ NameStr(prsForm->prsname), true); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(prsForm->prsnamespace, -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ ReleaseSysCache(tuple); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_type related security hooks -+ * -+ * ------------------------------------------------------------ */ -+Oid -+sepgsql_ts_template_create(const char *tmplName, Oid nspOid, -+ Oid initFn, Oid lexizeFn) -+{ -+ sepgsql_sid_t sid; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ sid = sepgsqlGetDefaultTupleSecid(TSTemplateRelationId); -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ SEPG_DB_TUPLE__INSERT, -+ tmplName, true); -+ -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(initFn)) -+ sepgsql_proc_common(initFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(lexizeFn)) -+ sepgsql_proc_common(lexizeFn, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_ts_template_alter(Oid tmplOid, const char *newName) -+{ -+ Form_pg_ts_template tmplForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TSTEMPLATEOID, -+ ObjectIdGetDatum(tmplOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search template %u", tmplOid); -+ tmplForm = (Form_pg_ts_template) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TSTemplateRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(tmplForm->tmplname), true); -+ if (newName) -+ { -+ sepgsql_schema_common(tmplForm->tmplnamespace, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ ReleaseSysCache(tuple); -+} -+ -+void -+sepgsql_ts_template_drop(Oid tmplOid) -+{ -+ Form_pg_ts_template tmplForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TSTEMPLATEOID, -+ ObjectIdGetDatum(tmplOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for text search template %u", tmplOid); -+ tmplForm = (Form_pg_ts_template) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TSTemplateRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ NameStr(tmplForm->tmplname), true); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(tmplForm->tmplnamespace, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ ReleaseSysCache(tuple); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Pg_type related security hooks -+ * -+ * ------------------------------------------------------------ */ -+Oid -+sepgsql_type_create(const char *typName, HeapTuple oldTup, Oid nspOid, -+ Oid inputProc, Oid outputProc, Oid recvProc, Oid sendProc, -+ Oid modinProc, Oid modoutProc, Oid analyzeProc) -+{ -+ sepgsql_sid_t sid; -+ uint32 required; -+ -+ if (!sepgsqlIsEnabled()) -+ return InvalidOid; -+ -+ if (!HeapTupleIsValid(oldTup)) -+ { -+ sid = sepgsqlGetDefaultTupleSecid(TypeRelationId); -+ required = SEPG_DB_TUPLE__INSERT; -+ } -+ else -+ { -+ sid = sepgsqlGetTupleSecid(TypeRelationId, oldTup, NULL); -+ required = SEPG_DB_TUPLE__UPDATE; -+ } -+ sepgsqlClientHasPerms(sid, SEPG_CLASS_DB_TUPLE, -+ required, typName, true); -+ /* db_schema:{add_name} */ -+ sepgsql_schema_common(nspOid, SEPG_DB_SCHEMA__ADD_NAME, true); -+ -+ /* db_procedure:{install} */ -+ if (OidIsValid(inputProc)) -+ sepgsql_proc_common(inputProc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(outputProc)) -+ sepgsql_proc_common(outputProc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(recvProc)) -+ sepgsql_proc_common(recvProc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(sendProc)) -+ sepgsql_proc_common(sendProc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(modinProc)) -+ sepgsql_proc_common(modinProc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(modoutProc)) -+ sepgsql_proc_common(modoutProc, SEPG_DB_PROCEDURE__INSTALL, true); -+ if (OidIsValid(analyzeProc)) -+ sepgsql_proc_common(analyzeProc, SEPG_DB_PROCEDURE__INSTALL, true); -+ -+ return sid.secid; -+} -+ -+void -+sepgsql_type_alter(Oid typOid, const char *newName, Oid newNsp) -+{ -+ Form_pg_type typForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TYPEOID, -+ ObjectIdGetDatum(typOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for type: %u", typOid); -+ typForm = (Form_pg_type) GETSTRUCT(tuple); -+ -+ sid = sepgsqlGetTupleSecid(TypeRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__UPDATE, -+ NameStr(typForm->typname), true); -+ -+ if (newName || OidIsValid(newNsp)) -+ { -+ Oid oldNsp = typForm->typnamespace; -+ -+ if (!OidIsValid(newNsp)) -+ { -+ sepgsql_schema_common(oldNsp, -+ SEPG_DB_SCHEMA__ADD_NAME | -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ } -+ else -+ { -+ sepgsql_schema_common(oldNsp, SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ sepgsql_schema_common(newNsp, SEPG_DB_SCHEMA__ADD_NAME, true); -+ } -+ } -+ ReleaseSysCache(tuple); -+} -+ -+void -+sepgsql_type_drop(Oid typOid) -+{ -+ Form_pg_type typForm; -+ HeapTuple tuple; -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ tuple = SearchSysCache(TYPEOID, -+ ObjectIdGetDatum(typOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for type: %u", typOid); -+ typForm = (Form_pg_type) GETSTRUCT(tuple); -+ -+ if (typForm->typtype == TYPTYPE_COMPOSITE || -+ (typForm->typtype == TYPTYPE_BASE && OidIsValid(typForm->typarray))) -+ { -+ /* -+ * No need to check for composite type and implicitly -+ * declared array type here. -+ */ -+ ReleaseSysCache(tuple); -+ return; -+ } -+ -+ sid = sepgsqlGetTupleSecid(TypeRelationId, tuple, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__DELETE, -+ NameStr(typForm->typname), true); -+ -+ /* db_schema:{remove_name} */ -+ sepgsql_schema_common(typForm->typnamespace, -+ SEPG_DB_SCHEMA__REMOVE_NAME, true); -+ -+ ReleaseSysCache(tuple); -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Misc system object related security hooks -+ * -+ * ------------------------------------------------------------ */ -+ -+void -+sepgsql_sysobj_drop(const ObjectAddress *object) -+{ -+ switch (object->classId) -+ { -+ case RelationRelationId: -+ if (object->objectSubId == 0) -+ sepgsql_relation_drop(object->objectId); -+ else -+ sepgsql_attribute_drop(object->objectId, -+ object->objectSubId); -+ break; -+ -+ case ProcedureRelationId: -+ sepgsql_proc_drop(object->objectId); -+ break; -+ -+ case TypeRelationId: -+ sepgsql_type_drop(object->objectId); -+ break; -+ -+ case CastRelationId: -+ sepgsql_cast_drop(object->objectId); -+ break; -+ -+ case ConversionRelationId: -+ sepgsql_conversion_drop(object->objectId); -+ break; -+ -+ case LanguageRelationId: -+ sepgsql_language_drop(object->objectId); -+ break; -+ -+ case OperatorRelationId: -+ sepgsql_operator_drop(object->objectId); -+ break; -+ -+ case OperatorClassRelationId: -+ sepgsql_opclass_drop(object->objectId); -+ break; -+ -+ case OperatorFamilyRelationId: -+ sepgsql_opfamily_drop(object->objectId); -+ break; -+ -+ case NamespaceRelationId: -+ sepgsql_schema_drop(object->objectId); -+ break; -+ -+ case TSParserRelationId: -+ sepgsql_ts_parser_drop(object->objectId); -+ break; -+ -+ case TSDictionaryRelationId: -+ sepgsql_ts_dict_drop(object->objectId); -+ break; -+ -+ case TSTemplateRelationId: -+ sepgsql_ts_template_drop(object->objectId); -+ break; -+ -+ case TSConfigRelationId: -+ sepgsql_ts_config_drop(object->objectId); -+ break; -+ -+ case AuthIdRelationId: -+ break; -+ -+ case DatabaseRelationId: -+ sepgsql_database_drop(object->objectId); -+ break; -+ -+ case TableSpaceRelationId: -+ break; -+ -+ case ForeignDataWrapperRelationId: -+ sepgsql_fdw_drop(object->objectId); -+ break; -+ -+ case ForeignServerRelationId: -+ sepgsql_foreign_server_drop(object->objectId); -+ break; -+ -+ case UserMappingRelationId: -+ break; -+ -+ default: -+ /* do nothing */ -+ break; -+ } -+} -+ -+/* ------------------------------------------------------------ * -+ * -+ * Filesystem object related security hooks -+ * -+ * ------------------------------------------------------------ */ -+static char * -+sepgsql_getfilecon(const char *path) -+{ -+ security_context_t context; -+ char *result; -+ -+ if (getfilecon_raw(path, &context) < 0) -+ ereport(ERROR, -+ (errcode_for_file_access(), -+ errmsg("could not get context of \"%s\": %m", path))); -+ -+ PG_TRY(); -+ { -+ result = pstrdup(context); -+ } -+ PG_CATCH(); -+ { -+ freecon(context); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(context); -+ -+ return result; -+} -+ -+static void -+sepgsql_file_common(const char *filename, uint32 required, bool may_create) -+{ -+ struct stat stbuf; -+ -+ if (stat(filename, &stbuf) == 0) -+ { -+ uint16 tclass; -+ -+ /* -+ * Get file object class -+ */ -+ if (S_ISDIR(stbuf.st_mode)) -+ tclass = SEPG_CLASS_DIR; -+ else if (S_ISCHR(stbuf.st_mode)) -+ tclass = SEPG_CLASS_CHR_FILE; -+ else if (S_ISBLK(stbuf.st_mode)) -+ tclass = SEPG_CLASS_BLK_FILE; -+ else if (S_ISFIFO(stbuf.st_mode)) -+ tclass = SEPG_CLASS_FIFO_FILE; -+ else if (S_ISLNK(stbuf.st_mode)) -+ tclass = SEPG_CLASS_LNK_FILE; -+ else if (S_ISSOCK(stbuf.st_mode)) -+ tclass = SEPG_CLASS_SOCK_FILE; -+ else -+ tclass = SEPG_CLASS_FILE; -+ -+ /* -+ * Check permission (no cached operation) -+ */ -+ sepgsqlComputePerms(sepgsqlGetClientLabel(), -+ sepgsql_getfilecon(filename), -+ tclass, required, -+ filename, true); -+ } -+ else if (may_create) -+ { -+ /* -+ * If the required file is not found, we check permission to -+ * create a new file and required permission on the new file. -+ */ -+ security_context_t dcontext; -+ security_context_t ncontext; -+ char *copy = pstrdup(filename); -+ -+ /* -+ * Compute a security context for the new file -+ */ -+ dcontext = sepgsql_getfilecon(dirname(copy)); -+ -+ ncontext = sepgsqlComputeCreate(sepgsqlGetServerLabel(), -+ dcontext, -+ SEPG_CLASS_FILE); -+ /* -+ * Check permission (no cached operation) -+ */ -+ required |= SEPG_FILE__CREATE; -+ -+ sepgsqlComputePerms(sepgsqlGetClientLabel(), -+ sepgsql_getfilecon(filename), -+ SEPG_CLASS_FILE, -+ required, filename, true); -+ } -+ else -+ { -+ ereport(ERROR, -+ (errcode_for_file_access(), -+ errmsg("could not stat file \"%s\": %m", filename))); -+ } -+} -+ -+void -+sepgsql_file_stat(const char *filename) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_file_common(filename, SEPG_FILE__GETATTR, false); -+} -+ -+void -+sepgsql_file_read(const char *filename) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_file_common(filename, SEPG_FILE__READ, false); -+} -+ -+void -+sepgsql_file_write(const char *filename) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ sepgsql_file_common(filename, SEPG_FILE__WRITE, true); -+} -+ -+/* -+ * TODO: add check for pg_ls_dir() -+ */ -diff --git a/src/backend/security/sepgsql/checker.c b/src/backend/security/sepgsql/checker.c -new file mode 100644 -index 0000000..9e573c3 ---- /dev/null -+++ b/src/backend/security/sepgsql/checker.c -@@ -0,0 +1,432 @@ -+/* -+ * src/backend/security/sepgsql/checker.c -+ * walks on given Query tree and applies checks -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+#include "postgres.h" -+ -+#include "access/sysattr.h" -+#include "catalog/catalog.h" -+#include "catalog/pg_largeobject.h" -+#include "catalog/pg_security.h" -+#include "miscadmin.h" -+#include "security/sepgsql.h" -+#include "storage/bufmgr.h" -+#include "utils/lsyscache.h" -+#include "utils/syscache.h" -+#include "utils/tqual.h" -+ -+/* -+ * fixupWholeRowReference -+ */ -+static Bitmapset * -+fixupWholeRowReference(Oid relid, int nattrs, Bitmapset *columns) -+{ -+ Bitmapset *result; -+ AttrNumber attno; -+ -+ attno = InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber; -+ -+ if (!bms_is_member(attno, columns)) -+ return columns; /* no need to fixup */ -+ -+ result = bms_copy(columns); -+ result = bms_del_member(result, attno); -+ -+ for (attno=1; attno <= nattrs; attno++) -+ { -+ Form_pg_attribute attform; -+ HeapTuple atttup; -+ -+ atttup = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(relid), -+ Int16GetDatum(attno), -+ 0, 0); -+ if (!HeapTupleIsValid(atttup)) -+ continue; -+ -+ attform = (Form_pg_attribute) GETSTRUCT(atttup); -+ if (!attform->attisdropped) -+ { -+ int cindex = attno - FirstLowInvalidHeapAttributeNumber; -+ result = bms_add_member(result, cindex); -+ } -+ ReleaseSysCache(atttup); -+ } -+ -+ return result; -+} -+ -+/* -+ * checkTabelColumnPerms -+ * This functions applies table/column level permissions for -+ * all the appeared ones in user's query, and raises an error -+ * if violated. -+ * It also applies a few hardwired policy which prevent to -+ * modified some of system catalogs. -+ */ -+static void -+checkTabelColumnPerms(Oid relid, Bitmapset *selected, Bitmapset *modified, -+ access_vector_t required) -+{ -+ Bitmapset *columns; -+ Bitmapset *selected_ex; -+ Bitmapset *modified_ex; -+ Form_pg_class relForm; -+ HeapTuple reltup; -+ sepgsql_sid_t relsid; -+ sepgsql_sid_t attsid; -+ AttrNumber attno; -+ uint16 tclass; -+ -+ /* -+ * Hardwired Policy: -+ * SE-PostgreSQL enforces that clients cannot modify system -+ * catalogs and access toast values using DML statements, -+ * except initial setting up phase. -+ */ -+ if (sepgsqlGetEnforce()) -+ { -+ if (IsSystemNamespace(get_rel_namespace(relid)) && -+ (required & (SEPG_DB_TABLE__UPDATE | -+ SEPG_DB_TABLE__INSERT | -+ SEPG_DB_TABLE__DELETE)) != 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SE-PostgreSQL prevents to modidy \"%s\"", -+ get_rel_name(relid)))); -+ if (get_rel_relkind(relid) == RELKIND_TOASTVALUE) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SE-PostgreSQL prevents to access \"%s\"", -+ get_rel_name(relid)))); -+ } -+ -+ /* -+ * Check db_table:{...} or db_sequence permissions -+ */ -+ reltup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(relid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(reltup)) -+ elog(ERROR, "SELinux: cache lookup failed for relation %u", relid); -+ -+ relForm = (Form_pg_class) GETSTRUCT(reltup); -+ -+ relsid = sepgsqlGetTupleSecid(RelationRelationId, reltup, &tclass); -+ -+ if (tclass != SEPG_CLASS_DB_TABLE) -+ { -+ /* check db_sequence:{xxx} permission */ -+ if (tclass == SEPG_CLASS_DB_SEQUENCE) -+ { -+ if (required & SEPG_DB_TABLE__SELECT) -+ { -+ sepgsqlClientHasPerms(relsid, tclass, -+ SEPG_DB_SEQUENCE__GET_VALUE, -+ NameStr(relForm->relname), true); -+ } -+ } -+ ReleaseSysCache(reltup); -+ return; -+ } -+ sepgsqlClientHasPerms(relsid, tclass, required, -+ NameStr(relForm->relname), true); -+ -+ /* -+ * Check db_column:{...} permissions -+ */ -+ selected_ex = fixupWholeRowReference(relid, relForm->relnatts, selected); -+ modified_ex = fixupWholeRowReference(relid, relForm->relnatts, modified); -+ columns = bms_union(selected_ex, modified_ex); -+ -+ while ((attno = bms_first_member(columns)) >= 0) -+ { -+ Form_pg_attribute attForm; -+ HeapTuple atttup; -+ uint32 attperms = 0; -+ char auname[2 * NAMEDATALEN + 3]; -+ -+ if (bms_is_member(attno, selected_ex)) -+ attperms |= SEPG_DB_COLUMN__SELECT; -+ if (bms_is_member(attno, modified_ex)) -+ { -+ if (required & SEPG_DB_TABLE__UPDATE) -+ attperms |= SEPG_DB_COLUMN__UPDATE; -+ if (required & SEPG_DB_TABLE__INSERT) -+ attperms |= SEPG_DB_COLUMN__INSERT; -+ } -+ if (attperms == 0) -+ continue; -+ -+ /* remove the attribute number offset */ -+ attno += FirstLowInvalidHeapAttributeNumber; -+ atttup = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(relid), -+ Int16GetDatum(attno), -+ 0, 0); -+ if (!HeapTupleIsValid(atttup)) -+ elog(ERROR, "cache lookup failed for attribute %d of relation %u", -+ attno, relid); -+ -+ attForm = (Form_pg_attribute) GETSTRUCT(atttup); -+ if (attForm->attisdropped) -+ elog(ERROR, "attribute %d of relation %u does not exist", -+ attno, relid); -+ -+ snprintf(auname, sizeof(auname), "%s.%s", -+ NameStr(relForm->relname), -+ NameStr(attForm->attname)); -+ attsid = sepgsqlGetTupleSecid(AttributeRelationId, -+ atttup, &tclass); -+ sepgsqlClientHasPerms(attsid, tclass, attperms, auname, true); -+ -+ ReleaseSysCache(atttup); -+ } -+ -+ ReleaseSysCache(reltup); -+ -+ if (selected_ex != selected) -+ bms_free(selected_ex); -+ -+ if (modified_ex != modified) -+ bms_free(modified_ex); -+ -+ bms_free(columns); -+} -+ -+/* -+ * sepgsqlCheckQueryPerms -+ * It checks permission for all the required tables/columns on -+ * generic user queries. -+ */ -+void -+sepgsqlCheckRTEPerms(RangeTblEntry *rte) -+{ -+ access_vector_t required = 0; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ if (rte->rtekind != RTE_RELATION) -+ return; -+ -+ if (rte->requiredPerms & ACL_SELECT) -+ required |= SEPG_DB_TABLE__SELECT; -+ if (rte->requiredPerms & ACL_INSERT) -+ required |= SEPG_DB_TABLE__INSERT; -+ if (rte->requiredPerms & ACL_UPDATE) -+ { -+ /* -+ * ACL_SELECT_FOR_UPDATE is defined as an aliase of ACL_UPDATE, -+ * so we cannot determine whether the given relation is accessed -+ * with UPDATE statement or SELECT FOR SHARE/UPDATE immediately. -+ * UPDATE statements set a bit on rte->modifiedCols at least, -+ * so we use it as a watermark. -+ */ -+ if (!bms_is_empty(rte->modifiedCols)) -+ required |= SEPG_DB_TABLE__UPDATE; -+ else -+ required |= SEPG_DB_TABLE__LOCK; -+ } -+ if (rte->requiredPerms & ACL_DELETE) -+ required |= SEPG_DB_TABLE__DELETE; -+ -+ if (required == 0) -+ return; -+ -+ checkTabelColumnPerms(rte->relid, -+ rte->selectedCols, -+ rte->modifiedCols, -+ required); -+} -+ -+/* -+ * sepgsqlCheckCopyTable -+ * It checks permissions on COPY TO/FROM. -+ */ -+void -+sepgsqlCheckCopyTable(Relation rel, List *attnumlist, bool is_from) -+{ -+ Bitmapset *selected = NULL; -+ Bitmapset *modified = NULL; -+ ListCell *l; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* all checkes are done in sepgsqlCheckRTEPerms */ -+ if (!rel) -+ return; -+ -+ foreach (l, attnumlist) -+ { -+ AttrNumber attno = lfirst_int(l); -+ -+ attno -= FirstLowInvalidHeapAttributeNumber; -+ if (is_from) -+ modified = bms_add_member(modified, attno); -+ else -+ selected = bms_add_member(selected, attno); -+ } -+ -+ checkTabelColumnPerms(RelationGetRelid(rel), -+ selected, modified, -+ is_from ? SEPG_DB_TABLE__INSERT -+ : SEPG_DB_TABLE__SELECT); -+} -+ -+/* -+ * sepgsqlExecScan -+ * makes a decision on the given tuple. -+ */ -+bool -+sepgsqlExecScan(Relation rel, HeapTuple tuple, uint32 required, bool abort) -+{ -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled() || -+ !required || -+ RelationGetForm(rel)->relkind != RELKIND_RELATION || -+ RelationGetRelid(rel) == SecurityRelationId) -+ return true; -+ -+ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), tuple, &tclass); -+ /* -+ * Insert/Delete to an external attribute is equivalent to -+ * the set-attribute on the master -+ */ -+ if (sid.relid != RelationGetRelid(rel) && -+ (required & (SEPG_DB_TUPLE__INSERT | SEPG_DB_TUPLE__DELETE))) -+ { -+ required &= ~(SEPG_DB_TUPLE__INSERT | SEPG_DB_TUPLE__DELETE); -+ required |= SEPG_DB_TUPLE__UPDATE; -+ } -+ -+ return sepgsqlClientHasPerms(sid, tclass, required, NULL, abort); -+} -+ -+uint32 -+sepgsqlSetupTuplePerms(RangeTblEntry *rte) -+{ -+ AclMode perms = 0; -+ -+ if (!sepgsqlIsEnabled()) -+ return 0; -+ -+ if (rte->rtekind != RTE_RELATION) -+ return 0; -+ -+ if (rte->requiredPerms & ACL_SELECT) -+ perms |= SEPG_DB_TUPLE__SELECT; -+ if (rte->requiredPerms & ACL_UPDATE && !bms_is_empty(rte->modifiedCols)) -+ perms |= SEPG_DB_TUPLE__UPDATE; -+ if (rte->requiredPerms & ACL_DELETE) -+ perms |= SEPG_DB_TUPLE__DELETE; -+ -+ /* -+ * Special case in pg_largeobject -+ */ -+ if (rte->relid == LargeObjectRelationId && -+ bms_is_member(Anum_pg_largeobject_data -+ - FirstLowInvalidHeapAttributeNumber, -+ rte->selectedCols)) -+ perms |= SEPG_DB_BLOB__READ; -+ -+ return perms; -+} -+ -+/* -+ * sepgsqlHeapTupleInsert -+ * It assigns a default security label, if no explicit security labels -+ * were given. In addition, it also checks db_tuple:{insert} for the -+ * tuple newly inserted, when it invoked from user's query. -+ */ -+void -+sepgsqlHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal) -+{ -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* -+ * Assign a default security label, if necessary -+ */ -+ if (HeapTupleHasSecid(newtup) && -+ !OidIsValid(HeapTupleGetSecid(newtup))) -+ sepgsqlSetDefaultSecid(rel, newtup); -+ -+ /* -+ * It does not check permission for the new tuples -+ * inserted by system internal stuff using -+ * simple_heap_insert(); -+ */ -+ if (internal) -+ return; -+ -+ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), -+ newtup, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, SEPG_DB_TUPLE__INSERT, NULL, true); -+} -+ -+/* -+ * sepgsqlHeapTupleUpdate -+ * It checks db_tuple:{relabelfrom relabelto} permission on -+ * the user queries. (Please note that it does not check -+ * system internal stuff via simple_heap_update) -+ */ -+void -+sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup) -+{ -+ Oid secid; -+ HeapTupleData oldtup; -+ Buffer oldbuf; -+ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* -+ * heap_update() preserves the original security label -+ * of the given tuple, if no explicit security label -+ * is assigned on the newer version. -+ * In this case, db_tuple:{update} is already checked -+ * at the sepgsqlExecScan() hook, so we don't need to -+ * check anything more. -+ */ -+ secid = HeapTupleGetSecid(newtup); -+ if (!OidIsValid(secid)) -+ return; -+ -+ /* -+ * User gave an explicit security label -+ */ -+ ItemPointerCopy(otid, &oldtup.t_self); -+ if (!heap_fetch(rel, SnapshotAny, &oldtup, &oldbuf, false, NULL)) -+ elog(ERROR, "failed to fetch old version of the tuple"); -+ -+ if (secid != HeapTupleGetSecid(&oldtup)) -+ { -+ sepgsql_sid_t sid; -+ uint16 tclass; -+ -+ /* db_tuple:{relabelfrom} for older security context */ -+ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), -+ &oldtup, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__RELABELFROM, -+ NULL, true); -+ -+ /* db_tuple:{relabelto} for newer security label */ -+ sid = sepgsqlGetTupleSecid(RelationGetRelid(rel), -+ newtup, &tclass); -+ sepgsqlClientHasPerms(sid, tclass, -+ SEPG_DB_TUPLE__RELABELTO, -+ NULL, true); -+ } -+ ReleaseBuffer(oldbuf); -+} -diff --git a/src/backend/security/sepgsql/dummy.c b/src/backend/security/sepgsql/dummy.c -new file mode 100644 -index 0000000..6df24d3 ---- /dev/null -+++ b/src/backend/security/sepgsql/dummy.c -@@ -0,0 +1,79 @@ -+/* -+ * src/backend/utils/sepgsql/dummy.c -+ * A set of stubs when SE-PostgreSQL is not activated -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+#include "postgres.h" -+ -+#include "security/sepgsql.h" -+ -+static Datum -+unavailable_function(const char *fn_name) -+{ -+ ereport(ERROR, -+ (errcode(ERRCODE_SELINUX_ERROR), -+ errmsg("function \"%s\" is not available", fn_name))); -+ PG_RETURN_VOID(); -+} -+ -+Datum -+sepgsql_getcon(PG_FUNCTION_ARGS) -+{ -+ return unavailable_function(__FUNCTION__); -+} -+ -+Datum -+sepgsql_server_getcon(PG_FUNCTION_ARGS) -+{ -+ return unavailable_function(__FUNCTION__); -+} -+ -+Datum -+sepgsql_get_user(PG_FUNCTION_ARGS) -+{ -+ return unavailable_function(__FUNCTION__); -+} -+ -+Datum -+sepgsql_get_role(PG_FUNCTION_ARGS) -+{ -+ return unavailable_function(__FUNCTION__); -+} -+ -+Datum -+sepgsql_get_type(PG_FUNCTION_ARGS) -+{ -+ return unavailable_function(__FUNCTION__); -+} -+ -+Datum -+sepgsql_get_range(PG_FUNCTION_ARGS) -+{ -+ return unavailable_function(__FUNCTION__); -+} -+ -+Datum -+sepgsql_set_user(PG_FUNCTION_ARGS) -+{ -+ return unavailable_function(__FUNCTION__); -+} -+ -+Datum -+sepgsql_set_role(PG_FUNCTION_ARGS) -+{ -+ return unavailable_function(__FUNCTION__); -+} -+ -+Datum -+sepgsql_set_type(PG_FUNCTION_ARGS) -+{ -+ return unavailable_function(__FUNCTION__); -+} -+ -+Datum -+sepgsql_set_range(PG_FUNCTION_ARGS) -+{ -+ return unavailable_function(__FUNCTION__); -+} -diff --git a/src/backend/security/sepgsql/label.c b/src/backend/security/sepgsql/label.c -new file mode 100644 -index 0000000..e91f8c9 ---- /dev/null -+++ b/src/backend/security/sepgsql/label.c -@@ -0,0 +1,1213 @@ -+/* -+ * src/backend/security/sepgsql/label.c -+ * SE-PostgreSQL security label management -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+#include "postgres.h" -+ -+#include "access/sysattr.h" -+#include "access/xact.h" -+#include "catalog/catalog.h" -+#include "catalog/pg_constraint.h" -+#include "catalog/heap.h" -+#include "catalog/indexing.h" -+#include "catalog/namespace.h" -+#include "catalog/pg_aggregate.h" -+#include "catalog/pg_amop.h" -+#include "catalog/pg_amproc.h" -+#include "catalog/pg_attrdef.h" -+#include "catalog/pg_attribute.h" -+#include "catalog/pg_auth_members.h" -+#include "catalog/pg_authid.h" -+#include "catalog/pg_cast.h" -+#include "catalog/pg_class.h" -+#include "catalog/pg_conversion.h" -+#include "catalog/pg_database.h" -+#include "catalog/pg_description.h" -+#include "catalog/pg_enum.h" -+#include "catalog/pg_foreign_data_wrapper.h" -+#include "catalog/pg_foreign_server.h" -+#include "catalog/pg_inherits.h" -+#include "catalog/pg_language.h" -+#include "catalog/pg_largeobject.h" -+#include "catalog/pg_largeobject_metadata.h" -+#include "catalog/pg_namespace.h" -+#include "catalog/pg_opclass.h" -+#include "catalog/pg_operator.h" -+#include "catalog/pg_opfamily.h" -+#include "catalog/pg_proc.h" -+#include "catalog/pg_rewrite.h" -+#include "catalog/pg_security.h" -+#include "catalog/pg_shdescription.h" -+#include "catalog/pg_statistic.h" -+#include "catalog/pg_tablespace.h" -+#include "catalog/pg_trigger.h" -+#include "catalog/pg_ts_config.h" -+#include "catalog/pg_ts_config_map.h" -+#include "catalog/pg_ts_dict.h" -+#include "catalog/pg_ts_parser.h" -+#include "catalog/pg_ts_template.h" -+#include "catalog/pg_type.h" -+#include "catalog/pg_user_mapping.h" -+#include "miscadmin.h" -+#include "nodes/makefuncs.h" -+#include "security/sepgsql.h" -+#include "storage/fd.h" -+#include "utils/fmgroids.h" -+#include "utils/lsyscache.h" -+#include "utils/syscache.h" -+#include "utils/tqual.h" -+ -+/* GUC: to turn on/off row level controls in SE-PostgreSQL */ -+bool sepostgresql_row_level; -+ -+/* GUC parameter to turn on/off mcstrans */ -+bool sepostgresql_mcstrans; -+ -+/* -+ * sepgsqlTupleDescHasSecid -+ * -+ * returns a hint whether we should allocate a field to store -+ * security label on the given relation, or not. -+ */ -+bool -+sepgsqlTupleDescHasSecid(Oid relid, char relkind) -+{ -+ /* -+ * sepgsqlIsEnabled() is not available because it always returns -+ * false in bootstraping mode -+ */ -+ if (sepostgresql_mode == SEPGSQL_MODE_DISABLED || -+ is_selinux_enabled() < 1) -+ return false; -+ -+ if (!OidIsValid(relid)) -+ return sepostgresql_row_level; /* Target of SELECT INTO */ -+ -+ /* These system catalogs always have its secid */ -+ if (relid == DatabaseRelationId || -+ relid == NamespaceRelationId || -+ relid == RelationRelationId || -+ relid == AttributeRelationId || -+ relid == ProcedureRelationId) -+ return true; -+ -+ /* These system catalogs are an external attributes */ -+ if (relid == AggregateRelationId || -+ relid == AccessMethodOperatorRelationId || -+ relid == AccessMethodProcedureRelationId || -+ relid == AttrDefaultRelationId || -+ relid == AuthMemRelationId || -+ relid == ConstraintRelationId || -+ relid == DescriptionRelationId || -+ relid == EnumRelationId || -+ relid == IndexRelationId || -+ relid == InheritsRelationId || -+ relid == LargeObjectRelationId || -+ relid == RewriteRelationId || -+ relid == SecurityRelationId || -+ relid == SharedDescriptionRelationId || -+ relid == StatisticRelationId || -+ relid == TriggerRelationId) -+ return false; -+ -+ return sepostgresql_row_level; -+} -+ -+/* -+ * defaultSecidWithXXXX -+ */ -+static sepgsql_sid_t -+defaultSecidWithDatabase(Oid relOid, Oid datOid, uint16 tclass) -+{ -+ HeapTuple tuple; -+ sepgsql_sid_t datSid; -+ -+ tuple = SearchSysCache(DATABASEOID, -+ ObjectIdGetDatum(datOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for database: %u", datOid); -+ -+ datSid.relid = DatabaseRelationId; -+ datSid.secid = HeapTupleGetSecid(tuple); -+ -+ ReleaseSysCache(tuple); -+ -+ return sepgsqlClientCreateSecid(datSid, tclass, relOid); -+} -+ -+static sepgsql_sid_t -+defaultSecidWithSchema(Oid relOid, Oid nspOid, uint16 tclass) -+{ -+ HeapTuple tuple; -+ sepgsql_sid_t nspSid; -+ -+ tuple = SearchSysCache(NAMESPACEOID, -+ ObjectIdGetDatum(nspOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for schema: %u", nspOid); -+ -+ nspSid.relid = NamespaceRelationId; -+ nspSid.secid = HeapTupleGetSecid(tuple); -+ -+ ReleaseSysCache(tuple); -+ -+ return sepgsqlClientCreateSecid(nspSid, tclass, relOid); -+} -+ -+static sepgsql_sid_t -+defaultSecidWithTable(Oid relOid, Oid tblOid, uint16 tclass) -+{ -+ HeapTuple tuple; -+ sepgsql_sid_t tblSid; -+ -+ tuple = SearchSysCache(RELOID, -+ ObjectIdGetDatum(tblOid), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tuple)) -+ elog(ERROR, "cache lookup failed for relation: %u", tblOid); -+ -+ tblSid.relid = RelationRelationId; -+ tblSid.secid = HeapTupleGetSecid(tuple); -+ -+ ReleaseSysCache(tuple); -+ -+ return sepgsqlClientCreateSecid(tblSid, tclass, relOid); -+} -+ -+/* -+ * sepgsqlGetDefaultDatabaseSecid -+ * It returns the default security label of a database object. -+ */ -+sepgsql_sid_t -+sepgsqlGetDefaultDatabaseSecid(Oid source_database_oid) -+{ -+ return defaultSecidWithDatabase(DatabaseRelationId, -+ source_database_oid, -+ SEPG_CLASS_DB_DATABASE); -+} -+ -+sepgsql_sid_t -+sepgsqlGetDefaultSchemaSecid(Oid database_oid) -+{ -+ return defaultSecidWithDatabase(NamespaceRelationId, -+ database_oid, -+ SEPG_CLASS_DB_SCHEMA); -+} -+ -+sepgsql_sid_t -+sepgsqlGetDefaultTableSecid(Oid namespace_oid) -+{ -+ return defaultSecidWithSchema(RelationRelationId, -+ namespace_oid, -+ SEPG_CLASS_DB_TABLE); -+} -+ -+sepgsql_sid_t -+sepgsqlGetDefaultSequenceSecid(Oid namespace_oid) -+{ -+ return defaultSecidWithSchema(RelationRelationId, -+ namespace_oid, -+ SEPG_CLASS_DB_SEQUENCE); -+} -+ -+sepgsql_sid_t -+sepgsqlGetDefaultProcedureSecid(Oid namespace_oid) -+{ -+ return defaultSecidWithSchema(ProcedureRelationId, -+ namespace_oid, -+ SEPG_CLASS_DB_PROCEDURE); -+} -+ -+sepgsql_sid_t -+sepgsqlGetDefaultColumnSecid(Oid table_oid) -+{ -+ return defaultSecidWithTable(AttributeRelationId, -+ table_oid, -+ SEPG_CLASS_DB_COLUMN); -+} -+ -+sepgsql_sid_t -+sepgsqlGetDefaultTupleSecid(Oid table_oid) -+{ -+ return defaultSecidWithTable(table_oid, -+ table_oid, -+ SEPG_CLASS_DB_TUPLE); -+} -+ -+sepgsql_sid_t -+sepgsqlGetDefaultBlobSecid(Oid database_oid) -+{ -+ return defaultSecidWithDatabase(LargeObjectMetadataRelationId, -+ MyDatabaseId, -+ SEPG_CLASS_DB_BLOB); -+} -+ -+void -+sepgsqlSetDefaultSecid(Relation rel, HeapTuple tuple) -+{ -+ sepgsql_sid_t newSid; -+ Oid relOid = RelationGetRelid(rel); -+ Oid nspOid, tblOid; -+ char relkind; -+ -+ if (!HeapTupleHasSecid(tuple)) -+ return; -+ -+ /* initialize */ -+ newSid.relid = relOid; -+ newSid.secid = InvalidOid; -+ -+ switch (relOid) -+ { -+ case DatabaseRelationId: -+ /* should be never happen */ -+ elog(WARNING, "bug? pg_database tuple without security label"); -+ break; -+ -+ case NamespaceRelationId: -+ newSid = sepgsqlGetDefaultSchemaSecid(MyDatabaseId); -+ break; -+ -+ case RelationRelationId: -+ nspOid = ((Form_pg_class) GETSTRUCT(tuple))->relnamespace; -+ relkind = ((Form_pg_class) GETSTRUCT(tuple))->relkind; -+ -+ switch (relkind) -+ { -+ case RELKIND_RELATION: -+ newSid = sepgsqlGetDefaultTableSecid(nspOid); -+ break; -+ -+ case RELKIND_SEQUENCE: -+ newSid = sepgsqlGetDefaultSequenceSecid(nspOid); -+ break; -+ -+ default: -+ newSid = sepgsqlGetDefaultTupleSecid(relOid); -+ break; -+ } -+ break; -+ -+ case ProcedureRelationId: -+ nspOid = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; -+ newSid = sepgsqlGetDefaultProcedureSecid(nspOid); -+ break; -+ -+ case AttributeRelationId: -+ tblOid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; -+ if (get_rel_relkind(tblOid) == RELKIND_RELATION) -+ newSid = sepgsqlGetDefaultColumnSecid(tblOid); -+ break; -+ -+ case LargeObjectMetadataRelationId: -+ newSid = sepgsqlGetDefaultBlobSecid(MyDatabaseId); -+ break; -+ -+ default: -+ newSid = sepgsqlGetDefaultTupleSecid(relOid); -+ break; -+ } -+ -+ HeapTupleSetSecid(tuple, newSid.secid); -+} -+ -+/* -+ * sepgsqlPostBootstrapingMode -+ * -+ * Assign initial security context -+ */ -+static void -+sepgsqlInitialLabeling(Oid relOid, char *seclabels[]) -+{ -+ Relation rel; -+ HeapScanDesc scan; -+ HeapTuple tuple; -+ HeapTuple newtup; -+ -+ rel = heap_open(relOid, RowExclusiveLock); -+ -+ scan = heap_beginscan(rel, SnapshotNow, 0, NULL); -+ -+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) -+ { -+ Oid secid = InvalidOid; -+ Oid attrelid; -+ char relkind; -+ -+ if (!HeapTupleHasSecid(tuple)) -+ continue; -+ -+ switch (relOid) -+ { -+ case DatabaseRelationId: -+ secid = securityRawSecLabelIn(relOid, seclabels[0]); -+ break; -+ -+ case NamespaceRelationId: -+ secid = securityRawSecLabelIn(relOid, seclabels[1]); -+ break; -+ -+ case RelationRelationId: -+ relkind = ((Form_pg_class) GETSTRUCT(tuple))->relkind; -+ switch (relkind) -+ { -+ case RELKIND_RELATION: -+ secid = securityRawSecLabelIn(relOid, seclabels[2]); -+ break; -+ case RELKIND_SEQUENCE: -+ secid = securityRawSecLabelIn(relOid, seclabels[3]); -+ break; -+ default: -+ secid = securityRawSecLabelIn(relOid, seclabels[6]); -+ break; -+ } -+ break; -+ -+ case AttributeRelationId: -+ attrelid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; -+ if (get_rel_relkind(attrelid) == RELKIND_RELATION) -+ secid = securityRawSecLabelIn(relOid, seclabels[5]); -+ break; -+ -+ case ProcedureRelationId: -+ secid = securityRawSecLabelIn(relOid, seclabels[4]); -+ break; -+ -+ case LargeObjectMetadataRelationId: -+ secid = securityRawSecLabelIn(relOid, seclabels[7]); -+ break; -+ -+ default: -+ secid = securityRawSecLabelIn(relOid, seclabels[6]); -+ break; -+ } -+ -+ /* -+ * Inplace update -+ */ -+ newtup = heap_copytuple(tuple); -+ -+ HeapTupleSetSecid(newtup, secid); -+ -+ heap_inplace_update(rel, newtup); -+ } -+ heap_endscan(scan); -+ -+ heap_close(rel, RowExclusiveLock); -+} -+ -+void -+sepgsqlPostBootstrapingMode(void) -+{ -+ Form_pg_class classForm; -+ Relation rel; -+ ScanKeyData skey; -+ HeapScanDesc scan; -+ HeapTuple tuple; -+ char *scontext; -+ char *seclabels[8]; -+ -+ /* -+ * sepgsqlIsEnabled() is not available because it always returns -+ * false in bootstraping mode -+ */ -+ Assert(IsBootstrapProcessingMode()); -+ if (sepostgresql_mode == SEPGSQL_MODE_DISABLED || -+ is_selinux_enabled() < 1) -+ return; -+ -+ /* -+ * Compute default initial security context -+ */ -+ if (getprevcon_raw(&scontext) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("could not obtain current context"))); -+ -+ seclabels[0] = sepgsqlComputeCreate(scontext, scontext, -+ SEPG_CLASS_DB_DATABASE); -+ seclabels[1] = sepgsqlComputeCreate(scontext, seclabels[0], -+ SEPG_CLASS_DB_SCHEMA); -+ seclabels[2] = sepgsqlComputeCreate(scontext, seclabels[1], -+ SEPG_CLASS_DB_TABLE); -+ seclabels[3] = sepgsqlComputeCreate(scontext, seclabels[1], -+ SEPG_CLASS_DB_SEQUENCE); -+ seclabels[4] = sepgsqlComputeCreate(scontext, seclabels[1], -+ SEPG_CLASS_DB_PROCEDURE); -+ seclabels[5] = sepgsqlComputeCreate(scontext, seclabels[2], -+ SEPG_CLASS_DB_COLUMN); -+ seclabels[6] = sepgsqlComputeCreate(scontext, seclabels[2], -+ SEPG_CLASS_DB_TUPLE); -+ seclabels[7] = sepgsqlComputeCreate(scontext, seclabels[0], -+ SEPG_CLASS_DB_BLOB); -+ /* -+ * Inplace update -+ */ -+ StartTransactionCommand(); -+ -+ rel = heap_open(RelationRelationId, AccessShareLock); -+ -+ ScanKeyInit(&skey, -+ Anum_pg_class_relkind, -+ BTEqualStrategyNumber, F_CHAREQ, -+ CharGetDatum(RELKIND_RELATION)); -+ -+ scan = heap_beginscan(rel, SnapshotNow, 1, &skey); -+ -+ while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) -+ sepgsqlInitialLabeling(HeapTupleGetOid(tuple), seclabels); -+ -+ heap_endscan(scan); -+ -+ heap_close(rel, AccessShareLock); -+ -+ CommitTransactionCommand(); -+} -+ -+/* -+ * sepgsqlGetSysobjSecid -+ * -+ * It returns a pair of relid/secid for the given OID. -+ */ -+static sepgsql_sid_t -+getSysobjSecidDirect(Oid classOid, Oid indexOid, Oid objectId, uint16 *tclass) -+{ -+ sepgsql_sid_t sid; -+ Relation rel; -+ HeapTuple tup; -+ ScanKeyData skey; -+ SysScanDesc scan; -+ -+ rel = heap_open(CastRelationId, AccessShareLock); -+ -+ ScanKeyInit(&skey, -+ ObjectIdAttributeNumber, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(objectId)); -+ -+ scan = systable_beginscan(rel, CastOidIndexId, true, -+ SnapshotNow, 1, &skey); -+ tup = systable_getnext(scan); -+ -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "system object lookup failed for oid %u on relation %u", -+ objectId, classOid); -+ -+ sid = sepgsqlGetTupleSecid(classOid, tup, tclass); -+ -+ systable_endscan(scan); -+ -+ heap_close(rel, AccessShareLock); -+ -+ return sid; -+} -+ -+sepgsql_sid_t -+sepgsqlGetSysobjSecid(Oid classOid, Oid objectId, int32 objsubId, uint16 *tclass) -+{ -+ sepgsql_sid_t sid; -+ HeapTuple tup; -+ -+ switch (classOid) -+ { -+ case AccessMethodRelationId: -+ tup = SearchSysCache(AMOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for access method: %u", objectId); -+ break; -+ -+ case AccessMethodOperatorRelationId: -+ return getSysobjSecidDirect(AccessMethodOperatorRelationId, -+ AccessMethodOperatorOidIndexId, -+ objectId, tclass); -+ -+ case AccessMethodProcedureRelationId: -+ return getSysobjSecidDirect(AccessMethodProcedureRelationId, -+ AccessMethodProcedureOidIndexId, -+ objectId, tclass); -+ -+ case AuthIdRelationId: -+ tup = SearchSysCache(AUTHOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for role: %u", objectId); -+ break; -+ -+ case CastRelationId: -+ return getSysobjSecidDirect(CastRelationId, -+ CastOidIndexId, -+ objectId, tclass); -+ -+ case ConstraintRelationId: -+ tup = SearchSysCache(CONSTROID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for constraint: %u", objectId); -+ break; -+ -+ case ConversionRelationId: -+ tup = SearchSysCache(CONVOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for conversion: %u", objectId); -+ break; -+ -+ case DatabaseRelationId: -+ tup = SearchSysCache(DATABASEOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for database: %u", objectId); -+ break; -+ -+ case ForeignDataWrapperRelationId: -+ tup = SearchSysCache(FOREIGNDATAWRAPPEROID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for FDW: %u", objectId); -+ break; -+ -+ case ForeignServerRelationId: -+ tup = SearchSysCache(FOREIGNSERVEROID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for foreign server: %u", objectId); -+ break; -+ -+ case LanguageRelationId: -+ tup = SearchSysCache(LANGOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ break; -+ -+ case LargeObjectRelationId: -+ case LargeObjectMetadataRelationId: -+ { -+ Relation rel; -+ ScanKeyData skey; -+ SysScanDesc scan; -+ -+ rel = heap_open(LargeObjectMetadataRelationId, AccessShareLock); -+ -+ ScanKeyInit(&skey, -+ ObjectIdAttributeNumber, -+ BTEqualStrategyNumber, F_OIDEQ, -+ ObjectIdGetDatum(objectId)); -+ -+ scan = systable_beginscan(rel, LargeObjectMetadataOidIndexId, -+ true, SnapshotNow, 1, &skey); -+ -+ tup = systable_getnext(scan); -+ -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "largeobject %u lookup failed", objectId); -+ -+ sid = sepgsqlGetTupleSecid(classOid, tup, tclass); -+ systable_endscan(scan); -+ -+ heap_close(rel, AccessShareLock); -+ } -+ return sid; -+ -+ case RelationRelationId: -+ if (objsubId != 0) -+ { -+ classOid = AttributeRelationId; -+ tup = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(objectId), -+ Int16GetDatum(objsubId), -+ 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for attribute %d of relation %u", -+ objsubId, objectId); -+ } -+ else -+ { -+ classOid = RelationRelationId; -+ tup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for relation %u", objectId); -+ } -+ break; -+ -+ case NamespaceRelationId: -+ tup = SearchSysCache(NAMESPACEOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for schema %u", objectId); -+ break; -+ -+ case OperatorClassRelationId: -+ tup = SearchSysCache(CLAOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for opclass %u", objectId); -+ break; -+ -+ case OperatorFamilyRelationId: -+ tup = SearchSysCache(OPFAMILYOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for opfamily %u", objectId); -+ break; -+ -+ case OperatorRelationId: -+ tup = SearchSysCache(OPEROID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for operator %u", objectId); -+ break; -+ -+ case ProcedureRelationId: -+ tup = SearchSysCache(PROCOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for procedure %u", objectId); -+ break; -+ -+ case RewriteRelationId: -+ return getSysobjSecidDirect(RewriteRelationId, -+ RewriteOidIndexId, -+ objectId, tclass); -+ -+ case TableSpaceRelationId: -+ return getSysobjSecidDirect(TableSpaceRelationId, -+ TablespaceOidIndexId, -+ objectId, tclass); -+ -+ case TriggerRelationId: -+ return getSysobjSecidDirect(TriggerRelationId, -+ TriggerOidIndexId, -+ objectId, tclass); -+ -+ case TSConfigRelationId: -+ tup = SearchSysCache(TSCONFIGOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for text search configuration %u", objectId); -+ break; -+ -+ case TSDictionaryRelationId: -+ tup = SearchSysCache(TSDICTOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for text search dictionary %u", objectId); -+ break; -+ -+ case TSParserRelationId: -+ tup = SearchSysCache(TSPARSEROID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for text search parser %u", objectId); -+ break; -+ -+ case TSTemplateRelationId: -+ tup = SearchSysCache(TSTEMPLATEOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for text search template %u", objectId); -+ break; -+ -+ case TypeRelationId: -+ tup = SearchSysCache(TYPEOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for type %u", objectId); -+ break; -+ -+ case UserMappingRelationId: -+ tup = SearchSysCache(USERMAPPINGOID, -+ ObjectIdGetDatum(objectId), -+ 0, 0, 0); -+ if (!HeapTupleIsValid(tup)) -+ elog(ERROR, "cache lookup failed for user mapping %u", objectId); -+ break; -+ -+ default: -+ elog(ERROR, "unexpected class OID: %u", classOid); -+ tup = NULL; /* for compiler quiet */ -+ break; -+ } -+ -+ Assert(HeapTupleIsValid(tup)); -+ -+ sid = sepgsqlGetTupleSecid(classOid, tup, tclass); -+ -+ ReleaseSysCache(tup); -+ -+ return sid; -+} -+ -+/* -+ * sepgsqlGetTupleSecid -+ * -+ * It returns a pair of relid/secid for the given HeapTuple. -+ * A few system catalogs is handled as an attribute of other -+ * system objects. -+ * E.g) pg_attrdef is an attribute of a certain pg_attribute -+ */ -+sepgsql_sid_t -+sepgsqlGetTupleSecid(Oid tableOid, HeapTuple tuple, uint16 *tclass) -+{ -+ sepgsql_sid_t sid; -+ HeapTuple exttup; -+ Oid extid; -+ Oid extcls; -+ AttrNumber extsub; -+ -+ /* initialize (unlabeled security context) */ -+ sid.relid = tableOid; -+ sid.secid = InvalidOid; -+ if (tclass) -+ *tclass = SEPG_CLASS_DB_TUPLE; -+ -+ switch (tableOid) -+ { -+ case AggregateRelationId: -+ extid = ((Form_pg_aggregate) GETSTRUCT(tuple))->aggfnoid; -+ exttup = SearchSysCache(PROCOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(ProcedureRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case AccessMethodOperatorRelationId: -+ extid = ((Form_pg_amop) GETSTRUCT(tuple))->amopfamily; -+ exttup = SearchSysCache(OPFAMILYOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case AccessMethodProcedureRelationId: -+ extid = ((Form_pg_amproc) GETSTRUCT(tuple))->amprocfamily; -+ exttup = SearchSysCache(OPFAMILYOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(OperatorFamilyRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case AttrDefaultRelationId: -+ extid = ((Form_pg_attrdef) GETSTRUCT(tuple))->adrelid; -+ extsub = ((Form_pg_attrdef) GETSTRUCT(tuple))->adnum; -+ exttup = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(extid), -+ Int16GetDatum(extsub), -+ 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(AttributeRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case AttributeRelationId: -+ extid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; -+ exttup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ char relkind = ((Form_pg_class) GETSTRUCT(exttup))->relkind; -+ -+ if (relkind == RELKIND_RELATION) -+ { -+ if (tclass) -+ *tclass = SEPG_CLASS_DB_COLUMN; -+ sid.secid = HeapTupleGetSecid(tuple); -+ } -+ else -+ sid = sepgsqlGetTupleSecid(RelationRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case AuthMemRelationId: -+ extid = ((Form_pg_auth_members) GETSTRUCT(tuple))->roleid; -+ exttup = SearchSysCache(AUTHOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(AuthIdRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case ConstraintRelationId: -+ /* CHECK constraint is an attribute of the relation */ -+ extid = ((Form_pg_constraint) GETSTRUCT(tuple))->conrelid; -+ if (OidIsValid(extid)) -+ { -+ exttup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(RelationRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ } -+ /* DOMAIN constraint is an attribute of the domain type */ -+ extid = ((Form_pg_constraint) GETSTRUCT(tuple))->contypid; -+ if (OidIsValid(extid)) -+ { -+ sid.relid = TypeRelationId; -+ exttup = SearchSysCache(TYPEOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(TypeRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ } -+ /* Database's context for global assertion */ -+ exttup = SearchSysCache(DATABASEOID, -+ ObjectIdGetDatum(MyDatabaseId), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(DatabaseRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case DatabaseRelationId: -+ sid.secid = HeapTupleGetSecid(tuple); -+ if (tclass) -+ *tclass = SEPG_CLASS_DB_DATABASE; -+ break; -+ -+ case DescriptionRelationId: -+ /* recursive call */ -+ extid = ((Form_pg_description) GETSTRUCT(tuple))->objoid; -+ extcls = ((Form_pg_description) GETSTRUCT(tuple))->classoid; -+ return sepgsqlGetSysobjSecid(extcls, extid, 0, tclass); -+ -+ case EnumRelationId: -+ extid = ((Form_pg_enum) GETSTRUCT(tuple))->enumtypid; -+ exttup = SearchSysCache(TYPEOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(TypeRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case IndexRelationId: -+ extid = ((Form_pg_index) GETSTRUCT(tuple))->indrelid; -+ exttup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(RelationRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case InheritsRelationId: -+ extid = ((Form_pg_inherits) GETSTRUCT(tuple))->inhrelid; -+ exttup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(RelationRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case LargeObjectRelationId: -+ extid = ((Form_pg_largeobject) GETSTRUCT(tuple))->loid; -+ extcls = LargeObjectMetadataRelationId; -+ return sepgsqlGetSysobjSecid(extcls, extid, 0, tclass); -+ -+ case LargeObjectMetadataRelationId: -+ sid.secid = HeapTupleGetSecid(tuple); -+ if (tclass) -+ *tclass = SEPG_CLASS_DB_BLOB; -+ break; -+ -+ case NamespaceRelationId: -+ sid.secid = HeapTupleGetSecid(tuple); -+ if (tclass) -+ *tclass = SEPG_CLASS_DB_SCHEMA; -+ break; -+ -+ case ProcedureRelationId: -+ sid.secid = HeapTupleGetSecid(tuple); -+ if (tclass) -+ *tclass = SEPG_CLASS_DB_PROCEDURE; -+ break; -+ -+ case RelationRelationId: -+ sid.secid = HeapTupleGetSecid(tuple); -+ if (tclass) -+ { -+ char relkind = ((Form_pg_class) GETSTRUCT(tuple))->relkind; -+ -+ switch (relkind) -+ { -+ case RELKIND_RELATION: -+ *tclass = SEPG_CLASS_DB_TABLE; -+ break; -+ -+ case RELKIND_SEQUENCE: -+ *tclass = SEPG_CLASS_DB_SEQUENCE; -+ break; -+ -+ default: -+ *tclass = SEPG_CLASS_DB_TUPLE; -+ break; -+ } -+ } -+ break; -+ -+ case RewriteRelationId: -+ extid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class; -+ exttup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(RelationRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case SharedDescriptionRelationId: -+ /* recursive invocation */ -+ extid = ((Form_pg_shdescription) GETSTRUCT(tuple))->objoid; -+ extcls = ((Form_pg_shdescription) GETSTRUCT(tuple))->classoid; -+ return sepgsqlGetSysobjSecid(extcls, extid, 0, tclass); -+ -+ case StatisticRelationId: -+ extid = ((Form_pg_statistic) GETSTRUCT(tuple))->starelid; -+ extsub = ((Form_pg_statistic) GETSTRUCT(tuple))->staattnum; -+ exttup = SearchSysCache(ATTNUM, -+ ObjectIdGetDatum(extid), -+ Int16GetDatum(extsub), -+ 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(AttributeRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case TriggerRelationId: -+ extid = ((Form_pg_trigger) GETSTRUCT(tuple))->tgrelid; -+ exttup = SearchSysCache(RELOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(RelationRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ case TSConfigMapRelationId: -+ extid = ((Form_pg_ts_config_map) GETSTRUCT(tuple))->mapcfg; -+ exttup = SearchSysCache(TSCONFIGOID, -+ ObjectIdGetDatum(extid), -+ 0, 0, 0); -+ if (HeapTupleIsValid(exttup)) -+ { -+ sid = sepgsqlGetTupleSecid(TSConfigRelationId, -+ exttup, tclass); -+ ReleaseSysCache(exttup); -+ } -+ break; -+ -+ default: -+ /* No external lookups (normal case) */ -+ sid.secid = HeapTupleGetSecid(tuple); -+ break; -+ } -+ -+ return sid; -+} -+ -+/* -+ * sepgsqlRawSecLabelIn -+ * correctness checks for the given security context -+ */ -+char * -+sepgsqlRawSecLabelIn(char *seclabel) -+{ -+ if (!sepgsqlIsEnabled()) -+ return seclabel; -+ -+ if (!seclabel || security_check_context_raw(seclabel) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_INVALID_SECURITY_LABEL), -+ errmsg("Invalid security context: \"%s\"", seclabel))); -+ -+ return seclabel; -+} -+ -+/* -+ * sepgsqlRawSecLabelOut -+ * correctness checks for the given security context, -+ * and replace it if invalid security context -+ */ -+char * -+sepgsqlRawSecLabelOut(char *seclabel) -+{ -+ if (!sepgsqlIsEnabled()) -+ return seclabel; -+ -+ if (!seclabel || security_check_context_raw(seclabel) < 0) -+ { -+ security_context_t unlabeledcon; -+ -+ if (security_get_initial_context_raw("unlabeled", -+ &unlabeledcon) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("Unabled to get unlabeled security context"))); -+ PG_TRY(); -+ { -+ seclabel = pstrdup(unlabeledcon); -+ } -+ PG_CATCH(); -+ { -+ freecon(unlabeledcon); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(unlabeledcon); -+ } -+ return seclabel; -+} -+ -+/* -+ * sepgsqlTransSecLabelIn -+ * sepgsqlTransSecLabelOut -+ * translation between human-readable and raw format -+ */ -+char * -+sepgsqlTransSecLabelIn(char *seclabel) -+{ -+ security_context_t rawlabel; -+ security_context_t result; -+ -+ if (!sepgsqlIsEnabled() || -+ !sepostgresql_mcstrans) -+ return seclabel; -+ -+ if (selinux_trans_to_raw_context(seclabel, &rawlabel) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("SELinux: failed to translate \"%s\"", seclabel))); -+ PG_TRY(); -+ { -+ result = pstrdup(rawlabel); -+ } -+ PG_CATCH(); -+ { -+ freecon(rawlabel); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(rawlabel); -+ -+ return result; -+} -+ -+char * -+sepgsqlTransSecLabelOut(char *seclabel) -+{ -+ security_context_t translabel; -+ security_context_t result; -+ -+ if (!sepgsqlIsEnabled() || -+ !sepostgresql_mcstrans) -+ return seclabel; -+ -+ if (selinux_raw_to_trans_context(seclabel, &translabel) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("SELinux: failed to translate \"%s\"", seclabel))); -+ PG_TRY(); -+ { -+ result = pstrdup(translabel); -+ } -+ PG_CATCH(); -+ { -+ freecon(translabel); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(translabel); -+ -+ return result; -+} -+ -+char * -+sepgsqlSysattSecLabelOut(Oid relid, HeapTuple tuple) -+{ -+ sepgsql_sid_t sid; -+ -+ sid = sepgsqlGetTupleSecid(relid, tuple, NULL); -+ -+ return securityTransSecLabelOut(sid.relid, sid.secid); -+} -diff --git a/src/backend/security/sepgsql/misc.c b/src/backend/security/sepgsql/misc.c -new file mode 100644 -index 0000000..2f7c466 ---- /dev/null -+++ b/src/backend/security/sepgsql/misc.c -@@ -0,0 +1,214 @@ -+/* -+ * src/backend/security/sepgsql/misc.c -+ * Miscellaneous facilities in SE-PostgreSQL -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+#include "postgres.h" -+#include "libpq/libpq-be.h" -+#include "miscadmin.h" -+#include "security/sepgsql.h" -+#include "utils/builtins.h" -+ -+/* -+ * SE-PostgreSQL specific functions -+ */ -+Datum -+sepgsql_getcon(PG_FUNCTION_ARGS) -+{ -+ security_context_t context; -+ -+ if (!sepgsqlIsEnabled()) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SELinux: disabled now"))); -+ -+ context = sepgsqlGetClientLabel(); -+ context = sepgsqlTransSecLabelOut(context); -+ return CStringGetTextDatum(context); -+} -+ -+Datum -+sepgsql_server_getcon(PG_FUNCTION_ARGS) -+{ -+ char *context; -+ -+ if (!sepgsqlIsEnabled()) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SELinux: disabled now"))); -+ -+ context = sepgsqlGetServerLabel(); -+ context = sepgsqlTransSecLabelOut(context); -+ -+ return CStringGetTextDatum(context); -+} -+ -+/* -+ * sepgsql_(get|set)_(user|role|type|range) -+ * get/set a component of security context. -+ */ -+static void -+parse_security_context(security_context_t context, -+ char **user, char **role, char **type, char **range) -+{ -+ security_context_t raw_context; -+ char *tok; -+ -+ if (!sepgsqlIsEnabled()) -+ ereport(ERROR, -+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -+ errmsg("SELinux: disabled now"))); -+ -+ if (selinux_trans_to_raw_context(context, &raw_context) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("could not translate mls label: %s", context))); -+ -+ PG_TRY(); -+ { -+ tok = strtok(raw_context, ":"); -+ if (user) -+ *user = (!tok ? NULL : pstrdup(tok)); -+ -+ tok = strtok(NULL, ":"); -+ if (role) -+ *role = (!tok ? NULL : pstrdup(tok)); -+ -+ tok = strtok(NULL, ":"); -+ if (type) -+ *type = (!tok ? NULL : pstrdup(tok)); -+ -+ tok = strtok(NULL, "\0"); -+ if (range) -+ *range = (!tok ? NULL : pstrdup(tok)); -+ } -+ PG_CATCH(); -+ { -+ freecon(raw_context); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(raw_context); -+} -+ -+Datum -+sepgsql_get_user(PG_FUNCTION_ARGS) -+{ -+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); -+ char *user; -+ -+ parse_security_context(context, &user, NULL, NULL, NULL); -+ if (!user) -+ ereport(ERROR, -+ (errcode(ERRCODE_INVALID_SECURITY_LABEL), -+ errmsg("could not extract user of \"%s\"", context))); -+ -+ PG_RETURN_TEXT_P(CStringGetTextDatum(user)); -+} -+ -+Datum -+sepgsql_get_role(PG_FUNCTION_ARGS) -+{ -+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); -+ char *role; -+ -+ parse_security_context(context, NULL, &role, NULL, NULL); -+ if (!role) -+ ereport(ERROR, -+ (errcode(ERRCODE_INVALID_SECURITY_LABEL), -+ errmsg("could not extract role of \"%s\"", context))); -+ -+ PG_RETURN_TEXT_P(CStringGetTextDatum(role)); -+} -+ -+Datum -+sepgsql_get_type(PG_FUNCTION_ARGS) -+{ -+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); -+ char *type; -+ -+ parse_security_context(context, NULL, NULL, &type, NULL); -+ if (!type) -+ ereport(ERROR, -+ (errcode(ERRCODE_INVALID_SECURITY_LABEL), -+ errmsg("could not extract type of \"%s\"", context))); -+ -+ PG_RETURN_TEXT_P(CStringGetTextDatum(type)); -+} -+ -+Datum -+sepgsql_get_range(PG_FUNCTION_ARGS) -+{ -+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); -+ char *range; -+ -+ parse_security_context(context, NULL, NULL, NULL, &range); -+ if (!range) -+ ereport(ERROR, -+ (errcode(ERRCODE_INVALID_SECURITY_LABEL), -+ errmsg("could not extract range of \"%s\"", context))); -+ -+ PG_RETURN_TEXT_P(CStringGetTextDatum(range)); -+} -+ -+static Datum -+sepgsql_set_common(char *context, -+ char *user, char *role, char *type, char *range) -+{ -+ StringInfoData newcon; -+ -+ parse_security_context(context, -+ !user ? &user : NULL, -+ !role ? &role : NULL, -+ !type ? &type : NULL, -+ !range ? &range : NULL); -+ if (!user || !role || !type) -+ ereport(ERROR, -+ (errcode(ERRCODE_INVALID_SECURITY_LABEL), -+ errmsg("invalid security context: \"%s\"", context))); -+ -+ initStringInfo(&newcon); -+ appendStringInfo(&newcon, "%s:%s:%s", user, role, type); -+ if (range) -+ appendStringInfo(&newcon, ":%s", range); -+ -+ return CStringGetTextDatum(sepgsqlTransSecLabelOut(newcon.data)); -+} -+ -+Datum -+sepgsql_set_user(PG_FUNCTION_ARGS) -+{ -+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); -+ char *user = TextDatumGetCString(PG_GETARG_TEXT_P(1)); -+ -+ return sepgsql_set_common(context, user, NULL, NULL, NULL); -+} -+ -+Datum -+sepgsql_set_role(PG_FUNCTION_ARGS) -+{ -+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); -+ char *role = TextDatumGetCString(PG_GETARG_TEXT_P(1)); -+ -+ return sepgsql_set_common(context, NULL, role, NULL, NULL); -+} -+ -+Datum -+sepgsql_set_type(PG_FUNCTION_ARGS) -+{ -+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); -+ char *type = TextDatumGetCString(PG_GETARG_TEXT_P(1)); -+ -+ return sepgsql_set_common(context, NULL, NULL, type, NULL); -+} -+ -+Datum -+sepgsql_set_range(PG_FUNCTION_ARGS) -+{ -+ security_context_t context = TextDatumGetCString(PG_GETARG_TEXT_P(0)); -+ char *range = TextDatumGetCString(PG_GETARG_TEXT_P(1)); -+ -+ return sepgsql_set_common(context, NULL, NULL, NULL, range); -+} -diff --git a/src/backend/security/sepgsql/perms.c b/src/backend/security/sepgsql/perms.c -new file mode 100644 -index 0000000..5943f40 ---- /dev/null -+++ b/src/backend/security/sepgsql/perms.c -@@ -0,0 +1,597 @@ -+/* -+ * src/backend/utils/sepgsql/perms.c -+ * SE-PostgreSQL permission checks -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+#include "postgres.h" -+ -+#include "catalog/pg_database.h" -+#include "catalog/pg_proc.h" -+#include "catalog/pg_largeobject.h" -+#include "catalog/pg_namespace.h" -+#include "catalog/pg_type.h" -+#include "miscadmin.h" -+#include "security/sepgsql.h" -+#include "utils/lsyscache.h" -+ -+/* -+ * Dynamic object class/permissions mapping -+ * -+ * SELinux exports the list of object classes and permissions at -+ * /selinux/class. The libselinux provides an interface to translate -+ * between their names and codes. -+ */ -+static struct -+{ -+ const char *class_name; -+ security_class_t class_code; -+ struct -+ { -+ const char *perm_name; -+ access_vector_t perm_code; -+ } av[sizeof(access_vector_t) * 8]; -+} selinux_catalog[] = { -+ { -+ "process", SEPG_CLASS_PROCESS, -+ { -+ {"translation", SEPG_PROCESS__TRANSITION }, -+ {NULL, 0} -+ } -+ }, -+ { -+ "file", SEPG_CLASS_FILE, -+ { -+ {"read", SEPG_FILE__READ }, -+ {"write", SEPG_FILE__WRITE }, -+ {"create", SEPG_FILE__CREATE }, -+ {"getattr", SEPG_FILE__GETATTR }, -+ {NULL, 0} -+ } -+ }, -+ { -+ "dir", SEPG_CLASS_DIR, -+ { -+ {"read", SEPG_DIR__READ }, -+ {"write", SEPG_DIR__WRITE }, -+ {"create", SEPG_DIR__CREATE }, -+ {"getattr", SEPG_DIR__GETATTR }, -+ {NULL,0} -+ } -+ }, -+ { -+ "lnk_file", SEPG_CLASS_LNK_FILE, -+ { -+ {"read", SEPG_LNK_FILE__READ }, -+ {"write", SEPG_LNK_FILE__WRITE }, -+ {"create", SEPG_LNK_FILE__CREATE }, -+ {"getattr", SEPG_LNK_FILE__GETATTR }, -+ {NULL,0} -+ } -+ }, -+ { -+ "chr_file", SEPG_CLASS_CHR_FILE, -+ { -+ {"read", SEPG_CHR_FILE__READ }, -+ {"write", SEPG_CHR_FILE__WRITE }, -+ {"create", SEPG_CHR_FILE__CREATE }, -+ {"getattr", SEPG_CHR_FILE__GETATTR }, -+ {NULL,0} -+ } -+ }, -+ { -+ "blk_file", SEPG_CLASS_BLK_FILE, -+ { -+ {"read", SEPG_BLK_FILE__READ }, -+ {"write", SEPG_BLK_FILE__WRITE }, -+ {"create", SEPG_BLK_FILE__CREATE }, -+ {"getattr", SEPG_BLK_FILE__GETATTR }, -+ {NULL,0} -+ } -+ }, -+ { -+ "sock_file", SEPG_CLASS_SOCK_FILE, -+ { -+ {"read", SEPG_SOCK_FILE__READ }, -+ {"write", SEPG_SOCK_FILE__WRITE }, -+ {"create", SEPG_SOCK_FILE__CREATE }, -+ {"getattr", SEPG_SOCK_FILE__GETATTR }, -+ {NULL,0} -+ } -+ }, -+ { -+ "fifo_file", SEPG_CLASS_FIFO_FILE, -+ { -+ {"read", SEPG_FIFO_FILE__READ }, -+ {"write", SEPG_FIFO_FILE__WRITE }, -+ {"create", SEPG_FIFO_FILE__CREATE }, -+ {"getattr", SEPG_FIFO_FILE__GETATTR }, -+ {NULL, 0UL } -+ } -+ }, -+ { -+ "db_database", SEPG_CLASS_DB_DATABASE, -+ { -+ { "create", SEPG_DB_DATABASE__CREATE }, -+ { "drop", SEPG_DB_DATABASE__DROP }, -+ { "getattr", SEPG_DB_DATABASE__GETATTR }, -+ { "setattr", SEPG_DB_DATABASE__SETATTR }, -+ { "relabelfrom", SEPG_DB_DATABASE__RELABELFROM }, -+ { "relabelto", SEPG_DB_DATABASE__RELABELTO }, -+ { "access", SEPG_DB_DATABASE__ACCESS }, -+ { "install_module", SEPG_DB_DATABASE__INSTALL_MODULE }, -+ { "load_module", SEPG_DB_DATABASE__LOAD_MODULE }, -+ { "superuser", SEPG_DB_DATABASE__SUPERUSER }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_schema", SEPG_CLASS_DB_SCHEMA, -+ { -+ { "create", SEPG_DB_SCHEMA__CREATE }, -+ { "drop", SEPG_DB_SCHEMA__DROP }, -+ { "getattr", SEPG_DB_SCHEMA__GETATTR }, -+ { "setattr", SEPG_DB_SCHEMA__SETATTR }, -+ { "relabelfrom", SEPG_DB_SCHEMA__RELABELFROM }, -+ { "relabelto", SEPG_DB_SCHEMA__RELABELTO }, -+ { "search", SEPG_DB_SCHEMA__SEARCH }, -+ { "add_name", SEPG_DB_SCHEMA__ADD_NAME }, -+ { "remove_name", SEPG_DB_SCHEMA__REMOVE_NAME }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_table", SEPG_CLASS_DB_TABLE, -+ { -+ { "create", SEPG_DB_TABLE__CREATE }, -+ { "drop", SEPG_DB_TABLE__DROP }, -+ { "getattr", SEPG_DB_TABLE__GETATTR }, -+ { "setattr", SEPG_DB_TABLE__SETATTR }, -+ { "relabelfrom", SEPG_DB_TABLE__RELABELFROM }, -+ { "relabelto", SEPG_DB_TABLE__RELABELTO }, -+ { "select", SEPG_DB_TABLE__SELECT }, -+ { "update", SEPG_DB_TABLE__UPDATE }, -+ { "insert", SEPG_DB_TABLE__INSERT }, -+ { "delete", SEPG_DB_TABLE__DELETE }, -+ { "lock", SEPG_DB_TABLE__LOCK }, -+ { "reference", SEPG_DB_TABLE__REFERENCE }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_sequence", SEPG_CLASS_DB_SEQUENCE, -+ { -+ { "create", SEPG_DB_SEQUENCE__CREATE }, -+ { "drop", SEPG_DB_SEQUENCE__DROP }, -+ { "getattr", SEPG_DB_SEQUENCE__GETATTR }, -+ { "setattr", SEPG_DB_SEQUENCE__SETATTR }, -+ { "relabelfrom", SEPG_DB_SEQUENCE__RELABELFROM }, -+ { "relabelto", SEPG_DB_SEQUENCE__RELABELTO }, -+ { "get_value", SEPG_DB_SEQUENCE__GET_VALUE }, -+ { "next_value", SEPG_DB_SEQUENCE__NEXT_VALUE }, -+ { "set_value", SEPG_DB_SEQUENCE__SET_VALUE }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_procedure", SEPG_CLASS_DB_PROCEDURE, -+ { -+ { "create", SEPG_DB_PROCEDURE__CREATE }, -+ { "drop", SEPG_DB_PROCEDURE__DROP }, -+ { "getattr", SEPG_DB_PROCEDURE__GETATTR }, -+ { "setattr", SEPG_DB_PROCEDURE__SETATTR }, -+ { "relabelfrom", SEPG_DB_PROCEDURE__RELABELFROM }, -+ { "relabelto", SEPG_DB_PROCEDURE__RELABELTO }, -+ { "execute", SEPG_DB_PROCEDURE__EXECUTE }, -+ { "entrypoint", SEPG_DB_PROCEDURE__ENTRYPOINT }, -+ { "install", SEPG_DB_PROCEDURE__INSTALL }, -+ { "untrusted", SEPG_DB_PROCEDURE__UNTRUSTED }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_column", SEPG_CLASS_DB_COLUMN, -+ { -+ { "create", SEPG_DB_COLUMN__CREATE }, -+ { "drop", SEPG_DB_COLUMN__DROP }, -+ { "getattr", SEPG_DB_COLUMN__GETATTR }, -+ { "setattr", SEPG_DB_COLUMN__SETATTR }, -+ { "relabelfrom", SEPG_DB_COLUMN__RELABELFROM }, -+ { "relabelto", SEPG_DB_COLUMN__RELABELTO }, -+ { "select", SEPG_DB_COLUMN__SELECT }, -+ { "update", SEPG_DB_COLUMN__UPDATE }, -+ { "insert", SEPG_DB_COLUMN__INSERT }, -+ { "reference", SEPG_DB_COLUMN__REFERENCE }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_tuple", SEPG_CLASS_DB_TUPLE, -+ { -+ { "relabelfrom", SEPG_DB_TUPLE__RELABELFROM }, -+ { "relabelto", SEPG_DB_TUPLE__RELABELTO }, -+ { "select", SEPG_DB_TUPLE__SELECT }, -+ { "update", SEPG_DB_TUPLE__UPDATE }, -+ { "insert", SEPG_DB_TUPLE__INSERT }, -+ { "delete", SEPG_DB_TUPLE__DELETE }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_blob", SEPG_CLASS_DB_BLOB, -+ { -+ { "create", SEPG_DB_BLOB__CREATE }, -+ { "drop", SEPG_DB_BLOB__DROP }, -+ { "getattr", SEPG_DB_BLOB__GETATTR }, -+ { "setattr", SEPG_DB_BLOB__SETATTR }, -+ { "relabelfrom", SEPG_DB_BLOB__RELABELFROM }, -+ { "relabelto", SEPG_DB_BLOB__RELABELTO }, -+ { "read", SEPG_DB_BLOB__READ }, -+ { "write", SEPG_DB_BLOB__WRITE }, -+ { "import", SEPG_DB_BLOB__IMPORT }, -+ { "export", SEPG_DB_BLOB__EXPORT }, -+ { NULL, 0UL }, -+ } -+ } -+}; -+ -+/* -+ * sepgsqlTransToExternalClass -+ * It translate the given class code (defined as SEPGCLASS_(class)) into -+ * external code which is necessary to communicate in-kernel SELinux -+ */ -+extern security_class_t -+sepgsqlTransToExternalClass(uint16 tclass) -+{ -+ Assert(tclass < SEPG_CLASS_MAX); -+ -+ return string_to_security_class(selinux_catalog[tclass].class_name); -+} -+ -+/* -+ * sepgsqlTransToInternalPerms -+ * It translate the given permission masks into internal representation -+ * defined as SEPG_(class)_(permission). -+ */ -+extern void -+sepgsqlTransToInternalPerms(security_class_t tclass, struct av_decision *avd) -+{ -+ security_class_t tclass_ex; -+ struct av_decision i_avd; -+ int i, deny_unknown; -+ -+ Assert(tclass < SEPG_CLASS_MAX); -+ -+ memset(&i_avd, 0, sizeof(struct av_decision)); -+ -+ deny_unknown = security_deny_unknown(); -+ -+ tclass_ex = sepgsqlTransToExternalClass(tclass); -+ for (i=0; selinux_catalog[tclass].av[i].perm_name; i++) -+ { -+ const char *perm_name = selinux_catalog[tclass].av[i].perm_name; -+ access_vector_t perm_code = selinux_catalog[tclass].av[i].perm_code; -+ access_vector_t perm_code_ex; -+ -+ perm_code_ex = string_to_av_perm(tclass_ex, perm_name); -+ if (!perm_code_ex) -+ { -+ /* fill up undefined permission */ -+ if (!deny_unknown) -+ i_avd.allowed |= perm_code; -+ i_avd.decided |= perm_code; -+ i_avd.auditdeny |= perm_code; -+ continue; -+ } -+ -+ if (avd->allowed & perm_code_ex) -+ i_avd.allowed |= perm_code; -+ if (avd->decided & perm_code_ex) -+ i_avd.decided |= perm_code; -+ if (avd->auditallow & perm_code_ex) -+ i_avd.auditallow |= perm_code; -+ if (avd->auditdeny & perm_code_ex) -+ i_avd.auditdeny |= perm_code; -+ } -+ -+ avd->allowed = i_avd.allowed; -+ avd->decided = i_avd.decided; -+ avd->auditallow = i_avd.auditallow; -+ avd->auditdeny = i_avd.auditdeny; -+} -+ -+/* -+ * sepgsqlGetClassString -+ * sepgsqlGetPermissionString -+ * It returns text representation of object classes/permissions -+ */ -+const char * -+sepgsqlGetClassString(uint16 tclass) -+{ -+ Assert(tclass < SEPG_CLASS_MAX); -+ -+ return selinux_catalog[tclass].class_name; -+} -+ -+const char * -+sepgsqlGetPermString(uint16 tclass, uint32 permission) -+{ -+ int i; -+ -+ Assert(tclass < SEPG_CLASS_MAX); -+ -+ for (i=0; selinux_catalog[tclass].av[i].perm_name; i++) -+ { -+ if (selinux_catalog[tclass].av[i].perm_code == permission) -+ return selinux_catalog[tclass].av[i].perm_name; -+ } -+ return NULL; -+} -+ -+#if 0 -+ -+/* -+ * sepgsqlFileObjectClass -+ * -+ * It returns proper object class of filesystem object already opened. -+ * It is necessary to check privileges voluntarily. -+ */ -+uint16 -+sepgsqlFileObjectClass(int fdesc) -+{ -+ struct stat stbuf; -+ -+ if (fstat(fdesc, &stbuf) != 0) -+ ereport(ERROR, -+ (errcode_for_file_access(), -+ errmsg("could not stat file descriptor: %d", fdesc))); -+ -+ if (S_ISDIR(stbuf.st_mode)) -+ return SEPG_CLASS_DIR; -+ else if (S_ISCHR(stbuf.st_mode)) -+ return SEPG_CLASS_CHR_FILE; -+ else if (S_ISBLK(stbuf.st_mode)) -+ return SEPG_CLASS_BLK_FILE; -+ else if (S_ISFIFO(stbuf.st_mode)) -+ return SEPG_CLASS_FIFO_FILE; -+ else if (S_ISLNK(stbuf.st_mode)) -+ return SEPG_CLASS_LNK_FILE; -+ else if (S_ISSOCK(stbuf.st_mode)) -+ return SEPG_CLASS_SOCK_FILE; -+ -+ return SEPG_CLASS_FILE; -+} -+ -+/* -+ * sepgsqlTupleObjectClass -+ * -+ * It returns correct object class of given tuple -+ */ -+uint16 -+sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple) -+{ -+ Form_pg_class clsForm; -+ Form_pg_attribute attForm; -+ -+ switch (relid) -+ { -+ case DatabaseRelationId: -+ return SEPG_CLASS_DB_DATABASE; -+ -+ case NamespaceRelationId: -+ return SEPG_CLASS_DB_SCHEMA; -+ -+ case RelationRelationId: -+ clsForm = (Form_pg_class) GETSTRUCT(tuple); -+ if (clsForm->relkind == RELKIND_RELATION) -+ return SEPG_CLASS_DB_TABLE; -+ if (clsForm->relkind == RELKIND_SEQUENCE) -+ return SEPG_CLASS_DB_SEQUENCE; -+ break; -+ -+ case AttributeRelationId: -+ attForm = (Form_pg_attribute) GETSTRUCT(tuple); -+ if (IsBootstrapProcessingMode() && -+ (attForm->attrelid == TypeRelationId || -+ attForm->attrelid == ProcedureRelationId || -+ attForm->attrelid == AttributeRelationId || -+ attForm->attrelid == RelationRelationId)) -+ return SEPG_CLASS_DB_COLUMN; -+ -+ if (get_rel_relkind(attForm->attrelid) == RELKIND_RELATION) -+ return SEPG_CLASS_DB_COLUMN; -+ break; -+ -+ case ProcedureRelationId: -+ return SEPG_CLASS_DB_PROCEDURE; -+ -+ case LargeObjectRelationId: -+ return SEPG_CLASS_DB_BLOB; -+ } -+ return SEPG_CLASS_DB_TUPLE; -+} -+ -+/* -+ * sepgsqlTupleNamespace -+ * -+ * It returns an OID of the namespace, if the given system object is -+ * deployed under a certain namespace. -+ */ -+Oid -+sepgsqlTupleNamespace(Oid relOid, HeapTuple tuple) -+{ -+ Oid nspOid; -+ -+ switch (relOid) -+ { -+ case RelationRelationId: -+ nspOid = ((Form_pg_class) GETSTRUCT(tuple))->relnamespace; -+ break; -+ -+ case ConstraintRelationId: -+ nspOid = ((Form_pg_constraint) GETSTRUCT(tuple))->connamespace; -+ break; -+ -+ case ConversionRelationId: -+ nspOid = ((Form_pg_conversion) GETSTRUCT(tuple))->connamespace; -+ break; -+ -+ case OperatorClassRelationId: -+ nspOid = ((Form_pg_opclass) GETSTRUCT(tuple))->opcnamespace; -+ break; -+ -+ case OperatorRelationId: -+ nspOid = ((Form_pg_operator) GETSTRUCT(tuple))->oprnamespace; -+ break; -+ -+ case OperatorFamilyRelationId: -+ nspOid = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfnamespace; -+ break; -+ -+ case ProcedureRelationId: -+ nspOid = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; -+ break; -+ -+ case TSConfigRelationId: -+ nspOid = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgnamespace; -+ break; -+ -+ case TSDictionaryRelationId: -+ nspOid = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictnamespace; -+ break; -+ -+ case TSParserRelationId: -+ nspOid = ((Form_pg_ts_parser) GETSTRUCT(tuple))->prsnamespace; -+ break; -+ -+ case TSTemplateRelationId: -+ nspOid = ((Form_pg_ts_template) GETSTRUCT(tuple))->tmplnamespace; -+ break; -+ -+ default: -+ /* no specific namespace */ -+ nspOid = InvalidOid; -+ break; -+ } -+ -+ return nspOid; -+} -+ -+/* -+ * sepgsqlTupleAuditName -+ * -+ * It returns an OID of the namespace, if the given system object is -+ * deployed under a certain namespace. -+ */ -+void -+sepgsqlTupleAuditName(Oid relid, HeapTuple tuple, char *auname_buf) -+{ -+ char *name; -+ Oid extid; -+ -+ switch (relid) -+ { -+ case AccessMethodRelationId: -+ name = NameStr(((Form_pg_am) GETSTRUCT(tuple))->amname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case AttributeRelationId: -+ name = NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname); -+ extid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; -+ sprintf(audit_name, "%s.%s", name, extid); -+ return; -+ -+ case AuthIdRelationId: -+ name = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case ConversionRelationId: -+ name = NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case DatabaseRelationId: -+ name = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case ForeignDataWrapperRelationId: -+ name = NameStr(((Form_pg_foreign_data_wrapper) GETSTRUCT(tuple))->fdwname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case ForeignServerRelationId: -+ name = NameStr(((Form_pg_foreign_server) GETSTRUCT(tuple))->srvname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case LanguageRelationId: -+ name = NameStr(((Form_pg_language) GETSTRUCT(tuple))->lanname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case NamespaceRelationId: -+ name = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case OperatorClassRelationId: -+ name = NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case OperatorRelationId: -+ name = NameStr(((Form_pg_operator) GETSTRUCT(tuple))->oprname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case OperatorFamilyRelationId: -+ name = NameStr(((Form_pg_opfamily) GETSTRUCT(tuple))->opfname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case ProcedureRelationId: -+ name = NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case RelationRelationId: -+ name = NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case TableSpaceRelationId: -+ name = NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case TSConfigRelationId: -+ name = NameStr(((Form_pg_ts_config) GETSTRUCT(tuple))->cfgname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case TSDictionaryRelationId: -+ name = NameStr(((Form_pg_ts_dict) GETSTRUCT(tuple))->dictname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case TSParserRelationId: -+ name = NameStr(((Form_pg_ts_parser) GETSTRUCT(tuple))->prsname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ case TSTemplateRelationId: -+ name = NameStr(((Form_pg_templace) GETSTRUCT(tuple))->tmplname); -+ strncpy(auname_buf, name, NAMEDATALEN); -+ break; -+ -+ default: -+ /* no auditable name */ -+ auname_buf[0] = '\0'; -+ break; -+ } -+} -+#endif -diff --git a/src/backend/security/sepgsql/policy/Makefile b/src/backend/security/sepgsql/policy/Makefile -new file mode 100644 -index 0000000..fc71b0c ---- /dev/null -+++ b/src/backend/security/sepgsql/policy/Makefile -@@ -0,0 +1,28 @@ -+# -+# Makefile for SE-PostgreSQL security policy module -+# -+top_builddir = ../../../../.. -+include $(top_builddir)/src/Makefile.global -+ -+POLICY_BASEDIR := $(DESTDIR)/usr/share/selinux -+POLICY_MAKEFILE := $(POLICY_BASEDIR)/devel/Makefile -+POLICY_INSTDIR := $(POLICY_BASEDIR)/packages -+PREFIX_RULE := "s/%%__prefix__%%/$(shell echo $(prefix)|sed 's/\//\\\//g')/g" -+BINDIR_RULE := "s/%%__bindir__%%/$(shell echo $(bindir)|sed 's/\//\\\//g')/g" -+LIBDIR_RULE := "s/%%__libdir__%%/$(shell echo $(pkglibdir)|sed 's/\//\\\//g')/g" -+ -+all: sepostgresql-devel.pp -+ -+install: all -+ test -d $(POLICY_INSTDIR) || mkdir -p $(POLICY_INSTDIR) -+ install -p -m 0644 sepostgresql-devel.pp $(POLICY_INSTDIR) -+ -+sepostgresql-devel.pp: sepostgresql-devel.te sepostgresql-devel.fc -+ $(MAKE) -f $(POLICY_MAKEFILE) -+ -+sepostgresql-devel.fc: sepostgresql-devel.fc.template -+ cat $< | sed -e $(PREFIX_RULE) -e $(BINDIR_RULE) -e $(LIBDIR_RULE) > $@ -+ -+clean: -+ $(MAKE) -f $(POLICY_MAKEFILE) clean -+ rm -f *.fc -diff --git a/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template b/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template -new file mode 100644 -index 0000000..380ada4 ---- /dev/null -+++ b/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template -@@ -0,0 +1,12 @@ -+# -+# SE-PostgreSQL install path -+# -+%%__prefix__%%(/.*)? -- gen_context(system_u:object_r:usr_t,s0) -+ -+%%__bindir__%%/(se)?postgres -- gen_context(system_u:object_r:postgresql_exec_t,s0) -+%%__bindir__%%/(se)?pg_ctl -- gen_context(system_u:object_r:initrc_exec_t,s0) -+%%__bindir__%%/initdb(\.sepgsql)? -- gen_context(system_u:object_r:postgresql_exec_t,s0) -+%%__bindir__%%(/.*)? -- gen_context(system_u:object_r:bin_t,s0) -+ -+%%__libdir__%%(/.*)? -- gen_context(system_u:object_r:lib_t,s0) -+ -diff --git a/src/backend/security/sepgsql/policy/sepostgresql-devel.te b/src/backend/security/sepgsql/policy/sepostgresql-devel.te -new file mode 100644 -index 0000000..4b960a8 ---- /dev/null -+++ b/src/backend/security/sepgsql/policy/sepostgresql-devel.te -@@ -0,0 +1,123 @@ -+policy_module(sepostgresql-devel, 3.29) -+ -+gen_require(` -+ class db_database all_db_database_perms; -+ class db_table all_db_table_perms; -+ class db_procedure all_db_procedure_perms; -+ class db_column all_db_column_perms; -+ class db_tuple all_db_tuple_perms; -+ class db_blob all_db_blob_perms; -+ -+ attribute sepgsql_client_type; -+ attribute sepgsql_unconfined_type; -+ -+ attribute sepgsql_database_type; -+ attribute sepgsql_table_type; -+ attribute sepgsql_sysobj_table_type; -+ attribute sepgsql_procedure_type; -+ attribute sepgsql_blob_type; -+ attribute sepgsql_module_type; -+ -+ # for regression test -+ type bin_t; -+ type user_home_t; -+ type sepgsql_trusted_proc_exec_t; -+ -+ attribute tmpfile; -+') -+ -+################################# -+# -+# Domain for Testcases -+# -+ -+role sepgsql_test_r; -+ -+userdom_unpriv_user_template(sepgsql_test) -+postgresql_role(sepgsql_test_r, sepgsql_test_t) -+ -+allow sepgsql_test_t tmpfile : dir search_dir_perms; -+allow sepgsql_test_t tmpfile : file rw_file_perms; -+ -+optional_policy(` -+ term_write_all_terms(sepgsql_test_t) -+') -+ -+optional_policy(` -+ gen_require(` -+ type unconfined_t; -+ role unconfined_r; -+ ') -+ -+ tunable_policy(`sepgsql_regression_test_mode',` -+ allow unconfined_t sepgsql_test_t : process transition; -+ ') -+ unconfined_rw_pipes(sepgsql_test_t) -+ role unconfined_r types sepgsql_test_t; -+ role unconfined_r types sepgsql_trusted_proc_t; -+') -+ -+################################# -+# -+# SE-PostgreSQL Declarations -+# -+ -+## -+##

-+## Allow to generate auditallow logs -+##

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

-+## Allow to generate auditdeny logs -+##

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

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

-+##
-+gen_tunable(sepgsql_regression_test_mode, false) -+ -+######################################## -+# -+# SE-PostgreSQL audit switch for debugging -+# -+tunable_policy(`sepgsql_enable_auditallow',` -+ auditallow domain sepgsql_database_type : db_database *; -+ auditallow domain sepgsql_table_type : db_table *; -+ auditallow domain sepgsql_table_type : db_column *; -+ auditallow domain sepgsql_table_type : db_tuple { relabelfrom relabelto }; -+ auditallow domain sepgsql_sysobj_table_type : db_tuple *; -+ auditallow domain sepgsql_procedure_type : db_procedure *; -+ auditallow domain sepgsql_blob_type : db_blob *; -+ auditallow domain sepgsql_module_type : db_database { install_module }; -+ auditallow sepgsql_database_type sepgsql_module_type : db_database { load_module }; -+') -+ -+tunable_policy(`! sepgsql_enable_auditdeny',` -+ dontaudit domain sepgsql_database_type : db_database *; -+ dontaudit domain sepgsql_table_type : db_table *; -+ dontaudit domain sepgsql_table_type : db_column *; -+ dontaudit domain sepgsql_table_type : db_tuple { relabelfrom relabelto }; -+ dontaudit domain sepgsql_sysobj_table_type : db_tuple *; -+ dontaudit domain sepgsql_procedure_type : db_procedure *; -+ dontaudit domain sepgsql_blob_type : db_blob *; -+ dontaudit domain sepgsql_module_type : db_database { install_module }; -+ dontaudit sepgsql_database_type sepgsql_module_type : db_database { load_module }; -+') -+ -+######################################## -+# -+# SE-PostgreSQL regression test mode switch -+# -+tunable_policy(`sepgsql_regression_test_mode',` -+ allow sepgsql_client_type user_home_t : db_database { install_module }; -+ allow sepgsql_unconfined_type user_home_t : db_database { install_module }; -+ allow sepgsql_database_type user_home_t : db_database { load_module }; -+') -diff --git a/src/backend/security/sepgsql/selinux.c b/src/backend/security/sepgsql/selinux.c -new file mode 100644 -index 0000000..16f50e0 ---- /dev/null -+++ b/src/backend/security/sepgsql/selinux.c -@@ -0,0 +1,1305 @@ -+/* -+ * src/backend/security/sepgsql/selinux.c -+ * Routines to communicate with SELinux. -+ * -+ * 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 "access/xact.h" -+#include "catalog/pg_security.h" -+#include "lib/stringinfo.h" -+#include "libpq/libpq-be.h" -+#include "libpq/pqsignal.h" -+#include "miscadmin.h" -+#include "security/sepgsql.h" -+#include "storage/fd.h" -+#include "utils/builtins.h" -+#include "utils/memutils.h" -+ -+#include -+#include -+#include -+ -+/* -+ * selinux_catalog -+ * -+ * This static translation lookup table enables to associate a certain -+ * object class/permission name with its internal code, such as -+ * SEPG_CLASS_DB_SCHEMA. -+ * -+ * SELinux requires applications to represent object class and a set of -+ * permissions in code, instead of its name, when we ask SELinux's decision. -+ * -+ * See the definition of security_compute_av(3) API in libselinux. -+ * We need to gives a code of object class, and interpret what permissions -+ * are allowed on the object class from av_decision structure. -+ * Actual values of the code depend on the security policy. In other words, -+ * we cannot know what number is assigned on a certain object class and -+ * permissions. -+ * The string_to_security_class(3) and string_to_av_perm(3) APIs takes -+ * arguments with the name of object class/permission, and returns the -+ * code for the given object class/permissions. -+ * For example, we can know what code is assigned on the "db_table" class -+ * using these functions as follows: -+ * -+ * uint16 tclass_ex = string_to_security_class("db_table"); -+ * -+ * On the other hand, we use an alternative code internally to simplify -+ * the implementation, such as SEPG_CLASS_* for object class. -+ * The following selinux_catalog is used to translate the 'internal' -+ * code and the 'external' code. -+ * -+ * It allows to lookup name of the object class or permission corresponding -+ * to a certain 'internal' code. Then, we can give the name to SELinux's -+ * API to obtain 'external' code which can be used to ask in-kernel SELinux. -+ */ -+static struct -+{ -+ const char *class_name; -+ uint16 class_code; -+ struct -+ { -+ const char *perm_name; -+ uint32 perm_code; -+ } perms[32]; -+} selinux_catalog[] = { -+ { -+ "process", SEPG_CLASS_PROCESS, -+ { -+ {"translation", SEPG_PROCESS__TRANSITION }, -+ {NULL, 0} -+ } -+ }, -+ { -+ "file", SEPG_CLASS_FILE, -+ { -+ {"read", SEPG_FILE__READ }, -+ {"write", SEPG_FILE__WRITE }, -+ {"create", SEPG_FILE__CREATE }, -+ {"getattr", SEPG_FILE__GETATTR }, -+ {NULL, 0} -+ } -+ }, -+ { -+ "dir", SEPG_CLASS_DIR, -+ { -+ {"read", SEPG_DIR__READ }, -+ {"write", SEPG_DIR__WRITE }, -+ {"create", SEPG_DIR__CREATE }, -+ {"getattr", SEPG_DIR__GETATTR }, -+ {NULL,0} -+ } -+ }, -+ { -+ "lnk_file", SEPG_CLASS_LNK_FILE, -+ { -+ {"read", SEPG_LNK_FILE__READ }, -+ {"write", SEPG_LNK_FILE__WRITE }, -+ {"create", SEPG_LNK_FILE__CREATE }, -+ {"getattr", SEPG_LNK_FILE__GETATTR }, -+ {NULL,0} -+ } -+ }, -+ { -+ "chr_file", SEPG_CLASS_CHR_FILE, -+ { -+ {"read", SEPG_CHR_FILE__READ }, -+ {"write", SEPG_CHR_FILE__WRITE }, -+ {"create", SEPG_CHR_FILE__CREATE }, -+ {"getattr", SEPG_CHR_FILE__GETATTR }, -+ {NULL,0} -+ } -+ }, -+ { -+ "blk_file", SEPG_CLASS_BLK_FILE, -+ { -+ {"read", SEPG_BLK_FILE__READ }, -+ {"write", SEPG_BLK_FILE__WRITE }, -+ {"create", SEPG_BLK_FILE__CREATE }, -+ {"getattr", SEPG_BLK_FILE__GETATTR }, -+ {NULL,0} -+ } -+ }, -+ { -+ "sock_file", SEPG_CLASS_SOCK_FILE, -+ { -+ {"read", SEPG_SOCK_FILE__READ }, -+ {"write", SEPG_SOCK_FILE__WRITE }, -+ {"create", SEPG_SOCK_FILE__CREATE }, -+ {"getattr", SEPG_SOCK_FILE__GETATTR }, -+ {NULL,0} -+ } -+ }, -+ { -+ "fifo_file", SEPG_CLASS_FIFO_FILE, -+ { -+ {"read", SEPG_FIFO_FILE__READ }, -+ {"write", SEPG_FIFO_FILE__WRITE }, -+ {"create", SEPG_FIFO_FILE__CREATE }, -+ {"getattr", SEPG_FIFO_FILE__GETATTR }, -+ {NULL, 0UL } -+ } -+ }, -+ { -+ "db_database", SEPG_CLASS_DB_DATABASE, -+ { -+ { "create", SEPG_DB_DATABASE__CREATE }, -+ { "drop", SEPG_DB_DATABASE__DROP }, -+ { "getattr", SEPG_DB_DATABASE__GETATTR }, -+ { "setattr", SEPG_DB_DATABASE__SETATTR }, -+ { "relabelfrom", SEPG_DB_DATABASE__RELABELFROM }, -+ { "relabelto", SEPG_DB_DATABASE__RELABELTO }, -+ { "access", SEPG_DB_DATABASE__ACCESS }, -+ { "load_module", SEPG_DB_DATABASE__LOAD_MODULE }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_schema", SEPG_CLASS_DB_SCHEMA, -+ { -+ { "create", SEPG_DB_SCHEMA__CREATE }, -+ { "drop", SEPG_DB_SCHEMA__DROP }, -+ { "getattr", SEPG_DB_SCHEMA__GETATTR }, -+ { "setattr", SEPG_DB_SCHEMA__SETATTR }, -+ { "relabelfrom", SEPG_DB_SCHEMA__RELABELFROM }, -+ { "relabelto", SEPG_DB_SCHEMA__RELABELTO }, -+ { "search", SEPG_DB_SCHEMA__SEARCH }, -+ { "add_name", SEPG_DB_SCHEMA__ADD_NAME }, -+ { "remove_name", SEPG_DB_SCHEMA__REMOVE_NAME }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_table", SEPG_CLASS_DB_TABLE, -+ { -+ { "create", SEPG_DB_TABLE__CREATE }, -+ { "drop", SEPG_DB_TABLE__DROP }, -+ { "getattr", SEPG_DB_TABLE__GETATTR }, -+ { "setattr", SEPG_DB_TABLE__SETATTR }, -+ { "relabelfrom", SEPG_DB_TABLE__RELABELFROM }, -+ { "relabelto", SEPG_DB_TABLE__RELABELTO }, -+ { "select", SEPG_DB_TABLE__SELECT }, -+ { "update", SEPG_DB_TABLE__UPDATE }, -+ { "insert", SEPG_DB_TABLE__INSERT }, -+ { "delete", SEPG_DB_TABLE__DELETE }, -+ { "lock", SEPG_DB_TABLE__LOCK }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_view", SEPG_CLASS_DB_VIEW, -+ { -+ { "create", SEPG_DB_VIEW__CREATE }, -+ { "drop", SEPG_DB_VIEW__DROP }, -+ { "getattr", SEPG_DB_VIEW__GETATTR }, -+ { "setattr", SEPG_DB_VIEW__SETATTR }, -+ { "relabelfrom", SEPG_DB_VIEW__RELABELFROM }, -+ { "relabelto", SEPG_DB_VIEW__RELABELTO }, -+ { "usage", SEPG_DB_VIEW__USAGE }, -+ { NULL, 0UL } -+ } -+ }, -+ { -+ "db_sequence", SEPG_CLASS_DB_SEQUENCE, -+ { -+ { "create", SEPG_DB_SEQUENCE__CREATE }, -+ { "drop", SEPG_DB_SEQUENCE__DROP }, -+ { "getattr", SEPG_DB_SEQUENCE__GETATTR }, -+ { "setattr", SEPG_DB_SEQUENCE__SETATTR }, -+ { "relabelfrom", SEPG_DB_SEQUENCE__RELABELFROM }, -+ { "relabelto", SEPG_DB_SEQUENCE__RELABELTO }, -+ { "get_value", SEPG_DB_SEQUENCE__GET_VALUE }, -+ { "next_value", SEPG_DB_SEQUENCE__NEXT_VALUE }, -+ { "set_value", SEPG_DB_SEQUENCE__SET_VALUE }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_procedure", SEPG_CLASS_DB_PROCEDURE, -+ { -+ { "create", SEPG_DB_PROCEDURE__CREATE }, -+ { "drop", SEPG_DB_PROCEDURE__DROP }, -+ { "getattr", SEPG_DB_PROCEDURE__GETATTR }, -+ { "setattr", SEPG_DB_PROCEDURE__SETATTR }, -+ { "relabelfrom", SEPG_DB_PROCEDURE__RELABELFROM }, -+ { "relabelto", SEPG_DB_PROCEDURE__RELABELTO }, -+ { "execute", SEPG_DB_PROCEDURE__EXECUTE }, -+ { "entrypoint", SEPG_DB_PROCEDURE__ENTRYPOINT }, -+ { "install", SEPG_DB_PROCEDURE__INSTALL }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_column", SEPG_CLASS_DB_COLUMN, -+ { -+ { "create", SEPG_DB_COLUMN__CREATE }, -+ { "drop", SEPG_DB_COLUMN__DROP }, -+ { "getattr", SEPG_DB_COLUMN__GETATTR }, -+ { "setattr", SEPG_DB_COLUMN__SETATTR }, -+ { "relabelfrom", SEPG_DB_COLUMN__RELABELFROM }, -+ { "relabelto", SEPG_DB_COLUMN__RELABELTO }, -+ { "select", SEPG_DB_COLUMN__SELECT }, -+ { "update", SEPG_DB_COLUMN__UPDATE }, -+ { "insert", SEPG_DB_COLUMN__INSERT }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_tuple", SEPG_CLASS_DB_TUPLE, -+ { -+ { "relabelfrom", SEPG_DB_TUPLE__RELABELFROM }, -+ { "relabelto", SEPG_DB_TUPLE__RELABELTO }, -+ { "select", SEPG_DB_TUPLE__SELECT }, -+ { "update", SEPG_DB_TUPLE__UPDATE }, -+ { "insert", SEPG_DB_TUPLE__INSERT }, -+ { "delete", SEPG_DB_TUPLE__DELETE }, -+ { NULL, 0UL }, -+ } -+ }, -+ { -+ "db_blob", SEPG_CLASS_DB_BLOB, -+ { -+ { "create", SEPG_DB_BLOB__CREATE }, -+ { "drop", SEPG_DB_BLOB__DROP }, -+ { "getattr", SEPG_DB_BLOB__GETATTR }, -+ { "setattr", SEPG_DB_BLOB__SETATTR }, -+ { "relabelfrom", SEPG_DB_BLOB__RELABELFROM }, -+ { "relabelto", SEPG_DB_BLOB__RELABELTO }, -+ { "read", SEPG_DB_BLOB__READ }, -+ { "write", SEPG_DB_BLOB__WRITE }, -+ { "import", SEPG_DB_BLOB__IMPORT }, -+ { "export", SEPG_DB_BLOB__EXPORT }, -+ { NULL, 0UL }, -+ } -+ } -+}; -+ -+/* -+ * GUC option: sepostgresql = [default|enforcing|permissive|disabled] -+ * -+ * SEPGSQL_MODE_DEFAULT : It follows system setting -+ * SEPGSQL_MODE_ENFORCING : Use enforcing mode always -+ * SEPGSQL_MODE_PERMISSIVE : Use permissive mode always -+ * SEPGSQL_MODE_INTERNAL : Internally used mode. Same as permissive mode -+ * except for silence in audit logs -+ * SEPGSQL_MODE_DISABLED : It always disables SE-PgSQL configuration -+ */ -+int sepostgresql_mode; -+ -+/* -+ * userspace access vector cache -+ * -+ * It enables to cache access control decisions in userspace, and minimize -+ * the number of system call invocations. -+ */ -+static MemoryContext AvcMemCtx = NULL; -+ -+#define AVC_HASH_NUM_SLOTS 256 -+#define AVC_HASH_NUM_NODES 180 -+ -+typedef struct _avc_datum -+{ -+ uint32 hash_key; -+ -+ uint16 tclass; -+ sepgsql_sid_t tsid; -+ sepgsql_sid_t nsid; -+ char *tcontext; -+ char *ncontext; -+ -+ uint32 allowed; -+ uint32 auditallow; -+ uint32 auditdeny; -+ bool permissive; -+ -+ bool hot_cache; -+} avc_datum; -+ -+typedef struct _avc_page -+{ -+ struct _avc_page *next; -+ -+ List *slot[AVC_HASH_NUM_SLOTS]; -+ -+ uint32 avc_count; -+ uint32 lru_hint; -+ -+ char scontext[1]; -+} avc_page; -+ -+static avc_page *current_page = NULL; -+ -+static int avc_version; -+ -+/* -+ * selinux_state -+ * -+ * It is deployed on the shared memory region, to show the system -+ * state of SELinux and its security policy. -+ * -+ * The selinux_state->version should be checked prior to avc accesses. -+ * If it does not match with the local avc_version, it means that -+ * system security policy was reloaded or system state (enforcing -+ * or permissive) was changed. -+ * -+ * The state monitoring worker process receives messages from the -+ * kernel using libselinux, and it updates the selinux_state. -+ */ -+struct -+{ -+ int version; -+ -+ bool enforcing; -+} *selinux_state = NULL; -+ -+/* -+ * sepgsqlShmemSize -+ * -+ * It returns required size for shared memory segment -+ */ -+Size -+sepgsqlShmemSize(void) -+{ -+ if (!sepgsqlIsEnabled()) -+ return 0; -+ -+ return sizeof(*selinux_state); -+} -+ -+/* -+ * sepgsqlShmemInit -+ * -+ * It attaches shared memory segment. -+ */ -+static void -+sepgsqlShmemInit(void) -+{ -+ bool found; -+ -+ selinux_state = ShmemInitStruct("SELinux system state", -+ sepgsqlShmemSize(), &found); -+ if (!found) -+ { -+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); -+ -+ selinux_state->version = 0; -+ selinux_state->enforcing = (security_getenforce() > 0); -+ -+ LWLockRelease(SepgsqlAvcLock); -+ } -+} -+ -+/* -+ * sepgsqlIsEnabled -+ * sepgsqlIsEnabledBootstrap -+ * -+ * If it returns true, SE-PgSQL is enabled. Otherwise, it is disabled. -+ */ -+bool -+sepgsqlIsEnabledBootstrap(void) -+{ -+ static int enabled = -1; -+ -+ /* -+ * If sepostgresql = off, it is always disabled. -+ */ -+ if (sepostgresql_mode == SEPGSQL_MODE_DISABLED) -+ return false; -+ -+ /* -+ * SE-PgSQL needs SELinux is enabled on the operating system. -+ * If it is disabled, SE-PgSQL has to be also disabled, even if -+ * 'enforcing' or 'permissive' are specified. -+ */ -+ if (enabled < 0) -+ enabled = is_selinux_enabled(); -+ -+ return enabled > 0 ? true : false; -+} -+ -+bool -+sepgsqlIsEnabled(void) -+{ -+ /* -+ * SE-PgSQL is not ready in bootstraping mode, -+ * except for initial labeling process -+ */ -+ if (IsBootstrapProcessingMode()) -+ return false; -+ -+ return sepgsqlIsEnabledBootstrap(); -+} -+ -+/* -+ * sepgsqlGetEnforce -+ * -+ * It returns true, if SE-PgSQL performs in enforcing mode. -+ * -+ * In enforcing mode, SE-PgSQL performs as expected. It checks permissions -+ * on the required action, and it prevents them if violated. -+ * In permissive mode, SE-PgSQL also checks permissions, but it does not -+ * prevent anything, even if violated. It generates audit logs for access -+ * violations, so we can use this mode to debug security policy itself. -+ */ -+bool -+sepgsqlGetEnforce(void) -+{ -+ if (sepostgresql_mode == SEPGSQL_MODE_DEFAULT) -+ { -+ bool rc; -+ -+ LWLockAcquire(SepgsqlAvcLock, LW_SHARED); -+ rc = selinux_state->enforcing; -+ LWLockRelease(SepgsqlAvcLock); -+ -+ return rc; -+ } -+ else if (sepostgresql_mode == SEPGSQL_MODE_ENFORCING) -+ return true; -+ -+ return false; -+} -+ -+/* -+ * sepgsqlShowMode -+ * -+ * It returns the current performing mode ('selinux_support') -+ * in human readable form. -+ */ -+char * -+sepgsqlShowMode(void) -+{ -+ if (!sepgsqlIsEnabled()) -+ return "disabled"; -+ -+ if (!sepgsqlGetEnforce()) -+ return "permissive"; -+ -+ return "enforcing"; -+} -+ -+/* -+ * sepgsqlGetClientLabel -+ * sepgsqlSetClientLabel -+ * sepgsqlGetServerLabel -+ */ -+static char *clientLabel = NULL; -+ -+char * -+sepgsqlGetClientLabel(void) -+{ -+ if (clientLabel) -+ return clientLabel; -+ -+ if (!MyProcPort) -+ { -+ /* -+ * When this server process was launched in single-user mode, -+ * it does not have any client socket, and the server process also -+ * performs as a client in same time. So, we apply a security context -+ * of the current process as a client's one. -+ * The getcon_raw(3) is an libselinux API to obtain security context -+ * of the current process in raw format. -+ */ -+ if (getprevcon_raw(&clientLabel) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("could not get server's security context"))); -+ } -+ else -+ { -+ /* -+ * Otherwise, SE-PgSQL obtains the security context of the client -+ * process using getpeercon(3). It is an API of SELinux to obtain -+ * the security context of the peer process for the given file -+ * descriptor of the client socket. -+ * For example, a process labeled as "system_u:system_r:httpd_t:s0" -+ * (which is typically apache/httpd) connect to the PgSQL server, -+ * getpeercon_raw() in server side returns the security context -+ * in client side. -+ * If MyProcPort->sock came from unix domain socket, we don't need -+ * any special configuration. OS handles them correctly. -+ * If it is tcp/ip socket, either labeled ipsec or static fallback -+ * context should be configured. -+ * The labeled ipsec is a feature to deliver the security context -+ * of remote peer processes with an enhancement of key exchange -+ * server (racoon). If SELinux is also available in the client host -+ * also, it is the most preferable option. -+ * The static fallback context is a feature to assign an alternative -+ * security context based on the source address and network device -+ * in usage. It can be applied, even if Windows is run on the client. -+ */ -+ if (getpeercon_raw(MyProcPort->sock, &clientLabel) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("could not get client's security context"))); -+ } -+ return clientLabel; -+} -+ -+char * -+sepgsqlSetClientLabel(char *new_label) -+{ -+ char *old_label = clientLabel; -+ avc_page *new_page; -+ int i, length; -+ -+ /* -+ * (1) Set new security context -+ */ -+ clientLabel = new_label; -+ -+ /* -+ * (2) Switch current AVC page -+ */ -+ if (current_page) -+ { -+ new_page = current_page; -+ do { -+ if (strcmp(new_page->scontext, new_label) == 0) -+ { -+ current_page = new_page; -+ return old_label; -+ } -+ new_page = new_page->next; -+ } while (new_page != current_page); -+ } -+ -+ /* Not found, create a new avc_page */ -+ length = sizeof(avc_page) + strlen(new_label); -+ new_page = MemoryContextAllocZero(AvcMemCtx, length); -+ -+ strcpy(new_page->scontext, new_label); -+ for (i=0; i < AVC_HASH_NUM_SLOTS; i++) -+ new_page->slot[i] = NIL; -+ -+ if (!current_page) -+ new_page->next = new_page; -+ else -+ { -+ new_page->next = current_page->next; -+ current_page->next = new_page; -+ } -+ -+ current_page = new_page; -+ -+ /* return old label */ -+ return old_label; -+} -+ -+char * -+sepgsqlGetServerLabel(void) -+{ -+ static char *serverLabel = NULL; -+ -+ if (!serverLabel) -+ { -+ if (getcon_raw(&serverLabel) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("could not get server's security context"))); -+ } -+ return serverLabel; -+} -+ -+/* -+ * sepgsqlAuditLog -+ * -+ * It generates a security audit record. In the default, it writes out -+ * audit records into standard PG's logfile. It also allows to set up -+ * external audit log receiver, such as auditd in Linux, using the -+ * sepgsql_audit_hook. -+ * -+ * SELinux can control what should be audited and should not using -+ * "auditdeny" and "auditallow" rules in the security policy. In the -+ * default, all the access violations are audited, and all the access -+ * allowed are not audited. But we can set up the security policy, so -+ * we can have exceptions. So, it is necessary to follow the suggestion -+ * come from the security policy. (av_decision.auditallow and auditdeny) -+ * -+ * Security audit is an important feature, because it enables us to check -+ * what was happen if we have a security incident. In fact, ISO/IEC15408 -+ * defines several security functionalities for audit features. -+ */ -+static void -+sepgsqlAuditLog(bool denied, char *scontext, char *tcontext, -+ uint16 tclass, uint32 audited, const char *audit_name) -+{ -+ //static int auditfd = -2; -+ StringInfoData buf; -+ const char *tclass_name; -+ const char *perm_name; -+ int i; -+ -+ /* -+ * translation of security contexts to human readable format, -+ * if sepgsql_mcstrans is turned on. -+ */ -+ scontext = sepgsqlTransSecLabelOut(scontext); -+ tcontext = sepgsqlTransSecLabelOut(tcontext); -+ -+ /* lookup name of the object class */ -+ tclass_name = selinux_catalog[tclass].class_name; -+ -+ /* lookup name of the permissions */ -+ initStringInfo(&buf); -+ appendStringInfo(&buf, "{"); -+ -+ for (i=0; selinux_catalog[tclass].perms[i].perm_name; i++) -+ { -+ if (audited & (1UL << i)) -+ { -+ perm_name = selinux_catalog[tclass].perms[i].perm_name; -+ appendStringInfo(&buf, " %s", perm_name); -+ } -+ } -+ appendStringInfo(&buf, " }"); -+ -+ appendStringInfo(&buf, " scontext=%s tcontext=%s tclass=%s", -+ scontext, tcontext, tclass_name); -+ if (audit_name) -+ appendStringInfo(&buf, " name=%s", audit_name); -+ -+ ereport(LOG, -+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -+ errmsg("SELinux: %s %s", -+ (denied ? "denied" : "allowed"), buf.data))); -+} -+ -+/* -+ * computePermsInternal -+ * -+ * It actually asks SELinux what permissions are allowed on a pair of -+ * the security contexts and object class. It also returns what permissions -+ * should be audited on access violation or allowed. -+ * In most cases, subject's security context (scontext) is a client, and -+ * target security context (tcontext) is a database object. -+ * -+ * The access control decision shall be set on the given av_decision. -+ * The av_decision.allowed has a bitmask of SEPG___ -+ * to suggest a set of allowed actions in this object class. -+ */ -+static void -+computePermsInternal(char *scontext, char *tcontext, -+ uint16 tclass, struct av_decision *avd) -+{ -+ const char *tclass_name; -+ security_class_t tclass_ex; -+ struct av_decision avd_ex; -+ int i, deny_unknown = security_deny_unknown(); -+ -+ /* Get external code of the object class*/ -+ Assert(tclass < SEPG_CLASS_MAX); -+ Assert(tclass == selinux_catalog[tclass].class_code); -+ -+ tclass_name = selinux_catalog[tclass].class_name; -+ tclass_ex = string_to_security_class(tclass_name); -+ -+ if (tclass_ex == 0) -+ { -+ /* -+ * If the current security policy does not support permissions -+ * corresponding to database objects, we fill up them with dummy -+ * data. -+ * If security_deny_unknown() returns positive value, undefined -+ * permissions should be denied. Otherwise, allowed -+ */ -+ avd->allowed = (deny_unknown > 0 ? 0 : ~0UL); -+ avd->auditallow = 0UL; -+ avd->auditdeny = ~0UL; -+ avd->flags = 0; -+ -+ return; -+ } -+ -+ /* -+ * Ask SELinux what is allowed set of permissions on a pair of the -+ * security contexts and the given object class. -+ */ -+ if (security_compute_av_flags_raw(scontext, tcontext, -+ tclass_ex, 0, &avd_ex) < 0) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("SELinux could not compute av_decision: " -+ "scontext=%s tcontext=%s tclass=%s", -+ scontext, tcontext, tclass_name))); -+ -+ /* -+ * SELinux returns its access control decision as a set of permissions -+ * represented in external code which depends on run-time environment. -+ * So, we need to translate it to the internal representation before -+ * returning results for the caller. -+ */ -+ memset(avd, 0, sizeof(struct av_decision)); -+ -+ for (i=0; selinux_catalog[tclass].perms[i].perm_name; i++) -+ { -+ access_vector_t perm_code_ex; -+ const char *perm_name = selinux_catalog[tclass].perms[i].perm_name; -+ uint32 perm_code = selinux_catalog[tclass].perms[i].perm_code; -+ -+ perm_code_ex = string_to_av_perm(tclass_ex, perm_name); -+ if (perm_code_ex == 0) -+ { -+ /* fill up undefined permissions */ -+ if (!deny_unknown) -+ avd->allowed |= perm_code; -+ avd->auditdeny |= perm_code; -+ -+ continue; -+ } -+ -+ if (avd_ex.allowed & perm_code_ex) -+ avd->allowed |= perm_code; -+ if (avd_ex.auditallow & perm_code_ex) -+ avd->auditallow |= perm_code; -+ if (avd_ex.auditdeny & perm_code_ex) -+ avd->auditdeny |= perm_code; -+ } -+ -+ return; -+} -+ -+/* -+ * sepgsqlComputePerms -+ * -+ * It makes access control decision communicating with SELinux. -+ * If SELinux does not allow required permissions on a pair of the security -+ * contexts, it raises an error or returns false. -+ * -+ * scontext : The security context of subject. In most cases, it is client. -+ * tcontext : The security context of target database object. -+ * tclass : One of the object class code (SEPG_CLASS_*) declared in the -+ * header file. -+ * required : A bitmap of the required permissions (SEPG___) -+ * declared in the header file. -+ * audit_name : A human readable name of the database object for auditing. -+ * abort : True, if caller want to raise an error on access violation. -+ */ -+extern bool -+sepgsqlComputePerms(char *scontext, char *tcontext, -+ uint16 tclass, uint32 required, -+ const char *audit_name, bool abort) -+{ -+ struct av_decision avd; -+ uint32 denied; -+ uint32 audited; -+ -+ computePermsInternal(scontext, tcontext, tclass, &avd); -+ -+ /* -+ * It logs a security audit record for the given request, if necessary. -+ * When SE-PgSQL performs 'internal' mode, it needs to keep silent. -+ */ -+ denied = required & ~avd.allowed; -+ audited = denied ? (denied & avd.auditdeny) -+ : (required & avd.auditallow); -+ -+ if (audited && sepostgresql_mode != SEPGSQL_MODE_INTERNAL) -+ { -+ sepgsqlAuditLog(!!denied, scontext, tcontext, -+ tclass, audited, audit_name); -+ } -+ -+ /* -+ * If here is no policy violations, or SE-PgSQL performs in permissive -+ * mode, or the client process peforms in permissive domain, it returns -+ * normally with 'true'. -+ */ -+ if (!denied || -+ !sepgsqlGetEnforce() || -+ (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) != 0) -+ return true; -+ -+ /* -+ * Otherwise, it raises an error or returns 'false', depending on the -+ * caller's indication by 'abort'. -+ */ -+ if (abort) -+ ereport(ERROR, -+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -+ errmsg("SELinux: security policy violation"))); -+ -+ return false; -+} -+ -+/* -+ * sepgsqlComputeCreate -+ * -+ * It returns a default security context to be assigned on a new database -+ * object. SELinux compute it based on a combination of client, upper object -+ * which owns the new object and object class. -+ * -+ * For example, when a client (staff_u:staff_r:staff_t:s0) tries to create -+ * a new table within a schema (system_u:object_r:sepgsql_schema_t:s0), -+ * SELinux looks-up its security policy. If it has a special rule on the -+ * combination of these security contexts and object class (db_table), -+ * it returns the security context suggested by the special rule. -+ * Otherwise, it returns the security context of schema, as is. -+ * -+ * We expect the caller already applies sanity/validation checks on the -+ * given security context. -+ * -+ * scontext : The security context of subject. In most cases, it is client. -+ * tcontext : The security context of the parent database object.. -+ * tclass : One of the object class code (SEPG_CLASS_*) declared in the -+ * header file. -+ */ -+char * -+sepgsqlComputeCreate(char *scontext, char *tcontext, uint16 tclass) -+{ -+ security_context_t ncontext; -+ security_class_t tclass_ex; -+ const char *tclass_name; -+ char *result; -+ -+ /* Get external code of the object class*/ -+ Assert(tclass < SEPG_CLASS_MAX); -+ Assert(tclass == selinux_catalog[tclass].class_code); -+ -+ tclass_name = selinux_catalog[tclass].class_name; -+ tclass_ex = string_to_security_class(tclass_name); -+ -+ /* -+ * Ask SELinux what is the default context for the given object class -+ * on a pair of security contexts -+ */ -+ if (security_compute_create_raw(scontext, tcontext, -+ tclass_ex, &ncontext)) -+ ereport(ERROR, -+ (errcode(ERRCODE_INTERNAL_ERROR), -+ errmsg("SELinux could not compute a new context: " -+ "scontext=%s tcontext=%s tclass=%s", -+ scontext, tcontext, tclass_name))); -+ /* -+ * libselinux returns malloc()'ed string, so we need to copy it -+ * on the palloc()'ed region. -+ */ -+ PG_TRY(); -+ { -+ result = pstrdup(ncontext); -+ } -+ PG_CATCH(); -+ { -+ freecon(ncontext); -+ PG_RE_THROW(); -+ } -+ PG_END_TRY(); -+ freecon(ncontext); -+ -+ return result; -+} -+ -+/* -+ * sepgsqlAvcReset -+ * -+ * Invalidate all the cached access control decision -+ */ -+static void -+sepgsqlAvcReset(void) -+{ -+ Assert(AvcMemCtx != NULL); -+ -+ MemoryContextReset(AvcMemCtx); -+ -+ current_page = NULL; -+ -+ sepgsqlSetClientLabel(sepgsqlGetClientLabel()); -+} -+ -+static void -+sepgsqlAvcResetOnAbort(XactEvent event, void *arg) -+{ -+ if (event == XACT_EVENT_ABORT) -+ sepgsqlAvcReset(); -+} -+ -+static void -+sepgsqlAvcResetOnSubAbort(SubXactEvent event, SubTransactionId mySubid, -+ SubTransactionId parentSubid, void *arg) -+{ -+ if (event == SUBXACT_EVENT_ABORT_SUB) -+ sepgsqlAvcReset(); -+} -+ -+/* -+ * sepgsqlAvcCheckValid -+ * -+ * It checks whether the current AVC pages are valid, or not. -+ */ -+static bool -+sepgsqlAvcCheckValid(void) -+{ -+ bool result = true; -+ -+ LWLockAcquire(SepgsqlAvcLock, LW_SHARED); -+ if (avc_version != selinux_state->version) -+ { -+ sepgsqlAvcReset(); -+ -+ /* Copy the current version to local */ -+ avc_version = selinux_state->version; -+ -+ result = false; -+ } -+ LWLockRelease(SepgsqlAvcLock); -+ -+ return result; -+} -+ -+/* -+ * sepgsqlAvcReclaim -+ * -+ * It wipes recently unused AVC entries, if necessary. -+ */ -+static void -+sepgsqlAvcReclaim(avc_page *page) -+{ -+ ListCell *l; -+ avc_datum *cache; -+ -+ while (page->avc_count > AVC_HASH_NUM_NODES - 10) -+ { -+ foreach (l, page->slot[page->lru_hint]) -+ { -+ cache = lfirst(l); -+ -+ if (cache->hot_cache) -+ cache->hot_cache = false; -+ else -+ { -+ list_delete_ptr(page->slot[page->lru_hint], cache); -+ pfree(cache); -+ page->avc_count--; -+ } -+ } -+ page->lru_hint = (page->lru_hint + 1) % AVC_HASH_NUM_SLOTS; -+ } -+} -+ -+/* -+ * sepgsqlAvcMakeEntry -+ * -+ * It makes a new avc entry, and insert it to the given page. -+ */ -+#define avc_hash_key(trelid, tsecid, tclass, nrelid) \ -+ (hash_uint32((trelid) ^ (tsecid) ^ ((tclass) << 3) ^ (nrelid))) -+ -+static avc_datum * -+sepgsqlAvcMakeEntry(avc_page *page, sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) -+{ -+ MemoryContext oldctx; -+ char *scontext; -+ char *tcontext; -+ char *ncontext; -+ avc_datum *cache; -+ uint32 hash_key, index; -+ -+ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass, nrelid); -+ index = hash_key % AVC_HASH_NUM_SLOTS; -+ -+ oldctx = MemoryContextSwitchTo(AvcMemCtx); -+ -+ scontext = page->scontext; -+ tcontext = securityRawSecLabelOut(tsid.relid, tsid.secid); -+ ncontext = sepgsqlComputeCreate(scontext, tcontext, tclass); -+ -+ cache = palloc0(sizeof(avc_datum)); -+ -+ cache->hash_key = hash_key; -+ -+ cache->tclass = tclass; -+ -+ cache->hot_cache = true; -+ cache->tcontext = tcontext; -+ cache->ncontext = ncontext; -+ cache->tsid.relid = tsid.relid; -+ cache->tsid.secid = tsid.secid; -+ cache->nsid.relid = nrelid; -+ -+ if (OidIsValid(nrelid)) -+ cache->nsid.secid = securityRawSecLabelIn(nrelid, ncontext); -+ else -+ cache->nsid.secid = InvalidOid; -+ -+ if (!OidIsValid(nrelid)) -+ { -+ struct av_decision avd; -+ -+ computePermsInternal(scontext, tcontext, tclass, &avd); -+ cache->allowed = avd.allowed; -+ cache->auditallow = avd.auditallow; -+ cache->auditdeny = avd.auditdeny; -+ -+ if (avd.flags & SELINUX_AVD_FLAGS_PERMISSIVE) -+ cache->permissive = true; -+ } -+ -+ if (page->avc_count > AVC_HASH_NUM_NODES) -+ sepgsqlAvcReclaim(page); -+ -+ page->slot[index] = lcons(cache, page->slot[index]); -+ page->avc_count++; -+ -+ MemoryContextSwitchTo(oldctx); -+ -+ return cache; -+} -+ -+/* -+ * sepgsqlAvcLookup -+ * -+ * It lookups required AVC entry -+ */ -+static avc_datum * -+sepgsqlAvcLookup(avc_page *page, sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) -+{ -+ avc_datum *cache = NULL; -+ uint32 hash_key, index; -+ ListCell *l; -+ -+ hash_key = avc_hash_key(tsid.relid, tsid.secid, tclass, nrelid); -+ index = hash_key % AVC_HASH_NUM_SLOTS; -+ -+ foreach (l, page->slot[index]) -+ { -+ cache = lfirst(l); -+ if (cache->hash_key == hash_key && -+ cache->tclass == tclass && -+ cache->tsid.relid == tsid.relid && -+ cache->tsid.secid == tsid.secid && -+ cache->nsid.relid == nrelid) -+ { -+ cache->hot_cache = true; -+ return cache; -+ } -+ } -+ return NULL; -+} -+ -+/* -+ * sepgsqlClientHasPerms -+ * -+ * It checks client's privileges on the given object using avc. -+ */ -+bool -+sepgsqlClientHasPerms(sepgsql_sid_t tsid, -+ uint16 tclass, uint32 required, -+ const char *audit_name, bool abort) -+{ -+ avc_datum *cache; -+ uint32 denied, audited; -+ bool result = true; -+ -+ do { -+ cache = sepgsqlAvcLookup(current_page, tsid, tclass, InvalidOid); -+ if (!cache) -+ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass, InvalidOid); -+ } while (!sepgsqlAvcCheckValid()); -+ -+ denied = required & ~cache->allowed; -+ audited = denied ? (denied & cache->auditdeny) -+ : (required & cache->auditallow); -+ if (audited) -+ { -+ sepgsqlAuditLog(!!denied, -+ current_page->scontext, -+ securityRawSecLabelOut(tsid.relid, tsid.secid), -+ cache->tclass, audited, audit_name); -+ } -+ -+ if (denied) -+ { -+ if (!sepgsqlGetEnforce() || cache->permissive) -+ cache->allowed |= required; /* prevent flood of audit log */ -+ else -+ { -+ if (abort) -+ ereport(ERROR, -+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -+ errmsg("SELinux: security policy violation"))); -+ result = false; -+ } -+ } -+ -+ return result; -+} -+ -+/* -+ * sepgsqlClientCreateSecid -+ * sepgsqlClientCreateLabel -+ */ -+sepgsql_sid_t -+sepgsqlClientCreateSecid(sepgsql_sid_t tsid, uint16 tclass, Oid nrelid) -+{ -+ avc_datum *cache; -+ -+ do { -+ cache = sepgsqlAvcLookup(current_page, tsid, tclass, nrelid); -+ if (!cache) -+ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass, nrelid); -+ } while (!sepgsqlAvcCheckValid()); -+ -+ return cache->nsid; -+} -+ -+security_context_t -+sepgsqlClientCreateLabel(sepgsql_sid_t tsid, uint16 tclass) -+{ -+ avc_datum *cache; -+ -+ do { -+ cache = sepgsqlAvcLookup(current_page, tsid, tclass, InvalidOid); -+ if (!cache) -+ cache = sepgsqlAvcMakeEntry(current_page, tsid, tclass, InvalidOid); -+ } while (!sepgsqlAvcCheckValid()); -+ -+ return cache->ncontext; -+} -+ -+/* -+ * SELinux state monitoring process -+ * -+ * This process is forked from postmaster to monitor the state of SELinux. -+ * SELinux can make a notifier message to userspace object manager via -+ * netlink socket. When it receives the message, it updates selinux_state -+ * structure assigned on shared memory region to make any instance reset -+ * its AVC soon. -+ */ -+static int -+sepgsql_cb_log(int type, const char *fmt, ...) -+{ -+ char *c, buffer[1024]; -+ va_list ap; -+ -+ va_start(ap, fmt); -+ vsnprintf(buffer, sizeof(buffer), fmt, ap); -+ va_end(ap); -+ -+ c = strrchr(buffer, '\n'); -+ if (c) -+ *c = '\0'; -+ -+ ereport(LOG,(errmsg("%s", buffer))); -+ -+ return 0; -+} -+ -+static int -+sepgsql_cb_setenforce(int enforce) -+{ -+ /* switch enforcing/permissive */ -+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); -+ selinux_state->enforcing = (enforce ? true : false); -+ selinux_state->version++; -+ LWLockRelease(SepgsqlAvcLock); -+ -+ return 0; -+} -+ -+static int -+sepgsql_cb_policyload(int seqno) -+{ -+ /* invalidate local avc */ -+ LWLockAcquire(SepgsqlAvcLock, LW_EXCLUSIVE); -+ selinux_state->version++; -+ LWLockRelease(SepgsqlAvcLock); -+ -+ return 0; -+} -+ -+bool -+sepgsqlReceiverStart(void) -+{ -+ return sepgsqlIsEnabled(); -+} -+ -+void -+sepgsqlReceiverMain(void) -+{ -+ union selinux_callback cb; -+ -+ Assert(sepgsqlIsEnabled()); -+ -+#ifdef HAVE_SETSID -+ if (setsid() < 0) -+ elog(FATAL, "setsid() failed: %m"); -+#endif -+ -+ /* -+ * setup the signal handler -+ */ -+ pqinitmask(); -+ pqsignal(SIGHUP, SIG_IGN); -+ pqsignal(SIGINT, SIG_IGN); -+ pqsignal(SIGTERM, exit); -+ pqsignal(SIGQUIT, exit); -+ pqsignal(SIGUSR1, SIG_IGN); -+ pqsignal(SIGUSR2, SIG_IGN); -+ pqsignal(SIGCHLD, SIG_DFL); -+ PG_SETMASK(&UnBlockSig); -+ -+ /* -+ * map shared memory segment -+ */ -+ sepgsqlShmemInit(); -+ -+ ereport(LOG, (errmsg("SELinux: netlink receiver (pid=%u)", getpid()))); -+ -+ /* -+ * setup callback functions from avc_netlink_loop() -+ */ -+ cb.func_log = sepgsql_cb_log; -+ selinux_set_callback(SELINUX_CB_LOG, cb); -+ cb.func_setenforce = sepgsql_cb_setenforce; -+ selinux_set_callback(SELINUX_CB_SETENFORCE, cb); -+ cb.func_policyload = sepgsql_cb_policyload; -+ selinux_set_callback(SELINUX_CB_POLICYLOAD, cb); -+ -+ /* -+ * open netlink socket and wait for messages -+ */ -+ avc_netlink_open(1); -+ -+ avc_netlink_loop(); -+ -+ exit(0); -+} -+ -+/* -+ * sepgsqlInitialize -+ * -+ * It sets up the privilege (security context) of the client and initializes -+ * a few internal stuff. -+ */ -+void -+sepgsqlInitialize(void) -+{ -+ if (!sepgsqlIsEnabled()) -+ return; -+ -+ /* -+ * SE-PgSQL does not prevent anything in single-user mode. -+ */ -+ if (!MyProcPort) -+ sepostgresql_mode = SEPGSQL_MODE_INTERNAL; -+ -+ sepgsqlShmemInit(); -+ -+ AvcMemCtx = AllocSetContextCreate(TopMemoryContext, -+ "SE-PgSQL userspace AVC", -+ ALLOCSET_DEFAULT_MINSIZE, -+ ALLOCSET_DEFAULT_INITSIZE, -+ ALLOCSET_DEFAULT_MAXSIZE); -+ -+ RegisterXactCallback(sepgsqlAvcResetOnAbort, NULL); -+ RegisterSubXactCallback(sepgsqlAvcResetOnSubAbort, NULL); -+ -+ /* -+ * Set client's security context -+ */ -+ sepgsqlSetClientLabel(sepgsqlGetClientLabel()); -+} -diff --git a/src/backend/storage/file/fd.c b/src/backend/storage/file/fd.c -index fd248fc..b24239d 100644 ---- a/src/backend/storage/file/fd.c -+++ b/src/backend/storage/file/fd.c -@@ -1329,6 +1329,13 @@ FileTruncate(File file, off_t offset) - return returnCode; - } - -+int -+FileRawDescriptor(File file) -+{ -+ Assert(FileIsValid(file)); -+ -+ return VfdCache[file].fd; -+} - - /* - * Routines that want to use stdio (ie, FILE*) should use AllocateFile -diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c -index 3022867..cf6fc3d 100644 ---- a/src/backend/storage/ipc/ipci.c -+++ b/src/backend/storage/ipc/ipci.c -@@ -25,6 +25,7 @@ - #include "postmaster/autovacuum.h" - #include "postmaster/bgwriter.h" - #include "postmaster/postmaster.h" -+#include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/ipc.h" - #include "storage/pg_shmem.h" -@@ -119,6 +120,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port) - #ifdef EXEC_BACKEND - size = add_size(size, ShmemBackendArraySize()); - #endif -+ size = add_size(size, sepgsqlShmemSize()); - - /* freeze the addin request size and include it */ - addin_request_allowed = false; -diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c -index a946972..7abf8e3 100644 ---- a/src/backend/storage/large_object/inv_api.c -+++ b/src/backend/storage/large_object/inv_api.c -@@ -197,14 +197,14 @@ getbytealen(bytea *data) - * in use. - */ - Oid --inv_create(Oid lobjId) -+inv_create(Oid lobjId, Oid secid) - { - Oid lobjId_new; - - /* - * Create a new largeobject with empty data pages - */ -- lobjId_new = LargeObjectCreate(lobjId); -+ lobjId_new = LargeObjectCreate(lobjId, secid); - - /* - * dependency on the owner of largeobject -diff --git a/src/backend/tcop/fastpath.c b/src/backend/tcop/fastpath.c -index ab2249a..bcc5448 100644 ---- a/src/backend/tcop/fastpath.c -+++ b/src/backend/tcop/fastpath.c -@@ -26,6 +26,7 @@ - #include "libpq/pqformat.h" - #include "mb/pg_wchar.h" - #include "miscadmin.h" -+#include "security/sepgsql.h" - #include "tcop/fastpath.h" - #include "tcop/tcopprot.h" - #include "utils/acl.h" -@@ -343,11 +344,13 @@ HandleFunctionRequest(StringInfo msgBuf) - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(fip->namespace)); -+ sepgsql_schema_search(fip->namespace, true); - - aclresult = pg_proc_aclcheck(fid, GetUserId(), ACL_EXECUTE); - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_PROC, - get_func_name(fid)); -+ sepgsql_proc_execute(fid); - - /* - * Prepare function call info block and insert arguments. -diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c -index 61b329d..5a379aa 100644 ---- a/src/backend/tcop/pquery.c -+++ b/src/backend/tcop/pquery.c -@@ -573,7 +573,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot) - Assert(pstmt->returningLists); - portal->tupDesc = - ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), -- false); -+ false, false); - } - - /* -diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c -index 9e82a48..96d25c6 100644 ---- a/src/backend/tcop/utility.c -+++ b/src/backend/tcop/utility.c -@@ -50,6 +50,7 @@ - #include "postmaster/bgwriter.h" - #include "rewrite/rewriteDefine.h" - #include "rewrite/rewriteRemove.h" -+#include "security/sepgsql.h" - #include "storage/fd.h" - #include "tcop/pquery.h" - #include "tcop/utility.h" -@@ -162,6 +163,7 @@ check_xact_readonly(Node *parsetree) - case T_AlterRoleSetStmt: - case T_AlterObjectSchemaStmt: - case T_AlterOwnerStmt: -+ case T_AlterSecLabelStmt: - case T_AlterSeqStmt: - case T_AlterTableStmt: - case T_RenameStmt: -@@ -634,6 +636,10 @@ ProcessUtility(Node *parsetree, - ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree); - break; - -+ case T_AlterSecLabelStmt: -+ ExecAlterSecLabelStmt((AlterSecLabelStmt *) parsetree); -+ break; -+ - case T_AlterTableStmt: - { - List *stmts; -@@ -917,6 +923,7 @@ ProcessUtility(Node *parsetree, - LoadStmt *stmt = (LoadStmt *) parsetree; - - closeAllVfds(); /* probably not necessary... */ -+ - /* Allowed names are restricted if you're not superuser */ - load_file(stmt->filename, !superuser()); - } -@@ -1664,6 +1671,31 @@ CreateCommandTag(Node *parsetree) - } - break; - -+ case T_AlterSecLabelStmt: -+ switch (((AlterSecLabelStmt *) parsetree)->objectType) -+ { -+ case OBJECT_DATABASE: -+ tag = "ALTER DATABASE"; -+ break; -+ case OBJECT_SCHEMA: -+ tag = "ALTER SCHEMA"; -+ break; -+ case OBJECT_TABLE: -+ case OBJECT_COLUMN: -+ tag = "ALTER TABLE"; -+ break; -+ case OBJECT_SEQUENCE: -+ tag = "ALTER SEQUENCE"; -+ break; -+ case OBJECT_FUNCTION: -+ tag = "ALTER FUNCTION"; -+ break; -+ default: -+ tag = "???"; -+ break; -+ } -+ break; -+ - case T_AlterTableStmt: - switch (((AlterTableStmt *) parsetree)->relkind) - { -@@ -2242,6 +2274,10 @@ GetCommandLogLevel(Node *parsetree) - lev = LOGSTMT_DDL; - break; - -+ case T_AlterSecLabelStmt: -+ lev = LOGSTMT_DDL; -+ break; -+ - case T_AlterTableStmt: - lev = LOGSTMT_DDL; - break; -diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c -index e1328dc..7041047 100644 ---- a/src/backend/utils/adt/genfile.c -+++ b/src/backend/utils/adt/genfile.c -@@ -24,6 +24,7 @@ - #include "funcapi.h" - #include "miscadmin.h" - #include "postmaster/syslogger.h" -+#include "security/sepgsql.h" - #include "storage/fd.h" - #include "utils/builtins.h" - #include "utils/memutils.h" -@@ -99,6 +100,9 @@ pg_read_file(PG_FUNCTION_ARGS) - - filename = convert_and_check_filename(filename_t); - -+ /* SELinux: check file:{read} permission */ -+ sepgsql_file_read(filename); -+ - if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL) - ereport(ERROR, - (errcode_for_file_access(), -@@ -159,6 +163,8 @@ pg_stat_file(PG_FUNCTION_ARGS) - (errmsg("must be superuser to get file information")))); - - filename = convert_and_check_filename(filename_t); -+ /* SELinux: check file:{getattr} permission */ -+ sepgsql_file_stat(filename); - - if (stat(filename, &fst) < 0) - ereport(ERROR, -diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c -index 81684e3..b681214 100644 ---- a/src/backend/utils/adt/ri_triggers.c -+++ b/src/backend/utils/adt/ri_triggers.c -@@ -39,6 +39,7 @@ - #include "parser/parse_coerce.h" - #include "parser/parse_relation.h" - #include "miscadmin.h" -+#include "security/rowlevel.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/fmgroids.h" -@@ -2627,6 +2628,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) - const char *sep; - int i; - int old_work_mem; -+ int save_rowlv; - char workmembuf[32]; - int spi_result; - SPIPlanPtr qplan; -@@ -2759,6 +2761,11 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) - SPI_result, querybuf.data); - - /* -+ * Disables the Row-level stuff during the internal consistency checks. -+ */ -+ save_rowlv = rowlvSetPerformingMode(ROWLV_BYPASS_MODE); -+ -+ /* - * Run the plan. For safety we force a current snapshot to be used. (In - * serializable mode, this arguably violates serializability, but we - * really haven't got much choice.) We don't need to register the -@@ -2771,6 +2778,9 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) - InvalidSnapshot, - true, false, 1); - -+ /* Restore Row-level stuff */ -+ rowlvSetPerformingMode(save_rowlv); -+ - /* Check result */ - if (spi_result != SPI_OK_SELECT) - elog(ERROR, "SPI_execute_snapshot returned %d", spi_result); -@@ -3265,6 +3275,7 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan, - int spi_result; - Oid save_userid; - int save_sec_context; -+ int save_rowlv, temp_rowlv; - Datum vals[RI_MAX_NUMKEYS * 2]; - char nulls[RI_MAX_NUMKEYS * 2]; - -@@ -3348,12 +3359,19 @@ ri_PerformCheck(RI_QueryKey *qkey, SPIPlanPtr qplan, - SetUserIdAndSecContext(RelationGetForm(query_rel)->relowner, - save_sec_context | SECURITY_LOCAL_USERID_CHANGE); - -+ /* Switch Row-level stuff behavior on FK checks, if necessary */ -+ temp_rowlv = (detectNewRows ? ROWLV_ABORT_MODE : ROWLV_FILTER_MODE); -+ save_rowlv = rowlvSetPerformingMode(temp_rowlv); -+ - /* Finally we can run the query. */ - spi_result = SPI_execute_snapshot(qplan, - vals, nulls, - test_snapshot, crosscheck_snapshot, - false, false, limit); - -+ /* Restore Row-level stuff behavior */ -+ rowlvSetPerformingMode(save_rowlv); -+ - /* Restore UID and security context */ - SetUserIdAndSecContext(save_userid, save_sec_context); - -diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c -index 5bb166d..ef9c2e9 100644 ---- a/src/backend/utils/adt/tid.c -+++ b/src/backend/utils/adt/tid.c -@@ -27,6 +27,7 @@ - #include "libpq/pqformat.h" - #include "miscadmin.h" - #include "parser/parsetree.h" -+#include "security/sepgsql.h" - #include "utils/acl.h" - #include "utils/builtins.h" - #include "utils/rel.h" -@@ -347,6 +348,8 @@ currtid_byreloid(PG_FUNCTION_ARGS) - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_CLASS, - RelationGetRelationName(rel)); -+ /* SELinux checks */ -+ sepgsql_relation_get_transaction_id(RelationGetRelid(rel)); - - if (rel->rd_rel->relkind == RELKIND_VIEW) - return currtid_for_view(rel, tid); -@@ -377,6 +380,8 @@ currtid_byrelname(PG_FUNCTION_ARGS) - if (aclresult != ACLCHECK_OK) - aclcheck_error(aclresult, ACL_KIND_CLASS, - RelationGetRelationName(rel)); -+ /* SELinux checks */ -+ sepgsql_relation_get_transaction_id(RelationGetRelid(rel)); - - if (rel->rd_rel->relkind == RELKIND_VIEW) - return currtid_for_view(rel, tid); -diff --git a/src/backend/utils/adt/trigfuncs.c b/src/backend/utils/adt/trigfuncs.c -index ceab88d..80db58d 100644 ---- a/src/backend/utils/adt/trigfuncs.c -+++ b/src/backend/utils/adt/trigfuncs.c -@@ -76,6 +76,10 @@ suppress_redundant_updates_trigger(PG_FUNCTION_ARGS) - !OidIsValid(HeapTupleHeaderGetOid(newheader))) - HeapTupleHeaderSetOid(newheader, HeapTupleHeaderGetOid(oldheader)); - -+ if (HeapTupleHeaderHasSecid(newheader) && -+ !OidIsValid(HeapTupleHeaderGetSecid(newheader))) -+ HeapTupleHeaderSetSecid(newheader, HeapTupleHeaderGetSecid(oldheader)); -+ - /* if the tuple payload is the same ... */ - if (newtuple->t_len == oldtuple->t_len && - newheader->t_hoff == oldheader->t_hoff && -diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c -index 8fc2d5a..b943e80 100644 ---- a/src/backend/utils/cache/plancache.c -+++ b/src/backend/utils/cache/plancache.c -@@ -859,12 +859,12 @@ PlanCacheComputeResultDesc(List *stmt_list) - if (IsA(node, Query)) - { - query = (Query *) node; -- return ExecCleanTypeFromTL(query->targetList, false); -+ return ExecCleanTypeFromTL(query->targetList, false, false); - } - if (IsA(node, PlannedStmt)) - { - pstmt = (PlannedStmt *) node; -- return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false); -+ return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false, false); - } - /* other cases shouldn't happen, but return NULL */ - break; -@@ -875,13 +875,14 @@ PlanCacheComputeResultDesc(List *stmt_list) - { - query = (Query *) node; - Assert(query->returningList); -- return ExecCleanTypeFromTL(query->returningList, false); -+ return ExecCleanTypeFromTL(query->returningList, false, false); - } - if (IsA(node, PlannedStmt)) - { - pstmt = (PlannedStmt *) node; - Assert(pstmt->returningLists); -- return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false); -+ return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), -+ false, false); - } - /* other cases shouldn't happen, but return NULL */ - break; -diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c -index df0a172..a48d474 100644 ---- a/src/backend/utils/cache/relcache.c -+++ b/src/backend/utils/cache/relcache.c -@@ -55,6 +55,7 @@ - #include "optimizer/prep.h" - #include "optimizer/var.h" - #include "rewrite/rewriteDefine.h" -+#include "security/sepgsql.h" - #include "storage/fd.h" - #include "storage/lmgr.h" - #include "storage/smgr.h" -@@ -865,6 +866,10 @@ RelationBuildDesc(Oid targetRelId, Relation oldrelation) - /* extract reloptions if any */ - RelationParseRelOptions(relation, pg_class_tuple); - -+ /* Fixup relation->rd_att->tdhassecid */ -+ RelationGetDescr(relation)->tdhassecid -+ = securityTupleDescHasSecid(relid, relp->relkind); -+ - /* - * initialize the relation lock manager information - */ -@@ -1460,6 +1465,11 @@ formrdesc(const char *relationName, Oid relationReltype, - RelationGetRelid(relation) = relation->rd_att->attrs[0]->attrelid; - relation->rd_rel->relfilenode = RelationGetRelid(relation); - -+ /* Fixup relation->rd_att->tdhassecid */ -+ RelationGetDescr(relation)->tdhassecid -+ = securityTupleDescHasSecid(RelationGetRelid(relation), -+ RELKIND_RELATION); -+ - /* - * initialize the relation lock manager information - */ -@@ -2749,6 +2759,13 @@ BuildHardcodedDescriptor(int natts, Form_pg_attribute attrs, bool hasoids) - result = CreateTemplateTupleDesc(natts, hasoids); - result->tdtypeid = RECORDOID; /* not right, but we don't care */ - result->tdtypmod = -1; -+ /* -+ * NOTE: we assume the returned TupleDesc is only used for -+ * references to toast'ed data, and it is not delivered to -+ * heap_form_tuple(), so TupleDesc->tdhassecid don't give us -+ * any effect. -+ * We omit to invoke securityTupleDescHasSecid() here. -+ */ - - for (i = 0; i < natts; i++) - { -@@ -3503,6 +3520,11 @@ load_relcache_init_file(void) - rel->rd_options = NULL; - } - -+ /* Fixup rel->rd_att->tdhassecid */ -+ RelationGetDescr(rel)->tdhassecid -+ = securityTupleDescHasSecid(RelationGetRelid(rel), -+ RelationGetForm(rel)->relkind); -+ - /* mark not-null status */ - if (has_not_null) - { -diff --git a/src/backend/utils/fmgr/dfmgr.c b/src/backend/utils/fmgr/dfmgr.c -index 529940c..fe06e2f 100644 ---- a/src/backend/utils/fmgr/dfmgr.c -+++ b/src/backend/utils/fmgr/dfmgr.c -@@ -23,6 +23,7 @@ - #endif - #include "lib/stringinfo.h" - #include "miscadmin.h" -+#include "security/sepgsql.h" - #include "utils/dynamic_loader.h" - #include "utils/hsearch.h" - -@@ -109,6 +110,9 @@ load_external_function(char *filename, char *funcname, - /* Expand the possibly-abbreviated filename to an exact path name */ - fullname = expand_dynamic_library_name(filename); - -+ /* SELinux checks db_database:{load_module} */ -+ sepgsql_database_load_module(MyDatabaseId, fullname); -+ - /* Load the shared library, unless we already did */ - lib_handle = internal_load_library(fullname); - -@@ -149,6 +153,9 @@ load_file(const char *filename, bool restricted) - /* Expand the possibly-abbreviated filename to an exact path name */ - fullname = expand_dynamic_library_name(filename); - -+ /* SELinux checks db_database:{load_module} */ -+ sepgsql_database_load_module(MyDatabaseId, fullname); -+ - /* Unload the library if currently loaded */ - internal_unload_library(fullname); - -diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c -index 9a1da59..76264f3 100644 ---- a/src/backend/utils/fmgr/fmgr.c -+++ b/src/backend/utils/fmgr/fmgr.c -@@ -24,6 +24,7 @@ - #include "miscadmin.h" - #include "nodes/nodeFuncs.h" - #include "pgstat.h" -+#include "security/sepgsql.h" - #include "utils/builtins.h" - #include "utils/fmgrtab.h" - #include "utils/guc.h" -@@ -232,6 +233,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, - */ - if (!ignore_security && - (procedureStruct->prosecdef || -+ sepgsql_proc_entrypoint(procedureTuple) || - !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig))) - { - finfo->fn_addr = fmgr_security_definer; -@@ -860,6 +862,7 @@ struct fmgr_security_definer_cache - { - FmgrInfo flinfo; /* lookup info for target function */ - Oid userid; /* userid to set, or InvalidOid */ -+ char *seclabel; /* security label to set, or NULL */ - ArrayType *proconfig; /* GUC values to set, or NULL */ - }; - -@@ -881,6 +884,7 @@ fmgr_security_definer(PG_FUNCTION_ARGS) - FmgrInfo *save_flinfo; - Oid save_userid; - int save_sec_context; -+ char *save_label = NULL; - volatile int save_nestlevel; - PgStat_FunctionCallUsage fcusage; - -@@ -910,6 +914,9 @@ fmgr_security_definer(PG_FUNCTION_ARGS) - if (procedureStruct->prosecdef) - fcache->userid = procedureStruct->proowner; - -+ fcache->seclabel -+ = sepgsql_proc_trusted(tuple, fcinfo->flinfo->fn_mcxt); -+ - datum = SysCacheGetAttr(PROCOID, tuple, Anum_pg_proc_proconfig, - &isnull); - if (!isnull) -@@ -936,6 +943,8 @@ fmgr_security_definer(PG_FUNCTION_ARGS) - if (OidIsValid(fcache->userid)) - SetUserIdAndSecContext(fcache->userid, - save_sec_context | SECURITY_LOCAL_USERID_CHANGE); -+ if (fcache->seclabel) -+ save_label = sepgsqlSetClientLabel(fcache->seclabel); - - if (fcache->proconfig) - { -@@ -983,6 +992,8 @@ fmgr_security_definer(PG_FUNCTION_ARGS) - AtEOXact_GUC(true, save_nestlevel); - if (OidIsValid(fcache->userid)) - SetUserIdAndSecContext(save_userid, save_sec_context); -+ if (fcache->seclabel) -+ sepgsqlSetClientLabel(save_label); - - return result; - } -diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c -index 327ba7c..0fc1c8d 100644 ---- a/src/backend/utils/init/postinit.c -+++ b/src/backend/utils/init/postinit.c -@@ -32,6 +32,7 @@ - #include "pgstat.h" - #include "postmaster/autovacuum.h" - #include "postmaster/postmaster.h" -+#include "security/sepgsql.h" - #include "storage/backendid.h" - #include "storage/bufmgr.h" - #include "storage/fd.h" -@@ -201,7 +202,7 @@ CheckMyDatabase(const char *name, bool am_superuser) - name))); - - /* -- * Check privilege to connect to the database. (The am_superuser test -+ * Check privilege to connect to the database. (The am_superuser test - * is redundant, but since we have the flag, might as well check it - * and save a few cycles.) - */ -@@ -213,6 +214,9 @@ CheckMyDatabase(const char *name, bool am_superuser) - errmsg("permission denied for database \"%s\"", name), - errdetail("User does not have CONNECT privilege."))); - -+ /* SELinux: db_database:{access} */ -+ sepgsql_database_access(MyDatabaseId); -+ - /* - * Check connection limit for this database. - * -@@ -607,6 +611,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, - /* set up ACL framework (so CheckMyDatabase can check permissions) */ - initialize_acl(); - -+ /* Initialize SE-PostgreSQL */ -+ sepgsqlInitialize(); -+ - /* - * Read the real pg_database row for our database, check permissions and - * set up database-specific GUC settings. We can't do this until all the -diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c -index db1d933..568ef80 100644 ---- a/src/backend/utils/misc/guc.c -+++ b/src/backend/utils/misc/guc.c -@@ -57,6 +57,7 @@ - #include "postmaster/syslogger.h" - #include "postmaster/walwriter.h" - #include "regex/regex.h" -+#include "security/sepgsql.h" - #include "storage/bufmgr.h" - #include "storage/fd.h" - #include "tcop/tcopprot.h" -@@ -257,6 +258,18 @@ static const struct config_enum_entry isolation_level_options[] = { - {NULL, 0} - }; - -+#ifdef HAVE_SELINUX -+static const struct config_enum_entry sepostgresql_mode_options [] = { -+ {"on", SEPGSQL_MODE_DEFAULT, true}, -+ {"off", SEPGSQL_MODE_DISABLED, true}, -+ {"default", SEPGSQL_MODE_DEFAULT, false}, -+ {"permissive", SEPGSQL_MODE_PERMISSIVE, false}, -+ {"enforcing", SEPGSQL_MODE_ENFORCING, false}, -+ {"disabled", SEPGSQL_MODE_DISABLED, false}, -+ {NULL, 0} -+}; -+#endif -+ - static const struct config_enum_entry session_replication_role_options[] = { - {"origin", SESSION_REPLICATION_ROLE_ORIGIN, false}, - {"replica", SESSION_REPLICATION_ROLE_REPLICA, false}, -@@ -1221,6 +1234,24 @@ static struct config_bool ConfigureNamesBool[] = - &IgnoreSystemIndexes, - false, NULL, NULL - }, -+#ifdef HAVE_SELINUX -+ { -+ {"sepostgresql_row_level", PGC_POSTMASTER, CONN_AUTH_SECURITY, -+ gettext_noop("Row-level access controls on SE-PostgreSQL"), -+ NULL, -+ }, -+ &sepostgresql_row_level, -+ true, NULL, NULL -+ }, -+ { -+ {"sepostgresql_mcstrans", PGC_USERSET, CONN_AUTH_SECURITY, -+ gettext_noop("SE-PostgreSQL uses mcstrans on printing security labels"), -+ NULL, -+ }, -+ &sepostgresql_mcstrans, -+ true, NULL, NULL -+ }, -+#endif - - { - {"lo_compat_privileges", PGC_SUSET, COMPAT_OPTIONS_PREVIOUS, -@@ -2640,7 +2671,17 @@ static struct config_enum ConfigureNamesEnum[] = - ®ex_flavor, - REG_ADVANCED, regex_flavor_options, NULL, NULL - }, -- -+#ifdef HAVE_SELINUX -+ { -+ {"sepostgresql", PGC_POSTMASTER, CONN_AUTH_SECURITY, -+ gettext_noop("SE-PostgreSQL performing mode"), -+ NULL, -+ }, -+ &sepostgresql_mode, -+ SEPGSQL_MODE_DISABLED, sepostgresql_mode_options, -+ NULL, sepgsqlShowMode -+ }, -+#endif - { - {"session_replication_role", PGC_SUSET, CLIENT_CONN_STATEMENT, - gettext_noop("Sets the session's behavior for triggers and rewrite rules."), -diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample -index 85acc4e..e298197 100644 ---- a/src/backend/utils/misc/postgresql.conf.sample -+++ b/src/backend/utils/misc/postgresql.conf.sample -@@ -51,7 +51,7 @@ - - - #------------------------------------------------------------------------------ --# CONNECTIONS AND AUTHENTICATION -+# CONNECTIONS, AUTHENTICATION AND SECURITY - #------------------------------------------------------------------------------ - - # - Connection Settings - -@@ -95,7 +95,7 @@ - # 0 selects the system default - #tcp_keepalives_count = 0 # TCP_KEEPCNT; - # 0 selects the system default -- -+#sepostgresql = off # SE-PostgreSQL support - - #------------------------------------------------------------------------------ - # RESOURCE USAGE (except WAL) -diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c -index 1fcf590..620ac5c 100644 ---- a/src/bin/initdb/initdb.c -+++ b/src/bin/initdb/initdb.c -@@ -87,6 +87,7 @@ static bool debug = false; - static bool noclean = false; - static bool show_setting = false; - static char *xlog_dir = ""; -+static bool enable_selinux = false; - - - /* internal vars */ -@@ -1205,6 +1206,13 @@ setup_config(void) - "#default_text_search_config = 'pg_catalog.simple'", - repltok); - -+ if (enable_selinux) -+ { -+ strcpy(repltok, "sepostgresql = on"); -+ conflines = replace_token(conflines, -+ "#sepostgresql = off", repltok); -+ } -+ - snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data); - - writefile(path, conflines); -@@ -2444,6 +2452,7 @@ usage(const char *progname) - printf(_(" -U, --username=NAME database superuser name\n")); - printf(_(" -W, --pwprompt prompt for a password for the new superuser\n")); - printf(_(" -X, --xlogdir=XLOGDIR location for the transaction log directory\n")); -+ printf(_(" --enable-selinux enables SELinux support, if compiled\n")); - printf(_("\nLess commonly used options:\n")); - printf(_(" -d, --debug generate lots of debugging output\n")); - printf(_(" -L DIRECTORY where to find the input files\n")); -@@ -2479,6 +2488,7 @@ main(int argc, char *argv[]) - {"auth", required_argument, NULL, 'A'}, - {"pwprompt", no_argument, NULL, 'W'}, - {"pwfile", required_argument, NULL, 9}, -+ {"enable-selinux", no_argument, NULL, 10}, - {"username", required_argument, NULL, 'U'}, - {"help", no_argument, NULL, '?'}, - {"version", no_argument, NULL, 'V'}, -@@ -2595,6 +2605,9 @@ main(int argc, char *argv[]) - case 9: - pwfilename = xstrdup(optarg); - break; -+ case 10: -+ enable_selinux = true; -+ break; - case 's': - show_setting = true; - break; -diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c -index 57b5f7d..4e48601 100644 ---- a/src/bin/pg_dump/pg_dump.c -+++ b/src/bin/pg_dump/pg_dump.c -@@ -112,6 +112,8 @@ static int disable_dollar_quoting = 0; - static int dump_inserts = 0; - static int column_inserts = 0; - -+/* flag to turn on/off security_context */ -+static int security_context = 0; - - static void help(const char *progname); - static void expand_schema_name_patterns(SimpleStringList *patterns, -@@ -277,6 +279,7 @@ main(int argc, char **argv) - {"no-tablespaces", no_argument, &outputNoTablespaces, 1}, - {"role", required_argument, NULL, 3}, - {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, -+ {"security-context", no_argument, &security_context, 1}, - - {NULL, 0, NULL, 0} - }; -@@ -425,6 +428,8 @@ main(int argc, char **argv) - outputNoTablespaces = 1; - else if (strcmp(optarg, "use-set-session-authorization") == 0) - use_setsessauth = 1; -+ else if (strcmp(optarg, "security-context") == 0) -+ security_context = 1; - else - { - fprintf(stderr, -@@ -573,6 +578,28 @@ main(int argc, char **argv) - std_strings = PQparameterStatus(g_conn, "standard_conforming_strings"); - g_fout->std_strings = (std_strings && strcmp(std_strings, "on") == 0); - -+ /* Check availability of SE-PostgreSQL */ -+ if (security_context > 0) -+ { -+ PGresult *res; -+ -+ res = PQexec(g_conn, "SHOW sepostgresql"); -+ if (PQresultStatus(res) != PGRES_TUPLES_OK || -+ PQntuples(res) != 1 || -+ strcmp(PQgetvalue(res, 0, 0), "on") != 0) -+ { -+ write_msg(NULL, "SE-PostgreSQL is not available now."); -+ exit(1); -+ } -+ } -+ -+ /* -+ * It needs to force column insertion mode, when --inserts -+ * and either --security-label or --security-acl is given. -+ */ -+ if (security_context > 0 && dump_inserts) -+ column_inserts = 1; -+ - /* Set the role if requested */ - if (use_role && g_fout->remoteVersion >= 80100) - { -@@ -826,6 +853,8 @@ help(const char *progname) - printf(_(" --use-set-session-authorization\n" - " use SET SESSION AUTHORIZATION commands instead of\n" - " ALTER OWNER commands to set ownership\n")); -+ printf(_(" --security-label dump SE-PostgreSQL security labels\n")); -+ printf(_(" --security-acl dump row-level database ACLs\n")); - - printf(_("\nConnection options:\n")); - printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); -@@ -1227,7 +1256,8 @@ dumpTableData_insert(Archive *fout, void *dcontext) - if (fout->remoteVersion >= 70100) - { - appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " -- "SELECT * FROM ONLY %s", -+ "SELECT %s* FROM ONLY %s", -+ (security_context > 0 ? "security_context, " : ""), - fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, - classname)); - } -@@ -1583,7 +1613,8 @@ dumpDatabase(Archive *AH) - i_collate, - i_ctype, - i_frozenxid, -- i_tablespace; -+ i_tablespace, -+ i_seclabel; - CatalogId dbCatId; - DumpId dbDumpId; - const char *datname, -@@ -1591,7 +1622,8 @@ dumpDatabase(Archive *AH) - *encoding, - *collate, - *ctype, -- *tablespace; -+ *tablespace, -+ *seclabel; - uint32 frozenxid; - - datname = PQdb(g_conn); -@@ -1610,11 +1642,12 @@ dumpDatabase(Archive *AH) - "pg_encoding_to_char(encoding) AS encoding, " - "datcollate, datctype, datfrozenxid, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " -- "shobj_description(oid, 'pg_database') AS description " -- -+ "shobj_description(oid, 'pg_database') AS description, " -+ "%s as security_context " - "FROM pg_database " - "WHERE datname = ", -- username_subquery); -+ username_subquery, -+ security_context ? "security_context" : "NULL"); - appendStringLiteralAH(dbQry, datname, AH); - } - else if (g_fout->remoteVersion >= 80200) -@@ -1624,8 +1657,8 @@ dumpDatabase(Archive *AH) - "pg_encoding_to_char(encoding) AS encoding, " - "NULL AS datcollate, NULL AS datctype, datfrozenxid, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " -- "shobj_description(oid, 'pg_database') AS description " -- -+ "shobj_description(oid, 'pg_database') AS description, " -+ "NULL as security_context " - "FROM pg_database " - "WHERE datname = ", - username_subquery); -@@ -1637,7 +1670,8 @@ dumpDatabase(Archive *AH) - "(%s datdba) AS dba, " - "pg_encoding_to_char(encoding) AS encoding, " - "NULL AS datcollate, NULL AS datctype, datfrozenxid, " -- "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace " -+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " -+ "NULL as security_context " - "FROM pg_database " - "WHERE datname = ", - username_subquery); -@@ -1650,7 +1684,8 @@ dumpDatabase(Archive *AH) - "pg_encoding_to_char(encoding) AS encoding, " - "NULL AS datcollate, NULL AS datctype, " - "0 AS datfrozenxid, " -- "NULL AS tablespace " -+ "NULL AS tablespace, " -+ "NULL AS security_context " - "FROM pg_database " - "WHERE datname = ", - username_subquery); -@@ -1665,7 +1700,8 @@ dumpDatabase(Archive *AH) - "pg_encoding_to_char(encoding) AS encoding, " - "NULL AS datcollate, NULL AS datctype, " - "0 AS datfrozenxid, " -- "NULL AS tablespace " -+ "NULL AS tablespace, " -+ "NULL as security_context " - "FROM pg_database " - "WHERE datname = ", - username_subquery); -@@ -1699,6 +1735,7 @@ dumpDatabase(Archive *AH) - i_ctype = PQfnumber(res, "datctype"); - i_frozenxid = PQfnumber(res, "datfrozenxid"); - i_tablespace = PQfnumber(res, "tablespace"); -+ i_seclabel = PQfnumber(res, "security_context"); - - dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid)); - dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid)); -@@ -1708,6 +1745,7 @@ dumpDatabase(Archive *AH) - ctype = PQgetvalue(res, 0, i_ctype); - frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid)); - tablespace = PQgetvalue(res, 0, i_tablespace); -+ seclabel = PQgetvalue(res, 0, i_seclabel); - - appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0", - fmtId(datname)); -@@ -1729,6 +1767,9 @@ dumpDatabase(Archive *AH) - if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0) - appendPQExpBuffer(creaQry, " TABLESPACE = %s", - fmtId(tablespace)); -+ if (strlen(seclabel) > 0) -+ appendPQExpBuffer(creaQry, " SECURITY_CONTEXT = '%s'", seclabel); -+ - appendPQExpBuffer(creaQry, ";\n"); - - if (binary_upgrade) -@@ -3230,6 +3271,7 @@ getTables(int *numTables) - int i_reltablespace; - int i_reloptions; - int i_toastreloptions; -+ int i_relseclabel; - - /* Make sure we are in proper schema */ - selectSourceSchema("pg_catalog"); -@@ -3271,7 +3313,8 @@ getTables(int *numTables) - "d.refobjsubid AS owning_col, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " - "array_to_string(c.reloptions, ', ') AS reloptions, " -- "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " -+ "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions, " -+ "%s as security_context " - "FROM pg_class c " - "LEFT JOIN pg_depend d ON " - "(c.relkind = '%c' AND " -@@ -3282,6 +3325,7 @@ getTables(int *numTables) - "WHERE c.relkind in ('%c', '%c', '%c', '%c') " - "ORDER BY c.oid", - username_subquery, -+ security_context ? "c.security_context" : "NULL", - RELKIND_SEQUENCE, - RELKIND_RELATION, RELKIND_SEQUENCE, - RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); -@@ -3303,7 +3347,8 @@ getTables(int *numTables) - "d.refobjsubid AS owning_col, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " - "array_to_string(c.reloptions, ', ') AS reloptions, " -- "NULL AS toast_reloptions " -+ "NULL AS toast_reloptions, " -+ "NULL as security_context " - "FROM pg_class c " - "LEFT JOIN pg_depend d ON " - "(c.relkind = '%c' AND " -@@ -3334,7 +3379,8 @@ getTables(int *numTables) - "d.refobjsubid AS owning_col, " - "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " - "NULL AS reloptions, " -- "NULL AS toast_reloptions " -+ "NULL AS toast_reloptions, " -+ "NULL as security_context " - "FROM pg_class c " - "LEFT JOIN pg_depend d ON " - "(c.relkind = '%c' AND " -@@ -3365,7 +3411,8 @@ getTables(int *numTables) - "d.refobjsubid AS owning_col, " - "NULL AS reltablespace, " - "NULL AS reloptions, " -- "NULL AS toast_reloptions " -+ "NULL AS toast_reloptions, " -+ "NULL as security_context " - "FROM pg_class c " - "LEFT JOIN pg_depend d ON " - "(c.relkind = '%c' AND " -@@ -3392,7 +3439,8 @@ getTables(int *numTables) - "NULL::int4 AS owning_col, " - "NULL AS reltablespace, " - "NULL AS reloptions, " -- "NULL AS toast_reloptions " -+ "NULL AS toast_reloptions, " -+ "NULL AS security_context " - "FROM pg_class " - "WHERE relkind IN ('%c', '%c', '%c') " - "ORDER BY oid", -@@ -3414,7 +3462,8 @@ getTables(int *numTables) - "NULL::int4 AS owning_col, " - "NULL AS reltablespace, " - "NULL AS reloptions, " -- "NULL AS toast_reloptions " -+ "NULL AS toast_reloptions, " -+ "NULL AS security_context " - "FROM pg_class " - "WHERE relkind IN ('%c', '%c', '%c') " - "ORDER BY oid", -@@ -3446,7 +3495,8 @@ getTables(int *numTables) - "NULL::int4 AS owning_col, " - "NULL AS reltablespace, " - "NULL AS reloptions, " -- "NULL AS toast_reloptions " -+ "NULL AS toast_reloptions, " -+ "NULL as security_context " - "FROM pg_class c " - "WHERE relkind IN ('%c', '%c') " - "ORDER BY oid", -@@ -3491,6 +3541,7 @@ getTables(int *numTables) - i_reltablespace = PQfnumber(res, "reltablespace"); - i_reloptions = PQfnumber(res, "reloptions"); - i_toastreloptions = PQfnumber(res, "toast_reloptions"); -+ i_relseclabel = PQfnumber(res, "security_context"); - - if (lockWaitTimeout && g_fout->remoteVersion >= 70300) - { -@@ -3538,6 +3589,7 @@ getTables(int *numTables) - tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace)); - tblinfo[i].reloptions = strdup(PQgetvalue(res, i, i_reloptions)); - tblinfo[i].toast_reloptions = strdup(PQgetvalue(res, i, i_toastreloptions)); -+ tblinfo[i].relseclabel = strdup(PQgetvalue(res, i, i_relseclabel)); - - /* other fields were zeroed above */ - -@@ -4737,6 +4789,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) - int i_attlen; - int i_attalign; - int i_attislocal; -+ int i_attseclabel; - PGresult *res; - int ntups; - bool hasdefaults; -@@ -4781,12 +4834,14 @@ getTableAttrs(TableInfo *tblinfo, int numTables) - "a.attstattarget, a.attstorage, t.typstorage, " - "a.attnotnull, a.atthasdef, a.attisdropped, " - "a.attlen, a.attalign, a.attislocal, " -- "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname " -+ "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " -+ "%s as security_context " - "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " - "ON a.atttypid = t.oid " - "WHERE a.attrelid = '%u'::pg_catalog.oid " - "AND a.attnum > 0::pg_catalog.int2 " - "ORDER BY a.attrelid, a.attnum", -+ security_context ? "a.security_context" : "NULL", - tbinfo->dobj.catId.oid); - } - else if (g_fout->remoteVersion >= 70100) -@@ -4801,7 +4856,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables) - "t.typstorage, a.attnotnull, a.atthasdef, " - "false AS attisdropped, a.attlen, " - "a.attalign, false AS attislocal, " -- "format_type(t.oid,a.atttypmod) AS atttypname " -+ "format_type(t.oid,a.atttypmod) AS atttypname, " -+ "NULL as security_context " - "FROM pg_attribute a LEFT JOIN pg_type t " - "ON a.atttypid = t.oid " - "WHERE a.attrelid = '%u'::oid " -@@ -4818,7 +4874,8 @@ getTableAttrs(TableInfo *tblinfo, int numTables) - "attnotnull, atthasdef, false AS attisdropped, " - "attlen, attalign, " - "false AS attislocal, " -- "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname " -+ "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname, " -+ "NULL as security_context " - "FROM pg_attribute a " - "WHERE attrelid = '%u'::oid " - "AND attnum > 0::int2 " -@@ -4844,6 +4901,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) - i_attlen = PQfnumber(res, "attlen"); - i_attalign = PQfnumber(res, "attalign"); - i_attislocal = PQfnumber(res, "attislocal"); -+ i_attseclabel = PQfnumber(res, "security_context"); - - tbinfo->numatts = ntups; - tbinfo->attnames = (char **) malloc(ntups * sizeof(char *)); -@@ -4856,6 +4914,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) - tbinfo->attlen = (int *) malloc(ntups * sizeof(int)); - tbinfo->attalign = (char *) malloc(ntups * sizeof(char)); - tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool)); -+ tbinfo->attseclabel = (char **) malloc(ntups * sizeof(char *)); - tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool)); - tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *)); - tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool)); -@@ -4881,6 +4940,7 @@ getTableAttrs(TableInfo *tblinfo, int numTables) - tbinfo->attlen[j] = atoi(PQgetvalue(res, j, i_attlen)); - tbinfo->attalign[j] = *(PQgetvalue(res, j, i_attalign)); - tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't'); -+ tbinfo->attseclabel[j] = strdup(PQgetvalue(res, j, i_attseclabel)); - tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't'); - tbinfo->attrdefs[j] = NULL; /* fix below */ - if (PQgetvalue(res, j, i_atthasdef)[0] == 't') -@@ -7131,6 +7191,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - char *proconfig; - char *procost; - char *prorows; -+ char *proseclabel; - char *lanname; - char *rettypename; - int nallargs; -@@ -7167,9 +7228,11 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - "pg_catalog.pg_get_function_result(oid) AS funcresult, " - "proiswindow, provolatile, proisstrict, prosecdef, " - "proconfig, procost, prorows, " -- "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " -+ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " -+ "%s as security_context " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", -+ security_context ? "security_context" : "NULL", - finfo->dobj.catId.oid); - } - else if (g_fout->remoteVersion >= 80300) -@@ -7180,7 +7243,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - "false AS proiswindow, " - "provolatile, proisstrict, prosecdef, " - "proconfig, procost, prorows, " -- "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " -+ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " -+ "NULL AS security_context " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", - finfo->dobj.catId.oid); -@@ -7193,7 +7257,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - "false AS proiswindow, " - "provolatile, proisstrict, prosecdef, " - "null AS proconfig, 0 AS procost, 0 AS prorows, " -- "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " -+ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " -+ "NULL AS security_context " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", - finfo->dobj.catId.oid); -@@ -7208,7 +7273,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - "false AS proiswindow, " - "provolatile, proisstrict, prosecdef, " - "null AS proconfig, 0 AS procost, 0 AS prorows, " -- "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " -+ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " -+ "NULL AS security_context " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", - finfo->dobj.catId.oid); -@@ -7223,7 +7289,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - "false AS proiswindow, " - "provolatile, proisstrict, prosecdef, " - "null AS proconfig, 0 AS procost, 0 AS prorows, " -- "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " -+ "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " -+ "NULL AS security_context " - "FROM pg_catalog.pg_proc " - "WHERE oid = '%u'::pg_catalog.oid", - finfo->dobj.catId.oid); -@@ -7240,7 +7307,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - "proisstrict, " - "false AS prosecdef, " - "null AS proconfig, 0 AS procost, 0 AS prorows, " -- "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " -+ "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname, " -+ "NULL AS security_context " - "FROM pg_proc " - "WHERE oid = '%u'::oid", - finfo->dobj.catId.oid); -@@ -7257,7 +7325,8 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - "false AS proisstrict, " - "false AS prosecdef, " - "NULL AS proconfig, 0 AS procost, 0 AS prorows, " -- "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " -+ "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname, " -+ "NULL AS security_context " - "FROM pg_proc " - "WHERE oid = '%u'::oid", - finfo->dobj.catId.oid); -@@ -7301,6 +7370,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig")); - procost = PQgetvalue(res, 0, PQfnumber(res, "procost")); - prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows")); -+ proseclabel = PQgetvalue(res, 0, PQfnumber(res, "security_context")); - lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname")); - - /* -@@ -7459,6 +7529,9 @@ dumpFunc(Archive *fout, FuncInfo *finfo) - if (prosecdef[0] == 't') - appendPQExpBuffer(q, " SECURITY DEFINER"); - -+ if (security_context > 0 && strlen(proseclabel) > 0) -+ appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'", proseclabel); -+ - /* - * COST and ROWS are emitted only if present and not default, so as not to - * break backwards-compatibility of the dump without need. Keep this code -@@ -9917,6 +9990,17 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) - if (tbinfo->notnull[j] && - (!tbinfo->inhNotNull[j] || binary_upgrade)) - appendPQExpBuffer(q, " NOT NULL"); -+ -+ /* -+ * Security label -- if SE-PostgreSQL enabled -+ */ -+ if (security_context > 0 && -+ strlen(tbinfo->attseclabel[j]) > 0 && -+ strcmp(tbinfo->relseclabel, tbinfo->attseclabel[j]) != 0) -+ appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'", -+ tbinfo->attseclabel[j]); -+ -+ actual_atts++; - } - } - -@@ -9979,6 +10063,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) - appendPQExpBuffer(q, ")"); - } - -+ if (security_context > 0 && strlen(tbinfo->relseclabel) > 0) -+ appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'", tbinfo->relseclabel); -+ - appendPQExpBuffer(q, ";\n"); - - /* -@@ -11550,6 +11637,13 @@ fmtCopyColumnList(const TableInfo *ti) - - appendPQExpBuffer(q, "("); - needComma = false; -+ -+ if (security_context > 0) -+ { -+ appendPQExpBuffer(q, "security_context"); -+ needComma = true; -+ } -+ - for (i = 0; i < numatts; i++) - { - if (attisdropped[i]) -diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h -index a9b3dae..350025c 100644 ---- a/src/bin/pg_dump/pg_dump.h -+++ b/src/bin/pg_dump/pg_dump.h -@@ -228,6 +228,7 @@ typedef struct _tableInfo - bool hasoids; /* does it have OIDs? */ - uint32 frozenxid; /* for restore frozen xid */ - int ncheck; /* # of CHECK expressions */ -+ char *relseclabel; /* security labels of relation */ - /* these two are set only if table is a sequence owned by a column: */ - Oid owning_tab; /* OID of table owning sequence */ - int owning_col; /* attr # of column owning sequence */ -@@ -249,6 +250,7 @@ typedef struct _tableInfo - int *attlen; /* attribute length, used by binary_upgrade */ - char *attalign; /* attribute align, used by binary_upgrade */ - bool *attislocal; /* true if attr has local definition */ -+ char **attseclabel; /* security labels of attributes */ - - /* - * Note: we need to store per-attribute notnull, default, and constraint -diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c -index b265398..5011551 100644 ---- a/src/bin/pg_dump/pg_dumpall.c -+++ b/src/bin/pg_dump/pg_dumpall.c -@@ -69,6 +69,9 @@ static int no_tablespaces = 0; - static int use_setsessauth = 0; - static int server_version; - -+static int security_label = 0; -+static int security_acl = 0; -+ - static FILE *OPF; - static char *filename = NULL; - -@@ -130,6 +133,8 @@ main(int argc, char *argv[]) - {"no-tablespaces", no_argument, &no_tablespaces, 1}, - {"role", required_argument, NULL, 3}, - {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, -+ {"security-label", no_argument, &security_label, 1}, -+ {"security-acl", no_argument, &security_acl, 1}, - - {NULL, 0, NULL, 0} - }; -@@ -283,6 +288,10 @@ main(int argc, char *argv[]) - no_tablespaces = 1; - else if (strcmp(optarg, "use-set-session-authorization") == 0) - use_setsessauth = 1; -+ else if (strcmp(optarg, "security-label") == 0) -+ security_label = 1; -+ else if (strcmp(optarg, "security-acl") == 0) -+ security_acl = 1; - else - { - fprintf(stderr, -@@ -328,6 +337,10 @@ main(int argc, char *argv[]) - appendPQExpBuffer(pgdumpopts, " --no-tablespaces"); - if (use_setsessauth) - appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization"); -+ if (security_label) -+ appendPQExpBuffer(pgdumpopts, " --security-label"); -+ if (security_acl) -+ appendPQExpBuffer(pgdumpopts, " --security-acl"); - - if (optind < argc) - { -@@ -403,6 +416,19 @@ main(int argc, char *argv[]) - } - } - -+ if (security_label > 0) -+ { -+ PGresult *res -+ = PQexec(conn, "SHOW sepostgresql"); -+ if (PQresultStatus(res) != PGRES_TUPLES_OK || -+ PQntuples(res) != 1 || -+ strcmp(PQgetvalue(res, 0, 0), "on") != 0) -+ { -+ fprintf(stderr, "SE-PostgreSQL is not available now."); -+ exit(1); -+ } -+ } -+ - /* - * Open the output file if required, otherwise use stdout - */ -@@ -1130,55 +1156,56 @@ dumpCreateDB(PGconn *conn) - - /* Now collect all the information about databases to dump */ - if (server_version >= 80400) -- res = executeQuery(conn, -- "SELECT datname, " -+ appendPQExpBuffer(buf, "SELECT datname, " - "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "datcollate, datctype, datfrozenxid, " - "datistemplate, datacl, datconnlimit, " -- "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " -+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, " -+ "%s AS security_label " - "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " -- "WHERE datallowconn ORDER BY 1"); -+ "WHERE datallowconn ORDER BY 1", -+ security_label ? "sepgsql_raw_to_trans(datselabel)" : "null::text"); - else if (server_version >= 80100) -- res = executeQuery(conn, -- "SELECT datname, " -+ appendPQExpBuffer(buf, "SELECT datname, " - "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, datfrozenxid, " - "datistemplate, datacl, datconnlimit, " -- "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " -+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, " -+ "null::text " - "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " - "WHERE datallowconn ORDER BY 1"); - else if (server_version >= 80000) -- res = executeQuery(conn, -- "SELECT datname, " -+ appendPQExpBuffer(buf, "SELECT datname, " - "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, datfrozenxid, " - "datistemplate, datacl, -1 as datconnlimit, " -- "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " -+ "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, " -+ "null::text " - "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " - "WHERE datallowconn ORDER BY 1"); - else if (server_version >= 70300) -- res = executeQuery(conn, -- "SELECT datname, " -+ appendPQExpBuffer(buf, "SELECT datname, " - "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, datfrozenxid, " - "datistemplate, datacl, -1 as datconnlimit, " -- "'pg_default' AS dattablespace " -+ "'pg_default' AS dattablespace, " -+ "null::text " - "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " - "WHERE datallowconn ORDER BY 1"); - else if (server_version >= 70100) -- res = executeQuery(conn, -- "SELECT datname, " -+ appendPQExpBuffer(buf, "SELECT datname, " - "coalesce(" - "(select usename from pg_shadow where usesysid=datdba), " - "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " - "datistemplate, '' as datacl, -1 as datconnlimit, " -- "'pg_default' AS dattablespace " -+ "'pg_default' AS dattablespace, " -+ "null::text " - "FROM pg_database d " - "WHERE datallowconn ORDER BY 1"); - else -@@ -1187,18 +1214,20 @@ dumpCreateDB(PGconn *conn) - * Note: 7.0 fails to cope with sub-select in COALESCE, so just deal - * with getting a NULL by not printing any OWNER clause. - */ -- res = executeQuery(conn, -- "SELECT datname, " -+ appendPQExpBuffer(buf, "SELECT datname, " - "(select usename from pg_shadow where usesysid=datdba), " - "pg_encoding_to_char(d.encoding), " - "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " - "'f' as datistemplate, " - "'' as datacl, -1 as datconnlimit, " -- "'pg_default' AS dattablespace " -+ "'pg_default' AS dattablespace, " -+ "null::text " - "FROM pg_database d " - "ORDER BY 1"); - } - -+ res = PQexec(conn, buf->data); -+ - for (i = 0; i < PQntuples(res); i++) - { - char *dbname = PQgetvalue(res, i, 0); -@@ -1211,6 +1240,7 @@ dumpCreateDB(PGconn *conn) - char *dbacl = PQgetvalue(res, i, 7); - char *dbconnlimit = PQgetvalue(res, i, 8); - char *dbtablespace = PQgetvalue(res, i, 9); -+ char *dbseclabel = PQgetvalue(res, i, 9); - char *fdbname; - - fdbname = strdup(fmtId(dbname)); -@@ -1266,6 +1296,10 @@ dumpCreateDB(PGconn *conn) - appendPQExpBuffer(buf, " CONNECTION LIMIT = %s", - dbconnlimit); - -+ if (security_label > 0 && strlen(dbseclabel) > 0) -+ appendPQExpBuffer(buf, " SECURITY_LABEL = '%s'", -+ dbseclabel); -+ - appendPQExpBuffer(buf, ";\n"); - - if (strcmp(dbistemplate, "t") == 0) -diff --git a/src/include/access/htup.h b/src/include/access/htup.h -index f271cbc..adf12c0 100644 ---- a/src/include/access/htup.h -+++ b/src/include/access/htup.h -@@ -163,7 +163,7 @@ typedef HeapTupleHeaderData *HeapTupleHeader; - #define HEAP_HASVARWIDTH 0x0002 /* has variable-width attribute(s) */ - #define HEAP_HASEXTERNAL 0x0004 /* has external stored attribute(s) */ - #define HEAP_HASOID 0x0008 /* has an object-id field */ --/* bit 0x0010 is available */ -+#define HEAP_HASSECID 0x0010 /* has an security-id field */ - #define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */ - #define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */ - #define HEAP_XMAX_SHARED_LOCK 0x0080 /* xmax is shared locker */ -@@ -290,6 +290,9 @@ do { \ - (tup)->t_choice.t_datum.datum_typmod = (typmod) \ - ) - -+#define HeapTupleHeaderHasOid(tup) \ -+ ((tup)->t_infomask & HEAP_HASOID) -+ - #define HeapTupleHeaderGetOid(tup) \ - ( \ - ((tup)->t_infomask & HEAP_HASOID) ? \ -@@ -349,6 +352,25 @@ do { \ - (tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \ - ) - -+#define HeapTupleHeaderHasSecid(tup) \ -+ ((tup)->t_infomask & HEAP_HASSECID) -+ -+#define HeapTupleHeaderGetSecid(tup) \ -+ ( \ -+ HeapTupleHeaderHasSecid(tup) \ -+ ? (*(Oid *)((char *)(tup) + (tup)->t_hoff \ -+ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ -+ - sizeof(Oid))) \ -+ : InvalidOid \ -+ ) -+ -+#define HeapTupleHeaderSetSecid(tup, secid) \ -+ do { \ -+ Assert(HeapTupleHeaderHasSecid(tup)); \ -+ *((Oid *)((char *)(tup) + (tup)->t_hoff \ -+ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ -+ - sizeof(Oid))) = (secid); \ -+ } while(0) - - /* - * BITMAPLEN(NATTS) - -@@ -549,6 +571,14 @@ typedef HeapTupleData *HeapTuple; - #define HeapTupleSetOid(tuple, oid) \ - HeapTupleHeaderSetOid((tuple)->t_data, (oid)) - -+#define HeapTupleHasSecid(tuple) \ -+ HeapTupleHeaderHasSecid((tuple)->t_data) -+ -+#define HeapTupleGetSecid(tuple) \ -+ HeapTupleHeaderGetSecid((tuple)->t_data) -+ -+#define HeapTupleSetSecid(tuple, secid) \ -+ HeapTupleHeaderSetSecid((tuple)->t_data, (secid)) - - /* - * WAL record definitions for heapam.c's WAL operations -diff --git a/src/include/access/sysattr.h b/src/include/access/sysattr.h -index f8fa910..762219f 100644 ---- a/src/include/access/sysattr.h -+++ b/src/include/access/sysattr.h -@@ -25,7 +25,19 @@ - #define MaxTransactionIdAttributeNumber (-5) - #define MaxCommandIdAttributeNumber (-6) - #define TableOidAttributeNumber (-7) --#define FirstLowInvalidHeapAttributeNumber (-8) -+#define SecurityAttributeNumber (-8) -+#define FirstLowInvalidHeapAttributeNumber (-9) - -+/* -+ * Attribute names for the system-defined attributes -+ */ -+#define SelfItemPointerAttributeName "ctid" -+#define ObjectIdAttributeName "oid" -+#define MinTransactionIdAttributeName "xmin" -+#define MinCommandIdAttributeName "cmin" -+#define MaxTransactionIdAttributeName "xmax" -+#define MaxCommandIdAttributeName "cmax" -+#define TableOidAttributeName "tableoid" -+#define SecurityAttributeName "security_context" - - #endif /* SYSATTR_H */ -diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h -index 87c931a..b102e90 100644 ---- a/src/include/access/tupdesc.h -+++ b/src/include/access/tupdesc.h -@@ -75,6 +75,7 @@ typedef struct tupleDesc - Oid tdtypeid; /* composite type ID for tuple type */ - int32 tdtypmod; /* typmod for tuple type */ - bool tdhasoid; /* tuple has oid attribute in its header */ -+ bool tdhassecid; /* tuple has secid attribute in its header */ - int tdrefcount; /* reference count, or -1 if not counting */ - } *TupleDesc; - -diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h -index b43408f..c4342b2 100644 ---- a/src/include/bootstrap/bootstrap.h -+++ b/src/include/bootstrap/bootstrap.h -@@ -70,7 +70,8 @@ typedef enum - BootstrapProcess, - StartupProcess, - BgWriterProcess, -- WalWriterProcess -+ WalWriterProcess, -+ SelinuxReceiverProcess, - } AuxProcType; - - #endif /* BOOTSTRAP_H */ -diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h -index 62b9a52..5c008b9 100644 ---- a/src/include/catalog/dependency.h -+++ b/src/include/catalog/dependency.h -@@ -156,6 +156,9 @@ typedef enum ObjectClass - extern void performDeletion(const ObjectAddress *object, - DropBehavior behavior); - -+extern void performDeletionNoPerms(const ObjectAddress *object, -+ DropBehavior behavior); -+ - extern void performMultipleDeletions(const ObjectAddresses *objects, - DropBehavior behavior); - -diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h -index d618319..e660545 100644 ---- a/src/include/catalog/heap.h -+++ b/src/include/catalog/heap.h -@@ -56,7 +56,8 @@ extern Oid heap_create_with_catalog(const char *relname, - int oidinhcount, - OnCommitAction oncommit, - Datum reloptions, -- bool allow_system_table_mods); -+ bool allow_system_table_mods, -+ Oid *secLabels); - - extern void heap_drop_with_catalog(Oid relid); - -@@ -68,12 +69,14 @@ extern List *heap_truncate_find_FKs(List *relationIds); - - extern void InsertPgAttributeTuple(Relation pg_attribute_rel, - Form_pg_attribute new_attribute, -- CatalogIndexState indstate); -+ CatalogIndexState indstate, -+ Oid new_att_secid); - - extern void InsertPgClassTuple(Relation pg_class_desc, - Relation new_rel_desc, - Oid new_rel_oid, -- Datum reloptions); -+ Datum reloptions, -+ Oid new_rel_secid); - - extern List *AddRelationNewConstraints(Relation rel, - List *newColDefaults, -@@ -103,6 +106,8 @@ extern Form_pg_attribute SystemAttributeDefinition(AttrNumber attno, - 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 --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h -index 0a46611..70024b1 100644 ---- a/src/include/catalog/indexing.h -+++ b/src/include/catalog/indexing.h -@@ -252,6 +252,11 @@ DECLARE_UNIQUE_INDEX(pg_type_oid_index, 2703, on pg_type using btree(oid oid_ops - DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typname name_ops, typnamespace oid_ops)); - #define TypeNameNspIndexId 2704 - -+DECLARE_UNIQUE_INDEX(pg_security_secid_index, 3401, on pg_security using btree(secid oid_ops, datid oid_ops, relid oid_ops)); -+#define SecuritySecidIndexId 3401 -+DECLARE_INDEX(pg_security_secattr_index, 3402, on pg_security using btree(datid oid_ops, relid oid_ops, secattr text_ops)); -+#define SecuritySecattrIndexId 3402 -+ - DECLARE_UNIQUE_INDEX(pg_foreign_data_wrapper_oid_index, 112, on pg_foreign_data_wrapper using btree(oid oid_ops)); - #define ForeignDataWrapperOidIndexId 112 - -diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h -index eaa405f..165b54d 100644 ---- a/src/include/catalog/pg_attribute.h -+++ b/src/include/catalog/pg_attribute.h -@@ -276,6 +276,7 @@ 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_)); -+DATA(insert ( 1247 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); - - /* ---------------- - * pg_proc -@@ -340,6 +341,7 @@ 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_)); -+DATA(insert ( 1255 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); - - /* ---------------- - * pg_attribute -@@ -390,6 +392,7 @@ 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_)); -+DATA(insert ( 1249 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); - - /* ---------------- - * pg_class -@@ -454,6 +457,7 @@ 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_)); -+DATA(insert ( 1259 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); - - /* ---------------- - * pg_index -diff --git a/src/include/catalog/pg_conversion_fn.h b/src/include/catalog/pg_conversion_fn.h -index 83cf657..eecebd3 100644 ---- a/src/include/catalog/pg_conversion_fn.h -+++ b/src/include/catalog/pg_conversion_fn.h -@@ -17,7 +17,7 @@ - extern Oid ConversionCreate(const char *conname, Oid connamespace, - Oid conowner, - int32 conforencoding, int32 contoencoding, -- Oid conproc, bool def); -+ Oid conproc, Oid consecid, bool def); - extern void RemoveConversionById(Oid conversionOid); - extern Oid FindConversion(const char *conname, Oid connamespace); - extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding); -diff --git a/src/include/catalog/pg_largeobject.h b/src/include/catalog/pg_largeobject.h -index 6dd2fb0..b84c0d2 100644 ---- a/src/include/catalog/pg_largeobject.h -+++ b/src/include/catalog/pg_largeobject.h -@@ -51,7 +51,7 @@ typedef FormData_pg_largeobject *Form_pg_largeobject; - #define Anum_pg_largeobject_pageno 2 - #define Anum_pg_largeobject_data 3 - --extern Oid LargeObjectCreate(Oid loid); -+extern Oid LargeObjectCreate(Oid loid, Oid secid); - extern void LargeObjectDrop(Oid loid); - extern void LargeObjectAlterOwner(Oid loid, Oid newOwnerId); - extern bool LargeObjectExists(Oid loid); -diff --git a/src/include/catalog/pg_namespace.h b/src/include/catalog/pg_namespace.h -index 9168079..7e8487a 100644 ---- a/src/include/catalog/pg_namespace.h -+++ b/src/include/catalog/pg_namespace.h -@@ -77,6 +77,6 @@ DESCR("standard public schema"); - /* - * prototypes for functions in pg_namespace.c - */ --extern Oid NamespaceCreate(const char *nspName, Oid ownerId); -+extern Oid NamespaceCreate(const char *nspName, Oid ownerId, Oid nspsecid); - - #endif /* PG_NAMESPACE_H */ -diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h -index 0285acd..c6a5247 100644 ---- a/src/include/catalog/pg_proc.h -+++ b/src/include/catalog/pg_proc.h -@@ -4335,6 +4335,19 @@ DESCR("I/O"); - 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"); - -+/* SE-PostgreSQL related functions */ -+DATA(insert OID = 3415 ( seclabel_to_secid PGNSP PGUID 12 1 0 0 f f f t f v 1 0 26 "2249" _null_ _null_ _null_ _null_ seclabel_to_secid _null_ _null_ _null_ )); -+DATA(insert OID = 3416 ( sepgsql_getcon PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ sepgsql_getcon _null_ _null_ _null_ )); -+DATA(insert OID = 3417 ( sepgsql_server_getcon PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ sepgsql_server_getcon _null_ _null_ _null_ )); -+DATA(insert OID = 3418 ( sepgsql_get_user PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_user _null_ _null_ _null_ )); -+DATA(insert OID = 3419 ( sepgsql_set_user PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_user _null_ _null_ _null_ )); -+DATA(insert OID = 3420 ( sepgsql_get_role PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_role _null_ _null_ _null_ )); -+DATA(insert OID = 3421 ( sepgsql_set_role PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_role _null_ _null_ _null_ )); -+DATA(insert OID = 3422 ( sepgsql_get_type PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_type _null_ _null_ _null_ )); -+DATA(insert OID = 3423 ( sepgsql_set_type PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_type _null_ _null_ _null_ )); -+DATA(insert OID = 3424 ( sepgsql_get_range PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ sepgsql_get_range _null_ _null_ _null_ )); -+DATA(insert OID = 3425 ( sepgsql_set_range PGNSP PGUID 12 1 0 0 f f f t f v 2 0 25 "25 25" _null_ _null_ _null_ _null_ sepgsql_set_range _null_ _null_ _null_ )); -+ - /* enum related procs */ - DATA(insert OID = 3504 ( anyenum_in PGNSP PGUID 12 1 0 0 f f f t f i 1 0 3500 "2275" _null_ _null_ _null_ _null_ anyenum_in _null_ _null_ _null_ )); - DESCR("I/O"); -diff --git a/src/include/catalog/pg_proc_fn.h b/src/include/catalog/pg_proc_fn.h -index e3453f2..c25af6b 100644 ---- a/src/include/catalog/pg_proc_fn.h -+++ b/src/include/catalog/pg_proc_fn.h -@@ -37,7 +37,8 @@ extern Oid ProcedureCreate(const char *procedureName, - List *parameterDefaults, - Datum proconfig, - float4 procost, -- float4 prorows); -+ float4 prorows, -+ Node *proseclabel); - - extern bool function_parse_error_transpose(const char *prosrc); - -diff --git a/src/include/catalog/pg_security.h b/src/include/catalog/pg_security.h -new file mode 100644 -index 0000000..973df01 ---- /dev/null -+++ b/src/include/catalog/pg_security.h -@@ -0,0 +1,89 @@ -+/* -+ * src/include/catalog/pg_security.h -+ * Definition of the security label relation (pg_security) -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+#ifndef PG_SECURITY_H -+#define PG_SECURITY_H -+ -+#include "catalog/genbki.h" -+ -+#include "access/htup.h" -+#include "nodes/parsenodes.h" -+#include "utils/acl.h" -+#include "utils/relcache.h" -+ -+#define SecurityRelationId 3400 -+ -+CATALOG(pg_security,3400) BKI_SHARED_RELATION BKI_WITHOUT_OIDS -+{ -+ /* Identifier of the security attribute */ -+ Oid secid; -+ -+ /* OID of the database which referes the entry */ -+ Oid datid; -+ -+ /* OID of the table which refers the entry */ -+ Oid relid; -+ -+ /* Text representation of security attribute */ -+ text secattr; -+} FormData_pg_security; -+ -+/* -+ * Form_pg_security corresponds to a pointer to a tuple with -+ * the format of pg_security relation. -+ */ -+typedef FormData_pg_security *Form_pg_security; -+ -+/* -+ * Compiler constants for pg_security -+ */ -+#define Natts_pg_security 4 -+#define Anum_pg_security_secid 1 -+#define Anum_pg_security_datid 2 -+#define Anum_pg_security_relid 3 -+#define Anum_pg_security_secattr 4 -+ -+/* -+ * Functions to translate between security label and identifier -+ */ -+extern void -+securityPostBootstrapingMode(void); -+ -+extern void -+securityOnCreateDatabase(Oid src_datid, Oid dst_datid); -+ -+extern void -+securityOnDropDatabase(Oid datid); -+ -+extern bool -+securityTupleDescHasSecid(Oid relid, char relkind); -+ -+extern Oid -+securityRawSecLabelIn(Oid relid, char *seclabel); -+ -+extern char * -+securityRawSecLabelOut(Oid relid, Oid secid); -+ -+extern Oid -+securityTransSecLabelIn(Oid relid, char *seclabel); -+ -+extern char * -+securityTransSecLabelOut(Oid relid, Oid secid); -+ -+extern Datum -+securitySysattSecLabelOut(Oid relid, HeapTuple tuple); -+ -+extern void -+securityReclaimOnDropTable(Oid relid); -+ -+extern void -+seclabelRelationReclaim(Oid relOid); -+ -+extern Datum -+seclabel_to_secid(PG_FUNCTION_ARGS); -+ -+#endif /* PG_SECURITY_H */ -diff --git a/src/include/catalog/toasting.h b/src/include/catalog/toasting.h -index bd6e0cf..b7c39f0 100644 ---- a/src/include/catalog/toasting.h -+++ b/src/include/catalog/toasting.h -@@ -58,5 +58,8 @@ DECLARE_TOAST(pg_database, 2844, 2845); - DECLARE_TOAST(pg_shdescription, 2846, 2847); - #define PgShdescriptionToastTable 2846 - #define PgShdescriptionToastIndex 2847 -+DECLARE_TOAST(pg_security, 3403, 3404); -+#define PgSecurityToastTable 3403 -+#define PgSecurityToastIndex 3404 - - #endif /* TOASTING_H */ -diff --git a/src/include/commands/alter.h b/src/include/commands/alter.h -index ab5d6f4..c332b71 100644 ---- a/src/include/commands/alter.h -+++ b/src/include/commands/alter.h -@@ -19,5 +19,6 @@ - extern void ExecRenameStmt(RenameStmt *stmt); - extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt); - extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt); -+extern void ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt); - - #endif /* ALTER_H */ -diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h -index 0ec23d7..3980056 100644 ---- a/src/include/commands/dbcommands.h -+++ b/src/include/commands/dbcommands.h -@@ -58,6 +58,7 @@ extern void RenameDatabase(const char *oldname, const char *newname); - extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel); - extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt); - extern void AlterDatabaseOwner(const char *dbname, Oid newOwnerId); -+extern void AlterDatabaseSecLabel(const char *dbname, DefElem *seclabel); - - extern Oid get_database_oid(const char *dbname); - extern char *get_database_name(Oid dbid); -diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h -index 4356492..8f903af 100644 ---- a/src/include/commands/defrem.h -+++ b/src/include/commands/defrem.h -@@ -53,6 +53,7 @@ extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType); - extern void RenameFunction(List *name, List *argtypes, const char *newname); - extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId); - extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId); -+extern void AlterFunctionSecLabel(List *name, List *argtypes, DefElem *seclabel); - extern void AlterFunction(AlterFunctionStmt *stmt); - extern void CreateCast(CreateCastStmt *stmt); - extern void DropCast(DropCastStmt *stmt); -diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h -index 5f384a1..1fc113b 100644 ---- a/src/include/commands/schemacmds.h -+++ b/src/include/commands/schemacmds.h -@@ -26,5 +26,6 @@ extern void RemoveSchemaById(Oid schemaOid); - extern void RenameSchema(const char *oldname, const char *newname); - extern void AlterSchemaOwner(const char *name, Oid newOwnerId); - extern void AlterSchemaOwner_oid(Oid schemaOid, Oid newOwnerId); -+extern void AlterSchemaSecLabel(const char *name, DefElem *seclabel); - - #endif /* SCHEMACMDS_H */ -diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h -index 21b067c..2ac7b3c 100644 ---- a/src/include/commands/tablecmds.h -+++ b/src/include/commands/tablecmds.h -@@ -35,6 +35,9 @@ extern void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, - Oid oldNspOid, Oid newNspOid, - bool hasDependEntry); - -+extern void AlterRelationSecLabel(RangeVar *relation, const char *attname, -+ ObjectType objtype, DefElem *seclabel); -+ - extern void CheckTableNotInUse(Relation rel, const char *stmt); - - extern void ExecuteTruncate(TruncateStmt *stmt); -diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h -index 43395e9..09d6148 100644 ---- a/src/include/executor/executor.h -+++ b/src/include/executor/executor.h -@@ -130,7 +130,7 @@ extern TupleHashEntry FindTupleHashEntry(TupleHashTable hashtable, - /* - * prototypes from functions in execJunk.c - */ --extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, -+extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, bool hasseclabel, - TupleTableSlot *slot); - extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, - TupleDesc cleanTupType, -@@ -163,6 +163,7 @@ extern void InitResultRelInfo(ResultRelInfo *resultRelInfo, - bool doInstrument); - extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); - extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); -+extern bool ExecContextForcesSecids(PlanState *planstate, bool *hassecids); - extern void ExecConstraints(ResultRelInfo *resultRelInfo, - TupleTableSlot *slot, EState *estate); - extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, -@@ -216,8 +217,8 @@ extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate); - extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate); - extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, - TupleDesc tupType); --extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid); --extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid); -+extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid, bool hasseclabel); -+extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hasseclabel); - extern TupleDesc ExecTypeFromExprList(List *exprList); - extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); - -diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h -index e40082d..7fd299c 100644 ---- a/src/include/executor/tuptable.h -+++ b/src/include/executor/tuptable.h -@@ -127,6 +127,7 @@ typedef struct TupleTableSlot - MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */ - HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */ - long tts_off; /* saved state for slot_deform_tuple */ -+ Datum tts_seclabel; /* temp storage for the given security_label */ - } TupleTableSlot; - - #define TTS_HAS_PHYSICAL_TUPLE(slot) \ -diff --git a/src/include/libpq/be-fsstubs.h b/src/include/libpq/be-fsstubs.h -index 862b014..03ca71b 100644 ---- a/src/include/libpq/be-fsstubs.h -+++ b/src/include/libpq/be-fsstubs.h -@@ -37,6 +37,9 @@ extern Datum lo_tell(PG_FUNCTION_ARGS); - extern Datum lo_unlink(PG_FUNCTION_ARGS); - extern Datum lo_truncate(PG_FUNCTION_ARGS); - -+extern Datum lo_get_security(PG_FUNCTION_ARGS); -+extern Datum lo_set_security(PG_FUNCTION_ARGS); -+ - /* - * compatibility option for access control - */ -diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h -index 925375b..0285a39 100644 ---- a/src/include/nodes/nodes.h -+++ b/src/include/nodes/nodes.h -@@ -337,6 +337,7 @@ typedef enum NodeTag - T_CreateUserMappingStmt, - T_AlterUserMappingStmt, - T_DropUserMappingStmt, -+ T_AlterSecLabelStmt, - - /* - * TAGS FOR PARSE TREE NODES (parsenodes.h) -diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h -index 487a226..cdb49d4 100644 ---- a/src/include/nodes/parsenodes.h -+++ b/src/include/nodes/parsenodes.h -@@ -463,6 +463,7 @@ typedef struct ColumnDef - Node *raw_default; /* default value (untransformed parse tree) */ - Node *cooked_default; /* default value (transformed expr tree) */ - List *constraints; /* other constraints on column */ -+ Node *secLabel; /* security label of column */ - } ColumnDef; - - /* -@@ -1069,6 +1070,7 @@ typedef struct CreateSchemaStmt - NodeTag type; - char *schemaname; /* the name of the schema to create */ - char *authid; /* the owner of the created schema */ -+ Node *secLabel; /* explicitly specified security label */ - List *schemaElts; /* schema components (list of parsenodes) */ - } CreateSchemaStmt; - -@@ -1335,6 +1337,7 @@ typedef struct CreateStmt - List *options; /* options from WITH clause */ - OnCommitAction oncommit; /* what do we do at COMMIT? */ - char *tablespacename; /* table space to use, or NULL */ -+ List *secLabel; /* explicitly specified security label */ - } CreateStmt; - - /* ---------- -@@ -1639,6 +1642,7 @@ typedef struct CreateSeqStmt - NodeTag type; - RangeVar *sequence; /* the sequence to create */ - List *options; -+ Node *secLabel; - } CreateSeqStmt; - - typedef struct AlterSeqStmt -@@ -1993,6 +1997,20 @@ typedef struct AlterOwnerStmt - char *newowner; /* the new owner */ - } AlterOwnerStmt; - -+/* ---------------------- -+ * Alter Object Security Label Statement -+ * ---------------------- -+ */ -+typedef struct AlterSecLabelStmt -+{ -+ NodeTag type; -+ ObjectType objectType; /* OBJECT_TABLE, OBJECT_COLUMN, etc */ -+ RangeVar *relation; /* in case it's a table */ -+ List *object; /* in case it's some other object */ -+ List *objarg; /* argument types, if applicable */ -+ char *subname; /* column name, if needed */ -+ Node *secLabel; /* the new security label */ -+} AlterSecLabelStmt; - - /* ---------------------- - * Create Rule Statement -diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h -index 23a5117..2525e22 100644 ---- a/src/include/nodes/plannodes.h -+++ b/src/include/nodes/plannodes.h -@@ -16,6 +16,7 @@ - - #include "access/sdir.h" - #include "nodes/bitmapset.h" -+#include "nodes/parsenodes.h" - #include "nodes/primnodes.h" - #include "storage/itemptr.h" - -@@ -239,6 +240,12 @@ typedef struct Scan - { - Plan plan; - Index scanrelid; /* relid is index into the range table */ -+ -+ /* -+ * Row-level access control stuff. Zero means we don't need -+ * to apply row-level access control on the Scan. -+ */ -+ uint32 rowlvPerms; - } Scan; - - /* ---------------- -diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h -index ea48889..6133c38 100644 ---- a/src/include/nodes/relation.h -+++ b/src/include/nodes/relation.h -@@ -383,6 +383,15 @@ typedef struct RelOptInfo - * list just to avoid recomputing the best inner indexscan repeatedly for - * similar outer relations. See comments for InnerIndexscanInfo. - */ -+ -+ /* -+ * Permissions used in Row-level access control features both of DAC -+ * and MAC. The lower 16bit is used for DAC, and rest of upper bits -+ * are used for MAC. When rowlvPerms is zero, so it means we don't need -+ * to apply the row-level stuff on the relation in both of levels. -+ * It can be used as a hint for optimization stuff. -+ */ -+ uint32 rowlvPerms; - } RelOptInfo; - - /* -diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h -index 67e9cb4..df9bb5b 100644 ---- a/src/include/parser/kwlist.h -+++ b/src/include/parser/kwlist.h -@@ -88,6 +88,7 @@ PG_KEYWORD("connection", CONNECTION, UNRESERVED_KEYWORD) - PG_KEYWORD("constraint", CONSTRAINT, RESERVED_KEYWORD) - PG_KEYWORD("constraints", CONSTRAINTS, UNRESERVED_KEYWORD) - PG_KEYWORD("content", CONTENT_P, UNRESERVED_KEYWORD) -+PG_KEYWORD("context", CONTEXT_P, UNRESERVED_KEYWORD) - PG_KEYWORD("continue", CONTINUE_P, UNRESERVED_KEYWORD) - PG_KEYWORD("conversion", CONVERSION_P, UNRESERVED_KEYWORD) - PG_KEYWORD("copy", COPY, UNRESERVED_KEYWORD) -diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in -index 3473227..28301d0 100644 ---- a/src/include/pg_config.h.in -+++ b/src/include/pg_config.h.in -@@ -263,6 +263,9 @@ - /* Define to 1 if you have the header file. */ - #undef HAVE_LDAP_H - -+/* Define to 1 if you have the `audit' library (-laudit). */ -+#undef HAVE_LIBAUDIT -+ - /* Define to 1 if you have the `crypto' library (-lcrypto). */ - #undef HAVE_LIBCRYPTO - -@@ -391,6 +394,9 @@ - /* Define to 1 if you have the header file. */ - #undef HAVE_SECURITY_PAM_APPL_H - -+/* Define to 1 if you enable SELinux support */ -+#undef HAVE_SELINUX -+ - /* Define to 1 if you have the `setproctitle' function. */ - #undef HAVE_SETPROCTITLE - -diff --git a/src/include/security/rowlevel.h b/src/include/security/rowlevel.h -new file mode 100644 -index 0000000..a737a0d ---- /dev/null -+++ b/src/include/security/rowlevel.h -@@ -0,0 +1,44 @@ -+/* -+ * src/include/security/rowlevel.h -+ * Definition of the facility of row-level access controls -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+#ifndef ROWLEVEL_H -+#define ROWLEVEL_H -+ -+#include "access/htup.h" -+#include "executor/tuptable.h" -+#include "nodes/plannodes.h" -+#include "utils/relcache.h" -+ -+#define ROWLV_BYPASS_MODE 1 -+#define ROWLV_FILTER_MODE 2 -+#define ROWLV_ABORT_MODE 3 -+ -+extern int -+rowlvGetPerformingMode(void); -+ -+extern int -+rowlvSetPerformingMode(int mode); -+ -+extern uint32 -+rowlvSetupPermissions(RangeTblEntry *rte); -+ -+extern bool -+rowlvExecScanFilter(Scan *scan, Relation rel, TupleTableSlot *slot); -+ -+extern void -+rowlvExecScanAbort(Scan *scan, Relation rel, TupleTableSlot *slot); -+ -+extern void -+rowlvHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal); -+ -+extern void -+rowlvHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup); -+ -+extern bool -+rowlvCopyToTuple(Relation rel, HeapTuple tuple); -+ -+#endif /* ROWLEVEL_H */ -diff --git a/src/include/security/sepgsql.h b/src/include/security/sepgsql.h -new file mode 100644 -index 0000000..d5ac80b ---- /dev/null -+++ b/src/include/security/sepgsql.h -@@ -0,0 +1,725 @@ -+/* -+ * src/include/security/sepgsql.h -+ * Headers of SE-PostgreSQL -+ * -+ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group -+ * Portions Copyright (c) 1994, Regents of the University of California -+ */ -+#ifndef SEPGSQL_H -+#define SEPGSQL_H -+ -+#include "access/htup.h" -+#include "catalog/dependency.h" -+#include "executor/execdesc.h" -+#include "fmgr.h" -+#include "nodes/parsenodes.h" -+#include "storage/large_object.h" -+#include "utils/relcache.h" -+ -+#ifdef HAVE_SELINUX -+ -+#include -+ -+/* GUC parameter to turn on/off SE-PostgreSQL */ -+extern int sepostgresql_mode; -+ -+#define SEPGSQL_MODE_DEFAULT 1 -+#define SEPGSQL_MODE_ENFORCING 2 -+#define SEPGSQL_MODE_PERMISSIVE 3 -+#define SEPGSQL_MODE_INTERNAL 4 -+#define SEPGSQL_MODE_DISABLED 5 -+ -+/* GUC parameter to turn on/off Row-level controls */ -+extern bool sepostgresql_row_level; -+ -+/* GUC parameter to turn on/off mcstrans */ -+extern bool sepostgresql_mcstrans; -+ -+/* Objject classes and permissions internally used */ -+enum SepgsqlClasses -+{ -+ SEPG_CLASS_PROCESS = 0, -+ SEPG_CLASS_FILE, -+ SEPG_CLASS_DIR, -+ SEPG_CLASS_LNK_FILE, -+ SEPG_CLASS_CHR_FILE, -+ SEPG_CLASS_BLK_FILE, -+ SEPG_CLASS_SOCK_FILE, -+ SEPG_CLASS_FIFO_FILE, -+ SEPG_CLASS_DB_DATABASE, -+ SEPG_CLASS_DB_SCHEMA, -+ SEPG_CLASS_DB_TABLE, -+ SEPG_CLASS_DB_VIEW, -+ SEPG_CLASS_DB_SEQUENCE, -+ SEPG_CLASS_DB_PROCEDURE, -+ SEPG_CLASS_DB_COLUMN, -+ SEPG_CLASS_DB_TUPLE, -+ SEPG_CLASS_DB_BLOB, -+ SEPG_CLASS_MAX, -+}; -+ -+#define SEPG_PROCESS__TRANSITION (1<<0) -+ -+#define SEPG_FILE__READ (1<<0) -+#define SEPG_FILE__WRITE (1<<1) -+#define SEPG_FILE__CREATE (1<<2) -+#define SEPG_FILE__GETATTR (1<<3) -+ -+#define SEPG_DIR__READ (SEPG_FILE__READ) -+#define SEPG_DIR__WRITE (SEPG_FILE__WRITE) -+#define SEPG_DIR__CREATE (SEPG_FILE__CREATE) -+#define SEPG_DIR__GETATTR (SEPG_FILE__GETATTR) -+ -+#define SEPG_LNK_FILE__READ (SEPG_FILE__READ) -+#define SEPG_LNK_FILE__WRITE (SEPG_FILE__WRITE) -+#define SEPG_LNK_FILE__CREATE (SEPG_FILE__CREATE) -+#define SEPG_LNK_FILE__GETATTR (SEPG_FILE__GETATTR) -+ -+#define SEPG_CHR_FILE__READ (SEPG_FILE__READ) -+#define SEPG_CHR_FILE__WRITE (SEPG_FILE__WRITE) -+#define SEPG_CHR_FILE__CREATE (SEPG_FILE__CREATE) -+#define SEPG_CHR_FILE__GETATTR (SEPG_FILE__GETATTR) -+ -+#define SEPG_BLK_FILE__READ (SEPG_FILE__READ) -+#define SEPG_BLK_FILE__WRITE (SEPG_FILE__WRITE) -+#define SEPG_BLK_FILE__CREATE (SEPG_FILE__CREATE) -+#define SEPG_BLK_FILE__GETATTR (SEPG_FILE__GETATTR) -+ -+#define SEPG_SOCK_FILE__READ (SEPG_FILE__READ) -+#define SEPG_SOCK_FILE__WRITE (SEPG_FILE__WRITE) -+#define SEPG_SOCK_FILE__CREATE (SEPG_FILE__CREATE) -+#define SEPG_SOCK_FILE__GETATTR (SEPG_FILE__GETATTR) -+ -+#define SEPG_FIFO_FILE__READ (SEPG_FILE__READ) -+#define SEPG_FIFO_FILE__WRITE (SEPG_FILE__WRITE) -+#define SEPG_FIFO_FILE__CREATE (SEPG_FILE__CREATE) -+#define SEPG_FIFO_FILE__GETATTR (SEPG_FILE__GETATTR) -+ -+#define SEPG_DB_DATABASE__CREATE (1<<0) -+#define SEPG_DB_DATABASE__DROP (1<<1) -+#define SEPG_DB_DATABASE__GETATTR (1<<2) -+#define SEPG_DB_DATABASE__SETATTR (1<<3) -+#define SEPG_DB_DATABASE__RELABELFROM (1<<4) -+#define SEPG_DB_DATABASE__RELABELTO (1<<5) -+#define SEPG_DB_DATABASE__ACCESS (1<<6) -+#define SEPG_DB_DATABASE__LOAD_MODULE (1<<7) -+ -+#define SEPG_DB_SCHEMA__CREATE (SEPG_DB_DATABASE__CREATE) -+#define SEPG_DB_SCHEMA__DROP (SEPG_DB_DATABASE__DROP) -+#define SEPG_DB_SCHEMA__GETATTR (SEPG_DB_DATABASE__GETATTR) -+#define SEPG_DB_SCHEMA__SETATTR (SEPG_DB_DATABASE__SETATTR) -+#define SEPG_DB_SCHEMA__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+#define SEPG_DB_SCHEMA__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+#define SEPG_DB_SCHEMA__SEARCH (1<<6) -+#define SEPG_DB_SCHEMA__ADD_NAME (1<<7) -+#define SEPG_DB_SCHEMA__REMOVE_NAME (1<<8) -+ -+#define SEPG_DB_TABLE__CREATE (SEPG_DB_DATABASE__CREATE) -+#define SEPG_DB_TABLE__DROP (SEPG_DB_DATABASE__DROP) -+#define SEPG_DB_TABLE__GETATTR (SEPG_DB_DATABASE__GETATTR) -+#define SEPG_DB_TABLE__SETATTR (SEPG_DB_DATABASE__SETATTR) -+#define SEPG_DB_TABLE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+#define SEPG_DB_TABLE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+#define SEPG_DB_TABLE__SELECT (1<<6) -+#define SEPG_DB_TABLE__UPDATE (1<<7) -+#define SEPG_DB_TABLE__INSERT (1<<8) -+#define SEPG_DB_TABLE__DELETE (1<<9) -+#define SEPG_DB_TABLE__LOCK (1<<10) -+#define SEPG_DB_TABLE__REFERENCE (1<<11) -+ -+#define SEPG_DB_SEQUENCE__CREATE (SEPG_DB_DATABASE__CREATE) -+#define SEPG_DB_SEQUENCE__DROP (SEPG_DB_DATABASE__DROP) -+#define SEPG_DB_SEQUENCE__GETATTR (SEPG_DB_DATABASE__GETATTR) -+#define SEPG_DB_SEQUENCE__SETATTR (SEPG_DB_DATABASE__SETATTR) -+#define SEPG_DB_SEQUENCE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+#define SEPG_DB_SEQUENCE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+#define SEPG_DB_SEQUENCE__GET_VALUE (1<<6) -+#define SEPG_DB_SEQUENCE__NEXT_VALUE (1<<7) -+#define SEPG_DB_SEQUENCE__SET_VALUE (1<<8) -+ -+#define SEPG_DB_VIEW__CREATE (SEPG_DB_DATABASE__CREATE) -+#define SEPG_DB_VIEW__DROP (SEPG_DB_DATABASE__DROP) -+#define SEPG_DB_VIEW__GETATTR (SEPG_DB_DATABASE__GETATTR) -+#define SEPG_DB_VIEW__SETATTR (SEPG_DB_DATABASE__SETATTR) -+#define SEPG_DB_VIEW__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+#define SEPG_DB_VIEW__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+#define SEPG_DB_VIEW__USAGE (1<<6) -+ -+#define SEPG_DB_PROCEDURE__CREATE (SEPG_DB_DATABASE__CREATE) -+#define SEPG_DB_PROCEDURE__DROP (SEPG_DB_DATABASE__DROP) -+#define SEPG_DB_PROCEDURE__GETATTR (SEPG_DB_DATABASE__GETATTR) -+#define SEPG_DB_PROCEDURE__SETATTR (SEPG_DB_DATABASE__SETATTR) -+#define SEPG_DB_PROCEDURE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+#define SEPG_DB_PROCEDURE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+#define SEPG_DB_PROCEDURE__EXECUTE (1<<6) -+#define SEPG_DB_PROCEDURE__ENTRYPOINT (1<<7) -+#define SEPG_DB_PROCEDURE__INSTALL (1<<8) -+ -+#define SEPG_DB_COLUMN__CREATE (SEPG_DB_DATABASE__CREATE) -+#define SEPG_DB_COLUMN__DROP (SEPG_DB_DATABASE__DROP) -+#define SEPG_DB_COLUMN__GETATTR (SEPG_DB_DATABASE__GETATTR) -+#define SEPG_DB_COLUMN__SETATTR (SEPG_DB_DATABASE__SETATTR) -+#define SEPG_DB_COLUMN__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+#define SEPG_DB_COLUMN__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+#define SEPG_DB_COLUMN__SELECT (1<<6) -+#define SEPG_DB_COLUMN__UPDATE (1<<7) -+#define SEPG_DB_COLUMN__INSERT (1<<8) -+#define SEPG_DB_COLUMN__REFERENCE (1<<9) -+ -+#define SEPG_DB_TUPLE__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+#define SEPG_DB_TUPLE__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+#define SEPG_DB_TUPLE__SELECT (SEPG_DB_DATABASE__GETATTR) -+#define SEPG_DB_TUPLE__UPDATE (SEPG_DB_DATABASE__SETATTR) -+#define SEPG_DB_TUPLE__INSERT (SEPG_DB_DATABASE__CREATE) -+#define SEPG_DB_TUPLE__DELETE (SEPG_DB_DATABASE__DROP) -+ -+#define SEPG_DB_BLOB__CREATE (SEPG_DB_DATABASE__CREATE) -+#define SEPG_DB_BLOB__DROP (SEPG_DB_DATABASE__DROP) -+#define SEPG_DB_BLOB__GETATTR (SEPG_DB_DATABASE__GETATTR) -+#define SEPG_DB_BLOB__SETATTR (SEPG_DB_DATABASE__SETATTR) -+#define SEPG_DB_BLOB__RELABELFROM (SEPG_DB_DATABASE__RELABELFROM) -+#define SEPG_DB_BLOB__RELABELTO (SEPG_DB_DATABASE__RELABELTO) -+#define SEPG_DB_BLOB__READ (1<<6) -+#define SEPG_DB_BLOB__WRITE (1<<7) -+#define SEPG_DB_BLOB__IMPORT (1<<8) -+#define SEPG_DB_BLOB__EXPORT (1<<9) -+ -+/* -+ * sepgsql_sid_t : alternative representation of security context -+ */ -+typedef struct { -+ Oid relid; -+ Oid secid; -+} sepgsql_sid_t; -+ -+#define SidIsValid(sid) (OidIsValid((sid).relid) && OidIsValid((sid).secid)) -+ -+/* -+ * selinux.c : communication to in-kernel SELinux -+ */ -+extern void sepgsqlInitialize(void); -+extern Size sepgsqlShmemSize(void); -+extern bool sepgsqlIsEnabled(void); -+extern bool sepgsqlIsEnabledBootstrap(void); -+extern bool sepgsqlGetEnforce(void); -+extern char *sepgsqlShowMode(void); -+extern char *sepgsqlGetServerLabel(void); -+extern char *sepgsqlGetClientLabel(void); -+extern char *sepgsqlSetClientLabel(char *new_label); -+extern bool -+sepgsqlComputePerms(char *scontext, char *tcontext, -+ uint16 tclass, uint32 required, -+ const char *audit_name, bool abort); -+extern char * -+sepgsqlComputeCreate(char *scontext, char *tcontext, uint16 tclass); -+extern bool -+sepgsqlClientHasPerms(sepgsql_sid_t tsid, uint16 tclass, uint32 required, -+ const char *audit_name, bool abort); -+extern sepgsql_sid_t -+sepgsqlClientCreateSecid(sepgsql_sid_t tsid, uint16 tclass, Oid nrelid); -+extern char * -+sepgsqlClientCreateLabel(sepgsql_sid_t tsid, uint16 tclass); -+ -+extern bool sepgsqlReceiverStart(void); -+extern void sepgsqlReceiverMain(void); -+ -+/* -+ * bridge.c : new style security hooks -+ */ -+ -+/* pg_attribute */ -+extern Oid -+sepgsql_attribute_create(Oid relOid, ColumnDef *cdef); -+extern void -+sepgsql_attribute_alter(Oid relOid, const char *attname); -+extern void -+sepgsql_attribute_drop(Oid relOid, AttrNumber attnum); -+extern void -+sepgsql_attribute_grant(Oid relOid, AttrNumber attnum); -+extern Oid -+sepgsql_attribute_relabel(Oid relOid, AttrNumber attnum, DefElem *newLabel); -+ -+/* pg_cast */ -+extern Oid -+sepgsql_cast_create(Oid sourceTypOid, Oid targetTypOid, Oid funcOid); -+extern void -+sepgsql_cast_drop(Oid castOid); -+ -+/* pg_class */ -+extern Oid * -+sepgsql_relation_create(const char *relName, -+ char relkind, -+ TupleDesc tupDesc, -+ Oid nspOid, -+ DefElem *relLabel, -+ List *colList, -+ bool createAs, -+ bool permission); -+extern Oid * -+sepgsql_relation_copy(Relation src); -+extern void -+sepgsql_relation_alter(Oid relOid, const char *newName, Oid newNsp); -+extern void -+sepgsql_relation_drop(Oid relOid); -+extern void -+sepgsql_relation_grant(Oid relOid); -+extern Oid -+sepgsql_relation_relabel(Oid relOid, DefElem *newLabel); -+extern void -+sepgsql_relation_get_transaction_id(Oid relOid); -+extern void -+sepgsql_relation_copy_definition(Oid relOid); -+extern void -+sepgsql_relation_truncate(Relation rel); -+extern void -+sepgsql_relation_references(Relation rel, int16 *attnums, int natts); -+extern void -+sepgsql_relation_lock(Oid relOid); -+extern void -+sepgsql_view_replace(Oid viewOid); -+extern void -+sepgsql_index_create(Oid relOid, Oid nspOid); -+extern void -+sepgsql_sequence_get_value(Oid seqOid); -+extern void -+sepgsql_sequence_next_value(Oid seqOid); -+extern void -+sepgsql_sequence_set_value(Oid seqOid); -+ -+/* pg_conversion */ -+extern Oid -+sepgsql_conversion_create(const char *convName, Oid nspOid, Oid procOid); -+extern void -+sepgsql_conversion_alter(Oid convOid, const char *newName); -+extern void -+sepgsql_conversion_drop(Oid convOid); -+ -+/* pg_database */ -+extern Oid -+sepgsql_database_create(const char *datName, Oid srcDatOid, DefElem *newLabel); -+extern void -+sepgsql_database_alter(Oid datOid); -+extern void -+sepgsql_database_drop(Oid datOid); -+extern Oid -+sepgsql_database_relabel(Oid datOid, DefElem *newLabel); -+extern void -+sepgsql_database_grant(Oid datOid); -+extern void -+sepgsql_database_access(Oid datOid); -+extern bool -+sepgsql_database_superuser(Oid datOid); -+extern void -+sepgsql_database_load_module(Oid datOid, const char *filename); -+ -+/* pg_foreign_data_wrapper */ -+extern Oid -+sepgsql_fdw_create(const char *fdwName, Oid fdwValidator); -+extern void -+sepgsql_fdw_alter(Oid fdwOid, Oid newValidator); -+extern void -+sepgsql_fdw_drop(Oid fdwOid); -+extern void -+sepgsql_fdw_grant(Oid fdwOid); -+ -+/* pg_foreign_server */ -+extern Oid -+sepgsql_foreign_server_create(const char *fsrvName); -+extern void -+sepgsql_foreign_server_alter(Oid fsrvOid); -+extern void -+sepgsql_foreign_server_drop(Oid fsrvOid); -+extern void -+sepgsql_foreign_server_grant(Oid fsrvOid); -+ -+/* pg_language */ -+extern Oid -+sepgsql_language_create(const char *langName, Oid handlerOid, Oid validatorOid); -+extern void -+sepgsql_language_alter(Oid langOid); -+extern void -+sepgsql_language_drop(Oid langOid); -+extern void -+sepgsql_language_grant(Oid langOid); -+ -+/* pg_largeobject */ -+extern Oid -+sepgsql_largeobject_create(Oid loid, Value *secLabel); -+extern void -+sepgsql_largeobject_alter(Oid loid); -+extern void -+sepgsql_largeobject_relabel(Oid loid, Value *secLabel); -+extern void -+sepgsql_largeobject_drop(Oid loid); -+extern void -+sepgsql_largeobject_read(Oid loid, Snapshot snapshot); -+extern void -+sepgsql_largeobject_write(Oid loid, Snapshot snapshot); -+extern void -+sepgsql_largeobject_export(Oid loid, const char *filename); -+extern Oid -+sepgsql_largeobject_import(Oid loid, const char *filename); -+ -+/* pg_namespace */ -+extern Oid -+sepgsql_schema_create(const char *nspName, bool isTemp, DefElem *newLabel); -+extern void -+sepgsql_schema_alter(Oid nspOid); -+extern void -+sepgsql_schema_drop(Oid nspOid); -+extern Oid -+sepgsql_schema_relabel(Oid nspOid, DefElem *newLabel); -+extern void -+sepgsql_schema_grant(Oid nspOid); -+extern bool -+sepgsql_schema_search(Oid nspOid, bool abort); -+ -+/* pg_opclass */ -+extern Oid -+sepgsql_opclass_create(const char *opcName, Oid nspOid); -+extern void -+sepgsql_opclass_alter(Oid opcOid, const char *newName); -+extern void -+sepgsql_opclass_drop(Oid opcOid); -+ -+/* pg_opfamily */ -+extern Oid -+sepgsql_opfamily_create(const char *opfName, Oid nspOid); -+extern void -+sepgsql_opfamily_alter(Oid opfOid, const char *newName); -+extern void -+sepgsql_opfamily_drop(Oid opfOid); -+extern void -+sepgsql_opfamily_add_operator(Oid opfOid, Oid operOid); -+extern void -+sepgsql_opfamily_add_procedure(Oid opfOid, Oid procOid); -+ -+/* pg_operator */ -+extern Oid -+sepgsql_operator_create(const char *oprName, Oid oprOid, Oid nspOid, -+ Oid codeFn, Oid restFn, Oid joinFn); -+extern void -+sepgsql_operator_alter(Oid oprOid); -+extern void -+sepgsql_operator_drop(Oid oprOid); -+ -+/* pg_proc */ -+extern Oid -+sepgsql_proc_create(const char *procName, HeapTuple oldTup, -+ Oid nspOid, Oid langOid, DefElem *newLabel); -+extern void -+sepgsql_proc_alter(Oid procOid, const char *newName, Oid newNsp); -+extern void -+sepgsql_proc_drop(Oid procOid); -+extern Oid -+sepgsql_proc_relabel(Oid procOid, DefElem *newLabel); -+extern void -+sepgsql_proc_grant(Oid procOid); -+extern void -+sepgsql_proc_execute(Oid procOid); -+extern bool -+sepgsql_proc_hint_inlined(HeapTuple protup); -+extern bool -+sepgsql_proc_entrypoint(HeapTuple protup); -+extern char * -+sepgsql_proc_trusted(HeapTuple protup, MemoryContext mcxt); -+ -+/* pg_rewrite */ -+extern void -+sepgsql_rule_create(Oid relOid, const char *ruleName); -+extern void -+sepgsql_rule_drop(Oid relOid, const char *ruleName); -+ -+/* pg_trigger */ -+extern void -+sepgsql_trigger_create(Oid relOid, const char *trigName, Oid procOid); -+extern void -+sepgsql_trigger_alter(Oid relOid, const char *trigName); -+extern void -+sepgsql_trigger_drop(Oid relOid, const char *trigName); -+ -+/* pg_ts_config */ -+extern Oid -+sepgsql_ts_config_create(const char *cfgName, Oid nspOid); -+extern void -+sepgsql_ts_config_alter(Oid cfgOid, const char *newName); -+extern void -+sepgsql_ts_config_drop(Oid cfgOid); -+ -+/* pg_ts_dict */ -+extern Oid -+sepgsql_ts_dict_create(const char *dictName, Oid nspOid); -+extern void -+sepgsql_ts_dict_alter(Oid dictOid, const char *newName); -+extern void -+sepgsql_ts_dict_drop(Oid dictOid); -+ -+/* pg_ts_parser */ -+extern Oid -+sepgsql_ts_parser_create(const char *prsName, Oid nspOid, -+ Oid startFn, Oid tokenFn, Oid sendFn, -+ Oid headlineFn, Oid lextypeFn); -+extern void -+sepgsql_ts_parser_alter(Oid prsOid, const char *newName); -+extern void -+sepgsql_ts_parser_drop(Oid prsOid); -+ -+/* pg_ts_templace */ -+extern Oid -+sepgsql_ts_template_create(const char *tmplName, Oid nspOid, -+ Oid initFn, Oid lexizeFn); -+extern void -+sepgsql_ts_template_alter(Oid tmplOid, const char *newName); -+extern void -+sepgsql_ts_template_drop(Oid tmplOid); -+ -+/* pg_type */ -+extern Oid -+sepgsql_type_create(const char *typName, HeapTuple oldTup, Oid nspOid, -+ Oid inputProc, Oid outputProc, Oid recvProc, Oid sendProc, -+ Oid modinProc, Oid modoutProc, Oid analyzeProc); -+extern void -+sepgsql_type_alter(Oid typOid, const char *newName, Oid newNsp); -+extern void -+sepgsql_type_drop(Oid typOid); -+ -+/* misc objects */ -+extern void -+sepgsql_sysobj_drop(const ObjectAddress *object); -+ -+/* filesystem objects */ -+void -+sepgsql_file_stat(const char *filename); -+void -+sepgsql_file_read(const char *filename); -+void -+sepgsql_file_write(const char *filename); -+ -+/* -+ * checker.c : check permission on given queries -+ */ -+extern void -+sepgsqlCheckRTEPerms(RangeTblEntry *rte); -+ -+extern void -+sepgsqlCheckCopyTable(Relation rel, List *attnumlist, bool is_from); -+ -+extern void -+sepgsqlCheckSelectInto(Oid relaionId); -+ -+extern bool -+sepgsqlExecScan(Relation rel, HeapTuple tuple, uint32 required, bool abort); -+ -+extern uint32 -+sepgsqlSetupTuplePerms(RangeTblEntry *rte); -+ -+extern void -+sepgsqlHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal); -+ -+extern void -+sepgsqlHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup); -+ -+/* -+ * label.c : security label management -+ */ -+extern bool sepgsqlTupleDescHasSecid(Oid relid, char relkind); -+ -+extern void sepgsqlPostBootstrapingMode(void); -+ -+extern void sepgsqlSetDefaultSecid(Relation rel, HeapTuple tuple); -+extern sepgsql_sid_t sepgsqlGetDefaultDatabaseSecid(Oid src_database_oid); -+extern sepgsql_sid_t sepgsqlGetDefaultSchemaSecid(Oid database_oid); -+extern sepgsql_sid_t sepgsqlGetDefaultSchemaTempSecid(Oid database_oid); -+extern sepgsql_sid_t sepgsqlGetDefaultTableSecid(Oid namespace_oid); -+extern sepgsql_sid_t sepgsqlGetDefaultSequenceSecid(Oid namespace_oid); -+extern sepgsql_sid_t sepgsqlGetDefaultProcedureSecid(Oid namespace_oid); -+extern sepgsql_sid_t sepgsqlGetDefaultColumnSecid(Oid table_oid); -+extern sepgsql_sid_t sepgsqlGetDefaultTupleSecid(Oid table_oid); -+extern sepgsql_sid_t sepgsqlGetDefaultBlobSecid(Oid database_oid); -+ -+extern Oid *sepgsqlCreateTableColumns(CreateStmt *stmt, -+ const char *relname, Oid namespace_oid, -+ TupleDesc tupdesc, char relkind); -+extern Oid *sepgsqlCopyTableColumns(Relation source); -+ -+extern sepgsql_sid_t -+sepgsqlGetTupleSecid(Oid tableOid, HeapTuple tuple, uint16 *tclass); -+extern sepgsql_sid_t -+sepgsqlGetSysobjSecid(Oid tableOid, Oid objectId, int32 objsubId, uint16 *tclass); -+ -+extern char *sepgsqlTransSecLabelIn(char *seclabel); -+extern char *sepgsqlTransSecLabelOut(char *seclabel); -+extern char *sepgsqlRawSecLabelIn(char *seclabel); -+extern char *sepgsqlRawSecLabelOut(char *seclabel); -+extern char *sepgsqlSysattSecLabelOut(Oid relid, HeapTuple tuple); -+ -+#else /* HAVE_SELINUX */ -+ -+/* avc.c */ -+#define sepgsqlShmemSize() (0) -+ -+/* checker.c */ -+#define sepgsqlCheckRTEPerms(a) do {} while(0) -+#define sepgsqlCheckCopyTable(a,b,c) do {} while(0) -+#define sepgsqlCheckSelectInto(a) do {} while(0) -+#define sepgsqlExecScan(a,b,c) (true) -+#define sepgsqlSetupTuplePerms(a) (0) -+#define sepgsqlHeapTupleInsert(a,b,c) do {} while(0) -+#define sepgsqlHeapTupleUpdate(a,b,c) do {} while(0) -+ -+/* core.c */ -+#define sepgsqlIsEnabled() (false) -+#define sepgsqlInitialize() do {} while(0) -+ -+/* bridge.c */ -+#define sepgsql_attribute_create(a,b) (InvalidOid) -+#define sepgsql_attribute_alter(a,b) do {} while(0) -+#define sepgsql_attribute_drop(a,b) do {} while(0) -+#define sepgsql_attribute_grant(a,b) do {} while(0) -+#define sepgsql_attribute_relabel(a,b,c) (InvalidOid) -+ -+#define sepgsql_cast_create(a,b,c) (InvalidOid) -+#define sepgsql_cast_drop(a) (InvalidOid) -+ -+#define sepgsql_relation_create(a,b,c,d,e,f) (NULL) -+#define sepgsql_relation_copy(a) (NULL) -+#define sepgsql_relation_alter(a,b,c) do {} while(0) -+#define sepgsql_relation_drop(a) do {} while(0) -+#define sepgsql_relation_grant(a) do {} while(0) -+#define sepgsql_relation_relabel(a,b) do {} while(0) -+#define sepgsql_relation_get_transaction_id(a) do {} while(0) -+#define sepgsql_relation_copy_definition(a) do {} while(0) -+#define sepgsql_relation_truncate(a) do {} while(0) -+#define sepgsql_relation_references(a,b,c) do {} while(0) -+#define sepgsql_relation_lock(a) do {} while(0) -+#define sepgsql_view_replace(a) do {} while(0) -+#define sepgsql_index_create(a,b,c) do {} while(0) -+#define sepgsql_sequence_get_value(a) do {} while(0) -+#define sepgsql_sequence_next_value(a) do {} while(0) -+#define sepgsql_sequence_set_value(a) do {} while(0) -+ -+#define sepgsql_conversion_create(a,b,c) do {} while(0) -+#define sepgsql_conversion_alter(a,b) do {} while(0) -+#define sepgsql_conversion_drop(a) do {} while(0) -+ -+#define sepgsql_database_create(a,b) (InvalidOid) -+#define sepgsql_database_alter(a) do {} while(0) -+#define sepgsql_database_drop(a) do {} while(0) -+#define sepgsql_database_relabel(a,b) (InvalidOid) -+#define sepgsql_database_grant(a) do {} while(0) -+#define sepgsql_database_access(a) do {} while(0) -+#define sepgsql_database_superuser(a) (true) -+#define sepgsql_database_load_module(a,b) do {} while(0) -+ -+#define sepgsql_fdw_create(a,b) (InvalidOid) -+#define sepgsql_fdw_alter(a,b) do {} while(0) -+#define sepgsql_fdw_drop(a) do {} while(0) -+#define sepgsql_fdw_grant(a) do {} while(0) -+ -+#define sepgsql_foreign_server_create(a) (InvalidOid) -+#define sepgsql_foreign_server_alter(a) do {} while(0) -+#define sepgsql_foreign_server_drop(a) do {} while(0) -+#define sepgsql_foreign_server_grant(a) do {} while(0) -+ -+#define sepgsql_language_create(a,b,c) (InvalidOid) -+#define sepgsql_language_alter(a) do {} while(0) -+#define sepgsql_language_drop(a) do {} while(0) -+#define sepgsql_language_grant(a) do {} while(0) -+ -+#define sepgsql_largeobject_create(a,b) (InvalidOid) -+#define sepgsql_largeobject_alter(a,b) do {} while(0) -+#define sepgsql_largeobject_drop(a) do {} while(0) -+#define sepgsql_largeobject_read(a) do {} while(0) -+#define sepgsql_largeobject_write(a) do {} while(0) -+#define sepgsql_largeobject_export(a,b) do {} while(0) -+#define sepgsql_largeobject_import(a,b) (InvalidOid) -+ -+#define sepgsql_schema_create(a,b,c) (InvalidOid) -+#define sepgsql_schema_alter(a) do {} while(0) -+#define sepgsql_schema_drop(a) do {} while(0) -+#define sepgsql_schema_relabel(a,b) (InvalidOid) -+#define sepgsql_schema_grant(a) do {} while(0) -+#define sepgsql_schema_search(a,b) (true) -+ -+#define sepgsql_opclass_create(a,b) (InvalidOid) -+#define sepgsql_opclass_alter(a,b) do {} while(0) -+#define sepgsql_opclass_drop(a) do {} while(0) -+ -+#define sepgsql_opfamily_create(a,b) (InvalidOid) -+#define sepgsql_opfamily_alter(a,b) do {} while(0) -+#define sepgsql_opfamily_drop(a) do {} while(0) -+#define sepgsql_opfamily_add_operator(a,b) do {} while(0) -+#define sepgsql_opfamily_add_procedure(a,b) do {} while(0) -+ -+#define sepgsql_operator_create(a,b,c,d,e,f) (InvalidOid) -+#define sepgsql_operator_alter(a) do {} while(0) -+#define sepgsql_operator_drop(a) do {} while(0) -+ -+#define sepgsql_proc_create(a,b,c,d,e) (InvalidOid) -+#define sepgsql_proc_alter(a,b,c) do {} while(0) -+#define sepgsql_proc_drop(a) do {} while(0) -+#define sepgsql_proc_relabel(a,b) (InvalidOid) -+#define sepgsql_proc_grant(a) do {} while(0) -+#define sepgsql_proc_execute(a) do {} while(0) -+#define sepgsql_proc_hint_inlined(a) (true) -+#define sepgsql_proc_entrypoint(a,b) do {} while(0) -+ -+#define sepgsql_rule_create(a,b) do {} while(0) -+#define sepgsql_rule_drop(a,b) do {} while(0) -+ -+#define sepgsql_trigger_create(a,b,c) do {} while(0) -+#define sepgsql_trigger_alter(a,b) do {} while(0) -+#define sepgsql_trigger_drop(a,b) do {} while(0) -+ -+#define sepgsql_ts_config_create(a,b) (InvalidOid) -+#define sepgsql_ts_config_alter(a,b) do {} while(0) -+#define sepgsql_ts_config_drop(a) do {} while(0) -+ -+#define sepgsql_ts_config_create(a,b) (InvalidOid) -+#define sepgsql_ts_config_alter(a,b) do {} while(0) -+#define sepgsql_ts_config_drop(a) do {} while(0) -+ -+#define sepgsql_ts_dict_create(a,b) (InvalidOid) -+#define sepgsql_ts_dict_alter(a,b) do {} while(0) -+#define sepgsql_ts_dict_drop(a) do {} while(0) -+ -+#define sepgsql_ts_parser_create(a,b,c,d,e,f,g) (InvalidOid) -+#define sepgsql_ts_parser_alter(a,b) do {} while(0) -+#define sepgsql_ts_parser_drop(a) do {} while(0) -+ -+#define sepgsql_ts_template_create(a,b,c,d) (InvalidOid) -+#define sepgsql_ts_template_alter(a,b) do {} while(0) -+#define sepgsql_ts_template_drop(a) do {} while(0) -+ -+#define sepgsql_type_create(a,b,c,d,e,f,g,h,i,j) (InvalidOid) -+#define sepgsql_type_alter(a,b,c) do {} while(0) -+#define sepgsql_type_drop(a) do {} while(0) -+ -+#define sepgsql_sysobj_drop(a) do {} while(0) -+ -+#define sepgsql_file_stat(a) do {} while(0) -+#define sepgsql_file_read(a) do {} while(0) -+#define sepgsql_file_write(a) do {} while(0) -+ -+/* label.c */ -+#define sepgsqlTupleDescHasSecLabel(a,b) (false) -+#define sepgsqlSetDefaultSecLabel(a,b) do {} while(0) -+#define sepgsqlTransSecLabelIn(a) (a) -+#define sepgsqlTransSecLabelOut(a) (a) -+#define sepgsqlRawSecLabelIn(a) (a) -+#define sepgsqlRawSecLabelOut(a) (a) -+ -+#endif /* HAVE_SELINUX */ -+ -+extern Datum sepgsql_getcon(PG_FUNCTION_ARGS); -+extern Datum sepgsql_server_getcon(PG_FUNCTION_ARGS); -+extern Datum sepgsql_get_user(PG_FUNCTION_ARGS); -+extern Datum sepgsql_get_role(PG_FUNCTION_ARGS); -+extern Datum sepgsql_get_type(PG_FUNCTION_ARGS); -+extern Datum sepgsql_get_range(PG_FUNCTION_ARGS); -+extern Datum sepgsql_set_user(PG_FUNCTION_ARGS); -+extern Datum sepgsql_set_role(PG_FUNCTION_ARGS); -+extern Datum sepgsql_set_type(PG_FUNCTION_ARGS); -+extern Datum sepgsql_set_range(PG_FUNCTION_ARGS); -+ -+#endif /* SEPGSQL_H */ -diff --git a/src/include/storage/fd.h b/src/include/storage/fd.h -index 462f6d1..d2c5e6e 100644 ---- a/src/include/storage/fd.h -+++ b/src/include/storage/fd.h -@@ -68,6 +68,7 @@ extern int FileWrite(File file, char *buffer, int amount); - 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 --git a/src/include/storage/large_object.h b/src/include/storage/large_object.h -index 818db40..feb066f 100644 ---- a/src/include/storage/large_object.h -+++ b/src/include/storage/large_object.h -@@ -70,7 +70,7 @@ typedef struct LargeObjectDesc - - /* inversion stuff in inv_api.c */ - extern void close_lo_relation(bool isCommit); --extern Oid inv_create(Oid lobjId); -+extern Oid inv_create(Oid lobjId, Oid secid); - extern LargeObjectDesc *inv_open(Oid lobjId, int flags, MemoryContext mcxt); - extern void inv_close(LargeObjectDesc *obj_desc); - extern int inv_drop(Oid lobjId); -diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h -index e389c61..1ea6c09 100644 ---- a/src/include/storage/lwlock.h -+++ b/src/include/storage/lwlock.h -@@ -67,6 +67,7 @@ typedef enum LWLockId - AutovacuumLock, - AutovacuumScheduleLock, - SyncScanLock, -+ SepgsqlAvcLock, - /* Individual lock IDs end here */ - FirstBufMappingLock, - FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS, -diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h -index 48872d9..3383637 100644 ---- a/src/include/storage/proc.h -+++ b/src/include/storage/proc.h -@@ -143,8 +143,10 @@ typedef struct PROC_HDR - * normal operation. Startup process also consumes one slot, but WAL - * writer and autovacuum launcher are launched only after it has - * exited. -+ * In addition, a netlink receiver process may be launched, if SELinux -+ * support is enabled. - */ --#define NUM_AUXILIARY_PROCS 3 -+#define NUM_AUXILIARY_PROCS 4 - - - /* configurable options */ -diff --git a/src/include/utils/errcodes.h b/src/include/utils/errcodes.h -index 44018cd..b225468 100644 ---- a/src/include/utils/errcodes.h -+++ b/src/include/utils/errcodes.h -@@ -301,6 +301,7 @@ - #define ERRCODE_INVALID_SCHEMA_DEFINITION MAKE_SQLSTATE('4','2', 'P','1','5') - #define ERRCODE_INVALID_TABLE_DEFINITION MAKE_SQLSTATE('4','2', 'P','1','6') - #define ERRCODE_INVALID_OBJECT_DEFINITION MAKE_SQLSTATE('4','2', 'P','1','7') -+#define ERRCODE_INVALID_SECURITY_LABEL MAKE_SQLSTATE('4','2', 'P','9','9') - - /* Class 44 - WITH CHECK OPTION Violation */ - #define ERRCODE_WITH_CHECK_OPTION_VIOLATION MAKE_SQLSTATE('4','4', '0','0','0') -diff --git a/src/test/regress/GNUmakefile b/src/test/regress/GNUmakefile -index 8ce5d25..70d7def 100644 ---- a/src/test/regress/GNUmakefile -+++ b/src/test/regress/GNUmakefile -@@ -38,6 +38,12 @@ ifdef NO_LOCALE - NOLOCALE += --no-locale - endif - -+# SELinux support -+ENABLE_SELINUX = -+ifdef SELINUX -+ENABLE_SELINUX += --enable-selinux -+endif -+ - # stuff to pass into build of pg_regress - EXTRADEFS = '-DHOST_TUPLE="$(host_tuple)"' \ - '-DMAKEPROG="$(MAKE)"' \ -@@ -138,7 +144,7 @@ tablespace-setup: - ## Run tests - ## - --pg_regress_call = ./pg_regress --inputdir=$(srcdir) --dlpath=. --multibyte=$(MULTIBYTE) --load-language=plpgsql $(NOLOCALE) -+pg_regress_call = ./pg_regress --inputdir=$(srcdir) --dlpath=. --multibyte=$(MULTIBYTE) --load-language=plpgsql $(NOLOCALE) $(ENABLE_SELINUX) - - check: all - $(pg_regress_call) --temp-install=./tmp_check --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(TEMP_CONF) -diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out -index 9a66ba0..e0efefc 100644 ---- a/src/test/regress/expected/sanity_check.out -+++ b/src/test/regress/expected/sanity_check.out -@@ -113,6 +113,7 @@ SELECT relname, relhasindex - pg_pltemplate | t - pg_proc | t - pg_rewrite | t -+ pg_security | t - pg_shdepend | t - pg_shdescription | t - pg_statistic | t -diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c -index 37193ae..a5f05cc 100644 ---- a/src/test/regress/pg_regress.c -+++ b/src/test/regress/pg_regress.c -@@ -82,6 +82,7 @@ static _stringlist *schedulelist = NULL; - static _stringlist *extra_tests = NULL; - static char *temp_install = NULL; - static char *temp_config = NULL; -+static bool enable_selinux = false; - static char *top_builddir = NULL; - static bool nolocale = false; - static char *hostname = NULL; -@@ -1863,6 +1864,7 @@ help(void) - printf(_(" --top-builddir=DIR (relative) path to top level build directory\n")); - printf(_(" --port=PORT start postmaster on PORT\n")); - printf(_(" --temp-config=PATH append contents of PATH to temporary config\n")); -+ printf(_(" --enable-selinux enables SELinux support, if available\n")); - printf(_("\n")); - printf(_("Options for using an existing installation:\n")); - printf(_(" --host=HOST use postmaster running on HOST\n")); -@@ -1907,6 +1909,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc - {"dlpath", required_argument, NULL, 17}, - {"create-role", required_argument, NULL, 18}, - {"temp-config", required_argument, NULL, 19}, -+ {"enable-selinux", optional_argument, NULL, 20}, - {NULL, 0, NULL, 0} - }; - -@@ -1997,6 +2000,9 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc - case 19: - temp_config = strdup(optarg); - break; -+ case 20: -+ enable_selinux = true; -+ break; - default: - /* getopt_long already emitted a complaint */ - fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"), -@@ -2086,10 +2092,11 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc - /* initdb */ - header(_("initializing database system")); - snprintf(buf, sizeof(buf), -- SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE, -+ SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean%s%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE, - bindir, temp_install, datadir, - debug ? " --debug" : "", - nolocale ? " --no-locale" : "", -+ enable_selinux ? " --enable-selinux" : "", - outputdir); - if (system(buf)) - { diff --git a/sepostgresql-fedora-prefix.patch b/sepostgresql-fedora-prefix.patch deleted file mode 100644 index a9dd24b..0000000 --- a/sepostgresql-fedora-prefix.patch +++ /dev/null @@ -1,87 +0,0 @@ -Index: sepgsql/src/Makefile.global.in -=================================================================== ---- sepgsql/src/Makefile.global.in (revision 2237) -+++ sepgsql/src/Makefile.global.in (working copy) -@@ -75,14 +75,14 @@ - datadir := @datadir@ - ifeq "$(findstring pgsql, $(datadir))" "" - ifeq "$(findstring postgres, $(datadir))" "" --override datadir := $(datadir)/postgresql -+override datadir := $(datadir)/sepgsql - endif - endif - - sysconfdir := @sysconfdir@ - ifeq "$(findstring pgsql, $(sysconfdir))" "" - ifeq "$(findstring postgres, $(sysconfdir))" "" --override sysconfdir := $(sysconfdir)/postgresql -+override sysconfdir := $(sysconfdir)/sepgsql - endif - endif - -@@ -91,7 +91,7 @@ - pkglibdir = $(libdir) - ifeq "$(findstring pgsql, $(pkglibdir))" "" - ifeq "$(findstring postgres, $(pkglibdir))" "" --override pkglibdir := $(pkglibdir)/postgresql -+override pkglibdir := $(pkglibdir)/sepgsql - endif - endif - -@@ -100,7 +100,7 @@ - pkgincludedir = $(includedir) - ifeq "$(findstring pgsql, $(pkgincludedir))" "" - ifeq "$(findstring postgres, $(pkgincludedir))" "" --override pkgincludedir := $(pkgincludedir)/postgresql -+override pkgincludedir := $(pkgincludedir)/sepgsql - endif - endif - -@@ -109,7 +109,7 @@ - docdir := @docdir@ - ifeq "$(findstring pgsql, $(docdir))" "" - ifeq "$(findstring postgres, $(docdir))" "" --override docdir := $(docdir)/postgresql -+override docdir := $(docdir)/sepgsql - endif - endif - -Index: sepgsql/src/bin/pg_ctl/pg_ctl.c -=================================================================== ---- sepgsql/src/bin/pg_ctl/pg_ctl.c (revision 2237) -+++ sepgsql/src/bin/pg_ctl/pg_ctl.c (working copy) -@@ -643,7 +643,7 @@ - - postmaster_path = pg_malloc(MAXPGPATH); - -- if ((ret = find_other_exec(argv0, "postgres", PG_BACKEND_VERSIONSTR, -+ if ((ret = find_other_exec(argv0, "sepostgres", PG_BACKEND_VERSIONSTR, - postmaster_path)) < 0) - { - char full_path[MAXPGPATH]; -Index: sepgsql/src/bin/initdb/initdb.c -=================================================================== ---- sepgsql/src/bin/initdb/initdb.c (revision 2237) -+++ sepgsql/src/bin/initdb/initdb.c (working copy) -@@ -2763,7 +2763,7 @@ - sprintf(pgdenv, "PGDATA=%s", pg_data); - putenv(pgdenv); - -- if ((ret = find_other_exec(argv[0], "postgres", PG_BACKEND_VERSIONSTR, -+ if ((ret = find_other_exec(argv[0], "sepostgres", PG_BACKEND_VERSIONSTR, - backend_exec)) < 0) - { - char full_path[MAXPGPATH]; -Index: sepgsql/src/bin/pg_dump/pg_dumpall.c -=================================================================== ---- sepgsql/src/bin/pg_dump/pg_dumpall.c (revision 2237) -+++ sepgsql/src/bin/pg_dump/pg_dumpall.c (working copy) -@@ -157,7 +157,7 @@ - } - } - -- if ((ret = find_other_exec(argv[0], "pg_dump", PGDUMP_VERSIONSTR, -+ if ((ret = find_other_exec(argv[0], "sepg_dump", PGDUMP_VERSIONSTR, - pg_dump_bin)) < 0) - { - char full_path[MAXPGPATH]; diff --git a/sepostgresql.8 b/sepostgresql.8 deleted file mode 100644 index 9c60ef5..0000000 --- a/sepostgresql.8 +++ /dev/null @@ -1,137 +0,0 @@ -.TH "sepostgresql" "8" "Jul 15 2007" "kaigai@kaigai.gr.jp" "Security-Enhanced PostgreSQL" - -.SH "NAME" -sepostgresql \- Security-Enhances PostgreSQL - -.SH "DESCRIPTION" -Security-Enhanced PostgreSQL (SE-PostgreSQL) is an enhancement of PostgreSQL, -to apply fine grained mandatory access control for database objects based on -the security policy of SELinux. -These features enable to apply flexible integrated access control policy -on both of operating system and database management system, during all -stages of the life of the information. -.PP -This document describes the way to customize SE-PostgreSQL on the default -security policy. - -.SH "BOOLEANS" -The SELinux policy is customizable via BOOLEAN variable. This variable has -two states, 1 (on) or 0 (off). We can validate or invalidate a part of the -security policy depending on the state of boolean variables. - -\fBsepgsql_enable_users_ddl\fP enables to toggle permissions of confined -users/applications to invoke DDL statement, like CREATE TABLE. It is set to -\fBon\fP in the default. -In most cases, DDL statements are used to set up initial database structure, -and permissions to invoke them are not necessary on operation phase. -You can turn off this boolean as follows: - -.EX -setsebool -P sepgsql_enable_users_ddl ( \fBon\fP | off ) -.EE - -Rest of booleans are provided by \fBselinux-devel.pp\fP policy module. -It provides developments/debugs related permissions. -You can install it as follows: - -.EX -semodule -i /usr/share/selinux/targeted/sepostgresql-devel.pp -.EE - -\fBsepgsql_enable_auditallow\fP toggles output of audit messages in the case -when required permission checks are allowed, except for tuples because it -easily make a flood of audit logs. -In the default, it is set to off. You can set it as follows: - -.EX -setsebool -P sepgsql_enable_auditallow ( on | \fBoff\fP ) -.EE - -\fBsepgsql_enable_auditdeny\fP toggles output of audit messages in the case -when required permission checks are denied, except for tuples because it -easily make a flood of audit logs. -In the default, it is set to on. You can set it as follows: - -.EX -setsebool -P sepgsql_enable_auditdeny ( \fBon\fP | off ) -.EE - -\fBsepgsql_regression_test_mode\fP allows to load shared libraries deployed -on user's home directory. We recommend you to keep \fBoff\fP in operation -phase to prevent to load malicious libraries. -However, typical PostgreSQL regression test requires to load it, so we -have to reduce several restriction during the test. -In the default, it is set to off. You can set it as follows: - -.EX -setsebool -P sepgsql_regression_test_mode ( on | \fBoff\fP ) -.EE - -.SH "TYPES" - -\fBsepgsql_db_t\fP is a only type for database itself. -It is attched for newly created databases in the default. - -\fBsepgsql_table_t\fP is a type for tables, columns and tuples. -It is the default type of newly created tables by unconfined or -non-roled domain. It allows confined clietns to access with any -kind of operations except for relabeling, so we can use this type -for compatible purpose. - -\fBsepgsql_secret_table_t\fP is a type for tables, columns and tuples. -It never allows confined clients to access, so we can use this type -to store sensitive information. We reccomend to apply trusted procedures -to access tables/columns/tuples with this type under safe operation. - -\fBsepgsql_ro_table_t\fP is a type for read-only tables, columns and tuples. -It does not allow confined clients to modify any objects with this type. - -\fBsepgsql_fixed_table_t\fP is a type for non-manupulatable tables, columns -and tuples. It does not allow confined clients to update or delete any -objects with this type. - -\fBsepgsql_ROLE_table_t\fP is a type for a role specific tables, columns -and tuples. It allows confined clients with its role to access with any -kind of operations except for relabeling. -It is the default type of newly created tables by confined clients with -its role, and we can use this type to describe role level separation. - -\fBsepgsql_proc_t\fP is a type for procedures. -It is attached for newly created procedures by unconfined clients. -It allows any clients to invoke procedures with this type. -All of PostgreSQL built-in functions are labeled as this type in the default. - -\fBsepgsql_ROLE_proc_t\fP is a type for a role specific procedure. -It is attached for newly created procedures by confined clients with its role. -It allows clients with same role to invoke procedure with this type. -Note that unconfined clients cannot invoke this type to avoid to execute -dangerous functions with unconfined authorities. They have to confirm its -contains and relabel to \fBsepgsql_proc_t\fP for its invocation. - -\fBsepgsql_trusted_proc_exec_t\fP is a type for trusted procedures. -To call procedures with this type invokes domain transition to -unconfined domain, so it can access any kind of database objects. -We can use this type to provide a secure method to access sensitive -information. - -\fBsepgsql_blob_t\fP is a type for binary large objects (blob). -It is attached for newly created blob in the default. -Non-administrative clients can read and write the blobs with this type. - -\fBsepgsql_ro_blob_t\fP is a type for read-only binary large objects (blob). -Non-administrative clients cannot write the blobs with this type. - -.SH "BACKUP and RESTORE" -\fI--enable-selinux\fP option in \fBsepg_dump\fP and \fBsepg_dumpall\fP enable to dump database image with security context. We can restore the dumped image using the standard \fIpg_restore\fP and so on. - -.EX -Example) -$ sepg_dump -Ft -b --enable-selinux postgres | gzip -c > postgres.tgz -.EE - -.SH AUTHOR -This manual page was written by KaiGai Kohei - -.SH "SEE ALSO" - -selinux(8), boolean(8) diff --git a/sepostgresql.init b/sepostgresql.init deleted file mode 100644 index 866aaba..0000000 --- a/sepostgresql.init +++ /dev/null @@ -1,212 +0,0 @@ -#!/bin/sh -# sepostgresql This is the init script for starting up SE-PostgreSQL -# -# chkconfig: - 62 38 -# description: Starts and stops the SE-PostgreSQL backend daemon -# processname: postmaster -# pidfile: /var/run/postmaster.pid -#--------------------------------------------------------------------- - -PGVERSION="8.4.1" -PGMAJORVERSION=`echo "$PGVERSION" | sed 's/^\([0-9]*\.[0-9a-z]*\).*$/\1/'` - -# source function library -. /etc/rc.d/init.d/functions - -# get config -. /etc/sysconfig/network - -# find the name of the script -NAME=`basename $0` -if [ ${NAME:0:1} = "S" -o ${NAME:0:1} = "K" ]; then - NAME=${NAME:3} -fi - -# set defaults for configurable variables -SEPGSQL_BIN="/usr/bin" -SEPGSQL_DATA="/var/lib/sepgsql/data" -SEPGSQL_OPTS="-i -p 5432" -SEPGSQL_STARTUP_LOG="/var/lib/sepgsql/pgstartup.log" -SEPGSQL_LOG="/var/log/sepostgresql.log" -SEPGSQL_FALLBACK_CONTEXT="user_u:user_r:user_t" - -# override defaults from /etc/sysconfig/sepostgresql -test -f /etc/sysconfig/${NAME} && . /etc/sysconfig/${NAME} - -export SEPGSQL_FALLBACK_CONTEXT - -# Check that networking is up. -test "${NETWORKING}" = "no" && exit 0 -test -f "${SEPGSQL_BIN}/sepostgres" || exit 1 - -script_result=0 - -do_start() { - PSQL_START=$"Starting ${NAME} service: " - echo -n "$PSQL_START" - - # make sure startup-time log file is valid - if [ ! -e "${SEPGSQL_STARTUP_LOG}" -a ! -h "${SEPGSQL_STARTUP_LOG}" ]; then - touch "${SEPGSQL_STARTUP_LOG}" || exit 1 - chown sepgsql:sepgsql "${SEPGSQL_STARTUP_LOG}" - chmod 600 "${SEPGSQL_STARTUP_LOG}" - /sbin/restorecon "${SEPGSQL_STARTUP_LOG}" - fi - - # check for the SEPGSQL_DATA structure - if [ -f "${SEPGSQL_DATA}/PG_VERSION" ] && [ -d "${SEPGSQL_DATA}/base" ]; then - if [ x`cat "${SEPGSQL_DATA}/PG_VERSION"` != x"${PGMAJORVERSION}" ]; then - echo_failure - echo - echo "HINT: An old version of the database format was found." - echo "HINT: You need to upgrade the data format before using SE-PostgreSQL." - exit 1 - fi - else - echo_failure - echo - echo "HINT: ${SEPGSQL_DATA} is missing." - echo "HINT: Use '/etc/init.d/${NAME} initdb'" - echo "HINT: to initialize the database cluster first." - exit 1 - fi - - # make sure SEPGSQL_LOG - touch ${SEPGSQL_LOG} - chown sepgsql:sepgsql ${SEPGSQL_LOG} - chmod 600 ${SEPGSQL_LOG} - test -x /sbin/restorecon && /sbin/restorecon ${SEPGSQL_LOG} - - cd ${SEPGSQL_BIN} - /sbin/runuser sepgsql -c "./sepg_ctl -w -t 10 -l ${SEPGSQL_LOG} -D ${SEPGSQL_DATA} -o '${SEPGSQL_OPTS}' start" \ - >> ${SEPGSQL_STARTUP_LOG} 2>&1 < /dev/null - sleep 1 - PID=`/sbin/runuser sepgsql -c "./sepg_ctl -D ${SEPGSQL_DATA} status 2>/dev/null \ - | sed 's/^.*PID: //g' | sed 's/[^0-9].*$//g'"` - if [ ${PIPESTATUS[0]} -eq 0 ]; then - echo "$PID" > "/var/run/${NAME}.pid" - touch "/var/lock/subsys/${NAME}.lock" - echo_success - else - script_result=1 - echo_failure - fi - echo -} - -do_stop() { - echo -n $"Stopping ${NAME} service: " - cd ${SEPGSQL_BIN} - /sbin/runuser sepgsql -c "./sepg_ctl -D ${SEPGSQL_DATA} stop" \ - >> ${SEPGSQL_STARTUP_LOG} 2>&1 < /dev/null - ret=$? - if [ $ret -eq 0 ]; then - echo_success - else - echo_failure - script_result=1 - fi - echo - rm -f "/var/run/${NAME}.pid" - rm -f "/var/lock/subsys/${NAME}.lock" -} - -do_status() { - cd ${SEPGSQL_BIN} - /sbin/runuser sepgsql -- -c "./sepg_ctl -D ${SEPGSQL_DATA} status" 2>/dev/null \ - | head -1 | sed "s/^sepg_ctl:/${NAME}:/g" - - if [ ${PIPESTATUS[0]} -ne 0 ]; then - script_result=3 - test -e "/var/run/${NAME}.pid" && script_result=1 - test -e "/var/lock/subsys/${NAME}.lock" && script_result=2 - fi -} - -do_condrestart() { - cd ${SEPGSQL_BIN} - /sbin/runuser sepgsql -- -c "./sepg_ctl -D ${SEPGSQL_DATA} status" &>/dev/null && do_stop && do_start -} - -do_condstop() { - cd ${SEPGSQL_BIN} - /sbin/runuser sepgsql -- -c "./sepg_ctl -D ${SEPGSQL_DATA} status" &>/dev/null && do_stop -} - -do_reload() { - echo -n $"Reloading ${NAME} service: " - cd ${SEPGSQL_BIN} - /sbin/runuser sepgsql -- -c "./sepg_ctl -D ${SEPGSQL_DATA} reload" &>/dev/null < /dev/null - if [ $? -eq 0 ]; then - echo_success - else - echo_failure - script_result=1 - fi - echo -} - -do_initdb() { - echo -n $"Initializing database: " - - if [ -f "${SEPGSQL_DATA}/PG_VERSION" ]; then - echo_failure - echo - echo "HINT: Data directory is not empty" - script_result=1 - else - if [ ! -e "${SEPGSQL_DATA}" -a ! -h "${SEPGSQL_DATA}" ]; then - mkdir -p "${SEPGSQL_DATA}" || exit 1 - chown sepgsql:sepgsql "${SEPGSQL_DATA}" - chmod 600 "${SEPGSQL_DATA}" - fi - # cleanup SELinux labeling for "${SEPGSQL_DATA}" - test -x /sbin/restorecon && /sbin/restorecon -R "${SEPGSQL_DATA}" - # Initialize the database - cd ${SEPGSQL_BIN} - /sbin/runuser -- sepgsql -c "./initdb.sepgsql --enable-selinux --pgdata='${SEPGSQL_DATA}' --auth='ident'" \ - >> "${SEPGSQL_STARTUP_LOG}" 2>&1 < /dev/null - if [ -f "${SEPGSQL_DATA}/PG_VERSION" ]; then - echo_success - else - echo_failure - script_result=1 - fi - echo - fi -} - -# see how we were called. -case "$1" in - start) - do_start - ;; - stop) - do_stop - ;; - status) - do_status - ;; - restart) - do_stop - do_start - ;; - condrestart) - do_condrestart - ;; - condstop) - do_condstop - ;; - reload|force-reload) - do_reload - ;; - initdb) - do_initdb - ;; - *) - echo $"Usage: $0 {start|stop|status|restart|condrestart|condstop|reload|force-reload|initdb}" - exit 1 - ;; -esac - -exit $script_result diff --git a/sepostgresql.logrotate b/sepostgresql.logrotate deleted file mode 100644 index 3ed7081..0000000 --- a/sepostgresql.logrotate +++ /dev/null @@ -1,11 +0,0 @@ -# logrotate configuration for SE-PostgreSQL - -/var/log/sepostgresql.log { - rotate 4 - compress - size 4M - notifempty - postrotate - /sbin/service sepostgresql restart >& /dev/null - endscript -} diff --git a/sepostgresql.spec b/sepostgresql.spec deleted file mode 100644 index 5fe52de..0000000 --- a/sepostgresql.spec +++ /dev/null @@ -1,473 +0,0 @@ -# -# Security Enhanced PostgreSQL (SE-PostgreSQL) -# -# Copyright 2007 KaiGai Kohei -# ----------------------------------------------------- - -# SE-PostgreSQL status extension -%define selinux_policy_stores targeted mls - -%{!?ssl:%define ssl 1} - -Summary: Security Enhanced PostgreSQL -Name: sepostgresql -Version: 8.4.2 -Release: 2488%{?dist} -License: BSD -Group: Applications/Databases -Url: http://code.google.com/p/sepgsql/ -Buildroot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) -Source0: ftp://ftp.postgresql.org/pub/source/v%{version}/postgresql-%{version}.tar.bz2 -Source1: sepostgresql.init -Source2: sepostgresql.8 -Source3: sepostgresql.logrotate -Patch0: sepostgresql-fedora-prefix.patch -Patch1: pgsql-01-8.4-blobs.patch -Patch2: pgsql-02-8.4-sepgsql.patch -BuildRequires: perl glibc-devel bison flex readline-devel zlib-devel >= 1.0.4 -BuildRequires: checkpolicy libselinux-devel >= 2.0.80 -BuildRequires: selinux-policy >= 3.6.8 -%if %{ssl} -BuildRequires: openssl-devel -%endif -Requires(pre): shadow-utils -Requires(post): policycoreutils /sbin/chkconfig -Requires(preun): /sbin/chkconfig /sbin/service -Requires(postun): policycoreutils -Requires: postgresql-server = %{version} -Requires: policycoreutils >= 2.0.16 libselinux >= 2.0.80 -Requires: selinux-policy >= 3.6.8 -Requires: tzdata logrotate - -%description -Security Enhanced PostgreSQL is an extension of PostgreSQL -based on SELinux security policy, that applies fine grained -mandatory access control to many objects within the database, -and takes advantage of user authorization integrated within -the operating system. SE-PostgreSQL works as a userspace -reference monitor to check any SQL query. - -%prep -%setup -q -n postgresql-%{version} -%patch0 -p1 -%patch1 -p1 -%patch2 -p1 - -%build -CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS -CXXFLAGS="${CXXFLAGS:-%optflags}" ; export CXXFLAGS - -# build SE-PostgreSQL -%configure --disable-rpath \ - --enable-selinux \ -%if %{ssl} - --with-openssl \ -%endif - --enable-debug \ - --enable-cassert \ - --libdir=%{_libdir}/pgsql \ - --datadir=%{_datadir}/sepgsql \ - --with-system-tzdata=/usr/share/zoneinfo - -# parallel build, if possible -make %{?_smp_mflags} -touch src/backend/security/sepgsql/policy/sepostgresql-devel.fc -make -C src/backend/security/sepgsql/policy - -%install -rm -rf %{buildroot} - -make DESTDIR=%{buildroot} install - -install -d %{buildroot}%{_datadir}/selinux/packages -install -p -m 644 src/backend/security/sepgsql/policy/sepostgresql-devel.pp \ - %{buildroot}%{_datadir}/selinux/packages - -# avoid to conflict with native postgresql package -mv %{buildroot}%{_bindir} %{buildroot}%{_bindir}.orig -install -d %{buildroot}%{_bindir} -mv %{buildroot}%{_bindir}.orig/initdb %{buildroot}%{_bindir}/initdb.sepgsql -mv %{buildroot}%{_bindir}.orig/pg_ctl %{buildroot}%{_bindir}/sepg_ctl -mv %{buildroot}%{_bindir}.orig/postgres %{buildroot}%{_bindir}/sepostgres -mv %{buildroot}%{_bindir}.orig/pg_dump %{buildroot}%{_bindir}/sepg_dump -mv %{buildroot}%{_bindir}.orig/pg_dumpall %{buildroot}%{_bindir}/sepg_dumpall - -# remove unnecessary files -rm -rf %{buildroot}%{_bindir}.orig -rm -rf %{buildroot}%{_libdir} -rm -rf %{buildroot}%{_includedir} -rm -rf %{buildroot}%{_datadir}/doc -rm -rf %{buildroot}%{_datadir}/sepgsql/timezone -rm -rf %{buildroot}%{_mandir} - -# /var/lib/sepgsql -install -d -m 700 %{buildroot}%{_localstatedir}/lib/sepgsql -install -d -m 700 %{buildroot}%{_localstatedir}/lib/sepgsql/data -install -d -m 700 %{buildroot}%{_localstatedir}/lib/sepgsql/backups - -# /etc/rc.d/init.d/* -mkdir -p %{buildroot}%{_initrddir} -install -p -m 755 %{SOURCE1} %{buildroot}%{_initrddir}/sepostgresql - -# /usr/share/man/* -mkdir -p %{buildroot}%{_mandir}/man8 -install -p -m 644 %{SOURCE2} %{buildroot}%{_mandir}/man8 - -# /etc/logrotate.d/ -mkdir -p %{buildroot}%{_sysconfdir}/logrotate.d -install -p -m 644 %{SOURCE3} %{buildroot}%{_sysconfdir}/logrotate.d/sepostgresql - -%clean -rm -rf %{buildroot} - -%pre -getent group sepgsql >/dev/null || groupadd -r sepgsql -getent passwd sepgsql >/dev/null || \ - useradd -r -g sepgsql -d %{_localstatedir}/lib/sepgsql -s /bin/bash \ - -c "SE-PostgreSQL server" sepgsql -exit 0 - -%post -/sbin/chkconfig --add %{name} -/sbin/ldconfig - -for store in %{selinux_policy_stores} -do - # clean up legacy policy module (now it is unnecessary) - %{_sbindir}/semodule -s ${store} -r sepostgresql >& /dev/null || : - if %{_sbindir}/semodule -s ${store} -l 2>/dev/null | grep -Eq "^sepostgresql-devel"; then - %{_sbindir}/semodule -s ${store} \ - -i %{_datadir}/selinux/packages/sepostgresql-devel.pp >& /dev/null || : - fi -done - -# Fix up non-standard file contexts -/sbin/fixfiles -R %{name} restore || : -/sbin/restorecon -R %{_localstatedir}/lib/sepgsql || : - -%preun -if [ $1 -eq 0 ]; then # rpm -e case - /sbin/service %{name} condstop >/dev/null 2>&1 - /sbin/chkconfig --del %{name} -fi - -%postun -/sbin/ldconfig -if [ $1 -ge 1 ]; then # rpm -U case - /sbin/service %{name} condrestart >/dev/null 2>&1 || : -fi -if [ $1 -eq 0 ]; then # rpm -e case - for store in %{selinux_policy_stores} - do - %{_sbindir}/semodule -s ${store} -r sepostgresql-devel >& /dev/null || : - done - /sbin/fixfiles -R %{name} restore || : - test -d %{_localstatedir}/lib/sepgsql && /sbin/restorecon -R %{_localstatedir}/lib/sepgsql || : -fi - -%files -%defattr(-,root,root,-) -%doc COPYRIGHT README -%{_initrddir}/sepostgresql -%{_sysconfdir}/logrotate.d/sepostgresql -%{_bindir}/initdb.sepgsql -%{_bindir}/sepg_ctl -%{_bindir}/sepostgres -%{_bindir}/sepg_dump -%{_bindir}/sepg_dumpall -%{_mandir}/man8/sepostgresql.* -%dir %{_datadir}/sepgsql -%{_datadir}/sepgsql/postgres.bki -%{_datadir}/sepgsql/postgres.description -%{_datadir}/sepgsql/postgres.shdescription -%{_datadir}/sepgsql/system_views.sql -%{_datadir}/sepgsql/*.sample -%{_datadir}/sepgsql/snowball_create.sql -%{_datadir}/sepgsql/timezonesets/ -%{_datadir}/sepgsql/tsearch_data/ -%{_datadir}/sepgsql/conversion_create.sql -%{_datadir}/sepgsql/information_schema.sql -%{_datadir}/sepgsql/sql_features.txt -%attr(644,root,root) %{_datadir}/selinux/packages/sepostgresql-devel.pp -%attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql -%attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/data -%attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/backups - -%changelog -* Mon Feb 15 2010 KaiGai Kohei - 8.4.2-2488 -- fix: build failed due to an implicit header file include -- update: feature backport from v8.5 development - -* Wed Dec 16 2009 KaiGai Kohei - 8.4.2-2487 -- upgrade base version 8.4.1->8.4.2 - -* Fri Dec 8 2009 KaiGai Kohei - 8.4.1-2464 -- rework: backport features from v8.5devel tree -- fixbug: selinux netlink receiver process didn't have correct ps display - -* Fri Sep 11 2009 KaiGai Kohei - 8.4.1-2305 -- Upgrade base SE-PostgreSQL v8.4.0->v8.4.1 -- rework: backport features from v8.5devel tree - -* Fri Aug 21 2009 Tomas Mraz - 8.4.0-2238 -- rebuilt with new openssl - -* Wed Aug 19 2009 KaiGai Kohei - 8.4.0-2237 -- Upgrade SE-PostgreSQL to 8.4.x series - -* Sun Jul 26 2009 Fedora Release Engineering - 8.3.7-1991 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild - -* Tue Jun 9 2009 KaiGai Kohei - 8.3.7-1990 -- backport features from v8.4devel, it also needs libselinux-2.0.80 - -* Fri Apr 17 2009 KaiGai Kohei - 8.3.7-1772 -- bugfix: /etc/init.d/sepostgresql initdb didn't work correctly - -* Fri Mar 27 2009 KaiGai Kohei - 8.3.7-1770 -- upgrade base PostgreSQL versin 8.3.6->8.3.7 -- backport features from v8.4devel - -* Thu Feb 26 2009 KaiGai Kohei - 8.3.6-2.1635 -- bugfix: possible information leak by the order of permission checks - in row level permission checks. - -* Wed Feb 25 2009 Fedora Release Engineering - 8.3.6-3.1518 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild - -* Fri Feb 6 2009 - 8.3.6-2.1523 -- upgrade base PostgreSQL version 8.3.5->8.3.6 -- backport features from 8.4devel tree -- security policy fix for Fedora 9 - -* Sat Jan 17 2009 Tomas Mraz - 8.3.5-2.1183 -- rebuild with new openssl - -* Wed Nov 5 2008 - 8.3.5-2.1182 -- upgrade base PostgreSQL version 8.3.4->8.3.5 -- backport cumulative bugfixes from 8.4devel series - -* Thu Oct 2 2008 - 8.3.4-2.1076 -- bugfix: "(null)" audit logs for non-cached decision making. -- A hook is added for "COPY TO/FROM " cases. - -* Sat Sep 27 2008 - 8.3.4-2.1066 -- update base version to 8.3.4 -- sepostgresql.pp was marked as obsolute - -* Tue Sep 23 2008 - 8.3.3-2.1043 -- bugfix: a case when INSERT a FK reference to invisible PK - -* Wed Aug 13 2008 - 8.3.3-2.964 -- bugfix: trusted procedure invokation - -* Fri Jul 11 2008 - 8.3.3-2.952 -- Security policy module updates - -* Fri Jul 11 2008 - 8.3.3-2.945 -- Add OpenSSL support -- backport 8.4devel fixes - -* Sun Jun 15 2008 - 8.3.3-2.889 -- backport 8.4devel features. - -* Fri Jun 13 2008 - 8.3.3-2.869 -- upgrade base PostgreSQL 8.3.1 -> 8.3.3 - -* Wed Apr 30 2008 - 8.3.1-2.197 -- Inconsistent version number format at Changelogs - -* Wed Apr 30 2008 - 8.3.1-2.196 -- BUGFIX: ROW-level control did not work correctly on TRUNCATE - -* Sun Mar 9 2008 - 8.3.0-2.129 -- BUGFIX: more conprehensive fixes in "SELECT COUNT(*) ..." - -* Sun Mar 2 2008 - 8.3.0-2.120 -- BUGFIX: CREATE TABLE statement with explicit labeled columns -- BUGFIX: SELECT count(*) does not filter unallowed tuples - -* Wed Feb 27 2008 - 8.3.0-2.117 -- ".beta" removed. - -* Wed Feb 27 2008 - 8.3.0-2.114 -- Security policy updates - -* Tue Feb 26 2008 - 8.3.0-2.113 -- BUGFIX: CREATE/ALTER TABLE with CONTEXT='...' did nothing. - -* Thu Feb 7 2008 - 8.3.0-2.108 -- add /etc/logrotate.d/sepostgresql - -* Thu Feb 7 2008 - 8.3.0-2.105 -- update base version to stable 8.3.0 -- add tzdata dependency -- allow db_database:{get_param set_param} for generic domain -- error message cleanups -- Improve large object hooks in PGACE framework -- BUGFIX: db_blob:{drop} was checked at loread() -- BUGFIX: incorrect permission in DELETE with RETURNING clause -- incorrect permission when we read and update security_context in same time. - -* Fri Jan 25 2008 - 8.3RC2-2.62 -- BUGFIX: add handling to invalid contexts already stored - -* Tue Jan 22 2008 - 8.3RC2-2.56 -- BUGFIX: lack of locks when refering buffer pages at update/delete hooks -- BUGFIX: explicit labeling using SELECT ... INTO statement. - -* Sun Jan 20 2008 - 8.3RC2-2.52 -- shares /usr/lib/pgsql/*.so libraries, with original postgresql. - -* Thu Jan 10 2008 - 8.3RC1-2.37 -- add sepg_dump/sepg_dumpall support for 8.3base package. - -* Mon Nov 26 2007 - 8.3beta3-2.0 -- Branch from 8.2.x tree - -* Wed Nov 21 2007 - 8.2.5-1.66 -- Add a policy module hotfix for labeled networking - -* Thu Nov 1 2007 - 8.2.5-1.51 -- Re-organize repository to prepare to branch 8.3.x based tree. - (no differences from 8.2.5-1.33) - -* Wed Oct 17 2007 - 8.2.5-1.33 -- Fix bug: security context was not canonicalized - when irregular context (but interpretable) was inputed. - -* Mon Oct 15 2007 - 8.2.5-1.31 -- Fix bug: type definitions of security_label_to_text() - and text_to_security_label() are mismatched. - -* Sat Sep 22 2007 - 8.2.5-1.23 -- update base PostgreSQL to 8.2.5 - -* Mon Sep 1 2007 - 8.2.4-1.0 -- mark as SE-PostgreSQL 8.2.4-1.0 - -* Thu Aug 28 2007 - 8.2.4-0.434.beta -- add Requires: postgresql-server, instead of Conflicts: tag - (Some sharable files are removed from sepostgresql package) - -* Fri Aug 24 2007 - 8.2.4-0.429.beta -- add policycoreutils to Requires(post/postun) -- upstreamed selinux-policy got SE-PostgreSQL related object classes definition. - -* Sat Aug 18 2007 - 8.2.4-0.427.beta -- sepg_dumpall uses /usr/bin/sepg_dump - -* Fri Aug 17 2007 - 8.2.4-0.423.beta -- fix policy not to execute sepgsql_user_proc_t from administrative domain - -* Fri Aug 10 2007 - 8.2.4-0.418.beta -- object classes are renamed with "db_" prefix -- /etc/init.d/sepostgresql script is improved. - -* Thu Aug 2 2007 - 8.2.4-0.409.beta -- specfile updated based on the following comments - https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=249522#c5 - -* Mon Jul 30 2007 - 8.2.4-0.407.beta -- fix spec file based on Fedora reviewing process -- add rawhide support - -* Mon Jul 23 2007 - 8.2.4-0.402.beta -- add manpage of sepostgresql -- fix specfile convention for Fedora suitable - -* Sun Jul 15 2007 - 8.2.4-0.398.beta -- SECCLASS_DATABASE is updated (fc7->62, fc6->61) - -* Sun Jul 1 2007 - 8.2.4-0.391.beta -- Mark as a beta version. - -* Sat Jun 30 2007 - 8.2.4-0.384.alpha -- add fallback context support with $SEPGSQL_FALLBACK_CONTEXT -- add sepgsql_enable_users_ddl boolean to restrict sepgsql_sysobj_t -- BUGFIX: incorrect inherited attribute expanding for RECORD type (attno=0) -- BUGFIX: trigger functions were not checked in COPY FROM statement - -* Tue Jun 26 2007 - 8.2.4-0.376.alpha -- add pgaceExecutorStart() to hook ExecutorStart() - -* Mon Jun 25 2007 - 8.2.4-0.372.alpha -- add table name prefix for column name on audit messages -- use security_label_raw_in as an alternative for security_label_in -- add hook for query execution path with SPI_ interface -- add trigger function suppoer -- BUGFIX: remove unnecessary checks for COPY TO/FROM on non-table relation -- BUGFIX: remove unnecessary checks for LOCK on non-table relation -- BUGFIX: incorrect object id for tuples within pg_security -- BUGFIX: CommandCounterIncrement() might be called during heap_create_with_catalog. -- BUGFIX: correct self-deadlock -- update security policy: sepgsql_sysobj_t, sepgsql_user_proc_t, sepgsql_ro_blob_t - -* Tue Jun 19 2007 - 8.2.4-0.351.alpha -- BUGFIX: sepgsql_compute_avc_datum() accessed userspace AVC without - holding any lock. -- improve build scripts. - -* Sat Jun 16 2007 - 8.2.4-0.320.alpha -- update: sepostgresql.pp security policy fot strict/mls suitable -- BUGFIX: column:drop evaluation for ALTER TABLE tbl DROP col; statement -- add --enable-security option for pg_dumpall command -- add {use} permission for table/column/tuple object classes - -* Tue May 29 2007 - 8.2.4-0.306.alpha -- BUGFIX: RangeTblEntry->requiredPerms are polluted. - -* Sun May 27 2007 - 8.2.4-0.304.alpha -- add support for dynamic object class/access vector mapping -- BUGFIX: Lack of implicit labeling on COPY FROM statement for system catalogs -- BUGFIX: Incorrect security context handling for inherited tables - -* Fri May 25 2007 - 8.2.4-0.292.alpha -- add pg_dump/pg_dumpall/pg_restore with --enable-security option -- add support on OUTER JOIN by rewriting query. -- add security_context support on COPY TO/FROM statement -- add unlabeled security context support (enable to obtain /selinux/initial_contexts/*) -- BUGFIX: lack of checks on JOIN ON condition -- BUGFIX: pseudo relation object (sequence, toast, ...) are not handled as database obj. -- BUGFIX: lack of tuple:insert checks at COPY FROM statement -- BUGFIX: server crash when CREATE TABLE command with newly defined CONTEXT = '...'. - -* Wed May 16 2007 - 8.2.4-0.266.alpha -- BUGFIX: incorrect security context of newly generated system object. -- BUGFIX: missing error text when audit log is disabled. -- BUGFIX: incorrect Oid of newly generated tuples within pg_security. -- BUGFIX: sepgsql_enable_audittuple is misconditioned. -- add checks for T_RowExpr/T_RowCompareExpr/T_BooleanTest - T_DistinctExpr/T_ConvertRowtypeExpr -- add support CONTEXT = 'xxx' for CREATE TABLE/FUNCTION/DATABASE statement - -* Sun Apr 30 2007 - 8.2.4-0.240.alpha -- update base version 8.2.3 -> 8.2.4 -- BUGFIX: unexpected expose in OUTER JOIN statement. - add rewrite OUTER JOIN into SUBQUERY to ensure filtering violated tuples. -- BUGFIX: strange operation in text_to_security_label() -- BUGFIX: infinite recursive call on security label -> oid mapping -- BUGFIX: sepgsql_avc_init() is called in policy state monitoring process - to avoid nonsense initialization of avc_shmem. - -* Fri Apr 27 2007 - 8.2.3-0.232.alpha -- object class numbers were redefined. (SECCLASS_DATABASE got into 61) -- is_selinux_enabled() was cached on the shared memory segment. -- BUGFIX: server went into infinit loop on foreign key constraint. - -* Mon Apr 16 2007 - 8.2.3-0.226.alpha -- BUGFIX: cases when several variables with same type in a single table - -* Sat Apr 07 2007 - 8.2.3-0.214.alpha -- add the first implementation of SE-PostgreSQL on PGACE framework - -* Wed Mar 21 2007 - 8.2.3-0.212.alpha -- BUGFIX: SetOperation didn't handle its subquery correctly. - So, it caused server crash. - -* Wed Mar 07 2007 - 8.2.3-0.209.alpha -- BUGFIX: var->varlevelsup was ignored, so outer references - from subqueries cause a fault. - -* Tue Feb 27 2007 - 8.2.3-0.178.alpha -- Initial RPM build diff --git a/sources b/sources deleted file mode 100644 index 0aef01a..0000000 --- a/sources +++ /dev/null @@ -1 +0,0 @@ -d738227e2f1f742d2f2d4ab56496c5c6 postgresql-8.4.2.tar.bz2