From 62301f6c4e04faaa989010c1b3e0af63757909db Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Thu, 18 Mar 2010 01:11:03 +0000 Subject: [PATCH] upgrade base postgresql 8.4.2->8.4.3 --- .cvsignore | 2 +- pgsql-01-8.4-blobs.patch | 4800 +++-- pgsql-02-8.4-sepgsql.patch | 37849 +++++++++++++++++++---------------- sepostgresql.spec | 7 +- sources | 2 +- 5 files changed, 22860 insertions(+), 19800 deletions(-) diff --git a/.cvsignore b/.cvsignore index 227e30f..8fb639c 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1 +1 @@ -postgresql-8.4.2.tar.bz2 +postgresql-8.4.3.tar.bz2 diff --git a/pgsql-01-8.4-blobs.patch b/pgsql-01-8.4-blobs.patch index fefc54a..b2ef970 100644 --- a/pgsql-01-8.4-blobs.patch +++ b/pgsql-01-8.4-blobs.patch @@ -1,2210 +1,2600 @@ -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 -+-------- +diff -Nrpc base/contrib/lo/lo_test.sql blob/contrib/lo/lo_test.sql +*** base/contrib/lo/lo_test.sql Sat Nov 17 20:15:40 2007 +--- blob/contrib/lo/lo_test.sql Fri Dec 18 09:40:55 2009 +*************** SET search_path = public; +*** 12,18 **** + -- + + -- Check what is in pg_largeobject +! SELECT count(DISTINCT loid) FROM pg_largeobject; + + -- ignore any errors here - simply drop the table if it already exists + DROP TABLE a; +--- 12,18 ---- + -- + + -- Check what is in pg_largeobject +! SELECT count(oid) FROM pg_largeobject_metadata; + + -- ignore any errors here - simply drop the table if it already exists + DROP TABLE a; +*************** DELETE FROM a; +*** 74,79 **** + DROP TABLE a; + + -- Check what is in pg_largeobject ... if different from original, trouble +! SELECT count(DISTINCT loid) FROM pg_largeobject; + + -- end of tests +--- 74,79 ---- + DROP TABLE a; + + -- Check what is in pg_largeobject ... if different from original, trouble +! SELECT count(oid) FROM pg_largeobject_metadata; + + -- end of tests +diff -Nrpc base/contrib/vacuumlo/vacuumlo.c blob/contrib/vacuumlo/vacuumlo.c +*** base/contrib/vacuumlo/vacuumlo.c Mon Mar 2 13:43:07 2009 +--- blob/contrib/vacuumlo/vacuumlo.c Fri Dec 18 09:40:55 2009 +*************** vacuumlo(char *database, struct _param * +*** 142,148 **** + */ + buf[0] = '\0'; + strcat(buf, "CREATE TEMP TABLE vacuum_l AS "); +! strcat(buf, "SELECT DISTINCT loid AS lo FROM pg_largeobject "); + res = PQexec(conn, buf); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { +--- 142,151 ---- + */ + buf[0] = '\0'; + strcat(buf, "CREATE TEMP TABLE vacuum_l AS "); +! 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 -Nrpc base/src/backend/catalog/Makefile blob/src/backend/catalog/Makefile +*** base/src/backend/catalog/Makefile Wed May 13 11:30:07 2009 +--- blob/src/backend/catalog/Makefile Fri Dec 18 09:40:55 2009 +*************** POSTGRES_BKI_SRCS = $(addprefix $(top_sr +*** 29,37 **** + 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_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 \ +--- 29,37 ---- + 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_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 -Nrpc base/src/backend/catalog/aclchk.c blob/src/backend/catalog/aclchk.c +*** base/src/backend/catalog/aclchk.c Thu Mar 18 01:40:54 2010 +--- blob/src/backend/catalog/aclchk.c Thu Mar 18 09:43:03 2010 +*************** +*** 30,35 **** +--- 30,37 ---- + #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" +*************** static void ExecGrant_Fdw(InternalGrant +*** 57,62 **** +--- 59,65 ---- + 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); + +*************** restrict_and_check_grant(bool is_grant, +*** 200,205 **** +--- 203,211 ---- + 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; +*************** ExecuteGrantStmt(GrantStmt *stmt) +*** 380,385 **** +--- 386,395 ---- + 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"); +*************** ExecGrantStmt_oids(InternalGrant *istmt) +*** 485,490 **** +--- 495,503 ---- + case ACL_OBJECT_LANGUAGE: + ExecGrant_Language(istmt); + break; ++ case ACL_OBJECT_LARGEOBJECT: ++ ExecGrant_Largeobject(istmt); ++ break; + case ACL_OBJECT_NAMESPACE: + ExecGrant_Namespace(istmt); + break; +*************** objectNamesToOids(GrantObjectType objtyp +*** 569,574 **** +--- 582,601 ---- + ReleaseSysCache(tuple); + } + break; ++ case ACL_OBJECT_LARGEOBJECT: ++ foreach(cell, objnames) ++ { ++ Oid lobjOid = intVal(lfirst(cell)); + -+(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 -+-------- ++ if (!LargeObjectExists(lobjOid)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_OBJECT), ++ errmsg("large object %u does not exist", ++ lobjOid))); + -+(1 row) -+ -+SELECT loread(lo_open(1004, x'40000'::int), 32); -+ loread -+-------- ++ objects = lappend_oid(objects, lobjOid); ++ } ++ break; + case ACL_OBJECT_NAMESPACE: + foreach(cell, objnames) + { +*************** ExecGrant_Language(InternalGrant *istmt) +*** 1782,1787 **** +--- 1809,1946 ---- + } + + static void ++ ExecGrant_Largeobject(InternalGrant *istmt) ++ { ++ Relation relation; ++ ListCell *cell; + -+(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 -+-------- ++ if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS) ++ istmt->privileges = ACL_ALL_RIGHTS_LARGEOBJECT; + -+(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 -+-------- ++ relation = heap_open(LargeObjectMetadataRelationId, ++ RowExclusiveLock); + -+(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; ++ 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; +*************** static const char *const no_priv_msg[MAX +*** 2121,2126 **** +--- 2280,2287 ---- + 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 */ +*************** static const char *const not_owner_msg[M +*** 2159,2164 **** +--- 2320,2327 ---- + 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 */ +*************** pg_aclmask(AclObjectKind objkind, Oid ta +*** 2278,2283 **** +--- 2441,2449 ---- + 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: +*************** pg_language_aclmask(Oid lang_oid, Oid ro +*** 2661,2666 **** +--- 2827,2916 ---- + } + + /* ++ * 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 +*************** pg_language_aclcheck(Oid lang_oid, Oid r +*** 3111,3116 **** +--- 3361,3380 ---- + } + + /* ++ * 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 +*************** pg_language_ownercheck(Oid lan_oid, Oid +*** 3301,3306 **** +--- 3565,3617 ---- + } + + /* ++ * 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 -Nrpc base/src/backend/catalog/dependency.c blob/src/backend/catalog/dependency.c +*** base/src/backend/catalog/dependency.c Tue Dec 15 17:16:51 2009 +--- blob/src/backend/catalog/dependency.c Fri Dec 18 09:40:55 2009 +*************** +*** 36,41 **** +--- 36,42 ---- + #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" +*************** static const Oid object_classes[MAX_OCLA +*** 129,134 **** +--- 130,136 ---- + ConversionRelationId, /* OCLASS_CONVERSION */ + AttrDefaultRelationId, /* OCLASS_DEFAULT */ + LanguageRelationId, /* OCLASS_LANGUAGE */ ++ LargeObjectRelationId, /* OCLASS_LARGEOBJECT */ + OperatorRelationId, /* OCLASS_OPERATOR */ + OperatorClassRelationId, /* OCLASS_OPCLASS */ + OperatorFamilyRelationId, /* OCLASS_OPFAMILY */ +*************** doDeletion(const ObjectAddress *object) +*** 1071,1076 **** +--- 1073,1082 ---- + DropProceduralLanguageById(object->objectId); + break; + ++ case OCLASS_LARGEOBJECT: ++ LargeObjectDrop(object->objectId); ++ break; ++ + case OCLASS_OPERATOR: + RemoveOperatorById(object->objectId); + break; +*************** getObjectClass(const ObjectAddress *obje +*** 1984,1989 **** +--- 1990,1999 ---- + 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; +*************** getObjectDescription(const ObjectAddress +*** 2232,2237 **** +--- 2242,2251 ---- + ReleaseSysCache(langTup); + break; + } ++ case OCLASS_LARGEOBJECT: ++ appendStringInfo(&buffer, _("large object %u"), ++ object->objectId); ++ break; + + case OCLASS_OPERATOR: + appendStringInfo(&buffer, _("operator %s"), +diff -Nrpc base/src/backend/catalog/pg_largeobject.c blob/src/backend/catalog/pg_largeobject.c +*** base/src/backend/catalog/pg_largeobject.c Sat Jan 3 13:01:35 2009 +--- blob/src/backend/catalog/pg_largeobject.c Fri Dec 18 09:40:55 2009 +*************** +*** 16,23 **** +--- 16,31 ---- + + #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,139 **** + /* + * 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. + */ +! void + LargeObjectCreate(Oid loid) + { +! Relation pg_largeobject; + HeapTuple ntup; +! Datum values[Natts_pg_largeobject]; +! bool nulls[Natts_pg_largeobject]; +! int i; + +! pg_largeobject = heap_open(LargeObjectRelationId, RowExclusiveLock); + + /* +! * Form new tuple + */ +! for (i = 0; i < Natts_pg_largeobject; i++) +! { +! values[i] = (Datum) NULL; +! nulls[i] = false; +! } + +! i = 0; +! values[i++] = ObjectIdGetDatum(loid); +! values[i++] = Int32GetDatum(0); +! values[i++] = DirectFunctionCall1(byteain, +! CStringGetDatum("")); + +! ntup = heap_form_tuple(pg_largeobject->rd_att, values, nulls); + +! /* +! * Insert it +! */ +! simple_heap_insert(pg_largeobject, ntup); + +! /* Update indexes */ +! CatalogUpdateIndexes(pg_largeobject, ntup); + +! heap_close(pg_largeobject, RowExclusiveLock); + +! heap_freetuple(ntup); + } + + void + LargeObjectDrop(Oid loid) + { +! bool found = false; + Relation pg_largeobject; + ScanKeyData skey[1]; +! SysScanDesc sd; + HeapTuple tuple; + + ScanKeyInit(&skey[0], +! Anum_pg_largeobject_loid, + BTEqualStrategyNumber, F_OIDEQ, +! ObjectIdGetDatum(loid)); + +! pg_largeobject = heap_open(LargeObjectRelationId, RowExclusiveLock); + +! sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true, +! SnapshotNow, 1, skey); + +! while ((tuple = systable_getnext(sd)) != NULL) + { + simple_heap_delete(pg_largeobject, &tuple->t_self); +- found = true; + } + +! systable_endscan(sd); + + heap_close(pg_largeobject, RowExclusiveLock); + +! if (!found) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("large object %u does not exist", loid))); + } + + bool + LargeObjectExists(Oid loid) + { + 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, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(loid)); + +! pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock); + +! sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true, + SnapshotNow, 1, skey); + +! if (systable_getnext(sd) != NULL) + retval = true; + + systable_endscan(sd); + +! heap_close(pg_largeobject, AccessShareLock); + + return retval; + } +--- 35,292 ---- + /* + * Create a large object having the given LO identifier. + * +! * 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. + */ +! Oid + LargeObjectCreate(Oid loid) + { +! Relation pg_lo_meta; + HeapTuple ntup; +! Oid loid_new; +! Datum values[Natts_pg_largeobject_metadata]; +! bool nulls[Natts_pg_largeobject_metadata]; + +! pg_lo_meta = heap_open(LargeObjectMetadataRelationId, +! RowExclusiveLock); + + /* +! * Insert metadata of the largeobject + */ +! memset(values, 0, sizeof(values)); +! memset(nulls, false, sizeof(nulls)); + +! values[Anum_pg_largeobject_metadata_lomowner - 1] +! = ObjectIdGetDatum(GetUserId()); +! nulls[Anum_pg_largeobject_metadata_lomacl - 1] = true; +! +! ntup = heap_form_tuple(RelationGetDescr(pg_lo_meta), +! values, nulls); +! if (OidIsValid(loid)) +! HeapTupleSetOid(ntup, loid); + +! loid_new = simple_heap_insert(pg_lo_meta, ntup); +! Assert(!OidIsValid(loid) || loid == loid_new); + +! 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) + { +! Relation pg_lo_meta; + Relation pg_largeobject; + ScanKeyData skey[1]; +! 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], +! ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, +! ObjectIdGetDatum(loid)); + +! scan = systable_beginscan(pg_lo_meta, +! LargeObjectMetadataOidIndexId, 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)); + +! scan = systable_beginscan(pg_largeobject, +! LargeObjectLOidPNIndexId, true, +! SnapshotNow, 1, skey); +! while (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + simple_heap_delete(pg_largeobject, &tuple->t_self); + } + +! systable_endscan(scan); + + heap_close(pg_largeobject, RowExclusiveLock); + +! 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; + + ScanKeyInit(&skey[0], +! ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(loid)); + +! pg_lo_meta = heap_open(LargeObjectMetadataRelationId, +! AccessShareLock); + +! sd = systable_beginscan(pg_lo_meta, +! LargeObjectMetadataOidIndexId, true, + SnapshotNow, 1, skey); + +! tuple = systable_getnext(sd); +! if (HeapTupleIsValid(tuple)) + retval = true; + + systable_endscan(sd); + +! heap_close(pg_lo_meta, AccessShareLock); + + return retval; + } +diff -Nrpc base/src/backend/catalog/pg_shdepend.c blob/src/backend/catalog/pg_shdepend.c +*** base/src/backend/catalog/pg_shdepend.c Thu Jun 18 10:20:52 2009 +--- blob/src/backend/catalog/pg_shdepend.c Fri Dec 18 09:40:55 2009 +*************** +*** 24,29 **** +--- 24,30 ---- + #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" +*************** shdepDropOwned(List *roleids, DropBehavi +*** 1210,1215 **** +--- 1211,1219 ---- + case LanguageRelationId: + istmt.objtype = ACL_OBJECT_LANGUAGE; + break; ++ case LargeObjectRelationId: ++ istmt.objtype = ACL_OBJECT_LARGEOBJECT; ++ break; + case NamespaceRelationId: + istmt.objtype = ACL_OBJECT_NAMESPACE; + break; +*************** shdepReassignOwned(List *roleids, Oid ne +*** 1365,1370 **** +--- 1369,1378 ---- + AlterLanguageOwner_oid(sdepForm->objid, newrole); + break; + ++ case LargeObjectRelationId: ++ LargeObjectAlterOwner(sdepForm->objid, newrole); ++ break; ++ + default: + elog(ERROR, "unexpected classid %d", sdepForm->classid); + break; +diff -Nrpc base/src/backend/commands/alter.c blob/src/backend/commands/alter.c +*** base/src/backend/commands/alter.c Sat Jan 3 13:01:35 2009 +--- blob/src/backend/commands/alter.c Fri Dec 18 09:40:55 2009 +*************** +*** 15,20 **** +--- 15,21 ---- + #include "postgres.h" + + #include "catalog/namespace.h" ++ #include "catalog/pg_largeobject.h" + #include "commands/alter.h" + #include "commands/conversioncmds.h" + #include "commands/dbcommands.h" +*************** ExecAlterOwnerStmt(AlterOwnerStmt *stmt) +*** 233,238 **** +--- 234,243 ---- + 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 -Nrpc base/src/backend/commands/comment.c blob/src/backend/commands/comment.c +*** base/src/backend/commands/comment.c Thu Jun 18 10:20:52 2009 +--- blob/src/backend/commands/comment.c Fri Dec 18 09:40:55 2009 +*************** +*** 25,30 **** +--- 25,31 ---- + #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,47 **** +--- 43,49 ---- + #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" +*************** CommentLargeObject(List *qualname, char +*** 1422,1428 **** + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("large object %u does not exist", loid))); + +! /* Call CreateComments() to create/drop the comments */ + CreateComments(loid, LargeObjectRelationId, 0, comment); + } + +--- 1424,1443 ---- + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("large object %u does not exist", loid))); + +! /* 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 -Nrpc base/src/backend/commands/tablecmds.c blob/src/backend/commands/tablecmds.c +*** base/src/backend/commands/tablecmds.c Tue Dec 15 17:16:51 2009 +--- blob/src/backend/commands/tablecmds.c Fri Dec 18 09:40:55 2009 +*************** ATExecAlterColumnType(AlteredTableInfo * +*** 5902,5907 **** +--- 5902,5908 ---- + case OCLASS_CAST: + case OCLASS_CONVERSION: + case OCLASS_LANGUAGE: ++ case OCLASS_LARGEOBJECT: + case OCLASS_OPERATOR: + case OCLASS_OPCLASS: + case OCLASS_OPFAMILY: +diff -Nrpc base/src/backend/libpq/be-fsstubs.c blob/src/backend/libpq/be-fsstubs.c +*** base/src/backend/libpq/be-fsstubs.c Thu Jun 18 10:20:52 2009 +--- blob/src/backend/libpq/be-fsstubs.c Fri Dec 18 09:40:55 2009 +*************** +*** 42,55 **** +--- 42,61 ---- + #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 +*************** lo_read(int fd, char *buf, int len) +*** 156,161 **** +--- 162,178 ---- + (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; +*************** lo_write(int fd, const char *buf, int le +*** 177,182 **** +--- 194,210 ---- + 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; +*************** lo_unlink(PG_FUNCTION_ARGS) +*** 251,256 **** +--- 279,291 ---- + { + 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. + */ +*************** lo_truncate(PG_FUNCTION_ARGS) +*** 482,487 **** +--- 517,533 ---- + (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 -Nrpc base/src/backend/parser/gram.y blob/src/backend/parser/gram.y +*** base/src/backend/parser/gram.y Sun Sep 6 19:40:49 2009 +--- blob/src/backend/parser/gram.y Fri Dec 18 09:40:55 2009 +*************** static TypeName *TableFuncTypeName(List +*** 378,383 **** +--- 378,384 ---- + %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 +*************** privilege_target: +*** 4379,4384 **** +--- 4380,4392 ---- + 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)); +*************** AlterOwnerStmt: ALTER AGGREGATE func_nam +*** 5506,5511 **** +--- 5514,5527 ---- + 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); +*************** SignedIconst: Iconst { $$ = $1; } +*** 10066,10071 **** +--- 10082,10091 ---- + | '-' Iconst { $$ = - $2; } + ; + ++ Iconst_list: Iconst { $$ = list_make1(makeInteger($1)); } ++ | Iconst_list ',' Iconst { $$ = lappend($1, makeInteger($3)); } ++ ; ++ + /* + * Name classification hierarchy. + * +diff -Nrpc base/src/backend/storage/large_object/inv_api.c blob/src/backend/storage/large_object/inv_api.c +*** base/src/backend/storage/large_object/inv_api.c Thu Jun 18 10:20:52 2009 +--- blob/src/backend/storage/large_object/inv_api.c Fri Dec 18 09:40:55 2009 +*************** +*** 32,49 **** +--- 32,54 ---- + + #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" + + +*************** close_lo_relation(bool isCommit) +*** 139,168 **** + static bool + myLargeObjectExists(Oid loid, Snapshot snapshot) + { + bool retval = false; +- Relation pg_largeobject; +- ScanKeyData skey[1]; +- SysScanDesc sd; + +- /* +- * See if we can find any tuples belonging to the specified LO +- */ + ScanKeyInit(&skey[0], +! Anum_pg_largeobject_loid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(loid)); + +! pg_largeobject = heap_open(LargeObjectRelationId, AccessShareLock); + +! sd = systable_beginscan(pg_largeobject, LargeObjectLOidPNIndexId, true, + snapshot, 1, skey); + +! if (systable_getnext(sd) != NULL) + retval = true; + + systable_endscan(sd); + +! heap_close(pg_largeobject, AccessShareLock); + + return retval; + } +--- 144,174 ---- + static bool + myLargeObjectExists(Oid loid, Snapshot snapshot) + { ++ Relation pg_lo_meta; ++ ScanKeyData skey[1]; ++ SysScanDesc sd; ++ HeapTuple tuple; + bool retval = false; + + ScanKeyInit(&skey[0], +! ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(loid)); + +! pg_lo_meta = heap_open(LargeObjectMetadataRelationId, +! AccessShareLock); + +! sd = systable_beginscan(pg_lo_meta, +! LargeObjectMetadataOidIndexId, true, + snapshot, 1, skey); + +! tuple = systable_getnext(sd); +! if (HeapTupleIsValid(tuple)) + retval = true; + + systable_endscan(sd); + +! heap_close(pg_lo_meta, AccessShareLock); + + return retval; + } +*************** getbytealen(bytea *data) +*** 193,223 **** + Oid + inv_create(Oid lobjId) + { + /* +! * 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. + */ +! if (!OidIsValid(lobjId)) +! { +! open_lo_relation(); +! +! lobjId = GetNewOidWithIndex(lo_heap_r, LargeObjectLOidPNIndexId, +! Anum_pg_largeobject_loid); +! } + + /* +! * Create the LO by writing an empty first page for it in pg_largeobject +! * (will fail if duplicate) + */ +! LargeObjectCreate(lobjId); +! + /* + * Advance command counter to make new tuple visible to later operations. + */ + CommandCounterIncrement(); + +! return lobjId; + } + + /* +--- 199,229 ---- + Oid + inv_create(Oid lobjId) + { ++ Oid lobjId_new; ++ + /* +! * Create a new largeobject with empty data pages + */ +! lobjId_new = LargeObjectCreate(lobjId); + + /* +! * 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. + */ +! recordDependencyOnOwner(LargeObjectRelationId, +! lobjId_new, GetUserId()); + /* + * Advance command counter to make new tuple visible to later operations. + */ + CommandCounterIncrement(); + +! return lobjId_new; + } + + /* +*************** inv_close(LargeObjectDesc *obj_desc) +*** 292,301 **** + int + inv_drop(Oid lobjId) + { +! LargeObjectDrop(lobjId); + +! /* Delete any comments on the large object */ +! DeleteComments(lobjId, LargeObjectRelationId, 0); + + /* + * Advance command counter so that tuple removal will be seen by later +--- 298,312 ---- + int + inv_drop(Oid lobjId) + { +! ObjectAddress object; + +! /* +! * 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 +*************** inv_drop(Oid lobjId) +*** 315,321 **** + static uint32 + inv_getsize(LargeObjectDesc *obj_desc) + { +- bool found = false; + uint32 lastbyte = 0; + ScanKeyData skey[1]; + SysScanDesc sd; +--- 326,331 ---- +*************** inv_getsize(LargeObjectDesc *obj_desc) +*** 339,351 **** + * 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) + { + 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); +--- 349,361 ---- + * large object in reverse pageno order. So, it's sufficient to examine + * the first valid tuple (== last valid page). + */ +! tuple = systable_getnext_ordered(sd, BackwardScanDirection); +! if (HeapTupleIsValid(tuple)) + { + Form_pg_largeobject data; + bytea *datafield; + bool pfreeit; + + if (HeapTupleHasNulls(tuple)) /* paranoia */ + elog(ERROR, "null field found in pg_largeobject"); + data = (Form_pg_largeobject) GETSTRUCT(tuple); +*************** inv_getsize(LargeObjectDesc *obj_desc) +*** 360,374 **** + 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; + } + +--- 370,379 ---- +*************** inv_write(LargeObjectDesc *obj_desc, con +*** 545,550 **** +--- 550,561 ---- + 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; + +*************** inv_truncate(LargeObjectDesc *obj_desc, +*** 736,741 **** +--- 747,758 ---- + 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 -Nrpc base/src/backend/tcop/utility.c blob/src/backend/tcop/utility.c +*** base/src/backend/tcop/utility.c Tue Dec 15 17:16:51 2009 +--- blob/src/backend/tcop/utility.c Fri Dec 18 09:40:55 2009 +*************** CreateCommandTag(Node *parsetree) +*** 1625,1630 **** +--- 1625,1633 ---- + case OBJECT_LANGUAGE: + tag = "ALTER LANGUAGE"; + break; ++ case OBJECT_LARGEOBJECT: ++ tag = "ALTER LARGEOBJECT"; ++ break; + case OBJECT_OPERATOR: + tag = "ALTER OPERATOR"; + break; +diff -Nrpc base/src/backend/utils/adt/acl.c blob/src/backend/utils/adt/acl.c +*** base/src/backend/utils/adt/acl.c Thu Jun 18 10:20:52 2009 +--- blob/src/backend/utils/adt/acl.c Fri Dec 18 09:40:55 2009 +*************** acldefault(GrantObjectType objtype, Oid +*** 631,636 **** +--- 631,641 ---- + 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 -Nrpc base/src/backend/utils/misc/guc.c blob/src/backend/utils/misc/guc.c +*** base/src/backend/utils/misc/guc.c Thu Mar 18 01:40:54 2010 +--- blob/src/backend/utils/misc/guc.c Thu Mar 18 09:43:03 2010 +*************** +*** 38,43 **** +--- 38,44 ---- + #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" +*************** static struct config_bool ConfigureNames +*** 1222,1227 **** +--- 1223,1238 ---- + 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 -Nrpc base/src/backend/utils/misc/postgresql.conf.sample blob/src/backend/utils/misc/postgresql.conf.sample +*** base/src/backend/utils/misc/postgresql.conf.sample Thu Mar 18 01:40:54 2010 +--- blob/src/backend/utils/misc/postgresql.conf.sample Thu Mar 18 09:43:03 2010 +*************** +*** 484,489 **** +--- 484,490 ---- + #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 -Nrpc base/src/bin/initdb/initdb.c blob/src/bin/initdb/initdb.c +*** base/src/bin/initdb/initdb.c Tue Dec 15 17:16:51 2009 +--- blob/src/bin/initdb/initdb.c Fri Dec 18 09:40:55 2009 +*************** setup_privileges(void) +*** 1815,1820 **** +--- 1815,1821 ---- + " 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 -Nrpc base/src/bin/pg_dump/dumputils.c blob/src/bin/pg_dump/dumputils.c +*** base/src/bin/pg_dump/dumputils.c Thu Mar 18 01:40:54 2010 +--- blob/src/bin/pg_dump/dumputils.c Thu Mar 18 09:43:03 2010 +*************** do { \ +*** 758,763 **** +--- 758,768 ---- + CONVERT_PRIV('U', "USAGE"); + else if (strcmp(type, "FOREIGN SERVER") == 0) + CONVERT_PRIV('U', "USAGE"); ++ else if (strcmp(type, "LARGE OBJECT") == 0) ++ { ++ CONVERT_PRIV('r', "SELECT"); ++ CONVERT_PRIV('w', "UPDATE"); ++ } + else + abort(); + +diff -Nrpc base/src/bin/pg_dump/pg_dump.c blob/src/bin/pg_dump/pg_dump.c +*** base/src/bin/pg_dump/pg_dump.c Thu Mar 18 01:40:54 2010 +--- blob/src/bin/pg_dump/pg_dump.c Thu Mar 18 09:43:03 2010 +*************** hasBlobs(Archive *AH) +*** 1923,1929 **** + selectSourceSchema("pg_catalog"); + + /* Check for BLOB OIDs */ +! if (AH->remoteVersion >= 70100) + blobQry = "SELECT loid FROM pg_largeobject LIMIT 1"; + else + blobQry = "SELECT oid FROM pg_class WHERE relkind = 'l' LIMIT 1"; +--- 1923,1931 ---- + selectSourceSchema("pg_catalog"); + + /* Check for BLOB OIDs */ +! 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"; +*************** dumpBlobs(Archive *AH, void *arg) +*** 1959,1965 **** + selectSourceSchema("pg_catalog"); + + /* Cursor to get all BLOB OIDs */ +! 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'"; +--- 1961,1969 ---- + selectSourceSchema("pg_catalog"); + + /* Cursor to get all BLOB OIDs */ +! 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'"; +*************** dumpBlobs(Archive *AH, void *arg) +*** 2023,2029 **** + + /* + * dumpBlobComments +! * dump all blob comments + * + * 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 +--- 2027,2035 ---- + + /* + * dumpBlobComments +! * 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 +*************** dumpBlobComments(Archive *AH, void *arg) +*** 2034,2063 **** + { + const char *blobQry; + const char *blobFetchQry; +! PQExpBuffer commentcmd = createPQExpBuffer(); + PGresult *res; + int i; + + if (g_verbose) +! write_msg(NULL, "saving large object comments\n"); + + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); + + /* Cursor to get all BLOB comments */ +! if (AH->remoteVersion >= 70300) + blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, " +! "obj_description(loid, 'pg_largeobject') " + "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') " + "FROM (SELECT DISTINCT loid FROM pg_largeobject) ss"; + else if (AH->remoteVersion >= 70100) + blobQry = "DECLARE blobcmt CURSOR FOR SELECT loid, " +! "obj_description(loid) " + "FROM (SELECT DISTINCT loid FROM pg_largeobject) ss"; + else + blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, " +--- 2040,2074 ---- + { + const char *blobQry; + const char *blobFetchQry; +! PQExpBuffer cmdQry = createPQExpBuffer(); + PGresult *res; + int i; + + if (g_verbose) +! 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 >= 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'), 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'), 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), NULL, NULL " + "FROM (SELECT DISTINCT loid FROM pg_largeobject) ss"; + else + blobQry = "DECLARE blobcmt CURSOR FOR SELECT oid, " +*************** dumpBlobComments(Archive *AH, void *arg) +*** 2065,2071 **** + " SELECT description " + " FROM pg_description pd " + " WHERE pd.objoid=pc.oid " +! " ) " + "FROM pg_class pc WHERE relkind = 'l'"; + + res = PQexec(g_conn, blobQry); +--- 2076,2082 ---- + " 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); +*************** dumpBlobComments(Archive *AH, void *arg) +*** 2085,2106 **** + /* Process the tuples, if any */ + for (i = 0; i < PQntuples(res); i++) + { +! Oid blobOid; +! char *comment; + +! /* ignore blobs without comments */ +! if (PQgetisnull(res, i, 1)) +! continue; + +! blobOid = atooid(PQgetvalue(res, i, 0)); +! comment = PQgetvalue(res, i, 1); + +! printfPQExpBuffer(commentcmd, "COMMENT ON LARGE OBJECT %u IS ", +! blobOid); +! appendStringLiteralAH(commentcmd, comment, AH); +! appendPQExpBuffer(commentcmd, ";\n"); + +! archputs(commentcmd->data, AH); + } + } while (PQntuples(res) > 0); + +--- 2096,2146 ---- + /* Process the tuples, if any */ + for (i = 0; i < PQntuples(res); i++) + { +! 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]; + +! resetPQExpBuffer(cmdQry); + +! /* 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); +! } + +! /* 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(); +! } +! } + +! if (cmdQry->len > 0) +! { +! appendPQExpBuffer(cmdQry, "\n"); +! archputs(cmdQry->data, AH); +! } + } + } while (PQntuples(res) > 0); + +*************** dumpBlobComments(Archive *AH, void *arg) +*** 2108,2114 **** + + archputs("\n", AH); + +! destroyPQExpBuffer(commentcmd); + + return 1; + } +--- 2148,2154 ---- + + archputs("\n", AH); + +! destroyPQExpBuffer(cmdQry); + + return 1; + } +diff -Nrpc base/src/bin/psql/large_obj.c blob/src/bin/psql/large_obj.c +*** base/src/bin/psql/large_obj.c Sat Jan 3 12:49:23 2009 +--- blob/src/bin/psql/large_obj.c Fri Dec 18 09:40:55 2009 +*************** do_lo_list(void) +*** 278,290 **** + 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")); + + res = PSQLexec(buf, false); + if (!res) +--- 278,305 ---- + char buf[1024]; + printQueryOpt myopt = pset.popt; + +! 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 -Nrpc base/src/bin/psql/tab-complete.c blob/src/bin/psql/tab-complete.c +*** base/src/bin/psql/tab-complete.c Thu Jun 18 10:20:52 2009 +--- blob/src/bin/psql/tab-complete.c Fri Dec 18 09:40:55 2009 +*************** psql_completion(char *text, int start, i +*** 693,699 **** + { + static const char *const list_ALTER[] = + {"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FOREIGN DATA WRAPPER", "FUNCTION", +! "GROUP", "INDEX", "LANGUAGE", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE", + "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL}; + + COMPLETE_WITH_LIST(list_ALTER); +--- 693,699 ---- + { + static const char *const list_ALTER[] = + {"AGGREGATE", "CONVERSION", "DATABASE", "DOMAIN", "FOREIGN DATA WRAPPER", "FUNCTION", +! "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); +*************** psql_completion(char *text, int start, i +*** 762,767 **** +--- 762,778 ---- + 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) && +*************** psql_completion(char *text, int start, i +*** 1703,1708 **** +--- 1714,1720 ---- + " UNION SELECT 'FOREIGN SERVER'" + " UNION SELECT 'FUNCTION'" + " UNION SELECT 'LANGUAGE'" ++ " UNION SELECT 'LARGE OBJECT'" + " UNION SELECT 'SCHEMA'" + " UNION SELECT 'TABLESPACE'"); + +diff -Nrpc base/src/include/catalog/catversion.h blob/src/include/catalog/catversion.h +*** base/src/include/catalog/catversion.h Thu Jun 18 10:20:52 2009 +--- blob/src/include/catalog/catversion.h Fri Dec 18 09:40:55 2009 +*************** +*** 53,58 **** + */ + + /* yyyymmddN */ +! #define CATALOG_VERSION_NO 200904091 + + #endif +--- 53,58 ---- + */ + + /* yyyymmddN */ +! #define CATALOG_VERSION_NO 200912151 + + #endif +diff -Nrpc base/src/include/catalog/dependency.h blob/src/include/catalog/dependency.h +*** base/src/include/catalog/dependency.h Thu Jun 18 10:20:52 2009 +--- blob/src/include/catalog/dependency.h Fri Dec 18 09:40:55 2009 +*************** typedef enum ObjectClass +*** 128,133 **** +--- 128,134 ---- + 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 -Nrpc base/src/include/catalog/indexing.h blob/src/include/catalog/indexing.h +*** base/src/include/catalog/indexing.h Thu Jun 18 10:20:52 2009 +--- blob/src/include/catalog/indexing.h Fri Dec 18 09:40:55 2009 +*************** DECLARE_UNIQUE_INDEX(pg_language_oid_ind +*** 165,170 **** +--- 165,173 ---- + 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 -Nrpc base/src/include/catalog/pg_largeobject.h blob/src/include/catalog/pg_largeobject.h +*** base/src/include/catalog/pg_largeobject.h Sat Jan 3 12:25:21 2009 +--- blob/src/include/catalog/pg_largeobject.h Fri Dec 18 09:40:55 2009 +*************** typedef FormData_pg_largeobject *Form_pg +*** 51,58 **** + #define Anum_pg_largeobject_pageno 2 + #define Anum_pg_largeobject_data 3 + +! extern void LargeObjectCreate(Oid loid); + extern void LargeObjectDrop(Oid loid); + extern bool LargeObjectExists(Oid loid); + + #endif /* PG_LARGEOBJECT_H */ +--- 51,59 ---- + #define Anum_pg_largeobject_pageno 2 + #define Anum_pg_largeobject_data 3 + +! 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 -Nrpc base/src/include/catalog/pg_largeobject_metadata.h blob/src/include/catalog/pg_largeobject_metadata.h +*** base/src/include/catalog/pg_largeobject_metadata.h Thu Jan 1 09:00:00 1970 +--- blob/src/include/catalog/pg_largeobject_metadata.h Fri Dec 18 09:41:26 2009 +*************** +*** 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 -Nrpc base/src/include/libpq/be-fsstubs.h blob/src/include/libpq/be-fsstubs.h +*** base/src/include/libpq/be-fsstubs.h Sat Jan 3 12:25:21 2009 +--- blob/src/include/libpq/be-fsstubs.h Fri Dec 18 09:40:55 2009 +*************** extern Datum lo_unlink(PG_FUNCTION_ARGS) +*** 38,43 **** +--- 38,48 ---- + 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 -Nrpc base/src/include/nodes/parsenodes.h blob/src/include/nodes/parsenodes.h +*** base/src/include/nodes/parsenodes.h Tue Dec 15 17:16:51 2009 +--- blob/src/include/nodes/parsenodes.h Fri Dec 18 09:40:55 2009 +*************** typedef enum GrantObjectType +*** 1186,1191 **** +--- 1186,1192 ---- + 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 -Nrpc base/src/include/utils/acl.h blob/src/include/utils/acl.h +*** base/src/include/utils/acl.h Thu Jun 18 10:20:52 2009 +--- blob/src/include/utils/acl.h Fri Dec 18 09:40:55 2009 +*************** +*** 26,31 **** +--- 26,32 ---- + + #include "nodes/parsenodes.h" + #include "utils/array.h" ++ #include "utils/snapshot.h" + + + /* +*************** typedef ArrayType Acl; +*** 151,156 **** +--- 152,158 ---- + #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) + +*************** typedef enum AclObjectKind +*** 181,186 **** +--- 183,189 ---- + 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 */ +*************** extern AclMode pg_proc_aclmask(Oid proc_ +*** 273,278 **** +--- 276,283 ---- + 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, +*************** extern AclResult pg_class_aclcheck(Oid t +*** 290,295 **** +--- 295,302 ---- + 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); +*************** extern bool pg_type_ownercheck(Oid type_ +*** 307,312 **** +--- 314,320 ---- + 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 -Nrpc base/src/test/regress/expected/privileges.out blob/src/test/regress/expected/privileges.out +*** base/src/test/regress/expected/privileges.out Fri Mar 6 09:45:33 2009 +--- blob/src/test/regress/expected/privileges.out Fri Dec 18 09:40:55 2009 +*************** DROP ROLE IF EXISTS regressuser2; +*** 11,16 **** +--- 11,22 ---- + 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; +*************** SELECT has_table_privilege('regressuser1 +*** 815,820 **** +--- 821,1014 ---- + 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); +*************** DROP TABLE atest6; +*** 836,841 **** +--- 1030,1045 ---- + 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; +*************** DROP USER regressuser2; +*** 844,846 **** +--- 1048,1052 ---- + DROP USER regressuser3; + DROP USER regressuser4; + DROP USER regressuser5; ++ DROP USER regressuser6; ++ ERROR: role "regressuser6" does not exist +diff -Nrpc base/src/test/regress/expected/sanity_check.out blob/src/test/regress/expected/sanity_check.out +*** base/src/test/regress/expected/sanity_check.out Tue Feb 10 10:10:02 2009 +--- blob/src/test/regress/expected/sanity_check.out Fri Dec 18 09:40:55 2009 +*************** SELECT relname, relhasindex +*** 104,109 **** +--- 104,110 ---- + pg_inherits | t + pg_language | t + pg_largeobject | t ++ pg_largeobject_metadata | t + pg_listener | f + pg_namespace | t + pg_opclass | t +*************** SELECT relname, relhasindex +*** 151,157 **** + timetz_tbl | f + tinterval_tbl | f + varchar_tbl | f +! (140 rows) + + -- + -- another sanity check: every system catalog that has OIDs should have +--- 152,158 ---- + timetz_tbl | f + tinterval_tbl | f + varchar_tbl | f +! (141 rows) + + -- + -- another sanity check: every system catalog that has OIDs should have +diff -Nrpc base/src/test/regress/sql/privileges.sql blob/src/test/regress/sql/privileges.sql +*** base/src/test/regress/sql/privileges.sql Fri Mar 6 09:45:33 2009 +--- blob/src/test/regress/sql/privileges.sql Fri Dec 18 09:40:55 2009 +*************** DROP ROLE IF EXISTS regressuser2; +*** 15,20 **** +--- 15,23 ---- + 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; + +*************** ALTER GROUP regressgroup2 ADD USER regre +*** 36,42 **** + ALTER GROUP regressgroup2 DROP USER regressuser2; + ALTER GROUP regressgroup2 ADD USER regressuser4; + +- + -- test owner privileges + + SET SESSION AUTHORIZATION regressuser1; +--- 39,44 ---- +*************** SELECT has_table_privilege('regressuser3 +*** 468,473 **** +--- 470,552 ---- + + 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 + +*************** DROP TABLE atestc; +*** 493,498 **** +--- 572,579 ---- + DROP TABLE atestp1; + DROP TABLE atestp2; + ++ SELECT lo_unlink(oid) FROM pg_largeobject_metadata; ++ + DROP GROUP regressgroup1; + DROP GROUP regressgroup2; + +*************** DROP USER regressuser2; +*** 502,504 **** +--- 583,586 ---- + 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 index 9bd8c12..0339b07 100644 --- a/pgsql-02-8.4-sepgsql.patch +++ b/pgsql-02-8.4-sepgsql.patch @@ -1,17591 +1,20258 @@ -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 -Nrpc blob/configure sepgsql/configure +*** blob/configure Thu Mar 18 09:43:03 2010 +--- sepgsql/configure Thu Mar 18 01:55:40 2010 +*************** with_libxml +*** 710,715 **** +--- 710,717 ---- + with_libxslt + with_system_tzdata + with_zlib ++ enable_selinux ++ SELINUX_LIBS + GREP + EGREP + ELF_SYS +*************** Optional Features: +*** 1378,1383 **** +--- 1380,1386 ---- + --enable-thread-safety make client libraries thread-safe + --enable-thread-safety-force + force thread-safety despite thread test failure ++ --enable-selinux enable to build with SELinux support + --disable-largefile omit support for large files + --disable-float4-byval disable float4 passed by value + --disable-float8-byval disable float8 passed by value +*************** fi +*** 5532,5537 **** +--- 5535,5717 ---- + + + # ++ # 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 + # + +*************** with_libxml!$with_libxml$ac_delim +*** 28125,28135 **** + with_libxslt!$with_libxslt$ac_delim + with_system_tzdata!$with_system_tzdata$ac_delim + with_zlib!$with_zlib$ac_delim + GREP!$GREP$ac_delim + EGREP!$EGREP$ac_delim + ELF_SYS!$ELF_SYS$ac_delim +- LDFLAGS_SL!$LDFLAGS_SL$ac_delim +- LD!$LD$ac_delim + _ACEOF + + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then +--- 28305,28315 ---- + 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 + _ACEOF + + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then +*************** _ACEOF +*** 28171,28176 **** +--- 28351,28358 ---- + 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 +*************** vpath_build!$vpath_build$ac_delim +*** 28233,28239 **** + LTLIBOBJS!$LTLIBOBJS$ac_delim + _ACEOF + +! if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 60; then + break + elif $ac_last_try; then + { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +--- 28415,28421 ---- + LTLIBOBJS!$LTLIBOBJS$ac_delim + _ACEOF + +! 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 -Nrpc blob/configure.in sepgsql/configure.in +*** blob/configure.in Thu Mar 18 09:43:03 2010 +--- sepgsql/configure.in Thu Mar 18 01:55:40 2010 +*************** PGAC_ARG_BOOL(with, zlib, yes, +*** 764,769 **** +--- 764,787 ---- + 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 -Nrpc blob/src/Makefile.global.in sepgsql/src/Makefile.global.in +*** blob/src/Makefile.global.in Tue Jun 30 01:26:47 2009 +--- sepgsql/src/Makefile.global.in Sun Dec 20 00:41:22 2009 +*************** enable_nls = @enable_nls@ +*** 165,170 **** +--- 165,171 ---- + 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@ +*************** TCL_INCLUDE_SPEC = @TCL_INCLUDE_SPEC@ +*** 184,189 **** +--- 185,192 ---- + 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 -Nrpc blob/src/backend/Makefile sepgsql/src/backend/Makefile +*** blob/src/backend/Makefile Thu Mar 18 09:43:03 2010 +--- sepgsql/src/backend/Makefile Thu Mar 18 01:55:40 2010 +*************** include $(top_builddir)/src/Makefile.glo +*** 16,22 **** + + SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \ + main nodes optimizer port postmaster regex rewrite \ +! storage tcop tsearch utils $(top_builddir)/src/timezone + + include $(srcdir)/common.mk + +--- 16,22 ---- + + SUBDIRS = access bootstrap catalog parser commands executor foreign lib libpq \ + main nodes optimizer port postmaster regex rewrite \ +! security storage tcop tsearch utils $(top_builddir)/src/timezone + + include $(srcdir)/common.mk + +*************** LIBS := $(filter-out -lpgport, $(LIBS)) +*** 40,45 **** +--- 40,48 ---- + # 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 -Nrpc blob/src/backend/access/common/heaptuple.c sepgsql/src/backend/access/common/heaptuple.c +*** blob/src/backend/access/common/heaptuple.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/access/common/heaptuple.c Thu Sep 10 15:18:03 2009 +*************** +*** 60,65 **** +--- 60,66 ---- + #include "access/heapam.h" + #include "access/sysattr.h" + #include "access/tuptoaster.h" ++ #include "catalog/pg_security.h" + #include "executor/tuptable.h" + + +*************** heap_attisnull(HeapTuple tup, int attnum +*** 287,292 **** +--- 288,294 ---- + case MinCommandIdAttributeNumber: + case MaxTransactionIdAttributeNumber: + case MaxCommandIdAttributeNumber: ++ case SecurityAttributeNumber: + /* these are never null */ + break; + +*************** heap_getsysattr(HeapTuple tup, int attnu +*** 599,604 **** +--- 601,609 ---- + case TableOidAttributeNumber: + result = ObjectIdGetDatum(tup->t_tableOid); + break; ++ case SecurityAttributeNumber: ++ result = securitySysattSecLabelOut(tup->t_tableOid, tup); ++ break; + default: + elog(ERROR, "invalid attnum: %d", attnum); + result = 0; /* keep compiler quiet */ +*************** heap_form_tuple(TupleDesc tupleDescripto +*** 722,727 **** +--- 727,734 ---- + + if (tupleDescriptor->tdhasoid) + len += sizeof(Oid); ++ if (tupleDescriptor->tdhassecid) ++ len += sizeof(Oid); + + hoff = len = MAXALIGN(len); /* align user data safely */ + +*************** heap_form_tuple(TupleDesc tupleDescripto +*** 753,758 **** +--- 760,767 ---- + + if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ + td->t_infomask = HEAP_HASOID; ++ if (tupleDescriptor->tdhassecid) ++ td->t_infomask |= HEAP_HASSECID; + + heap_fill_tuple(tupleDescriptor, + values, +*************** heap_modify_tuple(HeapTuple tuple, +*** 864,869 **** +--- 873,880 ---- + newTuple->t_tableOid = tuple->t_tableOid; + if (tupleDesc->tdhasoid) + HeapTupleSetOid(newTuple, HeapTupleGetOid(tuple)); ++ if (HeapTupleHasSecid(newTuple)) ++ HeapTupleSetSecid(newTuple, HeapTupleGetSecid(tuple)); + + return newTuple; + } +*************** heap_form_minimal_tuple(TupleDesc tupleD +*** 1474,1479 **** +--- 1485,1492 ---- + + if (tupleDescriptor->tdhasoid) + len += sizeof(Oid); ++ if (tupleDescriptor->tdhassecid) ++ len += sizeof(Oid); + + hoff = len = MAXALIGN(len); /* align user data safely */ + +*************** heap_form_minimal_tuple(TupleDesc tupleD +*** 1495,1500 **** +--- 1508,1515 ---- + + if (tupleDescriptor->tdhasoid) /* else leave infomask = 0 */ + tuple->t_infomask = HEAP_HASOID; ++ if (tupleDescriptor->tdhassecid) ++ tuple->t_infomask |= HEAP_HASSECID; + + heap_fill_tuple(tupleDescriptor, + values, +diff -Nrpc blob/src/backend/access/common/tupdesc.c sepgsql/src/backend/access/common/tupdesc.c +*** blob/src/backend/access/common/tupdesc.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/access/common/tupdesc.c Wed Sep 9 13:14:37 2009 +*************** CreateTemplateTupleDesc(int natts, bool +*** 88,93 **** +--- 88,94 ---- + desc->tdtypeid = RECORDOID; + desc->tdtypmod = -1; + desc->tdhasoid = hasoid; ++ desc->tdhassecid = false; + desc->tdrefcount = -1; /* assume not reference-counted */ + + return desc; +*************** CreateTupleDesc(int natts, bool hasoid, +*** 121,126 **** +--- 122,128 ---- + desc->tdtypeid = RECORDOID; + desc->tdtypmod = -1; + desc->tdhasoid = hasoid; ++ desc->tdhassecid = false; + desc->tdrefcount = -1; /* assume not reference-counted */ + + return desc; +*************** CreateTupleDescCopy(TupleDesc tupdesc) +*** 150,155 **** +--- 152,158 ---- + + desc->tdtypeid = tupdesc->tdtypeid; + desc->tdtypmod = tupdesc->tdtypmod; ++ desc->tdhassecid = tupdesc->tdhassecid; + + return desc; + } +*************** CreateTupleDescCopyConstr(TupleDesc tupd +*** 208,213 **** +--- 211,217 ---- + + desc->tdtypeid = tupdesc->tdtypeid; + desc->tdtypmod = tupdesc->tdtypmod; ++ desc->tdhassecid = tupdesc->tdhassecid; + + return desc; + } +*************** equalTupleDescs(TupleDesc tupdesc1, Tupl +*** 314,319 **** +--- 318,325 ---- + return false; + if (tupdesc1->tdhasoid != tupdesc2->tdhasoid) + return false; ++ if (tupdesc1->tdhassecid != tupdesc2->tdhassecid) ++ return false; + + for (i = 0; i < tupdesc1->natts; i++) + { +diff -Nrpc blob/src/backend/access/heap/heapam.c sepgsql/src/backend/access/heap/heapam.c +*** blob/src/backend/access/heap/heapam.c Sun Sep 6 19:40:49 2009 +--- sepgsql/src/backend/access/heap/heapam.c Sun Dec 20 16:30:19 2009 +*************** +*** 54,59 **** +--- 54,60 ---- + #include "catalog/namespace.h" + #include "miscadmin.h" + #include "pgstat.h" ++ #include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/freespace.h" + #include "storage/lmgr.h" +*************** heap_insert(Relation relation, HeapTuple +*** 2016,2021 **** +--- 2017,2028 ---- + Oid + simple_heap_insert(Relation relation, HeapTuple tup) + { ++ /* ++ * SELinux assigns default security label for the tuple, ++ * but does not check permissions to the internal operations. ++ */ ++ sepgsqlHeapTupleInsert(relation, tup, true); ++ + return heap_insert(relation, tup, GetCurrentCommandId(true), 0, NULL); + } + +*************** l2: +*** 2558,2563 **** +--- 2565,2575 ---- + Assert(!(newtup->t_data->t_infomask & HEAP_HASOID)); + } + ++ /* Preserve SecurityId, if not changed */ ++ if (HeapTupleHasSecid(newtup) && ++ !OidIsValid(HeapTupleGetSecid(newtup))) ++ HeapTupleSetSecid(newtup, HeapTupleGetSecid(&oldtup)); ++ + newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK); + newtup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK); + newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED); +*************** heap_inplace_update(Relation relation, H +*** 3499,3504 **** +--- 3511,3518 ---- + 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 -Nrpc blob/src/backend/access/heap/tuptoaster.c sepgsql/src/backend/access/heap/tuptoaster.c +*** blob/src/backend/access/heap/tuptoaster.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/access/heap/tuptoaster.c Tue Sep 8 23:55:48 2009 +*************** toast_insert_or_update(Relation rel, Hea +*** 591,596 **** +--- 591,598 ---- + hoff += BITMAPLEN(numAttrs); + if (newtup->t_data->t_infomask & HEAP_HASOID) + hoff += sizeof(Oid); ++ if (HeapTupleHasSecid(newtup)) ++ hoff += sizeof(Oid); + hoff = MAXALIGN(hoff); + Assert(hoff == newtup->t_data->t_hoff); + /* now convert to a limit on the tuple data size */ +*************** toast_insert_or_update(Relation rel, Hea +*** 864,869 **** +--- 866,873 ---- + new_len += BITMAPLEN(numAttrs); + if (olddata->t_infomask & HEAP_HASOID) + new_len += sizeof(Oid); ++ if (HeapTupleHeaderHasSecid(olddata)) ++ new_len += sizeof(Oid); + new_len = MAXALIGN(new_len); + Assert(new_len == olddata->t_hoff); + new_data_len = heap_compute_data_size(tupleDesc, +*************** toast_flatten_tuple_attribute(Datum valu +*** 1015,1020 **** +--- 1019,1026 ---- + new_len += BITMAPLEN(numAttrs); + if (olddata->t_infomask & HEAP_HASOID) + new_len += sizeof(Oid); ++ if (HeapTupleHeaderHasSecid(olddata)) ++ new_len += sizeof(Oid); + new_len = MAXALIGN(new_len); + Assert(new_len == olddata->t_hoff); + new_data_len = heap_compute_data_size(tupleDesc, +*************** toast_save_datum(Relation rel, Datum val +*** 1213,1218 **** +--- 1219,1230 ---- + memcpy(VARDATA(&chunk_data), data_p, chunk_size); + toasttup = heap_form_tuple(toasttupDesc, t_values, t_isnull); + ++ /* ++ * NOTE: SE-PostgreSQL does not assign any security label ++ * for tuples within the TOASTVALUE relation, so we omit ++ * to put sepgsqlHeapTupleInsert() hook here. ++ */ ++ + heap_insert(toastrel, toasttup, mycid, options, NULL); + + /* +diff -Nrpc blob/src/backend/access/transam/xact.c sepgsql/src/backend/access/transam/xact.c +*** blob/src/backend/access/transam/xact.c Thu Mar 18 09:43:03 2010 +--- sepgsql/src/backend/access/transam/xact.c Thu Mar 18 01:55:40 2010 +*************** +*** 36,41 **** +--- 36,43 ---- + #include "libpq/be-fsstubs.h" + #include "miscadmin.h" + #include "pgstat.h" ++ #include "security/rowlevel.h" ++ #include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/fd.h" + #include "storage/lmgr.h" +*************** typedef struct TransactionStateData +*** 140,145 **** +--- 142,149 ---- + 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; + +*************** static TransactionStateData TopTransacti +*** 168,173 **** +--- 172,179 ---- + 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 */ + }; + +*************** StartTransaction(void) +*** 1527,1532 **** +--- 1533,1541 ---- + /* SecurityRestrictionContext should never be set outside a transaction */ + Assert(s->prevSecContext == 0); + ++ s->prevSecLabel = sepgsqlGetClientLabel(); ++ s->prevRowlv = rowlvGetPerformingMode(); ++ + /* + * initialize other subsystems for new transaction + */ +*************** AbortTransaction(void) +*** 2031,2036 **** +--- 2040,2051 ---- + SetUserIdAndSecContext(s->prevUser, s->prevSecContext); + + /* ++ * Reset SELinux features ++ */ ++ sepgsqlSetClientLabel(s->prevSecLabel); ++ rowlvSetPerformingMode(s->prevRowlv); ++ ++ /* + * do abort processing + */ + AfterTriggerEndXact(false); +*************** AbortSubTransaction(void) +*** 3877,3882 **** +--- 3892,3903 ---- + 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... + */ +*************** PushTransaction(void) +*** 4018,4023 **** +--- 4039,4046 ---- + s->blockState = TBLOCK_SUBBEGIN; + GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext); + s->prevXactReadOnly = XactReadOnly; ++ s->prevSecLabel = sepgsqlGetClientLabel(); ++ s->prevRowlv = rowlvGetPerformingMode(); + + CurrentTransactionState = s; + +diff -Nrpc blob/src/backend/bootstrap/bootparse.y sepgsql/src/backend/bootstrap/bootparse.y +*** blob/src/backend/bootstrap/bootparse.y Sat Jan 3 13:01:35 2009 +--- sepgsql/src/backend/bootstrap/bootparse.y Thu Oct 8 09:29:32 2009 +*************** +*** 42,47 **** +--- 42,48 ---- + #include "nodes/pg_list.h" + #include "nodes/primnodes.h" + #include "rewrite/prs2lock.h" ++ #include "security/sepgsql.h" + #include "storage/block.h" + #include "storage/fd.h" + #include "storage/ipc.h" +*************** Boot_CreateStmt: +*** 211,216 **** +--- 212,224 ---- + else + { + Oid id; ++ Oid *secLabels = ++ sepgsql_relation_create(LexIDStr($5), ++ RELKIND_RELATION, ++ tupdesc, ++ PG_CATALOG_NAMESPACE, ++ NULL, NIL, ++ false, false); + + id = heap_create_with_catalog(LexIDStr($5), + PG_CATALOG_NAMESPACE, +*************** Boot_CreateStmt: +*** 225,231 **** + 0, + ONCOMMIT_NOOP, + (Datum) 0, +! true); + elog(DEBUG4, "relation created with oid %u", id); + } + do_end(); +--- 233,240 ---- + 0, + ONCOMMIT_NOOP, + (Datum) 0, +! true, +! secLabels); + elog(DEBUG4, "relation created with oid %u", id); + } + do_end(); +diff -Nrpc blob/src/backend/bootstrap/bootstrap.c sepgsql/src/backend/bootstrap/bootstrap.c +*** blob/src/backend/bootstrap/bootstrap.c Fri Feb 20 22:15:36 2009 +--- sepgsql/src/backend/bootstrap/bootstrap.c Sun Dec 20 16:30:19 2009 +*************** +*** 26,37 **** +--- 26,39 ---- + #include "access/xact.h" + #include "bootstrap/bootstrap.h" + #include "catalog/index.h" ++ #include "catalog/pg_security.h" + #include "catalog/pg_type.h" + #include "libpq/pqsignal.h" + #include "miscadmin.h" + #include "nodes/makefuncs.h" + #include "postmaster/bgwriter.h" + #include "postmaster/walwriter.h" ++ #include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/ipc.h" + #include "storage/proc.h" +*************** AuxiliaryProcessMain(int argc, char *arg +*** 338,343 **** +--- 340,350 ---- + case WalWriterProcess: + statmsg = "wal writer process"; + break; ++ #ifdef HAVE_SELINUX ++ case SelinuxReceiverProcess: ++ statmsg = "selinux netlink receiver"; ++ break; ++ #endif + default: + statmsg = "??? process"; + break; +*************** AuxiliaryProcessMain(int argc, char *arg +*** 430,435 **** +--- 437,448 ---- + WalWriterMain(); + proc_exit(1); /* should never return */ + ++ #ifdef HAVE_SELINUX ++ case SelinuxReceiverProcess: ++ sepgsqlReceiverMain(); ++ proc_exit(1); /* should nener return */ ++ #endif ++ + default: + elog(PANIC, "unrecognized process type: %d", auxType); + proc_exit(1); +*************** BootstrapModeMain(void) +*** 497,502 **** +--- 510,520 ---- + */ + boot_yyparse(); + ++ /* ++ * SELinux initial labeling ++ */ ++ sepgsqlPostBootstrapingMode(); ++ + /* Perform a checkpoint to ensure everything's down to disk */ + SetProcessingMode(NormalProcessing); + CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE); +*************** InsertOneTuple(Oid objectid) +*** 794,799 **** +--- 812,819 ---- + tupDesc = CreateTupleDesc(numattr, + RelationGetForm(boot_reldesc)->relhasoids, + attrtypes); ++ tupDesc->tdhassecid = RelationGetDescr(boot_reldesc)->tdhassecid; ++ + tuple = heap_form_tuple(tupDesc, values, Nulls); + if (objectid != (Oid) 0) + HeapTupleSetOid(tuple, objectid); +diff -Nrpc blob/src/backend/catalog/Makefile sepgsql/src/backend/catalog/Makefile +*** blob/src/backend/catalog/Makefile Fri Dec 18 09:40:55 2009 +--- sepgsql/src/backend/catalog/Makefile Fri Dec 18 10:27:56 2009 +*************** include $(top_builddir)/src/Makefile.glo +*** 13,19 **** + OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ + pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o pg_enum.o \ + pg_inherits.o pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o \ +! pg_shdepend.o pg_type.o storage.o toasting.o + + BKIFILES = postgres.bki postgres.description postgres.shdescription + +--- 13,19 ---- + OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ + pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o pg_enum.o \ + pg_inherits.o pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o \ +! pg_security.o pg_shdepend.o pg_type.o storage.o toasting.o + + BKIFILES = postgres.bki postgres.description postgres.shdescription + +*************** POSTGRES_BKI_SRCS = $(addprefix $(top_sr +*** 34,40 **** + pg_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_ts_parser.h pg_ts_template.h \ + pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ + toasting.h indexing.h \ +--- 34,40 ---- + pg_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_security.h pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ + pg_ts_parser.h pg_ts_template.h \ + pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ + toasting.h indexing.h \ +diff -Nrpc blob/src/backend/catalog/aclchk.c sepgsql/src/backend/catalog/aclchk.c +*** blob/src/backend/catalog/aclchk.c Thu Mar 18 09:43:03 2010 +--- sepgsql/src/backend/catalog/aclchk.c Thu Mar 18 01:55:40 2010 +*************** +*** 37,42 **** +--- 37,43 ---- + #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,50 **** +--- 46,52 ---- + #include "foreign/foreign.h" + #include "miscadmin.h" + #include "parser/parse_func.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/fmgroids.h" + #include "utils/lsyscache.h" +*************** expand_all_col_privileges(Oid table_oid, +*** 735,740 **** +--- 737,748 ---- + if (curr_att == ObjectIdAttributeNumber && !classForm->relhasoids) + continue; + ++ /* Skip OID column, if it doesn't exist */ ++ if (curr_att == SecurityAttributeNumber && ++ (classForm->relkind != RELKIND_RELATION || ++ table_oid == SecurityRelationId)) ++ continue; ++ + /* Views don't have any system columns at all */ + if (classForm->relkind == RELKIND_VIEW && curr_att < 0) + continue; +*************** ExecGrant_Attribute(InternalGrant *istmt +*** 837,842 **** +--- 845,852 ---- + relOid, grantorId, ACL_KIND_COLUMN, + relname, attnum, + NameStr(pg_attribute_tuple->attname)); ++ /* SELinux checks */ ++ sepgsql_attribute_grant(relOid, attnum); + + /* + * Generate new ACL. +*************** ExecGrant_Relation(InternalGrant *istmt) +*** 1092,1097 **** +--- 1102,1109 ---- + ? ACL_KIND_SEQUENCE : ACL_KIND_CLASS, + NameStr(pg_class_tuple->relname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_relation_grant(relOid); + + /* + * Generate new ACL. +*************** ExecGrant_Database(InternalGrant *istmt) +*** 1280,1285 **** +--- 1292,1299 ---- + datId, grantorId, ACL_KIND_DATABASE, + NameStr(pg_database_tuple->datname), + 0, NULL); ++ /* SELinux permission checks */ ++ sepgsql_database_grant(datId); + + /* + * Generate new ACL. +*************** ExecGrant_Fdw(InternalGrant *istmt) +*** 1398,1403 **** +--- 1412,1419 ---- + fdwid, grantorId, ACL_KIND_FDW, + NameStr(pg_fdw_tuple->fdwname), + 0, NULL); ++ /* SELinux permission checks */ ++ sepgsql_fdw_grant(fdwid); + + /* + * Generate new ACL. +*************** ExecGrant_ForeignServer(InternalGrant *i +*** 1517,1522 **** +--- 1533,1540 ---- + srvid, grantorId, ACL_KIND_FOREIGN_SERVER, + NameStr(pg_server_tuple->srvname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_foreign_server_grant(srvid); + + /* + * Generate new ACL. +*************** ExecGrant_Function(InternalGrant *istmt) +*** 1635,1640 **** +--- 1653,1660 ---- + funcId, grantorId, ACL_KIND_PROC, + NameStr(pg_proc_tuple->proname), + 0, NULL); ++ /* SELinux: db_procedure:{setattr} */ ++ sepgsql_proc_grant(funcId); + + /* + * Generate new ACL. +*************** ExecGrant_Language(InternalGrant *istmt) +*** 1759,1764 **** +--- 1779,1786 ---- + langId, grantorId, ACL_KIND_LANGUAGE, + NameStr(pg_language_tuple->lanname), + 0, NULL); ++ /* SELinux checks */ ++ sepgsql_language_grant(langId); + + /* + * Generate new ACL. +*************** ExecGrant_Namespace(InternalGrant *istmt +*** 2010,2015 **** +--- 2032,2040 ---- + NameStr(pg_namespace_tuple->nspname), + 0, NULL); + ++ /* SELinux: db_schema:{setattr} */ ++ sepgsql_schema_grant(nspid); ++ + /* + * Generate new ACL. + * +diff -Nrpc blob/src/backend/catalog/catalog.c sepgsql/src/backend/catalog/catalog.c +*** blob/src/backend/catalog/catalog.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/catalog/catalog.c Wed Jul 15 19:30:50 2009 +*************** +*** 31,36 **** +--- 31,37 ---- + #include "catalog/pg_database.h" + #include "catalog/pg_namespace.h" + #include "catalog/pg_pltemplate.h" ++ #include "catalog/pg_security.h" + #include "catalog/pg_shdepend.h" + #include "catalog/pg_shdescription.h" + #include "catalog/pg_tablespace.h" +*************** IsSharedRelation(Oid relationId) +*** 304,309 **** +--- 305,311 ---- + relationId == AuthMemRelationId || + relationId == DatabaseRelationId || + relationId == PLTemplateRelationId || ++ relationId == SecurityRelationId || + relationId == SharedDescriptionRelationId || + relationId == SharedDependRelationId || + relationId == TableSpaceRelationId) +*************** IsSharedRelation(Oid relationId) +*** 316,321 **** +--- 318,325 ---- + relationId == DatabaseNameIndexId || + relationId == DatabaseOidIndexId || + relationId == PLTemplateNameIndexId || ++ relationId == SecuritySecidIndexId || ++ relationId == SecuritySecattrIndexId || + relationId == SharedDescriptionObjIndexId || + relationId == SharedDependDependerIndexId || + relationId == SharedDependReferenceIndexId || +*************** IsSharedRelation(Oid relationId) +*** 327,332 **** +--- 331,338 ---- + relationId == PgAuthidToastIndex || + relationId == PgDatabaseToastTable || + relationId == PgDatabaseToastIndex || ++ relationId == PgSecurityToastTable || ++ relationId == PgSecurityToastIndex || + relationId == PgShdescriptionToastTable || + relationId == PgShdescriptionToastIndex) + return true; +diff -Nrpc blob/src/backend/catalog/dependency.c sepgsql/src/backend/catalog/dependency.c +*** blob/src/backend/catalog/dependency.c Fri Dec 18 09:40:55 2009 +--- sepgsql/src/backend/catalog/dependency.c Fri Dec 18 10:27:56 2009 +*************** +*** 64,69 **** +--- 64,70 ---- + #include "nodes/nodeFuncs.h" + #include "parser/parsetree.h" + #include "rewrite/rewriteRemove.h" ++ #include "security/sepgsql.h" + #include "storage/lmgr.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +*************** static void reportDependentObjects(const +*** 162,168 **** + DropBehavior behavior, + int msglevel, + const ObjectAddress *origObject); +! static void deleteOneObject(const ObjectAddress *object, Relation depRel); + static void doDeletion(const ObjectAddress *object); + static void AcquireDeletionLock(const ObjectAddress *object); + static void ReleaseDeletionLock(const ObjectAddress *object); +--- 163,170 ---- + DropBehavior behavior, + int msglevel, + const ObjectAddress *origObject); +! static void deleteOneObject(const ObjectAddress *object, +! Relation depRel, bool permission); + static void doDeletion(const ObjectAddress *object); + static void AcquireDeletionLock(const ObjectAddress *object); + static void ReleaseDeletionLock(const ObjectAddress *object); +*************** static void getOpFamilyDescription(Strin +*** 194,202 **** + * are variants on the same theme; if you change anything here you'll likely + * need to fix them too. + */ +! void +! performDeletion(const ObjectAddress *object, +! DropBehavior behavior) + { + Relation depRel; + ObjectAddresses *targetObjects; +--- 196,204 ---- + * are variants on the same theme; if you change anything here you'll likely + * need to fix them too. + */ +! static void +! performDeletionInternal(const ObjectAddress *object, +! DropBehavior behavior, bool permission) + { + Relation depRel; + ObjectAddresses *targetObjects; +*************** performDeletion(const ObjectAddress *obj +*** 242,248 **** + { + ObjectAddress *thisobj = targetObjects->refs + i; + +! deleteOneObject(thisobj, depRel); + } + + /* And clean up */ +--- 244,250 ---- + { + ObjectAddress *thisobj = targetObjects->refs + i; + +! deleteOneObject(thisobj, depRel, permission); + } + + /* And clean up */ +*************** performDeletion(const ObjectAddress *obj +*** 251,256 **** +--- 253,270 ---- + heap_close(depRel, RowExclusiveLock); + } + ++ void ++ performDeletion(const ObjectAddress *object, DropBehavior behavior) ++ { ++ performDeletionInternal(object, behavior, true); ++ } ++ ++ void ++ performDeletionNoPerms(const ObjectAddress *object, DropBehavior behavior) ++ { ++ performDeletionInternal(object, behavior, false); ++ } ++ + /* + * performMultipleDeletions: Similar to performDeletion, but act on multiple + * objects at once. +*************** performMultipleDeletions(const ObjectAdd +*** 324,330 **** + { + ObjectAddress *thisobj = targetObjects->refs + i; + +! deleteOneObject(thisobj, depRel); + } + + /* And clean up */ +--- 338,345 ---- + { + ObjectAddress *thisobj = targetObjects->refs + i; + +! /* currently, all the caller path need permission checks */ +! deleteOneObject(thisobj, depRel, true); + } + + /* And clean up */ +*************** deleteWhatDependsOn(const ObjectAddress +*** 395,401 **** + if (thisextra->flags & DEPFLAG_ORIGINAL) + continue; + +! deleteOneObject(thisobj, depRel); + } + + /* And clean up */ +--- 410,416 ---- + if (thisextra->flags & DEPFLAG_ORIGINAL) + continue; + +! deleteOneObject(thisobj, depRel, false); + } + + /* And clean up */ +*************** reportDependentObjects(const ObjectAddre +*** 945,957 **** + * depRel is the already-open pg_depend relation. + */ + static void +! deleteOneObject(const ObjectAddress *object, Relation depRel) + { + ScanKeyData key[3]; + int nkeys; + SysScanDesc scan; + HeapTuple tup; + + /* + * First remove any pg_depend records that link from this object to + * others. (Any records linking to this object should be gone already.) +--- 960,976 ---- + * depRel is the already-open pg_depend relation. + */ + static void +! deleteOneObject(const ObjectAddress *object, Relation depRel, bool permission) + { + ScanKeyData key[3]; + int nkeys; + SysScanDesc scan; + HeapTuple tup; + ++ /* SELinux checks db_xxx:{drop}, if necessary */ ++ if (permission) ++ sepgsql_sysobj_drop(object); ++ + /* + * First remove any pg_depend records that link from this object to + * others. (Any records linking to this object should be gone already.) +diff -Nrpc blob/src/backend/catalog/heap.c sepgsql/src/backend/catalog/heap.c +*** blob/src/backend/catalog/heap.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/catalog/heap.c Wed Sep 9 16:47:01 2009 +*************** +*** 43,48 **** +--- 43,49 ---- + #include "catalog/pg_constraint.h" + #include "catalog/pg_inherits.h" + #include "catalog/pg_namespace.h" ++ #include "catalog/pg_security.h" + #include "catalog/pg_statistic.h" + #include "catalog/pg_tablespace.h" + #include "catalog/pg_type.h" +*************** +*** 56,61 **** +--- 57,63 ---- + #include "parser/parse_coerce.h" + #include "parser/parse_expr.h" + #include "parser/parse_relation.h" ++ #include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/freespace.h" + #include "storage/smgr.h" +*************** static void AddNewRelationTuple(Relation +*** 74,80 **** + Oid new_rel_oid, Oid new_type_oid, + Oid relowner, + char relkind, +! Datum reloptions); + static Oid AddNewRelationType(const char *typeName, + Oid typeNamespace, + Oid new_rel_oid, +--- 76,83 ---- + Oid new_rel_oid, Oid new_type_oid, + Oid relowner, + char relkind, +! Datum reloptions, +! Oid *secLabels); + static Oid AddNewRelationType(const char *typeName, + Oid typeNamespace, + Oid new_rel_oid, +*************** static FormData_pg_attribute a7 = { +*** 158,164 **** + true, 'p', 'i', true, false, false, true, 0, {0} + }; + +! static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7}; + + /* + * This function returns a Form_pg_attribute pointer for a system attribute. +--- 161,176 ---- + true, 'p', 'i', true, false, false, true, 0, {0} + }; + +! /* +! * System columns for enhanced security features +! */ +! static FormData_pg_attribute a8 = { +! 0, {SecurityAttributeName}, TEXTOID, 0, -1, +! SecurityAttributeNumber, 0, -1, -1, +! false, 'x', 'i', true, false, false, true, 0, {0} +! }; +! +! static const Form_pg_attribute SysAtt[] = {&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8}; + + /* + * This function returns a Form_pg_attribute pointer for a system attribute. +*************** SystemAttributeByName(const char *attnam +*** 198,203 **** +--- 210,226 ---- + return NULL; + } + ++ /* ++ * If the given attribute number is writable, returns true. ++ */ ++ bool ++ SystemAttributeIsWritable(AttrNumber attnum) ++ { ++ if (attnum == SecurityAttributeNumber) ++ return true; ++ ++ return false; ++ } + + /* ---------------------------------------------------------------- + * XXX END OF UGLY HARD CODED BADNESS XXX +*************** heap_create(const char *relname, +*** 293,298 **** +--- 316,326 ---- + relid, + reltablespace, + shared_relation); ++ /* ++ * Does the relation have security attribute? ++ */ ++ RelationGetDescr(rel)->tdhassecid ++ = securityTupleDescHasSecid(relid, relkind); + + /* + * Have the storage manager create the relation's disk file, if needed. +*************** CheckAttributeType(const char *attname, +*** 487,493 **** + void + InsertPgAttributeTuple(Relation pg_attribute_rel, + Form_pg_attribute new_attribute, +! CatalogIndexState indstate) + { + Datum values[Natts_pg_attribute]; + bool nulls[Natts_pg_attribute]; +--- 515,522 ---- + void + InsertPgAttributeTuple(Relation pg_attribute_rel, + Form_pg_attribute new_attribute, +! CatalogIndexState indstate, +! Oid new_att_secid) + { + Datum values[Natts_pg_attribute]; + bool nulls[Natts_pg_attribute]; +*************** InsertPgAttributeTuple(Relation pg_attri +*** 520,525 **** +--- 549,557 ---- + + tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls); + ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, new_att_secid); ++ + /* finally insert the new tuple, update the indexes, and clean up */ + simple_heap_insert(pg_attribute_rel, tup); + +*************** AddNewAttributeTuples(Oid new_rel_oid, +*** 543,555 **** + TupleDesc tupdesc, + char relkind, + bool oidislocal, +! int oidinhcount) + { + Form_pg_attribute attr; + int i; + Relation rel; + CatalogIndexState indstate; + int natts = tupdesc->natts; + ObjectAddress myself, + referenced; + +--- 575,589 ---- + TupleDesc tupdesc, + char relkind, + bool oidislocal, +! int oidinhcount, +! Oid *secLabels) + { + Form_pg_attribute attr; + int i; + Relation rel; + CatalogIndexState indstate; + int natts = tupdesc->natts; ++ Oid new_att_secid; + ObjectAddress myself, + referenced; + +*************** AddNewAttributeTuples(Oid new_rel_oid, +*** 573,579 **** + attr->attstattarget = -1; + attr->attcacheoff = -1; + +! InsertPgAttributeTuple(rel, attr, indstate); + + /* Add dependency info */ + myself.classId = RelationRelationId; +--- 607,617 ---- + attr->attstattarget = -1; + attr->attcacheoff = -1; + +! /* Security label of the column */ +! new_att_secid = (!secLabels ? InvalidOid +! : secLabels[i - FirstLowInvalidHeapAttributeNumber]); +! +! InsertPgAttributeTuple(rel, attr, indstate, new_att_secid); + + /* Add dependency info */ + myself.classId = RelationRelationId; +*************** AddNewAttributeTuples(Oid new_rel_oid, +*** 601,606 **** +--- 639,650 ---- + SysAtt[i]->attnum == ObjectIdAttributeNumber) + continue; + ++ /* skip Secid where appropriate */ ++ if (SysAtt[i]->attnum == SecurityAttributeNumber && ++ (relkind != RELKIND_RELATION || ++ new_rel_oid == SecurityRelationId)) ++ continue; ++ + memcpy(&attStruct, (char *) SysAtt[i], sizeof(FormData_pg_attribute)); + + /* Fill in the correct relation OID in the copied tuple */ +*************** AddNewAttributeTuples(Oid new_rel_oid, +*** 613,619 **** + attStruct.attinhcount = oidinhcount; + } + +! InsertPgAttributeTuple(rel, &attStruct, indstate); + } + } + +--- 657,667 ---- + attStruct.attinhcount = oidinhcount; + } + +! /* Security label of the system column */ +! new_att_secid = (!secLabels ? InvalidOid +! : secLabels[SysAtt[i]->attnum - FirstLowInvalidHeapAttributeNumber]); +! +! InsertPgAttributeTuple(rel, &attStruct, indstate, new_att_secid); + } + } + +*************** void +*** 641,647 **** + InsertPgClassTuple(Relation pg_class_desc, + Relation new_rel_desc, + Oid new_rel_oid, +! Datum reloptions) + { + Form_pg_class rd_rel = new_rel_desc->rd_rel; + Datum values[Natts_pg_class]; +--- 689,696 ---- + InsertPgClassTuple(Relation pg_class_desc, + Relation new_rel_desc, + Oid new_rel_oid, +! Datum reloptions, +! Oid new_rel_secid) + { + Form_pg_class rd_rel = new_rel_desc->rd_rel; + Datum values[Natts_pg_class]; +*************** InsertPgClassTuple(Relation pg_class_des +*** 690,695 **** +--- 739,747 ---- + */ + HeapTupleSetOid(tup, new_rel_oid); + ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, new_rel_secid); ++ + /* finally insert the new tuple, update the indexes, and clean up */ + simple_heap_insert(pg_class_desc, tup); + +*************** AddNewRelationTuple(Relation pg_class_de +*** 712,720 **** + Oid new_type_oid, + Oid relowner, + char relkind, +! Datum reloptions) + { + Form_pg_class new_rel_reltup; + + /* + * first we update some of the information in our uncataloged relation's +--- 764,774 ---- + Oid new_type_oid, + Oid relowner, + char relkind, +! Datum reloptions, +! Oid *secLabels) + { + Form_pg_class new_rel_reltup; ++ Oid new_rel_secid = InvalidOid; + + /* + * first we update some of the information in our uncataloged relation's +*************** AddNewRelationTuple(Relation pg_class_de +*** 771,778 **** + + new_rel_desc->rd_att->tdtypeid = new_type_oid; + + /* Now build and insert the tuple */ +! InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions); + } + + +--- 825,836 ---- + + new_rel_desc->rd_att->tdtypeid = new_type_oid; + ++ if (secLabels) ++ new_rel_secid = secLabels[0]; ++ + /* Now build and insert the tuple */ +! InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, +! reloptions, new_rel_secid); + } + + +*************** heap_create_with_catalog(const char *rel +*** 843,849 **** + int oidinhcount, + OnCommitAction oncommit, + Datum reloptions, +! bool allow_system_table_mods) + { + Relation pg_class_desc; + Relation new_rel_desc; +--- 901,908 ---- + int oidinhcount, + OnCommitAction oncommit, + Datum reloptions, +! bool allow_system_table_mods, +! Oid *secLabels) + { + Relation pg_class_desc; + Relation new_rel_desc; +*************** heap_create_with_catalog(const char *rel +*** 1019,1031 **** + new_type_oid, + ownerid, + relkind, +! reloptions); + + /* + * now add tuples to pg_attribute for the attributes in our new relation. + */ + AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind, +! oidislocal, oidinhcount); + + /* + * Make a dependency link to force the relation to be deleted if its +--- 1078,1091 ---- + new_type_oid, + ownerid, + relkind, +! reloptions, +! secLabels); + + /* + * now add tuples to pg_attribute for the attributes in our new relation. + */ + AddNewAttributeTuples(relid, new_rel_desc->rd_att, relkind, +! oidislocal, oidinhcount, secLabels); + + /* + * Make a dependency link to force the relation to be deleted if its +*************** heap_drop_with_catalog(Oid relid) +*** 1484,1489 **** +--- 1544,1554 ---- + * delete relation tuple + */ + DeleteRelationTuple(relid); ++ ++ /* ++ * delete orphan pg_security entries ++ */ ++ securityReclaimOnDropTable(relid); + } + + +diff -Nrpc blob/src/backend/catalog/index.c sepgsql/src/backend/catalog/index.c +*** blob/src/backend/catalog/index.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/catalog/index.c Tue Dec 15 17:30:25 2009 +*************** +*** 48,53 **** +--- 48,54 ---- + #include "nodes/nodeFuncs.h" + #include "optimizer/clauses.h" + #include "optimizer/var.h" ++ #include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/lmgr.h" + #include "storage/procarray.h" +*************** AppendAttributeTuples(Relation indexRela +*** 352,358 **** + Assert(indexTupDesc->attrs[i]->attnum == i + 1); + Assert(indexTupDesc->attrs[i]->attcacheoff == -1); + +! InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], indstate); + } + + CatalogCloseIndexes(indstate); +--- 353,360 ---- + Assert(indexTupDesc->attrs[i]->attnum == i + 1); + Assert(indexTupDesc->attrs[i]->attcacheoff == -1); + +! InsertPgAttributeTuple(pg_attribute, indexTupDesc->attrs[i], +! indstate, InvalidOid); + } + + CatalogCloseIndexes(indstate); +*************** index_create(Oid heapRelationId, +*** 653,659 **** + */ + InsertPgClassTuple(pg_class, indexRelation, + RelationGetRelid(indexRelation), +! reloptions); + + /* done with pg_class */ + heap_close(pg_class, RowExclusiveLock); +--- 655,661 ---- + */ + InsertPgClassTuple(pg_class, indexRelation, + RelationGetRelid(indexRelation), +! reloptions, InvalidOid); + + /* done with pg_class */ + heap_close(pg_class, RowExclusiveLock); +diff -Nrpc blob/src/backend/catalog/namespace.c sepgsql/src/backend/catalog/namespace.c +*** blob/src/backend/catalog/namespace.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/catalog/namespace.c Thu Sep 17 17:04:16 2009 +*************** +*** 39,44 **** +--- 39,45 ---- + #include "miscadmin.h" + #include "nodes/makefuncs.h" + #include "parser/parse_func.h" ++ #include "security/sepgsql.h" + #include "storage/backendid.h" + #include "storage/ipc.h" + #include "utils/acl.h" +*************** LookupExplicitNamespace(const char *nspn +*** 2105,2111 **** +--- 2106,2115 ---- + if (strcmp(nspname, "pg_temp") == 0) + { + if (OidIsValid(myTempNamespace)) ++ { ++ sepgsql_schema_search(myTempNamespace, true); + return myTempNamespace; ++ } + + /* + * Since this is used only for looking up existing objects, there is +*************** LookupExplicitNamespace(const char *nspn +*** 2127,2132 **** +--- 2131,2137 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + nspname); ++ sepgsql_schema_search(namespaceId, true); + + return namespaceId; + } +*************** recomputeNamespacePath(void) +*** 2722,2728 **** + if (OidIsValid(namespaceId) && + !list_member_oid(oidlist, namespaceId) && + pg_namespace_aclcheck(namespaceId, roleid, +! ACL_USAGE) == ACLCHECK_OK) + oidlist = lappend_oid(oidlist, namespaceId); + } + } +--- 2727,2734 ---- + if (OidIsValid(namespaceId) && + !list_member_oid(oidlist, namespaceId) && + pg_namespace_aclcheck(namespaceId, roleid, +! ACL_USAGE) == ACLCHECK_OK && +! sepgsql_schema_search(namespaceId, false)) + oidlist = lappend_oid(oidlist, namespaceId); + } + } +*************** recomputeNamespacePath(void) +*** 2731,2737 **** + /* pg_temp --- substitute temp namespace, if any */ + if (OidIsValid(myTempNamespace)) + { +! if (!list_member_oid(oidlist, myTempNamespace)) + oidlist = lappend_oid(oidlist, myTempNamespace); + } + else +--- 2737,2744 ---- + /* pg_temp --- substitute temp namespace, if any */ + if (OidIsValid(myTempNamespace)) + { +! if (!list_member_oid(oidlist, myTempNamespace) && +! sepgsql_schema_search(myTempNamespace, false)) + oidlist = lappend_oid(oidlist, myTempNamespace); + } + else +*************** recomputeNamespacePath(void) +*** 2750,2756 **** + if (OidIsValid(namespaceId) && + !list_member_oid(oidlist, namespaceId) && + pg_namespace_aclcheck(namespaceId, roleid, +! ACL_USAGE) == ACLCHECK_OK) + oidlist = lappend_oid(oidlist, namespaceId); + } + } +--- 2757,2764 ---- + if (OidIsValid(namespaceId) && + !list_member_oid(oidlist, namespaceId) && + pg_namespace_aclcheck(namespaceId, roleid, +! ACL_USAGE) == ACLCHECK_OK && +! sepgsql_schema_search(namespaceId, false)) + oidlist = lappend_oid(oidlist, namespaceId); + } + } +*************** InitTempTableNamespace(void) +*** 2816,2821 **** +--- 2824,2830 ---- + char namespaceName[NAMEDATALEN]; + Oid namespaceId; + Oid toastspaceId; ++ Oid nspsecid; + + Assert(!OidIsValid(myTempNamespace)); + +*************** InitTempTableNamespace(void) +*** 2836,2841 **** +--- 2845,2853 ---- + errmsg("permission denied to create temporary tables in database \"%s\"", + get_database_name(MyDatabaseId)))); + ++ /* SELinux checks permission to create temp schema */ ++ nspsecid = sepgsql_schema_create(namespaceName, true, NULL); ++ + snprintf(namespaceName, sizeof(namespaceName), "pg_temp_%d", MyBackendId); + + namespaceId = GetSysCacheOid(NAMESPACENAME, +*************** InitTempTableNamespace(void) +*** 2851,2857 **** + * temp tables. This works because the places that access the temp + * namespace for my own backend skip permissions checks on it. + */ +! namespaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); + /* Advance command counter to make namespace visible */ + CommandCounterIncrement(); + } +--- 2863,2871 ---- + * temp tables. This works because the places that access the temp + * namespace for my own backend skip permissions checks on it. + */ +! namespaceId = NamespaceCreate(namespaceName, +! BOOTSTRAP_SUPERUSERID, +! nspsecid); + /* Advance command counter to make namespace visible */ + CommandCounterIncrement(); + } +*************** InitTempTableNamespace(void) +*** 2877,2883 **** + 0, 0, 0); + if (!OidIsValid(toastspaceId)) + { +! toastspaceId = NamespaceCreate(namespaceName, BOOTSTRAP_SUPERUSERID); + /* Advance command counter to make namespace visible */ + CommandCounterIncrement(); + } +--- 2891,2899 ---- + 0, 0, 0); + if (!OidIsValid(toastspaceId)) + { +! toastspaceId = NamespaceCreate(namespaceName, +! BOOTSTRAP_SUPERUSERID, +! nspsecid); + /* Advance command counter to make namespace visible */ + CommandCounterIncrement(); + } +*************** RemoveTempRelations(Oid tempNamespaceId) +*** 3030,3035 **** +--- 3046,3058 ---- + object.objectId = tempNamespaceId; + object.objectSubId = 0; + ++ /* ++ * TODO: ++ * SELinux should not check db_xxx:{drop} permission during cleaning ++ * up all the temporary objects. It may be necessary a bool argument ++ * to control MAC permission check on deleteOneObject() called from ++ * deleteWhatDependsOn() and so on. ++ */ + deleteWhatDependsOn(&object, false); + } + +diff -Nrpc blob/src/backend/catalog/pg_aggregate.c sepgsql/src/backend/catalog/pg_aggregate.c +*** blob/src/backend/catalog/pg_aggregate.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/catalog/pg_aggregate.c Wed Jul 15 19:37:35 2009 +*************** AggregateCreate(const char *aggName, +*** 231,237 **** + NIL, /* parameterDefaults */ + PointerGetDatum(NULL), /* proconfig */ + 1, /* procost */ +! 0); /* prorows */ + + /* + * Okay to create the pg_aggregate entry. +--- 231,238 ---- + NIL, /* parameterDefaults */ + PointerGetDatum(NULL), /* proconfig */ + 1, /* procost */ +! 0, /* prorows */ +! NULL); /* proseclabel*/ + + /* + * Okay to create the pg_aggregate entry. +diff -Nrpc blob/src/backend/catalog/pg_conversion.c sepgsql/src/backend/catalog/pg_conversion.c +*** blob/src/backend/catalog/pg_conversion.c Sat Jan 3 13:01:35 2009 +--- sepgsql/src/backend/catalog/pg_conversion.c Thu Sep 17 22:10:19 2009 +*************** Oid +*** 40,46 **** + ConversionCreate(const char *conname, Oid connamespace, + Oid conowner, + int32 conforencoding, int32 contoencoding, +! Oid conproc, bool def) + { + int i; + Relation rel; +--- 40,46 ---- + ConversionCreate(const char *conname, Oid connamespace, + Oid conowner, + int32 conforencoding, int32 contoencoding, +! Oid conproc, Oid consecid, bool def) + { + int i; + Relation rel; +*************** ConversionCreate(const char *conname, Oi +*** 104,109 **** +--- 104,111 ---- + values[Anum_pg_conversion_condefault - 1] = BoolGetDatum(def); + + tup = heap_form_tuple(tupDesc, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, consecid); + + /* insert a new tuple */ + oid = simple_heap_insert(rel, tup); +diff -Nrpc blob/src/backend/catalog/pg_largeobject.c sepgsql/src/backend/catalog/pg_largeobject.c +*** blob/src/backend/catalog/pg_largeobject.c Fri Dec 18 09:40:55 2009 +--- sepgsql/src/backend/catalog/pg_largeobject.c Fri Dec 18 10:27:56 2009 +*************** +*** 25,30 **** +--- 25,31 ---- + #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,46 **** + * will appear to exist with size 0. + */ + Oid +! LargeObjectCreate(Oid loid) + { + Relation pg_lo_meta; + HeapTuple ntup; +--- 41,47 ---- + * will appear to exist with size 0. + */ + Oid +! LargeObjectCreate(Oid loid, Oid secid) + { + Relation pg_lo_meta; + HeapTuple ntup; +*************** LargeObjectCreate(Oid loid) +*** 65,70 **** +--- 66,73 ---- + 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); +*************** LargeObjectAlterOwner(Oid loid, Oid newO +*** 205,210 **** +--- 208,216 ---- + + /* 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 -Nrpc blob/src/backend/catalog/pg_namespace.c sepgsql/src/backend/catalog/pg_namespace.c +*** blob/src/backend/catalog/pg_namespace.c Sat Jan 3 13:01:35 2009 +--- sepgsql/src/backend/catalog/pg_namespace.c Tue Sep 8 23:55:48 2009 +*************** +*** 28,34 **** + * --------------- + */ + Oid +! NamespaceCreate(const char *nspName, Oid ownerId) + { + Relation nspdesc; + HeapTuple tup; +--- 28,34 ---- + * --------------- + */ + Oid +! NamespaceCreate(const char *nspName, Oid ownerId, Oid nspsecid) + { + Relation nspdesc; + HeapTuple tup; +*************** NamespaceCreate(const char *nspName, Oid +*** 66,71 **** +--- 66,73 ---- + tupDesc = nspdesc->rd_att; + + tup = heap_form_tuple(tupDesc, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, nspsecid); + + nspoid = simple_heap_insert(nspdesc, tup); + Assert(OidIsValid(nspoid)); +diff -Nrpc blob/src/backend/catalog/pg_operator.c sepgsql/src/backend/catalog/pg_operator.c +*** blob/src/backend/catalog/pg_operator.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/catalog/pg_operator.c Thu Sep 17 22:10:19 2009 +*************** +*** 28,33 **** +--- 28,34 ---- + #include "catalog/pg_type.h" + #include "miscadmin.h" + #include "parser/parse_oper.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +*************** OperatorShellMake(const char *operatorNa +*** 204,209 **** +--- 205,211 ---- + { + Relation pg_operator_desc; + Oid operatorObjectId; ++ Oid secid; + int i; + HeapTuple tup; + Datum values[Natts_pg_operator]; +*************** OperatorShellMake(const char *operatorNa +*** 220,225 **** +--- 222,231 ---- + errmsg("\"%s\" is not a valid operator name", + operatorName))); + ++ /* SELinux permission check */ ++ secid = sepgsql_operator_create(operatorName, InvalidOid, ++ operatorNamespace, ++ InvalidOid, InvalidOid, InvalidOid); + /* + * initialize our *nulls and *values arrays + */ +*************** OperatorShellMake(const char *operatorNa +*** 260,265 **** +--- 266,273 ---- + * create a new operator tuple + */ + tup = heap_form_tuple(tupDesc, values, nulls); ++ if (HeapTupleHasSecid(tup) && OidIsValid(secid)) ++ HeapTupleSetSecid(tup, secid); + + /* + * insert our "shell" operator tuple +*************** OperatorCreate(const char *operatorName, +*** 347,352 **** +--- 355,361 ---- + bool selfCommutator = false; + NameData oname; + TupleDesc tupDesc; ++ Oid secid; + int i; + + /* +*************** OperatorCreate(const char *operatorName, +*** 476,481 **** +--- 485,494 ---- + else + negatorId = InvalidOid; + ++ /* SELinux permission checks */ ++ secid = sepgsql_operator_create(operatorName, operatorObjectId, ++ operatorNamespace, ++ procedureId, restrictionId, joinId); + /* + * set up values in the operator tuple + */ +*************** OperatorCreate(const char *operatorName, +*** 523,528 **** +--- 536,543 ---- + values, + nulls, + replaces); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, secid); + + simple_heap_update(pg_operator_desc, &tup->t_self, tup); + } +*************** OperatorCreate(const char *operatorName, +*** 530,535 **** +--- 545,552 ---- + { + tupDesc = pg_operator_desc->rd_att; + tup = heap_form_tuple(tupDesc, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, secid); + + operatorObjectId = simple_heap_insert(pg_operator_desc, tup); + } +diff -Nrpc blob/src/backend/catalog/pg_proc.c sepgsql/src/backend/catalog/pg_proc.c +*** blob/src/backend/catalog/pg_proc.c Thu Mar 18 09:43:03 2010 +--- sepgsql/src/backend/catalog/pg_proc.c Thu Mar 18 01:55:40 2010 +*************** +*** 29,34 **** +--- 29,35 ---- + #include "miscadmin.h" + #include "nodes/nodeFuncs.h" + #include "parser/parse_type.h" ++ #include "security/sepgsql.h" + #include "tcop/pquery.h" + #include "tcop/tcopprot.h" + #include "utils/acl.h" +*************** ProcedureCreate(const char *procedureNam +*** 78,84 **** + List *parameterDefaults, + Datum proconfig, + float4 procost, +! float4 prorows) + { + Oid retval; + int parameterCount; +--- 79,86 ---- + List *parameterDefaults, + Datum proconfig, + float4 procost, +! float4 prorows, +! Node *proseclabel) + { + Oid retval; + int parameterCount; +*************** ProcedureCreate(const char *procedureNam +*** 97,102 **** +--- 99,105 ---- + Datum values[Natts_pg_proc]; + bool replaces[Natts_pg_proc]; + Oid relid; ++ Oid prosecid = InvalidOid; + NameData procname; + TupleDesc tupDesc; + bool is_update; +*************** ProcedureCreate(const char *procedureNam +*** 344,349 **** +--- 347,357 ---- + ObjectIdGetDatum(procNamespace), + 0); + ++ /* Check permission to create/replace a function */ ++ prosecid = sepgsql_proc_create(procedureName, oldtup, ++ procNamespace, languageObjectId, ++ (DefElem *)proseclabel); ++ + if (HeapTupleIsValid(oldtup)) + { + /* There is one; okay to replace it? */ +*************** ProcedureCreate(const char *procedureNam +*** 481,486 **** +--- 489,496 ---- + + /* Okay, do it... */ + tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, prosecid); + simple_heap_update(rel, &tup->t_self, tup); + + ReleaseSysCache(oldtup); +*************** ProcedureCreate(const char *procedureNam +*** 490,495 **** +--- 500,507 ---- + { + /* Creating a new procedure */ + tup = heap_form_tuple(tupDesc, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, prosecid); + simple_heap_insert(rel, tup); + is_update = false; + } +diff -Nrpc blob/src/backend/catalog/pg_security.c sepgsql/src/backend/catalog/pg_security.c +*** blob/src/backend/catalog/pg_security.c Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/catalog/pg_security.c Sun Dec 20 23:35:32 2009 +*************** +*** 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 -Nrpc blob/src/backend/catalog/pg_shdepend.c sepgsql/src/backend/catalog/pg_shdepend.c +*** blob/src/backend/catalog/pg_shdepend.c Fri Dec 18 09:40:55 2009 +--- sepgsql/src/backend/catalog/pg_shdepend.c Fri Dec 18 10:27:56 2009 +*************** +*** 37,42 **** +--- 37,43 ---- + #include "commands/schemacmds.h" + #include "commands/tablecmds.h" + #include "commands/typecmds.h" ++ #include "security/sepgsql.h" + #include "storage/lmgr.h" + #include "miscadmin.h" + #include "utils/acl.h" +*************** shdepReassignOwned(List *roleids, Oid ne +*** 1340,1345 **** +--- 1341,1348 ---- + break; + + case TypeRelationId: ++ /* SELinux checks */ ++ sepgsql_type_alter(sdepForm->objid, NULL, InvalidOid); + AlterTypeOwnerInternal(sdepForm->objid, newrole, true); + break; + +*************** shdepReassignOwned(List *roleids, Oid ne +*** 1352,1358 **** + break; + + case RelationRelationId: +! + /* + * Pass recursing = true so that we don't fail on indexes, + * owned sequences, etc when we happen to visit them +--- 1355,1362 ---- + break; + + case RelationRelationId: +! /* SELinux checks */ +! sepgsql_relation_alter(sdepForm->objid, NULL, InvalidOid); + /* + * Pass recursing = true so that we don't fail on indexes, + * owned sequences, etc when we happen to visit them +diff -Nrpc blob/src/backend/catalog/pg_type.c sepgsql/src/backend/catalog/pg_type.c +*** blob/src/backend/catalog/pg_type.c Sun Sep 6 19:40:49 2009 +--- sepgsql/src/backend/catalog/pg_type.c Fri Sep 18 17:39:46 2009 +*************** +*** 25,30 **** +--- 25,31 ---- + #include "commands/typecmds.h" + #include "miscadmin.h" + #include "parser/scansup.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +*************** TypeShellMake(const char *typeName, Oid +*** 56,65 **** +--- 57,73 ---- + Datum values[Natts_pg_type]; + bool nulls[Natts_pg_type]; + Oid typoid; ++ Oid typsid; + NameData name; + + Assert(PointerIsValid(typeName)); + ++ /* SELinux check permission to create a shell type */ ++ typsid = sepgsql_type_create(typeName, InvalidOid, typeNamespace, ++ F_SHELL_IN, F_SHELL_OUT, ++ InvalidOid, InvalidOid, ++ InvalidOid, InvalidOid, InvalidOid); ++ + /* + * open pg_type + */ +*************** TypeCreate(Oid newTypeOid, +*** 201,206 **** +--- 209,215 ---- + { + Relation pg_type_desc; + Oid typeObjectId; ++ Oid typeSecid = InvalidOid; + bool rebuildDeps = false; + HeapTuple tup; + bool nulls[Natts_pg_type]; +*************** TypeCreate(Oid newTypeOid, +*** 367,372 **** +--- 376,390 ---- + CStringGetDatum(typeName), + ObjectIdGetDatum(typeNamespace), + 0, 0); ++ ++ /* SELinux checks to create/replace type */ ++ if (!isImplicitArray && typeType != TYPTYPE_COMPOSITE) ++ typeSecid = sepgsql_type_create(typeName, tup, typeNamespace, ++ inputProcedure, outputProcedure, ++ receiveProcedure, sendProcedure, ++ typmodinProcedure, typmodoutProcedure, ++ analyzeProcedure); ++ + if (HeapTupleIsValid(tup)) + { + /* +*************** TypeCreate(Oid newTypeOid, +*** 412,417 **** +--- 430,437 ---- + /* Force the OID if requested by caller, else heap_insert does it */ + if (OidIsValid(newTypeOid)) + HeapTupleSetOid(tup, newTypeOid); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, typeSecid); + + typeObjectId = simple_heap_insert(pg_type_desc, tup); + } +diff -Nrpc blob/src/backend/catalog/toasting.c sepgsql/src/backend/catalog/toasting.c +*** blob/src/backend/catalog/toasting.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/catalog/toasting.c Thu Oct 8 09:29:32 2009 +*************** +*** 28,33 **** +--- 28,34 ---- + #include "catalog/toasting.h" + #include "miscadmin.h" + #include "nodes/makefuncs.h" ++ #include "security/sepgsql.h" + #include "utils/builtins.h" + #include "utils/syscache.h" + +*************** create_toast_table(Relation rel, Oid toa +*** 125,130 **** +--- 126,132 ---- + char toast_relname[NAMEDATALEN]; + char toast_idxname[NAMEDATALEN]; + IndexInfo *indexInfo; ++ Oid *secLabels; + Oid classObjectId[2]; + int16 coloptions[2]; + ObjectAddress baseobject, +*************** create_toast_table(Relation rel, Oid toa +*** 199,204 **** +--- 201,211 ---- + else + namespaceid = PG_TOAST_NAMESPACE; + ++ secLabels = sepgsql_relation_create(toast_relname, ++ RELKIND_TOASTVALUE, ++ tupdesc, namespaceid, ++ NULL, NIL, false, false); ++ + toast_relid = heap_create_with_catalog(toast_relname, + namespaceid, + rel->rd_rel->reltablespace, +*************** create_toast_table(Relation rel, Oid toa +*** 212,218 **** + 0, + ONCOMMIT_NOOP, + reloptions, +! true); + + /* make the toast relation visible, else index creation will fail */ + CommandCounterIncrement(); +--- 219,226 ---- + 0, + ONCOMMIT_NOOP, + reloptions, +! true, +! secLabels); + + /* make the toast relation visible, else index creation will fail */ + CommandCounterIncrement(); +diff -Nrpc blob/src/backend/commands/aggregatecmds.c sepgsql/src/backend/commands/aggregatecmds.c +*** blob/src/backend/commands/aggregatecmds.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/commands/aggregatecmds.c Thu Sep 17 22:10:19 2009 +*************** +*** 32,37 **** +--- 32,38 ---- + #include "miscadmin.h" + #include "parser/parse_func.h" + #include "parser/parse_type.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +*************** RenameAggregate(List *name, List *args, +*** 311,316 **** +--- 312,320 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux permission checks */ ++ sepgsql_proc_alter(procOid, newname, InvalidOid); ++ + /* rename */ + namestrcpy(&(((Form_pg_proc) GETSTRUCT(tup))->proname), newname); + simple_heap_update(rel, &tup->t_self, tup); +diff -Nrpc blob/src/backend/commands/alter.c sepgsql/src/backend/commands/alter.c +*** blob/src/backend/commands/alter.c Fri Dec 18 09:40:55 2009 +--- sepgsql/src/backend/commands/alter.c Fri Dec 18 10:27:56 2009 +*************** ExecAlterOwnerStmt(AlterOwnerStmt *stmt) +*** 289,291 **** +--- 289,320 ---- + (int) stmt->objectType); + } + } ++ ++ void ++ ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt) ++ { ++ DefElem *seclabel = (DefElem *)stmt->secLabel; ++ ++ switch (stmt->objectType) ++ { ++ case OBJECT_DATABASE: ++ AlterDatabaseSecLabel(strVal(linitial(stmt->object)), seclabel); ++ break; ++ case OBJECT_SCHEMA: ++ AlterSchemaSecLabel(strVal(linitial(stmt->object)), seclabel); ++ break; ++ case OBJECT_TABLE: ++ case OBJECT_SEQUENCE: ++ case OBJECT_COLUMN: ++ CheckRelationOwnership(stmt->relation, true); ++ AlterRelationSecLabel(stmt->relation, stmt->subname, ++ stmt->objectType, seclabel); ++ break; ++ case OBJECT_FUNCTION: ++ AlterFunctionSecLabel(stmt->object, stmt->objarg, seclabel); ++ break; ++ default: ++ elog(ERROR, "unrecognized AlterSecLabelStmt type: %d", ++ (int) stmt->objectType); ++ } ++ } +diff -Nrpc blob/src/backend/commands/cluster.c sepgsql/src/backend/commands/cluster.c +*** blob/src/backend/commands/cluster.c Thu Mar 18 09:43:03 2010 +--- sepgsql/src/backend/commands/cluster.c Thu Mar 18 01:55:40 2010 +*************** +*** 36,41 **** +--- 36,42 ---- + #include "commands/trigger.h" + #include "commands/vacuum.h" + #include "miscadmin.h" ++ #include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/procarray.h" + #include "utils/acl.h" +*************** rebuild_relation(Relation OldHeap, Oid i +*** 617,624 **** + /* + * The new relation is local to our transaction and we know nothing + * depends on it, so DROP_RESTRICT should be OK. + */ +! performDeletion(&object, DROP_RESTRICT); + + /* performDeletion does CommandCounterIncrement at end */ + +--- 618,626 ---- + /* + * The new relation is local to our transaction and we know nothing + * depends on it, so DROP_RESTRICT should be OK. ++ * SELinux does not check any permissions here. + */ +! performDeletionNoPerms(&object, DROP_RESTRICT); + + /* performDeletion does CommandCounterIncrement at end */ + +*************** make_new_heap(Oid OIDOldHeap, const char +*** 717,723 **** + 0, + ONCOMMIT_NOOP, + reloptions, +! allowSystemTableMods); + + ReleaseSysCache(tuple); + +--- 719,726 ---- + 0, + ONCOMMIT_NOOP, + reloptions, +! allowSystemTableMods, +! sepgsql_relation_copy(OldHeap)); + + ReleaseSysCache(tuple); + +*************** copy_heap_data(Oid OIDNewHeap, Oid OIDOl +*** 929,934 **** +--- 932,941 ---- + if (NewHeap->rd_rel->relhasoids) + HeapTupleSetOid(copiedTuple, HeapTupleGetOid(tuple)); + ++ /* Preserve SID, if any */ ++ if (HeapTupleHasSecid(copiedTuple)) ++ HeapTupleSetSecid(copiedTuple, HeapTupleGetSecid(tuple)); ++ + /* The heap rewrite module does the rest */ + rewrite_heap_tuple(rwstate, tuple, copiedTuple); + +diff -Nrpc blob/src/backend/commands/conversioncmds.c sepgsql/src/backend/commands/conversioncmds.c +*** blob/src/backend/commands/conversioncmds.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/commands/conversioncmds.c Thu Sep 17 22:10:19 2009 +*************** +*** 24,29 **** +--- 24,30 ---- + #include "mb/pg_wchar.h" + #include "miscadmin.h" + #include "parser/parse_func.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +*************** CreateConversionCommand(CreateConversion +*** 45,50 **** +--- 46,52 ---- + int from_encoding; + int to_encoding; + Oid funcoid; ++ Oid secid; + const char *from_encoding_name = stmt->for_encoding_name; + const char *to_encoding_name = stmt->to_encoding_name; + List *func_name = stmt->func_name; +*************** CreateConversionCommand(CreateConversion +*** 96,101 **** +--- 98,106 ---- + aclcheck_error(aclresult, ACL_KIND_PROC, + NameListToString(func_name)); + ++ /* SELinux checks */ ++ secid = sepgsql_conversion_create(conversion_name, namespaceId, funcoid); ++ + /* + * Check that the conversion function is suitable for the requested source + * and target encodings. We do that by calling the function with an empty +*************** CreateConversionCommand(CreateConversion +*** 114,120 **** + * name) + */ + ConversionCreate(conversion_name, namespaceId, GetUserId(), +! from_encoding, to_encoding, funcoid, stmt->def); + } + + /* +--- 119,125 ---- + * name) + */ + ConversionCreate(conversion_name, namespaceId, GetUserId(), +! from_encoding, to_encoding, funcoid, secid, stmt->def); + } + + /* +*************** RenameConversion(List *name, const char +*** 240,245 **** +--- 245,253 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_conversion_alter(conversionOid, newname); ++ + /* rename */ + namestrcpy(&(((Form_pg_conversion) GETSTRUCT(tup))->conname), newname); + simple_heap_update(rel, &tup->t_self, tup); +*************** AlterConversionOwner_internal(Relation r +*** 336,341 **** +--- 344,351 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(convForm->connamespace)); + } ++ /* SELinux checks */ ++ sepgsql_conversion_alter(HeapTupleGetOid(tup), NULL); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy +diff -Nrpc blob/src/backend/commands/copy.c sepgsql/src/backend/commands/copy.c +*** blob/src/backend/commands/copy.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/commands/copy.c Mon Sep 28 09:29:32 2009 +*************** +*** 21,28 **** +--- 21,31 ---- + #include + + #include "access/heapam.h" ++ #include "access/sysattr.h" + #include "access/xact.h" ++ #include "catalog/heap.h" + #include "catalog/namespace.h" ++ #include "catalog/pg_security.h" + #include "catalog/pg_type.h" + #include "commands/copy.h" + #include "commands/trigger.h" +*************** +*** 34,39 **** +--- 37,44 ---- + #include "optimizer/planner.h" + #include "parser/parse_relation.h" + #include "rewrite/rewriteHandler.h" ++ #include "security/rowlevel.h" ++ #include "security/sepgsql.h" + #include "storage/fd.h" + #include "tcop/tcopprot.h" + #include "utils/acl.h" +*************** typedef struct CopyStateData +*** 160,165 **** +--- 165,174 ---- + char *raw_buf; + int raw_buf_index; /* next byte to process */ + int raw_buf_len; /* total # of bytes stored */ ++ ++ /* dump/restore support for security_label */ ++ FmgrInfo seclabel_out_function; ++ bool seclabel_force_quot; + } CopyStateData; + + typedef CopyStateData *CopyState; +*************** static const char BinarySignature[11] = +*** 243,250 **** + /* non-export function prototypes */ + static void DoCopyTo(CopyState cstate); + static void CopyTo(CopyState cstate); +! static void CopyOneRowTo(CopyState cstate, Oid tupleOid, +! Datum *values, bool *nulls); + static void CopyFrom(CopyState cstate); + static bool CopyReadLine(CopyState cstate); + static bool CopyReadLineText(CopyState cstate); +--- 252,259 ---- + /* non-export function prototypes */ + static void DoCopyTo(CopyState cstate); + static void CopyTo(CopyState cstate); +! static void CopyOneRowTo(CopyState cstate, HeapTuple tuple, +! Datum *values, bool *nulls); + static void CopyFrom(CopyState cstate); + static bool CopyReadLine(CopyState cstate); + static bool CopyReadLineText(CopyState cstate); +*************** DoCopy(const CopyStmt *stmt, const char +*** 958,969 **** + errmsg("CSV quote character must not appear in the NULL specification"))); + + /* Disallow file COPY except to superusers. */ +! if (!pipe && !superuser()) +! ereport(ERROR, +! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), +! errmsg("must be superuser to COPY to or from a file"), +! errhint("Anyone can COPY to stdout or from stdin. " +! "psql's \\copy command also works for anyone."))); + + if (stmt->relation) + { +--- 967,985 ---- + errmsg("CSV quote character must not appear in the NULL specification"))); + + /* Disallow file COPY except to superusers. */ +! if (!pipe) +! { +! if (!superuser()) +! ereport(ERROR, +! (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), +! errmsg("must be superuser to COPY to or from a file"), +! errhint("Anyone can COPY to stdout or from stdin. " +! "psql's \\copy command also works for anyone."))); +! if (is_from) +! sepgsql_file_read(stmt->filename); +! else +! sepgsql_file_write(stmt->filename); +! } + + if (stmt->relation) + { +*************** DoCopy(const CopyStmt *stmt, const char +*** 1090,1095 **** +--- 1106,1114 ---- + + num_phys_attrs = tupDesc->natts; + ++ /* SELinux: check table/column level permission */ ++ sepgsqlCheckCopyTable(cstate->rel, cstate->attnumlist, is_from); ++ + /* Convert FORCE QUOTE name list to per-column flags, check validity */ + cstate->force_quote_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool)); + if (force_quote) +*************** DoCopy(const CopyStmt *stmt, const char +*** 1104,1114 **** + int attnum = lfirst_int(cur); + + if (!list_member_int(cstate->attnumlist, attnum)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", +! NameStr(tupDesc->attrs[attnum - 1]->attname)))); +! cstate->force_quote_flags[attnum - 1] = true; + } + } + +--- 1123,1153 ---- + int attnum = lfirst_int(cur); + + if (!list_member_int(cstate->attnumlist, attnum)) ++ { ++ Form_pg_attribute attForm; ++ ++ if (SystemAttributeIsWritable(attnum)) ++ attForm = SystemAttributeDefinition(attnum, true); ++ else ++ attForm = tupDesc->attrs[attnum - 1]; ++ ++ Assert(attForm != NULL); ++ + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("FORCE QUOTE column \"%s\" not referenced by COPY", +! NameStr(attForm->attname)))); +! } +! +! switch (attnum) +! { +! case SecurityAttributeNumber: +! cstate->seclabel_force_quot = true; +! break; +! default: +! cstate->force_quote_flags[attnum - 1] = true; +! break; +! } + } + } + +*************** DoCopy(const CopyStmt *stmt, const char +*** 1126,1135 **** + int attnum = lfirst_int(cur); + + if (!list_member_int(cstate->attnumlist, attnum)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", +! NameStr(tupDesc->attrs[attnum - 1]->attname)))); + cstate->force_notnull_flags[attnum - 1] = true; + } + } +--- 1165,1187 ---- + int attnum = lfirst_int(cur); + + if (!list_member_int(cstate->attnumlist, attnum)) ++ { ++ Form_pg_attribute attForm; ++ ++ if (SystemAttributeIsWritable(attnum)) ++ attForm = SystemAttributeDefinition(attnum, true); ++ else ++ attForm = tupDesc->attrs[attnum - 1]; ++ ++ Assert(attForm != NULL); ++ + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), + errmsg("FORCE NOT NULL column \"%s\" not referenced by COPY", +! NameStr(attForm->attname)))); +! } +! if (SystemAttributeIsWritable(attnum)) +! continue; /* ignore, if specified */ + cstate->force_notnull_flags[attnum - 1] = true; + } + } +*************** CopyTo(CopyState cstate) +*** 1321,1336 **** + int attnum = lfirst_int(cur); + Oid out_func_oid; + bool isvarlena; + + if (cstate->binary) +! getTypeBinaryOutputInfo(attr[attnum - 1]->atttypid, + &out_func_oid, + &isvarlena); + else +! getTypeOutputInfo(attr[attnum - 1]->atttypid, + &out_func_oid, + &isvarlena); +! fmgr_info(out_func_oid, &cstate->out_functions[attnum - 1]); + } + + /* +--- 1373,1403 ---- + int attnum = lfirst_int(cur); + Oid out_func_oid; + bool isvarlena; ++ FmgrInfo *out_fmgr; ++ Form_pg_attribute attForm; ++ ++ switch (attnum) ++ { ++ case SecurityAttributeNumber: ++ attForm = SystemAttributeDefinition(attnum, true); ++ out_fmgr = &cstate->seclabel_out_function; ++ break; ++ ++ default: ++ attForm = attr[attnum - 1]; ++ out_fmgr = &cstate->out_functions[attnum - 1]; ++ break; ++ } + + if (cstate->binary) +! getTypeBinaryOutputInfo(attForm->atttypid, + &out_func_oid, + &isvarlena); + else +! getTypeOutputInfo(attForm->atttypid, + &out_func_oid, + &isvarlena); +! fmgr_info(out_func_oid, out_fmgr); + } + + /* +*************** CopyTo(CopyState cstate) +*** 1385,1391 **** + CopySendChar(cstate, cstate->delim[0]); + hdr_delim = true; + +! colname = NameStr(attr[attnum - 1]->attname); + + CopyAttributeOutCSV(cstate, colname, false, + list_length(cstate->attnumlist) == 1); +--- 1452,1465 ---- + CopySendChar(cstate, cstate->delim[0]); + hdr_delim = true; + +! if (SystemAttributeIsWritable(attnum)) +! { +! Form_pg_attribute attForm +! = SystemAttributeDefinition(attnum, true); +! colname = NameStr(attForm->attname); +! } +! else +! colname = NameStr(attr[attnum - 1]->attname); + + CopyAttributeOutCSV(cstate, colname, false, + list_length(cstate->attnumlist) == 1); +*************** CopyTo(CopyState cstate) +*** 1411,1421 **** + { + CHECK_FOR_INTERRUPTS(); + + /* Deconstruct the tuple ... faster than repeated heap_getattr */ + heap_deform_tuple(tuple, tupDesc, values, nulls); + + /* Format and send the data */ +! CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls); + } + + heap_endscan(scandesc); +--- 1485,1499 ---- + { + CHECK_FOR_INTERRUPTS(); + ++ /* check Row-level permission on the tuple */ ++ if (!rowlvCopyToTuple(cstate->rel, tuple)) ++ continue; ++ + /* Deconstruct the tuple ... faster than repeated heap_getattr */ + heap_deform_tuple(tuple, tupDesc, values, nulls); + + /* Format and send the data */ +! CopyOneRowTo(cstate, tuple, values, nulls); + } + + heap_endscan(scandesc); +*************** CopyTo(CopyState cstate) +*** 1441,1447 **** + * Emit one row during CopyTo(). + */ + static void +! CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) + { + bool need_delim = false; + FmgrInfo *out_functions = cstate->out_functions; +--- 1519,1526 ---- + * Emit one row during CopyTo(). + */ + static void +! CopyOneRowTo(CopyState cstate, HeapTuple tuple, +! Datum *values, bool *nulls) + { + bool need_delim = false; + FmgrInfo *out_functions = cstate->out_functions; +*************** CopyOneRowTo(CopyState cstate, Oid tuple +*** 1461,1467 **** + { + /* Hack --- assume Oid is same size as int32 */ + CopySendInt32(cstate, sizeof(int32)); +! CopySendInt32(cstate, tupleOid); + } + } + else +--- 1540,1546 ---- + { + /* Hack --- assume Oid is same size as int32 */ + CopySendInt32(cstate, sizeof(int32)); +! CopySendInt32(cstate, HeapTupleGetOid(tuple)); + } + } + else +*************** CopyOneRowTo(CopyState cstate, Oid tuple +*** 1471,1477 **** + if (cstate->oids) + { + string = DatumGetCString(DirectFunctionCall1(oidout, +! ObjectIdGetDatum(tupleOid))); + CopySendString(cstate, string); + need_delim = true; + } +--- 1550,1556 ---- + if (cstate->oids) + { + string = DatumGetCString(DirectFunctionCall1(oidout, +! ObjectIdGetDatum(HeapTupleGetOid(tuple)))); + CopySendString(cstate, string); + need_delim = true; + } +*************** CopyOneRowTo(CopyState cstate, Oid tuple +*** 1480,1487 **** + foreach(cur, cstate->attnumlist) + { + int attnum = lfirst_int(cur); +! Datum value = values[attnum - 1]; +! bool isnull = nulls[attnum - 1]; + + if (!cstate->binary) + { +--- 1559,1569 ---- + foreach(cur, cstate->attnumlist) + { + int attnum = lfirst_int(cur); +! Oid relid; +! Datum value; +! bool isnull; +! bool force_quot; +! FmgrInfo *out_fmgr; + + if (!cstate->binary) + { +*************** CopyOneRowTo(CopyState cstate, Oid tuple +*** 1490,1495 **** +--- 1572,1595 ---- + need_delim = true; + } + ++ switch (attnum) ++ { ++ case SecurityAttributeNumber: ++ relid = RelationGetRelid(cstate->rel); ++ value = securitySysattSecLabelOut(relid, tuple); ++ isnull = false; ++ force_quot = cstate->seclabel_force_quot; ++ out_fmgr = &cstate->seclabel_out_function; ++ break; ++ ++ default: ++ value = values[attnum - 1]; ++ isnull = nulls[attnum - 1]; ++ force_quot = cstate->force_quote_flags[attnum - 1]; ++ out_fmgr = &out_functions[attnum - 1]; ++ break; ++ } ++ + if (isnull) + { + if (!cstate->binary) +*************** CopyOneRowTo(CopyState cstate, Oid tuple +*** 1501,1511 **** + { + if (!cstate->binary) + { +! string = OutputFunctionCall(&out_functions[attnum - 1], +! value); + if (cstate->csv_mode) +! CopyAttributeOutCSV(cstate, string, +! cstate->force_quote_flags[attnum - 1], + list_length(cstate->attnumlist) == 1); + else + CopyAttributeOutText(cstate, string); +--- 1601,1609 ---- + { + if (!cstate->binary) + { +! string = OutputFunctionCall(out_fmgr, value); + if (cstate->csv_mode) +! CopyAttributeOutCSV(cstate, string, force_quot, + list_length(cstate->attnumlist) == 1); + else + CopyAttributeOutText(cstate, string); +*************** CopyOneRowTo(CopyState cstate, Oid tuple +*** 1514,1521 **** + { + bytea *outputbytes; + +! outputbytes = SendFunctionCall(&out_functions[attnum - 1], +! value); + CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); + CopySendData(cstate, VARDATA(outputbytes), + VARSIZE(outputbytes) - VARHDRSZ); +--- 1612,1618 ---- + { + bytea *outputbytes; + +! outputbytes = SendFunctionCall(out_fmgr, value); + CopySendInt32(cstate, VARSIZE(outputbytes) - VARHDRSZ); + CopySendData(cstate, VARDATA(outputbytes), + VARSIZE(outputbytes) - VARHDRSZ); +*************** CopyFrom(CopyState cstate) +*** 1649,1656 **** +--- 1746,1755 ---- + num_defaults; + FmgrInfo *in_functions; + FmgrInfo oid_in_function; ++ FmgrInfo seclabel_in_function; + Oid *typioparams; + Oid oid_typioparam; ++ Oid seclabel_typioparam; + int attnum; + int i; + Oid in_func_oid; +*************** CopyFrom(CopyState cstate) +*** 1888,1893 **** +--- 1987,2004 ---- + fmgr_info(in_func_oid, &oid_in_function); + } + ++ if (list_member_int(cstate->attnumlist, ++ SecurityAttributeNumber)) ++ { ++ if (!cstate->binary) ++ getTypeInputInfo(TEXTOID, ++ &in_func_oid, &seclabel_typioparam); ++ else ++ getTypeBinaryInputInfo(TEXTOID, ++ &in_func_oid, &seclabel_typioparam); ++ fmgr_info(in_func_oid, &seclabel_in_function); ++ } ++ + values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); + nulls = (bool *) palloc(num_phys_attrs * sizeof(bool)); + +*************** CopyFrom(CopyState cstate) +*** 1922,1927 **** +--- 2033,2039 ---- + { + bool skip_tuple; + Oid loaded_oid = InvalidOid; ++ Oid loaded_seclabel = InvalidOid; + + CHECK_FOR_INTERRUPTS(); + +*************** CopyFrom(CopyState cstate) +*** 1993,2006 **** + /* Loop to read the user attributes on the line. */ + foreach(cur, cstate->attnumlist) + { + int attnum = lfirst_int(cur); + int m = attnum - 1; + + if (fieldno >= fldct) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), + errmsg("missing data for column \"%s\"", +! NameStr(attr[m]->attname)))); + string = field_strings[fieldno++]; + + if (cstate->csv_mode && string == NULL && +--- 2105,2125 ---- + /* Loop to read the user attributes on the line. */ + foreach(cur, cstate->attnumlist) + { ++ Form_pg_attribute attForm; ++ Datum dat; + int attnum = lfirst_int(cur); + int m = attnum - 1; + ++ if (SystemAttributeIsWritable(attnum)) ++ attForm = SystemAttributeDefinition(attnum, true); ++ else ++ attForm = attr[m]; ++ + if (fieldno >= fldct) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), + errmsg("missing data for column \"%s\"", +! NameStr(attForm->attname)))); + string = field_strings[fieldno++]; + + if (cstate->csv_mode && string == NULL && +*************** CopyFrom(CopyState cstate) +*** 2010,2023 **** + string = cstate->null_print; + } + +! cstate->cur_attname = NameStr(attr[m]->attname); + cstate->cur_attval = string; +! values[m] = InputFunctionCall(&in_functions[m], +! string, +! typioparams[m], +! attr[m]->atttypmod); +! if (string != NULL) +! nulls[m] = false; + cstate->cur_attname = NULL; + cstate->cur_attval = NULL; + } +--- 2129,2168 ---- + string = cstate->null_print; + } + +! cstate->cur_attname = NameStr(attForm->attname); + cstate->cur_attval = string; +! +! switch (attnum) +! { +! case SecurityAttributeNumber: +! if (!string) +! break; +! +! dat = InputFunctionCall(&seclabel_in_function, +! string, +! seclabel_typioparam, +! attForm->atttypmod); +! loaded_seclabel +! = securityTransSecLabelIn(RelationGetRelid(cstate->rel), +! TextDatumGetCString(dat)); +! break; +! +! default: +! if (cstate->csv_mode && string == NULL && +! cstate->force_notnull_flags[m]) +! { +! /* Go ahead and read the NULL string */ +! string = cstate->null_print; +! } +! +! values[m] = InputFunctionCall(&in_functions[m], +! string, +! typioparams[m], +! attForm->atttypmod); +! if (string != NULL) +! nulls[m] = false; +! break; +! } + cstate->cur_attname = NULL; + cstate->cur_attval = NULL; + } +*************** CopyFrom(CopyState cstate) +*** 2063,2079 **** + i = 0; + foreach(cur, cstate->attnumlist) + { + int attnum = lfirst_int(cur); + int m = attnum - 1; + +! cstate->cur_attname = NameStr(attr[m]->attname); + i++; +! values[m] = CopyReadBinaryAttribute(cstate, +! i, +! &in_functions[m], +! typioparams[m], +! attr[m]->atttypmod, +! &nulls[m]); + cstate->cur_attname = NULL; + } + } +--- 2208,2248 ---- + i = 0; + foreach(cur, cstate->attnumlist) + { ++ Form_pg_attribute attForm; ++ Datum dat; + int attnum = lfirst_int(cur); + int m = attnum - 1; + +! if (SystemAttributeIsWritable(attnum)) +! attForm = SystemAttributeDefinition(attnum, false); +! else +! attForm = attr[m]; +! +! cstate->cur_attname = NameStr(attForm->attname); + i++; +! +! switch (attnum) +! { +! case SecurityAttributeNumber: +! dat = CopyReadBinaryAttribute(cstate, i, +! &seclabel_in_function, +! seclabel_typioparam, +! attForm->atttypmod, +! &isnull); +! if (!isnull) +! loaded_seclabel +! = securityTransSecLabelIn(RelationGetRelid(cstate->rel), +! TextDatumGetCString(dat)); +! break; +! +! default: +! values[m] = CopyReadBinaryAttribute(cstate, i, +! &in_functions[m], +! typioparams[m], +! attr[m]->atttypmod, +! &nulls[m]); +! break; +! } + cstate->cur_attname = NULL; + } + } +*************** CopyFrom(CopyState cstate) +*** 2094,2099 **** +--- 2263,2270 ---- + + if (cstate->oids && file_has_oids) + HeapTupleSetOid(tuple, loaded_oid); ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, loaded_seclabel); + + /* Triggers and stuff need to be invoked in query context. */ + MemoryContextSwitchTo(oldcontext); +*************** CopyFrom(CopyState cstate) +*** 2118,2123 **** +--- 2289,2297 ---- + } + + if (!skip_tuple) ++ sepgsqlHeapTupleInsert(cstate->rel, tuple, false); ++ ++ if (!skip_tuple) + { + /* Place tuple in tuple slot */ + ExecStoreTuple(tuple, slot, InvalidBuffer, false); +*************** CopyGetAttnums(TupleDesc tupDesc, Relati +*** 3398,3403 **** +--- 3572,3584 ---- + } + if (attnum == InvalidAttrNumber) + { ++ Form_pg_attribute attForm ++ = SystemAttributeByName(name, tupDesc->tdhasoid); ++ if (attForm && SystemAttributeIsWritable(attForm->attnum)) ++ attnum = attForm->attnum; ++ } ++ if (attnum == InvalidAttrNumber) ++ { + if (rel != NULL) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), +*************** copy_dest_receive(TupleTableSlot *slot, +*** 3445,3451 **** + slot_getallattrs(slot); + + /* And send the data */ +! CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull); + } + + /* +--- 3626,3633 ---- + slot_getallattrs(slot); + + /* And send the data */ +! CopyOneRowTo(cstate, slot->tts_tuple, +! slot->tts_values, slot->tts_isnull); + } + + /* +diff -Nrpc blob/src/backend/commands/dbcommands.c sepgsql/src/backend/commands/dbcommands.c +*** blob/src/backend/commands/dbcommands.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/commands/dbcommands.c Sun Dec 20 16:30:19 2009 +*************** +*** 33,38 **** +--- 33,39 ---- + #include "catalog/indexing.h" + #include "catalog/pg_authid.h" + #include "catalog/pg_database.h" ++ #include "catalog/pg_security.h" + #include "catalog/pg_tablespace.h" + #include "commands/comment.h" + #include "commands/dbcommands.h" +*************** +*** 41,46 **** +--- 42,48 ---- + #include "miscadmin.h" + #include "pgstat.h" + #include "postmaster/bgwriter.h" ++ #include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/fd.h" + #include "storage/lmgr.h" +*************** createdb(const CreatedbStmt *stmt) +*** 111,116 **** +--- 113,119 ---- + bool new_record_nulls[Natts_pg_database]; + Oid dboid; + Oid datdba; ++ Oid datsecid; + ListCell *option; + DefElem *dtablespacename = NULL; + DefElem *downer = NULL; +*************** createdb(const CreatedbStmt *stmt) +*** 119,124 **** +--- 122,128 ---- + DefElem *dcollate = NULL; + DefElem *dctype = NULL; + DefElem *dconnlimit = NULL; ++ DefElem *dseclabel = NULL; + char *dbname = stmt->dbname; + char *dbowner = NULL; + const char *dbtemplate = NULL; +*************** createdb(const CreatedbStmt *stmt) +*** 200,205 **** +--- 204,217 ---- + errmsg("LOCATION is not supported anymore"), + errhint("Consider using tablespaces instead."))); + } ++ else if (strcmp(defel->defname, "security_context") == 0) ++ { ++ if (dseclabel) ++ ereport(ERROR, ++ (errcode(ERRCODE_SYNTAX_ERROR), ++ errmsg("conflicting or redundant options"))); ++ dseclabel = defel; ++ } + else + elog(ERROR, "option \"%s\" not recognized", + defel->defname); +*************** createdb(const CreatedbStmt *stmt) +*** 294,299 **** +--- 306,314 ---- + 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. +*************** createdb(const CreatedbStmt *stmt) +*** 557,562 **** +--- 572,579 ---- + new_record, new_record_nulls); + + HeapTupleSetOid(tuple, dboid); ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, datsecid); + + simple_heap_insert(pg_database_rel, tuple); + +*************** createdb(const CreatedbStmt *stmt) +*** 573,578 **** +--- 590,598 ---- + /* Create pg_shdepend entries for objects within database */ + copyTemplateDependencies(src_dboid, dboid); + ++ /* Create pg_security entries for objects within database */ ++ securityOnCreateDatabase(src_dboid, dboid); ++ + /* + * Force a checkpoint before starting the copy. This will force dirty + * buffers out to disk, to ensure source database is up-to-date on disk +*************** dropdb(const char *dbname, bool missing_ +*** 776,781 **** +--- 796,804 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + dbname); + ++ /* SELinux checks db_database:{drop} permission */ ++ sepgsql_database_drop(db_id); ++ + /* + * Disallow dropping a DB that is marked istemplate. This is just to + * prevent people from accidentally dropping template0 or template1; they +*************** dropdb(const char *dbname, bool missing_ +*** 829,834 **** +--- 852,862 ---- + dropDatabaseDependencies(db_id); + + /* ++ * Remove pg_security entries for the database. ++ */ ++ securityOnDropDatabase(db_id); ++ ++ /* + * Drop pages for this database that are in the shared buffer cache. This + * is important to ensure that no remaining backend tries to write out a + * dirty buffer to the dead database later... +*************** RenameDatabase(const char *oldname, cons +*** 913,918 **** +--- 941,949 ---- + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to rename database"))); + ++ /* SELinux: check db_database:{setattr} */ ++ sepgsql_database_alter(db_id); ++ + /* + * Make sure the new name doesn't exist. See notes for same error in + * CREATE DATABASE. +*************** movedb(const char *dbname, const char *t +*** 1025,1030 **** +--- 1056,1064 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + dbname); + ++ /* SELinux checks db_database:{setattr} */ ++ sepgsql_database_alter(db_id); ++ + /* + * Obviously can't move the tables of my own database + */ +*************** AlterDatabase(AlterDatabaseStmt *stmt, b +*** 1377,1382 **** +--- 1411,1419 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + stmt->dbname); + ++ /* SELinux checks db_database:{setattr} */ ++ sepgsql_database_alter(HeapTupleGetOid(tuple)); ++ + /* + * Build an updated tuple, perusing the information just obtained + */ +*************** AlterDatabaseSet(AlterDatabaseSetStmt *s +*** 1449,1454 **** +--- 1486,1494 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, + stmt->dbname); + ++ /* SELinux checks db_database:{setattr} */ ++ sepgsql_database_alter(HeapTupleGetOid(tuple)); ++ + memset(repl_repl, false, sizeof(repl_repl)); + repl_repl[Anum_pg_database_datconfig - 1] = true; + +*************** AlterDatabaseOwner(const char *dbname, O +*** 1571,1576 **** +--- 1611,1619 ---- + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to change owner of database"))); + ++ /* SELinux checks db_database:{setattr} */ ++ sepgsql_database_alter(HeapTupleGetOid(tuple)); ++ + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); + +*************** AlterDatabaseOwner(const char *dbname, O +*** 1615,1620 **** +--- 1658,1715 ---- + */ + } + ++ /* ++ * ALTER DATABASE name SECURITY_LABEL [=] newlabel ++ */ ++ void ++ AlterDatabaseSecLabel(const char *dbname, DefElem *seclabel) ++ { ++ Relation rel; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ ScanKeyData scankey; ++ SysScanDesc scan; ++ Oid secid; ++ bool replaces[Natts_pg_database]; ++ ++ /* Fetch the old tuple */ ++ rel = heap_open(DatabaseRelationId, RowExclusiveLock); ++ ScanKeyInit(&scankey, ++ Anum_pg_database_datname, ++ BTEqualStrategyNumber, F_NAMEEQ, ++ NameGetDatum(dbname)); ++ scan = systable_beginscan(rel, DatabaseNameIndexId, true, ++ SnapshotNow, 1, &scankey); ++ oldtup = systable_getnext(scan); ++ if (!HeapTupleIsValid(oldtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_DATABASE), ++ errmsg("database \"%s\" does not exist", dbname))); ++ ++ memset(replaces, false, sizeof(replaces)); ++ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), ++ NULL, NULL, replaces); ++ if (!HeapTupleHasSecid(newtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to set security label on \"%s\"", dbname))); ++ systable_endscan(scan); ++ ++ /* check DAC permission */ ++ if (!pg_database_ownercheck(HeapTupleGetOid(newtup), GetUserId())) ++ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, dbname); ++ ++ /* SELinux checks db_database:{setattr relabelfrom relabelto} */ ++ secid = sepgsql_database_relabel(HeapTupleGetOid(newtup), seclabel); ++ HeapTupleSetSecid(newtup, secid); ++ ++ simple_heap_update(rel, &newtup->t_self, newtup); ++ CatalogUpdateIndexes(rel, newtup); ++ ++ heap_freetuple(newtup); ++ ++ heap_close(rel, RowExclusiveLock); ++ } + + /* + * Helper functions +diff -Nrpc blob/src/backend/commands/foreigncmds.c sepgsql/src/backend/commands/foreigncmds.c +*** blob/src/backend/commands/foreigncmds.c Thu Mar 18 09:43:03 2010 +--- sepgsql/src/backend/commands/foreigncmds.c Thu Mar 18 01:55:40 2010 +*************** +*** 27,32 **** +--- 27,33 ---- + #include "foreign/foreign.h" + #include "miscadmin.h" + #include "parser/parse_func.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +*************** AlterForeignDataWrapperOwner(const char +*** 234,239 **** +--- 235,243 ---- + + if (form->fdwowner != newOwnerId) + { ++ /* SELinux permission check */ ++ sepgsql_fdw_alter(fdwId, InvalidOid); ++ + form->fdwowner = newOwnerId; + + simple_heap_update(rel, &tup->t_self, tup); +*************** AlterForeignServerOwner(const char *name +*** 298,303 **** +--- 302,309 ---- + aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); + } + } ++ /* SELinux permission checks */ ++ sepgsql_foreign_server_alter(srvId); + + form->srvowner = newOwnerId; + +*************** CreateForeignDataWrapper(CreateFdwStmt * +*** 343,348 **** +--- 349,355 ---- + Oid fdwvalidator; + Datum fdwoptions; + Oid ownerId; ++ Oid secid; + + /* Must be super user */ + if (!superuser()) +*************** CreateForeignDataWrapper(CreateFdwStmt * +*** 381,386 **** +--- 388,396 ---- + else + fdwvalidator = InvalidOid; + ++ /* SELinux permission checks */ ++ secid = sepgsql_fdw_create(stmt->fdwname, fdwvalidator); ++ + values[Anum_pg_foreign_data_wrapper_fdwvalidator - 1] = fdwvalidator; + + nulls[Anum_pg_foreign_data_wrapper_fdwacl - 1] = true; +*************** CreateForeignDataWrapper(CreateFdwStmt * +*** 396,401 **** +--- 406,413 ---- + nulls[Anum_pg_foreign_data_wrapper_fdwoptions - 1] = true; + + tuple = heap_form_tuple(rel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, secid); + + fdwId = simple_heap_insert(rel, tuple); + CatalogUpdateIndexes(rel, tuple); +*************** AlterForeignDataWrapper(AlterFdwStmt *st +*** 490,495 **** +--- 502,510 ---- + fdwvalidator = DatumGetObjectId(datum); + } + ++ /* SELinux permission checks */ ++ sepgsql_fdw_alter(fdwId, fdwvalidator); ++ + /* + * Options specified, validate and update. + */ +*************** CreateForeignServer(CreateForeignServerS +*** 615,620 **** +--- 630,636 ---- + HeapTuple tuple; + Oid srvId; + Oid ownerId; ++ Oid secid; + AclResult aclresult; + ObjectAddress myself; + ObjectAddress referenced; +*************** CreateForeignServer(CreateForeignServerS +*** 642,647 **** +--- 658,665 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_FDW, fdw->fdwname); + ++ secid = sepgsql_foreign_server_create(stmt->fdwname); ++ + /* + * Insert tuple into pg_foreign_server. + */ +*************** CreateForeignServer(CreateForeignServerS +*** 684,689 **** +--- 702,709 ---- + nulls[Anum_pg_foreign_server_srvoptions - 1] = true; + + tuple = heap_form_tuple(rel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, secid); + + srvId = simple_heap_insert(rel, tuple); + +*************** AlterForeignServer(AlterForeignServerStm +*** 740,745 **** +--- 760,768 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_FOREIGN_SERVER, + stmt->servername); + ++ /* SELinux permission checks */ ++ sepgsql_foreign_server_alter(srvId); ++ + memset(repl_val, 0, sizeof(repl_val)); + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); +diff -Nrpc blob/src/backend/commands/functioncmds.c sepgsql/src/backend/commands/functioncmds.c +*** blob/src/backend/commands/functioncmds.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/commands/functioncmds.c Thu Sep 17 17:04:16 2009 +*************** +*** 53,58 **** +--- 53,59 ---- + #include "parser/parse_expr.h" + #include "parser/parse_func.h" + #include "parser/parse_type.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +*************** compute_attributes_sql_style(List *optio +*** 517,523 **** + bool *security_definer, + ArrayType **proconfig, + float4 *procost, +! float4 *prorows) + { + ListCell *option; + DefElem *as_item = NULL; +--- 518,525 ---- + bool *security_definer, + ArrayType **proconfig, + float4 *procost, +! float4 *prorows, +! Node **proseclabel) + { + ListCell *option; + DefElem *as_item = NULL; +*************** compute_attributes_sql_style(List *optio +*** 529,534 **** +--- 531,537 ---- + List *set_items = NIL; + DefElem *cost_item = NULL; + DefElem *rows_item = NULL; ++ DefElem *seclabel_item = NULL; + + foreach(option, options) + { +*************** compute_attributes_sql_style(List *optio +*** 558,563 **** +--- 561,574 ---- + errmsg("conflicting or redundant options"))); + windowfunc_item = defel; + } ++ else if (strcmp(defel->defname, "security_context") == 0) ++ { ++ if (seclabel_item) ++ ereport(ERROR, ++ (errcode(ERRCODE_SYNTAX_ERROR), ++ errmsg("conflicting or redundant options"))); ++ seclabel_item = defel; ++ } + else if (compute_common_attribute(defel, + &volatility_item, + &strict_item, +*************** compute_attributes_sql_style(List *optio +*** 622,627 **** +--- 633,640 ---- + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("ROWS must be positive"))); + } ++ if (seclabel_item) ++ *proseclabel = (Node *)seclabel_item; + } + + +*************** CreateFunction(CreateFunctionStmt *stmt, +*** 762,767 **** +--- 775,781 ---- + ArrayType *proconfig; + float4 procost; + float4 prorows; ++ Node *proseclabel; + HeapTuple languageTuple; + Form_pg_language languageStruct; + List *as_clause; +*************** CreateFunction(CreateFunctionStmt *stmt, +*** 784,796 **** + proconfig = NULL; + procost = -1; /* indicates not set */ + prorows = -1; /* indicates not set */ + + /* override attributes from explicit list */ + compute_attributes_sql_style(stmt->options, + &as_clause, &language, + &isWindowFunc, &volatility, + &isStrict, &security, +! &proconfig, &procost, &prorows); + + /* Convert language name to canonical case */ + languageName = case_translate_language_name(language); +--- 798,811 ---- + proconfig = NULL; + procost = -1; /* indicates not set */ + prorows = -1; /* indicates not set */ ++ proseclabel = NULL; + + /* override attributes from explicit list */ + compute_attributes_sql_style(stmt->options, + &as_clause, &language, + &isWindowFunc, &volatility, + &isStrict, &security, +! &proconfig, &procost, &prorows, &proseclabel); + + /* Convert language name to canonical case */ + languageName = case_translate_language_name(language); +*************** CreateFunction(CreateFunctionStmt *stmt, +*** 926,932 **** + parameterDefaults, + PointerGetDatum(proconfig), + procost, +! prorows); + } + + +--- 941,948 ---- + parameterDefaults, + PointerGetDatum(proconfig), + procost, +! prorows, +! proseclabel); + } + + +*************** RenameFunction(List *name, List *argtype +*** 1112,1117 **** +--- 1128,1136 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux permission checks */ ++ sepgsql_proc_alter(procOid, newname, InvalidOid); ++ + /* rename */ + namestrcpy(&(procForm->proname), newname); + simple_heap_update(rel, &tup->t_self, tup); +*************** AlterFunctionOwner_internal(Relation rel +*** 1220,1225 **** +--- 1239,1246 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(procForm->pronamespace)); + } ++ /* SELinux permission checks */ ++ sepgsql_proc_alter(procOid, NULL, InvalidOid); + + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); +*************** AlterFunctionOwner_internal(Relation rel +*** 1258,1263 **** +--- 1279,1337 ---- + } + + /* ++ * ALTER FUNCTION name(args,...) SECURITY_LABEL [=] newlabel ++ */ ++ void ++ AlterFunctionSecLabel(List *name, List *argtypes, DefElem *seclabel) ++ { ++ Relation rel; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ Oid procOid; ++ Oid secid; ++ bool replaces[Natts_pg_proc]; ++ ++ /* open pg_proc system catalog */ ++ rel = heap_open(ProcedureRelationId, RowExclusiveLock); ++ ++ procOid = LookupFuncNameTypeNames(name, argtypes, false); ++ ++ oldtup = SearchSysCache(PROCOID, ++ ObjectIdGetDatum(procOid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(oldtup)) ++ elog(ERROR, "cache lookup failed for function %u", procOid); ++ ++ memset(replaces, false, sizeof(replaces)); ++ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), ++ NULL, NULL, replaces); ++ ++ if (!HeapTupleHasSecid(newtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to set security label on \"%s\"", ++ get_func_name(procOid)))); ++ ++ ReleaseSysCache(oldtup); ++ ++ /* DAC permission checks */ ++ if (!pg_proc_ownercheck(HeapTupleGetOid(newtup), GetUserId())) ++ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, ++ get_func_name(HeapTupleGetOid(newtup))); ++ ++ /* SELinux permission checks */ ++ secid = sepgsql_proc_relabel(procOid, seclabel); ++ HeapTupleSetSecid(newtup, secid); ++ ++ simple_heap_update(rel, &newtup->t_self, newtup); ++ CatalogUpdateIndexes(rel, newtup); ++ ++ heap_freetuple(newtup); ++ ++ heap_close(rel, RowExclusiveLock); ++ } ++ ++ /* + * Implements the ALTER FUNCTION utility command (except for the + * RENAME and OWNER clauses, which are handled as part of the generic + * ALTER framework). +*************** AlterFunction(AlterFunctionStmt *stmt) +*** 1296,1301 **** +--- 1370,1378 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + NameListToString(stmt->func->funcname)); + ++ /* SELinux checks permissions */ ++ sepgsql_proc_alter(funcOid, NULL, InvalidOid); ++ + if (procForm->proisagg) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), +*************** CreateCast(CreateCastStmt *stmt) +*** 1473,1478 **** +--- 1550,1556 ---- + char sourcetyptype; + char targettyptype; + Oid funcid; ++ Oid secid; + int nargs; + char castcontext; + char castmethod; +*************** CreateCast(CreateCastStmt *stmt) +*** 1674,1679 **** +--- 1752,1759 ---- + castcontext = 0; /* keep compiler quiet */ + break; + } ++ /* SELinux permission check */ ++ secid = sepgsql_cast_create(sourcetypeid, targettypeid, funcid); + + relation = heap_open(CastRelationId, RowExclusiveLock); + +*************** CreateCast(CreateCastStmt *stmt) +*** 1704,1709 **** +--- 1784,1792 ---- + + tuple = heap_form_tuple(RelationGetDescr(relation), values, nulls); + ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, secid); ++ + simple_heap_insert(relation, tuple); + + CatalogUpdateIndexes(relation, tuple); +*************** AlterFunctionNamespace(List *name, List +*** 1897,1902 **** +--- 1980,1988 ---- + NameStr(proc->proname), + newschema))); + ++ /* SELinux checks permissions */ ++ sepgsql_proc_alter(procOid, NULL, nspOid); ++ + /* OK, modify the pg_proc row */ + + /* tup is a copy, so we can scribble directly on it */ +diff -Nrpc blob/src/backend/commands/indexcmds.c sepgsql/src/backend/commands/indexcmds.c +*** blob/src/backend/commands/indexcmds.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/commands/indexcmds.c Sun Dec 20 00:41:22 2009 +*************** +*** 37,42 **** +--- 37,43 ---- + #include "parser/parse_coerce.h" + #include "parser/parse_func.h" + #include "parser/parsetree.h" ++ #include "security/sepgsql.h" + #include "storage/lmgr.h" + #include "storage/proc.h" + #include "storage/procarray.h" +*************** DefineIndex(RangeVar *heapRelation, +*** 197,202 **** +--- 198,206 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceId)); ++ ++ /* SELinux checks */ ++ sepgsql_index_create(relationId, namespaceId); + } + + /* +diff -Nrpc blob/src/backend/commands/lockcmds.c sepgsql/src/backend/commands/lockcmds.c +*** blob/src/backend/commands/lockcmds.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/commands/lockcmds.c Fri Sep 18 14:51:00 2009 +*************** +*** 20,25 **** +--- 20,26 ---- + #include "commands/lockcmds.h" + #include "miscadmin.h" + #include "parser/parse_clause.h" ++ #include "security/sepgsql.h" + #include "storage/lmgr.h" + #include "utils/acl.h" + #include "utils/lsyscache.h" +*************** LockTableRecurse(Oid reloid, RangeVar *r +*** 140,145 **** +--- 141,149 ---- + errmsg("\"%s\" is not a table", + RelationGetRelationName(rel)))); + ++ /* SELinux: check db_table:{lock} permission */ ++ sepgsql_relation_lock(reloid); ++ + /* + * If requested, recurse to children. We use find_inheritance_children + * not find_all_inheritors to avoid taking locks far in advance of +diff -Nrpc blob/src/backend/commands/opclasscmds.c sepgsql/src/backend/commands/opclasscmds.c +*** blob/src/backend/commands/opclasscmds.c Sat Jan 3 13:01:35 2009 +--- sepgsql/src/backend/commands/opclasscmds.c Thu Sep 17 17:04:16 2009 +*************** +*** 35,40 **** +--- 35,41 ---- + #include "parser/parse_func.h" + #include "parser/parse_oper.h" + #include "parser/parse_type.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +*************** CreateOpFamily(char *amname, char *opfna +*** 177,182 **** +--- 178,184 ---- + HeapTuple tup; + Datum values[Natts_pg_opfamily]; + bool nulls[Natts_pg_opfamily]; ++ Oid opfSecid; + NameData opfName; + ObjectAddress myself, + referenced; +*************** CreateOpFamily(char *amname, char *opfna +*** 197,202 **** +--- 199,207 ---- + errmsg("operator family \"%s\" for access method \"%s\" already exists", + opfname, amname))); + ++ /* SELinux check permission */ ++ opfSecid = sepgsql_opfamily_create(opfname, namespaceoid); ++ + /* + * Okay, let's create the pg_opfamily entry. + */ +*************** CreateOpFamily(char *amname, char *opfna +*** 210,215 **** +--- 215,222 ---- + values[Anum_pg_opfamily_opfowner - 1] = ObjectIdGetDatum(GetUserId()); + + tup = heap_form_tuple(rel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, opfSecid); + + opfamilyoid = simple_heap_insert(rel, tup); + +*************** DefineOpClass(CreateOpClassStmt *stmt) +*** 265,270 **** +--- 272,278 ---- + Form_pg_am pg_am; + Datum values[Natts_pg_opclass]; + bool nulls[Natts_pg_opclass]; ++ Oid opcSecid; + AclResult aclresult; + NameData opcName; + ObjectAddress myself, +*************** DefineOpClass(CreateOpClassStmt *stmt) +*** 353,358 **** +--- 361,369 ---- + NameListToString(stmt->opfamilyname), stmt->amname))); + opfamilyoid = HeapTupleGetOid(tup); + ++ /* SELinux checks permission */ ++ sepgsql_opfamily_alter(opfamilyoid, NULL); ++ + /* + * XXX given the superuser check above, there's no need for an + * ownership check here +*************** DefineOpClass(CreateOpClassStmt *stmt) +*** 371,376 **** +--- 382,390 ---- + { + opfamilyoid = HeapTupleGetOid(tup); + ++ /* SELinux checks permission */ ++ sepgsql_opfamily_alter(opfamilyoid, NULL); ++ + /* + * XXX given the superuser check above, there's no need for an + * ownership check here +*************** DefineOpClass(CreateOpClassStmt *stmt) +*** 441,446 **** +--- 455,462 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + get_func_name(funcOid)); + #endif ++ /* SELinux check permission */ ++ sepgsql_opfamily_add_operator(opfamilyoid, operOid); + + /* Save the info */ + member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); +*************** DefineOpClass(CreateOpClassStmt *stmt) +*** 465,470 **** +--- 481,488 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + get_func_name(funcOid)); + #endif ++ /* SELinux check permission */ ++ sepgsql_opfamily_add_procedure(opfamilyoid, funcOid); + + /* Save the info */ + member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); +*************** DefineOpClass(CreateOpClassStmt *stmt) +*** 531,536 **** +--- 549,557 ---- + errmsg("operator class \"%s\" for access method \"%s\" already exists", + opcname, stmt->amname))); + ++ /* SELinux permission check */ ++ opcSecid = sepgsql_opclass_create(opcname, namespaceoid); ++ + /* + * If we are creating a default opclass, check there isn't one already. + * (Note we do not restrict this test to visible opclasses; this ensures +*************** DefineOpFamily(CreateOpFamilyStmt *stmt) +*** 657,662 **** +--- 678,684 ---- + HeapTuple tup; + Datum values[Natts_pg_opfamily]; + bool nulls[Natts_pg_opfamily]; ++ Oid opfSecid; + AclResult aclresult; + NameData opfName; + ObjectAddress myself, +*************** DefineOpFamily(CreateOpFamilyStmt *stmt) +*** 699,704 **** +--- 721,729 ---- + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to create an operator family"))); + ++ /* SELinux permission check */ ++ opfSecid = sepgsql_opfamily_create(opfname, namespaceoid); ++ + rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock); + + /* +*************** AlterOpFamily(AlterOpFamilyStmt *stmt) +*** 773,778 **** +--- 798,804 ---- + int maxOpNumber, /* amstrategies value */ + maxProcNumber; /* amsupport value */ + HeapTuple tup; ++ Oid opfSecid; + Form_pg_am pg_am; + + /* Get necessary info about access method */ +*************** AlterOpFamily(AlterOpFamilyStmt *stmt) +*** 805,810 **** +--- 831,837 ---- + errmsg("operator family \"%s\" does not exist for access method \"%s\"", + NameListToString(stmt->opfamilyname), stmt->amname))); + opfamilyoid = HeapTupleGetOid(tup); ++ opfSecid = HeapTupleGetSecid(tup); + ReleaseSysCache(tup); + + /* +*************** AlterOpFamily(AlterOpFamilyStmt *stmt) +*** 817,822 **** +--- 844,852 ---- + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to alter an operator family"))); + ++ /* SELinux permission checks */ ++ sepgsql_opfamily_alter(opfamilyoid, NULL); ++ + /* + * ADD and DROP cases need separate code from here on down. + */ +*************** AlterOpFamilyAdd(List *opfamilyname, Oid +*** 893,898 **** +--- 923,930 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + get_func_name(funcOid)); + #endif ++ /* SELinux permission check */ ++ sepgsql_opfamily_add_operator(opfamilyoid, operOid); + + /* Save the info */ + member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); +*************** AlterOpFamilyAdd(List *opfamilyname, Oid +*** 917,922 **** +--- 949,956 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + get_func_name(funcOid)); + #endif ++ /* SELinux permission check */ ++ sepgsql_opfamily_add_procedure(opfamilyoid, funcOid); + + /* Save the info */ + member = (OpFamilyMember *) palloc0(sizeof(OpFamilyMember)); +*************** RenameOpClass(List *name, const char *ac +*** 1815,1820 **** +--- 1849,1857 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux permission checks */ ++ sepgsql_opclass_alter(opcOid, newname); ++ + /* rename */ + namestrcpy(&(((Form_pg_opclass) GETSTRUCT(tup))->opcname), newname); + simple_heap_update(rel, &tup->t_self, tup); +*************** RenameOpFamily(List *name, const char *a +*** 1915,1920 **** +--- 1952,1960 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux check permissions */ ++ sepgsql_opfamily_alter(opfOid, newname); ++ + /* rename */ + namestrcpy(&(((Form_pg_opfamily) GETSTRUCT(tup))->opfname), newname); + simple_heap_update(rel, &tup->t_self, tup); +*************** AlterOpClassOwner_internal(Relation rel, +*** 2035,2040 **** +--- 2075,2082 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux permission check */ ++ sepgsql_opclass_alter(HeapTupleGetOid(tup), NULL); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy +*************** AlterOpFamilyOwner_internal(Relation rel +*** 2162,2167 **** +--- 2204,2211 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux permission checks */ ++ sepgsql_opfamily_alter(HeapTupleGetOid(tup), NULL); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy +diff -Nrpc blob/src/backend/commands/operatorcmds.c sepgsql/src/backend/commands/operatorcmds.c +*** blob/src/backend/commands/operatorcmds.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/commands/operatorcmds.c Thu Sep 17 22:10:19 2009 +*************** +*** 45,50 **** +--- 45,51 ---- + #include "parser/parse_func.h" + #include "parser/parse_oper.h" + #include "parser/parse_type.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/lsyscache.h" + #include "utils/rel.h" +*************** AlterOperatorOwner_internal(Relation rel +*** 432,437 **** +--- 433,440 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(oprForm->oprnamespace)); + } ++ /* SELinux permission check */ ++ sepgsql_operator_alter(operOid); + + /* + * Modify the owner --- okay to scribble on tup because it's a copy +diff -Nrpc blob/src/backend/commands/proclang.c sepgsql/src/backend/commands/proclang.c +*** blob/src/backend/commands/proclang.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/commands/proclang.c Thu Sep 17 22:10:19 2009 +*************** +*** 30,35 **** +--- 30,36 ---- + #include "miscadmin.h" + #include "parser/gramparse.h" + #include "parser/parse_func.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +*************** CreateProceduralLanguage(CreatePLangStmt +*** 151,157 **** + NIL, + PointerGetDatum(NULL), + 1, +! 0); + } + + /* +--- 152,159 ---- + NIL, + PointerGetDatum(NULL), + 1, +! 0, +! NULL); + } + + /* +*************** CreateProceduralLanguage(CreatePLangStmt +*** 186,192 **** + NIL, + PointerGetDatum(NULL), + 1, +! 0); + } + } + else +--- 188,195 ---- + NIL, + PointerGetDatum(NULL), + 1, +! 0, +! NULL); + } + } + else +*************** create_proc_lang(const char *languageNam +*** 275,284 **** +--- 278,293 ---- + bool nulls[Natts_pg_language]; + NameData langname; + HeapTuple tup; ++ Oid langSecid; + ObjectAddress myself, + referenced; + + /* ++ * SELinux permission checks ++ */ ++ langSecid = sepgsql_language_create(languageName, handlerOid, valOid); ++ ++ /* + * Insert the new language into pg_language + */ + rel = heap_open(LanguageRelationId, RowExclusiveLock); +*************** create_proc_lang(const char *languageNam +*** 297,302 **** +--- 306,313 ---- + nulls[Anum_pg_language_lanacl - 1] = true; + + tup = heap_form_tuple(tupDesc, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, langSecid); + + simple_heap_insert(rel, tup); + +*************** RenameLanguage(const char *oldname, cons +*** 518,523 **** +--- 529,537 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_LANGUAGE, + oldname); + ++ /* SELinux permission checks */ ++ sepgsql_language_alter(HeapTupleGetOid(tup)); ++ + /* rename */ + namestrcpy(&(((Form_pg_language) GETSTRUCT(tup))->lanname), newname); + simple_heap_update(rel, &tup->t_self, tup); +*************** AlterLanguageOwner_internal(HeapTuple tu +*** 613,618 **** +--- 627,635 ---- + /* Must be able to become new owner */ + check_is_member_of_role(GetUserId(), newOwnerId); + ++ /* SELinux permission checks */ ++ sepgsql_language_alter(HeapTupleGetOid(tup)); ++ + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); + +diff -Nrpc blob/src/backend/commands/schemacmds.c sepgsql/src/backend/commands/schemacmds.c +*** blob/src/backend/commands/schemacmds.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/commands/schemacmds.c Tue Dec 15 17:30:25 2009 +*************** +*** 25,30 **** +--- 25,31 ---- + #include "commands/schemacmds.h" + #include "miscadmin.h" + #include "parser/parse_utilcmd.h" ++ #include "security/sepgsql.h" + #include "tcop/utility.h" + #include "utils/acl.h" + #include "utils/builtins.h" +*************** CreateSchemaCommand(CreateSchemaStmt *st +*** 48,53 **** +--- 49,55 ---- + ListCell *parsetree_item; + Oid owner_uid; + Oid saved_uid; ++ Oid nspsecid; + int save_sec_context; + AclResult aclresult; + +*************** CreateSchemaCommand(CreateSchemaStmt *st +*** 75,80 **** +--- 77,86 ---- + + check_is_member_of_role(saved_uid, owner_uid); + ++ /* SELinux checks db_schema:{create} */ ++ nspsecid = sepgsql_schema_create(schemaName, false, ++ (DefElem *)stmt->secLabel); ++ + /* Additional check to protect reserved schema names */ + if (!allowSystemTableMods && IsReservedName(schemaName)) + ereport(ERROR, +*************** CreateSchemaCommand(CreateSchemaStmt *st +*** 95,101 **** + save_sec_context | SECURITY_LOCAL_USERID_CHANGE); + + /* Create the schema's namespace */ +! namespaceId = NamespaceCreate(schemaName, owner_uid); + + /* Advance cmd counter to make the namespace visible */ + CommandCounterIncrement(); +--- 101,107 ---- + save_sec_context | SECURITY_LOCAL_USERID_CHANGE); + + /* Create the schema's namespace */ +! namespaceId = NamespaceCreate(schemaName, owner_uid, nspsecid); + + /* Advance cmd counter to make the namespace visible */ + CommandCounterIncrement(); +*************** RenameSchema(const char *oldname, const +*** 268,275 **** + errmsg("schema \"%s\" does not exist", oldname))); + + /* make sure the new name doesn't exist */ +! if (HeapTupleIsValid( +! SearchSysCache(NAMESPACENAME, + CStringGetDatum(newname), + 0, 0, 0))) + ereport(ERROR, +--- 274,280 ---- + errmsg("schema \"%s\" does not exist", oldname))); + + /* make sure the new name doesn't exist */ +! if (HeapTupleIsValid(SearchSysCache(NAMESPACENAME, + CStringGetDatum(newname), + 0, 0, 0))) + ereport(ERROR, +*************** RenameSchema(const char *oldname, const +*** 287,292 **** +--- 292,300 ---- + aclcheck_error(aclresult, ACL_KIND_DATABASE, + get_database_name(MyDatabaseId)); + ++ /* SELinux checks db_schema:{setattr} */ ++ sepgsql_schema_alter(HeapTupleGetOid(tup)); ++ + if (!allowSystemTableMods && IsReservedName(newname)) + ereport(ERROR, + (errcode(ERRCODE_RESERVED_NAME), +*************** AlterSchemaOwner_internal(HeapTuple tup, +*** 398,403 **** +--- 406,414 ---- + aclcheck_error(aclresult, ACL_KIND_DATABASE, + get_database_name(MyDatabaseId)); + ++ /* SELinux checks db_schema:{setattr} */ ++ sepgsql_schema_alter(HeapTupleGetOid(tup)); ++ + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); + +*************** AlterSchemaOwner_internal(HeapTuple tup, +*** 432,434 **** +--- 443,493 ---- + } + + } ++ ++ /* ++ * ALTER SCHEMA name SECURITY_LABEL [=] newlabel ++ */ ++ void ++ AlterSchemaSecLabel(const char *name, DefElem *secLabel) ++ { ++ Relation rel; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ Oid secid; ++ bool replaces[Natts_pg_namespace]; ++ ++ /* open pg_namespace relation */ ++ rel = heap_open(NamespaceRelationId, RowExclusiveLock); ++ oldtup = SearchSysCache(NAMESPACENAME, ++ CStringGetDatum(name), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(oldtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_SCHEMA), ++ errmsg("schema \"%s\" does not exist", name))); ++ ++ memset(replaces, false, sizeof(replaces)); ++ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), ++ NULL, NULL, replaces); ++ if (!HeapTupleHasSecid(newtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to set security label on \"%s\"", name))); ++ ++ ReleaseSysCache(oldtup); ++ ++ /* DAC permission check */ ++ if (!pg_namespace_ownercheck(HeapTupleGetOid(newtup), GetUserId())) ++ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, name); ++ /* SELinux checks db_schema:{setattr relabelfrom relabelto} */ ++ secid = sepgsql_schema_relabel(HeapTupleGetOid(newtup), secLabel); ++ HeapTupleSetSecid(newtup, secid); ++ ++ simple_heap_update(rel, &newtup->t_self, newtup); ++ ++ CatalogUpdateIndexes(rel, newtup); ++ ++ heap_freetuple(newtup); ++ ++ heap_close(rel, RowExclusiveLock); ++ } +diff -Nrpc blob/src/backend/commands/sequence.c sepgsql/src/backend/commands/sequence.c +*** blob/src/backend/commands/sequence.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/commands/sequence.c Fri Sep 18 14:51:00 2009 +*************** +*** 26,31 **** +--- 26,32 ---- + #include "commands/tablecmds.h" + #include "miscadmin.h" + #include "nodes/makefuncs.h" ++ #include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/lmgr.h" + #include "storage/proc.h" +*************** DefineSequence(CreateSeqStmt *seq) +*** 201,206 **** +--- 202,208 ---- + stmt->options = list_make1(defWithOids(false)); + stmt->oncommit = ONCOMMIT_NOOP; + stmt->tablespacename = NULL; ++ stmt->secLabel = seq->secLabel; + + seqoid = DefineRelation(stmt, RELKIND_SEQUENCE); + +*************** AlterSequence(AlterSeqStmt *stmt) +*** 328,333 **** +--- 330,337 ---- + if (!pg_class_ownercheck(relid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + stmt->sequence->relname); ++ /* SELinux checks db_sequence:{setattr} */ ++ sepgsql_relation_alter(relid, NULL, InvalidOid); + + /* do the work */ + AlterSequenceInternal(relid, stmt->options); +*************** nextval_internal(Oid relid) +*** 467,472 **** +--- 471,479 ---- + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + ++ /* SELinux check db_sequence:{next_value} */ ++ sepgsql_sequence_next_value(elm->relid); ++ + if (elm->last != elm->cached) /* some numbers were cached */ + { + Assert(elm->last_valid); +*************** currval_oid(PG_FUNCTION_ARGS) +*** 662,667 **** +--- 669,677 ---- + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + ++ /* SELinux check db_sequence:{get_value} */ ++ sepgsql_sequence_get_value(elm->relid); ++ + if (!elm->last_valid) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), +*************** lastval(PG_FUNCTION_ARGS) +*** 706,711 **** +--- 716,724 ---- + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + ++ /* SELinux check db_sequence:{get_value} */ ++ sepgsql_sequence_get_value(last_used_seq->relid); ++ + result = last_used_seq->last; + relation_close(seqrel, NoLock); + +*************** do_setval(Oid relid, int64 next, bool is +*** 742,747 **** +--- 755,763 ---- + errmsg("permission denied for sequence %s", + RelationGetRelationName(seqrel)))); + ++ /* SELinux check db_sequence:{set_value} */ ++ sepgsql_sequence_set_value(elm->relid); ++ + /* lock page' buffer and read tuple */ + seq = read_info(elm, seqrel, &buf); + +diff -Nrpc blob/src/backend/commands/tablecmds.c sepgsql/src/backend/commands/tablecmds.c +*** blob/src/backend/commands/tablecmds.c Fri Dec 18 09:40:55 2009 +--- sepgsql/src/backend/commands/tablecmds.c Sun Dec 20 00:41:22 2009 +*************** +*** 62,67 **** +--- 62,68 ---- + #include "parser/parser.h" + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteHandler.h" ++ #include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/lmgr.h" + #include "storage/smgr.h" +*************** static void ATExecCmd(List **wqueue, Alt +*** 260,267 **** + static void ATRewriteTables(List **wqueue); + static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap); + static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel); +! static void ATSimplePermissions(Relation rel, bool allowView); +! static void ATSimplePermissionsRelationOrIndex(Relation rel); + static void ATSimpleRecursion(List **wqueue, Relation rel, + AlterTableCmd *cmd, bool recurse); + static void ATOneLevelRecursion(List **wqueue, Relation rel, +--- 261,268 ---- + static void ATRewriteTables(List **wqueue); + static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap); + static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel); +! static void ATSimplePermissions(Relation rel, const char *colname, bool allowView); +! static void ATSimplePermissionsRelationOrIndex(Relation rel, const char *colname); + static void ATSimpleRecursion(List **wqueue, Relation rel, + AlterTableCmd *cmd, bool recurse); + static void ATOneLevelRecursion(List **wqueue, Relation rel, +*************** DefineRelation(CreateStmt *stmt, char re +*** 351,356 **** +--- 352,358 ---- + List *rawDefaults; + List *cookedDefaults; + Datum reloptions; ++ Oid *secLabels; + ListCell *listptr; + AttrNumber attnum; + static char *validnsps[] = HEAP_RELOPT_NAMESPACES; +*************** DefineRelation(CreateStmt *stmt, char re +*** 454,459 **** +--- 456,471 ---- + localHasOids = interpretOidsOption(stmt->options); + descriptor->tdhasoid = (localHasOids || parentOidCount > 0); + ++ /* SELinux checks db_table:{create} and db_column:{create} */ ++ secLabels = sepgsql_relation_create(relname, ++ relkind, ++ descriptor, ++ namespaceId, ++ (DefElem *)stmt->secLabel, ++ schema, ++ false, ++ true); ++ + /* + * Find columns with default values and prepare for insertion of the + * defaults. Pre-cooked (that is, inherited) defaults go into a list of +*************** DefineRelation(CreateStmt *stmt, char re +*** 523,529 **** + parentOidCount, + stmt->oncommit, + reloptions, +! allowSystemTableMods); + + StoreCatalogInheritance(relationId, inheritOids); + +--- 535,542 ---- + parentOidCount, + stmt->oncommit, + reloptions, +! allowSystemTableMods, +! secLabels); + + StoreCatalogInheritance(relationId, inheritOids); + +*************** ExecuteTruncate(TruncateStmt *stmt) +*** 897,902 **** +--- 910,917 ---- + if (!pg_class_ownercheck(seq_relid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(seq_rel)); ++ /* SELinux checks */ ++ sepgsql_relation_alter(seq_relid, NULL, InvalidOid); + + seq_relids = lappend_oid(seq_relids, seq_relid); + +*************** truncate_check_rel(Relation rel) +*** 1052,1057 **** +--- 1067,1075 ---- + errmsg("permission denied: \"%s\" is a system catalog", + RelationGetRelationName(rel)))); + ++ /* SELinux: check db_table:{delete} permission */ ++ sepgsql_relation_truncate(rel); ++ + /* + * We can never allow truncation of shared or nailed-in-cache relations, + * because we can't support changing their relfilenode values. +*************** MergeAttributes(List *schema, List *supe +*** 1226,1231 **** +--- 1244,1251 ---- + if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(relation)); ++ /* SELinux checks db_table:{setattr} */ ++ sepgsql_relation_alter(RelationGetRelid(relation), NULL, InvalidOid); + + /* + * Reject duplications in the list of parents. +*************** renameatt(Oid myrelid, +*** 1931,1936 **** +--- 1951,1959 ---- + errmsg("cannot rename system column \"%s\"", + oldattname))); + ++ /* SELinux checks db_column:{setattr} */ ++ sepgsql_attribute_alter(myrelid, oldattname); ++ + /* + * if the attribute is inherited, forbid the renaming, unless we are + * already inside a recursive rename. +*************** RenameRelation(Oid myrelid, const char * +*** 2036,2041 **** +--- 2059,2067 ---- + Oid namespaceId; + char relkind; + ++ /* SELinux checks */ ++ sepgsql_relation_alter(myrelid, newrelname, InvalidOid); ++ + /* + * Grab an exclusive lock on the target table, index, sequence or view, + * which we will NOT release until end of transaction. +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2369,2382 **** + switch (cmd->subtype) + { + case AT_AddColumn: /* ADD COLUMN */ +! ATSimplePermissions(rel, false); + /* Performs own recursion */ + ATPrepAddColumn(wqueue, rel, recurse, cmd); + pass = AT_PASS_ADD_COL; + break; + case AT_AddColumnToView: /* add column via CREATE OR REPLACE + * VIEW */ +! ATSimplePermissions(rel, true); + /* Performs own recursion */ + ATPrepAddColumn(wqueue, rel, recurse, cmd); + pass = AT_PASS_ADD_COL; +--- 2395,2408 ---- + switch (cmd->subtype) + { + case AT_AddColumn: /* ADD COLUMN */ +! ATSimplePermissions(rel, NULL, false); + /* Performs own recursion */ + ATPrepAddColumn(wqueue, rel, recurse, cmd); + pass = AT_PASS_ADD_COL; + break; + case AT_AddColumnToView: /* add column via CREATE OR REPLACE + * VIEW */ +! ATSimplePermissions(rel, NULL, true); + /* Performs own recursion */ + ATPrepAddColumn(wqueue, rel, recurse, cmd); + pass = AT_PASS_ADD_COL; +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2389,2407 **** + * substitutes default values into INSERTs before it expands + * rules. + */ +! ATSimplePermissions(rel, true); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP; + break; + case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ +! ATSimplePermissions(rel, false); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = AT_PASS_DROP; + break; + case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */ +! ATSimplePermissions(rel, false); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = AT_PASS_ADD_CONSTR; +--- 2415,2433 ---- + * substitutes default values into INSERTs before it expands + * rules. + */ +! ATSimplePermissions(rel, cmd->name, true); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = cmd->def ? AT_PASS_ADD_CONSTR : AT_PASS_DROP; + break; + case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ +! ATSimplePermissions(rel, cmd->name, false); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = AT_PASS_DROP; + break; + case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */ +! ATSimplePermissions(rel, cmd->name, false); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = AT_PASS_ADD_CONSTR; +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2413,2425 **** + pass = AT_PASS_COL_ATTRS; + break; + case AT_SetStorage: /* ALTER COLUMN STORAGE */ +! ATSimplePermissions(rel, false); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = AT_PASS_COL_ATTRS; + break; + case AT_DropColumn: /* DROP COLUMN */ +! ATSimplePermissions(rel, false); + /* Recursion occurs during execution phase */ + /* No command-specific prep needed except saving recurse flag */ + if (recurse) +--- 2439,2451 ---- + pass = AT_PASS_COL_ATTRS; + break; + case AT_SetStorage: /* ALTER COLUMN STORAGE */ +! ATSimplePermissions(rel, cmd->name, false); + ATSimpleRecursion(wqueue, rel, cmd, recurse); + /* No command-specific prep needed */ + pass = AT_PASS_COL_ATTRS; + break; + case AT_DropColumn: /* DROP COLUMN */ +! ATSimplePermissions(rel, NULL, false); + /* Recursion occurs during execution phase */ + /* No command-specific prep needed except saving recurse flag */ + if (recurse) +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2427,2439 **** + pass = AT_PASS_DROP; + break; + case AT_AddIndex: /* ADD INDEX */ +! ATSimplePermissions(rel, false); + /* This command never recurses */ + /* No command-specific prep needed */ + pass = AT_PASS_ADD_INDEX; + break; + case AT_AddConstraint: /* ADD CONSTRAINT */ +! ATSimplePermissions(rel, false); + /* Recursion occurs during execution phase */ + /* No command-specific prep needed except saving recurse flag */ + if (recurse) +--- 2453,2465 ---- + pass = AT_PASS_DROP; + break; + case AT_AddIndex: /* ADD INDEX */ +! ATSimplePermissions(rel, NULL, false); + /* This command never recurses */ + /* No command-specific prep needed */ + pass = AT_PASS_ADD_INDEX; + break; + case AT_AddConstraint: /* ADD CONSTRAINT */ +! ATSimplePermissions(rel, NULL, false); + /* Recursion occurs during execution phase */ + /* No command-specific prep needed except saving recurse flag */ + if (recurse) +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2441,2447 **** + pass = AT_PASS_ADD_CONSTR; + break; + case AT_DropConstraint: /* DROP CONSTRAINT */ +! ATSimplePermissions(rel, false); + /* Recursion occurs during execution phase */ + /* No command-specific prep needed except saving recurse flag */ + if (recurse) +--- 2467,2473 ---- + pass = AT_PASS_ADD_CONSTR; + break; + case AT_DropConstraint: /* DROP CONSTRAINT */ +! ATSimplePermissions(rel, NULL, false); + /* Recursion occurs during execution phase */ + /* No command-specific prep needed except saving recurse flag */ + if (recurse) +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2449,2455 **** + pass = AT_PASS_DROP; + break; + case AT_AlterColumnType: /* ALTER COLUMN TYPE */ +! ATSimplePermissions(rel, false); + /* Performs own recursion */ + ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd); + pass = AT_PASS_ALTER_TYPE; +--- 2475,2481 ---- + pass = AT_PASS_DROP; + break; + case AT_AlterColumnType: /* ALTER COLUMN TYPE */ +! ATSimplePermissions(rel, cmd->name, false); + /* Performs own recursion */ + ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd); + pass = AT_PASS_ALTER_TYPE; +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2461,2480 **** + break; + case AT_ClusterOn: /* CLUSTER ON */ + case AT_DropCluster: /* SET WITHOUT CLUSTER */ +! ATSimplePermissions(rel, false); + /* These commands never recurse */ + /* No command-specific prep needed */ + pass = AT_PASS_MISC; + break; + case AT_AddOids: /* SET WITH OIDS */ +! ATSimplePermissions(rel, false); + /* Performs own recursion */ + if (!rel->rd_rel->relhasoids || recursing) + ATPrepAddOids(wqueue, rel, recurse, cmd); + pass = AT_PASS_ADD_COL; + break; + case AT_DropOids: /* SET WITHOUT OIDS */ +! ATSimplePermissions(rel, false); + /* Performs own recursion */ + if (rel->rd_rel->relhasoids) + { +--- 2487,2506 ---- + break; + case AT_ClusterOn: /* CLUSTER ON */ + case AT_DropCluster: /* SET WITHOUT CLUSTER */ +! ATSimplePermissions(rel, NULL, false); + /* These commands never recurse */ + /* No command-specific prep needed */ + pass = AT_PASS_MISC; + break; + case AT_AddOids: /* SET WITH OIDS */ +! ATSimplePermissions(rel, NULL, false); + /* Performs own recursion */ + if (!rel->rd_rel->relhasoids || recursing) + ATPrepAddOids(wqueue, rel, recurse, cmd); + pass = AT_PASS_ADD_COL; + break; + case AT_DropOids: /* SET WITHOUT OIDS */ +! ATSimplePermissions(rel, NULL, false); + /* Performs own recursion */ + if (rel->rd_rel->relhasoids) + { +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2488,2501 **** + pass = AT_PASS_DROP; + break; + case AT_SetTableSpace: /* SET TABLESPACE */ +! ATSimplePermissionsRelationOrIndex(rel); + /* This command never recurses */ + ATPrepSetTableSpace(tab, rel, cmd->name); + pass = AT_PASS_MISC; /* doesn't actually matter */ + break; + case AT_SetRelOptions: /* SET (...) */ + case AT_ResetRelOptions: /* RESET (...) */ +! ATSimplePermissionsRelationOrIndex(rel); + /* This command never recurses */ + /* No command-specific prep needed */ + pass = AT_PASS_MISC; +--- 2514,2527 ---- + pass = AT_PASS_DROP; + break; + case AT_SetTableSpace: /* SET TABLESPACE */ +! ATSimplePermissionsRelationOrIndex(rel, NULL); + /* This command never recurses */ + ATPrepSetTableSpace(tab, rel, cmd->name); + pass = AT_PASS_MISC; /* doesn't actually matter */ + break; + case AT_SetRelOptions: /* SET (...) */ + case AT_ResetRelOptions: /* RESET (...) */ +! ATSimplePermissionsRelationOrIndex(rel, NULL); + /* This command never recurses */ + /* No command-specific prep needed */ + pass = AT_PASS_MISC; +*************** ATPrepCmd(List **wqueue, Relation rel, A +*** 2514,2520 **** + case AT_DisableRule: + case AT_AddInherit: /* INHERIT / NO INHERIT */ + case AT_DropInherit: +! ATSimplePermissions(rel, false); + /* These commands never recurse */ + /* No command-specific prep needed */ + pass = AT_PASS_MISC; +--- 2540,2546 ---- + case AT_DisableRule: + case AT_AddInherit: /* INHERIT / NO INHERIT */ + case AT_DropInherit: +! ATSimplePermissions(rel, NULL, false); + /* These commands never recurse */ + /* No command-specific prep needed */ + pass = AT_PASS_MISC; +*************** ATRewriteTables(List **wqueue) +*** 2860,2867 **** + /* + * The new relation is local to our transaction and we know + * nothing depends on it, so DROP_RESTRICT should be OK. + */ +! performDeletion(&object, DROP_RESTRICT); + /* performDeletion does CommandCounterIncrement at end */ + + /* +--- 2886,2894 ---- + /* + * The new relation is local to our transaction and we know + * nothing depends on it, so DROP_RESTRICT should be OK. ++ * SELinux does not apply any permission checks here. + */ +! performDeletionNoPerms(&object, DROP_RESTRICT); + /* performDeletion does CommandCounterIncrement at end */ + + /* +*************** ATRewriteTable(AlteredTableInfo *tab, Oi +*** 3086,3096 **** +--- 3113,3126 ---- + if (newrel) + { + Oid tupOid = InvalidOid; ++ Oid tupSecid = InvalidOid; + + /* Extract data from old tuple */ + heap_deform_tuple(tuple, oldTupDesc, values, isnull); + if (oldTupDesc->tdhasoid) + tupOid = HeapTupleGetOid(tuple); ++ if (HeapTupleHasSecid(tuple)) ++ tupSecid = HeapTupleGetSecid(tuple); + + /* Set dropped attributes to null in new tuple */ + foreach(lc, dropped_attrs) +*************** ATRewriteTable(AlteredTableInfo *tab, Oi +*** 3122,3127 **** +--- 3152,3160 ---- + /* Preserve OID, if any */ + if (newTupDesc->tdhasoid) + HeapTupleSetOid(tuple, tupOid); ++ /* Preserve SID, if any */ ++ if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, tupSecid); + } + + /* Now check any constraints on the possibly-changed tuple */ +*************** ATGetQueueEntry(List **wqueue, Relation +*** 3223,3229 **** + * - Ensure that it is not a system table + */ + static void +! ATSimplePermissions(Relation rel, bool allowView) + { + if (rel->rd_rel->relkind != RELKIND_RELATION) + { +--- 3256,3262 ---- + * - Ensure that it is not a system table + */ + static void +! ATSimplePermissions(Relation rel, const char *colName, bool allowView) + { + if (rel->rd_rel->relkind != RELKIND_RELATION) + { +*************** ATSimplePermissions(Relation rel, bool a +*** 3247,3252 **** +--- 3280,3291 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(rel)); + ++ /* SELinux checks */ ++ if (!colName) ++ sepgsql_relation_alter(RelationGetRelid(rel), NULL, InvalidOid); ++ else ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + if (!allowSystemTableMods && IsSystemRelation(rel)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), +*************** ATSimplePermissions(Relation rel, bool a +*** 3262,3268 **** + * - Ensure that it is not a system table + */ + static void +! ATSimplePermissionsRelationOrIndex(Relation rel) + { + if (rel->rd_rel->relkind != RELKIND_RELATION && + rel->rd_rel->relkind != RELKIND_INDEX) +--- 3301,3307 ---- + * - Ensure that it is not a system table + */ + static void +! ATSimplePermissionsRelationOrIndex(Relation rel, const char *colName) + { + if (rel->rd_rel->relkind != RELKIND_RELATION && + rel->rd_rel->relkind != RELKIND_INDEX) +*************** ATSimplePermissionsRelationOrIndex(Relat +*** 3276,3281 **** +--- 3315,3326 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(rel)); + ++ /* SELinux checks */ ++ if (!colName) ++ sepgsql_relation_alter(RelationGetRelid(rel), NULL, InvalidOid); ++ else ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); ++ + if (!allowSystemTableMods && IsSystemRelation(rel)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), +*************** ATExecAddColumn(AlteredTableInfo *tab, R +*** 3519,3524 **** +--- 3564,3570 ---- + HeapTuple typeTuple; + Oid typeOid; + int32 typmod; ++ Oid attsecid; + Form_pg_type tform; + Expr *defval; + +*************** ATExecAddColumn(AlteredTableInfo *tab, R +*** 3556,3561 **** +--- 3602,3610 ---- + errmsg("child table \"%s\" has a conflicting \"%s\" column", + RelationGetRelationName(rel), colDef->colname))); + ++ /* SELinux checks db_column:{setattr} */ ++ sepgsql_attribute_alter(myrelid, colDef->colname); ++ + /* Bump the existing child att's inhcount */ + childatt->attinhcount++; + simple_heap_update(attrdesc, &tuple->t_self, tuple); +*************** ATExecAddColumn(AlteredTableInfo *tab, R +*** 3595,3600 **** +--- 3644,3652 ---- + errmsg("column \"%s\" of relation \"%s\" already exists", + colDef->colname, RelationGetRelationName(rel)))); + ++ /* SELinux checks db_column:{create} */ ++ attsecid = sepgsql_attribute_create(myrelid, colDef); ++ + /* Determine the new attribute's number */ + if (isOid) + newattnum = ObjectIdAttributeNumber; +*************** ATExecAddColumn(AlteredTableInfo *tab, R +*** 3637,3643 **** + + ReleaseSysCache(typeTuple); + +! InsertPgAttributeTuple(attrdesc, &attribute, NULL); + + heap_close(attrdesc, RowExclusiveLock); + +--- 3689,3695 ---- + + ReleaseSysCache(typeTuple); + +! InsertPgAttributeTuple(attrdesc, &attribute, NULL, attsecid); + + heap_close(attrdesc, RowExclusiveLock); + +*************** ATPrepSetStatistics(Relation rel, const +*** 4026,4031 **** +--- 4078,4085 ---- + if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(rel)); ++ /* SELinux checks */ ++ sepgsql_attribute_alter(RelationGetRelid(rel), colName); + } + + static void +*************** ATExecDropColumn(List **wqueue, Relation +*** 4181,4187 **** + + /* At top level, permission check was done in ATPrepCmd, else do it */ + if (recursing) +! ATSimplePermissions(rel, false); + + /* + * get the number of the attribute +--- 4235,4241 ---- + + /* At top level, permission check was done in ATPrepCmd, else do it */ + if (recursing) +! ATSimplePermissions(rel, NULL, false); + + /* + * get the number of the attribute +*************** ATAddCheckConstraint(List **wqueue, Alte +*** 4483,4489 **** + + /* At top level, permission check was done in ATPrepCmd, else do it */ + if (recursing) +! ATSimplePermissions(rel, false); + + /* + * Call AddRelationNewConstraints to do the work, making sure it works on +--- 4537,4543 ---- + + /* At top level, permission check was done in ATPrepCmd, else do it */ + if (recursing) +! ATSimplePermissions(rel, NULL, false); + + /* + * Call AddRelationNewConstraints to do the work, making sure it works on +*************** ATExecDropConstraint(Relation rel, const +*** 5385,5391 **** + + /* At top level, permission check was done in ATPrepCmd, else do it */ + if (recursing) +! ATSimplePermissions(rel, false); + + conrel = heap_open(ConstraintRelationId, RowExclusiveLock); + +--- 5439,5445 ---- + + /* At top level, permission check was done in ATPrepCmd, else do it */ + if (recursing) +! ATSimplePermissions(rel, NULL, false); + + conrel = heap_open(ConstraintRelationId, RowExclusiveLock); + +*************** ATExecChangeOwner(Oid relationOid, Oid n +*** 6319,6324 **** +--- 6373,6380 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux checks db_table:{setattr} */ ++ sepgsql_relation_alter(relationOid, NULL, InvalidOid); + } + + memset(repl_null, false, sizeof(repl_null)); +*************** ATExecAddInherit(Relation child_rel, Ran +*** 6923,6929 **** + * Must be owner of both parent and child -- child was checked by + * ATSimplePermissions call in ATPrepCmd + */ +! ATSimplePermissions(parent_rel, false); + + /* Permanent rels cannot inherit from temporary ones */ + if (parent_rel->rd_istemp && !child_rel->rd_istemp) +--- 6979,6985 ---- + * Must be owner of both parent and child -- child was checked by + * ATSimplePermissions call in ATPrepCmd + */ +! ATSimplePermissions(parent_rel, NULL, false); + + /* Permanent rels cannot inherit from temporary ones */ + if (parent_rel->rd_istemp && !child_rel->rd_istemp) +*************** AlterTableNamespace(RangeVar *relation, +*** 7581,7586 **** +--- 7637,7645 ---- + RelationGetRelationName(rel), + newschema))); + ++ /* SELinux checks */ ++ sepgsql_relation_alter(relid, NULL, nspOid); ++ + /* disallow renaming into or out of temp schemas */ + if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid)) + ereport(ERROR, +*************** AlterSeqNamespaces(Relation classRel, Re +*** 7773,7778 **** +--- 7832,7965 ---- + relation_close(depRel, AccessShareLock); + } + ++ /* ++ * ALTER TABLE/SEQUENCE name SECURITY_LABEL [=] newlabel ++ * ALTER TABLE/SEQUENCE name ALTER column SECURITY_LABEL [=] newlabel ++ */ ++ static void ++ ExecRelationSetSecLabel(Oid relid, DefElem *seclabel) ++ { ++ Relation rel; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ Oid secid; ++ bool replaces[Natts_pg_class]; ++ ++ rel = heap_open(RelationRelationId, RowExclusiveLock); ++ oldtup = SearchSysCache(RELOID, ++ ObjectIdGetDatum(relid), ++ 0, 0, 0); ++ if (!HeapTupleIsValid(oldtup)) ++ elog(ERROR, "cache lookup failed for relation: %u", relid); ++ ++ memset(replaces, false, sizeof(replaces)); ++ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), ++ NULL, NULL, replaces); ++ if (!HeapTupleHasSecid(newtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to set security label on \"%s\"", ++ get_rel_name(relid)))); ++ ++ ReleaseSysCache(oldtup); ++ ++ /* SELinux checks db_table:{setattr relabelfrom relabelto} */ ++ secid = sepgsql_relation_relabel(relid, seclabel); ++ ++ HeapTupleSetSecid(newtup, secid); ++ ++ simple_heap_update(rel, &newtup->t_self, newtup); ++ ++ CatalogUpdateIndexes(rel, newtup); ++ ++ heap_freetuple(newtup); ++ ++ heap_close(rel, RowExclusiveLock); ++ } ++ ++ static void ++ ExecAttributeSetSecLabel(Oid relid, const char *attname, DefElem *seclabel) ++ { ++ Relation rel; ++ HeapTuple oldtup; ++ HeapTuple newtup; ++ AttrNumber attnum; ++ Oid secid; ++ bool replaces[Natts_pg_attribute]; ++ ++ rel = heap_open(AttributeRelationId, RowExclusiveLock); ++ oldtup = SearchSysCacheAttName(relid, attname); ++ if (!HeapTupleIsValid(oldtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_UNDEFINED_COLUMN), ++ errmsg("column \"%s\" of relation \"%s\" does not exist", ++ attname, get_rel_name(relid)))); ++ attnum = ((Form_pg_attribute) GETSTRUCT(oldtup))->attnum; ++ ++ memset(replaces, false, sizeof(replaces)); ++ newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel), ++ NULL, NULL, replaces); ++ if (!HeapTupleHasSecid(newtup)) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to set security context on \"%s.%s\"", ++ get_rel_name(relid), attname))); ++ ++ ReleaseSysCache(oldtup); ++ ++ /* SELinux checks db_column:{setattr relabelfrom relabelto} */ ++ secid = sepgsql_attribute_relabel(relid, attnum, seclabel); ++ ++ HeapTupleSetSecid(newtup, secid); ++ ++ simple_heap_update(rel, &newtup->t_self, newtup); ++ ++ CatalogUpdateIndexes(rel, newtup); ++ ++ heap_freetuple(newtup); ++ ++ heap_close(rel, RowExclusiveLock); ++ } ++ ++ void ++ AlterRelationSecLabel(RangeVar *relation, const char *attname, ++ ObjectType objtype, DefElem *seclabel) ++ { ++ Oid relid; ++ char relkind; ++ ++ /* Check relation type against type specified in the ALTER command */ ++ relid = RangeVarGetRelid(relation, false); ++ relkind = get_rel_relkind(relid); ++ ++ switch (objtype) ++ { ++ case OBJECT_TABLE: ++ case OBJECT_COLUMN: ++ if (relkind != RELKIND_RELATION) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a table", get_rel_name(relid)))); ++ break; ++ ++ case OBJECT_SEQUENCE: ++ if (relkind != RELKIND_SEQUENCE) ++ ereport(ERROR, ++ (errcode(ERRCODE_WRONG_OBJECT_TYPE), ++ errmsg("\"%s\" is not a sequence", get_rel_name(relid)))); ++ break; ++ ++ default: ++ elog(ERROR, "unrecognized object type: %d", (int)objtype); ++ break; ++ } ++ ++ /* Exec set security label */ ++ if (objtype != OBJECT_COLUMN) ++ ExecRelationSetSecLabel(relid, seclabel); ++ else ++ ExecAttributeSetSecLabel(relid, attname, seclabel); ++ } + + /* + * This code supports +diff -Nrpc blob/src/backend/commands/trigger.c sepgsql/src/backend/commands/trigger.c +*** blob/src/backend/commands/trigger.c Thu Mar 18 09:43:03 2010 +--- sepgsql/src/backend/commands/trigger.c Thu Mar 18 01:55:40 2010 +*************** +*** 33,38 **** +--- 33,39 ---- + #include "nodes/makefuncs.h" + #include "parser/parse_func.h" + #include "pgstat.h" ++ #include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "tcop/utility.h" + #include "utils/acl.h" +*************** CreateTrigger(CreateTrigStmt *stmt, Oid +*** 182,187 **** +--- 183,192 ---- + NameListToString(stmt->funcname)))); + } + ++ /* SELinux checks */ ++ if (checkPermissions) ++ sepgsql_trigger_create(RelationGetRelid(rel), stmt->trigname, funcoid); ++ + /* + * If the command is a user-entered CREATE CONSTRAINT TRIGGER command that + * references one of the built-in RI_FKey trigger functions, assume it is +*************** DropTrigger(Oid relid, const char *trign +*** 746,751 **** +--- 751,757 ---- + if (!pg_class_ownercheck(relid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + get_rel_name(relid)); ++ sepgsql_trigger_drop(relid, trigname); + + object.classId = TriggerRelationId; + object.objectId = HeapTupleGetOid(tup); +*************** renametrig(Oid relid, +*** 862,867 **** +--- 868,876 ---- + */ + targetrel = heap_open(relid, AccessExclusiveLock); + ++ /* SELinux checks */ ++ sepgsql_trigger_alter(relid, oldname); ++ + /* + * Scan pg_trigger twice for existing triggers on relation. We do this in + * order to ensure a trigger does not exist with newname (The unique index +diff -Nrpc blob/src/backend/commands/tsearchcmds.c sepgsql/src/backend/commands/tsearchcmds.c +*** blob/src/backend/commands/tsearchcmds.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/commands/tsearchcmds.c Thu Sep 17 23:44:07 2009 +*************** +*** 35,40 **** +--- 35,41 ---- + #include "miscadmin.h" + #include "nodes/makefuncs.h" + #include "parser/parse_func.h" ++ #include "security/sepgsql.h" + #include "tsearch/ts_cache.h" + #include "tsearch/ts_public.h" + #include "tsearch/ts_utils.h" +*************** DefineTSParser(List *names, List *parame +*** 171,176 **** +--- 172,178 ---- + NameData pname; + Oid prsOid; + Oid namespaceoid; ++ Oid secid; + + if (!superuser()) + ereport(ERROR, +*************** DefineTSParser(List *names, List *parame +*** 250,261 **** +--- 252,273 ---- + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("text search parser lextypes method is required"))); + ++ /* Permission checks */ ++ secid = sepgsql_ts_parser_create(prsname, namespaceoid, ++ DatumGetObjectId(values[Anum_pg_ts_parser_prsstart - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_parser_prstoken - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_parser_prsend - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_parser_prsheadline - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_parser_prslextype - 1])); ++ + /* + * Looks good, insert + */ + prsRel = heap_open(TSParserRelationId, RowExclusiveLock); + + tup = heap_form_tuple(prsRel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, secid); + + prsOid = simple_heap_insert(prsRel, tup); + +*************** RenameTSParser(List *oldname, const char +*** 372,377 **** +--- 384,392 ---- + + prsId = TSParserGetPrsid(oldname, false); + ++ /* SELinux checks */ ++ sepgsql_ts_parser_alter(prsId, newname); ++ + tup = SearchSysCacheCopy(TSPARSEROID, + ObjectIdGetDatum(prsId), + 0, 0, 0); +*************** DefineTSDictionary(List *names, List *pa +*** 503,508 **** +--- 518,524 ---- + List *dictoptions = NIL; + Oid dictOid; + Oid namespaceoid; ++ Oid secid; + AclResult aclresult; + char *dictname; + +*************** DefineTSDictionary(List *names, List *pa +*** 515,520 **** +--- 531,539 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceoid)); + ++ /* SELinux check */ ++ secid = sepgsql_ts_dict_create(dictname, namespaceoid); ++ + /* + * loop over the definition list and extract the information we need. + */ +*************** DefineTSDictionary(List *names, List *pa +*** 563,568 **** +--- 582,589 ---- + dictRel = heap_open(TSDictionaryRelationId, RowExclusiveLock); + + tup = heap_form_tuple(dictRel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, secid); + + dictOid = simple_heap_insert(dictRel, tup); + +*************** RenameTSDictionary(List *oldname, const +*** 621,626 **** +--- 642,650 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* SELinux checks */ ++ sepgsql_ts_dict_alter(dictId, newname); ++ + namestrcpy(&(((Form_pg_ts_dict) GETSTRUCT(tup))->dictname), newname); + simple_heap_update(rel, &tup->t_self, tup); + CatalogUpdateIndexes(rel, tup); +*************** AlterTSDictionary(AlterTSDictionaryStmt +*** 762,767 **** +--- 786,794 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSDICTIONARY, + NameListToString(stmt->dictname)); + ++ /* SELinux checks */ ++ sepgsql_ts_dict_alter(dictId, NULL); ++ + /* deserialize the existing set of options */ + opt = SysCacheGetAttr(TSDICTOID, tup, + Anum_pg_ts_dict_dictinitoption, +*************** AlterTSDictionaryOwner(List *name, Oid n +*** 889,894 **** +--- 916,923 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux checks */ ++ sepgsql_ts_dict_alter(dictId, NULL); + + form->dictowner = newOwnerId; + +*************** DefineTSTemplate(List *names, List *para +*** 999,1004 **** +--- 1028,1034 ---- + NameData dname; + int i; + Oid dictOid; ++ Oid dictSecid; + Oid namespaceoid; + char *tmplname; + +*************** DefineTSTemplate(List *names, List *para +*** 1054,1059 **** +--- 1084,1094 ---- + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("text search template lexize method is required"))); + ++ /* SELinux checks */ ++ dictSecid = sepgsql_ts_template_create(tmplname, namespaceoid, ++ DatumGetObjectId(values[Anum_pg_ts_template_tmplinit - 1]), ++ DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1])); ++ + /* + * Looks good, insert + */ +*************** DefineTSTemplate(List *names, List *para +*** 1061,1066 **** +--- 1096,1103 ---- + tmplRel = heap_open(TSTemplateRelationId, RowExclusiveLock); + + tup = heap_form_tuple(tmplRel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, dictSecid); + + dictOid = simple_heap_insert(tmplRel, tup); + +*************** RenameTSTemplate(List *oldname, const ch +*** 1093,1098 **** +--- 1130,1138 ---- + + tmplId = TSTemplateGetTmplid(oldname, false); + ++ /* Permission checks */ ++ sepgsql_ts_template_alter(tmplId, newname); ++ + tup = SearchSysCacheCopy(TSTEMPLATEOID, + ObjectIdGetDatum(tmplId), + 0, 0, 0); +*************** DefineTSConfiguration(List *names, List +*** 1335,1340 **** +--- 1375,1381 ---- + Oid sourceOid = InvalidOid; + Oid prsOid = InvalidOid; + Oid cfgOid; ++ Oid cfgSecid; + ListCell *pl; + + /* Convert list of names to a name and namespace */ +*************** DefineTSConfiguration(List *names, List +*** 1399,1404 **** +--- 1440,1448 ---- + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("text search parser is required"))); + ++ /* SELinux checks */ ++ cfgSecid = sepgsql_ts_config_create(cfgname, namespaceoid); ++ + /* + * Looks good, build tuple and insert + */ +*************** DefineTSConfiguration(List *names, List +*** 1414,1419 **** +--- 1458,1465 ---- + cfgRel = heap_open(TSConfigRelationId, RowExclusiveLock); + + tup = heap_form_tuple(cfgRel->rd_att, values, nulls); ++ if (HeapTupleHasSecid(tup)) ++ HeapTupleSetSecid(tup, cfgSecid); + + cfgOid = simple_heap_insert(cfgRel, tup); + +*************** RenameTSConfiguration(List *oldname, con +*** 1519,1524 **** +--- 1565,1573 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + ++ /* permission checks */ ++ sepgsql_ts_config_alter(cfgId, newname); ++ + namestrcpy(&(((Form_pg_ts_config) GETSTRUCT(tup))->cfgname), newname); + simple_heap_update(rel, &tup->t_self, tup); + CatalogUpdateIndexes(rel, tup); +*************** AlterTSConfigurationOwner(List *name, Oi +*** 1690,1695 **** +--- 1739,1746 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + } ++ /* SELinux checks */ ++ sepgsql_ts_config_alter(cfgId, NULL); + + form->cfgowner = newOwnerId; + +*************** AlterTSConfiguration(AlterTSConfiguratio +*** 1727,1732 **** +--- 1778,1786 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TSCONFIGURATION, + NameListToString(stmt->cfgname)); + ++ /* SELinux checks */ ++ sepgsql_ts_config_alter(HeapTupleGetOid(tup), NULL); ++ + relMap = heap_open(TSConfigMapRelationId, RowExclusiveLock); + + /* Add or drop mappings */ +diff -Nrpc blob/src/backend/commands/typecmds.c sepgsql/src/backend/commands/typecmds.c +*** blob/src/backend/commands/typecmds.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/commands/typecmds.c Thu Sep 17 22:10:19 2009 +*************** +*** 56,61 **** +--- 56,62 ---- + #include "parser/parse_expr.h" + #include "parser/parse_func.h" + #include "parser/parse_type.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +*************** AlterDomainDefault(List *names, Node *de +*** 1543,1548 **** +--- 1544,1550 ---- + + /* Check it's a domain and check user has permission for ALTER DOMAIN */ + checkDomainOwner(tup, typename); ++ sepgsql_type_alter(domainoid, NULL, InvalidOid); + + /* Setup new tuple */ + MemSet(new_record, (Datum) 0, sizeof(new_record)); +*************** AlterDomainNotNull(List *names, bool not +*** 1671,1676 **** +--- 1673,1679 ---- + + /* Check it's a domain and check user has permission for ALTER DOMAIN */ + checkDomainOwner(tup, typename); ++ sepgsql_type_alter(domainoid, NULL, InvalidOid); + + /* Is the domain already set to the desired constraint? */ + if (typTup->typnotnull == notNull) +*************** AlterDomainDropConstraint(List *names, c +*** 1772,1777 **** +--- 1775,1781 ---- + + /* Check it's a domain and check user has permission for ALTER DOMAIN */ + checkDomainOwner(tup, typename); ++ sepgsql_type_alter(domainoid, NULL, InvalidOid); + + /* Grab an appropriate lock on the pg_constraint relation */ + conrel = heap_open(ConstraintRelationId, RowExclusiveLock); +*************** AlterDomainAddConstraint(List *names, No +*** 1848,1853 **** +--- 1852,1858 ---- + + /* Check it's a domain and check user has permission for ALTER DOMAIN */ + checkDomainOwner(tup, typename); ++ sepgsql_type_alter(domainoid, NULL, InvalidOid); + + /* Check for unsupported constraint types */ + if (IsA(newConstraint, FkConstraint)) +*************** RenameType(List *names, const char *newT +*** 2470,2475 **** +--- 2475,2483 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, + format_type_be(typeOid)); + ++ /* SELinux check permission */ ++ sepgsql_type_alter(typeOid, newTypeName, InvalidOid); ++ + /* + * If it's a composite type, we need to check that it really is a + * free-standing composite type, and not a table's rowtype. We want people +*************** AlterTypeOwner(List *names, Oid newOwner +*** 2590,2595 **** +--- 2598,2605 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(typTup->typnamespace)); + } ++ /* SELinux checks permissions */ ++ sepgsql_type_alter(HeapTupleGetOid(tup), NULL, InvalidOid); + + /* + * If it's a composite type, invoke ATExecChangeOwner so that we fix +*************** AlterTypeNamespace(List *names, const ch +*** 2706,2711 **** +--- 2716,2724 ---- + errhint("You can alter type %s, which will alter the array type as well.", + format_type_be(elemOid)))); + ++ /* SELinux checks permissions */ ++ sepgsql_type_alter(typeOid, NULL, nspOid); ++ + /* and do the work */ + AlterTypeNamespaceInternal(typeOid, nspOid, false, true); + } +diff -Nrpc blob/src/backend/commands/vacuum.c sepgsql/src/backend/commands/vacuum.c +*** blob/src/backend/commands/vacuum.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/commands/vacuum.c Sun Dec 20 23:35:32 2009 +*************** +*** 32,37 **** +--- 32,38 ---- + #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" +*************** vacuum_rel(Oid relid, VacuumStmt *vacstm +*** 1209,1214 **** +--- 1210,1218 ---- + /* 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 -Nrpc blob/src/backend/commands/view.c sepgsql/src/backend/commands/view.c +*** blob/src/backend/commands/view.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/commands/view.c Fri Sep 18 14:51:00 2009 +*************** +*** 28,33 **** +--- 28,34 ---- + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteManip.h" + #include "rewrite/rewriteSupport.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +*************** DefineVirtualRelation(const RangeVar *re +*** 166,171 **** +--- 167,175 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(rel)); + ++ /* SELinux checks */ ++ sepgsql_view_replace(viewOid); ++ + /* Also check it's not in use already */ + CheckTableNotInUse(rel, "CREATE OR REPLACE VIEW"); + +diff -Nrpc blob/src/backend/executor/execJunk.c sepgsql/src/backend/executor/execJunk.c +*** blob/src/backend/executor/execJunk.c Sat Jan 3 13:01:35 2009 +--- sepgsql/src/backend/executor/execJunk.c Wed Jul 15 19:30:50 2009 +*************** +*** 60,66 **** + * An optional resultSlot can be passed as well. + */ + JunkFilter * +! ExecInitJunkFilter(List *targetList, bool hasoid, TupleTableSlot *slot) + { + JunkFilter *junkfilter; + TupleDesc cleanTupType; +--- 60,67 ---- + * An optional resultSlot can be passed as well. + */ + JunkFilter * +! ExecInitJunkFilter(List *targetList, bool hasoid, bool hasseclabel, +! TupleTableSlot *slot) + { + JunkFilter *junkfilter; + TupleDesc cleanTupType; +*************** ExecInitJunkFilter(List *targetList, boo +*** 72,78 **** + /* + * Compute the tuple descriptor for the cleaned tuple. + */ +! cleanTupType = ExecCleanTypeFromTL(targetList, hasoid); + + /* + * Use the given slot, or make a new slot if we weren't given one. +--- 73,79 ---- + /* + * Compute the tuple descriptor for the cleaned tuple. + */ +! cleanTupType = ExecCleanTypeFromTL(targetList, hasoid, hasseclabel); + + /* + * Use the given slot, or make a new slot if we weren't given one. +diff -Nrpc blob/src/backend/executor/execMain.c sepgsql/src/backend/executor/execMain.c +*** blob/src/backend/executor/execMain.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/executor/execMain.c Tue Dec 15 17:30:25 2009 +*************** +*** 39,44 **** +--- 39,45 ---- + #include "access/xact.h" + #include "catalog/heap.h" + #include "catalog/namespace.h" ++ #include "catalog/pg_security.h" + #include "catalog/toasting.h" + #include "commands/tablespace.h" + #include "commands/trigger.h" +*************** +*** 50,55 **** +--- 51,57 ---- + #include "optimizer/clauses.h" + #include "parser/parse_clause.h" + #include "parser/parsetree.h" ++ #include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/lmgr.h" + #include "storage/smgr.h" +*************** ExecCheckRTPerms(List *rangeTable) +*** 442,448 **** + + foreach(l, rangeTable) + { +! ExecCheckRTEPerms((RangeTblEntry *) lfirst(l)); + } + } + +--- 444,453 ---- + + foreach(l, rangeTable) + { +! RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); +! +! ExecCheckRTEPerms(rte); +! sepgsqlCheckRTEPerms(rte); + } + } + +*************** InitPlan(QueryDesc *queryDesc, int eflag +*** 901,916 **** + for (i = 0; i < as_nplans; i++) + { + PlanState *subplan = appendplans[i]; + JunkFilter *j; + + if (operation == CMD_UPDATE) +! ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, +! subplan->plan->targetlist); + + j = ExecInitJunkFilter(subplan->plan->targetlist, +! resultRelInfo->ri_RelationDesc->rd_att->tdhasoid, +! ExecAllocTableSlot(estate->es_tupleTable)); +! + /* + * Since it must be UPDATE/DELETE, there had better be a + * "ctid" junk attribute in the tlist ... but ctid could +--- 906,921 ---- + for (i = 0; i < as_nplans; i++) + { + PlanState *subplan = appendplans[i]; ++ Relation resultRel = resultRelInfo->ri_RelationDesc; + JunkFilter *j; + + if (operation == CMD_UPDATE) +! ExecCheckPlanOutput(resultRel, subplan->plan->targetlist); + + j = ExecInitJunkFilter(subplan->plan->targetlist, +! RelationGetDescr(resultRel)->tdhasoid, +! RelationGetDescr(resultRel)->tdhassecid, +! ExecAllocTableSlot(estate->es_tupleTable)); + /* + * Since it must be UPDATE/DELETE, there had better be a + * "ctid" junk attribute in the tlist ... but ctid could +*************** InitPlan(QueryDesc *queryDesc, int eflag +*** 953,958 **** +--- 958,964 ---- + + j = ExecInitJunkFilter(planstate->plan->targetlist, + tupType->tdhasoid, ++ tupType->tdhassecid, + ExecAllocTableSlot(estate->es_tupleTable)); + estate->es_junkFilter = j; + if (estate->es_result_relation_info) +*************** InitPlan(QueryDesc *queryDesc, int eflag +*** 1023,1029 **** + * We assume all the sublists will generate the same output tupdesc. + */ + tupType = ExecTypeFromTL((List *) linitial(plannedstmt->returningLists), +! false); + + /* Set up a slot for the output of the RETURNING projection(s) */ + slot = ExecAllocTableSlot(estate->es_tupleTable); +--- 1029,1035 ---- + * We assume all the sublists will generate the same output tupdesc. + */ + tupType = ExecTypeFromTL((List *) linitial(plannedstmt->returningLists), +! false, false); + + /* Set up a slot for the output of the RETURNING projection(s) */ + slot = ExecAllocTableSlot(estate->es_tupleTable); +*************** ExecContextForcesOids(PlanState *plansta +*** 1346,1351 **** +--- 1352,1388 ---- + return false; + } + ++ /* ++ * ExecContextForcesSecids ++ * ++ * We need to ensure that result tuples have space for security identifier. ++ * if the security feature need to store it within the given relation. ++ */ ++ bool ExecContextForcesSecids(PlanState *planstate, bool *hassecid) ++ { ++ if (planstate->state->es_select_into) ++ { ++ *hassecid = securityTupleDescHasSecid(InvalidOid, ++ RELKIND_RELATION); ++ return true; ++ } ++ else ++ { ++ ResultRelInfo *ri = planstate->state->es_result_relation_info; ++ ++ if (ri && ri->ri_RelationDesc) ++ { ++ Oid relid = RelationGetRelid(ri->ri_RelationDesc); ++ char relkind = RelationGetForm(ri->ri_RelationDesc)->relkind; ++ ++ *hassecid = securityTupleDescHasSecid(relid, relkind); ++ ++ return true; ++ } ++ } ++ return false; ++ } ++ + /* ---------------------------------------------------------------- + * ExecEndPlan + * +*************** ExecEndPlan(PlanState *planstate, EState +*** 1426,1431 **** +--- 1463,1520 ---- + } + } + ++ /* ++ * fetchWritableSystemAttribute() fetches writable system column data ++ * using Junkfilter, and saves them at TupleTableSlot temporary. ++ * ++ * storeWritableSystemAttribute() copies these fetched data into ++ * header structure of HeapTuple. ++ */ ++ static void ++ fetchWritableSystemAttribute(JunkFilter *junkfilter, TupleTableSlot *slot, ++ Datum *tts_seclabel) ++ { ++ AttrNumber attno; ++ Datum datum; ++ bool isnull; ++ ++ /* for Security Label */ ++ attno = ExecFindJunkAttribute(junkfilter, SecurityAttributeName); ++ if (attno != InvalidAttrNumber) ++ { ++ datum = ExecGetJunkAttribute(slot, attno, &isnull); ++ if (isnull) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to set NULL on \"%s\"", ++ SecurityAttributeName))); ++ *tts_seclabel = datum; ++ } ++ } ++ ++ static void ++ storeWritableSystemAttribute(Relation rel, TupleTableSlot *slot, HeapTuple tuple) ++ { ++ Oid relid = RelationGetRelid(rel); ++ Oid secid; ++ ++ /* "security_label" */ ++ if (DatumGetPointer(slot->tts_seclabel) != NULL) ++ { ++ char *seclabel = TextDatumGetCString(slot->tts_seclabel); ++ ++ if (!HeapTupleHasSecid(tuple)) ++ ereport(ERROR, ++ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), ++ errmsg("Unable to assign security label on \"%s\"", ++ RelationGetRelationName(rel)))); ++ secid = securityTransSecLabelIn(relid, seclabel); ++ HeapTupleSetSecid(tuple, secid); ++ } ++ else if (HeapTupleHasSecid(tuple)) ++ HeapTupleSetSecid(tuple, InvalidOid); ++ } ++ + /* ---------------------------------------------------------------- + * ExecutePlan + * +*************** ExecutePlan(EState *estate, +*** 1487,1492 **** +--- 1576,1583 ---- + */ + for (;;) + { ++ Datum tts_seclabel = PointerGetDatum(NULL); ++ + /* Reset the per-output-tuple exprcontext */ + ResetPerTupleExprContext(estate); + +*************** lnext: ; +*** 1631,1636 **** +--- 1722,1732 ---- + } + + /* ++ * extract writable system attribute ++ */ ++ fetchWritableSystemAttribute(junkfilter, slot, &tts_seclabel); ++ ++ /* + * extract the 'ctid' junk attribute. + */ + if (operation == CMD_UPDATE || operation == CMD_DELETE) +*************** lnext: ; +*** 1657,1662 **** +--- 1753,1759 ---- + if (operation != CMD_DELETE) + slot = ExecFilterJunk(junkfilter, slot); + } ++ slot->tts_seclabel = tts_seclabel; + + /* + * now that we have a tuple, do the appropriate thing with it.. either +*************** ExecInsert(TupleTableSlot *slot, +*** 1781,1786 **** +--- 1878,1885 ---- + if (resultRelationDesc->rd_rel->relhasoids) + HeapTupleSetOid(tuple, InvalidOid); + ++ storeWritableSystemAttribute(resultRelationDesc, slot, tuple); ++ + /* BEFORE ROW INSERT Triggers */ + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) +*************** ExecInsert(TupleTableSlot *slot, +*** 1811,1816 **** +--- 1910,1921 ---- + } + + /* ++ * SELinux assigns default security label, and ++ * it also checks db_tuple:{insert} permission ++ */ ++ sepgsqlHeapTupleInsert(resultRelationDesc, tuple, false); ++ ++ /* + * Check the constraints of the tuple + */ + if (resultRelationDesc->rd_att->constr) +*************** ExecUpdate(TupleTableSlot *slot, +*** 2018,2023 **** +--- 2123,2130 ---- + resultRelInfo = estate->es_result_relation_info; + resultRelationDesc = resultRelInfo->ri_RelationDesc; + ++ storeWritableSystemAttribute(resultRelationDesc, slot, tuple); ++ + /* BEFORE ROW UPDATE Triggers */ + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) +*************** ExecUpdate(TupleTableSlot *slot, +*** 2048,2053 **** +--- 2155,2163 ---- + } + } + ++ /* SELinux checks db_tuple:{relabelfrom relabelto}, if needed */ ++ sepgsqlHeapTupleUpdate(resultRelationDesc, tupleid, tuple); ++ + /* + * Check the constraints of the tuple + * +*************** OpenIntoRel(QueryDesc *queryDesc) +*** 2843,2848 **** +--- 2953,2959 ---- + Oid namespaceId; + Oid tablespaceId; + Datum reloptions; ++ Oid *secLabels; + AclResult aclresult; + Oid intoRelationId; + TupleDesc tupdesc; +*************** OpenIntoRel(QueryDesc *queryDesc) +*** 2886,2891 **** +--- 2997,3010 ---- + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceId)); + ++ /* SELinux checks */ ++ secLabels = sepgsql_relation_create(intoName, ++ RELKIND_RELATION, ++ queryDesc->tupDesc, ++ namespaceId, ++ NULL, NIL, ++ true, true); ++ + /* + * Select tablespace to use. If not specified, use default tablespace + * (which may in turn default to database's default). +*************** OpenIntoRel(QueryDesc *queryDesc) +*** 2944,2950 **** + 0, + into->onCommit, + reloptions, +! allowSystemTableMods); + + FreeTupleDesc(tupdesc); + +--- 3063,3070 ---- + 0, + into->onCommit, + reloptions, +! allowSystemTableMods, +! secLabels); + + FreeTupleDesc(tupdesc); + +*************** intorel_receive(TupleTableSlot *slot, De +*** 3069,3074 **** +--- 3189,3198 ---- + if (myState->rel->rd_rel->relhasoids) + HeapTupleSetOid(tuple, InvalidOid); + ++ storeWritableSystemAttribute(myState->rel, slot, tuple); ++ /* SELinux checks db_tuple:{insert} */ ++ sepgsqlHeapTupleInsert(myState->rel, tuple, false); ++ + heap_insert(myState->rel, + tuple, + myState->estate->es_output_cid, +diff -Nrpc blob/src/backend/executor/execQual.c sepgsql/src/backend/executor/execQual.c +*** blob/src/backend/executor/execQual.c Thu Mar 18 09:43:03 2010 +--- sepgsql/src/backend/executor/execQual.c Thu Mar 18 01:55:40 2010 +*************** +*** 48,53 **** +--- 48,54 ---- + #include "optimizer/planner.h" + #include "parser/parse_coerce.h" + #include "pgstat.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +*************** init_fcache(Oid foid, FuncExprState *fca +*** 1138,1143 **** +--- 1139,1145 ---- + aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid)); ++ sepgsql_proc_execute(foid); + + /* + * Safety check on nargs. Under normal circumstances this should never +*************** ExecEvalArrayCoerceExpr(ArrayCoerceExprS +*** 4133,4138 **** +--- 4135,4141 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(acoerce->elemfuncid)); ++ sepgsql_proc_execute(acoerce->elemfuncid); + + /* Set up the primary fmgr lookup information */ + fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc), +diff -Nrpc blob/src/backend/executor/execScan.c sepgsql/src/backend/executor/execScan.c +*** blob/src/backend/executor/execScan.c Thu Apr 9 00:13:21 2009 +--- sepgsql/src/backend/executor/execScan.c Wed Sep 9 13:14:37 2009 +*************** +*** 20,25 **** +--- 20,26 ---- + + #include "executor/executor.h" + #include "miscadmin.h" ++ #include "security/rowlevel.h" + #include "utils/memutils.h" + + +*************** ExecScan(ScanState *node, +*** 53,58 **** +--- 54,60 ---- + ProjectionInfo *projInfo; + ExprDoneCond isDone; + TupleTableSlot *resultSlot; ++ Scan *scan = (Scan *)node->ps.plan; + + /* + * Fetch data from node +*************** ExecScan(ScanState *node, +*** 64,70 **** + * If we have neither a qual to check nor a projection to do, just skip + * all the overhead and return the raw scan tuple. + */ +! if (!qual && !projInfo) + return (*accessMtd) (node); + + /* +--- 66,72 ---- + * If we have neither a qual to check nor a projection to do, just skip + * all the overhead and return the raw scan tuple. + */ +! if (!qual && !projInfo && !scan->rowlvPerms) + return (*accessMtd) (node); + + /* +*************** ExecScan(ScanState *node, +*** 128,136 **** + * when the qual is nil ... saves only a few cycles, but they add up + * ... + */ +! if (!qual || ExecQual(qual, econtext, false)) + { + /* + * Found a satisfactory scan tuple. + */ + if (projInfo) +--- 130,147 ---- + * when the qual is nil ... saves only a few cycles, but they add up + * ... + */ +! if (rowlvExecScanFilter(scan, node->ss_currentRelation, slot) +! && (!qual || ExecQual(qual, econtext, false))) + { + /* ++ * NOTE: On FK checks, the Row-level feature needs to raise ++ * an error after evaluation of all the given quals to avoid ++ * incorrect error reporting. We assume FK implementation ++ * does not use malicious functions as the quals. ++ */ ++ rowlvExecScanAbort(scan, node->ss_currentRelation, slot); ++ ++ /* + * Found a satisfactory scan tuple. + */ + if (projInfo) +*************** tlist_matches_tupdesc(PlanState *ps, Lis +*** 197,202 **** +--- 208,214 ---- + int numattrs = tupdesc->natts; + int attrno; + bool hasoid; ++ bool hassecid; + ListCell *tlist_item = list_head(tlist); + + /* Check the tlist attributes */ +*************** tlist_matches_tupdesc(PlanState *ps, Lis +*** 240,251 **** + return false; /* tlist too long */ + + /* +! * If the plan context requires a particular hasoid setting, then that has +! * to match, too. + */ + if (ExecContextForcesOids(ps, &hasoid) && + hasoid != tupdesc->tdhasoid) + return false; + + return true; + } +--- 252,267 ---- + return false; /* tlist too long */ + + /* +! * If the plan context requires a particular hasoid or hassecid setting, +! * then that has to match, too. + */ + if (ExecContextForcesOids(ps, &hasoid) && + hasoid != tupdesc->tdhasoid) + return false; + ++ if (ExecContextForcesSecids(ps, &hassecid) && ++ hassecid != tupdesc->tdhassecid) ++ return false; ++ + return true; + } +diff -Nrpc blob/src/backend/executor/execTuples.c sepgsql/src/backend/executor/execTuples.c +*** blob/src/backend/executor/execTuples.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/executor/execTuples.c Wed Sep 9 13:14:37 2009 +*************** +*** 100,106 **** + + + static TupleDesc ExecTypeFromTLInternal(List *targetList, +! bool hasoid, bool skipjunk); + + + /* ---------------------------------------------------------------- +--- 100,106 ---- + + + static TupleDesc ExecTypeFromTLInternal(List *targetList, +! bool hasoid, bool hasseclabel, bool skipjunk); + + + /* ---------------------------------------------------------------- +*************** ExecInitNullTupleSlot(EState *estate, Tu +*** 968,976 **** + * ---------------------------------------------------------------- + */ + TupleDesc +! ExecTypeFromTL(List *targetList, bool hasoid) + { +! return ExecTypeFromTLInternal(targetList, hasoid, false); + } + + /* ---------------------------------------------------------------- +--- 968,976 ---- + * ---------------------------------------------------------------- + */ + TupleDesc +! ExecTypeFromTL(List *targetList, bool hasoid, bool hassecid) + { +! return ExecTypeFromTLInternal(targetList, hasoid, hassecid, false); + } + + /* ---------------------------------------------------------------- +*************** ExecTypeFromTL(List *targetList, bool ha +*** 980,992 **** + * ---------------------------------------------------------------- + */ + TupleDesc +! ExecCleanTypeFromTL(List *targetList, bool hasoid) + { +! return ExecTypeFromTLInternal(targetList, hasoid, true); + } + + static TupleDesc +! ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) + { + TupleDesc typeInfo; + ListCell *l; +--- 980,993 ---- + * ---------------------------------------------------------------- + */ + TupleDesc +! ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hassecid) + { +! return ExecTypeFromTLInternal(targetList, hasoid, hassecid, true); + } + + static TupleDesc +! ExecTypeFromTLInternal(List *targetList, bool hasoid, +! bool hassecid, bool skipjunk) + { + TupleDesc typeInfo; + ListCell *l; +*************** ExecTypeFromTLInternal(List *targetList, +*** 998,1003 **** +--- 999,1005 ---- + else + len = ExecTargetListLength(targetList); + typeInfo = CreateTemplateTupleDesc(len, hasoid); ++ typeInfo->tdhassecid = hassecid; + + foreach(l, targetList) + { +diff -Nrpc blob/src/backend/executor/execUtils.c sepgsql/src/backend/executor/execUtils.c +*** blob/src/backend/executor/execUtils.c Sun Sep 6 19:40:49 2009 +--- sepgsql/src/backend/executor/execUtils.c Wed Sep 9 13:14:37 2009 +*************** void +*** 512,517 **** +--- 512,518 ---- + ExecAssignResultTypeFromTL(PlanState *planstate) + { + bool hasoid; ++ bool hassecid; + TupleDesc tupDesc; + + if (ExecContextForcesOids(planstate, &hasoid)) +*************** ExecAssignResultTypeFromTL(PlanState *pl +*** 524,535 **** + hasoid = false; + } + + /* + * ExecTypeFromTL needs the parse-time representation of the tlist, not a + * list of ExprStates. This is good because some plan nodes don't bother + * to set up planstate->targetlist ... + */ +! tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid); + ExecAssignResultType(planstate, tupDesc); + } + +--- 525,539 ---- + hasoid = false; + } + ++ if (!ExecContextForcesSecids(planstate, &hassecid)) ++ hassecid = false; ++ + /* + * ExecTypeFromTL needs the parse-time representation of the tlist, not a + * list of ExprStates. This is good because some plan nodes don't bother + * to set up planstate->targetlist ... + */ +! tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid, hassecid); + ExecAssignResultType(planstate, tupDesc); + } + +diff -Nrpc blob/src/backend/executor/functions.c sepgsql/src/backend/executor/functions.c +*** blob/src/backend/executor/functions.c Thu Mar 18 09:43:03 2010 +--- sepgsql/src/backend/executor/functions.c Thu Mar 18 01:55:40 2010 +*************** check_sql_fn_retval(Oid func_id, Oid ret +*** 1158,1164 **** + + /* Set up junk filter if needed */ + if (junkFilter) +! *junkFilter = ExecInitJunkFilter(tlist, false, NULL); + } + else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) + { +--- 1158,1164 ---- + + /* Set up junk filter if needed */ + if (junkFilter) +! *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); + } + else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) + { +*************** check_sql_fn_retval(Oid func_id, Oid ret +*** 1197,1203 **** + } + /* Set up junk filter if needed */ + if (junkFilter) +! *junkFilter = ExecInitJunkFilter(tlist, false, NULL); + return false; /* NOT returning whole tuple */ + } + } +--- 1197,1203 ---- + } + /* Set up junk filter if needed */ + if (junkFilter) +! *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); + return false; /* NOT returning whole tuple */ + } + } +*************** check_sql_fn_retval(Oid func_id, Oid ret +*** 1210,1216 **** + * what the caller expects will happen at runtime. + */ + if (junkFilter) +! *junkFilter = ExecInitJunkFilter(tlist, false, NULL); + return true; + } + Assert(tupdesc); +--- 1210,1216 ---- + * what the caller expects will happen at runtime. + */ + if (junkFilter) +! *junkFilter = ExecInitJunkFilter(tlist, false, false, NULL); + return true; + } + Assert(tupdesc); +diff -Nrpc blob/src/backend/executor/nodeAgg.c sepgsql/src/backend/executor/nodeAgg.c +*** blob/src/backend/executor/nodeAgg.c Sun Sep 6 19:40:49 2009 +--- sepgsql/src/backend/executor/nodeAgg.c Thu Sep 17 17:04:16 2009 +*************** +*** 81,86 **** +--- 81,87 ---- + #include "parser/parse_agg.h" + #include "parser/parse_coerce.h" + #include "parser/parse_oper.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +*************** ExecInitAgg(Agg *node, EState *estate, i +*** 1431,1436 **** +--- 1432,1438 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(aggref->aggfnoid)); ++ sepgsql_proc_execute(aggref->aggfnoid); + + peraggstate->transfn_oid = transfn_oid = aggform->aggtransfn; + peraggstate->finalfn_oid = finalfn_oid = aggform->aggfinalfn; +*************** ExecInitAgg(Agg *node, EState *estate, i +*** 1454,1459 **** +--- 1456,1462 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(transfn_oid)); ++ sepgsql_proc_execute(transfn_oid); + if (OidIsValid(finalfn_oid)) + { + aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner, +*************** ExecInitAgg(Agg *node, EState *estate, i +*** 1461,1466 **** +--- 1464,1470 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(finalfn_oid)); ++ sepgsql_proc_execute(finalfn_oid); + } + } + +diff -Nrpc blob/src/backend/executor/nodeMergejoin.c sepgsql/src/backend/executor/nodeMergejoin.c +*** blob/src/backend/executor/nodeMergejoin.c Thu Mar 18 09:43:03 2010 +--- sepgsql/src/backend/executor/nodeMergejoin.c Thu Mar 18 01:55:40 2010 +*************** +*** 98,103 **** +--- 98,104 ---- + #include "executor/execdefs.h" + #include "executor/nodeMergejoin.h" + #include "miscadmin.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/lsyscache.h" + #include "utils/memutils.h" +*************** MJExamineQuals(List *mergeclauses, +*** 215,220 **** +--- 216,222 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(cmpproc)); ++ sepgsql_proc_execute(cmpproc); + + /* Set up the fmgr lookup information */ + fmgr_info(cmpproc, &(clause->cmpfinfo)); +diff -Nrpc blob/src/backend/executor/nodeSubplan.c sepgsql/src/backend/executor/nodeSubplan.c +*** blob/src/backend/executor/nodeSubplan.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/executor/nodeSubplan.c Wed Jul 15 19:30:50 2009 +*************** ExecInitSubPlan(SubPlan *subplan, PlanSt +*** 869,875 **** + * (hack alert!). The righthand expressions will be evaluated in our + * own innerecontext. + */ +! tupDesc = ExecTypeFromTL(leftptlist, false); + slot = ExecAllocTableSlot(tupTable); + ExecSetSlotDescriptor(slot, tupDesc); + sstate->projLeft = ExecBuildProjectionInfo(lefttlist, +--- 869,875 ---- + * (hack alert!). The righthand expressions will be evaluated in our + * own innerecontext. + */ +! tupDesc = ExecTypeFromTL(leftptlist, false, false); + slot = ExecAllocTableSlot(tupTable); + ExecSetSlotDescriptor(slot, tupDesc); + sstate->projLeft = ExecBuildProjectionInfo(lefttlist, +*************** ExecInitSubPlan(SubPlan *subplan, PlanSt +*** 877,883 **** + slot, + NULL); + +! tupDesc = ExecTypeFromTL(rightptlist, false); + slot = ExecAllocTableSlot(tupTable); + ExecSetSlotDescriptor(slot, tupDesc); + sstate->projRight = ExecBuildProjectionInfo(righttlist, +--- 877,883 ---- + slot, + NULL); + +! tupDesc = ExecTypeFromTL(rightptlist, false, false); + slot = ExecAllocTableSlot(tupTable); + ExecSetSlotDescriptor(slot, tupDesc); + sstate->projRight = ExecBuildProjectionInfo(righttlist, +diff -Nrpc blob/src/backend/executor/nodeWindowAgg.c sepgsql/src/backend/executor/nodeWindowAgg.c +*** blob/src/backend/executor/nodeWindowAgg.c Thu Mar 18 09:43:03 2010 +--- sepgsql/src/backend/executor/nodeWindowAgg.c Thu Mar 18 01:55:40 2010 +*************** +*** 43,48 **** +--- 43,49 ---- + #include "optimizer/clauses.h" + #include "parser/parse_agg.h" + #include "parser/parse_coerce.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/datum.h" +*************** ExecInitWindowAgg(WindowAgg *node, EStat +*** 1224,1229 **** +--- 1225,1231 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(wfunc->winfnoid)); ++ sepgsql_proc_execute(wfunc->winfnoid); + + /* Fill in the perfuncstate data */ + perfuncstate->wfuncstate = wfuncstate; +*************** initialize_peragg(WindowAggState *winsta +*** 1418,1423 **** +--- 1420,1426 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(transfn_oid)); ++ sepgsql_proc_execute(transfn_oid); + if (OidIsValid(finalfn_oid)) + { + aclresult = pg_proc_aclcheck(finalfn_oid, aggOwner, +*************** initialize_peragg(WindowAggState *winsta +*** 1425,1430 **** +--- 1428,1434 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(finalfn_oid)); ++ sepgsql_proc_execute(finalfn_oid); + } + } + +diff -Nrpc blob/src/backend/executor/spi.c sepgsql/src/backend/executor/spi.c +*** blob/src/backend/executor/spi.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/executor/spi.c Tue Dec 15 17:30:25 2009 +*************** SPI_modifytuple(Relation rel, HeapTuple +*** 705,710 **** +--- 705,712 ---- + mtuple->t_tableOid = tuple->t_tableOid; + if (rel->rd_att->tdhasoid) + HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple)); ++ if (HeapTupleHasSecid(mtuple)) ++ HeapTupleSetSecid(mtuple, HeapTupleGetSecid(tuple)); + } + else + { +diff -Nrpc blob/src/backend/libpq/be-fsstubs.c sepgsql/src/backend/libpq/be-fsstubs.c +*** blob/src/backend/libpq/be-fsstubs.c Fri Dec 18 09:40:55 2009 +--- sepgsql/src/backend/libpq/be-fsstubs.c Fri Dec 18 10:27:56 2009 +*************** +*** 46,51 **** +--- 46,52 ---- + #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" +*************** lo_read(int fd, char *buf, int len) +*** 173,178 **** +--- 174,182 ---- + 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; +*************** lo_write(int fd, const char *buf, int le +*** 205,210 **** +--- 209,217 ---- + 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; +*************** Datum +*** 233,238 **** +--- 240,249 ---- + 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 +*************** lo_creat(PG_FUNCTION_ARGS) +*** 240,246 **** + */ + CreateFSContext(); + +! lobjId = inv_create(InvalidOid); + + PG_RETURN_OID(lobjId); + } +--- 251,257 ---- + */ + CreateFSContext(); + +! lobjId = inv_create(InvalidOid, secid); + + PG_RETURN_OID(lobjId); + } +*************** Datum +*** 249,254 **** +--- 260,269 ---- + 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 +*************** lo_create(PG_FUNCTION_ARGS) +*** 256,262 **** + */ + CreateFSContext(); + +! lobjId = inv_create(lobjId); + + PG_RETURN_OID(lobjId); + } +--- 271,277 ---- + */ + CreateFSContext(); + +! lobjId = inv_create(lobjId, secid); + + PG_RETURN_OID(lobjId); + } +*************** lo_unlink(PG_FUNCTION_ARGS) +*** 286,291 **** +--- 301,309 ---- + (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. + */ +*************** lo_import_internal(text *filename, Oid l +*** 381,389 **** + int nbytes, + tmp; + char buf[BUFSIZE]; +! char fnamebuf[MAXPGPATH]; + LargeObjectDesc *lobj; + Oid oid; + + #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS + if (!superuser()) +--- 399,408 ---- + int nbytes, + tmp; + char buf[BUFSIZE]; +! char *fnamebuf = text_to_cstring(filename); + LargeObjectDesc *lobj; + Oid oid; ++ Oid secid; + + #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS + if (!superuser()) +*************** lo_import_internal(text *filename, Oid l +*** 392,404 **** + errmsg("must be superuser to use server-side lo_import()"), + errhint("Anyone can use the client-side lo_import() provided by libpq."))); + #endif + + 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, +--- 411,424 ---- + 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 + */ + fd = PathNameOpenFile(fnamebuf, O_RDONLY | PG_BINARY, 0666); + if (fd < 0) + ereport(ERROR, +*************** lo_import_internal(text *filename, Oid l +*** 409,415 **** + /* + * create an inversion object + */ +! oid = inv_create(lobjOid); + + /* + * read in from the filesystem and write to the inversion object +--- 429,435 ---- + /* + * create an inversion object + */ +! oid = inv_create(lobjOid, secid); + + /* + * read in from the filesystem and write to the inversion object +*************** lo_export(PG_FUNCTION_ARGS) +*** 447,453 **** + int nbytes, + tmp; + char buf[BUFSIZE]; +! char fnamebuf[MAXPGPATH]; + LargeObjectDesc *lobj; + mode_t oumask; + +--- 467,473 ---- + int nbytes, + tmp; + char buf[BUFSIZE]; +! char *fnamebuf = text_to_cstring(filename); + LargeObjectDesc *lobj; + mode_t oumask; + +*************** lo_export(PG_FUNCTION_ARGS) +*** 458,463 **** +--- 478,485 ---- + 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(); + +*************** lo_truncate(PG_FUNCTION_ARGS) +*** 528,533 **** +--- 550,558 ---- + 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 -Nrpc blob/src/backend/nodes/copyfuncs.c sepgsql/src/backend/nodes/copyfuncs.c +*** blob/src/backend/nodes/copyfuncs.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/nodes/copyfuncs.c Tue Dec 15 17:30:25 2009 +*************** CopyScanFields(Scan *from, Scan *newnode +*** 259,264 **** +--- 259,265 ---- + CopyPlanFields((Plan *) from, (Plan *) newnode); + + COPY_SCALAR_FIELD(scanrelid); ++ COPY_SCALAR_FIELD(rowlvPerms); + } + + /* +*************** _copyColumnDef(ColumnDef *from) +*** 2075,2080 **** +--- 2076,2082 ---- + COPY_NODE_FIELD(raw_default); + COPY_NODE_FIELD(cooked_default); + COPY_NODE_FIELD(constraints); ++ COPY_NODE_FIELD(secLabel); + + return newnode; + } +*************** _copyCreateStmt(CreateStmt *from) +*** 2414,2419 **** +--- 2416,2422 ---- + COPY_NODE_FIELD(options); + COPY_SCALAR_FIELD(oncommit); + COPY_STRING_FIELD(tablespacename); ++ COPY_NODE_FIELD(secLabel); + + return newnode; + } +*************** _copyAlterOwnerStmt(AlterOwnerStmt *from +*** 2638,2643 **** +--- 2641,2661 ---- + return newnode; + } + ++ static AlterSecLabelStmt * ++ _copyAlterSecLabelStmt(AlterSecLabelStmt *from) ++ { ++ AlterSecLabelStmt *newnode = makeNode(AlterSecLabelStmt); ++ ++ COPY_SCALAR_FIELD(objectType); ++ COPY_NODE_FIELD(relation); ++ COPY_NODE_FIELD(object); ++ COPY_NODE_FIELD(objarg); ++ COPY_STRING_FIELD(subname); ++ COPY_NODE_FIELD(secLabel); ++ ++ return newnode; ++ } ++ + static RuleStmt * + _copyRuleStmt(RuleStmt *from) + { +*************** _copyCreateSeqStmt(CreateSeqStmt *from) +*** 2887,2892 **** +--- 2905,2911 ---- + + COPY_NODE_FIELD(sequence); + COPY_NODE_FIELD(options); ++ COPY_NODE_FIELD(secLabel); + + return newnode; + } +*************** copyObject(void *from) +*** 3819,3824 **** +--- 3838,3846 ---- + case T_AlterOwnerStmt: + retval = _copyAlterOwnerStmt(from); + break; ++ case T_AlterSecLabelStmt: ++ retval = _copyAlterSecLabelStmt(from); ++ break; + case T_RuleStmt: + retval = _copyRuleStmt(from); + break; +diff -Nrpc blob/src/backend/nodes/equalfuncs.c sepgsql/src/backend/nodes/equalfuncs.c +*** blob/src/backend/nodes/equalfuncs.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/nodes/equalfuncs.c Tue Dec 15 17:30:25 2009 +*************** _equalCreateStmt(CreateStmt *a, CreateSt +*** 1078,1083 **** +--- 1078,1084 ---- + COMPARE_NODE_FIELD(options); + COMPARE_SCALAR_FIELD(oncommit); + COMPARE_STRING_FIELD(tablespacename); ++ COMPARE_NODE_FIELD(secLabel); + + return true; + } +*************** _equalAlterOwnerStmt(AlterOwnerStmt *a, +*** 1271,1276 **** +--- 1272,1290 ---- + } + + static bool ++ _equalAlterSecLabelStmt(AlterSecLabelStmt *a, AlterSecLabelStmt *b) ++ { ++ COMPARE_SCALAR_FIELD(objectType); ++ COMPARE_NODE_FIELD(relation); ++ COMPARE_NODE_FIELD(object); ++ COMPARE_NODE_FIELD(objarg); ++ COMPARE_STRING_FIELD(subname); ++ COMPARE_NODE_FIELD(secLabel); ++ ++ return true; ++ } ++ ++ static bool + _equalRuleStmt(RuleStmt *a, RuleStmt *b) + { + COMPARE_NODE_FIELD(relation); +*************** _equalCreateSeqStmt(CreateSeqStmt *a, Cr +*** 1477,1482 **** +--- 1491,1497 ---- + { + COMPARE_NODE_FIELD(sequence); + COMPARE_NODE_FIELD(options); ++ COMPARE_NODE_FIELD(secLabel); + + return true; + } +*************** _equalColumnDef(ColumnDef *a, ColumnDef +*** 2054,2059 **** +--- 2069,2075 ---- + COMPARE_NODE_FIELD(raw_default); + COMPARE_NODE_FIELD(cooked_default); + COMPARE_NODE_FIELD(constraints); ++ COMPARE_NODE_FIELD(secLabel); + + return true; + } +*************** equal(void *a, void *b) +*** 2596,2601 **** +--- 2612,2620 ---- + case T_AlterOwnerStmt: + retval = _equalAlterOwnerStmt(a, b); + break; ++ case T_AlterSecLabelStmt: ++ retval = _equalAlterSecLabelStmt(a, b); ++ break; + case T_RuleStmt: + retval = _equalRuleStmt(a, b); + break; +diff -Nrpc blob/src/backend/nodes/outfuncs.c sepgsql/src/backend/nodes/outfuncs.c +*** blob/src/backend/nodes/outfuncs.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/nodes/outfuncs.c Tue Dec 15 17:30:25 2009 +*************** _outScanInfo(StringInfo str, Scan *node) +*** 285,290 **** +--- 285,291 ---- + _outPlanInfo(str, (Plan *) node); + + WRITE_UINT_FIELD(scanrelid); ++ WRITE_UINT_FIELD(rowlvPerms); + } + + /* +*************** _outRelOptInfo(StringInfo str, RelOptInf +*** 1534,1539 **** +--- 1535,1541 ---- + WRITE_BOOL_FIELD(has_eclass_joins); + WRITE_BITMAPSET_FIELD(index_outer_relids); + WRITE_NODE_FIELD(index_inner_paths); ++ WRITE_UINT_FIELD(rowlvPerms); + } + + static void +*************** _outCreateStmt(StringInfo str, CreateStm +*** 1717,1722 **** +--- 1719,1725 ---- + WRITE_NODE_FIELD(options); + WRITE_ENUM_FIELD(oncommit, OnCommitAction); + WRITE_STRING_FIELD(tablespacename); ++ WRITE_NODE_FIELD(secLabel); + } + + static void +*************** _outColumnDef(StringInfo str, ColumnDef +*** 1839,1844 **** +--- 1842,1848 ---- + WRITE_NODE_FIELD(raw_default); + WRITE_NODE_FIELD(cooked_default); + WRITE_NODE_FIELD(constraints); ++ WRITE_NODE_FIELD(secLabel); + } + + static void +diff -Nrpc blob/src/backend/optimizer/plan/createplan.c sepgsql/src/backend/optimizer/plan/createplan.c +*** blob/src/backend/optimizer/plan/createplan.c Sun Sep 6 19:40:49 2009 +--- sepgsql/src/backend/optimizer/plan/createplan.c Sun Sep 6 19:53:10 2009 +*************** create_scan_plan(PlannerInfo *root, Path +*** 305,310 **** +--- 305,313 ---- + break; + } + ++ /* Copy of row-level permissions to Scan node */ ++ ((Scan *)plan)->rowlvPerms = rel->rowlvPerms; ++ + /* + * If there are any pseudoconstant clauses attached to this node, insert a + * gating Result node that evaluates the pseudoconstants as one-time +diff -Nrpc blob/src/backend/optimizer/util/clauses.c sepgsql/src/backend/optimizer/util/clauses.c +*** blob/src/backend/optimizer/util/clauses.c Thu Mar 18 09:43:03 2010 +--- sepgsql/src/backend/optimizer/util/clauses.c Thu Mar 18 01:55:40 2010 +*************** +*** 38,43 **** +--- 38,44 ---- + #include "parser/parse_coerce.h" + #include "parser/parse_func.h" + #include "rewrite/rewriteManip.h" ++ #include "security/sepgsql.h" + #include "tcop/tcopprot.h" + #include "utils/acl.h" + #include "utils/builtins.h" +*************** inline_function(Oid funcid, Oid result_t +*** 3503,3508 **** +--- 3504,3510 ---- + funcform->prosecdef || + funcform->proretset || + !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || ++ !sepgsql_proc_hint_inlined(func_tuple) || + funcform->pronargs != list_length(args)) + return NULL; + +*************** inline_set_returning_function(PlannerInf +*** 3974,3979 **** +--- 3976,3982 ---- + funcform->prosecdef || + !funcform->proretset || + !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || ++ !sepgsql_proc_hint_inlined(func_tuple) || + funcform->pronargs != list_length(fexpr->args)) + { + ReleaseSysCache(func_tuple); +diff -Nrpc blob/src/backend/optimizer/util/relnode.c sepgsql/src/backend/optimizer/util/relnode.c +*** blob/src/backend/optimizer/util/relnode.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/optimizer/util/relnode.c Wed Jul 15 19:39:56 2009 +*************** +*** 21,26 **** +--- 21,27 ---- + #include "optimizer/plancat.h" + #include "optimizer/restrictinfo.h" + #include "parser/parsetree.h" ++ #include "security/rowlevel.h" + #include "utils/hsearch.h" + + +*************** build_simple_rel(PlannerInfo *root, int +*** 91,96 **** +--- 92,98 ---- + rel->has_eclass_joins = false; + rel->index_outer_relids = NULL; + rel->index_inner_paths = NIL; ++ rel->rowlvPerms = rowlvSetupPermissions(rte); + + /* Check type of rtable entry */ + switch (rte->rtekind) +diff -Nrpc blob/src/backend/parser/analyze.c sepgsql/src/backend/parser/analyze.c +*** blob/src/backend/parser/analyze.c Thu Mar 18 09:43:03 2010 +--- sepgsql/src/backend/parser/analyze.c Thu Mar 18 01:55:40 2010 +*************** +*** 25,30 **** +--- 25,31 ---- + #include "postgres.h" + + #include "access/sysattr.h" ++ #include "catalog/heap.h" + #include "catalog/pg_type.h" + #include "nodes/makefuncs.h" + #include "nodes/nodeFuncs.h" +*************** transformInsertStmt(ParseState *pstate, +*** 660,666 **** + tle = makeTargetEntry(expr, + attr_num, + col->name, +! false); + qry->targetList = lappend(qry->targetList, tle); + + rte->modifiedCols = bms_add_member(rte->modifiedCols, +--- 661,667 ---- + tle = makeTargetEntry(expr, + attr_num, + col->name, +! attr_num < 0 ? true : false); + qry->targetList = lappend(qry->targetList, tle); + + rte->modifiedCols = bms_add_member(rte->modifiedCols, +*************** transformInsertRow(ParseState *pstate, L +*** 775,780 **** +--- 776,823 ---- + return result; + } + ++ static void ++ transformSelectIntoSystemColumn(ParseState *pstate, Query *qry) ++ { ++ ListCell *l; ++ uint32 system_attrs = 0; ++ bool relhasoids ++ = interpretOidsOption(qry->intoClause->options); ++ ++ foreach (l, qry->targetList) ++ { ++ Form_pg_attribute attr; ++ TargetEntry *tle = lfirst(l); ++ ++ if (tle->resjunk) ++ continue; ++ ++ attr = SystemAttributeByName(tle->resname, relhasoids); ++ if (attr && SystemAttributeIsWritable(attr->attnum)) ++ { ++ uint32 mask = (1<<(-attr->attnum)); ++ ++ /* duplication checks */ ++ if (system_attrs & mask) ++ continue; ++ system_attrs |= mask; ++ ++ if (exprType((Node *) tle->expr) != attr->atttypid) ++ { ++ tle->expr = ++ (Expr *) coerce_to_target_type(pstate, ++ (Node *) tle->expr, ++ exprType((Node *) tle->expr), ++ attr->atttypid, ++ attr->atttypmod, ++ COERCION_IMPLICIT, ++ COERCE_IMPLICIT_CAST, ++ -1); ++ } ++ tle->resjunk = true; ++ } ++ } ++ } + + /* + * transformSelectStmt - +*************** transformSelectStmt(ParseState *pstate, +*** 879,884 **** +--- 922,928 ---- + if (stmt->intoClause) + { + qry->intoClause = stmt->intoClause; ++ transformSelectIntoSystemColumn(pstate, qry); + if (stmt->intoClause->colNames) + applyColumnNames(qry->targetList, stmt->intoClause->colNames); + } +diff -Nrpc blob/src/backend/parser/gram.y sepgsql/src/backend/parser/gram.y +*** blob/src/backend/parser/gram.y Fri Dec 18 09:40:55 2009 +--- sepgsql/src/backend/parser/gram.y Thu Dec 24 21:59:25 2009 +*************** +*** 58,63 **** +--- 58,64 ---- + #include "nodes/makefuncs.h" + #include "nodes/nodeFuncs.h" + #include "parser/gramparse.h" ++ #include "security/sepgsql.h" + #include "storage/lmgr.h" + #include "utils/date.h" + #include "utils/datetime.h" +*************** static TypeName *TableFuncTypeName(List +*** 184,190 **** + %type stmt schema_stmt + AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt + AlterForeignServerStmt AlterGroupStmt +! AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt + AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt + AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt + ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt +--- 185,191 ---- + %type stmt schema_stmt + AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterFdwStmt + AlterForeignServerStmt AlterGroupStmt +! AlterObjectSchemaStmt AlterOwnerStmt AlterSecLabelStmt AlterSeqStmt AlterTableStmt + AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt + AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt + ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt +*************** static TypeName *TableFuncTypeName(List +*** 402,407 **** +--- 403,412 ---- + %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 +*************** static TypeName *TableFuncTypeName(List +*** 437,443 **** + 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 + CREATEROLE CREATEUSER CROSS CSV CURRENT_P + CURRENT_CATALOG CURRENT_DATE CURRENT_ROLE CURRENT_SCHEMA + CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE +--- 442,448 ---- + CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE + CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT + COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS +! 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 +*************** stmt : +*** 608,613 **** +--- 613,619 ---- + | AlterGroupStmt + | AlterObjectSchemaStmt + | AlterOwnerStmt ++ | AlterSecLabelStmt + | AlterSeqStmt + | AlterTableStmt + | AlterRoleSetStmt +*************** DropGroupStmt: +*** 1042,1048 **** + *****************************************************************************/ + + CreateSchemaStmt: +! CREATE SCHEMA OptSchemaName AUTHORIZATION RoleId OptSchemaEltList + { + CreateSchemaStmt *n = makeNode(CreateSchemaStmt); + /* One can omit the schema name or the authorization id. */ +--- 1048,1054 ---- + *****************************************************************************/ + + CreateSchemaStmt: +! CREATE SCHEMA OptSchemaName AUTHORIZATION RoleId OptSecLabel OptSchemaEltList + { + CreateSchemaStmt *n = makeNode(CreateSchemaStmt); + /* One can omit the schema name or the authorization id. */ +*************** CreateSchemaStmt: +*** 1051,1066 **** + else + n->schemaname = $5; + n->authid = $5; +! n->schemaElts = $6; + $$ = (Node *)n; + } +! | CREATE SCHEMA ColId OptSchemaEltList + { + CreateSchemaStmt *n = makeNode(CreateSchemaStmt); + /* ...but not both */ + n->schemaname = $3; + n->authid = NULL; +! n->schemaElts = $4; + $$ = (Node *)n; + } + ; +--- 1057,1074 ---- + else + n->schemaname = $5; + n->authid = $5; +! n->secLabel = $6; +! n->schemaElts = $7; + $$ = (Node *)n; + } +! | CREATE SCHEMA ColId OptSecLabel OptSchemaEltList + { + CreateSchemaStmt *n = makeNode(CreateSchemaStmt); + /* ...but not both */ + n->schemaname = $3; + n->authid = NULL; +! n->secLabel = $4; +! n->schemaElts = $5; + $$ = (Node *)n; + } + ; +*************** opt_using: +*** 2037,2043 **** + *****************************************************************************/ + + CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' +! OptInherit OptWith OnCommitOption OptTableSpace + { + CreateStmt *n = makeNode(CreateStmt); + $4->istemp = $2; +--- 2045,2051 ---- + *****************************************************************************/ + + CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')' +! OptInherit OptWith OnCommitOption OptTableSpace OptTableSecLabel + { + CreateStmt *n = makeNode(CreateStmt); + $4->istemp = $2; +*************** CreateStmt: CREATE OptTemp TABLE qualifi +*** 2048,2057 **** + n->options = $9; + n->oncommit = $10; + n->tablespacename = $11; + $$ = (Node *)n; + } + | CREATE OptTemp TABLE qualified_name OF qualified_name +! '(' OptTableElementList ')' OptWith OnCommitOption OptTableSpace + { + /* SQL99 CREATE TABLE OF (cols) seems to be satisfied + * by our inheritance capabilities. Let's try it... +--- 2056,2066 ---- + 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 OptTableSecLabel + { + /* SQL99 CREATE TABLE OF (cols) seems to be satisfied + * by our inheritance capabilities. Let's try it... +*************** CreateStmt: CREATE OptTemp TABLE qualifi +*** 2065,2070 **** +--- 2074,2080 ---- + n->options = $10; + n->oncommit = $11; + n->tablespacename = $12; ++ n->secLabel = $13; + $$ = (Node *)n; + } + ; +*************** columnDef: ColId Typename ColQualList +*** 2114,2119 **** +--- 2124,2130 ---- + n->typename = $2; + n->constraints = $3; + n->is_local = true; ++ n->secLabel = NULL; + $$ = (Node *)n; + } + ; +*************** opt_with_data: +*** 2585,2596 **** + *****************************************************************************/ + + CreateSeqStmt: +! CREATE OptTemp SEQUENCE qualified_name OptSeqOptList + { + CreateSeqStmt *n = makeNode(CreateSeqStmt); + $4->istemp = $2; + n->sequence = $4; + n->options = $5; + $$ = (Node *)n; + } + ; +--- 2596,2608 ---- + *****************************************************************************/ + + CreateSeqStmt: +! 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; + } + ; +*************** createfunc_opt_item: +*** 4893,4898 **** +--- 4905,4914 ---- + { + $$ = makeDefElem("window", (Node *)makeInteger(TRUE)); + } ++ | SecLabelItem ++ { ++ $$ = makeDefElem("security_context", $1); ++ } + | common_func_opt_item + { + $$ = $1; +*************** AlterOwnerStmt: ALTER AGGREGATE func_nam +*** 5607,5612 **** +--- 5623,5723 ---- + } + ; + ++ /***************************************************************************** ++ * ++ * 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); ++ } ++ ; + + /***************************************************************************** + * +*************** createdb_opt_item: +*** 6049,6054 **** +--- 6160,6169 ---- + { + $$ = makeDefElem("owner", NULL); + } ++ | SecLabelItem ++ { ++ $$ = makeDefElem("security_context", $1); ++ } + ; + + /* +*************** unreserved_keyword: +*** 10175,10180 **** +--- 10290,10296 ---- + | CONNECTION + | CONSTRAINTS + | CONTENT_P ++ | CONTEXT_P + | CONTINUE_P + | CONVERSION_P + | COPY +diff -Nrpc blob/src/backend/parser/parse_target.c sepgsql/src/backend/parser/parse_target.c +*** blob/src/backend/parser/parse_target.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/parser/parse_target.c Wed Jul 15 19:38:52 2009 +*************** +*** 14,19 **** +--- 14,20 ---- + */ + #include "postgres.h" + ++ #include "catalog/heap.h" + #include "catalog/pg_type.h" + #include "commands/dbcommands.h" + #include "funcapi.h" +*************** transformAssignedExpr(ParseState *pstate +*** 361,376 **** + Oid attrtype; /* type of target column */ + int32 attrtypmod; + Relation rd = pstate->p_target_relation; + + Assert(rd != NULL); +! if (attrno <= 0) +! ereport(ERROR, +! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), +! errmsg("cannot assign to system column \"%s\"", +! colname), +! parser_errposition(pstate, location))); +! attrtype = attnumTypeId(rd, attrno); +! attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; + + /* + * If the expression is a DEFAULT placeholder, insert the attribute's +--- 362,394 ---- + Oid attrtype; /* type of target column */ + int32 attrtypmod; + Relation rd = pstate->p_target_relation; ++ bool relhasoids = RelationGetForm(rd)->relhasoids; + + Assert(rd != NULL); +! if (attrno > 0) +! { +! attrtype = attnumTypeId(rd, attrno); +! attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; +! } +! else +! { +! Form_pg_attribute attForm +! = SystemAttributeDefinition(attrno, relhasoids); +! if (attForm && SystemAttributeIsWritable(attrno)) +! { +! attrtype = attForm->atttypid; +! attrtypmod = attForm->atttypmod; +! } +! else +! { +! ereport(ERROR, +! (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), +! errmsg("cannot assign to system column \"%s\"", +! colname), +! parser_errposition(pstate, location))); +! return NULL; /* compiler kindness */ +! } +! } + + /* + * If the expression is a DEFAULT placeholder, insert the attribute's +*************** updateTargetListEntry(ParseState *pstate +*** 515,520 **** +--- 533,541 ---- + */ + tle->resno = (AttrNumber) attrno; + tle->resname = colname; ++ ++ if (SystemAttributeIsWritable(attrno)) ++ tle->resjunk = true; + } + + +*************** checkInsertTargets(ParseState *pstate, L +*** 789,794 **** +--- 810,816 ---- + Bitmapset *wholecols = NULL; + Bitmapset *partialcols = NULL; + ListCell *tl; ++ uint32 system_attrs = 0UL; + + foreach(tl, cols) + { +*************** checkInsertTargets(ParseState *pstate, L +*** 797,810 **** + int attrno; + + /* Lookup column name, ereport on failure */ +! attrno = attnameAttNum(pstate->p_target_relation, name, false); + if (attrno == InvalidAttrNumber) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + name, + RelationGetRelationName(pstate->p_target_relation)), + parser_errposition(pstate, col->location))); + + /* + * Check for duplicates, but only of whole columns --- we allow +--- 819,855 ---- + int attrno; + + /* Lookup column name, ereport on failure */ +! attrno = attnameAttNum(pstate->p_target_relation, name, true); + if (attrno == InvalidAttrNumber) ++ { + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + name, + RelationGetRelationName(pstate->p_target_relation)), + parser_errposition(pstate, col->location))); ++ } ++ else if (attrno < 0) ++ { ++ if (SystemAttributeIsWritable(attrno)) ++ { ++ uint32 mask = (1<<(-attrno)); ++ ++ if ((system_attrs & mask) != 0) ++ ereport(ERROR, ++ (errcode(ERRCODE_DUPLICATE_COLUMN), ++ errmsg("column \"%s\" specified more than once", name), ++ parser_errposition(pstate, col->location))); ++ system_attrs |= mask; ++ *attrnos = lappend_int(*attrnos, attrno); ++ continue; ++ } ++ ereport(ERROR, ++ (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), ++ errmsg("column \"%s\" of relation \"%s\" is system column", ++ name, RelationGetRelationName(pstate->p_target_relation)), ++ parser_errposition(pstate, col->location))); ++ } + + /* + * Check for duplicates, but only of whole columns --- we allow +diff -Nrpc blob/src/backend/parser/parse_utilcmd.c sepgsql/src/backend/parser/parse_utilcmd.c +*** blob/src/backend/parser/parse_utilcmd.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/parser/parse_utilcmd.c Tue Dec 15 17:30:25 2009 +*************** +*** 49,54 **** +--- 49,55 ---- + #include "parser/parse_type.h" + #include "parser/parse_utilcmd.h" + #include "rewrite/rewriteManip.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/lsyscache.h" +*************** transformInhRelation(ParseState *pstate, +*** 565,570 **** +--- 566,573 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(relation)); ++ /* SELinux checks */ ++ sepgsql_relation_copy_definition(RelationGetRelid(relation)); + + tupleDesc = RelationGetDescr(relation); + constr = tupleDesc->constr; +diff -Nrpc blob/src/backend/postmaster/autovacuum.c sepgsql/src/backend/postmaster/autovacuum.c +*** blob/src/backend/postmaster/autovacuum.c Sun Sep 6 19:40:49 2009 +--- sepgsql/src/backend/postmaster/autovacuum.c Sun Sep 6 19:53:10 2009 +*************** do_autovacuum(void) +*** 2004,2010 **** + object.classId = RelationRelationId; + object.objectId = relid; + object.objectSubId = 0; +! performDeletion(&object, DROP_CASCADE); + } + else + { +--- 2004,2010 ---- + object.classId = RelationRelationId; + object.objectId = relid; + object.objectSubId = 0; +! performDeletionNoPerms(&object, DROP_CASCADE); + } + else + { +diff -Nrpc blob/src/backend/postmaster/postmaster.c sepgsql/src/backend/postmaster/postmaster.c +*** blob/src/backend/postmaster/postmaster.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/postmaster/postmaster.c Sun Dec 20 00:41:22 2009 +*************** +*** 108,113 **** +--- 108,114 ---- + #include "postmaster/pgarch.h" + #include "postmaster/postmaster.h" + #include "postmaster/syslogger.h" ++ #include "security/sepgsql.h" + #include "storage/fd.h" + #include "storage/ipc.h" + #include "storage/pg_shmem.h" +*************** static pid_t StartupPID = 0, +*** 209,215 **** + AutoVacPID = 0, + PgArchPID = 0, + PgStatPID = 0, +! SysLoggerPID = 0; + + /* Startup/shutdown state */ + #define NoShutdown 0 +--- 210,217 ---- + AutoVacPID = 0, + PgArchPID = 0, + PgStatPID = 0, +! SysLoggerPID = 0, +! sepgsqlReceiverPID = 0; + + /* Startup/shutdown state */ + #define NoShutdown 0 +*************** static void ShmemBackendArrayRemove(Back +*** 445,450 **** +--- 447,453 ---- + #define StartupDataBase() StartChildProcess(StartupProcess) + #define StartBackgroundWriter() StartChildProcess(BgWriterProcess) + #define StartWalWriter() StartChildProcess(WalWriterProcess) ++ #define StartSELinuxReceiver() StartChildProcess(SelinuxReceiverProcess) + + /* Macros to check exit status of a child process */ + #define EXIT_STATUS_0(st) ((st) == 0) +*************** ServerLoop(void) +*** 1436,1441 **** +--- 1439,1449 ---- + if (PgStatPID == 0 && pmState == PM_RUN) + PgStatPID = pgstat_start(); + ++ /* if we have lost the selinux netlink receiver, try to start */ ++ if (sepgsqlReceiverPID == 0 && pmState == PM_RUN && ++ sepgsqlReceiverStart()) ++ sepgsqlReceiverPID = StartSELinuxReceiver(); ++ + /* If we need to signal the autovacuum launcher, do so now */ + if (avlauncher_needs_signal) + { +*************** SIGHUP_handler(SIGNAL_ARGS) +*** 2055,2060 **** +--- 2063,2070 ---- + signal_child(SysLoggerPID, SIGHUP); + if (PgStatPID != 0) + signal_child(PgStatPID, SIGHUP); ++ if (sepgsqlReceiverPID != 0) ++ signal_child(sepgsqlReceiverPID, SIGHUP); + + /* Reload authentication config files too */ + if (!load_hba()) +*************** pmdie(SIGNAL_ARGS) +*** 2115,2120 **** +--- 2125,2133 ---- + /* and the walwriter too */ + if (WalWriterPID != 0) + signal_child(WalWriterPID, SIGTERM); ++ /* and the selinux netlink receiver too */ ++ if (sepgsqlReceiverPID != 0) ++ signal_child(sepgsqlReceiverPID, SIGTERM); + pmState = PM_WAIT_BACKUP; + } + +*************** pmdie(SIGNAL_ARGS) +*** 2162,2167 **** +--- 2175,2183 ---- + /* and the walwriter too */ + if (WalWriterPID != 0) + signal_child(WalWriterPID, SIGTERM); ++ /* and the selinux netlink receiver too */ ++ if (sepgsqlReceiverPID != 0) ++ signal_child(sepgsqlReceiverPID, SIGTERM); + pmState = PM_WAIT_BACKENDS; + } + +*************** pmdie(SIGNAL_ARGS) +*** 2195,2200 **** +--- 2211,2218 ---- + signal_child(PgArchPID, SIGQUIT); + if (PgStatPID != 0) + signal_child(PgStatPID, SIGQUIT); ++ if (sepgsqlReceiverPID != 0) ++ signal_child(sepgsqlReceiverPID, SIGQUIT); + ExitPostmaster(0); + break; + } +*************** reaper(SIGNAL_ARGS) +*** 2457,2462 **** +--- 2475,2490 ---- + continue; + } + ++ /* Was it the selinux netlink receiver process? */ ++ if (pid == sepgsqlReceiverPID) ++ { ++ sepgsqlReceiverPID = 0; ++ if (!EXIT_STATUS_0(exitstatus)) ++ LogChildExit(LOG, _("SELinux netlink receiver process"), ++ pid, exitstatus); ++ continue; ++ } ++ + /* + * Else do standard backend child cleanup. + */ +*************** HandleChildCrash(int pid, int exitstatus +*** 2648,2653 **** +--- 2676,2693 ---- + signal_child(AutoVacPID, (SendStop ? SIGSTOP : SIGQUIT)); + } + ++ /* Take care of the selinux netlink receiver too */ ++ if (pid == sepgsqlReceiverPID) ++ sepgsqlReceiverPID = 0; ++ else if (sepgsqlReceiverPID != 0 && !FatalError) ++ { ++ ereport(DEBUG2, ++ (errmsg_internal("sending %s to process %d", ++ (SendStop ? "SIGSTOP" : "SIGQUIT"), ++ (int) sepgsqlReceiverPID))); ++ signal_child(sepgsqlReceiverPID, (SendStop ? SIGSTOP : SIGQUIT)); ++ } ++ + /* + * Force a power-cycle of the pgarch process too. (This isn't absolutely + * necessary, but it seems like a good idea for robustness, and it +*************** PostmasterStateMachine(void) +*** 2780,2786 **** + StartupPID == 0 && + (BgWriterPID == 0 || !FatalError) && + WalWriterPID == 0 && +! AutoVacPID == 0) + { + if (FatalError) + { +--- 2820,2827 ---- + StartupPID == 0 && + (BgWriterPID == 0 || !FatalError) && + WalWriterPID == 0 && +! AutoVacPID == 0 && +! sepgsqlReceiverPID == 0) + { + if (FatalError) + { +*************** StartChildProcess(AuxProcType type) +*** 4323,4328 **** +--- 4364,4375 ---- + ereport(LOG, + (errmsg("could not fork WAL writer process: %m"))); + break; ++ #ifdef HAVE_SELINUX ++ case SelinuxReceiverProcess: ++ ereport(LOG, ++ (errmsg("could not fork selinux receiver process: %m"))); ++ break; ++ #endif + default: + ereport(LOG, + (errmsg("could not fork process: %m"))); +diff -Nrpc blob/src/backend/rewrite/rewriteDefine.c sepgsql/src/backend/rewrite/rewriteDefine.c +*** blob/src/backend/rewrite/rewriteDefine.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/rewrite/rewriteDefine.c Fri Sep 18 14:51:00 2009 +*************** +*** 27,32 **** +--- 27,33 ---- + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteManip.h" + #include "rewrite/rewriteSupport.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/inval.h" +*************** DefineQueryRewrite(char *rulename, +*** 266,271 **** +--- 267,275 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + RelationGetRelationName(event_relation)); + ++ /* SELinux checks */ ++ sepgsql_rule_create(event_relid, rulename); ++ + /* + * No rule actions that modify OLD or NEW + */ +diff -Nrpc blob/src/backend/rewrite/rewriteRemove.c sepgsql/src/backend/rewrite/rewriteRemove.c +*** blob/src/backend/rewrite/rewriteRemove.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/rewrite/rewriteRemove.c Fri Sep 18 14:51:00 2009 +*************** +*** 22,27 **** +--- 22,28 ---- + #include "catalog/pg_rewrite.h" + #include "miscadmin.h" + #include "rewrite/rewriteRemove.h" ++ #include "security/sepgsql.h" + #include "utils/acl.h" + #include "utils/fmgroids.h" + #include "utils/inval.h" +*************** RemoveRewriteRule(Oid owningRel, const c +*** 78,83 **** +--- 79,87 ---- + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, + get_rel_name(eventRelationOid)); + ++ /* SELinux checks */ ++ sepgsql_rule_drop(eventRelationOid, ruleName); ++ + /* + * Do the deletion + */ +diff -Nrpc blob/src/backend/security/Makefile sepgsql/src/backend/security/Makefile +*** blob/src/backend/security/Makefile Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/security/Makefile Wed Jul 15 19:39:56 2009 +*************** +*** 0 **** +--- 1,13 ---- ++ # ++ # Makefile for the enhanced security subsystem ++ # ++ ++ subdir = src/backend/security ++ top_builddir = ../../.. ++ include $(top_builddir)/src/Makefile.global ++ ++ SUBDIRS = sepgsql ++ ++ OBJS = rowlevel.o ++ ++ include $(top_srcdir)/src/backend/common.mk +diff -Nrpc blob/src/backend/security/rowlevel.c sepgsql/src/backend/security/rowlevel.c +*** blob/src/backend/security/rowlevel.c Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/security/rowlevel.c Thu Jul 16 17:22:29 2009 +*************** +*** 0 **** +--- 1,121 ---- ++ /* ++ * src/backend/security/common.c ++ * common facilities for row-level access controls both of DAC and MAC ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++ #include "postgres.h" ++ ++ #include "catalog/pg_security.h" ++ #include "security/rowlevel.h" ++ #include "security/sepgsql.h" ++ #include "storage/bufmgr.h" ++ #include "storage/bufpage.h" ++ #include "utils/rel.h" ++ #include "utils/tqual.h" ++ ++ /* ++ * rowlvGetPerformingMode ++ * rowlvSetPerformingMode ++ * enables to control the behavior of row-level features ++ * when violated tuples are detected. ++ * The default is ROWLV_FILTER_MODE which filters out ++ * violated tuples from result set, ROWLV_ABORT_MODE ++ * raises an error and ROWLV_BYPASS_MODE do nothing. ++ */ ++ static int rowlv_mode = ROWLV_FILTER_MODE; ++ ++ int rowlvGetPerformingMode(void) ++ { ++ return rowlv_mode; ++ } ++ ++ int rowlvSetPerformingMode(int new_mode) ++ { ++ int old_mode = new_mode; ++ ++ rowlv_mode = new_mode; ++ ++ return old_mode; ++ } ++ ++ /* ++ * rowlvSetupPermissions ++ * setups permissions for row-level access controls. ++ */ ++ uint32 ++ rowlvSetupPermissions(RangeTblEntry *rte) ++ { ++ return sepgsqlSetupTuplePerms(rte); ++ } ++ ++ /* ++ * rowlvExecScan ++ * a hook to filter out invisible/untouchable tuples. ++ */ ++ static bool ++ rowlvExecScan(Scan *scan, Relation rel, TupleTableSlot *slot, bool abort) ++ { ++ HeapTuple tuple; ++ uint32 perms = scan->rowlvPerms; ++ ++ if (!perms) ++ return true; ++ ++ tuple = ExecMaterializeSlot(slot); ++ ++ return sepgsqlExecScan(rel, tuple, perms, abort); ++ } ++ ++ bool ++ rowlvExecScanFilter(Scan *scan, Relation rel, TupleTableSlot *slot) ++ { ++ if (!rel || !scan->rowlvPerms || rowlv_mode != ROWLV_FILTER_MODE) ++ return true; ++ ++ return rowlvExecScan(scan, rel, slot, false); ++ } ++ ++ void ++ rowlvExecScanAbort(Scan *scan, Relation rel, TupleTableSlot *slot) ++ { ++ if (!rel || !scan->rowlvPerms || rowlv_mode != ROWLV_ABORT_MODE) ++ return; ++ ++ rowlvExecScan(scan, rel, slot, true); ++ } ++ ++ /* ++ * rowlvCopyToTuple ++ * checks permission on fetched tuple ++ */ ++ bool ++ rowlvCopyToTuple(Relation rel, HeapTuple tuple) ++ { ++ if (!sepgsqlExecScan(rel, tuple, SEPG_DB_TUPLE__SELECT, false)) ++ return false; ++ ++ return true; ++ } ++ ++ /* ++ * rowlvHeapTupleInsert ++ * assign default security attribute, and check permission ++ * if necessary. ++ */ ++ void ++ rowlvHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal) ++ { ++ sepgsqlHeapTupleInsert(rel, newtup, internal); ++ } ++ ++ /* ++ * rowlvHeapTupleUpdate ++ * check permission to change security attribute, if necesary ++ */ ++ void ++ rowlvHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup) ++ { ++ sepgsqlHeapTupleUpdate(rel, otid, newtup); ++ } +diff -Nrpc blob/src/backend/security/sepgsql/Makefile sepgsql/src/backend/security/sepgsql/Makefile +*** blob/src/backend/security/sepgsql/Makefile Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/security/sepgsql/Makefile Sun Dec 20 00:41:22 2009 +*************** +*** 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 -Nrpc blob/src/backend/security/sepgsql/avc.c sepgsql/src/backend/security/sepgsql/avc.c +*** blob/src/backend/security/sepgsql/avc.c Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/security/sepgsql/avc.c Thu Dec 10 10:36:18 2009 +*************** +*** 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 -Nrpc blob/src/backend/security/sepgsql/bridge.c sepgsql/src/backend/security/sepgsql/bridge.c +*** blob/src/backend/security/sepgsql/bridge.c Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/security/sepgsql/bridge.c Thu Mar 18 10:00:36 2010 +*************** +*** 0 **** +--- 1,2922 ---- ++ /* ++ * 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 ++ #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 -Nrpc blob/src/backend/security/sepgsql/checker.c sepgsql/src/backend/security/sepgsql/checker.c +*** blob/src/backend/security/sepgsql/checker.c Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/security/sepgsql/checker.c Sun Dec 20 18:14:37 2009 +*************** +*** 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 -Nrpc blob/src/backend/security/sepgsql/dummy.c sepgsql/src/backend/security/sepgsql/dummy.c +*** blob/src/backend/security/sepgsql/dummy.c Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/security/sepgsql/dummy.c Wed Jul 15 19:39:56 2009 +*************** +*** 0 **** +--- 1,79 ---- ++ /* ++ * src/backend/utils/sepgsql/dummy.c ++ * A set of stubs when SE-PostgreSQL is not activated ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++ #include "postgres.h" ++ ++ #include "security/sepgsql.h" ++ ++ static Datum ++ unavailable_function(const char *fn_name) ++ { ++ ereport(ERROR, ++ (errcode(ERRCODE_SELINUX_ERROR), ++ errmsg("function \"%s\" is not available", fn_name))); ++ PG_RETURN_VOID(); ++ } ++ ++ Datum ++ sepgsql_getcon(PG_FUNCTION_ARGS) ++ { ++ return unavailable_function(__FUNCTION__); ++ } ++ ++ Datum ++ sepgsql_server_getcon(PG_FUNCTION_ARGS) ++ { ++ return unavailable_function(__FUNCTION__); ++ } ++ ++ Datum ++ sepgsql_get_user(PG_FUNCTION_ARGS) ++ { ++ return unavailable_function(__FUNCTION__); ++ } ++ ++ Datum ++ sepgsql_get_role(PG_FUNCTION_ARGS) ++ { ++ return unavailable_function(__FUNCTION__); ++ } ++ ++ Datum ++ sepgsql_get_type(PG_FUNCTION_ARGS) ++ { ++ return unavailable_function(__FUNCTION__); ++ } ++ ++ Datum ++ sepgsql_get_range(PG_FUNCTION_ARGS) ++ { ++ return unavailable_function(__FUNCTION__); ++ } ++ ++ Datum ++ sepgsql_set_user(PG_FUNCTION_ARGS) ++ { ++ return unavailable_function(__FUNCTION__); ++ } ++ ++ Datum ++ sepgsql_set_role(PG_FUNCTION_ARGS) ++ { ++ return unavailable_function(__FUNCTION__); ++ } ++ ++ Datum ++ sepgsql_set_type(PG_FUNCTION_ARGS) ++ { ++ return unavailable_function(__FUNCTION__); ++ } ++ ++ Datum ++ sepgsql_set_range(PG_FUNCTION_ARGS) ++ { ++ return unavailable_function(__FUNCTION__); ++ } +diff -Nrpc blob/src/backend/security/sepgsql/hooks.c sepgsql/src/backend/security/sepgsql/hooks.c +*** blob/src/backend/security/sepgsql/hooks.c Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/security/sepgsql/hooks.c Fri Dec 18 09:11:54 2009 +*************** +*** 0 **** +--- 1,239 ---- ++ /* ++ * src/backend/security/sepgsql/hooks.c ++ * SE-PostgreSQL security hooks ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++ #include "postgres.h" ++ ++ #include "catalog/indexing.h" ++ #include "catalog/namespace.h" ++ #include "catalog/pg_database.h" ++ #include "catalog/pg_foreign_data_wrapper.h" ++ #include "catalog/pg_language.h" ++ #include "catalog/pg_largeobject.h" ++ #include "catalog/pg_namespace.h" ++ #include "catalog/pg_opclass.h" ++ #include "catalog/pg_operator.h" ++ #include "catalog/pg_opfamily.h" ++ #include "catalog/pg_proc.h" ++ #include "catalog/pg_security.h" ++ #include "catalog/pg_trigger.h" ++ #include "catalog/pg_ts_dict.h" ++ #include "catalog/pg_ts_parser.h" ++ #include "catalog/pg_ts_template.h" ++ #include "catalog/pg_type.h" ++ #include "catalog/pg_security.h" ++ #include "commands/dbcommands.h" ++ #include "miscadmin.h" ++ #include "security/sepgsql.h" ++ #include "utils/builtins.h" ++ #include "utils/fmgroids.h" ++ #include "utils/lsyscache.h" ++ #include "utils/syscache.h" ++ #include "utils/tqual.h" ++ ++ /* ------------------------------------------------------------ * ++ * Hooks corresponding to db_blob object class ++ * ------------------------------------------------------------ */ ++ ++ /* ++ * sepgsqlCheckBlobCreate ++ * assigns a default security label and checks db_blob:{create} ++ */ ++ void ++ sepgsqlCheckBlobCreate(Relation rel, HeapTuple lotup) ++ { ++ sepgsql_sid_t loSid; ++ Oid relid = RelationGetRelid(rel); ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* set a default security context */ ++ sepgsqlSetDefaultSecid(rel, lotup); ++ ++ loSid = sepgsqlGetTupleSecid(relid, lotup, NULL); ++ sepgsqlClientHasPerms(loSid, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_DB_BLOB__CREATE, ++ NULL, true); ++ } ++ ++ /* ++ * sepgsqlCheckBlobDrop ++ * checks db_blob:{drop} permission ++ */ ++ void ++ sepgsqlCheckBlobDrop(Relation rel, HeapTuple lotup) ++ { ++ sepgsql_sid_t loSid; ++ Oid relid = RelationGetRelid(rel); ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ loSid = sepgsqlGetTupleSecid(relid, lotup, NULL); ++ sepgsqlClientHasPerms(loSid, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_DB_BLOB__DROP, ++ NULL, true); ++ } ++ ++ /* ++ * sepgsqlCheckBlobRead ++ * checks db_blob:{read} permission ++ */ ++ void ++ sepgsqlCheckBlobRead(LargeObjectDesc *lobj) ++ { ++ sepgsql_sid_t loSid; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ loSid.relid = LargeObjectRelationId; ++ loSid.secid = lobj->secid; ++ sepgsqlClientHasPerms(loSid, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_DB_BLOB__READ, ++ NULL, true); ++ } ++ ++ /* ++ * sepgsqlCheckBlobWrite ++ * check db_blob:{write} permission ++ */ ++ void ++ sepgsqlCheckBlobWrite(LargeObjectDesc *lobj) ++ { ++ sepgsql_sid_t loSid; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ loSid.relid = LargeObjectRelationId; ++ loSid.secid = lobj->secid; ++ sepgsqlClientHasPerms(loSid, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_DB_BLOB__WRITE, ++ NULL, true); ++ } ++ ++ /* ++ * sepgsqlCheckBlobGetattr ++ * check db_blob:{getattr} permission ++ */ ++ void ++ sepgsqlCheckBlobGetattr(HeapTuple tuple) ++ { ++ sepgsql_sid_t loSid; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ loSid.relid = LargeObjectRelationId; ++ loSid.secid = HeapTupleGetSecid(tuple); ++ sepgsqlClientHasPerms(loSid, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_DB_BLOB__GETATTR, ++ NULL, true); ++ } ++ ++ /* ++ * sepgsqlCheckBlobSetattr ++ * check db_blob:{setattr} permission ++ */ ++ void ++ sepgsqlCheckBlobSetattr(HeapTuple tuple) ++ { ++ sepgsql_sid_t loSid; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ loSid.relid = LargeObjectRelationId; ++ loSid.secid = HeapTupleGetSecid(tuple); ++ sepgsqlClientHasPerms(loSid, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_DB_BLOB__SETATTR, ++ NULL, true); ++ } ++ ++ /* ++ * sepgsqlCheckBlobExport ++ * check db_blob:{read export} and file:{write} permission ++ */ ++ void ++ sepgsqlCheckBlobExport(LargeObjectDesc *lobj, const char *filename) ++ { ++ sepgsql_sid_t loSid; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* db_blob:{read export} */ ++ loSid.relid = LargeObjectRelationId; ++ loSid.secid = lobj->secid; ++ sepgsqlClientHasPerms(loSid, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_DB_BLOB__READ | SEPG_DB_BLOB__EXPORT, ++ NULL, true); ++ /* file:{write} */ ++ sepgsql_file_write(filename); ++ } ++ ++ /* ++ * sepgsqlCheckBlobImport ++ * check db_blob:{write import} and file:{read} permission ++ */ ++ void ++ sepgsqlCheckBlobImport(LargeObjectDesc *lobj, const char *filename) ++ { ++ sepgsql_sid_t loSid; ++ ++ if (!sepgsqlIsEnabled()) ++ return; ++ ++ /* db_blob:{write import} */ ++ loSid.relid = LargeObjectRelationId; ++ loSid.secid = lobj->secid; ++ sepgsqlClientHasPerms(loSid, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_DB_BLOB__WRITE | SEPG_DB_BLOB__IMPORT, ++ NULL, true); ++ /* file:{read} */ ++ sepgsql_file_read(filename); ++ } ++ ++ /* ++ * sepgsqlCheckBlobRelabel ++ * check db_blob:{setattr relabelfrom relabelto} ++ */ ++ void ++ sepgsqlCheckBlobRelabel(HeapTuple oldtup, HeapTuple newtup) ++ { ++ sepgsql_sid_t loSid; ++ access_vector_t required = SEPG_DB_BLOB__SETATTR; ++ ++ if (HeapTupleGetSecid(oldtup) != HeapTupleGetSecid(newtup)) ++ required |= SEPG_DB_BLOB__RELABELFROM; ++ ++ /* db_blob:{setattr relabelfrom} */ ++ loSid = sepgsqlGetTupleSecid(LargeObjectRelationId, oldtup, NULL); ++ sepgsqlClientHasPerms(loSid, ++ SEPG_CLASS_DB_BLOB, ++ required, ++ NULL, true); ++ ++ if ((required & SEPG_DB_BLOB__RELABELFROM) == 0) ++ return; ++ ++ /* db_blob:{relabelto} */ ++ loSid = sepgsqlGetTupleSecid(LargeObjectRelationId, newtup, NULL); ++ sepgsqlClientHasPerms(loSid, ++ SEPG_CLASS_DB_BLOB, ++ SEPG_DB_BLOB__RELABELTO, ++ NULL, true); ++ } +diff -Nrpc blob/src/backend/security/sepgsql/label.c sepgsql/src/backend/security/sepgsql/label.c +*** blob/src/backend/security/sepgsql/label.c Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/security/sepgsql/label.c Thu Dec 24 21:59:25 2009 +*************** +*** 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 -Nrpc blob/src/backend/security/sepgsql/misc.c sepgsql/src/backend/security/sepgsql/misc.c +*** blob/src/backend/security/sepgsql/misc.c Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/security/sepgsql/misc.c Sun Dec 20 00:41:22 2009 +*************** +*** 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 -Nrpc blob/src/backend/security/sepgsql/perms.c sepgsql/src/backend/security/sepgsql/perms.c +*** blob/src/backend/security/sepgsql/perms.c Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/security/sepgsql/perms.c Mon Sep 28 09:29:32 2009 +*************** +*** 0 **** +--- 1,597 ---- ++ /* ++ * src/backend/utils/sepgsql/perms.c ++ * SE-PostgreSQL permission checks ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++ #include "postgres.h" ++ ++ #include "catalog/pg_database.h" ++ #include "catalog/pg_proc.h" ++ #include "catalog/pg_largeobject.h" ++ #include "catalog/pg_namespace.h" ++ #include "catalog/pg_type.h" ++ #include "miscadmin.h" ++ #include "security/sepgsql.h" ++ #include "utils/lsyscache.h" ++ ++ /* ++ * Dynamic object class/permissions mapping ++ * ++ * SELinux exports the list of object classes and permissions at ++ * /selinux/class. The libselinux provides an interface to translate ++ * between their names and codes. ++ */ ++ static struct ++ { ++ const char *class_name; ++ security_class_t class_code; ++ struct ++ { ++ const char *perm_name; ++ access_vector_t perm_code; ++ } av[sizeof(access_vector_t) * 8]; ++ } selinux_catalog[] = { ++ { ++ "process", SEPG_CLASS_PROCESS, ++ { ++ {"translation", SEPG_PROCESS__TRANSITION }, ++ {NULL, 0} ++ } ++ }, ++ { ++ "file", SEPG_CLASS_FILE, ++ { ++ {"read", SEPG_FILE__READ }, ++ {"write", SEPG_FILE__WRITE }, ++ {"create", SEPG_FILE__CREATE }, ++ {"getattr", SEPG_FILE__GETATTR }, ++ {NULL, 0} ++ } ++ }, ++ { ++ "dir", SEPG_CLASS_DIR, ++ { ++ {"read", SEPG_DIR__READ }, ++ {"write", SEPG_DIR__WRITE }, ++ {"create", SEPG_DIR__CREATE }, ++ {"getattr", SEPG_DIR__GETATTR }, ++ {NULL,0} ++ } ++ }, ++ { ++ "lnk_file", SEPG_CLASS_LNK_FILE, ++ { ++ {"read", SEPG_LNK_FILE__READ }, ++ {"write", SEPG_LNK_FILE__WRITE }, ++ {"create", SEPG_LNK_FILE__CREATE }, ++ {"getattr", SEPG_LNK_FILE__GETATTR }, ++ {NULL,0} ++ } ++ }, ++ { ++ "chr_file", SEPG_CLASS_CHR_FILE, ++ { ++ {"read", SEPG_CHR_FILE__READ }, ++ {"write", SEPG_CHR_FILE__WRITE }, ++ {"create", SEPG_CHR_FILE__CREATE }, ++ {"getattr", SEPG_CHR_FILE__GETATTR }, ++ {NULL,0} ++ } ++ }, ++ { ++ "blk_file", SEPG_CLASS_BLK_FILE, ++ { ++ {"read", SEPG_BLK_FILE__READ }, ++ {"write", SEPG_BLK_FILE__WRITE }, ++ {"create", SEPG_BLK_FILE__CREATE }, ++ {"getattr", SEPG_BLK_FILE__GETATTR }, ++ {NULL,0} ++ } ++ }, ++ { ++ "sock_file", SEPG_CLASS_SOCK_FILE, ++ { ++ {"read", SEPG_SOCK_FILE__READ }, ++ {"write", SEPG_SOCK_FILE__WRITE }, ++ {"create", SEPG_SOCK_FILE__CREATE }, ++ {"getattr", SEPG_SOCK_FILE__GETATTR }, ++ {NULL,0} ++ } ++ }, ++ { ++ "fifo_file", SEPG_CLASS_FIFO_FILE, ++ { ++ {"read", SEPG_FIFO_FILE__READ }, ++ {"write", SEPG_FIFO_FILE__WRITE }, ++ {"create", SEPG_FIFO_FILE__CREATE }, ++ {"getattr", SEPG_FIFO_FILE__GETATTR }, ++ {NULL, 0UL } ++ } ++ }, ++ { ++ "db_database", SEPG_CLASS_DB_DATABASE, ++ { ++ { "create", SEPG_DB_DATABASE__CREATE }, ++ { "drop", SEPG_DB_DATABASE__DROP }, ++ { "getattr", SEPG_DB_DATABASE__GETATTR }, ++ { "setattr", SEPG_DB_DATABASE__SETATTR }, ++ { "relabelfrom", SEPG_DB_DATABASE__RELABELFROM }, ++ { "relabelto", SEPG_DB_DATABASE__RELABELTO }, ++ { "access", SEPG_DB_DATABASE__ACCESS }, ++ { "install_module", SEPG_DB_DATABASE__INSTALL_MODULE }, ++ { "load_module", SEPG_DB_DATABASE__LOAD_MODULE }, ++ { "superuser", SEPG_DB_DATABASE__SUPERUSER }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_schema", SEPG_CLASS_DB_SCHEMA, ++ { ++ { "create", SEPG_DB_SCHEMA__CREATE }, ++ { "drop", SEPG_DB_SCHEMA__DROP }, ++ { "getattr", SEPG_DB_SCHEMA__GETATTR }, ++ { "setattr", SEPG_DB_SCHEMA__SETATTR }, ++ { "relabelfrom", SEPG_DB_SCHEMA__RELABELFROM }, ++ { "relabelto", SEPG_DB_SCHEMA__RELABELTO }, ++ { "search", SEPG_DB_SCHEMA__SEARCH }, ++ { "add_name", SEPG_DB_SCHEMA__ADD_NAME }, ++ { "remove_name", SEPG_DB_SCHEMA__REMOVE_NAME }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_table", SEPG_CLASS_DB_TABLE, ++ { ++ { "create", SEPG_DB_TABLE__CREATE }, ++ { "drop", SEPG_DB_TABLE__DROP }, ++ { "getattr", SEPG_DB_TABLE__GETATTR }, ++ { "setattr", SEPG_DB_TABLE__SETATTR }, ++ { "relabelfrom", SEPG_DB_TABLE__RELABELFROM }, ++ { "relabelto", SEPG_DB_TABLE__RELABELTO }, ++ { "select", SEPG_DB_TABLE__SELECT }, ++ { "update", SEPG_DB_TABLE__UPDATE }, ++ { "insert", SEPG_DB_TABLE__INSERT }, ++ { "delete", SEPG_DB_TABLE__DELETE }, ++ { "lock", SEPG_DB_TABLE__LOCK }, ++ { "reference", SEPG_DB_TABLE__REFERENCE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_sequence", SEPG_CLASS_DB_SEQUENCE, ++ { ++ { "create", SEPG_DB_SEQUENCE__CREATE }, ++ { "drop", SEPG_DB_SEQUENCE__DROP }, ++ { "getattr", SEPG_DB_SEQUENCE__GETATTR }, ++ { "setattr", SEPG_DB_SEQUENCE__SETATTR }, ++ { "relabelfrom", SEPG_DB_SEQUENCE__RELABELFROM }, ++ { "relabelto", SEPG_DB_SEQUENCE__RELABELTO }, ++ { "get_value", SEPG_DB_SEQUENCE__GET_VALUE }, ++ { "next_value", SEPG_DB_SEQUENCE__NEXT_VALUE }, ++ { "set_value", SEPG_DB_SEQUENCE__SET_VALUE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_procedure", SEPG_CLASS_DB_PROCEDURE, ++ { ++ { "create", SEPG_DB_PROCEDURE__CREATE }, ++ { "drop", SEPG_DB_PROCEDURE__DROP }, ++ { "getattr", SEPG_DB_PROCEDURE__GETATTR }, ++ { "setattr", SEPG_DB_PROCEDURE__SETATTR }, ++ { "relabelfrom", SEPG_DB_PROCEDURE__RELABELFROM }, ++ { "relabelto", SEPG_DB_PROCEDURE__RELABELTO }, ++ { "execute", SEPG_DB_PROCEDURE__EXECUTE }, ++ { "entrypoint", SEPG_DB_PROCEDURE__ENTRYPOINT }, ++ { "install", SEPG_DB_PROCEDURE__INSTALL }, ++ { "untrusted", SEPG_DB_PROCEDURE__UNTRUSTED }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_column", SEPG_CLASS_DB_COLUMN, ++ { ++ { "create", SEPG_DB_COLUMN__CREATE }, ++ { "drop", SEPG_DB_COLUMN__DROP }, ++ { "getattr", SEPG_DB_COLUMN__GETATTR }, ++ { "setattr", SEPG_DB_COLUMN__SETATTR }, ++ { "relabelfrom", SEPG_DB_COLUMN__RELABELFROM }, ++ { "relabelto", SEPG_DB_COLUMN__RELABELTO }, ++ { "select", SEPG_DB_COLUMN__SELECT }, ++ { "update", SEPG_DB_COLUMN__UPDATE }, ++ { "insert", SEPG_DB_COLUMN__INSERT }, ++ { "reference", SEPG_DB_COLUMN__REFERENCE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_tuple", SEPG_CLASS_DB_TUPLE, ++ { ++ { "relabelfrom", SEPG_DB_TUPLE__RELABELFROM }, ++ { "relabelto", SEPG_DB_TUPLE__RELABELTO }, ++ { "select", SEPG_DB_TUPLE__SELECT }, ++ { "update", SEPG_DB_TUPLE__UPDATE }, ++ { "insert", SEPG_DB_TUPLE__INSERT }, ++ { "delete", SEPG_DB_TUPLE__DELETE }, ++ { NULL, 0UL }, ++ } ++ }, ++ { ++ "db_blob", SEPG_CLASS_DB_BLOB, ++ { ++ { "create", SEPG_DB_BLOB__CREATE }, ++ { "drop", SEPG_DB_BLOB__DROP }, ++ { "getattr", SEPG_DB_BLOB__GETATTR }, ++ { "setattr", SEPG_DB_BLOB__SETATTR }, ++ { "relabelfrom", SEPG_DB_BLOB__RELABELFROM }, ++ { "relabelto", SEPG_DB_BLOB__RELABELTO }, ++ { "read", SEPG_DB_BLOB__READ }, ++ { "write", SEPG_DB_BLOB__WRITE }, ++ { "import", SEPG_DB_BLOB__IMPORT }, ++ { "export", SEPG_DB_BLOB__EXPORT }, ++ { NULL, 0UL }, ++ } ++ } ++ }; ++ ++ /* ++ * sepgsqlTransToExternalClass ++ * It translate the given class code (defined as SEPGCLASS_(class)) into ++ * external code which is necessary to communicate in-kernel SELinux ++ */ ++ extern security_class_t ++ sepgsqlTransToExternalClass(uint16 tclass) ++ { ++ Assert(tclass < SEPG_CLASS_MAX); ++ ++ return string_to_security_class(selinux_catalog[tclass].class_name); ++ } ++ ++ /* ++ * sepgsqlTransToInternalPerms ++ * It translate the given permission masks into internal representation ++ * defined as SEPG_(class)_(permission). ++ */ ++ extern void ++ sepgsqlTransToInternalPerms(security_class_t tclass, struct av_decision *avd) ++ { ++ security_class_t tclass_ex; ++ struct av_decision i_avd; ++ int i, deny_unknown; ++ ++ Assert(tclass < SEPG_CLASS_MAX); ++ ++ memset(&i_avd, 0, sizeof(struct av_decision)); ++ ++ deny_unknown = security_deny_unknown(); ++ ++ tclass_ex = sepgsqlTransToExternalClass(tclass); ++ for (i=0; selinux_catalog[tclass].av[i].perm_name; i++) ++ { ++ const char *perm_name = selinux_catalog[tclass].av[i].perm_name; ++ access_vector_t perm_code = selinux_catalog[tclass].av[i].perm_code; ++ access_vector_t perm_code_ex; ++ ++ perm_code_ex = string_to_av_perm(tclass_ex, perm_name); ++ if (!perm_code_ex) ++ { ++ /* fill up undefined permission */ ++ if (!deny_unknown) ++ i_avd.allowed |= perm_code; ++ i_avd.decided |= perm_code; ++ i_avd.auditdeny |= perm_code; ++ continue; ++ } ++ ++ if (avd->allowed & perm_code_ex) ++ i_avd.allowed |= perm_code; ++ if (avd->decided & perm_code_ex) ++ i_avd.decided |= perm_code; ++ if (avd->auditallow & perm_code_ex) ++ i_avd.auditallow |= perm_code; ++ if (avd->auditdeny & perm_code_ex) ++ i_avd.auditdeny |= perm_code; ++ } ++ ++ avd->allowed = i_avd.allowed; ++ avd->decided = i_avd.decided; ++ avd->auditallow = i_avd.auditallow; ++ avd->auditdeny = i_avd.auditdeny; ++ } ++ ++ /* ++ * sepgsqlGetClassString ++ * sepgsqlGetPermissionString ++ * It returns text representation of object classes/permissions ++ */ ++ const char * ++ sepgsqlGetClassString(uint16 tclass) ++ { ++ Assert(tclass < SEPG_CLASS_MAX); ++ ++ return selinux_catalog[tclass].class_name; ++ } ++ ++ const char * ++ sepgsqlGetPermString(uint16 tclass, uint32 permission) ++ { ++ int i; ++ ++ Assert(tclass < SEPG_CLASS_MAX); ++ ++ for (i=0; selinux_catalog[tclass].av[i].perm_name; i++) ++ { ++ if (selinux_catalog[tclass].av[i].perm_code == permission) ++ return selinux_catalog[tclass].av[i].perm_name; ++ } ++ return NULL; ++ } ++ ++ #if 0 ++ ++ /* ++ * sepgsqlFileObjectClass ++ * ++ * It returns proper object class of filesystem object already opened. ++ * It is necessary to check privileges voluntarily. ++ */ ++ uint16 ++ sepgsqlFileObjectClass(int fdesc) ++ { ++ struct stat stbuf; ++ ++ if (fstat(fdesc, &stbuf) != 0) ++ ereport(ERROR, ++ (errcode_for_file_access(), ++ errmsg("could not stat file descriptor: %d", fdesc))); ++ ++ if (S_ISDIR(stbuf.st_mode)) ++ return SEPG_CLASS_DIR; ++ else if (S_ISCHR(stbuf.st_mode)) ++ return SEPG_CLASS_CHR_FILE; ++ else if (S_ISBLK(stbuf.st_mode)) ++ return SEPG_CLASS_BLK_FILE; ++ else if (S_ISFIFO(stbuf.st_mode)) ++ return SEPG_CLASS_FIFO_FILE; ++ else if (S_ISLNK(stbuf.st_mode)) ++ return SEPG_CLASS_LNK_FILE; ++ else if (S_ISSOCK(stbuf.st_mode)) ++ return SEPG_CLASS_SOCK_FILE; ++ ++ return SEPG_CLASS_FILE; ++ } ++ ++ /* ++ * sepgsqlTupleObjectClass ++ * ++ * It returns correct object class of given tuple ++ */ ++ uint16 ++ sepgsqlTupleObjectClass(Oid relid, HeapTuple tuple) ++ { ++ Form_pg_class clsForm; ++ Form_pg_attribute attForm; ++ ++ switch (relid) ++ { ++ case DatabaseRelationId: ++ return SEPG_CLASS_DB_DATABASE; ++ ++ case NamespaceRelationId: ++ return SEPG_CLASS_DB_SCHEMA; ++ ++ case RelationRelationId: ++ clsForm = (Form_pg_class) GETSTRUCT(tuple); ++ if (clsForm->relkind == RELKIND_RELATION) ++ return SEPG_CLASS_DB_TABLE; ++ if (clsForm->relkind == RELKIND_SEQUENCE) ++ return SEPG_CLASS_DB_SEQUENCE; ++ break; ++ ++ case AttributeRelationId: ++ attForm = (Form_pg_attribute) GETSTRUCT(tuple); ++ if (IsBootstrapProcessingMode() && ++ (attForm->attrelid == TypeRelationId || ++ attForm->attrelid == ProcedureRelationId || ++ attForm->attrelid == AttributeRelationId || ++ attForm->attrelid == RelationRelationId)) ++ return SEPG_CLASS_DB_COLUMN; ++ ++ if (get_rel_relkind(attForm->attrelid) == RELKIND_RELATION) ++ return SEPG_CLASS_DB_COLUMN; ++ break; ++ ++ case ProcedureRelationId: ++ return SEPG_CLASS_DB_PROCEDURE; ++ ++ case LargeObjectRelationId: ++ return SEPG_CLASS_DB_BLOB; ++ } ++ return SEPG_CLASS_DB_TUPLE; ++ } ++ ++ /* ++ * sepgsqlTupleNamespace ++ * ++ * It returns an OID of the namespace, if the given system object is ++ * deployed under a certain namespace. ++ */ ++ Oid ++ sepgsqlTupleNamespace(Oid relOid, HeapTuple tuple) ++ { ++ Oid nspOid; ++ ++ switch (relOid) ++ { ++ case RelationRelationId: ++ nspOid = ((Form_pg_class) GETSTRUCT(tuple))->relnamespace; ++ break; ++ ++ case ConstraintRelationId: ++ nspOid = ((Form_pg_constraint) GETSTRUCT(tuple))->connamespace; ++ break; ++ ++ case ConversionRelationId: ++ nspOid = ((Form_pg_conversion) GETSTRUCT(tuple))->connamespace; ++ break; ++ ++ case OperatorClassRelationId: ++ nspOid = ((Form_pg_opclass) GETSTRUCT(tuple))->opcnamespace; ++ break; ++ ++ case OperatorRelationId: ++ nspOid = ((Form_pg_operator) GETSTRUCT(tuple))->oprnamespace; ++ break; ++ ++ case OperatorFamilyRelationId: ++ nspOid = ((Form_pg_opfamily) GETSTRUCT(tuple))->opfnamespace; ++ break; ++ ++ case ProcedureRelationId: ++ nspOid = ((Form_pg_proc) GETSTRUCT(tuple))->pronamespace; ++ break; ++ ++ case TSConfigRelationId: ++ nspOid = ((Form_pg_ts_config) GETSTRUCT(tuple))->cfgnamespace; ++ break; ++ ++ case TSDictionaryRelationId: ++ nspOid = ((Form_pg_ts_dict) GETSTRUCT(tuple))->dictnamespace; ++ break; ++ ++ case TSParserRelationId: ++ nspOid = ((Form_pg_ts_parser) GETSTRUCT(tuple))->prsnamespace; ++ break; ++ ++ case TSTemplateRelationId: ++ nspOid = ((Form_pg_ts_template) GETSTRUCT(tuple))->tmplnamespace; ++ break; ++ ++ default: ++ /* no specific namespace */ ++ nspOid = InvalidOid; ++ break; ++ } ++ ++ return nspOid; ++ } ++ ++ /* ++ * sepgsqlTupleAuditName ++ * ++ * It returns an OID of the namespace, if the given system object is ++ * deployed under a certain namespace. ++ */ ++ void ++ sepgsqlTupleAuditName(Oid relid, HeapTuple tuple, char *auname_buf) ++ { ++ char *name; ++ Oid extid; ++ ++ switch (relid) ++ { ++ case AccessMethodRelationId: ++ name = NameStr(((Form_pg_am) GETSTRUCT(tuple))->amname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case AttributeRelationId: ++ name = NameStr(((Form_pg_attribute) GETSTRUCT(tuple))->attname); ++ extid = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; ++ sprintf(audit_name, "%s.%s", name, extid); ++ return; ++ ++ case AuthIdRelationId: ++ name = NameStr(((Form_pg_authid) GETSTRUCT(tuple))->rolname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case ConversionRelationId: ++ name = NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case DatabaseRelationId: ++ name = NameStr(((Form_pg_database) GETSTRUCT(tuple))->datname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case ForeignDataWrapperRelationId: ++ name = NameStr(((Form_pg_foreign_data_wrapper) GETSTRUCT(tuple))->fdwname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case ForeignServerRelationId: ++ name = NameStr(((Form_pg_foreign_server) GETSTRUCT(tuple))->srvname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case LanguageRelationId: ++ name = NameStr(((Form_pg_language) GETSTRUCT(tuple))->lanname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case NamespaceRelationId: ++ name = NameStr(((Form_pg_namespace) GETSTRUCT(tuple))->nspname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case OperatorClassRelationId: ++ name = NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case OperatorRelationId: ++ name = NameStr(((Form_pg_operator) GETSTRUCT(tuple))->oprname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case OperatorFamilyRelationId: ++ name = NameStr(((Form_pg_opfamily) GETSTRUCT(tuple))->opfname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case ProcedureRelationId: ++ name = NameStr(((Form_pg_proc) GETSTRUCT(tuple))->proname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case RelationRelationId: ++ name = NameStr(((Form_pg_class) GETSTRUCT(tuple))->relname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case TableSpaceRelationId: ++ name = NameStr(((Form_pg_tablespace) GETSTRUCT(tuple))->spcname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case TSConfigRelationId: ++ name = NameStr(((Form_pg_ts_config) GETSTRUCT(tuple))->cfgname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case TSDictionaryRelationId: ++ name = NameStr(((Form_pg_ts_dict) GETSTRUCT(tuple))->dictname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case TSParserRelationId: ++ name = NameStr(((Form_pg_ts_parser) GETSTRUCT(tuple))->prsname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ case TSTemplateRelationId: ++ name = NameStr(((Form_pg_templace) GETSTRUCT(tuple))->tmplname); ++ strncpy(auname_buf, name, NAMEDATALEN); ++ break; ++ ++ default: ++ /* no auditable name */ ++ auname_buf[0] = '\0'; ++ break; ++ } ++ } ++ #endif +diff -Nrpc blob/src/backend/security/sepgsql/policy/Makefile sepgsql/src/backend/security/sepgsql/policy/Makefile +*** blob/src/backend/security/sepgsql/policy/Makefile Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/security/sepgsql/policy/Makefile Wed Jul 15 19:35:52 2009 +*************** +*** 0 **** +--- 1,28 ---- ++ # ++ # Makefile for SE-PostgreSQL security policy module ++ # ++ top_builddir = ../../../../.. ++ include $(top_builddir)/src/Makefile.global ++ ++ POLICY_BASEDIR := $(DESTDIR)/usr/share/selinux ++ POLICY_MAKEFILE := $(POLICY_BASEDIR)/devel/Makefile ++ POLICY_INSTDIR := $(POLICY_BASEDIR)/packages ++ PREFIX_RULE := "s/%%__prefix__%%/$(shell echo $(prefix)|sed 's/\//\\\//g')/g" ++ BINDIR_RULE := "s/%%__bindir__%%/$(shell echo $(bindir)|sed 's/\//\\\//g')/g" ++ LIBDIR_RULE := "s/%%__libdir__%%/$(shell echo $(pkglibdir)|sed 's/\//\\\//g')/g" ++ ++ all: sepostgresql-devel.pp ++ ++ install: all ++ test -d $(POLICY_INSTDIR) || mkdir -p $(POLICY_INSTDIR) ++ install -p -m 0644 sepostgresql-devel.pp $(POLICY_INSTDIR) ++ ++ sepostgresql-devel.pp: sepostgresql-devel.te sepostgresql-devel.fc ++ $(MAKE) -f $(POLICY_MAKEFILE) ++ ++ sepostgresql-devel.fc: sepostgresql-devel.fc.template ++ cat $< | sed -e $(PREFIX_RULE) -e $(BINDIR_RULE) -e $(LIBDIR_RULE) > $@ ++ ++ clean: ++ $(MAKE) -f $(POLICY_MAKEFILE) clean ++ rm -f *.fc +diff -Nrpc blob/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template +*** blob/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.fc.template Wed Jul 15 19:35:52 2009 +*************** +*** 0 **** +--- 1,12 ---- ++ # ++ # SE-PostgreSQL install path ++ # ++ %%__prefix__%%(/.*)? -- gen_context(system_u:object_r:usr_t,s0) ++ ++ %%__bindir__%%/(se)?postgres -- gen_context(system_u:object_r:postgresql_exec_t,s0) ++ %%__bindir__%%/(se)?pg_ctl -- gen_context(system_u:object_r:initrc_exec_t,s0) ++ %%__bindir__%%/initdb(\.sepgsql)? -- gen_context(system_u:object_r:postgresql_exec_t,s0) ++ %%__bindir__%%(/.*)? -- gen_context(system_u:object_r:bin_t,s0) ++ ++ %%__libdir__%%(/.*)? -- gen_context(system_u:object_r:lib_t,s0) ++ +diff -Nrpc blob/src/backend/security/sepgsql/policy/sepostgresql-devel.te sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.te +*** blob/src/backend/security/sepgsql/policy/sepostgresql-devel.te Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/security/sepgsql/policy/sepostgresql-devel.te Tue Dec 1 17:11:40 2009 +*************** +*** 0 **** +--- 1,123 ---- ++ policy_module(sepostgresql-devel, 3.29) ++ ++ gen_require(` ++ class db_database all_db_database_perms; ++ class db_table all_db_table_perms; ++ class db_procedure all_db_procedure_perms; ++ class db_column all_db_column_perms; ++ class db_tuple all_db_tuple_perms; ++ class db_blob all_db_blob_perms; ++ ++ attribute sepgsql_client_type; ++ attribute sepgsql_unconfined_type; ++ ++ attribute sepgsql_database_type; ++ attribute sepgsql_table_type; ++ attribute sepgsql_sysobj_table_type; ++ attribute sepgsql_procedure_type; ++ attribute sepgsql_blob_type; ++ attribute sepgsql_module_type; ++ ++ # for regression test ++ type bin_t; ++ type user_home_t; ++ type sepgsql_trusted_proc_exec_t; ++ ++ attribute tmpfile; ++ ') ++ ++ ################################# ++ # ++ # Domain for Testcases ++ # ++ ++ role sepgsql_test_r; ++ ++ userdom_unpriv_user_template(sepgsql_test) ++ postgresql_role(sepgsql_test_r, sepgsql_test_t) ++ ++ allow sepgsql_test_t tmpfile : dir search_dir_perms; ++ allow sepgsql_test_t tmpfile : file rw_file_perms; ++ ++ optional_policy(` ++ term_write_all_terms(sepgsql_test_t) ++ ') ++ ++ optional_policy(` ++ gen_require(` ++ type unconfined_t; ++ role unconfined_r; ++ ') ++ ++ tunable_policy(`sepgsql_regression_test_mode',` ++ allow unconfined_t sepgsql_test_t : process transition; ++ ') ++ unconfined_rw_pipes(sepgsql_test_t) ++ role unconfined_r types sepgsql_test_t; ++ role unconfined_r types sepgsql_trusted_proc_t; ++ ') ++ ++ ################################# ++ # ++ # SE-PostgreSQL Declarations ++ # ++ ++ ## ++ ##

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

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

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

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

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

++ ##
++ gen_tunable(sepgsql_regression_test_mode, false) ++ ++ ######################################## ++ # ++ # SE-PostgreSQL audit switch for debugging ++ # ++ tunable_policy(`sepgsql_enable_auditallow',` ++ auditallow domain sepgsql_database_type : db_database *; ++ auditallow domain sepgsql_table_type : db_table *; ++ auditallow domain sepgsql_table_type : db_column *; ++ auditallow domain sepgsql_table_type : db_tuple { relabelfrom relabelto }; ++ auditallow domain sepgsql_sysobj_table_type : db_tuple *; ++ auditallow domain sepgsql_procedure_type : db_procedure *; ++ auditallow domain sepgsql_blob_type : db_blob *; ++ auditallow domain sepgsql_module_type : db_database { install_module }; ++ auditallow sepgsql_database_type sepgsql_module_type : db_database { load_module }; ++ ') ++ ++ tunable_policy(`! sepgsql_enable_auditdeny',` ++ dontaudit domain sepgsql_database_type : db_database *; ++ dontaudit domain sepgsql_table_type : db_table *; ++ dontaudit domain sepgsql_table_type : db_column *; ++ dontaudit domain sepgsql_table_type : db_tuple { relabelfrom relabelto }; ++ dontaudit domain sepgsql_sysobj_table_type : db_tuple *; ++ dontaudit domain sepgsql_procedure_type : db_procedure *; ++ dontaudit domain sepgsql_blob_type : db_blob *; ++ dontaudit domain sepgsql_module_type : db_database { install_module }; ++ dontaudit sepgsql_database_type sepgsql_module_type : db_database { load_module }; ++ ') ++ ++ ######################################## ++ # ++ # SE-PostgreSQL regression test mode switch ++ # ++ tunable_policy(`sepgsql_regression_test_mode',` ++ allow sepgsql_client_type user_home_t : db_database { install_module }; ++ allow sepgsql_unconfined_type user_home_t : db_database { install_module }; ++ allow sepgsql_database_type user_home_t : db_database { load_module }; ++ ') +diff -Nrpc blob/src/backend/security/sepgsql/selinux.c sepgsql/src/backend/security/sepgsql/selinux.c +*** blob/src/backend/security/sepgsql/selinux.c Thu Jan 1 09:00:00 1970 +--- sepgsql/src/backend/security/sepgsql/selinux.c Thu Dec 24 21:59:25 2009 +*************** +*** 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 -Nrpc blob/src/backend/storage/file/fd.c sepgsql/src/backend/storage/file/fd.c +*** blob/src/backend/storage/file/fd.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/storage/file/fd.c Tue Dec 15 17:30:25 2009 +*************** FileTruncate(File file, off_t offset) +*** 1329,1334 **** +--- 1329,1341 ---- + return returnCode; + } + ++ int ++ FileRawDescriptor(File file) ++ { ++ Assert(FileIsValid(file)); ++ ++ return VfdCache[file].fd; ++ } + + /* + * Routines that want to use stdio (ie, FILE*) should use AllocateFile +diff -Nrpc blob/src/backend/storage/ipc/ipci.c sepgsql/src/backend/storage/ipc/ipci.c +*** blob/src/backend/storage/ipc/ipci.c Thu May 7 08:49:32 2009 +--- sepgsql/src/backend/storage/ipc/ipci.c Wed Jul 15 19:35:52 2009 +*************** +*** 25,30 **** +--- 25,31 ---- + #include "postmaster/autovacuum.h" + #include "postmaster/bgwriter.h" + #include "postmaster/postmaster.h" ++ #include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/ipc.h" + #include "storage/pg_shmem.h" +*************** CreateSharedMemoryAndSemaphores(bool mak +*** 119,124 **** +--- 120,126 ---- + #ifdef EXEC_BACKEND + size = add_size(size, ShmemBackendArraySize()); + #endif ++ size = add_size(size, sepgsqlShmemSize()); + + /* freeze the addin request size and include it */ + addin_request_allowed = false; +diff -Nrpc blob/src/backend/storage/large_object/inv_api.c sepgsql/src/backend/storage/large_object/inv_api.c +*** blob/src/backend/storage/large_object/inv_api.c Fri Dec 18 09:40:55 2009 +--- sepgsql/src/backend/storage/large_object/inv_api.c Fri Dec 18 10:27:56 2009 +*************** getbytealen(bytea *data) +*** 197,210 **** + * in use. + */ + Oid +! inv_create(Oid lobjId) + { + Oid lobjId_new; + + /* + * Create a new largeobject with empty data pages + */ +! lobjId_new = LargeObjectCreate(lobjId); + + /* + * dependency on the owner of largeobject +--- 197,210 ---- + * in use. + */ + Oid +! inv_create(Oid lobjId, Oid secid) + { + Oid lobjId_new; + + /* + * Create a new largeobject with empty data pages + */ +! lobjId_new = LargeObjectCreate(lobjId, secid); + + /* + * dependency on the owner of largeobject +diff -Nrpc blob/src/backend/tcop/fastpath.c sepgsql/src/backend/tcop/fastpath.c +*** blob/src/backend/tcop/fastpath.c Sat Jan 3 13:01:35 2009 +--- sepgsql/src/backend/tcop/fastpath.c Thu Sep 17 17:04:16 2009 +*************** +*** 26,31 **** +--- 26,32 ---- + #include "libpq/pqformat.h" + #include "mb/pg_wchar.h" + #include "miscadmin.h" ++ #include "security/sepgsql.h" + #include "tcop/fastpath.h" + #include "tcop/tcopprot.h" + #include "utils/acl.h" +*************** HandleFunctionRequest(StringInfo msgBuf) +*** 343,353 **** +--- 344,356 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(fip->namespace)); ++ sepgsql_schema_search(fip->namespace, true); + + aclresult = pg_proc_aclcheck(fid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, + get_func_name(fid)); ++ sepgsql_proc_execute(fid); + + /* + * Prepare function call info block and insert arguments. +diff -Nrpc blob/src/backend/tcop/pquery.c sepgsql/src/backend/tcop/pquery.c +*** blob/src/backend/tcop/pquery.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/tcop/pquery.c Wed Jul 15 19:30:50 2009 +*************** PortalStart(Portal portal, ParamListInfo +*** 573,579 **** + Assert(pstmt->returningLists); + portal->tupDesc = + ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), +! false); + } + + /* +--- 573,579 ---- + Assert(pstmt->returningLists); + portal->tupDesc = + ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), +! false, false); + } + + /* +diff -Nrpc blob/src/backend/tcop/utility.c sepgsql/src/backend/tcop/utility.c +*** blob/src/backend/tcop/utility.c Fri Dec 18 09:40:55 2009 +--- sepgsql/src/backend/tcop/utility.c Fri Dec 18 10:27:56 2009 +*************** +*** 50,55 **** +--- 50,56 ---- + #include "postmaster/bgwriter.h" + #include "rewrite/rewriteDefine.h" + #include "rewrite/rewriteRemove.h" ++ #include "security/sepgsql.h" + #include "storage/fd.h" + #include "tcop/pquery.h" + #include "tcop/utility.h" +*************** check_xact_readonly(Node *parsetree) +*** 162,167 **** +--- 163,169 ---- + case T_AlterRoleSetStmt: + case T_AlterObjectSchemaStmt: + case T_AlterOwnerStmt: ++ case T_AlterSecLabelStmt: + case T_AlterSeqStmt: + case T_AlterTableStmt: + case T_RenameStmt: +*************** ProcessUtility(Node *parsetree, +*** 634,639 **** +--- 636,645 ---- + ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree); + break; + ++ case T_AlterSecLabelStmt: ++ ExecAlterSecLabelStmt((AlterSecLabelStmt *) parsetree); ++ break; ++ + case T_AlterTableStmt: + { + List *stmts; +*************** ProcessUtility(Node *parsetree, +*** 917,922 **** +--- 923,929 ---- + LoadStmt *stmt = (LoadStmt *) parsetree; + + closeAllVfds(); /* probably not necessary... */ ++ + /* Allowed names are restricted if you're not superuser */ + load_file(stmt->filename, !superuser()); + } +*************** CreateCommandTag(Node *parsetree) +*** 1664,1669 **** +--- 1671,1701 ---- + } + break; + ++ case T_AlterSecLabelStmt: ++ switch (((AlterSecLabelStmt *) parsetree)->objectType) ++ { ++ case OBJECT_DATABASE: ++ tag = "ALTER DATABASE"; ++ break; ++ case OBJECT_SCHEMA: ++ tag = "ALTER SCHEMA"; ++ break; ++ case OBJECT_TABLE: ++ case OBJECT_COLUMN: ++ tag = "ALTER TABLE"; ++ break; ++ case OBJECT_SEQUENCE: ++ tag = "ALTER SEQUENCE"; ++ break; ++ case OBJECT_FUNCTION: ++ tag = "ALTER FUNCTION"; ++ break; ++ default: ++ tag = "???"; ++ break; ++ } ++ break; ++ + case T_AlterTableStmt: + switch (((AlterTableStmt *) parsetree)->relkind) + { +*************** GetCommandLogLevel(Node *parsetree) +*** 2242,2247 **** +--- 2274,2283 ---- + lev = LOGSTMT_DDL; + break; + ++ case T_AlterSecLabelStmt: ++ lev = LOGSTMT_DDL; ++ break; ++ + case T_AlterTableStmt: + lev = LOGSTMT_DDL; + break; +diff -Nrpc blob/src/backend/utils/adt/genfile.c sepgsql/src/backend/utils/adt/genfile.c +*** blob/src/backend/utils/adt/genfile.c Sat Jan 3 13:01:35 2009 +--- sepgsql/src/backend/utils/adt/genfile.c Mon Sep 28 09:29:32 2009 +*************** +*** 24,29 **** +--- 24,30 ---- + #include "funcapi.h" + #include "miscadmin.h" + #include "postmaster/syslogger.h" ++ #include "security/sepgsql.h" + #include "storage/fd.h" + #include "utils/builtins.h" + #include "utils/memutils.h" +*************** pg_read_file(PG_FUNCTION_ARGS) +*** 99,104 **** +--- 100,108 ---- + + filename = convert_and_check_filename(filename_t); + ++ /* SELinux: check file:{read} permission */ ++ sepgsql_file_read(filename); ++ + if ((file = AllocateFile(filename, PG_BINARY_R)) == NULL) + ereport(ERROR, + (errcode_for_file_access(), +*************** pg_stat_file(PG_FUNCTION_ARGS) +*** 159,164 **** +--- 163,170 ---- + (errmsg("must be superuser to get file information")))); + + filename = convert_and_check_filename(filename_t); ++ /* SELinux: check file:{getattr} permission */ ++ sepgsql_file_stat(filename); + + if (stat(filename, &fst) < 0) + ereport(ERROR, +diff -Nrpc blob/src/backend/utils/adt/ri_triggers.c sepgsql/src/backend/utils/adt/ri_triggers.c +*** blob/src/backend/utils/adt/ri_triggers.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/utils/adt/ri_triggers.c Tue Dec 15 17:30:25 2009 +*************** +*** 39,44 **** +--- 39,45 ---- + #include "parser/parse_coerce.h" + #include "parser/parse_relation.h" + #include "miscadmin.h" ++ #include "security/rowlevel.h" + #include "utils/acl.h" + #include "utils/builtins.h" + #include "utils/fmgroids.h" +*************** RI_Initial_Check(Trigger *trigger, Relat +*** 2627,2632 **** +--- 2628,2634 ---- + const char *sep; + int i; + int old_work_mem; ++ int save_rowlv; + char workmembuf[32]; + int spi_result; + SPIPlanPtr qplan; +*************** RI_Initial_Check(Trigger *trigger, Relat +*** 2759,2764 **** +--- 2761,2771 ---- + SPI_result, querybuf.data); + + /* ++ * Disables the Row-level stuff during the internal consistency checks. ++ */ ++ save_rowlv = rowlvSetPerformingMode(ROWLV_BYPASS_MODE); ++ ++ /* + * Run the plan. For safety we force a current snapshot to be used. (In + * serializable mode, this arguably violates serializability, but we + * really haven't got much choice.) We don't need to register the +*************** RI_Initial_Check(Trigger *trigger, Relat +*** 2771,2776 **** +--- 2778,2786 ---- + InvalidSnapshot, + true, false, 1); + ++ /* Restore Row-level stuff */ ++ rowlvSetPerformingMode(save_rowlv); ++ + /* Check result */ + if (spi_result != SPI_OK_SELECT) + elog(ERROR, "SPI_execute_snapshot returned %d", spi_result); +*************** ri_PerformCheck(RI_QueryKey *qkey, SPIPl +*** 3265,3270 **** +--- 3275,3281 ---- + 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]; + +*************** ri_PerformCheck(RI_QueryKey *qkey, SPIPl +*** 3348,3359 **** +--- 3359,3377 ---- + 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 -Nrpc blob/src/backend/utils/adt/tid.c sepgsql/src/backend/utils/adt/tid.c +*** blob/src/backend/utils/adt/tid.c Sat Jan 3 13:01:35 2009 +--- sepgsql/src/backend/utils/adt/tid.c Sun Dec 20 16:30:19 2009 +*************** +*** 27,32 **** +--- 27,33 ---- + #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" +*************** currtid_byreloid(PG_FUNCTION_ARGS) +*** 347,352 **** +--- 348,355 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(rel)); ++ /* SELinux checks */ ++ sepgsql_relation_get_transaction_id(RelationGetRelid(rel)); + + if (rel->rd_rel->relkind == RELKIND_VIEW) + return currtid_for_view(rel, tid); +*************** currtid_byrelname(PG_FUNCTION_ARGS) +*** 377,382 **** +--- 380,387 ---- + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_CLASS, + RelationGetRelationName(rel)); ++ /* SELinux checks */ ++ sepgsql_relation_get_transaction_id(RelationGetRelid(rel)); + + if (rel->rd_rel->relkind == RELKIND_VIEW) + return currtid_for_view(rel, tid); +diff -Nrpc blob/src/backend/utils/adt/trigfuncs.c sepgsql/src/backend/utils/adt/trigfuncs.c +*** blob/src/backend/utils/adt/trigfuncs.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/backend/utils/adt/trigfuncs.c Tue Sep 8 23:55:48 2009 +*************** suppress_redundant_updates_trigger(PG_FU +*** 76,81 **** +--- 76,85 ---- + !OidIsValid(HeapTupleHeaderGetOid(newheader))) + HeapTupleHeaderSetOid(newheader, HeapTupleHeaderGetOid(oldheader)); + ++ if (HeapTupleHeaderHasSecid(newheader) && ++ !OidIsValid(HeapTupleHeaderGetSecid(newheader))) ++ HeapTupleHeaderSetSecid(newheader, HeapTupleHeaderGetSecid(oldheader)); ++ + /* if the tuple payload is the same ... */ + if (newtuple->t_len == oldtuple->t_len && + newheader->t_hoff == oldheader->t_hoff && +diff -Nrpc blob/src/backend/utils/cache/plancache.c sepgsql/src/backend/utils/cache/plancache.c +*** blob/src/backend/utils/cache/plancache.c Thu Mar 18 09:43:03 2010 +--- sepgsql/src/backend/utils/cache/plancache.c Thu Mar 18 01:55:40 2010 +*************** PlanCacheComputeResultDesc(List *stmt_li +*** 859,870 **** + if (IsA(node, Query)) + { + query = (Query *) node; +! return ExecCleanTypeFromTL(query->targetList, false); + } + if (IsA(node, PlannedStmt)) + { + pstmt = (PlannedStmt *) node; +! return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false); + } + /* other cases shouldn't happen, but return NULL */ + break; +--- 859,870 ---- + if (IsA(node, Query)) + { + query = (Query *) node; +! return ExecCleanTypeFromTL(query->targetList, false, false); + } + if (IsA(node, PlannedStmt)) + { + pstmt = (PlannedStmt *) node; +! return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false, false); + } + /* other cases shouldn't happen, but return NULL */ + break; +*************** PlanCacheComputeResultDesc(List *stmt_li +*** 875,887 **** + { + query = (Query *) node; + Assert(query->returningList); +! return ExecCleanTypeFromTL(query->returningList, false); + } + if (IsA(node, PlannedStmt)) + { + pstmt = (PlannedStmt *) node; + Assert(pstmt->returningLists); +! return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false); + } + /* other cases shouldn't happen, but return NULL */ + break; +--- 875,888 ---- + { + query = (Query *) node; + Assert(query->returningList); +! return ExecCleanTypeFromTL(query->returningList, false, false); + } + if (IsA(node, PlannedStmt)) + { + pstmt = (PlannedStmt *) node; + Assert(pstmt->returningLists); +! return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), +! false, false); + } + /* other cases shouldn't happen, but return NULL */ + break; +diff -Nrpc blob/src/backend/utils/cache/relcache.c sepgsql/src/backend/utils/cache/relcache.c +*** blob/src/backend/utils/cache/relcache.c Thu Mar 18 09:43:03 2010 +--- sepgsql/src/backend/utils/cache/relcache.c Thu Mar 18 01:55:40 2010 +*************** +*** 48,53 **** +--- 48,54 ---- + #include "catalog/pg_operator.h" + #include "catalog/pg_proc.h" + #include "catalog/pg_rewrite.h" ++ #include "catalog/pg_security.h" + #include "catalog/pg_trigger.h" + #include "catalog/pg_type.h" + #include "commands/trigger.h" +*************** RelationBuildDesc(Oid targetRelId, bool +*** 862,867 **** +--- 863,872 ---- + /* extract reloptions if any */ + RelationParseRelOptions(relation, pg_class_tuple); + ++ /* Fixup relation->rd_att->tdhassecid */ ++ RelationGetDescr(relation)->tdhassecid ++ = securityTupleDescHasSecid(relid, relp->relkind); ++ + /* + * initialize the relation lock manager information + */ +*************** formrdesc(const char *relationName, Oid +*** 1456,1461 **** +--- 1461,1471 ---- + RelationGetRelid(relation) = relation->rd_att->attrs[0]->attrelid; + relation->rd_rel->relfilenode = RelationGetRelid(relation); + ++ /* Fixup relation->rd_att->tdhassecid */ ++ RelationGetDescr(relation)->tdhassecid ++ = securityTupleDescHasSecid(RelationGetRelid(relation), ++ RELKIND_RELATION); ++ + /* + * initialize the relation lock manager information + */ +*************** BuildHardcodedDescriptor(int natts, Form +*** 2832,2837 **** +--- 2842,2854 ---- + result = CreateTemplateTupleDesc(natts, hasoids); + result->tdtypeid = RECORDOID; /* not right, but we don't care */ + result->tdtypmod = -1; ++ /* ++ * NOTE: we assume the returned TupleDesc is only used for ++ * references to toast'ed data, and it is not delivered to ++ * heap_form_tuple(), so TupleDesc->tdhassecid don't give us ++ * any effect. ++ * We omit to invoke securityTupleDescHasSecid() here. ++ */ + + for (i = 0; i < natts; i++) + { +*************** load_relcache_init_file(void) +*** 3586,3591 **** +--- 3603,3613 ---- + rel->rd_options = NULL; + } + ++ /* Fixup rel->rd_att->tdhassecid */ ++ RelationGetDescr(rel)->tdhassecid ++ = securityTupleDescHasSecid(RelationGetRelid(rel), ++ RelationGetForm(rel)->relkind); ++ + /* mark not-null status */ + if (has_not_null) + { +diff -Nrpc blob/src/backend/utils/fmgr/dfmgr.c sepgsql/src/backend/utils/fmgr/dfmgr.c +*** blob/src/backend/utils/fmgr/dfmgr.c Sun Sep 6 19:40:49 2009 +--- sepgsql/src/backend/utils/fmgr/dfmgr.c Thu Sep 17 17:04:16 2009 +*************** +*** 23,28 **** +--- 23,29 ---- + #endif + #include "lib/stringinfo.h" + #include "miscadmin.h" ++ #include "security/sepgsql.h" + #include "utils/dynamic_loader.h" + #include "utils/hsearch.h" + +*************** load_external_function(char *filename, c +*** 109,114 **** +--- 110,118 ---- + /* Expand the possibly-abbreviated filename to an exact path name */ + fullname = expand_dynamic_library_name(filename); + ++ /* SELinux checks db_database:{load_module} */ ++ sepgsql_database_load_module(MyDatabaseId, fullname); ++ + /* Load the shared library, unless we already did */ + lib_handle = internal_load_library(fullname); + +*************** load_file(const char *filename, bool res +*** 149,154 **** +--- 153,161 ---- + /* Expand the possibly-abbreviated filename to an exact path name */ + fullname = expand_dynamic_library_name(filename); + ++ /* SELinux checks db_database:{load_module} */ ++ sepgsql_database_load_module(MyDatabaseId, fullname); ++ + /* Unload the library if currently loaded */ + internal_unload_library(fullname); + +diff -Nrpc blob/src/backend/utils/fmgr/fmgr.c sepgsql/src/backend/utils/fmgr/fmgr.c +*** blob/src/backend/utils/fmgr/fmgr.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/backend/utils/fmgr/fmgr.c Sun Dec 20 16:30:19 2009 +*************** +*** 24,29 **** +--- 24,30 ---- + #include "miscadmin.h" + #include "nodes/nodeFuncs.h" + #include "pgstat.h" ++ #include "security/sepgsql.h" + #include "utils/builtins.h" + #include "utils/fmgrtab.h" + #include "utils/guc.h" +*************** fmgr_info_cxt_security(Oid functionId, F +*** 232,237 **** +--- 233,239 ---- + */ + if (!ignore_security && + (procedureStruct->prosecdef || ++ sepgsql_proc_entrypoint(procedureTuple) || + !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig))) + { + finfo->fn_addr = fmgr_security_definer; +*************** struct fmgr_security_definer_cache +*** 860,865 **** +--- 862,868 ---- + { + 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 */ + }; + +*************** fmgr_security_definer(PG_FUNCTION_ARGS) +*** 881,886 **** +--- 884,890 ---- + FmgrInfo *save_flinfo; + Oid save_userid; + int save_sec_context; ++ char *save_label = NULL; + volatile int save_nestlevel; + PgStat_FunctionCallUsage fcusage; + +*************** fmgr_security_definer(PG_FUNCTION_ARGS) +*** 910,915 **** +--- 914,922 ---- + 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) +*************** fmgr_security_definer(PG_FUNCTION_ARGS) +*** 936,941 **** +--- 943,950 ---- + 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) + { +*************** fmgr_security_definer(PG_FUNCTION_ARGS) +*** 983,988 **** +--- 992,999 ---- + AtEOXact_GUC(true, save_nestlevel); + if (OidIsValid(fcache->userid)) + SetUserIdAndSecContext(save_userid, save_sec_context); ++ if (fcache->seclabel) ++ sepgsqlSetClientLabel(save_label); + + return result; + } +diff -Nrpc blob/src/backend/utils/init/postinit.c sepgsql/src/backend/utils/init/postinit.c +*** blob/src/backend/utils/init/postinit.c Sun Sep 6 19:40:49 2009 +--- sepgsql/src/backend/utils/init/postinit.c Sun Dec 20 00:41:22 2009 +*************** +*** 32,37 **** +--- 32,38 ---- + #include "pgstat.h" + #include "postmaster/autovacuum.h" + #include "postmaster/postmaster.h" ++ #include "security/sepgsql.h" + #include "storage/backendid.h" + #include "storage/bufmgr.h" + #include "storage/fd.h" +*************** CheckMyDatabase(const char *name, bool a +*** 201,207 **** + name))); + + /* +! * Check privilege to connect to the database. (The am_superuser test + * is redundant, but since we have the flag, might as well check it + * and save a few cycles.) + */ +--- 202,208 ---- + name))); + + /* +! * Check privilege to connect to the database. (The am_superuser test + * is redundant, but since we have the flag, might as well check it + * and save a few cycles.) + */ +*************** CheckMyDatabase(const char *name, bool a +*** 213,218 **** +--- 214,222 ---- + errmsg("permission denied for database \"%s\"", name), + errdetail("User does not have CONNECT privilege."))); + ++ /* SELinux: db_database:{access} */ ++ sepgsql_database_access(MyDatabaseId); ++ + /* + * Check connection limit for this database. + * +*************** InitPostgres(const char *in_dbname, Oid +*** 607,612 **** +--- 611,619 ---- + /* 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 -Nrpc blob/src/backend/utils/misc/guc.c sepgsql/src/backend/utils/misc/guc.c +*** blob/src/backend/utils/misc/guc.c Thu Mar 18 09:43:03 2010 +--- sepgsql/src/backend/utils/misc/guc.c Thu Mar 18 01:55:40 2010 +*************** +*** 57,62 **** +--- 57,63 ---- + #include "postmaster/syslogger.h" + #include "postmaster/walwriter.h" + #include "regex/regex.h" ++ #include "security/sepgsql.h" + #include "storage/bufmgr.h" + #include "storage/fd.h" + #include "tcop/tcopprot.h" +*************** static const struct config_enum_entry is +*** 258,263 **** +--- 259,276 ---- + {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}, +*************** static struct config_bool ConfigureNames +*** 1222,1227 **** +--- 1235,1258 ---- + &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, +*************** static struct config_enum ConfigureNames +*** 2651,2657 **** + ®ex_flavor, + REG_ADVANCED, regex_flavor_options, NULL, NULL + }, +! + { + {"session_replication_role", PGC_SUSET, CLIENT_CONN_STATEMENT, + gettext_noop("Sets the session's behavior for triggers and rewrite rules."), +--- 2682,2698 ---- + ®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 -Nrpc blob/src/backend/utils/misc/postgresql.conf.sample sepgsql/src/backend/utils/misc/postgresql.conf.sample +*** blob/src/backend/utils/misc/postgresql.conf.sample Thu Mar 18 09:43:03 2010 +--- sepgsql/src/backend/utils/misc/postgresql.conf.sample Thu Mar 18 01:55:40 2010 +*************** +*** 51,57 **** + + + #------------------------------------------------------------------------------ +! # CONNECTIONS AND AUTHENTICATION + #------------------------------------------------------------------------------ + + # - Connection Settings - +--- 51,57 ---- + + + #------------------------------------------------------------------------------ +! # CONNECTIONS, AUTHENTICATION AND SECURITY + #------------------------------------------------------------------------------ + + # - Connection Settings - +*************** +*** 96,102 **** + # 0 selects the system default + #tcp_keepalives_count = 0 # TCP_KEEPCNT; + # 0 selects the system default +! + + #------------------------------------------------------------------------------ + # RESOURCE USAGE (except WAL) +--- 96,102 ---- + # 0 selects the system default + #tcp_keepalives_count = 0 # TCP_KEEPCNT; + # 0 selects the system default +! #sepostgresql = off # SE-PostgreSQL support + + #------------------------------------------------------------------------------ + # RESOURCE USAGE (except WAL) +diff -Nrpc blob/src/bin/initdb/initdb.c sepgsql/src/bin/initdb/initdb.c +*** blob/src/bin/initdb/initdb.c Fri Dec 18 09:40:55 2009 +--- sepgsql/src/bin/initdb/initdb.c Fri Dec 18 10:27:56 2009 +*************** static bool debug = false; +*** 87,92 **** +--- 87,93 ---- + static bool noclean = false; + static bool show_setting = false; + static char *xlog_dir = ""; ++ static bool enable_selinux = false; + + + /* internal vars */ +*************** setup_config(void) +*** 1205,1210 **** +--- 1206,1218 ---- + "#default_text_search_config = 'pg_catalog.simple'", + repltok); + ++ if (enable_selinux) ++ { ++ strcpy(repltok, "sepostgresql = on"); ++ conflines = replace_token(conflines, ++ "#sepostgresql = off", repltok); ++ } ++ + snprintf(path, sizeof(path), "%s/postgresql.conf", pg_data); + + writefile(path, conflines); +*************** usage(const char *progname) +*** 2444,2449 **** +--- 2452,2458 ---- + printf(_(" -U, --username=NAME database superuser name\n")); + printf(_(" -W, --pwprompt prompt for a password for the new superuser\n")); + printf(_(" -X, --xlogdir=XLOGDIR location for the transaction log directory\n")); ++ printf(_(" --enable-selinux enables SELinux support, if compiled\n")); + printf(_("\nLess commonly used options:\n")); + printf(_(" -d, --debug generate lots of debugging output\n")); + printf(_(" -L DIRECTORY where to find the input files\n")); +*************** main(int argc, char *argv[]) +*** 2479,2484 **** +--- 2488,2494 ---- + {"auth", required_argument, NULL, 'A'}, + {"pwprompt", no_argument, NULL, 'W'}, + {"pwfile", required_argument, NULL, 9}, ++ {"enable-selinux", no_argument, NULL, 10}, + {"username", required_argument, NULL, 'U'}, + {"help", no_argument, NULL, '?'}, + {"version", no_argument, NULL, 'V'}, +*************** main(int argc, char *argv[]) +*** 2595,2600 **** +--- 2605,2613 ---- + case 9: + pwfilename = xstrdup(optarg); + break; ++ case 10: ++ enable_selinux = true; ++ break; + case 's': + show_setting = true; + break; +diff -Nrpc blob/src/bin/pg_dump/pg_dump.c sepgsql/src/bin/pg_dump/pg_dump.c +*** blob/src/bin/pg_dump/pg_dump.c Thu Mar 18 09:43:03 2010 +--- sepgsql/src/bin/pg_dump/pg_dump.c Thu Mar 18 01:55:40 2010 +*************** static int disable_dollar_quoting = 0; +*** 112,117 **** +--- 112,119 ---- + static int dump_inserts = 0; + static int column_inserts = 0; + ++ /* flag to turn on/off security_context */ ++ static int security_context = 0; + + static void help(const char *progname); + static void expand_schema_name_patterns(SimpleStringList *patterns, +*************** main(int argc, char **argv) +*** 277,282 **** +--- 279,285 ---- + {"no-tablespaces", no_argument, &outputNoTablespaces, 1}, + {"role", required_argument, NULL, 3}, + {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ++ {"security-context", no_argument, &security_context, 1}, + + {NULL, 0, NULL, 0} + }; +*************** main(int argc, char **argv) +*** 425,430 **** +--- 428,435 ---- + outputNoTablespaces = 1; + else if (strcmp(optarg, "use-set-session-authorization") == 0) + use_setsessauth = 1; ++ else if (strcmp(optarg, "security-context") == 0) ++ security_context = 1; + else + { + fprintf(stderr, +*************** main(int argc, char **argv) +*** 573,578 **** +--- 578,605 ---- + std_strings = PQparameterStatus(g_conn, "standard_conforming_strings"); + g_fout->std_strings = (std_strings && strcmp(std_strings, "on") == 0); + ++ /* Check availability of SE-PostgreSQL */ ++ if (security_context > 0) ++ { ++ PGresult *res; ++ ++ res = PQexec(g_conn, "SHOW sepostgresql"); ++ if (PQresultStatus(res) != PGRES_TUPLES_OK || ++ PQntuples(res) != 1 || ++ strcmp(PQgetvalue(res, 0, 0), "on") != 0) ++ { ++ write_msg(NULL, "SE-PostgreSQL is not available now."); ++ exit(1); ++ } ++ } ++ ++ /* ++ * It needs to force column insertion mode, when --inserts ++ * and either --security-label or --security-acl is given. ++ */ ++ if (security_context > 0 && dump_inserts) ++ column_inserts = 1; ++ + /* Set the role if requested */ + if (use_role && g_fout->remoteVersion >= 80100) + { +*************** help(const char *progname) +*** 826,831 **** +--- 853,860 ---- + printf(_(" --use-set-session-authorization\n" + " use SET SESSION AUTHORIZATION commands instead of\n" + " ALTER OWNER commands to set ownership\n")); ++ printf(_(" --security-label dump SE-PostgreSQL security labels\n")); ++ printf(_(" --security-acl dump row-level database ACLs\n")); + + printf(_("\nConnection options:\n")); + printf(_(" -h, --host=HOSTNAME database server host or socket directory\n")); +*************** dumpTableData_insert(Archive *fout, void +*** 1227,1233 **** + if (fout->remoteVersion >= 70100) + { + appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " +! "SELECT * FROM ONLY %s", + fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, + classname)); + } +--- 1256,1263 ---- + if (fout->remoteVersion >= 70100) + { + appendPQExpBuffer(q, "DECLARE _pg_dump_cursor CURSOR FOR " +! "SELECT %s* FROM ONLY %s", +! (security_context > 0 ? "security_context, " : ""), + fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, + classname)); + } +*************** dumpDatabase(Archive *AH) +*** 1583,1589 **** + i_collate, + i_ctype, + i_frozenxid, +! i_tablespace; + CatalogId dbCatId; + DumpId dbDumpId; + const char *datname, +--- 1613,1620 ---- + i_collate, + i_ctype, + i_frozenxid, +! i_tablespace, +! i_seclabel; + CatalogId dbCatId; + DumpId dbDumpId; + const char *datname, +*************** dumpDatabase(Archive *AH) +*** 1591,1597 **** + *encoding, + *collate, + *ctype, +! *tablespace; + uint32 frozenxid; + + datname = PQdb(g_conn); +--- 1622,1629 ---- + *encoding, + *collate, + *ctype, +! *tablespace, +! *seclabel; + uint32 frozenxid; + + datname = PQdb(g_conn); +*************** dumpDatabase(Archive *AH) +*** 1610,1620 **** + "pg_encoding_to_char(encoding) AS encoding, " + "datcollate, datctype, datfrozenxid, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " +! "shobj_description(oid, 'pg_database') AS description " +! + "FROM pg_database " + "WHERE datname = ", +! username_subquery); + appendStringLiteralAH(dbQry, datname, AH); + } + else if (g_fout->remoteVersion >= 80200) +--- 1642,1653 ---- + "pg_encoding_to_char(encoding) AS encoding, " + "datcollate, datctype, datfrozenxid, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " +! "shobj_description(oid, 'pg_database') AS description, " +! "%s as security_context " + "FROM pg_database " + "WHERE datname = ", +! username_subquery, +! security_context ? "security_context" : "NULL"); + appendStringLiteralAH(dbQry, datname, AH); + } + else if (g_fout->remoteVersion >= 80200) +*************** dumpDatabase(Archive *AH) +*** 1624,1631 **** + "pg_encoding_to_char(encoding) AS encoding, " + "NULL AS datcollate, NULL AS datctype, datfrozenxid, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " +! "shobj_description(oid, 'pg_database') AS description " +! + "FROM pg_database " + "WHERE datname = ", + username_subquery); +--- 1657,1664 ---- + "pg_encoding_to_char(encoding) AS encoding, " + "NULL AS datcollate, NULL AS datctype, datfrozenxid, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " +! "shobj_description(oid, 'pg_database') AS description, " +! "NULL as security_context " + "FROM pg_database " + "WHERE datname = ", + username_subquery); +*************** dumpDatabase(Archive *AH) +*** 1637,1643 **** + "(%s datdba) AS dba, " + "pg_encoding_to_char(encoding) AS encoding, " + "NULL AS datcollate, NULL AS datctype, datfrozenxid, " +! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace " + "FROM pg_database " + "WHERE datname = ", + username_subquery); +--- 1670,1677 ---- + "(%s datdba) AS dba, " + "pg_encoding_to_char(encoding) AS encoding, " + "NULL AS datcollate, NULL AS datctype, datfrozenxid, " +! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = dattablespace) AS tablespace, " +! "NULL as security_context " + "FROM pg_database " + "WHERE datname = ", + username_subquery); +*************** dumpDatabase(Archive *AH) +*** 1650,1656 **** + "pg_encoding_to_char(encoding) AS encoding, " + "NULL AS datcollate, NULL AS datctype, " + "0 AS datfrozenxid, " +! "NULL AS tablespace " + "FROM pg_database " + "WHERE datname = ", + username_subquery); +--- 1684,1691 ---- + "pg_encoding_to_char(encoding) AS encoding, " + "NULL AS datcollate, NULL AS datctype, " + "0 AS datfrozenxid, " +! "NULL AS tablespace, " +! "NULL AS security_context " + "FROM pg_database " + "WHERE datname = ", + username_subquery); +*************** dumpDatabase(Archive *AH) +*** 1665,1671 **** + "pg_encoding_to_char(encoding) AS encoding, " + "NULL AS datcollate, NULL AS datctype, " + "0 AS datfrozenxid, " +! "NULL AS tablespace " + "FROM pg_database " + "WHERE datname = ", + username_subquery); +--- 1700,1707 ---- + "pg_encoding_to_char(encoding) AS encoding, " + "NULL AS datcollate, NULL AS datctype, " + "0 AS datfrozenxid, " +! "NULL AS tablespace, " +! "NULL as security_context " + "FROM pg_database " + "WHERE datname = ", + username_subquery); +*************** dumpDatabase(Archive *AH) +*** 1699,1704 **** +--- 1735,1741 ---- + i_ctype = PQfnumber(res, "datctype"); + i_frozenxid = PQfnumber(res, "datfrozenxid"); + i_tablespace = PQfnumber(res, "tablespace"); ++ i_seclabel = PQfnumber(res, "security_context"); + + dbCatId.tableoid = atooid(PQgetvalue(res, 0, i_tableoid)); + dbCatId.oid = atooid(PQgetvalue(res, 0, i_oid)); +*************** dumpDatabase(Archive *AH) +*** 1708,1713 **** +--- 1745,1751 ---- + ctype = PQgetvalue(res, 0, i_ctype); + frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid)); + tablespace = PQgetvalue(res, 0, i_tablespace); ++ seclabel = PQgetvalue(res, 0, i_seclabel); + + appendPQExpBuffer(creaQry, "CREATE DATABASE %s WITH TEMPLATE = template0", + fmtId(datname)); +*************** dumpDatabase(Archive *AH) +*** 1729,1734 **** +--- 1767,1775 ---- + if (strlen(tablespace) > 0 && strcmp(tablespace, "pg_default") != 0) + appendPQExpBuffer(creaQry, " TABLESPACE = %s", + fmtId(tablespace)); ++ if (strlen(seclabel) > 0) ++ appendPQExpBuffer(creaQry, " SECURITY_CONTEXT = '%s'", seclabel); ++ + appendPQExpBuffer(creaQry, ";\n"); + + if (binary_upgrade) +*************** getTables(int *numTables) +*** 3230,3235 **** +--- 3271,3277 ---- + int i_reltablespace; + int i_reloptions; + int i_toastreloptions; ++ int i_relseclabel; + + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); +*************** getTables(int *numTables) +*** 3271,3277 **** + "d.refobjsubid AS owning_col, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "array_to_string(c.reloptions, ', ') AS reloptions, " +! "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +--- 3313,3320 ---- + "d.refobjsubid AS owning_col, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "array_to_string(c.reloptions, ', ') AS reloptions, " +! "array_to_string(array(SELECT 'toast.' || x FROM unnest(tc.reloptions) x), ', ') AS toast_reloptions, " +! "%s as security_context " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +*************** getTables(int *numTables) +*** 3282,3287 **** +--- 3325,3331 ---- + "WHERE c.relkind in ('%c', '%c', '%c', '%c') " + "ORDER BY c.oid", + username_subquery, ++ security_context ? "c.security_context" : "NULL", + RELKIND_SEQUENCE, + RELKIND_RELATION, RELKIND_SEQUENCE, + RELKIND_VIEW, RELKIND_COMPOSITE_TYPE); +*************** getTables(int *numTables) +*** 3303,3309 **** + "d.refobjsubid AS owning_col, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "array_to_string(c.reloptions, ', ') AS reloptions, " +! "NULL AS toast_reloptions " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +--- 3347,3354 ---- + "d.refobjsubid AS owning_col, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "array_to_string(c.reloptions, ', ') AS reloptions, " +! "NULL AS toast_reloptions, " +! "NULL as security_context " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +*************** getTables(int *numTables) +*** 3334,3340 **** + "d.refobjsubid AS owning_col, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "NULL AS reloptions, " +! "NULL AS toast_reloptions " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +--- 3379,3386 ---- + "d.refobjsubid AS owning_col, " + "(SELECT spcname FROM pg_tablespace t WHERE t.oid = c.reltablespace) AS reltablespace, " + "NULL AS reloptions, " +! "NULL AS toast_reloptions, " +! "NULL as security_context " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +*************** getTables(int *numTables) +*** 3365,3371 **** + "d.refobjsubid AS owning_col, " + "NULL AS reltablespace, " + "NULL AS reloptions, " +! "NULL AS toast_reloptions " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +--- 3411,3418 ---- + "d.refobjsubid AS owning_col, " + "NULL AS reltablespace, " + "NULL AS reloptions, " +! "NULL AS toast_reloptions, " +! "NULL as security_context " + "FROM pg_class c " + "LEFT JOIN pg_depend d ON " + "(c.relkind = '%c' AND " +*************** getTables(int *numTables) +*** 3392,3398 **** + "NULL::int4 AS owning_col, " + "NULL AS reltablespace, " + "NULL AS reloptions, " +! "NULL AS toast_reloptions " + "FROM pg_class " + "WHERE relkind IN ('%c', '%c', '%c') " + "ORDER BY oid", +--- 3439,3446 ---- + "NULL::int4 AS owning_col, " + "NULL AS reltablespace, " + "NULL AS reloptions, " +! "NULL AS toast_reloptions, " +! "NULL AS security_context " + "FROM pg_class " + "WHERE relkind IN ('%c', '%c', '%c') " + "ORDER BY oid", +*************** getTables(int *numTables) +*** 3414,3420 **** + "NULL::int4 AS owning_col, " + "NULL AS reltablespace, " + "NULL AS reloptions, " +! "NULL AS toast_reloptions " + "FROM pg_class " + "WHERE relkind IN ('%c', '%c', '%c') " + "ORDER BY oid", +--- 3462,3469 ---- + "NULL::int4 AS owning_col, " + "NULL AS reltablespace, " + "NULL AS reloptions, " +! "NULL AS toast_reloptions, " +! "NULL AS security_context " + "FROM pg_class " + "WHERE relkind IN ('%c', '%c', '%c') " + "ORDER BY oid", +*************** getTables(int *numTables) +*** 3446,3452 **** + "NULL::int4 AS owning_col, " + "NULL AS reltablespace, " + "NULL AS reloptions, " +! "NULL AS toast_reloptions " + "FROM pg_class c " + "WHERE relkind IN ('%c', '%c') " + "ORDER BY oid", +--- 3495,3502 ---- + "NULL::int4 AS owning_col, " + "NULL AS reltablespace, " + "NULL AS reloptions, " +! "NULL AS toast_reloptions, " +! "NULL as security_context " + "FROM pg_class c " + "WHERE relkind IN ('%c', '%c') " + "ORDER BY oid", +*************** getTables(int *numTables) +*** 3491,3496 **** +--- 3541,3547 ---- + i_reltablespace = PQfnumber(res, "reltablespace"); + i_reloptions = PQfnumber(res, "reloptions"); + i_toastreloptions = PQfnumber(res, "toast_reloptions"); ++ i_relseclabel = PQfnumber(res, "security_context"); + + if (lockWaitTimeout && g_fout->remoteVersion >= 70300) + { +*************** getTables(int *numTables) +*** 3538,3543 **** +--- 3589,3595 ---- + tblinfo[i].reltablespace = strdup(PQgetvalue(res, i, i_reltablespace)); + tblinfo[i].reloptions = strdup(PQgetvalue(res, i, i_reloptions)); + tblinfo[i].toast_reloptions = strdup(PQgetvalue(res, i, i_toastreloptions)); ++ tblinfo[i].relseclabel = strdup(PQgetvalue(res, i, i_relseclabel)); + + /* other fields were zeroed above */ + +*************** getTableAttrs(TableInfo *tblinfo, int nu +*** 4737,4742 **** +--- 4789,4795 ---- + int i_attlen; + int i_attalign; + int i_attislocal; ++ int i_attseclabel; + PGresult *res; + int ntups; + bool hasdefaults; +*************** getTableAttrs(TableInfo *tblinfo, int nu +*** 4781,4792 **** + "a.attstattarget, a.attstorage, t.typstorage, " + "a.attnotnull, a.atthasdef, a.attisdropped, " + "a.attlen, a.attalign, a.attislocal, " +! "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname " + "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " + "ON a.atttypid = t.oid " + "WHERE a.attrelid = '%u'::pg_catalog.oid " + "AND a.attnum > 0::pg_catalog.int2 " + "ORDER BY a.attrelid, a.attnum", + tbinfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 70100) +--- 4834,4847 ---- + "a.attstattarget, a.attstorage, t.typstorage, " + "a.attnotnull, a.atthasdef, a.attisdropped, " + "a.attlen, a.attalign, a.attislocal, " +! "pg_catalog.format_type(t.oid,a.atttypmod) AS atttypname, " +! "%s as security_context " + "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " + "ON a.atttypid = t.oid " + "WHERE a.attrelid = '%u'::pg_catalog.oid " + "AND a.attnum > 0::pg_catalog.int2 " + "ORDER BY a.attrelid, a.attnum", ++ security_context ? "a.security_context" : "NULL", + tbinfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 70100) +*************** getTableAttrs(TableInfo *tblinfo, int nu +*** 4801,4807 **** + "t.typstorage, a.attnotnull, a.atthasdef, " + "false AS attisdropped, a.attlen, " + "a.attalign, false AS attislocal, " +! "format_type(t.oid,a.atttypmod) AS atttypname " + "FROM pg_attribute a LEFT JOIN pg_type t " + "ON a.atttypid = t.oid " + "WHERE a.attrelid = '%u'::oid " +--- 4856,4863 ---- + "t.typstorage, a.attnotnull, a.atthasdef, " + "false AS attisdropped, a.attlen, " + "a.attalign, false AS attislocal, " +! "format_type(t.oid,a.atttypmod) AS atttypname, " +! "NULL as security_context " + "FROM pg_attribute a LEFT JOIN pg_type t " + "ON a.atttypid = t.oid " + "WHERE a.attrelid = '%u'::oid " +*************** getTableAttrs(TableInfo *tblinfo, int nu +*** 4818,4824 **** + "attnotnull, atthasdef, false AS attisdropped, " + "attlen, attalign, " + "false AS attislocal, " +! "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname " + "FROM pg_attribute a " + "WHERE attrelid = '%u'::oid " + "AND attnum > 0::int2 " +--- 4874,4881 ---- + "attnotnull, atthasdef, false AS attisdropped, " + "attlen, attalign, " + "false AS attislocal, " +! "(SELECT typname FROM pg_type WHERE oid = atttypid) AS atttypname, " +! "NULL as security_context " + "FROM pg_attribute a " + "WHERE attrelid = '%u'::oid " + "AND attnum > 0::int2 " +*************** getTableAttrs(TableInfo *tblinfo, int nu +*** 4844,4849 **** +--- 4901,4907 ---- + i_attlen = PQfnumber(res, "attlen"); + i_attalign = PQfnumber(res, "attalign"); + i_attislocal = PQfnumber(res, "attislocal"); ++ i_attseclabel = PQfnumber(res, "security_context"); + + tbinfo->numatts = ntups; + tbinfo->attnames = (char **) malloc(ntups * sizeof(char *)); +*************** getTableAttrs(TableInfo *tblinfo, int nu +*** 4856,4861 **** +--- 4914,4920 ---- + tbinfo->attlen = (int *) malloc(ntups * sizeof(int)); + tbinfo->attalign = (char *) malloc(ntups * sizeof(char)); + tbinfo->attislocal = (bool *) malloc(ntups * sizeof(bool)); ++ tbinfo->attseclabel = (char **) malloc(ntups * sizeof(char *)); + tbinfo->notnull = (bool *) malloc(ntups * sizeof(bool)); + tbinfo->attrdefs = (AttrDefInfo **) malloc(ntups * sizeof(AttrDefInfo *)); + tbinfo->inhAttrs = (bool *) malloc(ntups * sizeof(bool)); +*************** getTableAttrs(TableInfo *tblinfo, int nu +*** 4881,4886 **** +--- 4940,4946 ---- + tbinfo->attlen[j] = atoi(PQgetvalue(res, j, i_attlen)); + tbinfo->attalign[j] = *(PQgetvalue(res, j, i_attalign)); + tbinfo->attislocal[j] = (PQgetvalue(res, j, i_attislocal)[0] == 't'); ++ tbinfo->attseclabel[j] = strdup(PQgetvalue(res, j, i_attseclabel)); + tbinfo->notnull[j] = (PQgetvalue(res, j, i_attnotnull)[0] == 't'); + tbinfo->attrdefs[j] = NULL; /* fix below */ + if (PQgetvalue(res, j, i_atthasdef)[0] == 't') +*************** dumpFunc(Archive *fout, FuncInfo *finfo) +*** 7131,7136 **** +--- 7191,7197 ---- + char *proconfig; + char *procost; + char *prorows; ++ char *proseclabel; + char *lanname; + char *rettypename; + int nallargs; +*************** dumpFunc(Archive *fout, FuncInfo *finfo) +*** 7167,7175 **** + "pg_catalog.pg_get_function_result(oid) AS funcresult, " + "proiswindow, provolatile, proisstrict, prosecdef, " + "proconfig, procost, prorows, " +! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 80300) +--- 7228,7238 ---- + "pg_catalog.pg_get_function_result(oid) AS funcresult, " + "proiswindow, provolatile, proisstrict, prosecdef, " + "proconfig, procost, prorows, " +! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " +! "%s as security_context " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", ++ security_context ? "security_context" : "NULL", + finfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 80300) +*************** dumpFunc(Archive *fout, FuncInfo *finfo) +*** 7180,7186 **** + "false AS proiswindow, " + "provolatile, proisstrict, prosecdef, " + "proconfig, procost, prorows, " +! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +--- 7243,7250 ---- + "false AS proiswindow, " + "provolatile, proisstrict, prosecdef, " + "proconfig, procost, prorows, " +! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " +! "NULL AS security_context " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +*************** dumpFunc(Archive *fout, FuncInfo *finfo) +*** 7193,7199 **** + "false AS proiswindow, " + "provolatile, proisstrict, prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " +! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +--- 7257,7264 ---- + "false AS proiswindow, " + "provolatile, proisstrict, prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " +! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " +! "NULL AS security_context " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +*************** dumpFunc(Archive *fout, FuncInfo *finfo) +*** 7208,7214 **** + "false AS proiswindow, " + "provolatile, proisstrict, prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " +! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +--- 7273,7280 ---- + "false AS proiswindow, " + "provolatile, proisstrict, prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " +! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " +! "NULL AS security_context " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +*************** dumpFunc(Archive *fout, FuncInfo *finfo) +*** 7223,7229 **** + "false AS proiswindow, " + "provolatile, proisstrict, prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " +! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +--- 7289,7296 ---- + "false AS proiswindow, " + "provolatile, proisstrict, prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " +! "(SELECT lanname FROM pg_catalog.pg_language WHERE oid = prolang) AS lanname, " +! "NULL AS security_context " + "FROM pg_catalog.pg_proc " + "WHERE oid = '%u'::pg_catalog.oid", + finfo->dobj.catId.oid); +*************** dumpFunc(Archive *fout, FuncInfo *finfo) +*** 7240,7246 **** + "proisstrict, " + "false AS prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " +! "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " + "FROM pg_proc " + "WHERE oid = '%u'::oid", + finfo->dobj.catId.oid); +--- 7307,7314 ---- + "proisstrict, " + "false AS prosecdef, " + "null AS proconfig, 0 AS procost, 0 AS prorows, " +! "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname, " +! "NULL AS security_context " + "FROM pg_proc " + "WHERE oid = '%u'::oid", + finfo->dobj.catId.oid); +*************** dumpFunc(Archive *fout, FuncInfo *finfo) +*** 7257,7263 **** + "false AS proisstrict, " + "false AS prosecdef, " + "NULL AS proconfig, 0 AS procost, 0 AS prorows, " +! "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname " + "FROM pg_proc " + "WHERE oid = '%u'::oid", + finfo->dobj.catId.oid); +--- 7325,7332 ---- + "false AS proisstrict, " + "false AS prosecdef, " + "NULL AS proconfig, 0 AS procost, 0 AS prorows, " +! "(SELECT lanname FROM pg_language WHERE oid = prolang) AS lanname, " +! "NULL AS security_context " + "FROM pg_proc " + "WHERE oid = '%u'::oid", + finfo->dobj.catId.oid); +*************** dumpFunc(Archive *fout, FuncInfo *finfo) +*** 7301,7306 **** +--- 7370,7376 ---- + proconfig = PQgetvalue(res, 0, PQfnumber(res, "proconfig")); + procost = PQgetvalue(res, 0, PQfnumber(res, "procost")); + prorows = PQgetvalue(res, 0, PQfnumber(res, "prorows")); ++ proseclabel = PQgetvalue(res, 0, PQfnumber(res, "security_context")); + lanname = PQgetvalue(res, 0, PQfnumber(res, "lanname")); + + /* +*************** dumpFunc(Archive *fout, FuncInfo *finfo) +*** 7459,7464 **** +--- 7529,7537 ---- + if (prosecdef[0] == 't') + appendPQExpBuffer(q, " SECURITY DEFINER"); + ++ if (security_context > 0 && strlen(proseclabel) > 0) ++ appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'", proseclabel); ++ + /* + * COST and ROWS are emitted only if present and not default, so as not to + * break backwards-compatibility of the dump without need. Keep this code +*************** dumpTableSchema(Archive *fout, TableInfo +*** 9917,9922 **** +--- 9990,10006 ---- + if (tbinfo->notnull[j] && + (!tbinfo->inhNotNull[j] || binary_upgrade)) + appendPQExpBuffer(q, " NOT NULL"); ++ ++ /* ++ * Security label -- if SE-PostgreSQL enabled ++ */ ++ if (security_context > 0 && ++ strlen(tbinfo->attseclabel[j]) > 0 && ++ strcmp(tbinfo->relseclabel, tbinfo->attseclabel[j]) != 0) ++ appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'", ++ tbinfo->attseclabel[j]); ++ ++ actual_atts++; + } + } + +*************** dumpTableSchema(Archive *fout, TableInfo +*** 9979,9984 **** +--- 10063,10071 ---- + appendPQExpBuffer(q, ")"); + } + ++ if (security_context > 0 && strlen(tbinfo->relseclabel) > 0) ++ appendPQExpBuffer(q, " SECURITY_CONTEXT = '%s'", tbinfo->relseclabel); ++ + appendPQExpBuffer(q, ";\n"); + + /* +*************** fmtCopyColumnList(const TableInfo *ti) +*** 11550,11555 **** +--- 11637,11649 ---- + + appendPQExpBuffer(q, "("); + needComma = false; ++ ++ if (security_context > 0) ++ { ++ appendPQExpBuffer(q, "security_context"); ++ needComma = true; ++ } ++ + for (i = 0; i < numatts; i++) + { + if (attisdropped[i]) +diff -Nrpc blob/src/bin/pg_dump/pg_dump.h sepgsql/src/bin/pg_dump/pg_dump.h +*** blob/src/bin/pg_dump/pg_dump.h Thu Jun 18 10:20:52 2009 +--- sepgsql/src/bin/pg_dump/pg_dump.h Wed Jul 15 20:03:59 2009 +*************** typedef struct _tableInfo +*** 228,233 **** +--- 228,234 ---- + bool hasoids; /* does it have OIDs? */ + uint32 frozenxid; /* for restore frozen xid */ + int ncheck; /* # of CHECK expressions */ ++ char *relseclabel; /* security labels of relation */ + /* these two are set only if table is a sequence owned by a column: */ + Oid owning_tab; /* OID of table owning sequence */ + int owning_col; /* attr # of column owning sequence */ +*************** typedef struct _tableInfo +*** 249,254 **** +--- 250,256 ---- + int *attlen; /* attribute length, used by binary_upgrade */ + char *attalign; /* attribute align, used by binary_upgrade */ + bool *attislocal; /* true if attr has local definition */ ++ char **attseclabel; /* security labels of attributes */ + + /* + * Note: we need to store per-attribute notnull, default, and constraint +diff -Nrpc blob/src/bin/pg_dump/pg_dumpall.c sepgsql/src/bin/pg_dump/pg_dumpall.c +*** blob/src/bin/pg_dump/pg_dumpall.c Thu Jun 18 10:20:52 2009 +--- sepgsql/src/bin/pg_dump/pg_dumpall.c Wed Jul 15 20:03:59 2009 +*************** static int no_tablespaces = 0; +*** 69,74 **** +--- 69,77 ---- + static int use_setsessauth = 0; + static int server_version; + ++ static int security_label = 0; ++ static int security_acl = 0; ++ + static FILE *OPF; + static char *filename = NULL; + +*************** main(int argc, char *argv[]) +*** 130,135 **** +--- 133,140 ---- + {"no-tablespaces", no_argument, &no_tablespaces, 1}, + {"role", required_argument, NULL, 3}, + {"use-set-session-authorization", no_argument, &use_setsessauth, 1}, ++ {"security-label", no_argument, &security_label, 1}, ++ {"security-acl", no_argument, &security_acl, 1}, + + {NULL, 0, NULL, 0} + }; +*************** main(int argc, char *argv[]) +*** 283,288 **** +--- 288,297 ---- + no_tablespaces = 1; + else if (strcmp(optarg, "use-set-session-authorization") == 0) + use_setsessauth = 1; ++ else if (strcmp(optarg, "security-label") == 0) ++ security_label = 1; ++ else if (strcmp(optarg, "security-acl") == 0) ++ security_acl = 1; + else + { + fprintf(stderr, +*************** main(int argc, char *argv[]) +*** 328,333 **** +--- 337,346 ---- + appendPQExpBuffer(pgdumpopts, " --no-tablespaces"); + if (use_setsessauth) + appendPQExpBuffer(pgdumpopts, " --use-set-session-authorization"); ++ if (security_label) ++ appendPQExpBuffer(pgdumpopts, " --security-label"); ++ if (security_acl) ++ appendPQExpBuffer(pgdumpopts, " --security-acl"); + + if (optind < argc) + { +*************** main(int argc, char *argv[]) +*** 403,408 **** +--- 416,434 ---- + } + } + ++ if (security_label > 0) ++ { ++ PGresult *res ++ = PQexec(conn, "SHOW sepostgresql"); ++ if (PQresultStatus(res) != PGRES_TUPLES_OK || ++ PQntuples(res) != 1 || ++ strcmp(PQgetvalue(res, 0, 0), "on") != 0) ++ { ++ fprintf(stderr, "SE-PostgreSQL is not available now."); ++ exit(1); ++ } ++ } ++ + /* + * Open the output file if required, otherwise use stdout + */ +*************** dumpCreateDB(PGconn *conn) +*** 1130,1184 **** + + /* Now collect all the information about databases to dump */ + if (server_version >= 80400) +! res = executeQuery(conn, +! "SELECT datname, " + "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "datcollate, datctype, datfrozenxid, " + "datistemplate, datacl, datconnlimit, " +! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " + "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " +! "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 80100) +! res = executeQuery(conn, +! "SELECT datname, " + "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, datfrozenxid, " + "datistemplate, datacl, datconnlimit, " +! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " + "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 80000) +! res = executeQuery(conn, +! "SELECT datname, " + "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, datfrozenxid, " + "datistemplate, datacl, -1 as datconnlimit, " +! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace " + "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 70300) +! res = executeQuery(conn, +! "SELECT datname, " + "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, datfrozenxid, " + "datistemplate, datacl, -1 as datconnlimit, " +! "'pg_default' AS dattablespace " + "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 70100) +! res = executeQuery(conn, +! "SELECT datname, " + "coalesce(" + "(select usename from pg_shadow where usesysid=datdba), " + "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " + "datistemplate, '' as datacl, -1 as datconnlimit, " +! "'pg_default' AS dattablespace " + "FROM pg_database d " + "WHERE datallowconn ORDER BY 1"); + else +--- 1156,1211 ---- + + /* Now collect all the information about databases to dump */ + if (server_version >= 80400) +! appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "datcollate, datctype, datfrozenxid, " + "datistemplate, datacl, datconnlimit, " +! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, " +! "%s AS security_label " + "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " +! "WHERE datallowconn ORDER BY 1", +! security_label ? "sepgsql_raw_to_trans(datselabel)" : "null::text"); + else if (server_version >= 80100) +! appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(rolname, (select rolname from pg_authid where oid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, datfrozenxid, " + "datistemplate, datacl, datconnlimit, " +! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, " +! "null::text " + "FROM pg_database d LEFT JOIN pg_authid u ON (datdba = u.oid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 80000) +! appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, datfrozenxid, " + "datistemplate, datacl, -1 as datconnlimit, " +! "(SELECT spcname FROM pg_tablespace t WHERE t.oid = d.dattablespace) AS dattablespace, " +! "null::text " + "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 70300) +! appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(usename, (select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, datfrozenxid, " + "datistemplate, datacl, -1 as datconnlimit, " +! "'pg_default' AS dattablespace, " +! "null::text " + "FROM pg_database d LEFT JOIN pg_shadow u ON (datdba = usesysid) " + "WHERE datallowconn ORDER BY 1"); + else if (server_version >= 70100) +! appendPQExpBuffer(buf, "SELECT datname, " + "coalesce(" + "(select usename from pg_shadow where usesysid=datdba), " + "(select usename from pg_shadow where usesysid=(select datdba from pg_database where datname='template0'))), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " + "datistemplate, '' as datacl, -1 as datconnlimit, " +! "'pg_default' AS dattablespace, " +! "null::text " + "FROM pg_database d " + "WHERE datallowconn ORDER BY 1"); + else +*************** dumpCreateDB(PGconn *conn) +*** 1187,1204 **** + * Note: 7.0 fails to cope with sub-select in COALESCE, so just deal + * with getting a NULL by not printing any OWNER clause. + */ +! res = executeQuery(conn, +! "SELECT datname, " + "(select usename from pg_shadow where usesysid=datdba), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " + "'f' as datistemplate, " + "'' as datacl, -1 as datconnlimit, " +! "'pg_default' AS dattablespace " + "FROM pg_database d " + "ORDER BY 1"); + } + + for (i = 0; i < PQntuples(res); i++) + { + char *dbname = PQgetvalue(res, i, 0); +--- 1214,1233 ---- + * Note: 7.0 fails to cope with sub-select in COALESCE, so just deal + * with getting a NULL by not printing any OWNER clause. + */ +! appendPQExpBuffer(buf, "SELECT datname, " + "(select usename from pg_shadow where usesysid=datdba), " + "pg_encoding_to_char(d.encoding), " + "null::text AS datcollate, null::text AS datctype, 0 AS datfrozenxid, " + "'f' as datistemplate, " + "'' as datacl, -1 as datconnlimit, " +! "'pg_default' AS dattablespace, " +! "null::text " + "FROM pg_database d " + "ORDER BY 1"); + } + ++ res = PQexec(conn, buf->data); ++ + for (i = 0; i < PQntuples(res); i++) + { + char *dbname = PQgetvalue(res, i, 0); +*************** dumpCreateDB(PGconn *conn) +*** 1211,1216 **** +--- 1240,1246 ---- + char *dbacl = PQgetvalue(res, i, 7); + char *dbconnlimit = PQgetvalue(res, i, 8); + char *dbtablespace = PQgetvalue(res, i, 9); ++ char *dbseclabel = PQgetvalue(res, i, 9); + char *fdbname; + + fdbname = strdup(fmtId(dbname)); +*************** dumpCreateDB(PGconn *conn) +*** 1266,1271 **** +--- 1296,1305 ---- + appendPQExpBuffer(buf, " CONNECTION LIMIT = %s", + dbconnlimit); + ++ if (security_label > 0 && strlen(dbseclabel) > 0) ++ appendPQExpBuffer(buf, " SECURITY_LABEL = '%s'", ++ dbseclabel); ++ + appendPQExpBuffer(buf, ";\n"); + + if (strcmp(dbistemplate, "t") == 0) +diff -Nrpc blob/src/include/access/htup.h sepgsql/src/include/access/htup.h +*** blob/src/include/access/htup.h Thu Jun 18 10:20:52 2009 +--- sepgsql/src/include/access/htup.h Tue Sep 8 23:55:48 2009 +*************** typedef HeapTupleHeaderData *HeapTupleHe +*** 163,169 **** + #define HEAP_HASVARWIDTH 0x0002 /* has variable-width attribute(s) */ + #define HEAP_HASEXTERNAL 0x0004 /* has external stored attribute(s) */ + #define HEAP_HASOID 0x0008 /* has an object-id field */ +! /* bit 0x0010 is available */ + #define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */ + #define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */ + #define HEAP_XMAX_SHARED_LOCK 0x0080 /* xmax is shared locker */ +--- 163,169 ---- + #define HEAP_HASVARWIDTH 0x0002 /* has variable-width attribute(s) */ + #define HEAP_HASEXTERNAL 0x0004 /* has external stored attribute(s) */ + #define HEAP_HASOID 0x0008 /* has an object-id field */ +! #define HEAP_HASSECID 0x0010 /* has an security-id field */ + #define HEAP_COMBOCID 0x0020 /* t_cid is a combo cid */ + #define HEAP_XMAX_EXCL_LOCK 0x0040 /* xmax is exclusive locker */ + #define HEAP_XMAX_SHARED_LOCK 0x0080 /* xmax is shared locker */ +*************** do { \ +*** 290,295 **** +--- 290,298 ---- + (tup)->t_choice.t_datum.datum_typmod = (typmod) \ + ) + ++ #define HeapTupleHeaderHasOid(tup) \ ++ ((tup)->t_infomask & HEAP_HASOID) ++ + #define HeapTupleHeaderGetOid(tup) \ + ( \ + ((tup)->t_infomask & HEAP_HASOID) ? \ +*************** do { \ +*** 349,354 **** +--- 352,376 ---- + (tup)->t_infomask2 = ((tup)->t_infomask2 & ~HEAP_NATTS_MASK) | (natts) \ + ) + ++ #define HeapTupleHeaderHasSecid(tup) \ ++ ((tup)->t_infomask & HEAP_HASSECID) ++ ++ #define HeapTupleHeaderGetSecid(tup) \ ++ ( \ ++ HeapTupleHeaderHasSecid(tup) \ ++ ? (*(Oid *)((char *)(tup) + (tup)->t_hoff \ ++ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ ++ - sizeof(Oid))) \ ++ : InvalidOid \ ++ ) ++ ++ #define HeapTupleHeaderSetSecid(tup, secid) \ ++ do { \ ++ Assert(HeapTupleHeaderHasSecid(tup)); \ ++ *((Oid *)((char *)(tup) + (tup)->t_hoff \ ++ - (HeapTupleHeaderHasOid(tup) ? sizeof(Oid) : 0) \ ++ - sizeof(Oid))) = (secid); \ ++ } while(0) + + /* + * BITMAPLEN(NATTS) - +*************** typedef HeapTupleData *HeapTuple; +*** 549,554 **** +--- 571,584 ---- + #define HeapTupleSetOid(tuple, oid) \ + HeapTupleHeaderSetOid((tuple)->t_data, (oid)) + ++ #define HeapTupleHasSecid(tuple) \ ++ HeapTupleHeaderHasSecid((tuple)->t_data) ++ ++ #define HeapTupleGetSecid(tuple) \ ++ HeapTupleHeaderGetSecid((tuple)->t_data) ++ ++ #define HeapTupleSetSecid(tuple, secid) \ ++ HeapTupleHeaderSetSecid((tuple)->t_data, (secid)) + + /* + * WAL record definitions for heapam.c's WAL operations +diff -Nrpc blob/src/include/access/sysattr.h sepgsql/src/include/access/sysattr.h +*** blob/src/include/access/sysattr.h Sat Jan 3 12:25:21 2009 +--- sepgsql/src/include/access/sysattr.h Wed Sep 9 16:47:01 2009 +*************** +*** 25,31 **** + #define MaxTransactionIdAttributeNumber (-5) + #define MaxCommandIdAttributeNumber (-6) + #define TableOidAttributeNumber (-7) +! #define FirstLowInvalidHeapAttributeNumber (-8) + + + #endif /* SYSATTR_H */ +--- 25,43 ---- + #define MaxTransactionIdAttributeNumber (-5) + #define MaxCommandIdAttributeNumber (-6) + #define TableOidAttributeNumber (-7) +! #define SecurityAttributeNumber (-8) +! #define FirstLowInvalidHeapAttributeNumber (-9) + ++ /* ++ * Attribute names for the system-defined attributes ++ */ ++ #define SelfItemPointerAttributeName "ctid" ++ #define ObjectIdAttributeName "oid" ++ #define MinTransactionIdAttributeName "xmin" ++ #define MinCommandIdAttributeName "cmin" ++ #define MaxTransactionIdAttributeName "xmax" ++ #define MaxCommandIdAttributeName "cmax" ++ #define TableOidAttributeName "tableoid" ++ #define SecurityAttributeName "security_context" + + #endif /* SYSATTR_H */ +diff -Nrpc blob/src/include/access/tupdesc.h sepgsql/src/include/access/tupdesc.h +*** blob/src/include/access/tupdesc.h Sat Jan 3 12:25:21 2009 +--- sepgsql/src/include/access/tupdesc.h Wed Sep 9 13:14:37 2009 +*************** typedef struct tupleDesc +*** 75,80 **** +--- 75,81 ---- + Oid tdtypeid; /* composite type ID for tuple type */ + int32 tdtypmod; /* typmod for tuple type */ + bool tdhasoid; /* tuple has oid attribute in its header */ ++ bool tdhassecid; /* tuple has secid attribute in its header */ + int tdrefcount; /* reference count, or -1 if not counting */ + } *TupleDesc; + +diff -Nrpc blob/src/include/bootstrap/bootstrap.h sepgsql/src/include/bootstrap/bootstrap.h +*** blob/src/include/bootstrap/bootstrap.h Sat Jan 3 12:25:21 2009 +--- sepgsql/src/include/bootstrap/bootstrap.h Tue Dec 8 14:04:25 2009 +*************** typedef enum +*** 70,76 **** + BootstrapProcess, + StartupProcess, + BgWriterProcess, +! WalWriterProcess + } AuxProcType; + + #endif /* BOOTSTRAP_H */ +--- 70,77 ---- + BootstrapProcess, + StartupProcess, + BgWriterProcess, +! WalWriterProcess, +! SelinuxReceiverProcess, + } AuxProcType; + + #endif /* BOOTSTRAP_H */ +diff -Nrpc blob/src/include/catalog/dependency.h sepgsql/src/include/catalog/dependency.h +*** blob/src/include/catalog/dependency.h Fri Dec 18 09:40:55 2009 +--- sepgsql/src/include/catalog/dependency.h Fri Dec 18 10:27:56 2009 +*************** typedef enum ObjectClass +*** 156,161 **** +--- 156,164 ---- + extern void performDeletion(const ObjectAddress *object, + DropBehavior behavior); + ++ extern void performDeletionNoPerms(const ObjectAddress *object, ++ DropBehavior behavior); ++ + extern void performMultipleDeletions(const ObjectAddresses *objects, + DropBehavior behavior); + +diff -Nrpc blob/src/include/catalog/heap.h sepgsql/src/include/catalog/heap.h +*** blob/src/include/catalog/heap.h Thu Jun 18 10:20:52 2009 +--- sepgsql/src/include/catalog/heap.h Wed Jul 15 19:38:52 2009 +*************** extern Oid heap_create_with_catalog(cons +*** 56,62 **** + int oidinhcount, + OnCommitAction oncommit, + Datum reloptions, +! bool allow_system_table_mods); + + extern void heap_drop_with_catalog(Oid relid); + +--- 56,63 ---- + int oidinhcount, + OnCommitAction oncommit, + Datum reloptions, +! bool allow_system_table_mods, +! Oid *secLabels); + + extern void heap_drop_with_catalog(Oid relid); + +*************** extern List *heap_truncate_find_FKs(List +*** 68,79 **** + + extern void InsertPgAttributeTuple(Relation pg_attribute_rel, + Form_pg_attribute new_attribute, +! CatalogIndexState indstate); + + extern void InsertPgClassTuple(Relation pg_class_desc, + Relation new_rel_desc, + Oid new_rel_oid, +! Datum reloptions); + + extern List *AddRelationNewConstraints(Relation rel, + List *newColDefaults, +--- 69,82 ---- + + extern void InsertPgAttributeTuple(Relation pg_attribute_rel, + Form_pg_attribute new_attribute, +! CatalogIndexState indstate, +! Oid new_att_secid); + + extern void InsertPgClassTuple(Relation pg_class_desc, + Relation new_rel_desc, + Oid new_rel_oid, +! Datum reloptions, +! Oid new_rel_secid); + + extern List *AddRelationNewConstraints(Relation rel, + List *newColDefaults, +*************** extern Form_pg_attribute SystemAttribute +*** 103,108 **** +--- 106,113 ---- + extern Form_pg_attribute SystemAttributeByName(const char *attname, + bool relhasoids); + ++ extern bool SystemAttributeIsWritable(AttrNumber attnum); ++ + extern void CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind); + + extern void CheckAttributeType(const char *attname, Oid atttypid); +diff -Nrpc blob/src/include/catalog/indexing.h sepgsql/src/include/catalog/indexing.h +*** blob/src/include/catalog/indexing.h Fri Dec 18 09:40:55 2009 +--- sepgsql/src/include/catalog/indexing.h Sun Dec 20 23:35:32 2009 +*************** DECLARE_UNIQUE_INDEX(pg_type_oid_index, +*** 252,257 **** +--- 252,262 ---- + DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typname name_ops, typnamespace oid_ops)); + #define TypeNameNspIndexId 2704 + ++ DECLARE_UNIQUE_INDEX(pg_security_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 -Nrpc blob/src/include/catalog/pg_attribute.h sepgsql/src/include/catalog/pg_attribute.h +*** blob/src/include/catalog/pg_attribute.h Thu Jun 18 10:20:52 2009 +--- sepgsql/src/include/catalog/pg_attribute.h Thu Sep 10 15:29:52 2009 +*************** DATA(insert ( 1247 cmin 29 0 4 -4 0 +*** 276,281 **** +--- 276,282 ---- + DATA(insert ( 1247 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1247 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1247 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); ++ DATA(insert ( 1247 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); + + /* ---------------- + * pg_proc +*************** DATA(insert ( 1255 cmin 29 0 4 -4 0 +*** 340,345 **** +--- 341,347 ---- + DATA(insert ( 1255 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1255 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1255 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); ++ DATA(insert ( 1255 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); + + /* ---------------- + * pg_attribute +*************** DATA(insert ( 1249 cmin 29 0 4 -4 0 +*** 390,395 **** +--- 392,398 ---- + DATA(insert ( 1249 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1249 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1249 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); ++ DATA(insert ( 1249 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); + + /* ---------------- + * pg_class +*************** DATA(insert ( 1259 cmin 29 0 4 -4 0 +*** 454,459 **** +--- 457,463 ---- + DATA(insert ( 1259 xmax 28 0 4 -5 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1259 cmax 29 0 4 -6 0 -1 -1 t p i t f f t 0 _null_)); + DATA(insert ( 1259 tableoid 26 0 4 -7 0 -1 -1 t p i t f f t 0 _null_)); ++ DATA(insert ( 1259 security_context 25 0 -1 -8 0 -1 -1 f x i t f f t 0 _null_)); + + /* ---------------- + * pg_index +diff -Nrpc blob/src/include/catalog/pg_conversion_fn.h sepgsql/src/include/catalog/pg_conversion_fn.h +*** blob/src/include/catalog/pg_conversion_fn.h Thu Jun 18 10:20:52 2009 +--- sepgsql/src/include/catalog/pg_conversion_fn.h Thu Sep 17 22:10:19 2009 +*************** +*** 17,23 **** + extern Oid ConversionCreate(const char *conname, Oid connamespace, + Oid conowner, + int32 conforencoding, int32 contoencoding, +! Oid conproc, bool def); + extern void RemoveConversionById(Oid conversionOid); + extern Oid FindConversion(const char *conname, Oid connamespace); + extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding); +--- 17,23 ---- + extern Oid ConversionCreate(const char *conname, Oid connamespace, + Oid conowner, + int32 conforencoding, int32 contoencoding, +! Oid conproc, Oid consecid, bool def); + extern void RemoveConversionById(Oid conversionOid); + extern Oid FindConversion(const char *conname, Oid connamespace); + extern Oid FindDefaultConversion(Oid connamespace, int32 for_encoding, int32 to_encoding); +diff -Nrpc blob/src/include/catalog/pg_largeobject.h sepgsql/src/include/catalog/pg_largeobject.h +*** blob/src/include/catalog/pg_largeobject.h Fri Dec 18 09:40:55 2009 +--- sepgsql/src/include/catalog/pg_largeobject.h Fri Dec 18 10:27:56 2009 +*************** typedef FormData_pg_largeobject *Form_pg +*** 51,57 **** + #define Anum_pg_largeobject_pageno 2 + #define Anum_pg_largeobject_data 3 + +! extern Oid LargeObjectCreate(Oid loid); + extern void LargeObjectDrop(Oid loid); + extern void LargeObjectAlterOwner(Oid loid, Oid newOwnerId); + extern bool LargeObjectExists(Oid loid); +--- 51,57 ---- + #define Anum_pg_largeobject_pageno 2 + #define Anum_pg_largeobject_data 3 + +! 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 -Nrpc blob/src/include/catalog/pg_namespace.h sepgsql/src/include/catalog/pg_namespace.h +*** blob/src/include/catalog/pg_namespace.h Sat Jan 3 12:25:21 2009 +--- sepgsql/src/include/catalog/pg_namespace.h Wed Jul 15 19:35:52 2009 +*************** DESCR("standard public schema"); +*** 77,82 **** + /* + * prototypes for functions in pg_namespace.c + */ +! extern Oid NamespaceCreate(const char *nspName, Oid ownerId); + + #endif /* PG_NAMESPACE_H */ +--- 77,82 ---- + /* + * prototypes for functions in pg_namespace.c + */ +! extern Oid NamespaceCreate(const char *nspName, Oid ownerId, Oid nspsecid); + + #endif /* PG_NAMESPACE_H */ +diff -Nrpc blob/src/include/catalog/pg_proc.h sepgsql/src/include/catalog/pg_proc.h +*** blob/src/include/catalog/pg_proc.h Thu Jun 18 10:20:52 2009 +--- sepgsql/src/include/catalog/pg_proc.h Sun Dec 20 23:35:32 2009 +*************** DESCR("I/O"); +*** 4335,4340 **** +--- 4335,4353 ---- + 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 -Nrpc blob/src/include/catalog/pg_proc_fn.h sepgsql/src/include/catalog/pg_proc_fn.h +*** blob/src/include/catalog/pg_proc_fn.h Thu Jun 18 10:20:52 2009 +--- sepgsql/src/include/catalog/pg_proc_fn.h Wed Jul 15 19:37:35 2009 +*************** extern Oid ProcedureCreate(const char *p +*** 37,43 **** + List *parameterDefaults, + Datum proconfig, + float4 procost, +! float4 prorows); + + extern bool function_parse_error_transpose(const char *prosrc); + +--- 37,44 ---- + List *parameterDefaults, + Datum proconfig, + float4 procost, +! float4 prorows, +! Node *proseclabel); + + extern bool function_parse_error_transpose(const char *prosrc); + +diff -Nrpc blob/src/include/catalog/pg_security.h sepgsql/src/include/catalog/pg_security.h +*** blob/src/include/catalog/pg_security.h Thu Jan 1 09:00:00 1970 +--- sepgsql/src/include/catalog/pg_security.h Sun Dec 20 23:35:32 2009 +*************** +*** 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 -Nrpc blob/src/include/catalog/toasting.h sepgsql/src/include/catalog/toasting.h +*** blob/src/include/catalog/toasting.h Thu Jun 18 10:20:52 2009 +--- sepgsql/src/include/catalog/toasting.h Wed Jul 15 19:30:50 2009 +*************** DECLARE_TOAST(pg_database, 2844, 2845); +*** 58,62 **** +--- 58,65 ---- + DECLARE_TOAST(pg_shdescription, 2846, 2847); + #define PgShdescriptionToastTable 2846 + #define PgShdescriptionToastIndex 2847 ++ DECLARE_TOAST(pg_security, 3403, 3404); ++ #define PgSecurityToastTable 3403 ++ #define PgSecurityToastIndex 3404 + + #endif /* TOASTING_H */ +diff -Nrpc blob/src/include/commands/alter.h sepgsql/src/include/commands/alter.h +*** blob/src/include/commands/alter.h Sat Jan 3 12:25:21 2009 +--- sepgsql/src/include/commands/alter.h Wed Jul 15 19:37:35 2009 +*************** +*** 19,23 **** +--- 19,24 ---- + extern void ExecRenameStmt(RenameStmt *stmt); + extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt); + extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt); ++ extern void ExecAlterSecLabelStmt(AlterSecLabelStmt *stmt); + + #endif /* ALTER_H */ +diff -Nrpc blob/src/include/commands/dbcommands.h sepgsql/src/include/commands/dbcommands.h +*** blob/src/include/commands/dbcommands.h Sat Jan 3 12:25:21 2009 +--- sepgsql/src/include/commands/dbcommands.h Wed Jul 15 19:37:35 2009 +*************** extern void RenameDatabase(const char *o +*** 58,63 **** +--- 58,64 ---- + extern void AlterDatabase(AlterDatabaseStmt *stmt, bool isTopLevel); + extern void AlterDatabaseSet(AlterDatabaseSetStmt *stmt); + extern void AlterDatabaseOwner(const char *dbname, Oid newOwnerId); ++ extern void AlterDatabaseSecLabel(const char *dbname, DefElem *seclabel); + + extern Oid get_database_oid(const char *dbname); + extern char *get_database_name(Oid dbid); +diff -Nrpc blob/src/include/commands/defrem.h sepgsql/src/include/commands/defrem.h +*** blob/src/include/commands/defrem.h Thu Apr 9 00:13:21 2009 +--- sepgsql/src/include/commands/defrem.h Wed Jul 15 19:37:35 2009 +*************** extern void SetFunctionArgType(Oid funcO +*** 53,58 **** +--- 53,59 ---- + extern void RenameFunction(List *name, List *argtypes, const char *newname); + extern void AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId); + extern void AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId); ++ extern void AlterFunctionSecLabel(List *name, List *argtypes, DefElem *seclabel); + extern void AlterFunction(AlterFunctionStmt *stmt); + extern void CreateCast(CreateCastStmt *stmt); + extern void DropCast(DropCastStmt *stmt); +diff -Nrpc blob/src/include/commands/schemacmds.h sepgsql/src/include/commands/schemacmds.h +*** blob/src/include/commands/schemacmds.h Sat Jan 3 12:25:21 2009 +--- sepgsql/src/include/commands/schemacmds.h Wed Jul 15 19:37:35 2009 +*************** extern void RemoveSchemaById(Oid schemaO +*** 26,30 **** +--- 26,31 ---- + extern void RenameSchema(const char *oldname, const char *newname); + extern void AlterSchemaOwner(const char *name, Oid newOwnerId); + extern void AlterSchemaOwner_oid(Oid schemaOid, Oid newOwnerId); ++ extern void AlterSchemaSecLabel(const char *name, DefElem *seclabel); + + #endif /* SCHEMACMDS_H */ +diff -Nrpc blob/src/include/commands/tablecmds.h sepgsql/src/include/commands/tablecmds.h +*** blob/src/include/commands/tablecmds.h Thu Jun 18 10:20:52 2009 +--- sepgsql/src/include/commands/tablecmds.h Wed Jul 15 19:37:35 2009 +*************** extern void AlterRelationNamespaceIntern +*** 35,40 **** +--- 35,43 ---- + Oid oldNspOid, Oid newNspOid, + bool hasDependEntry); + ++ extern void AlterRelationSecLabel(RangeVar *relation, const char *attname, ++ ObjectType objtype, DefElem *seclabel); ++ + extern void CheckTableNotInUse(Relation rel, const char *stmt); + + extern void ExecuteTruncate(TruncateStmt *stmt); +diff -Nrpc blob/src/include/executor/executor.h sepgsql/src/include/executor/executor.h +*** blob/src/include/executor/executor.h Sun Sep 6 19:40:49 2009 +--- sepgsql/src/include/executor/executor.h Wed Sep 9 13:14:37 2009 +*************** extern TupleHashEntry FindTupleHashEntry +*** 130,136 **** + /* + * prototypes from functions in execJunk.c + */ +! extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, + TupleTableSlot *slot); + extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, + TupleDesc cleanTupType, +--- 130,136 ---- + /* + * prototypes from functions in execJunk.c + */ +! extern JunkFilter *ExecInitJunkFilter(List *targetList, bool hasoid, bool hasseclabel, + TupleTableSlot *slot); + extern JunkFilter *ExecInitJunkFilterConversion(List *targetList, + TupleDesc cleanTupType, +*************** extern void InitResultRelInfo(ResultRelI +*** 163,168 **** +--- 163,169 ---- + bool doInstrument); + extern ResultRelInfo *ExecGetTriggerResultRel(EState *estate, Oid relid); + extern bool ExecContextForcesOids(PlanState *planstate, bool *hasoids); ++ extern bool ExecContextForcesSecids(PlanState *planstate, bool *hassecids); + extern void ExecConstraints(ResultRelInfo *resultRelInfo, + TupleTableSlot *slot, EState *estate); + extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, +*************** extern void ExecInitScanTupleSlot(EState +*** 216,223 **** + extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate); + extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, + TupleDesc tupType); +! extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid); +! extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid); + extern TupleDesc ExecTypeFromExprList(List *exprList); + extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); + +--- 217,224 ---- + extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate); + extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate, + TupleDesc tupType); +! extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid, bool hasseclabel); +! extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid, bool hasseclabel); + extern TupleDesc ExecTypeFromExprList(List *exprList); + extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg); + +diff -Nrpc blob/src/include/executor/tuptable.h sepgsql/src/include/executor/tuptable.h +*** blob/src/include/executor/tuptable.h Thu Jun 18 10:20:52 2009 +--- sepgsql/src/include/executor/tuptable.h Wed Jul 15 19:38:52 2009 +*************** typedef struct TupleTableSlot +*** 127,132 **** +--- 127,133 ---- + MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */ + HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */ + long tts_off; /* saved state for slot_deform_tuple */ ++ Datum tts_seclabel; /* temp storage for the given security_label */ + } TupleTableSlot; + + #define TTS_HAS_PHYSICAL_TUPLE(slot) \ +diff -Nrpc blob/src/include/libpq/be-fsstubs.h sepgsql/src/include/libpq/be-fsstubs.h +*** blob/src/include/libpq/be-fsstubs.h Fri Dec 18 09:40:55 2009 +--- sepgsql/src/include/libpq/be-fsstubs.h Fri Dec 18 10:27:56 2009 +*************** extern Datum lo_tell(PG_FUNCTION_ARGS); +*** 37,42 **** +--- 37,45 ---- + extern Datum lo_unlink(PG_FUNCTION_ARGS); + extern Datum lo_truncate(PG_FUNCTION_ARGS); + ++ extern Datum lo_get_security(PG_FUNCTION_ARGS); ++ extern Datum lo_set_security(PG_FUNCTION_ARGS); ++ + /* + * compatibility option for access control + */ +diff -Nrpc blob/src/include/nodes/nodes.h sepgsql/src/include/nodes/nodes.h +*** blob/src/include/nodes/nodes.h Thu Jun 18 10:20:52 2009 +--- sepgsql/src/include/nodes/nodes.h Wed Jul 15 19:37:35 2009 +*************** typedef enum NodeTag +*** 337,342 **** +--- 337,343 ---- + T_CreateUserMappingStmt, + T_AlterUserMappingStmt, + T_DropUserMappingStmt, ++ T_AlterSecLabelStmt, + + /* + * TAGS FOR PARSE TREE NODES (parsenodes.h) +diff -Nrpc blob/src/include/nodes/parsenodes.h sepgsql/src/include/nodes/parsenodes.h +*** blob/src/include/nodes/parsenodes.h Fri Dec 18 09:40:55 2009 +--- sepgsql/src/include/nodes/parsenodes.h Thu Dec 24 21:59:25 2009 +*************** typedef struct ColumnDef +*** 463,468 **** +--- 463,469 ---- + 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; + + /* +*************** typedef struct CreateSchemaStmt +*** 1069,1074 **** +--- 1070,1076 ---- + NodeTag type; + char *schemaname; /* the name of the schema to create */ + char *authid; /* the owner of the created schema */ ++ Node *secLabel; /* explicitly specified security label */ + List *schemaElts; /* schema components (list of parsenodes) */ + } CreateSchemaStmt; + +*************** typedef struct CreateStmt +*** 1335,1340 **** +--- 1337,1343 ---- + List *options; /* options from WITH clause */ + OnCommitAction oncommit; /* what do we do at COMMIT? */ + char *tablespacename; /* table space to use, or NULL */ ++ List *secLabel; /* explicitly specified security label */ + } CreateStmt; + + /* ---------- +*************** typedef struct CreateSeqStmt +*** 1639,1644 **** +--- 1642,1648 ---- + NodeTag type; + RangeVar *sequence; /* the sequence to create */ + List *options; ++ Node *secLabel; + } CreateSeqStmt; + + typedef struct AlterSeqStmt +*************** typedef struct AlterOwnerStmt +*** 1993,1998 **** +--- 1997,2016 ---- + char *newowner; /* the new owner */ + } AlterOwnerStmt; + ++ /* ---------------------- ++ * Alter Object Security Label Statement ++ * ---------------------- ++ */ ++ typedef struct AlterSecLabelStmt ++ { ++ NodeTag type; ++ ObjectType objectType; /* OBJECT_TABLE, OBJECT_COLUMN, etc */ ++ RangeVar *relation; /* in case it's a table */ ++ List *object; /* in case it's some other object */ ++ List *objarg; /* argument types, if applicable */ ++ char *subname; /* column name, if needed */ ++ Node *secLabel; /* the new security label */ ++ } AlterSecLabelStmt; + + /* ---------------------- + * Create Rule Statement +diff -Nrpc blob/src/include/nodes/plannodes.h sepgsql/src/include/nodes/plannodes.h +*** blob/src/include/nodes/plannodes.h Thu Jun 18 10:20:52 2009 +--- sepgsql/src/include/nodes/plannodes.h Wed Jul 15 19:39:56 2009 +*************** +*** 16,21 **** +--- 16,22 ---- + + #include "access/sdir.h" + #include "nodes/bitmapset.h" ++ #include "nodes/parsenodes.h" + #include "nodes/primnodes.h" + #include "storage/itemptr.h" + +*************** typedef struct Scan +*** 239,244 **** +--- 240,251 ---- + { + Plan plan; + Index scanrelid; /* relid is index into the range table */ ++ ++ /* ++ * Row-level access control stuff. Zero means we don't need ++ * to apply row-level access control on the Scan. ++ */ ++ uint32 rowlvPerms; + } Scan; + + /* ---------------- +diff -Nrpc blob/src/include/nodes/relation.h sepgsql/src/include/nodes/relation.h +*** blob/src/include/nodes/relation.h Thu Jun 18 10:20:52 2009 +--- sepgsql/src/include/nodes/relation.h Wed Jul 15 19:39:56 2009 +*************** typedef struct RelOptInfo +*** 383,388 **** +--- 383,397 ---- + * list just to avoid recomputing the best inner indexscan repeatedly for + * similar outer relations. See comments for InnerIndexscanInfo. + */ ++ ++ /* ++ * Permissions used in Row-level access control features both of DAC ++ * and MAC. The lower 16bit is used for DAC, and rest of upper bits ++ * are used for MAC. When rowlvPerms is zero, so it means we don't need ++ * to apply the row-level stuff on the relation in both of levels. ++ * It can be used as a hint for optimization stuff. ++ */ ++ uint32 rowlvPerms; + } RelOptInfo; + + /* +diff -Nrpc blob/src/include/parser/kwlist.h sepgsql/src/include/parser/kwlist.h +*** blob/src/include/parser/kwlist.h Thu Apr 9 00:13:21 2009 +--- sepgsql/src/include/parser/kwlist.h Thu Dec 24 21:59:25 2009 +*************** PG_KEYWORD("connection", CONNECTION, UNR +*** 88,93 **** +--- 88,94 ---- + 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 -Nrpc blob/src/include/pg_config.h.in sepgsql/src/include/pg_config.h.in +*** blob/src/include/pg_config.h.in Thu Mar 18 09:43:03 2010 +--- sepgsql/src/include/pg_config.h.in Thu Mar 18 01:55:40 2010 +*************** +*** 263,268 **** +--- 263,271 ---- + /* 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,396 **** +--- 394,402 ---- + /* Define to 1 if you have the header file. */ + #undef HAVE_SECURITY_PAM_APPL_H + ++ /* Define to 1 if you enable SELinux support */ ++ #undef HAVE_SELINUX ++ + /* Define to 1 if you have the `setproctitle' function. */ + #undef HAVE_SETPROCTITLE + +diff -Nrpc blob/src/include/security/rowlevel.h sepgsql/src/include/security/rowlevel.h +*** blob/src/include/security/rowlevel.h Thu Jan 1 09:00:00 1970 +--- sepgsql/src/include/security/rowlevel.h Thu Jul 16 17:22:29 2009 +*************** +*** 0 **** +--- 1,44 ---- ++ /* ++ * src/include/security/rowlevel.h ++ * Definition of the facility of row-level access controls ++ * ++ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group ++ * Portions Copyright (c) 1994, Regents of the University of California ++ */ ++ #ifndef ROWLEVEL_H ++ #define ROWLEVEL_H ++ ++ #include "access/htup.h" ++ #include "executor/tuptable.h" ++ #include "nodes/plannodes.h" ++ #include "utils/relcache.h" ++ ++ #define ROWLV_BYPASS_MODE 1 ++ #define ROWLV_FILTER_MODE 2 ++ #define ROWLV_ABORT_MODE 3 ++ ++ extern int ++ rowlvGetPerformingMode(void); ++ ++ extern int ++ rowlvSetPerformingMode(int mode); ++ ++ extern uint32 ++ rowlvSetupPermissions(RangeTblEntry *rte); ++ ++ extern bool ++ rowlvExecScanFilter(Scan *scan, Relation rel, TupleTableSlot *slot); ++ ++ extern void ++ rowlvExecScanAbort(Scan *scan, Relation rel, TupleTableSlot *slot); ++ ++ extern void ++ rowlvHeapTupleInsert(Relation rel, HeapTuple newtup, bool internal); ++ ++ extern void ++ rowlvHeapTupleUpdate(Relation rel, ItemPointer otid, HeapTuple newtup); ++ ++ extern bool ++ rowlvCopyToTuple(Relation rel, HeapTuple tuple); ++ ++ #endif /* ROWLEVEL_H */ +diff -Nrpc blob/src/include/security/sepgsql.h sepgsql/src/include/security/sepgsql.h +*** blob/src/include/security/sepgsql.h Thu Jan 1 09:00:00 1970 +--- sepgsql/src/include/security/sepgsql.h Thu Dec 24 21:59:25 2009 +*************** +*** 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 -Nrpc blob/src/include/storage/fd.h sepgsql/src/include/storage/fd.h +*** blob/src/include/storage/fd.h Tue Jan 13 09:22:28 2009 +--- sepgsql/src/include/storage/fd.h Wed Jul 15 19:48:58 2009 +*************** extern int FileWrite(File file, char *bu +*** 68,73 **** +--- 68,74 ---- + extern int FileSync(File file); + extern off_t FileSeek(File file, off_t offset, int whence); + extern int FileTruncate(File file, off_t offset); ++ extern int FileRawDescriptor(File file); + + /* Operations that allow use of regular stdio --- USE WITH CAUTION */ + extern FILE *AllocateFile(const char *name, const char *mode); +diff -Nrpc blob/src/include/storage/large_object.h sepgsql/src/include/storage/large_object.h +*** blob/src/include/storage/large_object.h Sat Jan 3 12:25:21 2009 +--- sepgsql/src/include/storage/large_object.h Fri Dec 18 10:27:56 2009 +*************** typedef struct LargeObjectDesc +*** 70,76 **** + + /* inversion stuff in inv_api.c */ + extern void close_lo_relation(bool isCommit); +! extern Oid inv_create(Oid lobjId); + extern LargeObjectDesc *inv_open(Oid lobjId, int flags, MemoryContext mcxt); + extern void inv_close(LargeObjectDesc *obj_desc); + extern int inv_drop(Oid lobjId); +--- 70,76 ---- + + /* inversion stuff in inv_api.c */ + extern void close_lo_relation(bool isCommit); +! 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 -Nrpc blob/src/include/storage/lwlock.h sepgsql/src/include/storage/lwlock.h +*** blob/src/include/storage/lwlock.h Fri Mar 6 09:45:33 2009 +--- sepgsql/src/include/storage/lwlock.h Wed Jul 15 19:35:52 2009 +*************** typedef enum LWLockId +*** 67,72 **** +--- 67,73 ---- + AutovacuumLock, + AutovacuumScheduleLock, + SyncScanLock, ++ SepgsqlAvcLock, + /* Individual lock IDs end here */ + FirstBufMappingLock, + FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS, +diff -Nrpc blob/src/include/storage/proc.h sepgsql/src/include/storage/proc.h +*** blob/src/include/storage/proc.h Thu Feb 26 10:18:55 2009 +--- sepgsql/src/include/storage/proc.h Tue Dec 8 14:04:25 2009 +*************** typedef struct PROC_HDR +*** 143,150 **** + * normal operation. Startup process also consumes one slot, but WAL + * writer and autovacuum launcher are launched only after it has + * exited. + */ +! #define NUM_AUXILIARY_PROCS 3 + + + /* configurable options */ +--- 143,152 ---- + * normal operation. Startup process also consumes one slot, but WAL + * writer and autovacuum launcher are launched only after it has + * exited. ++ * In addition, a netlink receiver process may be launched, if SELinux ++ * support is enabled. + */ +! #define NUM_AUXILIARY_PROCS 4 + + + /* configurable options */ +diff -Nrpc blob/src/include/utils/errcodes.h sepgsql/src/include/utils/errcodes.h +*** blob/src/include/utils/errcodes.h Fri Mar 6 09:45:33 2009 +--- sepgsql/src/include/utils/errcodes.h Sun Dec 20 00:41:22 2009 +*************** +*** 301,306 **** +--- 301,307 ---- + #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 -Nrpc blob/src/test/regress/GNUmakefile sepgsql/src/test/regress/GNUmakefile +*** blob/src/test/regress/GNUmakefile Sat Jan 3 13:01:35 2009 +--- sepgsql/src/test/regress/GNUmakefile Tue Dec 1 17:11:40 2009 +*************** ifdef NO_LOCALE +*** 38,43 **** +--- 38,49 ---- + NOLOCALE += --no-locale + endif + ++ # SELinux support ++ ENABLE_SELINUX = ++ ifdef SELINUX ++ ENABLE_SELINUX += --enable-selinux ++ endif ++ + # stuff to pass into build of pg_regress + EXTRADEFS = '-DHOST_TUPLE="$(host_tuple)"' \ + '-DMAKEPROG="$(MAKE)"' \ +*************** tablespace-setup: +*** 138,144 **** + ## Run tests + ## + +! pg_regress_call = ./pg_regress --inputdir=$(srcdir) --dlpath=. --multibyte=$(MULTIBYTE) --load-language=plpgsql $(NOLOCALE) + + check: all + $(pg_regress_call) --temp-install=./tmp_check --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(TEMP_CONF) +--- 144,150 ---- + ## Run tests + ## + +! pg_regress_call = ./pg_regress --inputdir=$(srcdir) --dlpath=. --multibyte=$(MULTIBYTE) --load-language=plpgsql $(NOLOCALE) $(ENABLE_SELINUX) + + check: all + $(pg_regress_call) --temp-install=./tmp_check --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(TEMP_CONF) +diff -Nrpc blob/src/test/regress/expected/sanity_check.out sepgsql/src/test/regress/expected/sanity_check.out +*** blob/src/test/regress/expected/sanity_check.out Fri Dec 18 09:40:55 2009 +--- sepgsql/src/test/regress/expected/sanity_check.out Fri Dec 18 10:27:56 2009 +*************** SELECT relname, relhasindex +*** 113,118 **** +--- 113,119 ---- + pg_pltemplate | t + pg_proc | t + pg_rewrite | t ++ pg_security | t + pg_shdepend | t + pg_shdescription | t + pg_statistic | t +diff -Nrpc blob/src/test/regress/pg_regress.c sepgsql/src/test/regress/pg_regress.c +*** blob/src/test/regress/pg_regress.c Tue Dec 15 17:16:51 2009 +--- sepgsql/src/test/regress/pg_regress.c Tue Dec 15 17:30:25 2009 +*************** static _stringlist *schedulelist = NULL; +*** 82,87 **** +--- 82,88 ---- + static _stringlist *extra_tests = NULL; + static char *temp_install = NULL; + static char *temp_config = NULL; ++ static bool enable_selinux = false; + static char *top_builddir = NULL; + static bool nolocale = false; + static char *hostname = NULL; +*************** help(void) +*** 1863,1868 **** +--- 1864,1870 ---- + printf(_(" --top-builddir=DIR (relative) path to top level build directory\n")); + printf(_(" --port=PORT start postmaster on PORT\n")); + printf(_(" --temp-config=PATH append contents of PATH to temporary config\n")); ++ printf(_(" --enable-selinux enables SELinux support, if available\n")); + printf(_("\n")); + printf(_("Options for using an existing installation:\n")); + printf(_(" --host=HOST use postmaster running on HOST\n")); +*************** regression_main(int argc, char *argv[], +*** 1907,1912 **** +--- 1909,1915 ---- + {"dlpath", required_argument, NULL, 17}, + {"create-role", required_argument, NULL, 18}, + {"temp-config", required_argument, NULL, 19}, ++ {"enable-selinux", optional_argument, NULL, 20}, + {NULL, 0, NULL, 0} + }; + +*************** regression_main(int argc, char *argv[], +*** 1997,2002 **** +--- 2000,2008 ---- + case 19: + temp_config = strdup(optarg); + break; ++ case 20: ++ enable_selinux = true; ++ break; + default: + /* getopt_long already emitted a complaint */ + fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"), +*************** regression_main(int argc, char *argv[], +*** 2086,2095 **** + /* initdb */ + header(_("initializing database system")); + snprintf(buf, sizeof(buf), +! SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE, + bindir, temp_install, datadir, + debug ? " --debug" : "", + nolocale ? " --no-locale" : "", + outputdir); + if (system(buf)) + { +--- 2092,2102 ---- + /* initdb */ + header(_("initializing database system")); + snprintf(buf, sizeof(buf), +! SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean%s%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE, + bindir, temp_install, datadir, + debug ? " --debug" : "", + nolocale ? " --no-locale" : "", ++ enable_selinux ? " --enable-selinux" : "", + outputdir); + if (system(buf)) + { diff --git a/sepostgresql.spec b/sepostgresql.spec index 5fe52de..bdc0765 100644 --- a/sepostgresql.spec +++ b/sepostgresql.spec @@ -11,8 +11,8 @@ Summary: Security Enhanced PostgreSQL Name: sepostgresql -Version: 8.4.2 -Release: 2488%{?dist} +Version: 8.4.3 +Release: 2582%{?dist} License: BSD Group: Applications/Databases Url: http://code.google.com/p/sepgsql/ @@ -194,6 +194,9 @@ fi %attr(700,sepgsql,sepgsql) %dir %{_localstatedir}/lib/sepgsql/backups %changelog +* Thu Mar 18 2010 KaiGai Kohei - 8.4.3-2582 +- upgrade base version 8.4.2->8.4.3 + * 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 diff --git a/sources b/sources index 0aef01a..b1c5788 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -d738227e2f1f742d2f2d4ab56496c5c6 postgresql-8.4.2.tar.bz2 +7f70e7b140fb190f268837255582b07e postgresql-8.4.3.tar.bz2